From de2adcb55a595e676a00fb839c25bed5ead0dcce Mon Sep 17 00:00:00 2001 From: Freezlex Date: Thu, 11 May 2023 20:43:35 +0200 Subject: [PATCH 01/75] fix: Add dash to MATRIX_ROOM_IDENTIFIER_REGEX in order to allow URLS such as foo-bar:foo.bar --- .../element/android/libraries/matrix/api/core/MatrixPatterns.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt index a30baadb55..4e6d468c54 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/core/MatrixPatterns.kt @@ -34,7 +34,7 @@ object MatrixPatterns { val PATTERN_CONTAIN_MATRIX_USER_IDENTIFIER = MATRIX_USER_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE) // regex pattern to find room ids in a string. - private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9]+$DOMAIN_REGEX" + private const val MATRIX_ROOM_IDENTIFIER_REGEX = "![A-Z0-9-]+$DOMAIN_REGEX" private val PATTERN_CONTAIN_MATRIX_ROOM_IDENTIFIER = MATRIX_ROOM_IDENTIFIER_REGEX.toRegex(RegexOption.IGNORE_CASE) // regex pattern to find room aliases in a string. From bbf1ad1adc2cabae5aacfa6e536d3ff781dd4d73 Mon Sep 17 00:00:00 2001 From: Kat Gerasimova Date: Fri, 26 May 2023 11:40:02 +0100 Subject: [PATCH 02/75] Update triage automation for signoff --- .github/workflows/triage-labelled.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/triage-labelled.yml b/.github/workflows/triage-labelled.yml index d7951a012b..ec69eae6bb 100644 --- a/.github/workflows/triage-labelled.yml +++ b/.github/workflows/triage-labelled.yml @@ -43,9 +43,21 @@ jobs: name: Add labelled issues to QA project runs-on: ubuntu-latest if: > - contains(github.event.issue.labels.*.name, 'Team: QA') + contains(github.event.issue.labels.*.name, 'Team: QA') || + contains(github.event.issue.labels.*.name, 'X-Needs-Signoff') steps: - uses: actions/add-to-project@main with: project-url: https://github.com/orgs/vector-im/projects/69 github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} + + signoff: + name: Add labelled issues to signoff project + runs-on: ubuntu-latest + if: > + contains(github.event.issue.labels.*.name, 'X-Needs-Signoff') + steps: + - uses: actions/add-to-project@main + with: + project-url: https://github.com/orgs/vector-im/projects/89 + github-token: ${{ secrets.ELEMENT_BOT_TOKEN }} From 175b7deccb1c34623e09d6a341f8ce283e7526ba Mon Sep 17 00:00:00 2001 From: bmarty Date: Mon, 29 May 2023 00:09:47 +0000 Subject: [PATCH 03/75] Sync Strings from Localazy --- .../src/main/res/values-cs/translations.xml | 17 +++ .../src/main/res/values-de/translations.xml | 8 + .../src/main/res/values-es/translations.xml | 1 - .../src/main/res/values-fr/translations.xml | 17 +++ .../src/main/res/values-it/translations.xml | 1 - .../src/main/res/values-ro/translations.xml | 1 - .../impl/src/main/res/values/localazy.xml | 1 - .../src/main/res/values-cs/translations.xml | 8 + .../src/main/res/values-de/translations.xml | 3 + .../src/main/res/values-fr/translations.xml | 5 + .../src/main/res/values-cs/translations.xml | 18 +++ .../src/main/res/values-de/translations.xml | 13 +- .../src/main/res/values-fr/translations.xml | 15 ++ .../impl/src/main/res/values/localazy.xml | 1 + .../src/main/res/values-cs/translations.xml | 8 + .../src/main/res/values-de/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 8 + .../src/main/res/values-cs/translations.xml | 8 + .../src/main/res/values-de/translations.xml | 4 + .../src/main/res/values-fr/translations.xml | 12 ++ .../src/main/res/values-ro/translations.xml | 6 + .../src/main/res/values-cs/translations.xml | 5 + .../src/main/res/values-de/translations.xml | 1 + .../src/main/res/values-fr/translations.xml | 5 + .../src/main/res/values-cs/translations.xml | 4 + .../src/main/res/values-de/translations.xml | 5 + .../src/main/res/values-fr/translations.xml | 5 + .../src/main/res/values-cs/translations.xml | 4 + .../src/main/res/values-de/translations.xml | 7 + .../src/main/res/values-fr/translations.xml | 14 ++ .../src/main/res/values-cs/translations.xml | 23 +++ .../src/main/res/values-de/translations.xml | 5 + .../src/main/res/values-fr/translations.xml | 25 +++ .../src/main/res/values-ro/translations.xml | 7 + .../impl/src/main/res/values/localazy.xml | 5 +- .../src/main/res/values-cs/translations.xml | 7 + .../src/main/res/values-de/translations.xml | 3 + .../src/main/res/values-fr/translations.xml | 7 + .../src/main/res/values-cs/translations.xml | 19 +++ .../src/main/res/values-de/translations.xml | 9 ++ .../src/main/res/values-fr/translations.xml | 19 +++ .../src/main/res/values-cs/translations.xml | 4 + .../src/main/res/values-de/translations.xml | 4 + .../src/main/res/values-fr/translations.xml | 4 + .../src/main/res/values-de/translations.xml | 38 +++-- .../src/main/res/values-fr/translations.xml | 57 +++++++ .../src/main/res/values-cs/translations.xml | 28 ++++ .../src/main/res/values-de/translations.xml | 12 ++ .../src/main/res/values-fr/translations.xml | 72 +++++++++ .../src/main/res/values-cs/translations.xml | 4 + .../src/main/res/values-de/translations.xml | 12 ++ .../src/main/res/values-fr/translations.xml | 17 +++ .../src/main/res/values-cs/translations.xml | 143 ++++++++++++++++++ .../src/main/res/values-de/translations.xml | 46 +++++- .../src/main/res/values-fr/translations.xml | 131 ++++++++++++++++ .../src/main/res/values-ro/translations.xml | 13 ++ .../src/main/res/values/localazy.xml | 11 +- 57 files changed, 912 insertions(+), 19 deletions(-) create mode 100644 features/createroom/impl/src/main/res/values-cs/translations.xml create mode 100644 features/createroom/impl/src/main/res/values-fr/translations.xml create mode 100644 features/invitelist/impl/src/main/res/values-cs/translations.xml create mode 100644 features/invitelist/impl/src/main/res/values-fr/translations.xml create mode 100644 features/login/impl/src/main/res/values-cs/translations.xml create mode 100644 features/logout/api/src/main/res/values-cs/translations.xml create mode 100644 features/logout/api/src/main/res/values-fr/translations.xml create mode 100644 features/messages/impl/src/main/res/values-cs/translations.xml create mode 100644 features/messages/impl/src/main/res/values-fr/translations.xml create mode 100644 features/onboarding/impl/src/main/res/values-cs/translations.xml create mode 100644 features/onboarding/impl/src/main/res/values-fr/translations.xml create mode 100644 features/rageshake/api/src/main/res/values-cs/translations.xml create mode 100644 features/rageshake/api/src/main/res/values-de/translations.xml create mode 100644 features/rageshake/api/src/main/res/values-fr/translations.xml create mode 100644 features/rageshake/impl/src/main/res/values-cs/translations.xml create mode 100644 features/rageshake/impl/src/main/res/values-fr/translations.xml create mode 100644 features/roomdetails/impl/src/main/res/values-cs/translations.xml create mode 100644 features/roomdetails/impl/src/main/res/values-fr/translations.xml create mode 100644 features/roomlist/impl/src/main/res/values-cs/translations.xml create mode 100644 features/roomlist/impl/src/main/res/values-fr/translations.xml create mode 100644 features/verifysession/impl/src/main/res/values-cs/translations.xml create mode 100644 features/verifysession/impl/src/main/res/values-fr/translations.xml create mode 100644 libraries/androidutils/src/main/res/values-cs/translations.xml create mode 100644 libraries/androidutils/src/main/res/values-de/translations.xml create mode 100644 libraries/androidutils/src/main/res/values-fr/translations.xml create mode 100644 libraries/eventformatter/impl/src/main/res/values-fr/translations.xml create mode 100644 libraries/push/impl/src/main/res/values-cs/translations.xml create mode 100644 libraries/push/impl/src/main/res/values-fr/translations.xml create mode 100644 libraries/textcomposer/src/main/res/values-cs/translations.xml create mode 100644 libraries/textcomposer/src/main/res/values-fr/translations.xml create mode 100644 libraries/ui-strings/src/main/res/values-cs/translations.xml diff --git a/features/createroom/impl/src/main/res/values-cs/translations.xml b/features/createroom/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..646519f91e --- /dev/null +++ b/features/createroom/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,17 @@ + + + "Nová místnost" + "Pozvat lidi" + "Přidat lidi" + "Při vytváření místnosti došlo k chybě" + "Zprávy v této místnosti jsou šifrované. Šifrování nelze později vypnout." + "Soukromá místnost (jen pro pozvané)" + "Zprávy nejsou šifrované a může si je přečíst kdokoli. Šifrování můžete povolit později." + "Veřejná místnost (kdokoli)" + "Název místnosti" + "např. Produktový sprint" + "Téma (nepovinné)" + "O čem je tato místnost?" + "Při pokusu o zahájení chatu došlo k chybě" + "Vytvořit místnost" + \ No newline at end of file diff --git a/features/createroom/impl/src/main/res/values-de/translations.xml b/features/createroom/impl/src/main/res/values-de/translations.xml index 6c3a7c8130..1d9f69ff63 100644 --- a/features/createroom/impl/src/main/res/values-de/translations.xml +++ b/features/createroom/impl/src/main/res/values-de/translations.xml @@ -3,7 +3,15 @@ "Neuer Raum" "Personen einladen" "Personen hinzufügen" + "Beim Erstellen des Raums ist ein Fehler aufgetreten" + "Die Nachrichten in diesem Raum sind verschlüsselt. Die Verschlüsselung kann nicht nachträglich deaktiviert werden." "Privater Raum (nur auf Einladung)" + "Nachrichten sind nicht verschlüsselt und jeder kann sie lesen. Du kannst die Verschlüsselung zu einem späteren Zeitpunkt aktivieren." + "Öffentlicher Raum (jeder)" "Raumname" + "z.B. Produkt-Sprint" "Thema (optional)" + "Worum geht es in diesem Raum?" + "Beim Versuch, einen Chat zu starten, ist ein Fehler aufgetreten" + "Raum erstellen" \ No newline at end of file diff --git a/features/createroom/impl/src/main/res/values-es/translations.xml b/features/createroom/impl/src/main/res/values-es/translations.xml index 8c8fc9b48f..9a3030ba71 100644 --- a/features/createroom/impl/src/main/res/values-es/translations.xml +++ b/features/createroom/impl/src/main/res/values-es/translations.xml @@ -4,6 +4,5 @@ "Invitar gente" "Añadir personas" "Se ha producido un error al intentar iniciar un chat" - "No podemos validar el ID de Matrix de este usuario. Es posible que no reciba la invitación." "Crear una sala" \ No newline at end of file diff --git a/features/createroom/impl/src/main/res/values-fr/translations.xml b/features/createroom/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..d0aab95e8d --- /dev/null +++ b/features/createroom/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,17 @@ + + + "Nouvelle salle" + "Inviter des personnes" + "Ajouter des personnes" + "Une erreur s\'est produite lors de la création de la salle" + "Les messages dans cette salle sont chiffrés. Le chiffrement ne peut pas être désactivé par la suite." + "Salle privée (sur invitation uniquement)" + "Les messages ne sont pas chiffrés et n\'importe qui peut les lire. Vous pouvez activer le chiffrement ultérieurement." + "Salle publique (n’importe qui)" + "Nom de la salle" + "Ex: Sprint Produit" + "Sujet (optionnel)" + "De quoi parle cette salle ?" + "Une erreur s\'est produite lors de la tentative de démarrage d\'une discussion" + "Créer une salle" + \ No newline at end of file diff --git a/features/createroom/impl/src/main/res/values-it/translations.xml b/features/createroom/impl/src/main/res/values-it/translations.xml index 3562a5f017..214cd9406d 100644 --- a/features/createroom/impl/src/main/res/values-it/translations.xml +++ b/features/createroom/impl/src/main/res/values-it/translations.xml @@ -4,6 +4,5 @@ "Invita persone" "Aggiungi persone" "Si è verificato un errore durante il tentativo di avviare una chat" - "Non possiamo convalidare l\'ID Matrix di questo utente. L\'invito potrebbe non essere ricevuto." "Crea una stanza" \ No newline at end of file diff --git a/features/createroom/impl/src/main/res/values-ro/translations.xml b/features/createroom/impl/src/main/res/values-ro/translations.xml index 8fd60b5a9e..55b9f6d692 100644 --- a/features/createroom/impl/src/main/res/values-ro/translations.xml +++ b/features/createroom/impl/src/main/res/values-ro/translations.xml @@ -13,6 +13,5 @@ "Subiect (opțional)" "Despre ce este această cameră?" "A apărut o eroare la încercarea începerii conversației" - "Nu am putut valida ID-ul Matrix al acestui utilizator. Este posibil ca invitația să nu fi fost primită." "Creați o cameră" \ No newline at end of file diff --git a/features/createroom/impl/src/main/res/values/localazy.xml b/features/createroom/impl/src/main/res/values/localazy.xml index 578c8334da..128855932a 100644 --- a/features/createroom/impl/src/main/res/values/localazy.xml +++ b/features/createroom/impl/src/main/res/values/localazy.xml @@ -13,6 +13,5 @@ "Topic (optional)" "What is this room about?" "An error occurred when trying to start a chat" - "We can’t validate this user’s Matrix ID. The invite might not be received." "Create a room" \ No newline at end of file diff --git a/features/invitelist/impl/src/main/res/values-cs/translations.xml b/features/invitelist/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..021830fbf1 --- /dev/null +++ b/features/invitelist/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,8 @@ + + + "Opravdu chcete odmítnout připojení k %1$s?" + "Odmítnout pozvání" + "Opravdu chcete odmítnout chat s %1$s?" + "Odmítnout chat" + "Žádné pozvánky" + \ No newline at end of file diff --git a/features/invitelist/impl/src/main/res/values-de/translations.xml b/features/invitelist/impl/src/main/res/values-de/translations.xml index 95e63cf5f2..cc86ca00a7 100644 --- a/features/invitelist/impl/src/main/res/values-de/translations.xml +++ b/features/invitelist/impl/src/main/res/values-de/translations.xml @@ -1,5 +1,8 @@ + "Möchten Sie den Beitritt zu %1$s wirklich ablehnen?" + "Einladung ablehnen" + "Möchten Sie den Chat mit %1$s wirklich ablehnen?" "Chat ablehnen" "Keine Einladungen" "%1$s hat dich eingeladen" diff --git a/features/invitelist/impl/src/main/res/values-fr/translations.xml b/features/invitelist/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..19935998f0 --- /dev/null +++ b/features/invitelist/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,5 @@ + + + "Aucune invitation" + "%1$s vous a invité." + \ No newline at end of file diff --git a/features/login/impl/src/main/res/values-cs/translations.xml b/features/login/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..37923cddd1 --- /dev/null +++ b/features/login/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,18 @@ + + + "Nepodařilo se nám připojit k tomuto domovskému serveru. Zkontrolujte prosím, zda jste správně zadali adresu URL domovského serveru. Pokud je adresa URL správná, obraťte se na správce domovského serveru, který vám poskytne další pomoc." + "Adresa URL domovského serveru" + "Jaká je adresa vašeho serveru?" + "Tento účet byl deaktivován." + "Nesprávné uživatelské jméno nebo heslo" + "Toto není platný identifikátor uživatele. Očekávaný formát: \'@user:homeserver.org\'" + "Vybraný domovský server nepodporuje přihlášení pomocí hesla nebo OIDC. Kontaktujte prosím svého správce nebo vyberte jiný domovský server." + "Zadejte své údaje" + "Kde budou vaše konverzace probíhat" + "Vítejte zpět!" + "Pokračovat" + "Vyberte svůj server" + "Heslo" + "Pokračovat" + "Uživatelské jméno" + \ No newline at end of file diff --git a/features/login/impl/src/main/res/values-de/translations.xml b/features/login/impl/src/main/res/values-de/translations.xml index 4c9b232147..cb380abfad 100644 --- a/features/login/impl/src/main/res/values-de/translations.xml +++ b/features/login/impl/src/main/res/values-de/translations.xml @@ -1,11 +1,20 @@ "Wir konnten diesen Homeserver nicht erreichen. Bitte überprüfen Sie, ob Sie die Homeserver-URL korrekt eingegeben haben. Wenn die URL korrekt ist, wenden Sie sich an Ihren Homeserver-Administrator, um weitere Hilfe zu erhalten." - "Dieser Server unterstützt derzeit kein Sliding Sync." + "Dieser Server unterstützt derzeit keine Sliding Sync." "Homeserver-URL" - "Sie können nur eine Verbindung zu einem vorhandenen Server herstellen, der Sliding Sync unterstützt. Ihr Homeserver-Administrator muss dies konfigurieren. %1$s" + "Du kannst dich nur mit einem existierenden Server verbinden, der Sliding Sync unterstützt. Dein Homeserver-Administrator muss es konfigurieren. %1$s" "Wie lautet die Adresse deines Servers?" + "Dieses Konto wurde deaktiviert." + "Falscher Benutzername und/oder Passwort" + "Dies ist kein gültiger Benutzeridentifikator. Erwartetes Format: \'@user:homeserver.org\'" + "Der ausgewählte Homeserver unterstützt kein Passwort- oder OIDC-Login. Bitte kontaktiere deinen Admin oder wähle einen anderen Homeserver." + "Gib deine Daten ein" + "Wo deine Gespräche leben" "Willkommen zurück!" + "Weiter" + "Wählen deinen Server" "Passwort" + "Weiter" "Benutzername" \ No newline at end of file diff --git a/features/login/impl/src/main/res/values-fr/translations.xml b/features/login/impl/src/main/res/values-fr/translations.xml index 1b64d9f5fd..9d8f50e979 100644 --- a/features/login/impl/src/main/res/values-fr/translations.xml +++ b/features/login/impl/src/main/res/values-fr/translations.xml @@ -1,5 +1,20 @@ + "Nous n\'avons pas pu atteindre ce serveur domestique. Vérifiez que vous avez correctement saisi l\'URL du serveur d\'accueil. Si l\'URL est correcte, contactez l\'administrateur de votre serveur domestique pour obtenir de l\'aide." + "Ce serveur ne prend actuellement pas en charge la synchronisation glissante." + "URL du serveur d\'accueil" + "Vous ne pouvez vous connecter qu\'à un serveur existant qui prend en charge la synchronisation glissante. L\'administrateur de votre serveur domestique devra la configurer. %1$s" + "Quelle est l\'adresse de votre serveur ?" + "Ce compte a été désactivé." + "Nom d\'utilisateur et/ou mot de passe incorrect" + "Il ne s\'agit pas d\'un identifiant utilisateur valide. Format attendu : « @user:homeserver.org »" + "Le serveur domestique sélectionné ne prend pas en charge le mot de passe ou la connexion OIDC. Contactez votre administrateur ou choisissez un autre serveur domestique." + "Saisir vos informations personnelles" + "Où se déroulent vos conversations" + "Heureux de vous revoir!" "Continuer" + "Sélectionnez votre serveur" + "Mot de passe" "Continuer" + "Nom d\'utilisateur" \ No newline at end of file diff --git a/features/login/impl/src/main/res/values/localazy.xml b/features/login/impl/src/main/res/values/localazy.xml index 6b0ecee43d..cf59844e89 100644 --- a/features/login/impl/src/main/res/values/localazy.xml +++ b/features/login/impl/src/main/res/values/localazy.xml @@ -12,6 +12,7 @@ "Enter your details" "Where your conversations live" "Welcome back!" + "Sign in to %1$s" "Continue" "Select your server" "Password" diff --git a/features/logout/api/src/main/res/values-cs/translations.xml b/features/logout/api/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..31761ee2d9 --- /dev/null +++ b/features/logout/api/src/main/res/values-cs/translations.xml @@ -0,0 +1,8 @@ + + + "Opravdu se chcete odhlásit?" + "Odhlásit se" + "Odhlašování…" + "Odhlásit se" + "Odhlásit se" + \ No newline at end of file diff --git a/features/logout/api/src/main/res/values-de/translations.xml b/features/logout/api/src/main/res/values-de/translations.xml index 429a6017da..5b9fac1031 100644 --- a/features/logout/api/src/main/res/values-de/translations.xml +++ b/features/logout/api/src/main/res/values-de/translations.xml @@ -1,5 +1,6 @@ + "Möchtest du Dich wirklich abmelden?" "Abmelden" "Abmeldung läuft…" "Abmelden" diff --git a/features/logout/api/src/main/res/values-fr/translations.xml b/features/logout/api/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..9d9ad724df --- /dev/null +++ b/features/logout/api/src/main/res/values-fr/translations.xml @@ -0,0 +1,8 @@ + + + "Êtes-vous sûr de vouloir vous déconnecter?" + "Se déconnecter" + "Déconnexion en cours…" + "Se déconnecter" + "Se déconnecter" + \ No newline at end of file diff --git a/features/messages/impl/src/main/res/values-cs/translations.xml b/features/messages/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..6072ac8545 --- /dev/null +++ b/features/messages/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,8 @@ + + + "Fotoaparát" + "Vyfotit" + "Natočit video" + "Příloha" + "Knihovna fotografií a videí" + \ No newline at end of file diff --git a/features/messages/impl/src/main/res/values-de/translations.xml b/features/messages/impl/src/main/res/values-de/translations.xml index 64f863c55f..78088386ad 100644 --- a/features/messages/impl/src/main/res/values-de/translations.xml +++ b/features/messages/impl/src/main/res/values-de/translations.xml @@ -1,5 +1,9 @@ + + "%1$d Raumänderung" + "%1$d Raumänderungen" + "Kamera" "Foto aufnehmen" "Video aufnehmen" diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..310d1eeaa4 --- /dev/null +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,12 @@ + + + + "Aucun changement dans la salle" + "%1$d changement dans la salle" + "%1$d changements dans la salle" + "%1$d changements dans la salle" + "%1$d changements dans la salle" + "%1$d changements dans la salle" + + "Prendre une photo" + \ No newline at end of file diff --git a/features/messages/impl/src/main/res/values-ro/translations.xml b/features/messages/impl/src/main/res/values-ro/translations.xml index 68d83cacfe..0a2aa20456 100644 --- a/features/messages/impl/src/main/res/values-ro/translations.xml +++ b/features/messages/impl/src/main/res/values-ro/translations.xml @@ -5,4 +5,10 @@ "%1$d schimbări ale camerei" "%1$d schimbări ale camerei" + "Cameră foto" + "Faceți o fotografie" + "Înregistrați un videoclip" + "Atașament" + "Bibliotecă foto și video" + "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." \ No newline at end of file diff --git a/features/onboarding/impl/src/main/res/values-cs/translations.xml b/features/onboarding/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..f9eb134f01 --- /dev/null +++ b/features/onboarding/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,5 @@ + + + "Vítejte v %1$s Beta. Vylepšený, pro rychlost a jednoduchost." + "Buďte ve svém živlu" + \ No newline at end of file diff --git a/features/onboarding/impl/src/main/res/values-de/translations.xml b/features/onboarding/impl/src/main/res/values-de/translations.xml index 82e01aa522..60f4334e45 100644 --- a/features/onboarding/impl/src/main/res/values-de/translations.xml +++ b/features/onboarding/impl/src/main/res/values-de/translations.xml @@ -1,4 +1,5 @@ + "Willkommen zur %1$s Beta. Verbessert, für Geschwindigkeit und Einfachheit." "Sei in deinem Element" \ No newline at end of file diff --git a/features/onboarding/impl/src/main/res/values-fr/translations.xml b/features/onboarding/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..502b464fa7 --- /dev/null +++ b/features/onboarding/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,5 @@ + + + "Bienvenue dans la version %1$s Beta. Affiné pour plus de rapidité et de simplicité." + "Soyez dans votre Element" + \ No newline at end of file diff --git a/features/rageshake/api/src/main/res/values-cs/translations.xml b/features/rageshake/api/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..ce53ad9285 --- /dev/null +++ b/features/rageshake/api/src/main/res/values-cs/translations.xml @@ -0,0 +1,4 @@ + + + "%1$s havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?" + \ No newline at end of file diff --git a/features/rageshake/api/src/main/res/values-de/translations.xml b/features/rageshake/api/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..1633cd340e --- /dev/null +++ b/features/rageshake/api/src/main/res/values-de/translations.xml @@ -0,0 +1,5 @@ + + + "%1$s ist bei der letzten Verwendung abgestürzt. Möchtest du uns einen Absturzbericht senden?" + "Du scheinst frustriert das Telefon zu schütteln. Möchtest du den Fehlerberichtsbildschirm öffnen?" + \ No newline at end of file diff --git a/features/rageshake/api/src/main/res/values-fr/translations.xml b/features/rageshake/api/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..32bdaf4685 --- /dev/null +++ b/features/rageshake/api/src/main/res/values-fr/translations.xml @@ -0,0 +1,5 @@ + + + "%1$s a planté la dernière fois qu\'il a été utilisé. Souhaitez-vous partager un rapport de crash avec nous ?" + "Vous semblez secouer le téléphone de frustration. Voulez-vous ouvrir le formulaire de rapport de problème ?" + \ No newline at end of file diff --git a/features/rageshake/impl/src/main/res/values-cs/translations.xml b/features/rageshake/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..b02e635326 --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,4 @@ + + + "%1$s havaroval při posledním použití. Chcete se s námi podělit o zprávu o selhání?" + \ No newline at end of file diff --git a/features/rageshake/impl/src/main/res/values-de/translations.xml b/features/rageshake/impl/src/main/res/values-de/translations.xml index 8ca17d23ee..8712bba1a0 100644 --- a/features/rageshake/impl/src/main/res/values-de/translations.xml +++ b/features/rageshake/impl/src/main/res/values-de/translations.xml @@ -1,7 +1,14 @@ "Bildschirmfoto anhängen" + "Sie können mich kontaktieren, wenn Sie weitere Fragen haben" + "Bildschirmfoto bearbeiten" + "Beschreibe bitte den Fehler. Was hast du gemacht? Was hätte passieren sollen? Was ist passiert? Bitte beschreibe alles mit so vielen Details wie möglich." "Beschreibe den Fehler…" + "Wenn möglich, verfassen Sie die Beschreibung bitte auf Englisch." "Absturzprotokolle senden" + "Senden Sie Protokolle, um zu helfen" "Bildschirmfoto senden" + "Um zu überprüfen, ob alles wie vorgesehen funktioniert, werden Protokolle mit deiner Nachricht gesendet. Diese werden privat sein. Um nur Ihre Nachricht zu senden, schalte diese Einstellung aus." + "%1$s ist bei der letzten Verwendung abgestürzt. Möchtest du uns einen Absturzbericht senden?" \ No newline at end of file diff --git a/features/rageshake/impl/src/main/res/values-fr/translations.xml b/features/rageshake/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..e5e88975a6 --- /dev/null +++ b/features/rageshake/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,14 @@ + + + "Joindre une capture d\'écran" + "Vous pouvez me contacter si vous avez des questions complémentaires" + "Modifier la capture d\'écran" + "S\'il vous plait, veuillez décrire le bogue. Qu\'avez-vous fait ? À quoi vous attendiez-vous ? Que s\'est-il réellement passé. Veuillez ajouter le plus de détails possible." + "Décrire le bogue" + "Si possible, veuillez rédiger la description en anglais." + "Envoyer des journaux d’incident" + "Envoyer le journal pour nous aider" + "Envoyer une capture d’écran" + "Pour vérifier que les choses fonctionnent comme prévu, les journaux seront envoyés avec votre message. Ceux-ci seront privées. Pour simplement envoyer votre message, désactivez ce paramètre." + "%1$s a planté la dernière fois qu\'il a été utilisé. Souhaitez-vous partager un rapport de crash avec nous ?" + \ No newline at end of file diff --git a/features/roomdetails/impl/src/main/res/values-cs/translations.xml b/features/roomdetails/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..97dcbdfa4b --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,23 @@ + + + + "1 osoba" + "%1$d osoby" + "%1$d osob" + + "Zprávy jsou zabezpečeny zámky. Pouze vy a příjemci máte jedinečné klíče k jejich odemčení." + "Šifrování zpráv povoleno" + "Sdílet místnost" + "Nevyřízeno" + "Zablokovat" + "Blokovaní uživatelé vám nebudou moci posílat zprávy a všechny zprávy od nich budou skryty. Tuto akci můžete kdykoli vrátit zpět." + "Zablokovat uživatele" + "Odblokovat" + "Po odblokování uživatele budete moci opět zobrazit všechny jeho zprávy." + "Odblokovat uživatele" + "Pozvat lidi" + "Opustit místnost" + "Lidé" + "Zabezpečení" + "Téma" + \ No newline at end of file diff --git a/features/roomdetails/impl/src/main/res/values-de/translations.xml b/features/roomdetails/impl/src/main/res/values-de/translations.xml index 4e5b3e9a74..f5eb451171 100644 --- a/features/roomdetails/impl/src/main/res/values-de/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-de/translations.xml @@ -5,13 +5,18 @@ "%1$d Personen" "Bereits eingeladen" + "Nachrichten sind mit Schlössern gesichert. Nur du und der Empfänger haben die eindeutigen Schlüssel, um sie zu entsperren." + "Nachrichtenverschlüsselung aktiviert" "Raum teilen" "Blockieren" + "Blockierte Benutzer können dir keine Nachrichten senden und alle Nachrichten von ihnen werden ausgeblendet. Du kannst diese Aktion jederzeit rückgängig machen." "Nutzer blockieren" "Blockierung aufheben" + "Wenn du den Benutzer entsperrst, kannst du wieder alle Nachrichten von ihm sehen." "Nutzer entblockieren" "Personen einladen" "Raum verlassen" + "Personen" "Sicherheit" "Thema" \ No newline at end of file diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..081e7b1a14 --- /dev/null +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,25 @@ + + + + "vide" + "1 personne" + "%1$d personnes" + "%1$d personnes" + "%1$d personnes" + "%1$d personnes" + + "Les messages sont sécurisés par des verrous. Seuls vous et les destinataires possédez les clés uniques pour les déverrouiller." + "Chiffrement des messages activé" + "Partager la salle" + "Bloquer" + "Les utilisateurs bloqués ne pourront pas vous envoyer de messages et tous leurs messages seront masqués. Vous pouvez annuler cette action à tout moment." + "Bloquer l\'utilisateur" + "Débloquer" + "Lorsque vous débloquez l\'utilisateur, vous pourrez à nouveau voir tous leur messages." + "Débloquer l\'utilisateur" + "Inviter des personnes" + "Quitter la salle" + "Personnes" + "Sécurité" + "Sujet" + \ No newline at end of file diff --git a/features/roomdetails/impl/src/main/res/values-ro/translations.xml b/features/roomdetails/impl/src/main/res/values-ro/translations.xml index dc154df2e0..8f9bd6f30a 100644 --- a/features/roomdetails/impl/src/main/res/values-ro/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-ro/translations.xml @@ -4,9 +4,16 @@ "o persoană" "%1$d persoane" + "Adăugare subiect" + "Deja membru" + "Deja invitat" + "A apărut o eroare la actualizarea detaliilor camerei" "Mesajele sunt securizate cu încuietori. Doar dumneavoastră și destinatarii aveți cheile unice pentru a le debloca." "Criptarea mesajelor este activată" "Partajați camera" + "Se actualizează camera…" + "În așteptare" + "Membrii camerei" "Blocați" "Utilizatorii blocați nu vă vor putea trimite mesaje și toate mesajele lor vor fi ascunse. Puteți anula această acțiune oricând." "Blocați utilizatorul" diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 393052591f..ec907c6078 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -4,9 +4,12 @@ "1 person" "%1$d people" + "Add topic" "Already a member" "Already invited" - "An error occurred when updating the room details" + "Edit Room" + "We were unable to update all the information for this room." + "Unable to update room" "Messages are secured with locks. Only you and the recipients have the unique keys to unlock them." "Message encryption enabled" "Share room" diff --git a/features/roomlist/impl/src/main/res/values-cs/translations.xml b/features/roomlist/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..a222367f09 --- /dev/null +++ b/features/roomlist/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,7 @@ + + + "Vytvořte novou konverzaci nebo místnost" + "Všechny chaty" + "Zdá se, že používáte nové zařízení. Ověřte přihlášení, abyste měli přístup k zašifrovaným zprávám." + "Přístup k historii zpráv" + \ No newline at end of file diff --git a/features/roomlist/impl/src/main/res/values-de/translations.xml b/features/roomlist/impl/src/main/res/values-de/translations.xml index be0109cbe2..ab61ed4c4e 100644 --- a/features/roomlist/impl/src/main/res/values-de/translations.xml +++ b/features/roomlist/impl/src/main/res/values-de/translations.xml @@ -1,4 +1,7 @@ + "Ein neues Gespräch oder einen neuen Raum erstellen" "Alle Chats" + "Es sieht so aus, als ob du ein neues Gerät verwendest. Verifiziere, dass du es bist, um auf deine verschlüsselten Nachrichten zuzugreifen." + "Greife auf deine Nachrichten-Historie zu" \ No newline at end of file diff --git a/features/roomlist/impl/src/main/res/values-fr/translations.xml b/features/roomlist/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..6a049a4e72 --- /dev/null +++ b/features/roomlist/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,7 @@ + + + "Créer une nouvelle conversation ou une nouvelle salle" + "Toutes les conversations" + "Il semblerait que vous utilisiez un nouvel appareil. Vérifiez que vous êtes bien autorisé à accéder à vos messages cryptés." + "Accédez à l\'historique de vos messages" + \ No newline at end of file diff --git a/features/verifysession/impl/src/main/res/values-cs/translations.xml b/features/verifysession/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..41f95d41fe --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,19 @@ + + + "Něco není v pořádku. Buď vypršel časový limit požadavku, nebo byl požadavek zamítnut." + "Zkontrolujte, zda se níže uvedené emotikony shodují s emotikony zobrazenými na jiné relaci." + "Porovnání emotikonů" + "Vaše nová relace je nyní ověřena. Má přístup k vašim zašifrovaným zprávám a ostatní uživatelé ji uvidí jako důvěryhodnou." + "Pro přístup k historii zašifrovaných zpráv prokažte, že jste to vy." + "Otevřete existující relaci" + "Opakovat ověření" + "Jsem připraven" + "Čekání na shodu" + "Porovnejte jedinečné emotikony a ujistěte se, že jsou zobrazeny ve stejném pořadí." + "Neshodují se" + "Shodují se" + "Pro pokračování přijměte požadavek na zahájení ověření v jiné relaci." + "Čekání na přijetí žádosti" + "Ověření zrušeno" + "Začít" + \ No newline at end of file diff --git a/features/verifysession/impl/src/main/res/values-de/translations.xml b/features/verifysession/impl/src/main/res/values-de/translations.xml index e3817c2507..53479844b4 100644 --- a/features/verifysession/impl/src/main/res/values-de/translations.xml +++ b/features/verifysession/impl/src/main/res/values-de/translations.xml @@ -1,10 +1,19 @@ + "Etwas scheint nicht zu stimmen. Entweder ist die Antwortzeit für die Anfrage abgelaufen oder die Anfrage wurde abgelehnt." + "Bestätige, dass die folgenden Emojis mit denen deiner anderen Sitzung übereinstimmen." "Emojis vergleichen" + "Deine neue Sitzung ist jetzt verifiziert. Sie hat Zugriff auf deine verschlüsselten Nachrichten und andere Benutzer werden sie als vertrauenswürdig sehen." "Beweise, dass du es bist, um auf deinen verschlüsselten Nachrichtenverlauf zuzugreifen." + "Eine bestehende Sitzung öffnen" + "Verifizierung erneut versuchen" "Ich bin bereit" "Warten auf Übereinstimmung" + "Vergleiche die einzigartigen Emojis und achte darauf, dass sie in derselben Reihenfolge erscheinen." "Sie stimmen nicht überein" "Sie stimmen überein" + "Akzeptiere die Aufforderung zum Starten des Verifizierungsprozesses in deiner anderen Sitzung, um fortzufahren." + "Warten auf die Annahme der Anfrage" "Verifizierung abgebrochen" + "Starten" \ No newline at end of file diff --git a/features/verifysession/impl/src/main/res/values-fr/translations.xml b/features/verifysession/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..cd06c5db8b --- /dev/null +++ b/features/verifysession/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,19 @@ + + + "Quelque chose ne semble pas normal. Soit la demande a dépassé le temps imparti, soit elle a été refusée." + "Confirmez que les emojis ci-dessous correspondent à ceux affichés sur votre autre session." + "Comparez les émojis" + "Votre nouvelle session est désormais vérifiée. Elle a accès à vos messages cryptés et les autres utilisateurs la verront identifiée comme fiable." + "Prouvez qu\'il s\'agit bien de vous pour accéder à l\'historique de vos messages cryptés." + "Ouvrir une session existante" + "Réessayer la vérification" + "Je suis prêt.e" + "En attente de correspondance" + "Comparez les emoji uniques en veillant à ce qu\'ils apparaissent dans le même ordre." + "Ils ne correspondent pas" + "Ils correspondent" + "Pour continuer, acceptez la demande de lancement de la procédure de vérification dans votre autre session." + "En attente d\'acceptation de la demande" + "Vérification annulée" + "Démarrer" + \ No newline at end of file diff --git a/libraries/androidutils/src/main/res/values-cs/translations.xml b/libraries/androidutils/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..ab592fee1d --- /dev/null +++ b/libraries/androidutils/src/main/res/values-cs/translations.xml @@ -0,0 +1,4 @@ + + + "Nebyla nalezena žádná kompatibilní aplikace, která by tuto akci zpracovala." + \ No newline at end of file diff --git a/libraries/androidutils/src/main/res/values-de/translations.xml b/libraries/androidutils/src/main/res/values-de/translations.xml new file mode 100644 index 0000000000..a34a5b393b --- /dev/null +++ b/libraries/androidutils/src/main/res/values-de/translations.xml @@ -0,0 +1,4 @@ + + + "Keine kompatible App für diese Aktion gefunden." + \ No newline at end of file diff --git a/libraries/androidutils/src/main/res/values-fr/translations.xml b/libraries/androidutils/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..d564c18817 --- /dev/null +++ b/libraries/androidutils/src/main/res/values-fr/translations.xml @@ -0,0 +1,4 @@ + + + "Aucune application compatible n\'a été trouvée pour gérer cette action." + \ No newline at end of file diff --git a/libraries/eventformatter/impl/src/main/res/values-de/translations.xml b/libraries/eventformatter/impl/src/main/res/values-de/translations.xml index 298bd3d40b..62854b7d46 100644 --- a/libraries/eventformatter/impl/src/main/res/values-de/translations.xml +++ b/libraries/eventformatter/impl/src/main/res/values-de/translations.xml @@ -1,17 +1,20 @@ - "(Avatar wurde ebenfalls geändert)" - "%1$s hat seinen Avatar geändert" + "(Profilbild wurde auch geändert)" + "%1$s hat sein Profilbild geändert" "Du hast deinen Avatar geändert" - "%1$s hat den Anzeigenamen von %2$s in %3$s geändert" + "%1$s hat seinen Anzeigenamen von %2$s in %3$s geändert" "Du hast deinen Anzeigenamen von %1$s in %2$s geändert" - "%1$s hat den Anzeigenamen entfernt (war %2$s)" - "Du hast deinen Anzeigenamen entfernt (war %1$s)" - "%1$s hat den Anzeigenamen auf %2$s gesetzt" - "Du hast deinen Anzeigenamen auf %1$s gesetzt" + "%1$s hat seinen Anzeigenamen entfernt (es war %2$s)" + "Du hast deinen Anzeigenamen entfernt (es war %1$s)" + "%1$s hat seinen Anzeigenamen zu %2$s geändert" + "Du hast deinen Anzeigenamen auf %1$s geändert" "%1$s hat den Raum-Avatar geändert" "Du hast den Raum-Avatar geändert" - "%1$s hat den Raum-Avatar entfernt" + "%1$s hat das Raumbild entfernt" + "Du hast das Raumbild entfernt" + "%1$s hat %2$s gebannt" + "Du hast %1$s gebannt" "%1$s hat den Raum erstellt" "Du hast den Raum erstellt" "%1$s hat %2$s eingeladen" @@ -21,19 +24,34 @@ "%1$s hat dich eingeladen" "%1$s ist dem Raum beigetreten" "Du bist dem Raum beigetreten" + "%1$s hat um Beitritt gebeten" + "%1$s hat %2$s erlaubt, beizutreten" + "%1$s hat dir erlaubt beizutreten" + "Du hast um Beitritt gebeten" + "%1$s hat die Beitrittsanfrage von %2$s abgelehnt" + "Du hast die Beitrittsanfrage von %1$s abgelehnt" "%1$s hat deine Beitrittsanfrage abgelehnt" + "%1$s ist nicht mehr daran interessiert, beizutreten" + "Du hast deine Beitrittsanfrage zurückgezogen" "%1$s hat den Raum verlassen" "Du hast den Raum verlassen" "%1$s hat den Raumnamen geändert in: %2$s" - "Sie haben den Raumnamen geändert in: %1$s" + "Du hast den Raumnamen geändert in: %1$s" "%1$s hat den Raumnamen entfernt" "Du hast den Raumnamen entfernt" "%1$s hat die Einladung abgelehnt" "Du hast die Einladung abgelehnt" "%1$s hat %2$s entfernt" "Du hast %1$s entfernt" + "%1$s hat eine Einladung an %2$s gesendet, um dem Raum beizutreten" + "Du hast eine Einladung an %1$s gesendet, um dem Raum beizutreten" + "%1$s hat die Einladung für %2$s widerrufen, dem Raum beizutreten" + "Du hast die Einladung für %1$s widerrufen, dem Raum beizutreten" "%1$s hat das Thema geändert zu: %2$s" - "Sie haben das Thema geändert zu: %1$s" + "Du hast das Thema geändert zu: %1$s" "%1$s hat das Raumthema entfernt" "Du hast das Raumthema entfernt" + "%1$s hat %2$s entbannt" + "Du hast %1$s entbannt" + "%1$s hat eine unbekannte Änderung an seiner Mitgliedschaft vorgenommen" \ No newline at end of file diff --git a/libraries/eventformatter/impl/src/main/res/values-fr/translations.xml b/libraries/eventformatter/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..bdf693e976 --- /dev/null +++ b/libraries/eventformatter/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,57 @@ + + + "(l\'avatar a aussi été modifié)" + "%1$s a changé son avatar" + "Vous avez changé d\'avatar" + "%1$s a changé son nom d\'affichage de %2$s à %3$s" + "Vous avez changé votre nom d\'affichage de %1$s à %2$s" + "%1$s a supprimé son nom d\'affichage (il s\'agissait de %2$s)" + "Vous avez supprimé votre nom d\'affichage (il s\'agissait de %1$s)" + "%1$s a défini son nom d\'affichage en tant que %2$s" + "Vous avez défini votre nom d\'affichage en tant que %1$s" + "%1$s a changé l\'avatar de la salle" + "Vous avez changé l\'avatar de la salle" + "%1$s a supprimé l\'avatar de la salle" + "Vous avez supprimé l\'avatar de la salle" + "%1$s a banni %2$s" + "Vous avez banni %1$s" + "%1$s a créé la salle" + "Vous avez créé la salle" + "%1$s a invité %2$s" + "%1$s a accepté l\'invitation" + "Vous avez accepté l\'invitation" + "Vous avez invité %1$s" + "%1$s vous a invité." + "%1$s a rejoint la salle" + "Vous avez rejoint la salle" + "%1$s a demandé à rejoindre" + "%1$s a autorisé %2$s à rejoindre" + "%1$s vous a autorisé à rejoindre" + "Vous avez demandé à rejoindre" + "%1$s a rejeté la demande d\'adhésion de %2$s" + "Vous avez rejeté la demande d\'adhésion de %1$s" + "%1$s a rejeté votre demande d\'adhésion" + "%1$s n’est plus intéressé à rejoindre" + "Vous avez annulé votre demande d\'adhésion" + "%1$s a quitté la salle" + "Vous avez quitté la salle" + "%1$s a changé le nom de la salle en : %2$s" + "Vous avez changé le nom de la salle en : %1$s" + "%1$s a supprimé le nom de la salle" + "Vous avez supprimé le nom de la salle" + "%1$s a rejeté l\'invitation" + "Vous avez refusé l\'invitation" + "%1$s a supprimé %2$s" + "Vous avez supprimé %1$s" + "%1$s a envoyé une invitation à %2$s à rejoindre la salle" + "Vous avez envoyé une invitation à %1$s pour rejoindre la salle" + "%1$s a révoqué l\'invitation de %2$s à rejoindre la salle" + "Vous avez révoqué l\'invitation de %1$s à rejoindre la salle" + "%1$s a changé le sujet en : %2$s" + "Vous avez changé le sujet en : %1$s" + "%1$s a supprimé le sujet de la salle" + "Vous avez supprimé le sujet de la salle" + "%1$s a débanni %2$s" + "Vous avez débanni %1$s" + "%1$s a apporté une modification inconnue à son adhésion" + \ No newline at end of file diff --git a/libraries/push/impl/src/main/res/values-cs/translations.xml b/libraries/push/impl/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..90806b854c --- /dev/null +++ b/libraries/push/impl/src/main/res/values-cs/translations.xml @@ -0,0 +1,28 @@ + + + "Hovor" + "Naslouchání událostem" + "Hlasitá oznámení" + "Tichá oznámení" + "Vstoupit" + "Odmítnout" + "Nové zprávy" + "Označit jako přečtené" + "Já" + "Prohlížíte si oznámení! Klikněte na mě!" + "%1$s: %2$s" + "%1$s: %2$s %3$s" + "%1$s a %2$s" + "%1$s in %2$s" + "%1$s v %2$s a %3$s" + + "%1$s: %2$d zpráva" + "%1$s: %2$d zprávy" + "%1$s: %2$d zpráv" + + "Vyberte, jak chcete přijímat oznámení" + "Synchronizace na pozadí" + "Služby Google" + "Nebyly nalezeny žádné funkční služby Google Play. Oznámení nemusí fungovat správně." + "Rychlá odpověď" + \ No newline at end of file diff --git a/libraries/push/impl/src/main/res/values-de/translations.xml b/libraries/push/impl/src/main/res/values-de/translations.xml index d1ce970c3c..be957bf68a 100644 --- a/libraries/push/impl/src/main/res/values-de/translations.xml +++ b/libraries/push/impl/src/main/res/values-de/translations.xml @@ -1,10 +1,16 @@ + "Anruf" + "Warte auf Ereignisse" "Laute Benachrichtigungen" + "Stumme Benachrichtigungen" + "** Senden fehlgeschlagen - bitte Raum öffnen" "Beitreten" "Ablehnen" "Neue Nachrichten" "Als gelesen markieren" + "Ich" + "Du siehst die Benachrichtigung an! Klick mich an!" "%1$s: %2$s" "%1$s: %2$s %3$s" "%1$s und %2$s" @@ -26,10 +32,16 @@ "%d neue Nachricht" "%d neue Nachrichten" + + "%d ungelesene benachrichtigte Nachricht" + "%d ungelesene benachrichtigte Nachrichten" + "%d Raum" "%d Räume" + "Auswählen, wie Benachrichtigungen empfangen werden sollen" + "Hintergrundsynchronisation" "Google-Dienste" "Keine gültigen Google Play-Dienste gefunden. Benachrichtigungen funktionieren möglicherweise nicht richtig." "Schnellantwort" diff --git a/libraries/push/impl/src/main/res/values-fr/translations.xml b/libraries/push/impl/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..00e15fc57f --- /dev/null +++ b/libraries/push/impl/src/main/res/values-fr/translations.xml @@ -0,0 +1,72 @@ + + + "Appel" + "À l\'écoute d\'événements" + "Notifications bruyantes" + "Notifications silencieuses" + "** Échec de l\'envoi - veuillez ouvrir la salle" + "Rejoindre" + "Refuser" + "Nouveaux messages" + "Marquer comme lu" + "Moi" + "Vous êtes en train de consulter la notification ! Cliquez-moi !" + "%1$s: %2$s" + "%1$s: %2$s %3$s" + "%1$s et %2$s" + "%1$s dans %2$s" + "%1$s dans %2$s et %3$s" + + "%1$s: %2$d message" + "%1$s: %2$d message" + "%1$s: %2$d messages" + "%1$s: %2$d messages" + "%1$s: %2$d messages" + "%1$s: %2$d messages" + + + "%d notification" + "%d notification" + "%d notifications" + "%d notification" + "%d notification" + "%d notification" + + + "%d invitation" + "%d invitation" + "%d invitations" + "%d invitations" + "%d invitations" + "%d invitations" + + + "Aucun nouveau message" + "%d nouveaux message" + "%d nouveaux message" + "%d nouveaux message" + "%d nouveaux message" + "%d nouveaux message" + + + "Aucun message notifié non lu" + "%d message notifié non lu" + "%d messages notifiés non lus" + "%d messages notifiés non lus" + "%d messages notifiés non lus" + "%d messages notifiés non lus" + + + "%d salle" + "%d salle" + "%d salles" + "%d salles" + "%d salles" + "%d salles" + + "Choisissez comment recevoir les notifications" + "Synchronisation en arrière-plan" + "Services Google" + "Aucun service Google Play valide n\'a été trouvé. Les notifications peuvent ne pas fonctionner correctement." + "Réponse rapide" + \ No newline at end of file diff --git a/libraries/textcomposer/src/main/res/values-cs/translations.xml b/libraries/textcomposer/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..85f2a46312 --- /dev/null +++ b/libraries/textcomposer/src/main/res/values-cs/translations.xml @@ -0,0 +1,4 @@ + + + "Zpráva…" + \ No newline at end of file diff --git a/libraries/textcomposer/src/main/res/values-de/translations.xml b/libraries/textcomposer/src/main/res/values-de/translations.xml index f016d4bdba..a9f1b3366e 100644 --- a/libraries/textcomposer/src/main/res/values-de/translations.xml +++ b/libraries/textcomposer/src/main/res/values-de/translations.xml @@ -1,5 +1,17 @@ + "Aufzählungsliste ein-/ausschalten" + "Codeblock umschalten" "Nachricht…" + "Fettformatierung anwenden" + "Kursivformat anwenden" + "Durchgestrichenes Format anwenden" + "Unterstreichungsformat anwenden" + "Vollbildmodus umschalten" + "Einrücken" + "Inline-Codeformat anwenden" "Link setzen" + "Nummerierte Liste ein-/ausschalten" + "Zitat umschalten" + "Einrücken aufheben" \ No newline at end of file diff --git a/libraries/textcomposer/src/main/res/values-fr/translations.xml b/libraries/textcomposer/src/main/res/values-fr/translations.xml new file mode 100644 index 0000000000..03143f5059 --- /dev/null +++ b/libraries/textcomposer/src/main/res/values-fr/translations.xml @@ -0,0 +1,17 @@ + + + "Afficher une liste à puces" + "Afficher le bloc de code" + "Envoyer un message…" + "Appliquer le format gras" + "Appliquer le format italique" + "Appliquer le format barré" + "Appliquer le format souligné" + "Afficher en mode plein écran" + "Décaler vers la droite" + "Appliquer le formatage de code en ligne" + "Définir un lien" + "Afficher une liste numérotée" + "Afficher une citation" + "Décaler vers la gauche" + \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml new file mode 100644 index 0000000000..f8fd5fd4df --- /dev/null +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -0,0 +1,143 @@ + + + "Skrýt heslo" + "Odeslat soubory" + "Zobrazit heslo" + "Uživatelské menu" + "Přijmout" + "Zpět" + "Zrušit" + "Vybrat fotku" + "Vymazat" + "Zavřít" + "Dokončit ověření" + "Potvrdit" + "Pokračovat" + "Kopírovat" + "Kopírovat odkaz" + "Vytvořit" + "Vytvořit místnost" + "Odmítnout" + "Zakázat" + "Hotovo" + "Upravit" + "Povolit" + "Pozvat" + "Pozvat přátele do %1$s" + "Pozvánky" + "Zjistit více" + "Odejít" + "Opustit místnost" + "Další" + "Ne" + "Teď ne" + "OK" + "Rychlá odpověď" + "Citovat" + "Odstranit" + "Odpovědět" + "Nahlásit chybu" + "Nahlásit obsah" + "Zkusit znovu" + "Opakovat dešifrování" + "Uložit" + "Hledat" + "Odeslat" + "Odeslat zprávu" + "Sdílet" + "Sdílet odkaz" + "Přeskočit" + "Začít" + "Zahájit chat" + "Zahájit ověření" + "Vyfotit" + "Zobrazit zdroj" + "Ano" + "O aplikaci" + "Analytika" + "Zvuk" + "Bubliny" + "Vytváření místnosti…" + "Opustit místnost" + "Chyba dešifrování" + "Možnosti pro vývojáře" + "(upraveno)" + "Úpravy" + "* %1$@ %2$@" + "Šifrování povoleno" + "Chyba" + "Soubor" + "GIF" + "Obrázek" + "Odkaz zkopírován do schránky" + "Načítání…" + "Zpráva" + "Rozložení zprávy" + "Zpráva byla odstraněna" + "Moderní" + "Žádné výsledky" + "Offline" + "Heslo" + "Lidé" + "Trvalý odkaz" + "Reakce" + "Odpověď na %1$s" + "Nahlásit chybu" + "Zpráva odeslána" + "Název místnosti" + "Hledat někoho" + "Výsledky hledání" + "Zabezpečení" + "Vyberte svůj server" + "Odesílání…" + "Server není podporován" + "URL serveru" + "Nastavení" + "Nálepka" + "Úspěch" + "Návrhy" + "Téma" + "Nelze dešifrovat" + "Nepodporovaná událost" + "Uživatelské jméno" + "Ověření zrušeno" + "Ověření dokončeno" + "Video" + "Čekání…" + "Potvrzení" + "Upozornění" + "Aktivity" + "Vlajky" + "Jídlo a nápoje" + "Zvířata a příroda" + "Předměty" + "Smajlíci a lidé" + "Cestování a místa" + "Symboly" + "Vytvoření trvalého odkazu se nezdařilo" + "Načítání zpráv se nezdařilo" + "Některé zprávy nebyly odeslány" + "Omlouváme se, došlo k chybě" + "Ahoj, ozvi se mi na %1$s: %2$s" + "Opravdu chcete opustit tuto místnost? Jste tu jediná osoba. Pokud odejdete, nikdo se v budoucnu nebude moci připojit, včetně vás." + "Opravdu chcete opustit tuto místnost? Tato místnost není veřejná a bez pozvánky se nebudete moci znovu připojit." + "Opravdu chcete opustit místnost?" + "%1$s Android" + + "%1$d člen" + "%1$d členové" + "%1$d členů" + + "Tato zpráva bude nahlášena správci vašeho domovského serveru. Nebude si moci přečíst žádné šifrované zprávy." + "Důvod nahlášení tohoto obsahu" + "Toto je začátek %1$s." + "Toto je začátek této konverzace." + "Nové" + "Rageshake" + "Obecné" + "Verze: %1$s (%2$s)" + "en" + "Chyba" + "Úspěch" + "Zablokovat uživatele" + \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-de/translations.xml b/libraries/ui-strings/src/main/res/values-de/translations.xml index 2fb21016e5..2a4558f756 100644 --- a/libraries/ui-strings/src/main/res/values-de/translations.xml +++ b/libraries/ui-strings/src/main/res/values-de/translations.xml @@ -4,15 +4,19 @@ "Dateien senden" "Passwort anzeigen" "Benutzermenü" + "Zustimmen" "Zurück" "Abbrechen" "Foto auswählen" + "Löschen" "Schließen" "Verifizierung abschließen" "Bestätigen" + "Weiter" "Kopieren" "Link kopieren" "Erstellen" + "Raum erstellen" "Ablehnen" "Deaktivieren" "Fertig" @@ -31,6 +35,7 @@ "Schnellantwort" "Zitieren" "Entfernen" + "Antworten" "Fehler melden" "Inhalt melden" "Erneut versuchen" @@ -42,31 +47,50 @@ "Teilen" "Link teilen" "Überspringen" + "Starten" "Chat starten" + "Verifizierung starten" "Foto aufnehmen" + "Quelltext anzeigen" "Ja" "Über" "Analyse" "Audio" "Blasen" + "Erstelle Raum…" + "Raum verlassen" "Entschlüsselungsfehler" "Entwickleroptionen" "(bearbeitet)" + "Bearbeiten" "Verschlüsselung aktiviert" "Fehler" "Datei" "GIF" "Bild" "Link in Zwischenablage kopiert" + "Wird geladen…" "Nachricht" + "Nachrichtenlayout" + "Nachricht wurde entfernt" "Modern" + "Keine Ergebnisse" "Offline" "Passwort" + "Personen" + "Permalink" "Reaktionen" - "Fehler melden" + "Auf %1$s antworten" + "Melde einen Fehler" + "Bericht gesendet" + "Raumname" + "Suche nach jemandem" "Suchergebnisse" "Sicherheit" + "Wählen deinen Server" + "Senden…" "Server wird nicht unterstützt" + "Server-URL" "Einstellungen" "Chat wird gestartet…" "Sticker" @@ -80,6 +104,7 @@ "Verifizierung abgeschlossen" "Video" "Warten…" + "Bestätigung" "Warnung" "Aktivitäten" "Flaggen" @@ -89,26 +114,45 @@ "Smileys & Personen" "Reisen & Orte" "Symbole" + "Fehler beim Erstellen des Permalinks" "Fehler beim Laden der Nachrichten" "Einige Nachrichten wurden nicht gesendet" "Entschuldigung, ein Fehler ist aufgetreten." + "Hey, sprich mit mir auf %1$s: %2$s" + "Bist du sicher, dass du diesen Raum verlassen willst? Du bist die einzige Person hier. Wenn du gehst, kann in Zukunft niemand mehr beitreten, auch du nicht." + "Bist du dir sicher, dass du den Raum verlassen möchtest? Dieser Raum ist nicht öffentlich und du kannst ihm ohne eine Einladung nicht mehr beitreten." + "Bist du dir sicher, dass du den Raum verlassen möchtest?" "%1$s Android" "%1$d Mitglied" "%1$d Mitglieder" + "Rageshake zum Melden von Fehlern" + "Du scheinst frustriert das Telefon zu schütteln. Möchtest du den Fehlerberichtsbildschirm öffnen?" + "Diese Nachricht wird an deinen Heimserver-Admin gemeldet werden. Er wird nicht in der Lage sein, verschlüsselte Nachrichten zu lesen." "Grund für die Meldung dieses Inhalts" "Dies ist der Anfang von %1$s." + "Dies ist der Beginn dieser Konversation." "Neu" "Wir erfassen und analysieren ""keine"" Account-Daten" + "Helfen Sie uns, Probleme zu identifizieren und %1$s zu verbessern, indem Sie anonyme Nutzungsdaten weitergeben." + "Sie können alle unsere Nutzerbedingungen %1$s lesen." + "hier" "Sie können die Analyse jederzeit in den Einstellungen deaktivieren" "Wir geben ""keine"" Informationen an Dritte weiter" + "Helfen Sie %1$s zu verbessern" "Teile Analyse-Daten" "Medienauswahl fehlgeschlagen, bitte versuche es erneut." + "Prüfe, ob du alle aktuellen und zukünftigen Nachrichten dieses Benutzers ausblenden möchtest" + "Rageshake" "Erkennungsschwelle" + "Allgemein" "Version: %1$s (%2$s)" "de" "Fehler" "Erfolg" + "Helfen Sie uns, Probleme zu identifizieren und %1$s zu verbessern, indem Sie anonyme Nutzungsdaten weitergeben." + "Sie können alle unsere Nutzerbedingungen %1$s lesen." + "hier" "Nutzer blockieren" \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index de1a80a9f5..ffddafb618 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -4,13 +4,144 @@ "Envoyer des fichiers" "Afficher le mot de passe" "Menu utilisateur" + "Accepter" "Retour" "Annuler" + "Choisir une photo" "Effacer" "Fermer" + "Compléter la vérification" "Confirmer" "Continuer" "Copier" "Copier le lien" + "Créer" + "Créer une salle" + "Refuser" + "Désactiver" + "Terminé" + "Éditer" + "Activer" + "Inviter" + "Inviter des amis à %1$s" + "Invitations" + "En savoir plus" + "Quitter" + "Quitter la salle" + "Suivant" + "Non" + "Pas maintenant" + "OK" + "Réponse rapide" + "Citer" + "Supprimer" + "Répondre" + "Signaler un bug" + "Signaler le contenu" + "Réessayer" + "Réessayer le déchiffrement" + "Enregistrer" + "Chercher" + "Envoyer" + "Envoyer un message" + "Partager" + "Partager le lien" + "Passer" + "Démarrer" + "Commencer un chat" + "Commencer la vérification" + "Prendre une photo" + "Voir la source" + "Oui" + "À propos" + "Audio" + "Bulles" + "Création de la salle…" + "La salle a été quittée" + "Erreur de déchiffrement" + "Options de développement" + "(édité)" + "Modification en cours" + "Chiffrement activé" + "Erreur" + "Fichier" + "GIF" + "Image" + "Lien copié dans le presse-papiers" + "Chargement…" + "Message" + "Mise en page du message" + "Message supprimé" + "Moderne" + "Aucun résultat" + "Hors ligne" + "Mot de passe" + "Personnes" + "Permalien" + "Réactions" + "En réponse à %1$s" + "Signaler un problème" + "Rapport envoyé" + "Nom de la salle" + "Rechercher quelqu\'un" + "Sécurité" + "Sélectionnez votre serveur" + "Envoi en cours…" + "Serveur non pris en charge" + "URL du serveur" + "Paramètres" + "Autocollant" + "Succès" + "Suggestions" + "Sujet" + "Incapable de décrypter" + "Événement non pris en charge" + "Nom d\'utilisateur" + "Vérification annulée" + "Vérification terminée" + "Vidéo" + "Patientez…" + "Confirmation" + "Attention" + "Activités" + "Drapeaux" + "Nourriture et boissons" + "Animaux et nature" + "Objets" + "Émoticônes et personnes" + "Voyages & lieux" + "Symboles" + "Échec de la création du permalien" + "Échec du chargement des messages" + "Certains messages n\'ont pas été envoyés" + "Désolé, une erreur est survenue." + "Salut, parle-moi sur %1$s : %2$s" + "Êtes-vous sûr de vouloir quitter cette salle ? Vous êtes la seule personne ici. Si vous partez, personne ne pourra rejoindre la salle à l\'avenir, y compris vous." + "Êtes-vous sûr de vouloir quitter cette salle ? Cette salle n\'est pas publique et vous ne pourrez pas la rejoindre sans invitation." + "Êtes-vous sûr de vouloir quitter la salle ?" + "%1$s Android" + + "Aucun membre" + "%1$d membre" + "%1$d membres" + "%1$d membres" + "%1$d membres" + "%1$d membres" + + "Rageshake pour signaler un bug" + "Vous semblez secouer le téléphone de frustration. Voulez-vous ouvrir le formulaire de rapport de problème ?" + "Ce message sera signalé à l’administrateur de votre serveur d\'accueil. Ils ne pourront lire aucun message crypté." + "Raison du signalement de ce contenu" + "Ceci est le début de %1$s." + "Ceci est le début de cette conversation." + "Nouveau" + "Cochez si vous souhaitez masquer tous les messages actuels et futurs de cet utilisateur." + "Rageshake" + "Seuil de détection" + "Général" + "Version: %1$s ( %2$s )" "fr" + "Erreur" + "Succès" + "Bloquer l\'utilisateur" \ No newline at end of file diff --git a/libraries/ui-strings/src/main/res/values-ro/translations.xml b/libraries/ui-strings/src/main/res/values-ro/translations.xml index efcd69b3fc..d3e67a98ef 100644 --- a/libraries/ui-strings/src/main/res/values-ro/translations.xml +++ b/libraries/ui-strings/src/main/res/values-ro/translations.xml @@ -23,6 +23,7 @@ "Editați" "Activați" "Invitați" + "Invitați prieteni" "Invitați prieteni în %1$s" "Invitații" "Aflați mai multe" @@ -63,11 +64,13 @@ "Opțiuni programator" "(editat)" "Editare" + "* %1$s %2$s" "Criptare activată" "Eroare" "Fişier" "GIF" "Imagine" + "Se părăsește conversația" "Linkul a fost copiat în clipboard" "Se încarcă…" "Mesaj" @@ -83,18 +86,23 @@ "Răspuns pentru %1$s" "Raportați o eroare" "Raport trimis" + "Numele camerei" "Căutați pe cineva" + "Rezultatele căutării" "Securitate" "Selectați serverul" "Se trimite…" "Serverul nu este compatibil" "Adresa URL a serverului" "Setări" + "Se începe conversația…" "Autocolant" "Succes" "Sugestii" "Subiect" "Nu s-a putut decripta" + "Nu am putut trimite cu succes invitații unuia sau mai multor utilizatori." + "Nu s-a putut trimite invitația (invitațiile)" "Eveniment neacceptat" "Utilizator" "Verificare anulată" @@ -115,6 +123,7 @@ "Încărcarea mesajelor a eșuat" "Unele mesaje nu au fost trimise" "Ne pare rău, a apărut o eroare" + "🔐️ Alăturați-vă mie pe %1$s" "Hei, vorbește cu mine pe %1$s: %2$s" "Sunteți sigur că vreți să părăsiți această cameră? Sunteți singura persoană de aici. Dacă o părasiți, nimeni nu se va mai putea alătura în viitor, inclusiv dumneavoastra." "Sunteți sigur că vrei să părăsiți această cameră? Această cameră nu este publică și nu va veti putea alătura din nou fără o invitație." @@ -139,6 +148,10 @@ "Puteți dezactiva această opțiune oricând din setări" "Nu"" împărtășim informații cu terți" "Ajutați la îmbunătățirea %1$s" + "Partajați datele analitice" + "Selectarea fișierelor media a eșuat, încercați din nou." + "Procesarea datelor media a eșuat, vă rugăm să încercați din nou." + "Încărcarea fișierelor media a eșuat, încercați din nou." "Confirmați că doriți să ascundeți toate mesajele curente și viitoare de la acest utilizator" "Rageshake" "Prag de detecție" diff --git a/libraries/ui-strings/src/main/res/values/localazy.xml b/libraries/ui-strings/src/main/res/values/localazy.xml index 163d65f19f..954d078d01 100644 --- a/libraries/ui-strings/src/main/res/values/localazy.xml +++ b/libraries/ui-strings/src/main/res/values/localazy.xml @@ -22,6 +22,7 @@ "Done" "Edit" "Enable" + "Forgot password?" "Invite" "Invite friends" "Invite friends to %1$s" @@ -70,6 +71,7 @@ "File" "GIF" "Image" + "We can’t validate this user’s Matrix ID. The invite might not be received." "Leaving room" "Link copied to clipboard" "Loading…" @@ -86,6 +88,7 @@ "Replying to %1$s" "Report a bug" "Report submitted" + "Room name" "Search for someone" "Search results" "Security" @@ -151,6 +154,12 @@ "Failed processing media to upload, please try again." "Failed uploading media, please try again." "Check if you want to hide all current and future messages from this user" + "Change account provider" + "A private server for Element employees." + "Matrix is an open network for secure, decentralised communication." + "This is where your conversations will live — just like you would use an email provider to keep your emails." + "You’re about to sign in to %1$s" + "You’re about to create an account on %1$s" "Rageshake" "Detection threshold" "General" @@ -163,4 +172,4 @@ "You can read all our terms %1$s." "here" "Block user" - + \ No newline at end of file From 6000a7ca5b70c929319c5df97fdd0a028db199ed Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 26 May 2023 12:45:43 +0200 Subject: [PATCH 04/75] Let TimelineItemsFactory group the item if necessary, so it's done on a computation dispatcher. Let the View manage the expanded/grouped state. --- .../features/messages/impl/MessagesView.kt | 8 ---- .../messages/impl/timeline/TimelineEvents.kt | 2 - .../impl/timeline/TimelinePresenter.kt | 12 +----- .../messages/impl/timeline/TimelineView.kt | 16 ++++---- .../factories/TimelineItemsFactory.kt | 12 ++++-- .../timeline/groups/TimelineItemGrouper.kt | 12 ++---- .../impl/timeline/model/TimelineItem.kt | 5 +-- .../messages/MessagesPresenterTest.kt | 2 - .../messages/fixtures/timelineItemsFactory.kt | 4 +- .../timeline/TimelinePresenterTest.kt | 41 ------------------- .../groups/TimelineItemGrouperTest.kt | 32 +-------------- 11 files changed, 29 insertions(+), 117 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index e3c6160b20..8d08b77a49 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -153,11 +153,6 @@ fun MessagesView( } } - fun onExpandGroupClick(event: TimelineItem.GroupedEvents) { - Timber.v("onExpandGroupClick= ${event.id}") - state.timelineState.eventSink(TimelineEvents.ToggleExpandGroup(event)) - } - fun onActionSelected(action: TimelineItemAction, event: TimelineItem.Event) { state.eventSink(MessagesEvents.HandleAction(action, event)) } @@ -208,7 +203,6 @@ fun MessagesView( .consumeWindowInsets(padding), onMessageClicked = ::onMessageClicked, onMessageLongClicked = ::onMessageLongClicked, - onExpandGroupClick = ::onExpandGroupClick, ) }, snackbarHost = { @@ -247,7 +241,6 @@ fun MessagesViewContent( modifier: Modifier = Modifier, onMessageClicked: (TimelineItem.Event) -> Unit = {}, onMessageLongClicked: (TimelineItem.Event) -> Unit = {}, - onExpandGroupClick: (TimelineItem.GroupedEvents) -> Unit = {}, ) { Column( modifier = modifier @@ -262,7 +255,6 @@ fun MessagesViewContent( modifier = Modifier.weight(1f), onMessageClicked = onMessageClicked, onMessageLongClicked = onMessageLongClicked, - onExpandGroupClick = onExpandGroupClick, ) } MessageComposerView( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt index 11f1a1a483..ff64441198 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineEvents.kt @@ -16,11 +16,9 @@ package io.element.android.features.messages.impl.timeline -import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.matrix.api.core.EventId sealed interface TimelineEvents { object LoadMore : TimelineEvents data class SetHighlightedEvent(val eventId: EventId?) : TimelineEvents - data class ToggleExpandGroup(val event: TimelineItem.GroupedEvents) : TimelineEvents } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt index 1693a623f3..26c0bedf01 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt @@ -20,19 +20,14 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.features.messages.impl.timeline.factories.TimelineItemsFactory -import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper import io.element.android.libraries.architecture.Presenter -import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimeline -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -45,7 +40,6 @@ private const val backPaginationPageSize = 50 class TimelinePresenter @Inject constructor( private val timelineItemsFactory: TimelineItemsFactory, - private val timelineItemGrouper: TimelineItemGrouper, room: MatrixRoom, ) : Presenter { @@ -57,7 +51,6 @@ class TimelinePresenter @Inject constructor( val highlightedEventId: MutableState = rememberSaveable { mutableStateOf(null) } - val expandedGroups = remember { mutableStateMapOf() } val timelineItems = timelineItemsFactory .flow() @@ -71,9 +64,6 @@ class TimelinePresenter @Inject constructor( when (event) { TimelineEvents.LoadMore -> localCoroutineScope.loadMore(paginationState.value) is TimelineEvents.SetHighlightedEvent -> highlightedEventId.value = event.eventId - is TimelineEvents.ToggleExpandGroup -> { - expandedGroups[event.event.identifier()] = expandedGroups[event.event.identifier()].orFalse().not() - } } } @@ -92,7 +82,7 @@ class TimelinePresenter @Inject constructor( return TimelineState( highlightedEventId = highlightedEventId.value, paginationState = paginationState.value, - timelineItems = timelineItemGrouper.group(timelineItems.value, expandedGroups).toImmutableList(), + timelineItems = timelineItems.value, eventSink = ::handleEvents ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index 0f31652ec8..d2707d2142 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -47,8 +47,10 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -95,7 +97,6 @@ fun TimelineView( modifier: Modifier = Modifier, onMessageClicked: (TimelineItem.Event) -> Unit = {}, onMessageLongClicked: (TimelineItem.Event) -> Unit = {}, - onExpandGroupClick: (TimelineItem.GroupedEvents) -> Unit = {}, ) { fun onReachedLoadMore() { @@ -119,7 +120,6 @@ fun TimelineView( highlightedItem = state.highlightedEventId?.value, onClick = onMessageClicked, onLongClick = onMessageLongClicked, - onExpandGroupClick = onExpandGroupClick, ) if (index == state.timelineItems.lastIndex) { onReachedLoadMore() @@ -141,7 +141,6 @@ fun TimelineItemRow( highlightedItem: String?, onClick: (TimelineItem.Event) -> Unit, onLongClick: (TimelineItem.Event) -> Unit, - onExpandGroupClick: (TimelineItem.GroupedEvents) -> Unit, modifier: Modifier = Modifier ) { when (timelineItem) { @@ -179,8 +178,10 @@ fun TimelineItemRow( } } is TimelineItem.GroupedEvents -> { + val isExpanded = rememberSaveable(key = timelineItem.identifier()) { mutableStateOf(false) } + fun onExpandGroupClick() { - onExpandGroupClick(timelineItem) + isExpanded.value = !isExpanded.value } Column(modifier = modifier.animateContentSize()) { @@ -190,11 +191,11 @@ fun TimelineItemRow( count = timelineItem.events.size, timelineItem.events.size ), - isExpanded = timelineItem.expanded, - isHighlighted = !timelineItem.expanded && timelineItem.events.any { it.identifier() == highlightedItem }, + isExpanded = isExpanded.value, + isHighlighted = !isExpanded.value && timelineItem.events.any { it.identifier() == highlightedItem }, onClick = ::onExpandGroupClick, ) - if (timelineItem.expanded) { + if (isExpanded.value) { Column { timelineItem.events.forEach { subGroupEvent -> TimelineItemRow( @@ -202,7 +203,6 @@ fun TimelineItemRow( highlightedItem = highlightedItem, onClick = onClick, onLongClick = onLongClick, - onExpandGroupClick = {} ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt index 44b49cbae0..f4e771a32d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/TimelineItemsFactory.kt @@ -21,9 +21,13 @@ import io.element.android.features.messages.impl.timeline.diff.CacheInvalidator import io.element.android.features.messages.impl.timeline.diff.MatrixTimelineItemsDiffCallback import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemEventFactory import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory +import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList +import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -38,9 +42,10 @@ class TimelineItemsFactory @Inject constructor( private val dispatchers: CoroutineDispatchers, private val eventItemFactory: TimelineItemEventFactory, private val virtualItemFactory: TimelineItemVirtualFactory, + private val timelineItemGrouper: TimelineItemGrouper, ) { - private val timelineItems = MutableStateFlow>(emptyList()) + private val timelineItems = MutableStateFlow(emptyList().toImmutableList()) private val timelineItemsCache = arrayListOf() // Items from rust sdk, used for diffing @@ -49,7 +54,7 @@ class TimelineItemsFactory @Inject constructor( private val lock = Mutex() private val cacheInvalidator = CacheInvalidator(timelineItemsCache) - fun flow(): StateFlow> = timelineItems.asStateFlow() + fun flow(): StateFlow> = timelineItems.asStateFlow() suspend fun replaceWith( timelineItems: List, @@ -72,7 +77,8 @@ class TimelineItemsFactory @Inject constructor( newTimelineItemStates.add(cacheItem) } } - this.timelineItems.emit(newTimelineItemStates) + val result = timelineItemGrouper.group(newTimelineItemStates).toPersistentList() + this.timelineItems.emit(result) } private fun calculateAndApplyDiff(newTimelineItems: List) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt index 24f74f4b5c..d996406102 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt @@ -29,16 +29,14 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent -import io.element.android.libraries.core.bool.orFalse import kotlinx.collections.immutable.toImmutableList - import javax.inject.Inject class TimelineItemGrouper @Inject constructor() { /** * Create a new list of [TimelineItem] by grouping some of them into [TimelineItem.GroupedEvents]. */ - fun group(from: List, expandedGroups: Map): List { + fun group(from: List): List { val result = mutableListOf() val currentGroup = mutableListOf() from.forEach { timelineItem -> @@ -48,14 +46,14 @@ class TimelineItemGrouper @Inject constructor() { // timelineItem cannot be grouped if (currentGroup.isNotEmpty()) { // There is a pending group, create a TimelineItem.GroupedEvents if there is more than 1 Event in the pending group. - result.addGroup(currentGroup, expandedGroups) + result.addGroup(currentGroup) currentGroup.clear() } result.add(timelineItem) } } if (currentGroup.isNotEmpty()) { - result.addGroup(currentGroup, expandedGroups) + result.addGroup(currentGroup) } return result } @@ -82,8 +80,7 @@ class TimelineItemGrouper @Inject constructor() { * Will add a group if there is more than 1 item, else add the item to the list. */ private fun MutableList.addGroup( - group: MutableList, - expandedGroups: Map, + group: MutableList ) { if (group.size == 1) { // Do not create a group with just 1 item, just add the item to the result @@ -91,7 +88,6 @@ private fun MutableList.addGroup( } else { add( TimelineItem.GroupedEvents( - expanded = expandedGroups[group.first().id + "_group"].orFalse(), events = group.toImmutableList() ) ) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt index 5daa75a8c4..c004901232 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItem.kt @@ -68,10 +68,9 @@ sealed interface TimelineItem { @Immutable data class GroupedEvents( - val expanded: Boolean, val events: ImmutableList, ) : TimelineItem { - // use first id with a suffix - val id = events.first().id + "_group" + // use last id with a suffix. Last will not change in cas of new event from backpagination. + val id = events.last().id + "_group" } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 1d8ee54505..3f6e4e2c79 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -28,7 +28,6 @@ import io.element.android.features.messages.impl.actionlist.ActionListPresenter import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter import io.element.android.features.messages.impl.timeline.TimelinePresenter -import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper import io.element.android.features.messages.media.FakeLocalMediaFactory import io.element.android.features.networkmonitor.test.FakeNetworkMonitor import io.element.android.libraries.designsystem.utils.SnackbarDispatcher @@ -142,7 +141,6 @@ class MessagesPresenterTest { ) val timelinePresenter = TimelinePresenter( timelineItemsFactory = aTimelineItemsFactory(), - timelineItemGrouper = TimelineItemGrouper(), room = matrixRoom, ) val actionListPresenter = ActionListPresenter() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt index 5d355be8d0..a695ae6401 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/timelineItemsFactory.kt @@ -30,6 +30,7 @@ import io.element.android.features.messages.impl.timeline.factories.event.Timeli import io.element.android.features.messages.impl.timeline.factories.event.TimelineItemEventFactory import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemDaySeparatorFactory import io.element.android.features.messages.impl.timeline.factories.virtual.TimelineItemVirtualFactory +import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper import io.element.android.libraries.dateformatter.test.FakeDaySeparatorFormatter import io.element.android.libraries.eventformatter.api.TimelineEventFormatter import io.element.android.libraries.matrix.api.timeline.item.event.EventTimelineItem @@ -56,7 +57,8 @@ internal fun aTimelineItemsFactory(): TimelineItemsFactory { daySeparatorFactory = TimelineItemDaySeparatorFactory( FakeDaySeparatorFormatter() ), - ) + ), + timelineItemGrouper = TimelineItemGrouper(), ) } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt index 7b13a93a5a..a88b2251a4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/TimelinePresenterTest.kt @@ -23,13 +23,8 @@ import com.google.common.truth.Truth.assertThat import io.element.android.features.messages.fixtures.aTimelineItemsFactory import io.element.android.features.messages.impl.timeline.TimelineEvents import io.element.android.features.messages.impl.timeline.TimelinePresenter -import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper -import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.room.FakeMatrixRoom -import io.element.android.libraries.matrix.test.room.anEventTimelineItem -import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline import kotlinx.coroutines.test.runTest import org.junit.Test @@ -38,7 +33,6 @@ class TimelinePresenterTest { fun `present - initial state`() = runTest { val presenter = TimelinePresenter( timelineItemsFactory = aTimelineItemsFactory(), - timelineItemGrouper = TimelineItemGrouper(), room = FakeMatrixRoom(), ) moleculeFlow(RecompositionClock.Immediate) { @@ -55,7 +49,6 @@ class TimelinePresenterTest { fun `present - load more`() = runTest { val presenter = TimelinePresenter( timelineItemsFactory = aTimelineItemsFactory(), - timelineItemGrouper = TimelineItemGrouper(), room = FakeMatrixRoom(), ) moleculeFlow(RecompositionClock.Immediate) { @@ -78,7 +71,6 @@ class TimelinePresenterTest { fun `present - set highlighted event`() = runTest { val presenter = TimelinePresenter( timelineItemsFactory = aTimelineItemsFactory(), - timelineItemGrouper = TimelineItemGrouper(), room = FakeMatrixRoom(), ) moleculeFlow(RecompositionClock.Immediate) { @@ -95,37 +87,4 @@ class TimelinePresenterTest { assertThat(withoutHighlightedState.highlightedEventId).isNull() } } - - @Test - fun `present - expand and collapse grouped events`() = runTest { - val fakeTimeline = FakeMatrixTimeline( - initialTimelineItems = listOf( - MatrixTimelineItem.Event(anEventTimelineItem() /* This is a groupable event */), - MatrixTimelineItem.Event(anEventTimelineItem() /* This is a groupable event */), - ) - ) - val fakeRoom = FakeMatrixRoom(matrixTimeline = fakeTimeline) - val presenter = TimelinePresenter( - timelineItemsFactory = aTimelineItemsFactory(), - timelineItemGrouper = TimelineItemGrouper(), - room = fakeRoom, - ) - moleculeFlow(RecompositionClock.Immediate) { - presenter.present() - }.test { - skipItems(1) - fakeTimeline.updateTimelineItems { it } - val loadedState = awaitItem() - val group1 = loadedState.timelineItems.first() as TimelineItem.GroupedEvents - assertThat(group1.expanded).isFalse() - loadedState.eventSink.invoke(TimelineEvents.ToggleExpandGroup(group1)) - val withExpandedGroup = awaitItem() - val group2 = withExpandedGroup.timelineItems.first() as TimelineItem.GroupedEvents - assertThat(group2.expanded).isTrue() - withExpandedGroup.eventSink.invoke(TimelineEvents.ToggleExpandGroup(group2)) - val withCollapsedGroup = awaitItem() - val group3 = withCollapsedGroup.timelineItems.first() as TimelineItem.GroupedEvents - assertThat(group3.expanded).isFalse() - } - } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt index 2644a12431..3edde16841 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt @@ -49,7 +49,7 @@ class TimelineItemGrouperTest { @Test fun `test empty`() { - val result = sut.group(emptyList(), emptyMap()) + val result = sut.group(emptyList()) assertThat(result).isEmpty() } @@ -60,7 +60,6 @@ class TimelineItemGrouperTest { aNonGroupableItem, aNonGroupableItem, ), - emptyMap() ) assertThat(result).isEqualTo( listOf( @@ -77,12 +76,10 @@ class TimelineItemGrouperTest { aGroupableItem.copy(id = AN_EVENT_ID_2.value), aGroupableItem, ), - emptyMap() ) assertThat(result).isEqualTo( listOf( TimelineItem.GroupedEvents( - expanded = false, events = listOf( aGroupableItem, aGroupableItem.copy(id = AN_EVENT_ID_2.value), @@ -92,28 +89,6 @@ class TimelineItemGrouperTest { ) } - @Test - fun `test groupables expanded`() { - val result = sut.group( - listOf( - aGroupableItem, - aGroupableItem.copy(id = AN_EVENT_ID_2.value), - ), - mapOf("${AN_EVENT_ID_2.value}_group" to true) - ) - assertThat(result).isEqualTo( - listOf( - TimelineItem.GroupedEvents( - expanded = true, - events = listOf( - aGroupableItem.copy(id = AN_EVENT_ID_2.value), - aGroupableItem, - ).toImmutableList() - ), - ) - ) - } - @Test fun `test 1 groupable, not group must be created`() { val listsToTest = listOf( @@ -130,7 +105,7 @@ class TimelineItemGrouperTest { listOf(aNonGroupableItemNoEvent), ) listsToTest.forEach { listToTest -> - val result = sut.group(listToTest, emptyMap()) + val result = sut.group(listToTest) assertThat(result).isEqualTo(listToTest) } } @@ -146,12 +121,10 @@ class TimelineItemGrouperTest { aGroupableItem, aGroupableItem, ), - emptyMap() ) assertThat(result).isEqualTo( listOf( TimelineItem.GroupedEvents( - expanded = false, events = listOf( aGroupableItem, aGroupableItem, @@ -159,7 +132,6 @@ class TimelineItemGrouperTest { ), aNonGroupableItem, TimelineItem.GroupedEvents( - expanded = false, events = listOf( aGroupableItem, aGroupableItem, From 6621163d2761bc090cf6782e340208576f87fb38 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 26 May 2023 15:35:33 +0200 Subject: [PATCH 05/75] Document [TimelineItemGroupPosition] and improve preview of [MessageEventBubble]. --- .../impl/timeline/TimelineStateProvider.kt | 2 +- .../timeline/components/MessageEventBubble.kt | 13 +++++-- .../event/TimelineItemEventFactory.kt | 2 +- .../model/TimelineItemGroupPosition.kt | 35 +++++++++++++++++++ .../model/bubble/BubbleStateProvider.kt | 1 + .../core/extensions/BasicExtensions.kt | 1 + ...leDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +-- ...leDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +-- ...eDarkPreview_0_null_10,NEXUS_5,1.0,en].png | 4 +-- ...eDarkPreview_0_null_11,NEXUS_5,1.0,en].png | 4 +-- ...eDarkPreview_0_null_12,NEXUS_5,1.0,en].png | 3 ++ ...eDarkPreview_0_null_13,NEXUS_5,1.0,en].png | 3 ++ ...eDarkPreview_0_null_14,NEXUS_5,1.0,en].png | 3 ++ ...eDarkPreview_0_null_15,NEXUS_5,1.0,en].png | 3 ++ ...leDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +-- ...leDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +-- ...leDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +-- ...leDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +-- ...leDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +-- ...leDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +-- ...leDarkPreview_0_null_8,NEXUS_5,1.0,en].png | 4 +-- ...leDarkPreview_0_null_9,NEXUS_5,1.0,en].png | 4 +-- ...eLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +-- ...eLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +-- ...LightPreview_0_null_10,NEXUS_5,1.0,en].png | 4 +-- ...LightPreview_0_null_11,NEXUS_5,1.0,en].png | 4 +-- ...LightPreview_0_null_12,NEXUS_5,1.0,en].png | 3 ++ ...LightPreview_0_null_13,NEXUS_5,1.0,en].png | 3 ++ ...LightPreview_0_null_14,NEXUS_5,1.0,en].png | 3 ++ ...LightPreview_0_null_15,NEXUS_5,1.0,en].png | 3 ++ ...eLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +-- ...eLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +-- ...eLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +-- ...eLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +-- ...eLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +-- ...eLightPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +-- ...eLightPreview_0_null_8,NEXUS_5,1.0,en].png | 4 +-- ...eLightPreview_0_null_9,NEXUS_5,1.0,en].png | 4 +-- 38 files changed, 122 insertions(+), 52 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt index a8e17ea462..2cee290871 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineStateProvider.kt @@ -94,7 +94,7 @@ internal fun aTimelineItemEvent( eventId: EventId = EventId("\$" + Random.nextInt().toString()), isMine: Boolean = false, content: TimelineItemEventContent = aTimelineItemTextContent(), - groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.First, + groupPosition: TimelineItemGroupPosition = TimelineItemGroupPosition.None, sendState: EventSendState = EventSendState.Sent(eventId), ): TimelineItem.Event { return TimelineItem.Event( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index 606d185568..b24140efc9 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -20,7 +20,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size @@ -35,13 +34,16 @@ import androidx.compose.ui.graphics.Shape import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.bubble.BubbleState import io.element.android.features.messages.impl.timeline.model.bubble.BubbleStateProvider +import io.element.android.libraries.core.extensions.to01 import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.ElementTheme import io.element.android.libraries.designsystem.theme.components.Surface +import io.element.android.libraries.designsystem.theme.components.Text private val BUBBLE_RADIUS = 16.dp @@ -141,7 +143,14 @@ private fun ContentToPreview(state: BubbleState) { state = state, interactionSource = MutableInteractionSource(), ) { - Spacer(modifier = Modifier.size(width = 120.dp, height = 32.dp)) + // Render the state as a text to better understand the previews + Text( + modifier = Modifier + .size(width = 120.dp, height = 32.dp) + .padding(horizontal = 12.dp, vertical = 6.dp), + fontSize = 10.sp, + text = "${state.groupPosition.javaClass.simpleName} m:${state.isMine.to01()} h:${state.isHighlighted.to01()}" + ) } } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index af3d7e6f24..07ae3bf321 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -105,7 +105,7 @@ class TimelineItemEventFactory @Inject constructor( return when { previousSender != currentSender && nextSender == currentSender -> TimelineItemGroupPosition.First previousSender == currentSender && nextSender == currentSender -> TimelineItemGroupPosition.Middle - previousSender == currentSender && nextSender != currentSender -> TimelineItemGroupPosition.Last + previousSender == currentSender /* && nextSender != currentSender (== true) */ -> TimelineItemGroupPosition.Last else -> TimelineItemGroupPosition.None } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemGroupPosition.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemGroupPosition.kt index ec3a94519b..5a93e87e73 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemGroupPosition.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/TimelineItemGroupPosition.kt @@ -18,13 +18,48 @@ package io.element.android.features.messages.impl.timeline.model import androidx.compose.runtime.Immutable +/** + * Attribute for a TimelineItem, used to render successive events from the same sender differently. + * + * Possible sequences in the timeline will be: + * + * Only one Event: + * - [None] + * + * Two Events + * - [First] + * - [Last] + * + * Many Events: + * - [First] + * - [Middle] (repeated if necessary) + * - [Last] + */ @Immutable sealed interface TimelineItemGroupPosition { + /** + * The event is part of a group of events from the same sender and is the first sent Event. + */ object First : TimelineItemGroupPosition + + /** + * The event is part of a group of events from the same sender and is neither the first nor the last sent Event. + */ object Middle : TimelineItemGroupPosition + + /** + * The event is part of a group of events from the same sender and is the last sent Event. + */ object Last : TimelineItemGroupPosition + + /** + * The event is not part of a group of events. Sender of previous event is different, and sender of next event is different. + */ object None : TimelineItemGroupPosition + /** + * Return true if the previous sender of the event is a different sender. + */ fun isNew(): Boolean = when (this) { First, None -> true else -> false diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt index 91804278ae..fbcbcc5454 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/model/bubble/BubbleStateProvider.kt @@ -25,6 +25,7 @@ open class BubbleStateProvider : PreviewParameterProvider { TimelineItemGroupPosition.First, TimelineItemGroupPosition.Middle, TimelineItemGroupPosition.Last, + TimelineItemGroupPosition.None, ).map { groupPosition -> sequenceOf(false, true).map { isMine -> sequenceOf(false, true).map { isHighlighted -> diff --git a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt index b845a5efeb..cc97b5fda0 100644 --- a/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt +++ b/libraries/core/src/main/kotlin/io/element/android/libraries/core/extensions/BasicExtensions.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.core.extensions fun Boolean.toOnOff() = if (this) "ON" else "OFF" +fun Boolean.to01() = if (this) "1" else "0" inline fun T.ooi(block: (T) -> Unit): T = also(block) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 4069bda25e..4e18865389 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb1648a000da198a30d3214adecf0b1266a150a226548ce77ac6d0eb001c65e2 -size 5238 +oid sha256:16b26c7ab2061dcc99daafef6507524969b942a39fa4f183325aa562ec76909a +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 219643d41a..f2afec828c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44487cb1f6115b388bb043081e15d904a090277fca6edea05ccb7b6c54d56de2 -size 5761 +oid sha256:4f82e5a6048cdcf803073af9a8c108f81ec4c6f70be2696182239fccd3cce2a3 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png index a13df6829d..d85cb6608e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:75ba23e1b82c3a5e4c3f4330a004b00d4eb2a2d3a27ce687ad6876c6e6e129be -size 5547 +oid sha256:0bdcdccd0ae75d5a36bcf41e6421d912086ef520fc5ad5ddee8343290be42aa5 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png index 0e2b15ff9b..d49b9693b1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:46067cba703ae02846a141dd53b888d48b6e55890cefed4cef4795c0b5910346 -size 5871 +oid sha256:294dca25a706fd0dc56e4997196e77ec1ce65d88f41f2a1ee1a4d27e75105a48 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ce98f9ebfe --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bccda39ab7124622f388f186f86ec203f91c721e397b0d23aa900c8272e574f4 +size 4550 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..06c91c8d57 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bc0f8753e58d9487a7c08bad4aa360f2aeae03b9309621b8f28a8eb8d8e03aba +size 5272 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9460611979 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:95a92355a64b63d223a45a99283ff4d9939a90a3da96387c0f6605cc3b0593cd +size 5011 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2dc3c4b881 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b4dc33b8578f34ad99e198897d80d2e9b6de7141bbc29bc2b39ee99376c2059f +size 4940 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 7c2e9b8c05..f67e0f71af 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbd968203eb9092f6ceab631da735a72bd15a44c3c8faaba45246a58bf3d9837 -size 5566 +oid sha256:708dd91f3622dfee4f569010a58dda1aed1866b1b4e13dd47af0c8dbacd33698 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png index b2fbd520c3..35ffe16cee 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d7f03a7e0ac8e63116d1c4806f07266ace800f52c8f089846c515ff12488918f -size 5875 +oid sha256:b126e4d6d9087ca246f9b3a70d93bdfb953052cbdf2089eca67513b6cb04d40a +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png index d6312cb012..15fd4fee6f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:492b8ec89b02bbba9abb849d6b28f61d77f448d66d1eef5eba5a6f66db725897 -size 5133 +oid sha256:cfec26d03db4df611e20908d3b5765497fcba74077070af80bbb16332f68c922 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 0d2618d910..2ca0425fcc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ccf5cf29bdcb6016203c849ea1013de2ab42ec21c9b202c56dea2d7b2b75434b -size 5516 +oid sha256:57af2ecc51482b53d331cf20b553dbaa580d9f472cf0db72c6e0f8ab3d51711f +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png index e25aab0f37..27ddf4bd21 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e666a4e785be44883e945e5ab22766c96358381467fc6860c3e8733a359d6801 -size 5462 +oid sha256:84efea63cf1faebc110c6b194411a9568c91c6e38eaf6c0b5df49bc2b105a0e1 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png index 192b3586fc..16915724c0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6fc64090e9c685246a0adc2a2250cb80fcc64cbe7d6578d63bc4827eaa50591e -size 5682 +oid sha256:971214d459626ff3e6cad74be7506a16d9ef33e97fcb428fc76fab9dc28f38f9 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png index 8754951421..460e2f93b6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b584ab84ca09c1fe9c2ce8433a92adb8f1f6c595ccc702f10b976b81951b9048 -size 5253 +oid sha256:0bf72e3bd9955500764925e339673ccf70889fb003550116e9a611b8ac4702f6 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png index 8996a9fbed..dbee0e2d8a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab7a53108da1eda0c1f24360d4c4073dca110dd3b368cc1f117cd3f351a385dc -size 5789 +oid sha256:5d1d06501e11adca85e4755ebcb8823aafa09ecd1fdd29765ca1971a130c8e5b +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png index 33e9fff6b1..7a75cd0940 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ace5a91e960c6570647e2b8ad2de428ba474e48531a2f74ab267d07668d7a9e8 -size 4928 +oid sha256:891218a3de22a8de7e8a28ea316190dfd9250e43ccd0a26f6f5ff0a92e8bba88 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png index 5c993c220e..b133a4a2a6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aec5d74e6f4d316c1ea124dc50e2abbfd22d6ee94b6d82519b3f9cab7a191dfa -size 5348 +oid sha256:1bfc2475f2f6b142a8674d44bfb27bd62d5972f7a9ee443c0cc57be300790464 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png index 1e9f02cdef..2811f1a1b4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dfaa4d6032641b332b111b20c81ec4bca917391cf122a06718fca7da849a7c94 -size 5124 +oid sha256:9f215f708626e853d1baf778c0a952881bf06c7981123a7bd27ac56df437658e +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png index 7b2413937c..7157775a74 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ac3eb3ebe9f2f30224389586f9b5901106b73cee9f5b4cdeb0f76b313ba0cd31 -size 5384 +oid sha256:ea44be04299641b1f525105b1fbafcb7ba46d97d2a9807efbb9bc63f0e29dc4d +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..06e0577cd7 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e816c683a931a21519eaa4e82b1ca3510b0e4d6f1dc6bd43487974bf0982a0b5 +size 4917 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7b670b1ebc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7909e03ee9d6e2811863ed9b93df5cd00814765c8421443606df425b80be7670 +size 5428 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4bc7db313e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5351ec4553c3650c52553f753d966ea9c726bb2ce949225be363891ab7d667c2 +size 5188 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4aba6b9f7c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74f0a3e254f9c34212ca93c17efca0b3a7edff6912c8f559f8ba276bd74946f0 +size 5065 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png index cb2f53102c..f51516b7d0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e0eaf65f627b7be068311b0c8f52cfbe847571684a0c63dda3e7c0621eac42c -size 5133 +oid sha256:a71faa0aee58b977af48b5f8d8df0edf9b0bc4d21c6d5f65bdb54de0065906a2 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png index 1324860ed0..37cf28dd62 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8359444a65d74682106930a7bbc6fb171acb95446cc5ff67eefa0dadc51e5932 -size 5397 +oid sha256:11296725ee57a3ba87f5c89e76253d964808c9991faa50369f3483b0b846299b +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png index 12176c5b98..63da30a757 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d774fca7f28623d7e464c233e5e2282b5c7d608d7aa6084b2350a4f71765bc5a -size 4809 +oid sha256:d88fd626137d1793981906989a1922193941b2367c59ff0c6bdc5e2d15cda97b +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png index 34a8c795e7..1e7f8c3c8a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:32c81961535a22ce1fccc15865cd76bc9844168552a2205f7f7342398866b622 -size 5109 +oid sha256:5c7248dd717838956900527431f8dda4aff2717a2c9238a80a828aaea2a4f9cf +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png index 4b3f1bfb0c..18be292080 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bd53dc603b4689eabe41e5d90ea0d8a70b769d60082ee57eee556ef9a7ea24ec -size 4984 +oid sha256:49e1f398b462cb05907142a14416e039faaf20bfc4c1733f36d63e69286525f3 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png index e4a872faf4..4a5157aa62 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:43e08e7c243192571121595483ddc2dd44b56892afff5ae949ac224cad13e73f -size 5165 +oid sha256:0a8caac9c140941bd71b8e9b36cd15df8ec37ef85755b3de8a3353cd602e0797 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png index 19cc971e97..d6a534b284 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c61846e0037aa7a4d6f146fa4f2b441654570db082b3f506bb6f49aa01f3999 -size 4944 +oid sha256:7d2be4bed342f82cd180f7dd31d8be9719e59123b991e9a804f5586a3a943ce1 +size 329 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png index b4bd02006b..33a03978a9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:137bfee9d776c8abff1a2428e3edc1c28cc7ca21d9849640e2aad2d945d7f7f2 -size 5361 +oid sha256:0bc50249a165de79de671860657998f771ec26ef8a4d07a78004423b2a44a82e +size 329 From 929ed22cb3c81dbcd429b3648db7cda35ee1f156 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 26 May 2023 16:10:18 +0200 Subject: [PATCH 06/75] Ensure GroupPosition is correct regarding Event which cannot be grouped. --- .../event/TimelineItemEventFactory.kt | 61 ++++++++++++++++++- 1 file changed, 58 insertions(+), 3 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index 07ae3bf321..4e99b37592 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -24,7 +24,17 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.EventSendState +import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent +import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent +import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails +import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent +import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent +import io.element.android.libraries.matrix.api.timeline.item.event.StateContent +import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent +import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent +import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent import kotlinx.collections.immutable.toImmutableList import java.text.DateFormat import java.util.Date @@ -102,11 +112,56 @@ class TimelineItemEventFactory @Inject constructor( val previousSender = prevTimelineItem?.event?.sender val nextSender = nextTimelineItem?.event?.sender + val previousIsGroupable = prevTimelineItem.isGroupable() + val nextIsGroupable = nextTimelineItem.isGroupable() + return when { - previousSender != currentSender && nextSender == currentSender -> TimelineItemGroupPosition.First - previousSender == currentSender && nextSender == currentSender -> TimelineItemGroupPosition.Middle - previousSender == currentSender /* && nextSender != currentSender (== true) */ -> TimelineItemGroupPosition.Last + previousSender != currentSender && nextSender == currentSender -> { + if (nextIsGroupable) { + TimelineItemGroupPosition.First + } else { + TimelineItemGroupPosition.None + } + } + previousSender == currentSender && nextSender == currentSender -> { + if (previousIsGroupable) { + if (nextIsGroupable) { + TimelineItemGroupPosition.Middle + } else { + TimelineItemGroupPosition.Last + } + } else { + if (nextIsGroupable) { + TimelineItemGroupPosition.First + } else { + TimelineItemGroupPosition.None + } + } + } + previousSender == currentSender /* && nextSender != currentSender (== true) */ -> { + if (previousIsGroupable) { + TimelineItemGroupPosition.Last + } else { + TimelineItemGroupPosition.None + } + } else -> TimelineItemGroupPosition.None } } } + +private fun MatrixTimelineItem.Event?.isGroupable(): Boolean { + if (this == null) return true + return when (event.content) { + is FailedToParseStateContent, + is ProfileChangeContent, + is RoomMembershipContent, + UnknownContent, + is StateContent -> false + is FailedToParseMessageLikeContent, + is MessageContent, + RedactedContent, + is StickerContent, + is UnableToDecryptContent -> true + } +} From c590a32942497fa993db6377f77ba5a4694b920d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 26 May 2023 16:25:45 +0200 Subject: [PATCH 07/75] Move extension to a common file for code clarity. --- .../event/TimelineItemEventFactory.kt | 32 +------- .../impl/timeline/groups/Groupability.kt | 76 +++++++++++++++++++ .../timeline/groups/TimelineItemGrouper.kt | 29 ------- 3 files changed, 80 insertions(+), 57 deletions(-) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt index 4e99b37592..87792edf1d 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/factories/event/TimelineItemEventFactory.kt @@ -16,25 +16,17 @@ package io.element.android.features.messages.impl.timeline.factories.event +import io.element.android.features.messages.impl.timeline.groups.canBeDisplayedInBubbleBlock import io.element.android.features.messages.impl.timeline.model.AggregatedReaction import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.TimelineItemGroupPosition import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions +import io.element.android.libraries.core.bool.orTrue import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.EventSendState -import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent -import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent -import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent -import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails -import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent -import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent -import io.element.android.libraries.matrix.api.timeline.item.event.StateContent -import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent -import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent -import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent import kotlinx.collections.immutable.toImmutableList import java.text.DateFormat import java.util.Date @@ -112,8 +104,8 @@ class TimelineItemEventFactory @Inject constructor( val previousSender = prevTimelineItem?.event?.sender val nextSender = nextTimelineItem?.event?.sender - val previousIsGroupable = prevTimelineItem.isGroupable() - val nextIsGroupable = nextTimelineItem.isGroupable() + val previousIsGroupable = prevTimelineItem?.canBeDisplayedInBubbleBlock().orTrue() + val nextIsGroupable = nextTimelineItem?.canBeDisplayedInBubbleBlock().orTrue() return when { previousSender != currentSender && nextSender == currentSender -> { @@ -149,19 +141,3 @@ class TimelineItemEventFactory @Inject constructor( } } } - -private fun MatrixTimelineItem.Event?.isGroupable(): Boolean { - if (this == null) return true - return when (event.content) { - is FailedToParseStateContent, - is ProfileChangeContent, - is RoomMembershipContent, - UnknownContent, - is StateContent -> false - is FailedToParseMessageLikeContent, - is MessageContent, - RedactedContent, - is StickerContent, - is UnableToDecryptContent -> true - } -} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt new file mode 100644 index 0000000000..9d54d372be --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.messages.impl.timeline.groups + +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRoomMembershipContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent +import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent +import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent +import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent +import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent +import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent +import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent +import io.element.android.libraries.matrix.api.timeline.item.event.StateContent +import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent +import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent +import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent + +/** + * Return true if the Event can be grouped in a collapse/expand block + * When [canBeGrouped] returns a value, [canBeDisplayedInBubbleBlock] MUST return the opposite value. + * Since the receiving type are not the same, the two functions exist. + */ +internal fun TimelineItem.Event.canBeGrouped(): Boolean { + return when (content) { + is TimelineItemTextBasedContent, + is TimelineItemEncryptedContent, + is TimelineItemImageContent, + TimelineItemRedactedContent, + TimelineItemUnknownContent -> false + is TimelineItemProfileChangeContent, + is TimelineItemRoomMembershipContent, + is TimelineItemStateEventContent -> true + } +} + +/** + * Return true if the Event can be grouped in a block of message bubbles. + * When [canBeDisplayedInBubbleBlock] returns a value, [canBeGrouped] MUST return the opposite value. + * Since the receiving type are not the same, the two functions exist. + */ +internal fun MatrixTimelineItem.Event.canBeDisplayedInBubbleBlock(): Boolean { + return when (event.content) { + is FailedToParseMessageLikeContent, + is MessageContent, + RedactedContent, + is StickerContent, + is UnableToDecryptContent -> true + is FailedToParseStateContent, + is ProfileChangeContent, + is RoomMembershipContent, + UnknownContent, + is StateContent -> false + } +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt index d996406102..b04509ba22 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt @@ -17,18 +17,6 @@ package io.element.android.features.messages.impl.timeline.groups import io.element.android.features.messages.impl.timeline.model.TimelineItem -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEmoteContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemNoticeContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRoomMembershipContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent -import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import kotlinx.collections.immutable.toImmutableList import javax.inject.Inject @@ -57,23 +45,6 @@ class TimelineItemGrouper @Inject constructor() { } return result } - - private fun TimelineItem.Event.canBeGrouped(): Boolean { - return when (content) { - is TimelineItemEncryptedContent, - is TimelineItemImageContent, - TimelineItemRedactedContent, - is TimelineItemEmoteContent, - is TimelineItemNoticeContent, - is TimelineItemTextContent, - is TimelineItemFileContent, - is TimelineItemVideoContent, - TimelineItemUnknownContent -> false - is TimelineItemProfileChangeContent, - is TimelineItemRoomMembershipContent, - is TimelineItemStateEventContent -> true - } - } } /** From 25e432d24e03aa8438b59b986aee571361cbc23b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 26 May 2023 16:56:13 +0200 Subject: [PATCH 08/75] Fix setting y offset to -12.dp can overlap a state event displayed above. Rework how the vertical offset are applied. --- .../features/messages/impl/timeline/TimelineView.kt | 8 +++++--- .../impl/timeline/components/MessageEventBubble.kt | 7 +++---- ...ageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...ageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png | 2 +- ...geEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png | 2 +- ...geEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png | 2 +- ...geEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png | 2 +- ...ageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...ageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...ageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...ageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...ageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...ageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...ageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...ageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...eEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...eEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png | 4 ++-- ...eEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png | 2 +- ...eEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png | 2 +- ...eEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png | 2 +- ...eEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png | 2 +- ...geEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...geEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...p_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...p_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...p_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...p_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...p_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...p_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...p_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ..._TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ..._TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ..._TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ..._TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ..._TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ..._TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...p_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...p_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...p_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...p_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 3 +++ ..._MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ..._MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ..._MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ..._MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 3 +++ 56 files changed, 110 insertions(+), 103 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index d2707d2142..cf7c3c9bf1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -247,14 +247,16 @@ fun TimelineItemEventRow( ) { Row { if (!event.isMine) { - Spacer(modifier = Modifier.width(16.dp)) + Spacer(modifier = Modifier.width(4.dp)) } Column(horizontalAlignment = contentAlignment) { if (event.showSenderInformation) { MessageSenderInformation( event.safeSenderName, event.senderAvatar, - Modifier.zIndex(1f) + Modifier + .zIndex(1f) + .offset(y = 12.dp) ) } val bubbleState = BubbleState( @@ -282,7 +284,7 @@ fun TimelineItemEventRow( reactionsState = event.reactionsState, modifier = Modifier .zIndex(1f) - .offset(x = if (event.isMine) 0.dp else 20.dp, y = -(16.dp)) + .offset(x = if (event.isMine) 0.dp else 20.dp, y = -(4.dp)) ) } if (event.isMine) { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index b24140efc9..7174d62521 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -86,10 +86,9 @@ fun MessageEventBubble( fun Modifier.offsetForItem(): Modifier { return if (state.isMine) { - // FIXME setting y offset to -12.dp can overlap a state event displayed above. - offset(y = -(12.dp)) + this } else { - offset(x = 20.dp, y = -(12.dp)) + offset(x = 20.dp) } } @@ -132,7 +131,7 @@ internal fun MessageEventBubbleDarkPreview(@PreviewParameter(BubbleStateProvider @Composable private fun ContentToPreview(state: BubbleState) { - // Due to y offset, surround with a Box + // Due to position offset, surround with a Box Box( modifier = Modifier .size(width = 240.dp, height = 64.dp) diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 4e18865389..4889ac5611 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:16b26c7ab2061dcc99daafef6507524969b942a39fa4f183325aa562ec76909a -size 329 +oid sha256:5cfe099bf35a5b6ccb106577036b5b1de21795ed1bbd0912051e8ac230945251 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png index f2afec828c..c5b38504e0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f82e5a6048cdcf803073af9a8c108f81ec4c6f70be2696182239fccd3cce2a3 -size 329 +oid sha256:e3a95beb2f39a13eeb8f179cfd25b2aa4bc13151dbef64fdb7e19da03bc85078 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png index d85cb6608e..5ffc66260f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bdcdccd0ae75d5a36bcf41e6421d912086ef520fc5ad5ddee8343290be42aa5 -size 329 +oid sha256:1cbf9779779729213e799514ac3a042751548faf0742c983715f9aa52581620a +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png index d49b9693b1..e8293c13e0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:294dca25a706fd0dc56e4997196e77ec1ce65d88f41f2a1ee1a4d27e75105a48 -size 329 +oid sha256:e3eb56cfdf843fb119398aeeebffb0d84fc37eb285157f1339d21421890b362f +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png index ce98f9ebfe..d477c66661 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bccda39ab7124622f388f186f86ec203f91c721e397b0d23aa900c8272e574f4 +oid sha256:aabb896501fb8f1010c0aba1041ef27b69ae91124bfb376286b483424ed89a3d size 4550 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png index 06c91c8d57..71ddb9f81b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc0f8753e58d9487a7c08bad4aa360f2aeae03b9309621b8f28a8eb8d8e03aba +oid sha256:8c7f71b105cbf5aa0012ab3a5fda593b7337a390a9a0ff3724efa65e4b7e438e size 5272 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png index 9460611979..9bc9dac66a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:95a92355a64b63d223a45a99283ff4d9939a90a3da96387c0f6605cc3b0593cd +oid sha256:751407c98563bf29a2da279e69cbe799b37979a19ba2e23adcd9b896249bfdc3 size 5011 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png index 2dc3c4b881..143ad7ee48 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b4dc33b8578f34ad99e198897d80d2e9b6de7141bbc29bc2b39ee99376c2059f +oid sha256:a95f16b287ceab493798abc44399282b6e5d802a09c426d34a314dcdd23f3433 size 4940 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png index f67e0f71af..630b20373f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:708dd91f3622dfee4f569010a58dda1aed1866b1b4e13dd47af0c8dbacd33698 -size 329 +oid sha256:4e0274af196e662b30da662f5183106693d45afd763ca734542daf28c21b0659 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 35ffe16cee..84f86b9da4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b126e4d6d9087ca246f9b3a70d93bdfb953052cbdf2089eca67513b6cb04d40a -size 329 +oid sha256:e53107dda370a166e3562a855c4d5d6084eb57b9ee95527e50fc2c4c038a7611 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 15fd4fee6f..a1c01ec525 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cfec26d03db4df611e20908d3b5765497fcba74077070af80bbb16332f68c922 -size 329 +oid sha256:34097a007b55152de80c509a7ad213bee1137ad6741bb2ca4c5fef918320f80c +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 2ca0425fcc..493a42a954 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:57af2ecc51482b53d331cf20b553dbaa580d9f472cf0db72c6e0f8ab3d51711f -size 329 +oid sha256:5aafe84b9920988d206fd9350092148ca50d1281a7d80c3cd12b90149ede39c2 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 27ddf4bd21..fc50ac469b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:84efea63cf1faebc110c6b194411a9568c91c6e38eaf6c0b5df49bc2b105a0e1 -size 329 +oid sha256:f4109ddeaa1b14f3d7f9183ba13f6c30fed6a507b3cccd26ae3d94e1189f5247 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png index 16915724c0..86b52c998d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:971214d459626ff3e6cad74be7506a16d9ef33e97fcb428fc76fab9dc28f38f9 -size 329 +oid sha256:962009a787b9c55d8a48fd1f0e104727a1c8e331ffeb87f9696b9e2b29118037 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png index 460e2f93b6..834dc8a992 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bf72e3bd9955500764925e339673ccf70889fb003550116e9a611b8ac4702f6 -size 329 +oid sha256:62da79ae3017c3abae148d73dcb4fac4f2624b5331c69dc971c1515ef2fa2dc2 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png index dbee0e2d8a..a7de294242 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d1d06501e11adca85e4755ebcb8823aafa09ecd1fdd29765ca1971a130c8e5b -size 329 +oid sha256:2af8578e0a63ec0322c628aa08293ad4709aa9496a9f9e84a5c305cb569b5af0 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png index 7a75cd0940..d4a41ee365 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:891218a3de22a8de7e8a28ea316190dfd9250e43ccd0a26f6f5ff0a92e8bba88 -size 329 +oid sha256:5ca0cfe51c16a5a7122f6f9b1977ff7744bfc17d91cefbb6850a54e2e7c9636f +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png index b133a4a2a6..f7236ed2d8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1bfc2475f2f6b142a8674d44bfb27bd62d5972f7a9ee443c0cc57be300790464 -size 329 +oid sha256:23d8cfa92569c8e61a2aa970630e91574c6c3e2bf754102edba36c599ddec099 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png index 2811f1a1b4..a72e0fb5bc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9f215f708626e853d1baf778c0a952881bf06c7981123a7bd27ac56df437658e -size 329 +oid sha256:986b9ce971dd579a739113132475eac81c05dcdcc9cb8d07b32d18f80e6aee23 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png index 7157775a74..10813647b8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ea44be04299641b1f525105b1fbafcb7ba46d97d2a9807efbb9bc63f0e29dc4d -size 329 +oid sha256:606e1069eca1facbbb5b78d80b9a6a377f7933ffef451cf43f3935cbd075d968 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png index 06e0577cd7..b803cd7f61 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e816c683a931a21519eaa4e82b1ca3510b0e4d6f1dc6bd43487974bf0982a0b5 +oid sha256:d8daf9315cd752d290111c1d4cb8d26945b3584073af63e2402411c0d66f6c59 size 4917 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png index 7b670b1ebc..66a05fefb2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7909e03ee9d6e2811863ed9b93df5cd00814765c8421443606df425b80be7670 +oid sha256:a8af1f9406db93eb3791e193c35e9ff67d6443498fd1ef9ef65f16434efdb76e size 5428 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png index 4bc7db313e..964ae969be 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5351ec4553c3650c52553f753d966ea9c726bb2ce949225be363891ab7d667c2 +oid sha256:d3d5818b55720aa211e9db20e84b49e7a548246be358f3d5dc30dbf8c194ec3f size 5188 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png index 4aba6b9f7c..512f0d8286 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:74f0a3e254f9c34212ca93c17efca0b3a7edff6912c8f559f8ba276bd74946f0 +oid sha256:4f8ac8dd3d06f02f2470a4c322db41bbfe7f90c3befb1a71221096cc575ce296 size 5065 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png index f51516b7d0..ed77e495a6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a71faa0aee58b977af48b5f8d8df0edf9b0bc4d21c6d5f65bdb54de0065906a2 -size 329 +oid sha256:2228db8b530ee59aa08697b90485cb39ed245de75e36fcb27a50471feddeffb7 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png index 37cf28dd62..9937d392a0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:11296725ee57a3ba87f5c89e76253d964808c9991faa50369f3483b0b846299b -size 329 +oid sha256:4074c82d1e98c484abf997ed07b8e56274d3fb213e98db5a72f3d6c2001215a8 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png index 63da30a757..7a30344edd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d88fd626137d1793981906989a1922193941b2367c59ff0c6bdc5e2d15cda97b -size 329 +oid sha256:a47281c07d1821618b03dcc08fd8b10154c2fa368fa84a924f9e6834b321a26d +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png index 1e7f8c3c8a..5a6edaea07 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c7248dd717838956900527431f8dda4aff2717a2c9238a80a828aaea2a4f9cf -size 329 +oid sha256:9901fc0ec796a0e4d7c30a8d44a94bbb8759714ee2f8672d782926c945ffdb43 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png index 18be292080..643222ba4f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49e1f398b462cb05907142a14416e039faaf20bfc4c1733f36d63e69286525f3 -size 329 +oid sha256:5d03540cc3ed64cb1d6baea27cda2a13f9f01a326d49f7a48e32a055544e0e6f +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png index 4a5157aa62..1a3a8bebc0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0a8caac9c140941bd71b8e9b36cd15df8ec37ef85755b3de8a3353cd602e0797 -size 329 +oid sha256:40dd3ea2ae03be9ef1edd14613a4a71126dd5d290e9296b1631de8d29bfe0c4e +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png index d6a534b284..3be35124de 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d2be4bed342f82cd180f7dd31d8be9719e59123b991e9a804f5586a3a943ce1 -size 329 +oid sha256:387c101f7706c27d2cb25a4d7fef9d8c84626b860d1f74fab12f5d21116495ed +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png index 33a03978a9..6e07280a10 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0bc50249a165de79de671860657998f771ec26ef8a4d07a78004423b2a44a82e -size 329 +oid sha256:a7949d19caf0c8ba0f7ca9a46e07de5694639dabcf6860cd1aa1f95f53eb4e28 +size 364 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index ac79e58737..c569ad3566 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0e7e30c460d75815023ed02cd6b0fcff4a15881916f0056e47d9fd034bf3a181 -size 41125 +oid sha256:a511056320bb3acbffbd91e2511f299c56bddaeb9998386ee83a6c8c846af333 +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 8ca1e75687..6c140ec1e2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:81b126a3d322d70f06db15622e560a146cea2c81f5873987550aba0a5c5efffa -size 53314 +oid sha256:31c8bd05947cbe3f74d217aac06b06a08b7ba328f5e3dbdac0c87ca3afe87732 +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index cbe31a3f09..97151f5bf8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b0ede4470399e2c7e6f3c99e59191a857c1e14254789307777359802980c59a1 -size 193735 +oid sha256:9143abd810ae66b73f7a42084146ec40a68a4e4112f7ca62ed220bc6473cf77d +size 368 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 809b1bba99..7ab5445424 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c9fe22528aad68a5dffc8489fd1ec3138a8edde44fb23a1391f6e53a07d44fff -size 193966 +oid sha256:f5aefb5101926948e4526f776a40ee4f0e9e9577880ef015393fe274791b57ae +size 368 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index fc4cbf27fd..1b9f743388 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d559a454af6dfe367d1256dcd7ceeb32ae6fe168b76b1b5d8860d328e9752fb3 -size 51433 +oid sha256:a48c409916255498c1e47440354c0009107858028559eb90cc6c591d57cb0ded +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index cd486afaf0..b20e2d0ae0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e45ec17c6353f96f0f777f8c638da010dbd062c680175d8ae80f6e700a6f928 -size 72941 +oid sha256:fd56a902b27629ea24fd11f163d347df6375dd597590d163d59b733a1538c0ec +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png index bd287148b7..d48f11e372 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:572d6f11354e2b1f02cb0979b35e2c798abe2d3b63f852ca6b868471ceecaed0 -size 42964 +oid sha256:8982700ec1f20d066594715a664ac7333b603feeca02a2b024314843c88559c6 +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 8a2c64bd25..b75f4df936 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a01bde6db3ce00e84e9bb5d6a8179c8d97af9dde939f6e31aa81c955dbdbf3a3 -size 40586 +oid sha256:d94bad50b1bc10151122baf37f0ceb37806d2116decbd0e8cc8634da13ccd48a +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index a07f4eb8ed..c9d35cb3b3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f12a13dd0fb3b569ce332ebc4e00fb55a6402bbf2b2a927b02abb4056c5e5c3 -size 53043 +oid sha256:4ae1583a5d4b2ae3a5a8dfe4f4f7809b6b46687ad33aa0a12ade7b18bf04eabe +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index cdfd4fd6be..e5e62c79b7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c0f6c785a1e20160355c72356744dedba65bda3d453fbd9772390350eaf6cc6e -size 195504 +oid sha256:4256dc3623b650038262f227d56b598d06214fa5831e89105c08bea452e7a792 +size 368 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index cb6574b5ad..d619dfaa04 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:37c702936170a0e4b37d608cf62b92c3b60b93950d4e9f32633af77e3ce4fdc9 -size 195775 +oid sha256:b3ad71dfc03acfc3b4a865305dda532213e8ff5f3183eb976fbc0739ede74ee7 +size 368 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 949cd3ef67..46d1b9d639 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0b885810087916d3fefe699e5abc5125138ad74c6377f560e010e7fe68892da -size 51388 +oid sha256:b83ed834c1335ad917019656ed042b2fde17403e739066df1d29bc322f01ff36 +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 6a5e4d9ec6..64f147681b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:adcb71f4b9639287beabf35c9e91837bebee8f6b070fa77c0e6520fdf24faaae -size 73679 +oid sha256:cc174a97059882be652fcfb87a033091ecdbe84a4e0e8a1ef3eff29101bc6b4c +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png index 07823941c7..ab7dc9af1f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb24bbba2e17417f4f3459353ee2c762517acfae2eada90c47b559828bad16a0 -size 42411 +oid sha256:0ec4528874e237526821edffebe957682399d5941cd502239f7f0c7044ba0cd2 +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 3884544dd1..ecd52fd69c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9c2c642f1595f503a1deb1c241f14543d7f15221baa1d0937595aa023f36a874 -size 44765 +oid sha256:5c1fc2f6bde122e2a7b603bcc02ace39044be876debcf06d8d649d3ee9a052b9 +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 0dd33fab8e..0059811dce 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1afd2c42012017f154fdf031d4a5fc7384fa5f239b56cf4567f42fd579806c9a -size 45363 +oid sha256:7d43ca1f19ca846cdcab60ba2ded0b7f502ee414e6273eb724496ba506c70e99 +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index a934e0c73e..7d136e12c5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9481f2994885a7649aa54d1c0043c44fe390004a7b58db4fc9d0e3e70bfc258a -size 43537 +oid sha256:a3bdd036cd704d8e9e2b227a363b0d2147a4b0c8a0db8f04d11e0a6189c988d7 +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2ce4f8cf53 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6fc67d78ba2a31540c1cbe949572fdcd98850dab0d8242146d6d73ab02dae572 +size 41017 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index a32dc0d376..55ff596385 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:220b9ac66b74724887d7b0bbc082b4d0240d9fc25a068d33491eb83fe76e45fd -size 43958 +oid sha256:609f2538ab4c9d9bc6a84f6218604c5b8752d227a33205765e83f5d36b828c8a +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 6c69a3850e..30156c7a0b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9aa83ed14fb23495e79500a1e7d9dee8215efe67eec89e65e382d4f695eac68b -size 44871 +oid sha256:2c63d90fd878182e29b1205d8d4c007994407f9879a1ddfb0c79c3a3c6a5cd9e +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 0be594f38f..7d31c0dd3c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aa4970f995c6ee618be3d868ff5a34442352a7f4493cad345093b889daf0d4c2 -size 42042 +oid sha256:da7edea058b6a8c1f6c224dd9bda45fbe0fe344610c6f2d5588ae268116fa0b2 +size 367 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..789bcc0c1f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e9073c23bec50592107aa7a3b05d770c8fbf35db211578c9ca4cae399b9ba78d +size 40319 From 0cbddd58bcdb2e6d20c84c974aa276b369602ea9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 10:58:14 +0200 Subject: [PATCH 09/75] Fix compilation issue after rebase. --- .../features/messages/impl/timeline/groups/Groupability.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt index 9d54d372be..879f33e328 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/Groupability.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline.groups import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEncryptedContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemFileContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemImageContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemProfileChangeContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemRedactedContent @@ -25,6 +26,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent import io.element.android.features.messages.impl.timeline.model.event.TimelineItemUnknownContent +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVideoContent import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent @@ -47,6 +49,8 @@ internal fun TimelineItem.Event.canBeGrouped(): Boolean { is TimelineItemTextBasedContent, is TimelineItemEncryptedContent, is TimelineItemImageContent, + is TimelineItemFileContent, + is TimelineItemVideoContent, TimelineItemRedactedContent, TimelineItemUnknownContent -> false is TimelineItemProfileChangeContent, From 34980d34add8d3c6f7b63cc501e886fce43d5bc9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 11:07:55 +0200 Subject: [PATCH 10/75] Improve preview again --- .../impl/timeline/components/MessageEventBubble.kt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt index 7174d62521..220d653e8f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/MessageEventBubble.kt @@ -143,13 +143,17 @@ private fun ContentToPreview(state: BubbleState) { interactionSource = MutableInteractionSource(), ) { // Render the state as a text to better understand the previews - Text( + Box( modifier = Modifier .size(width = 120.dp, height = 32.dp) .padding(horizontal = 12.dp, vertical = 6.dp), - fontSize = 10.sp, - text = "${state.groupPosition.javaClass.simpleName} m:${state.isMine.to01()} h:${state.isHighlighted.to01()}" - ) + contentAlignment = Alignment.Center, + ) { + Text( + fontSize = 10.sp, + text = "${state.groupPosition.javaClass.simpleName} m:${state.isMine.to01()} h:${state.isHighlighted.to01()}" + ) + } } } } From bf62be3b0f546597869d402972b768cf86cb030f Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 11:09:38 +0200 Subject: [PATCH 11/75] Record screenshots. --- ...MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png | 4 ++-- ...MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...ssageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...ssageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png | 4 ++-- ...ssageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png | 4 ++-- ...ssageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png | 4 ++-- ...ssageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png | 4 ++-- ...ssageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...essageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...oup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 3 --- ...roup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 3 --- 62 files changed, 120 insertions(+), 126 deletions(-) delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 4889ac5611..b70f830625 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5cfe099bf35a5b6ccb106577036b5b1de21795ed1bbd0912051e8ac230945251 -size 364 +oid sha256:7ddcd892538faa691ade67de5145392f89eaab4c903cc8fed00b24726753ed4e +size 6945 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png index c5b38504e0..90c95a6056 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3a95beb2f39a13eeb8f179cfd25b2aa4bc13151dbef64fdb7e19da03bc85078 -size 364 +oid sha256:cea39d32d858cd7b28885e634e746aea3652a56b665563e26f62346d9eaa1128 +size 7172 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png index 5ffc66260f..92f24c2b0a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1cbf9779779729213e799514ac3a042751548faf0742c983715f9aa52581620a -size 364 +oid sha256:7eb000677f48efbd9dfbe373b221dbc98b2da65404e86aea42e79416cc032205 +size 7107 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png index e8293c13e0..c5f5a8072c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e3eb56cfdf843fb119398aeeebffb0d84fc37eb285157f1339d21421890b362f -size 364 +oid sha256:1daab66cf9beb8a5bb9c4cdea0db76fdc690b850bde1ada9679bfaf0167e6982 +size 7150 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png index d477c66661..2fa9ef478f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:aabb896501fb8f1010c0aba1041ef27b69ae91124bfb376286b483424ed89a3d -size 4550 +oid sha256:37ba26495ac60e2857cfab755675993f44775c8c0bd36b5b7d69ccebf9a71a6d +size 7269 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png index 71ddb9f81b..8b3bf8a750 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_13,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c7f71b105cbf5aa0012ab3a5fda593b7337a390a9a0ff3724efa65e4b7e438e -size 5272 +oid sha256:dcdb3be85af22f6997b372d268d54ca287ef282a8c1353d4fcb47d44ceffcf8a +size 7595 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png index 9bc9dac66a..8d529d0de5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_14,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:751407c98563bf29a2da279e69cbe799b37979a19ba2e23adcd9b896249bfdc3 -size 5011 +oid sha256:65c434fc772482b1a884bc3c68447e228b470549ff72a510e43ba0aaae69387a +size 7396 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png index 143ad7ee48..91e0bfc7e0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_15,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a95f16b287ceab493798abc44399282b6e5d802a09c426d34a314dcdd23f3433 -size 4940 +oid sha256:23f468e1737a6a3789fdfd56322e4b15551c1a0d74249f92aee92879d476ea91 +size 7485 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 630b20373f..5bcbeaf3a9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e0274af196e662b30da662f5183106693d45afd763ca734542daf28c21b0659 -size 364 +oid sha256:7ab56f3950f19ed24fcd2592157046eb878a2dde9e9831b8a3d846f5f3207dc0 +size 7062 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 84f86b9da4..bd06f74e2a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e53107dda370a166e3562a855c4d5d6084eb57b9ee95527e50fc2c4c038a7611 -size 364 +oid sha256:2c6e69cd6e3687ef724ff2c8f5151fb6679fd6395d866d7f944507c130e938eb +size 7084 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png index a1c01ec525..d34a9e81f6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34097a007b55152de80c509a7ad213bee1137ad6741bb2ca4c5fef918320f80c -size 364 +oid sha256:d789e51e1f3c440f8577d465322c982a5a6aea54f7d1a7ab12e183ba841b0f14 +size 7274 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 493a42a954..75c1d45a91 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5aafe84b9920988d206fd9350092148ca50d1281a7d80c3cd12b90149ede39c2 -size 364 +oid sha256:15100ea369eab89e99c4252b88e33f362cca287e6ef0799e1e3e3a8f4be6e227 +size 7327 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png index fc50ac469b..0f6bef4954 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4109ddeaa1b14f3d7f9183ba13f6c30fed6a507b3cccd26ae3d94e1189f5247 -size 364 +oid sha256:92c2dc09ef213e2f55568f036d17e0ba81f9053a61774bd89ea27273c9a0aeb8 +size 7415 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png index 86b52c998d..9bfcee38c9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:962009a787b9c55d8a48fd1f0e104727a1c8e331ffeb87f9696b9e2b29118037 -size 364 +oid sha256:a035a78264d396b78c2653b89ab0edbc3ee6400d380eccd587f80cafdd57f165 +size 7335 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png index 834dc8a992..822ca7afe1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:62da79ae3017c3abae148d73dcb4fac4f2624b5331c69dc971c1515ef2fa2dc2 -size 364 +oid sha256:012db6849c7458978d11f0c56b171dcc01993ac3bbc940639a609a04e5f591ad +size 7093 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png index a7de294242..4834e3f8af 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleDarkPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2af8578e0a63ec0322c628aa08293ad4709aa9496a9f9e84a5c305cb569b5af0 -size 364 +oid sha256:d429ec724142f73fa7846ec26a21b62e1a3466bd1e85193bdf48e749bf188978 +size 7253 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png index d4a41ee365..625ec13c74 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5ca0cfe51c16a5a7122f6f9b1977ff7744bfc17d91cefbb6850a54e2e7c9636f -size 364 +oid sha256:87de1686f0e84c9f00f80dc02a63ad6d0b0c360762a9d4f996b315407aec37d6 +size 6700 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png index f7236ed2d8..859a6eed31 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23d8cfa92569c8e61a2aa970630e91574c6c3e2bf754102edba36c599ddec099 -size 364 +oid sha256:ab4af3d37dc161ccba7366517f60cb50923539615909c4bf76ec667095a4293c +size 6712 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png index a72e0fb5bc..8f13f13c53 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:986b9ce971dd579a739113132475eac81c05dcdcc9cb8d07b32d18f80e6aee23 -size 364 +oid sha256:a5962c60a9072b96cbd72e8ea9cf6344f715d8469a45f51fcdebacfec5344544 +size 6693 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png index 10813647b8..597309f4f9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_11,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:606e1069eca1facbbb5b78d80b9a6a377f7933ffef451cf43f3935cbd075d968 -size 364 +oid sha256:3ed947a2df42bc4cd7b0c181d608c69763897383f4846b2878e3df2705dfae0e +size 6715 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png index b803cd7f61..e3c939c48b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_12,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d8daf9315cd752d290111c1d4cb8d26945b3584073af63e2402411c0d66f6c59 -size 4917 +oid sha256:30f04a04ecde63528df19021a9b43b1bb2d26aaf31fa021e4b83f4503fd73782 +size 6997 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png index 66a05fefb2..44d40d43dc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_13,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a8af1f9406db93eb3791e193c35e9ff67d6443498fd1ef9ef65f16434efdb76e -size 5428 +oid sha256:a4e390e24a61c5ea42c2469bc71c0d7785de647991dbf5b8d5df360e76adcd0a +size 7184 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png index 964ae969be..eff7f05a7d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_14,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3d5818b55720aa211e9db20e84b49e7a548246be358f3d5dc30dbf8c194ec3f -size 5188 +oid sha256:eba96060e90371916f2e62f550cc0fd4fa44e2872263a353dd3c37c2664c0f47 +size 7001 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png index 512f0d8286..dddf73f879 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_15,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4f8ac8dd3d06f02f2470a4c322db41bbfe7f90c3befb1a71221096cc575ce296 -size 5065 +oid sha256:05f55e1df050651b48708dc3f40fe95a8ab68d1a0fea7838023cbadc2a21da57 +size 7078 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png index ed77e495a6..65d5cec935 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2228db8b530ee59aa08697b90485cb39ed245de75e36fcb27a50471feddeffb7 -size 364 +oid sha256:544dac21d0af65c94f1e38ee2b0ff5a20dd3f12c40a5c41037c12f3af54df9ca +size 6598 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png index 9937d392a0..651de32b9c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4074c82d1e98c484abf997ed07b8e56274d3fb213e98db5a72f3d6c2001215a8 -size 364 +oid sha256:dca857bfb1b6405488d96214b72e219f9aa4c4abd535cd1dd83b7efd7603c497 +size 6641 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png index 7a30344edd..451a6a11f5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a47281c07d1821618b03dcc08fd8b10154c2fa368fa84a924f9e6834b321a26d -size 364 +oid sha256:83613dcfe9968db44cabf918d6dc9ab011869c80d4bf089d5d4c46f7b6bedae4 +size 6968 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png index 5a6edaea07..3a4b4e33b9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9901fc0ec796a0e4d7c30a8d44a94bbb8759714ee2f8672d782926c945ffdb43 -size 364 +oid sha256:8d44801312b785f5c595a988124de6d70d65de01fb18c74876b1f6328e84c222 +size 6914 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png index 643222ba4f..08df2b5e02 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d03540cc3ed64cb1d6baea27cda2a13f9f01a326d49f7a48e32a055544e0e6f -size 364 +oid sha256:e884fe9fd0c212caf0bfc745206ea277ff77037356e4e94b844e1cd1100a8861 +size 6955 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png index 1a3a8bebc0..2d4ae50749 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:40dd3ea2ae03be9ef1edd14613a4a71126dd5d290e9296b1631de8d29bfe0c4e -size 364 +oid sha256:7abd5b98f55ca52bccd791196613029d2932520f062f08fd6356666b5fe1dbe8 +size 6878 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png index 3be35124de..a760810c62 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:387c101f7706c27d2cb25a4d7fef9d8c84626b860d1f74fab12f5d21116495ed -size 364 +oid sha256:cfaed204b2cc5ee8d1ec54f84c446346c62e392ad2ae305359a552f5f9a427f1 +size 6755 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png index 6e07280a10..c1a681d637 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessageEventBubbleLightPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7949d19caf0c8ba0f7ca9a46e07de5694639dabcf6860cd1aa1f95f53eb4e28 -size 364 +oid sha256:a801ced0c93a02e5fa22ca021f78d3ab1834169745353aaacbaad4bc41bbb112 +size 6834 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index c569ad3566..ef4d4b25ec 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a511056320bb3acbffbd91e2511f299c56bddaeb9998386ee83a6c8c846af333 -size 367 +oid sha256:3ad0404db281f88257ed3d343e798cd27afdb274614913f3badbbe8a47fc88e5 +size 41398 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 6c140ec1e2..b0960e54ee 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:31c8bd05947cbe3f74d217aac06b06a08b7ba328f5e3dbdac0c87ca3afe87732 -size 367 +oid sha256:420adabaf3640152a1e99cfd8cab9a64da50a1c4dc2ab8b3b048093635151318 +size 53385 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png index 2a2d363120..d0a2e7066f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7e8b8ff0aeeb517c47dfd987579b73a7e862e9a67a5100532f39087d42a4fe4 -size 46056 +oid sha256:424f2348662fc3a1e5d9bb12a6d675a8f967e43622a4f9192f825dc3f3a134f5 +size 45980 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 97151f5bf8..580880f9d4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9143abd810ae66b73f7a42084146ec40a68a4e4112f7ca62ed220bc6473cf77d -size 368 +oid sha256:a9e2ec40e67b0ae78651ae103d3046f557b3673345d60a143f06d5eb43b8e741 +size 197764 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 7ab5445424..f8c1e26d6a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5aefb5101926948e4526f776a40ee4f0e9e9577880ef015393fe274791b57ae -size 368 +oid sha256:e9b0668ebc68ec0e315a6ac3e4db4690070e8e486246e98b7fc23d4616268d42 +size 198102 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 1b9f743388..5c8d51ec12 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a48c409916255498c1e47440354c0009107858028559eb90cc6c591d57cb0ded -size 367 +oid sha256:34dbf27bf329b577a5c59271fa2bfc6ee1c9f652fb68f5b4edb5e44bba32ace0 +size 51774 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index b20e2d0ae0..360a055535 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fd56a902b27629ea24fd11f163d347df6375dd597590d163d59b733a1538c0ec -size 367 +oid sha256:4218f384064ced57840a7c20a3f5c8ab1cba226ecb5122b4cc396df9ffcfede8 +size 73519 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png index d48f11e372..aa78d719a1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8982700ec1f20d066594715a664ac7333b603feeca02a2b024314843c88559c6 -size 367 +oid sha256:dffca847b2f1790877df9a9c0794c468e8ddc0971af3e2307228863feff2cb3d +size 42911 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png index 842ea61125..e68537ac30 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b25f0497c08515125896756c89c5bc9e1757b25ec50478cd5cb67cdfd00c2dec -size 55019 +oid sha256:34513f8cbe510944522f53f90ea6cb6bedc568e4791ad1c0e3ae1dd63d0cc4a0 +size 55096 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png index d83002fedd..481abb65cd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3f22479bb2c6bd35c6f927c11ff19e644226df3d2a99b9c6b23f5bd679178d5a -size 39151 +oid sha256:075045fbb35241fecba52ff19ceb2339d93fcb4dd3d3229b5a5e7281c6b2fc90 +size 39301 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png index cc3b898a92..21f36a5449 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6920b50647111a70215c74ca27be736d6c208c0801eb4bcd323abcb33e903aea -size 56229 +oid sha256:0f5437f4635e642c9e2990ef41c40df5a939e5faba84eac19fc15f1e53ea1d29 +size 56436 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index b75f4df936..194bff61b2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d94bad50b1bc10151122baf37f0ceb37806d2116decbd0e8cc8634da13ccd48a -size 367 +oid sha256:0f73388ab1ddd48ea62269299e743c300c83ec84535d98ed9bf12e47132b6c5e +size 40880 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index c9d35cb3b3..0ccc392d07 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4ae1583a5d4b2ae3a5a8dfe4f4f7809b6b46687ad33aa0a12ade7b18bf04eabe -size 367 +oid sha256:638f7407848987d640dcb7697600ef124d830e1318806ed89dc94e59c56ce567 +size 53118 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png index bdb8e46417..233715bf0d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c50bb17bf530de6472d44232b0248c2a5761c67fde785b3aee6eb3f77fa4103e -size 45948 +oid sha256:f44841f51271e49655ecd93410d17645a0794c2d980836cf19724031231a5663 +size 45768 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index e5e62c79b7..1651935bd9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4256dc3623b650038262f227d56b598d06214fa5831e89105c08bea452e7a792 -size 368 +oid sha256:f0f3632b9ef6d76fa7233e7ed7dbcdcd362863c47446d2fc2bd7edbc4da83d37 +size 199660 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index d619dfaa04..27af9809a6 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3ad71dfc03acfc3b4a865305dda532213e8ff5f3183eb976fbc0739ede74ee7 -size 368 +oid sha256:4fe9756edcdabdcffae355fe6e9990e08bae636d6af303a8962873199cbe60c6 +size 199973 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 46d1b9d639..8ddace3d7e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b83ed834c1335ad917019656ed042b2fde17403e739066df1d29bc322f01ff36 -size 367 +oid sha256:2c587072086f938278fee42d597d279bbd927add5c77b1d61cdc6eac2db76c91 +size 51816 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 64f147681b..7bb178f4a9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc174a97059882be652fcfb87a033091ecdbe84a4e0e8a1ef3eff29101bc6b4c -size 367 +oid sha256:e679d5fc422984b5ca38aae320105d6efa88363d8025f191ab07890605b8f9e3 +size 74098 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png index ab7dc9af1f..27b687cd96 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0ec4528874e237526821edffebe957682399d5941cd502239f7f0c7044ba0cd2 -size 367 +oid sha256:ce9b51de72765b47cd9ef202012443593787badf77226a802ffa9746bcdf8ebc +size 42499 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png index 61aaf14dac..1574e39241 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d75c877c55c0f622aabee5ed8428b8a980cbc28daacb2127f2a17cc29db62a9d -size 55057 +oid sha256:61b4786185a26d74b3008ec112ec1602361086d03297553dec9f842715178e2b +size 55242 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png index 97d47f37f0..8886f1d0fd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a6edb33e04054aba49ea978d6b2c0d0c25332c4dd901cf2cadd6c368076e957d -size 38687 +oid sha256:9d3087419152bb771a75cc38f59b1684e4fceef5594669c2f7ae688d879701ba +size 38875 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png index 4bca8aaed3..1cd0e3c7af 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:996016191220540fde3d8aaa3124a074a6eaaf6e87c43bf0d57d32992faf47fc -size 56342 +oid sha256:c8dbcc16ff401ca55a27f756230fa2f2c42615ff31c952d5911ed2a9ddb5879e +size 56437 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index ecd52fd69c..a23aaa1aa9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5c1fc2f6bde122e2a7b603bcc02ace39044be876debcf06d8d649d3ee9a052b9 -size 367 +oid sha256:9edaeed6d8fff0087cbca9253d96453f289268c1acc53c12430e3d4a41dc9a54 +size 45535 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 0059811dce..b51c71886d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d43ca1f19ca846cdcab60ba2ded0b7f502ee414e6273eb724496ba506c70e99 -size 367 +oid sha256:9294a4c7c05d23af48e627e2f6fe2992c5f7d42a2dae0fcecd89d308b1bbe8ee +size 45715 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 7d136e12c5..8ad814bf31 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a3bdd036cd704d8e9e2b227a363b0d2147a4b0c8a0db8f04d11e0a6189c988d7 -size 367 +oid sha256:8c22066ff7d188508fce82666accde93eb3a73f84d7323c6c05ae35579465f65 +size 43796 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 2ce4f8cf53..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6fc67d78ba2a31540c1cbe949572fdcd98850dab0d8242146d6d73ab02dae572 -size 41017 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 55ff596385..3bf3974b35 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:609f2538ab4c9d9bc6a84f6218604c5b8752d227a33205765e83f5d36b828c8a -size 367 +oid sha256:5415abbb41df4db5974dd54b4de55e2109ea74290bb451731ff6ef905f4bd81c +size 44799 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 30156c7a0b..3ecd28e0ff 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c63d90fd878182e29b1205d8d4c007994407f9879a1ddfb0c79c3a3c6a5cd9e -size 367 +oid sha256:b516913846b769c12e437345762ff4de063abae27cf507a361d370aa92003b0c +size 45231 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 7d31c0dd3c..bdab17b4fa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da7edea058b6a8c1f6c224dd9bda45fbe0fe344610c6f2d5588ae268116fa0b2 -size 367 +oid sha256:220e6e22a3a98fa01fa5f125a226396f53beb5e1ee116c78a82bf005311127ec +size 42269 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png deleted file mode 100644 index 789bcc0c1f..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e9073c23bec50592107aa7a3b05d770c8fbf35db211578c9ca4cae399b9ba78d -size 40319 From 60593fb1d7cdc60731cc28536c38381d43ab25d5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 11:33:33 +0200 Subject: [PATCH 12/75] Add preview for TimelineEventTimestampView --- .../components/TimelineEventTimestampView.kt | 22 +++++++++++ ...melineItemEventForTimestampViewProvider.kt | 39 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt index 8358d6cc1e..f6daa68186 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt @@ -27,11 +27,15 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.features.messages.impl.timeline.model.event.TimelineItemTextBasedContent import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.designsystem.ElementTextStyles +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.ElementTheme import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text @@ -68,3 +72,21 @@ fun TimelineEventTimestampView( } } } + +@Preview +@Composable +internal fun TimelineEventTimestampViewLightPreview(@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event) = + ElementPreviewLight { ContentToPreview(event) } + +@Preview +@Composable +internal fun TimelineEventTimestampViewDarkPreview(@PreviewParameter(TimelineItemEventForTimestampViewProvider::class) event: TimelineItem.Event) = + ElementPreviewDark { ContentToPreview(event) } + +@Composable +private fun ContentToPreview(event: TimelineItem.Event) { + TimelineEventTimestampView( + event = event, + onClick = {} + ) +} diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt new file mode 100644 index 0000000000..24daed065a --- /dev/null +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineItemEventForTimestampViewProvider.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.messages.impl.timeline.components + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.impl.timeline.aTimelineItemEvent +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent +import io.element.android.libraries.matrix.api.timeline.item.event.EventSendState + +class TimelineItemEventForTimestampViewProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aTimelineItemEvent(), + // Sending failed + aTimelineItemEvent().copy(sendState = EventSendState.SendingFailed("AN_ERROR")), + // Edited + aTimelineItemEvent().copy(content = aTimelineItemTextContent().copy(isEdited = true)), + // Sending failed + Edited (not sure this is possible IRL, but should be covered by test) + aTimelineItemEvent().copy( + sendState = EventSendState.SendingFailed("AN_ERROR"), + content = aTimelineItemTextContent().copy(isEdited = true), + ), + ) +} From 03e2fa04cb11e164d3b48d189e14c3783b919409 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 11:34:34 +0200 Subject: [PATCH 13/75] Fix alignment issue when there is an error. --- .../impl/timeline/components/TimelineEventTimestampView.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt index f6daa68186..ed4febf982 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/TimelineEventTimestampView.kt @@ -25,6 +25,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Error import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview @@ -52,7 +53,10 @@ fun TimelineEventTimestampView( val hasMessageSendingFailed = event.sendState is EventSendState.SendingFailed val isMessageEdited = (event.content as? TimelineItemTextBasedContent)?.isEdited.orFalse() val tint = if (hasMessageSendingFailed) ElementTheme.colors.textActionCritical else null - Row(modifier = modifier.clickable(onClick = onClick)) { + Row( + modifier = modifier.clickable(onClick = onClick), + verticalAlignment = Alignment.CenterVertically, + ) { if (isMessageEdited) { Text( stringResource(R.string.common_edited_suffix), From 85f7a6aeb4c2caea77ae31d52955a13ef598a853 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 11:36:31 +0200 Subject: [PATCH 14/75] Record screenshots --- ...EventTimestampViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...EventTimestampViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...EventTimestampViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 3 +++ ...EventTimestampViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 3 +++ ...ventTimestampViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ventTimestampViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...ventTimestampViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 3 +++ ...ventTimestampViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 3 +++ ...Group_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...Group_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...oup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png | 4 ++-- ...roup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...Group_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...roup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 ++-- 36 files changed, 80 insertions(+), 56 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_3,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b244bdb73f --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdf7fe891aa40f4d626733deb130481d64b3315531712128d1b4073e5ccedf19 +size 5394 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..25c4fffbab --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0baf33f0ec99cd8e4b201d72b38208a02b0511da544276a38d17ff5653e2b754 +size 5906 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5aa7211ab3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c8d9dc01ec07f9e43032e3ec7610eda5588145f3003cc3d329ddf4325e19f239 +size 6674 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2ed9e07432 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5e0f86449d651c50966fe594a46e3a59ae9a5505013e3e36f2458be35ba1844e +size 7174 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f640d9af42 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:621facc4799c0069fe83b4be9a5085ecafb042de1cdfbbf4ccf0c15548373aa9 +size 5332 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8ed48c31f6 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:986f40750d69639e771c9bf1aac30c49dbf122aa60dc0280164a8868671b638a +size 5910 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9735300d61 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d4fb8d68603309963d434f3166821055d818836b1ebfb4fc4c637aba992277d +size 6617 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d265165330 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineEventTimestampViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1fa641ca8d5c796c858b54ad997052aabf817bc86e254d38d4b33038bc21eedf +size 7285 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index ef4d4b25ec..105b94a19e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3ad0404db281f88257ed3d343e798cd27afdb274614913f3badbbe8a47fc88e5 -size 41398 +oid sha256:121e9eb7013123e5647e351eda7d331ade8032ae5aa56bd1be943ee6c6acad53 +size 41444 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index b0960e54ee..b579039d11 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:420adabaf3640152a1e99cfd8cab9a64da50a1c4dc2ab8b3b048093635151318 -size 53385 +oid sha256:e19a24fa27305822af8ec3a4dfac727eb1297ccbbeffec726664c6bc29b1a2a4 +size 53421 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png index d0a2e7066f..2a39d3fa2d 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:424f2348662fc3a1e5d9bb12a6d675a8f967e43622a4f9192f825dc3f3a134f5 -size 45980 +oid sha256:9ab84b59725483b7187e91f3df18d1d55decbef7da37e186099027c6c98e3430 +size 46068 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 580880f9d4..ec8687992b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9e2ec40e67b0ae78651ae103d3046f557b3673345d60a143f06d5eb43b8e741 -size 197764 +oid sha256:5b388b6664ea0e5a86d6506a954cbcdc36c39054204739365eb0228b2e1d4fd4 +size 197778 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index f8c1e26d6a..9467cabc80 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e9b0668ebc68ec0e315a6ac3e4db4690070e8e486246e98b7fc23d4616268d42 -size 198102 +oid sha256:dbeca10cc6a489dd20390bbc3bf5f2fa4f726342083621b8161a2a4448622ffc +size 198122 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 5c8d51ec12..22b7e81257 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34dbf27bf329b577a5c59271fa2bfc6ee1c9f652fb68f5b4edb5e44bba32ace0 -size 51774 +oid sha256:23ed23fbc4a47152c4d9b6373111c7de5497900e9ccb3382f99364bde2444032 +size 51724 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 360a055535..c71a28244b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4218f384064ced57840a7c20a3f5c8ab1cba226ecb5122b4cc396df9ffcfede8 -size 73519 +oid sha256:8a715939660ec1a2bea4c5a3e9d0a3b4b0bc45f137fae4c3210476145346a3a9 +size 73507 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png index aa78d719a1..2fbafcf338 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dffca847b2f1790877df9a9c0794c468e8ddc0971af3e2307228863feff2cb3d -size 42911 +oid sha256:f3094dab56dd2c46a5c34162e717afcf674823be080959ec5e852227946237ff +size 42954 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png index e68537ac30..d89fe8b6dd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:34513f8cbe510944522f53f90ea6cb6bedc568e4791ad1c0e3ae1dd63d0cc4a0 -size 55096 +oid sha256:44534d8513114e666e7faa341a81184420fd84e8c3723c2d17236949f31e2fdb +size 55118 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png index 481abb65cd..fc9cacdcf3 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:075045fbb35241fecba52ff19ceb2339d93fcb4dd3d3229b5a5e7281c6b2fc90 -size 39301 +oid sha256:f988cd33ae74aa292f26f0c6f11b5fcec8ff298005423161921e912a5e416c97 +size 39341 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png index 21f36a5449..61e274ef18 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f5437f4635e642c9e2990ef41c40df5a939e5faba84eac19fc15f1e53ea1d29 -size 56436 +oid sha256:9a65842cd12943594ed28ffba0a958ff076d6c6492533bfc5ffd395b915b2b30 +size 56467 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 194bff61b2..d33815af64 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0f73388ab1ddd48ea62269299e743c300c83ec84535d98ed9bf12e47132b6c5e -size 40880 +oid sha256:f7822ba6ecc9e7cf7bfdb4446b6796dce685d2417f6ed29b6ef5015438e09ace +size 40935 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 0ccc392d07..ff256d7186 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:638f7407848987d640dcb7697600ef124d830e1318806ed89dc94e59c56ce567 -size 53118 +oid sha256:d3d8fae3d49c61850eef15c762d1f9615a735ca1943e78aa4dc116eaa830c02a +size 53182 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png index 233715bf0d..b2207d8ccb 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f44841f51271e49655ecd93410d17645a0794c2d980836cf19724031231a5663 -size 45768 +oid sha256:ebc4e13d35c918274f35445db57955780bfdc037d5bd85a98eddb9b3bac42d5e +size 45846 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 1651935bd9..dabf581ac8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f0f3632b9ef6d76fa7233e7ed7dbcdcd362863c47446d2fc2bd7edbc4da83d37 -size 199660 +oid sha256:ed3f266ce286216deca3747ded2b46bd7ef37c707145d1ae676e8afe227850b2 +size 199651 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index 27af9809a6..ca627ffc7f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4fe9756edcdabdcffae355fe6e9990e08bae636d6af303a8962873199cbe60c6 -size 199973 +oid sha256:685a607ef2c644b8844a1a83bc9427040f3890b4b9f777aa80592c98f0a96d87 +size 199963 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 8ddace3d7e..1ddc9bb4b0 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2c587072086f938278fee42d597d279bbd927add5c77b1d61cdc6eac2db76c91 -size 51816 +oid sha256:d5975a89ee4cf263317079be95b6f6cc199bfbbcf75ac88d622989bfd859c813 +size 51801 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 7bb178f4a9..213b36fafb 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e679d5fc422984b5ca38aae320105d6efa88363d8025f191ab07890605b8f9e3 -size 74098 +oid sha256:0375a01da0c470b8379d2edcfb282b4b55f5cee86dc9da3efbe66fbadf7019a5 +size 74079 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png index 27b687cd96..4360de3629 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ce9b51de72765b47cd9ef202012443593787badf77226a802ffa9746bcdf8ebc -size 42499 +oid sha256:f085dfb5b0147374af21b8df3f4cd2b9930e638188add6f3a4fa94008747c7ee +size 42553 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png index 1574e39241..0f0e1ba1aa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:61b4786185a26d74b3008ec112ec1602361086d03297553dec9f842715178e2b -size 55242 +oid sha256:7ec8c64bb4500bce08725d74753f45db93a369095626064ecdf9a83122aea326 +size 55285 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png index 8886f1d0fd..ecf8b2e64c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d3087419152bb771a75cc38f59b1684e4fceef5594669c2f7ae688d879701ba -size 38875 +oid sha256:54046d58d89dd8f44e8e2fdb83306682fd6305a32ba91c06c9bcd042e99bbdd3 +size 38928 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png index 1cd0e3c7af..6d8f83611f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c8dbcc16ff401ca55a27f756230fa2f2c42615ff31c952d5911ed2a9ddb5879e -size 56437 +oid sha256:a96004c7012bd1b83eb395511472a6c93f410f9504656f4e3621d7809eccca9f +size 56483 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index a23aaa1aa9..f1bc47d485 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9edaeed6d8fff0087cbca9253d96453f289268c1acc53c12430e3d4a41dc9a54 -size 45535 +oid sha256:49759fbb46a966d0e0f1c793545e320e7588ac482399a12ac45762474e6781f8 +size 45579 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index b51c71886d..c28a1d3ac7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9294a4c7c05d23af48e627e2f6fe2992c5f7d42a2dae0fcecd89d308b1bbe8ee -size 45715 +oid sha256:cdca9e94c071c20753aa2076f6c3a7ed931a2c1e90a78c29e00f27f7af99dfe8 +size 45758 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 8ad814bf31..6864f509ba 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8c22066ff7d188508fce82666accde93eb3a73f84d7323c6c05ae35579465f65 -size 43796 +oid sha256:4e47966820a6a77efece51aaac5adbe3dd1dad1880b1feb7da2e86e36b386f7e +size 43815 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 3bf3974b35..7009f86be7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5415abbb41df4db5974dd54b4de55e2109ea74290bb451731ff6ef905f4bd81c -size 44799 +oid sha256:bc3fc4c97a43c5786a75328e35828ed0e0cfd74ffab673832053ecc11db9b5e0 +size 44852 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 3ecd28e0ff..823b19bf1c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b516913846b769c12e437345762ff4de063abae27cf507a361d370aa92003b0c -size 45231 +oid sha256:7f82a51b6b6ed4ac030c4602da9da2629010169e998aaba16f9e05c652f6010d +size 45283 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index bdab17b4fa..49d46e6e23 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:220e6e22a3a98fa01fa5f125a226396f53beb5e1ee116c78a82bf005311127ec -size 42269 +oid sha256:5d18843f61267ae7385c23113be5c5c3d4376d95a843eef126ed670877f69b80 +size 42281 From 28ff9ea437210802881acb6c3e990876aeeed638 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 11:43:38 +0200 Subject: [PATCH 15/75] Reduce number of warnings --- .../messages/attachments/AttachmentsPreviewPresenterTest.kt | 3 +++ .../features/messages/media/viewer/MediaViewerPresenterTest.kt | 3 +++ .../android/tests/testutils/TestCoroutineDispatchers.kt | 3 +++ 3 files changed, 9 insertions(+) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt index 0b16a254a1..36983979fc 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package io.element.android.features.messages.attachments import androidx.media3.common.MimeTypes @@ -33,6 +35,7 @@ import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt index 16f2894303..fb5d8edf72 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package io.element.android.features.messages.media.viewer import androidx.media3.common.MimeTypes @@ -29,6 +31,7 @@ import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS import io.element.android.libraries.matrix.test.media.FakeMediaLoader import io.element.android.libraries.matrix.test.media.aMediaSource +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test diff --git a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/TestCoroutineDispatchers.kt b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/TestCoroutineDispatchers.kt index a19dd629ae..4628f4910c 100644 --- a/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/TestCoroutineDispatchers.kt +++ b/tests/testutils/src/main/kotlin/io/element/android/tests/testutils/TestCoroutineDispatchers.kt @@ -14,9 +14,12 @@ * limitations under the License. */ +@file:OptIn(ExperimentalCoroutinesApi::class) + package io.element.android.tests.testutils import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.StandardTestDispatcher import kotlinx.coroutines.test.TestCoroutineScheduler import kotlinx.coroutines.test.UnconfinedTestDispatcher From de4c27a4decd2ad6b958b2d0c7cf8ee3e14c1f5f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 10:18:22 +0000 Subject: [PATCH 16/75] Update dependency com.squareup:kotlinpoet to v1.14.2 (#474) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- anvilcodegen/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anvilcodegen/build.gradle.kts b/anvilcodegen/build.gradle.kts index 6712bddee0..e592d98b2e 100644 --- a/anvilcodegen/build.gradle.kts +++ b/anvilcodegen/build.gradle.kts @@ -23,7 +23,7 @@ dependencies { implementation(projects.anvilannotations) api(libs.anvil.compiler.api) implementation(libs.anvil.compiler.utils) - implementation("com.squareup:kotlinpoet:1.13.2") + implementation("com.squareup:kotlinpoet:1.14.2") implementation(libs.dagger) compileOnly("com.google.auto.service:auto-service-annotations:1.1.0") kapt("com.google.auto.service:auto-service:1.1.0") From 4a9b92a6fe6eabb9af2cd0c73ec6cdd481f4feae Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 10:20:06 +0000 Subject: [PATCH 17/75] Update dependency app.cash.turbine:turbine to v0.13.0 (#417) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 49823363bc..3b14b0d7ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -118,7 +118,7 @@ test_mockk = "io.mockk:mockk:1.13.5" test_barista = "com.adevinta.android:barista:4.3.0" test_hamcrest = "org.hamcrest:hamcrest:2.2" test_orchestrator = "androidx.test:orchestrator:1.4.2" -test_turbine = "app.cash.turbine:turbine:0.12.3" +test_turbine = "app.cash.turbine:turbine:0.13.0" test_truth = "com.google.truth:truth:1.1.3" test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.12" test_robolectric = "org.robolectric:robolectric:4.10.3" From f202791d1865db0c172dd1cfd4fe124c983775d3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 13:06:33 +0200 Subject: [PATCH 18/75] Trigger CI From 29fcfcf68c7ab2bd78f7c2a28f9ced52f3700db6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 11:18:51 +0000 Subject: [PATCH 19/75] Update dependency io.gitlab.arturbosch.detekt to v1.23.0 (#454) * Update dependency io.gitlab.arturbosch.detekt to v1.23.0 * Disable new detekt rules for unused params UnusedParameter and UnusedPrivateProperty are both potentially useful but we currently break them a lot, for the most part in places that haven't been fully implemented yet. The unused params are reported as build warnings anyway, so we're not missing much by disabling these for now. --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Chris Smith --- gradle/libs.versions.toml | 2 +- tools/detekt/detekt.yml | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3b14b0d7ca..39f033a491 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -49,7 +49,7 @@ dagger = "2.46.1" anvil = "2.4.6" # quality -detekt = "1.22.0" +detekt = "1.23.0" dependencygraph = "0.10" [libraries] diff --git a/tools/detekt/detekt.yml b/tools/detekt/detekt.yml index e77443fcab..dc797aace1 100644 --- a/tools/detekt/detekt.yml +++ b/tools/detekt/detekt.yml @@ -15,6 +15,12 @@ style: UnusedPrivateMember: # TODO Enable it active: false + UnusedParameter: + # TODO Enable it + active: false + UnusedPrivateProperty: + # TODO Enable it + active: false ThrowsCount: active: false LoopWithTooManyJumpStatements: From 67649507ff1c13b47e19c0baa5680ca6489cc497 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 11:19:11 +0000 Subject: [PATCH 20/75] Update dependency com.google.firebase:firebase-bom to v32.1.0 (#470) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 39f033a491..0a9f2a21e3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -59,7 +59,7 @@ android_gradle_plugin = { module = "com.android.tools.build:gradle", version.ref android_desugar = "com.android.tools:desugar_jdk_libs:2.0.3" kotlin_gradle_plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } # https://firebase.google.com/docs/android/setup#available-libraries -google_firebase_bom = "com.google.firebase:firebase-bom:32.0.0" +google_firebase_bom = "com.google.firebase:firebase-bom:32.1.0" # AndroidX androidx_material = { module = "com.google.android.material:material", version.ref = "material" } From 33d90104dda0aa38784663dd767a5003fc0805f6 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 11:19:20 +0000 Subject: [PATCH 21/75] Update plugin sonarqube to v4.1.0.3113 (#469) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0a9f2a21e3..9783cb1cf7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -178,6 +178,6 @@ ktlint = "org.jlleitschuh.gradle.ktlint:11.3.2" dependencygraph = { id = "com.savvasdalkitsis.module-dependency-graph", version.ref = "dependencygraph" } dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencycheck" } paparazzi = "app.cash.paparazzi:1.2.0" -sonarqube = "org.sonarqube:4.0.0.2929" +sonarqube = "org.sonarqube:4.1.0.3113" kover = "org.jetbrains.kotlinx.kover:0.6.1" sqldelight = { id = "com.squareup.sqldelight", version.ref = "sqldelight" } From ff1147e611ff89b88d253a8d1a7c3085c896281a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 13:55:23 +0200 Subject: [PATCH 22/75] Sync localazy to fix lint error. --- .../src/main/res/values-fr/translations.xml | 8 ++--- .../src/main/res/values-fr/translations.xml | 8 ++--- .../src/main/res/values-fr/translations.xml | 34 +++---------------- .../src/main/res/values-cs/translations.xml | 2 +- .../src/main/res/values-fr/translations.xml | 4 --- 5 files changed, 10 insertions(+), 46 deletions(-) diff --git a/features/messages/impl/src/main/res/values-fr/translations.xml b/features/messages/impl/src/main/res/values-fr/translations.xml index 310d1eeaa4..2eb6016f2f 100644 --- a/features/messages/impl/src/main/res/values-fr/translations.xml +++ b/features/messages/impl/src/main/res/values-fr/translations.xml @@ -1,12 +1,8 @@ - "Aucun changement dans la salle" - "%1$d changement dans la salle" - "%1$d changements dans la salle" - "%1$d changements dans la salle" - "%1$d changements dans la salle" - "%1$d changements dans la salle" + "%1$d changement dans la conversation" + "%1$d changements dans la conversation" "Prendre une photo" \ No newline at end of file diff --git a/features/roomdetails/impl/src/main/res/values-fr/translations.xml b/features/roomdetails/impl/src/main/res/values-fr/translations.xml index 081e7b1a14..56e76a2053 100644 --- a/features/roomdetails/impl/src/main/res/values-fr/translations.xml +++ b/features/roomdetails/impl/src/main/res/values-fr/translations.xml @@ -1,12 +1,8 @@ - "vide" - "1 personne" - "%1$d personnes" - "%1$d personnes" - "%1$d personnes" - "%1$d personnes" + "1 membre" + "%1$d membres" "Les messages sont sécurisés par des verrous. Seuls vous et les destinataires possédez les clés uniques pour les déverrouiller." "Chiffrement des messages activé" diff --git a/libraries/push/impl/src/main/res/values-fr/translations.xml b/libraries/push/impl/src/main/res/values-fr/translations.xml index 00e15fc57f..3d40a01065 100644 --- a/libraries/push/impl/src/main/res/values-fr/translations.xml +++ b/libraries/push/impl/src/main/res/values-fr/translations.xml @@ -17,52 +17,28 @@ "%1$s dans %2$s" "%1$s dans %2$s et %3$s" - "%1$s: %2$d message" "%1$s: %2$d message" - "%1$s: %2$d messages" - "%1$s: %2$d messages" - "%1$s: %2$d messages" "%1$s: %2$d messages" - "%d notification" "%d notification" - "%d notifications" - "%d notification" - "%d notification" - "%d notification" + "%d notifications" - "%d invitation" "%d invitation" - "%d invitations" - "%d invitations" - "%d invitations" "%d invitations" - "Aucun nouveau message" - "%d nouveaux message" - "%d nouveaux message" - "%d nouveaux message" - "%d nouveaux message" - "%d nouveaux message" + "%d nouveau message" + "%d nouveaux messages" - "Aucun message notifié non lu" "%d message notifié non lu" - "%d messages notifiés non lus" - "%d messages notifiés non lus" - "%d messages notifiés non lus" "%d messages notifiés non lus" - "%d salle" - "%d salle" - "%d salles" - "%d salles" - "%d salles" - "%d salles" + "%d conversation" + "%d conversations" "Choisissez comment recevoir les notifications" "Synchronisation en arrière-plan" diff --git a/libraries/ui-strings/src/main/res/values-cs/translations.xml b/libraries/ui-strings/src/main/res/values-cs/translations.xml index f8fd5fd4df..efa4d006d7 100644 --- a/libraries/ui-strings/src/main/res/values-cs/translations.xml +++ b/libraries/ui-strings/src/main/res/values-cs/translations.xml @@ -63,7 +63,7 @@ "Možnosti pro vývojáře" "(upraveno)" "Úpravy" - "* %1$@ %2$@" + "* %1$s %2$s" "Šifrování povoleno" "Chyba" "Soubor" diff --git a/libraries/ui-strings/src/main/res/values-fr/translations.xml b/libraries/ui-strings/src/main/res/values-fr/translations.xml index ffddafb618..76111046f8 100644 --- a/libraries/ui-strings/src/main/res/values-fr/translations.xml +++ b/libraries/ui-strings/src/main/res/values-fr/translations.xml @@ -121,11 +121,7 @@ "Êtes-vous sûr de vouloir quitter la salle ?" "%1$s Android" - "Aucun membre" "%1$d membre" - "%1$d membres" - "%1$d membres" - "%1$d membres" "%1$d membres" "Rageshake pour signaler un bug" From 8a750f3ae49a0143ff1e8445880881e046c42fc3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 12:05:25 +0200 Subject: [PATCH 23/75] Comment out hard-coded sliding sync url. --- .../element/android/libraries/matrix/impl/RustMatrixClient.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index bac98dd852..8f4b353e2e 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -153,7 +153,7 @@ class RustMatrixClient constructor( private val slidingSync = client .slidingSync() - .homeserver("https://slidingsync.lab.matrix.org") + // .homeserver("https://slidingsync.lab.matrix.org") .withCommonExtensions() .storageKey("ElementX") .addList(visibleRoomsSlidingSyncListBuilder) From 111cca99bff104c9af05099245ae3796f47f5a9c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 14:11:20 +0200 Subject: [PATCH 24/75] Add `EventType` to avoid using hard-coding strings. --- .../api/timeline/item/event/EventType.kt | 99 +++++++++++++++++++ .../libraries/matrix/impl/RustMatrixClient.kt | 13 +-- .../impl/timeline/RustMatrixTimeline.kt | 9 +- .../notifications/NotifiableEventProcessor.kt | 3 +- .../model/NotifiableMessageEvent.kt | 3 +- .../NotifiableEventProcessorTest.kt | 3 +- 6 files changed, 117 insertions(+), 13 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventType.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventType.kt new file mode 100644 index 0000000000..8141528f34 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/item/event/EventType.kt @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.api.timeline.item.event + +/** + * Constants defining known event types from Matrix specifications. + */ +object EventType { + const val PRESENCE = "m.presence" + const val MESSAGE = "m.room.message" + const val STICKER = "m.sticker" + const val ENCRYPTED = "m.room.encrypted" + const val FEEDBACK = "m.room.message.feedback" + const val TYPING = "m.typing" + const val REDACTION = "m.room.redaction" + const val RECEIPT = "m.receipt" + const val ROOM_KEY = "m.room_key" + const val PLUMBING = "m.room.plumbing" + const val BOT_OPTIONS = "m.room.bot.options" + const val PREVIEW_URLS = "org.matrix.room.preview_urls" + + // State Events + + const val STATE_ROOM_WIDGET_LEGACY = "im.vector.modular.widgets" + const val STATE_ROOM_WIDGET = "m.widget" + const val STATE_ROOM_NAME = "m.room.name" + const val STATE_ROOM_TOPIC = "m.room.topic" + const val STATE_ROOM_AVATAR = "m.room.avatar" + const val STATE_ROOM_MEMBER = "m.room.member" + const val STATE_ROOM_THIRD_PARTY_INVITE = "m.room.third_party_invite" + const val STATE_ROOM_CREATE = "m.room.create" + const val STATE_ROOM_JOIN_RULES = "m.room.join_rules" + const val STATE_ROOM_GUEST_ACCESS = "m.room.guest_access" + const val STATE_ROOM_POWER_LEVELS = "m.room.power_levels" + + const val STATE_SPACE_CHILD = "m.space.child" + const val STATE_SPACE_PARENT = "m.space.parent" + + /** + * Note that this Event has been deprecated, see + * - https://matrix.org/docs/spec/client_server/r0.6.1#historical-events + * - https://github.com/matrix-org/matrix-doc/pull/2432 + */ + const val STATE_ROOM_ALIASES = "m.room.aliases" + const val STATE_ROOM_TOMBSTONE = "m.room.tombstone" + const val STATE_ROOM_CANONICAL_ALIAS = "m.room.canonical_alias" + const val STATE_ROOM_HISTORY_VISIBILITY = "m.room.history_visibility" + const val STATE_ROOM_RELATED_GROUPS = "m.room.related_groups" + const val STATE_ROOM_PINNED_EVENT = "m.room.pinned_events" + const val STATE_ROOM_ENCRYPTION = "m.room.encryption" + const val STATE_ROOM_SERVER_ACL = "m.room.server_acl" + + // Call Events + const val CALL_INVITE = "m.call.invite" + const val CALL_CANDIDATES = "m.call.candidates" + const val CALL_ANSWER = "m.call.answer" + const val CALL_SELECT_ANSWER = "m.call.select_answer" + const val CALL_NEGOTIATE = "m.call.negotiate" + const val CALL_REJECT = "m.call.reject" + const val CALL_HANGUP = "m.call.hangup" + + // This type is not processed by the client, just sent to the server + const val CALL_REPLACES = "m.call.replaces" + + // Key share events + const val ROOM_KEY_REQUEST = "m.room_key_request" + const val FORWARDED_ROOM_KEY = "m.forwarded_room_key" + + const val REQUEST_SECRET = "m.secret.request" + const val SEND_SECRET = "m.secret.send" + + // Relation Events + const val REACTION = "m.reaction" + + fun isCallEvent(type: String): Boolean { + return type == CALL_INVITE || + type == CALL_CANDIDATES || + type == CALL_ANSWER || + type == CALL_HANGUP || + type == CALL_SELECT_ANSWER || + type == CALL_NEGOTIATE || + type == CALL_REJECT || + type == CALL_REPLACES + } +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 8f4b353e2e..4c3e895100 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.api.pusher.PushersService import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource +import io.element.android.libraries.matrix.api.timeline.item.event.EventType import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.api.verification.SessionVerificationService @@ -114,9 +115,9 @@ class RustMatrixClient constructor( .timelineLimit(limit = 1u) .requiredState( requiredState = listOf( - RequiredState(key = "m.room.avatar", value = ""), - RequiredState(key = "m.room.encryption", value = ""), - RequiredState(key = "m.room.join_rules", value = ""), + RequiredState(key = EventType.STATE_ROOM_AVATAR, value = ""), + RequiredState(key = EventType.STATE_ROOM_ENCRYPTION, value = ""), + RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""), ) ) .filters(visibleRoomsSlidingSyncFilters) @@ -136,9 +137,9 @@ class RustMatrixClient constructor( .timelineLimit(limit = 1u) .requiredState( requiredState = listOf( - RequiredState(key = "m.room.avatar", value = ""), - RequiredState(key = "m.room.encryption", value = ""), - RequiredState(key = "m.room.canonical_alias", value = ""), + RequiredState(key = EventType.STATE_ROOM_AVATAR, value = ""), + RequiredState(key = EventType.STATE_ROOM_ENCRYPTION, value = ""), + RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""), ) ) .filters(invitesSlidingSyncFilters) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt index 4884dc2d84..923815a714 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustMatrixTimeline.kt @@ -21,6 +21,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem +import io.element.android.libraries.matrix.api.timeline.item.event.EventType import io.element.android.libraries.matrix.impl.timeline.item.event.EventMessageMapper import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper @@ -149,10 +150,10 @@ class RustMatrixTimeline( runCatching { val settings = RoomSubscription( requiredState = listOf( - RequiredState(key = "m.room.canonical_alias", value = ""), - RequiredState(key = "m.room.topic", value = ""), - RequiredState(key = "m.room.join_rules", value = ""), - RequiredState(key = "m.room.power_levels", value = ""), + RequiredState(key = EventType.STATE_ROOM_CANONICAL_ALIAS, value = ""), + RequiredState(key = EventType.STATE_ROOM_TOPIC, value = ""), + RequiredState(key = EventType.STATE_ROOM_JOIN_RULES, value = ""), + RequiredState(key = EventType.STATE_ROOM_POWER_LEVELS, value = ""), ), timelineLimit = null ) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventProcessor.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventProcessor.kt index 1b1fe2723e..07c3a99708 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventProcessor.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventProcessor.kt @@ -16,6 +16,7 @@ package io.element.android.libraries.push.impl.notifications +import io.element.android.libraries.matrix.api.timeline.item.event.EventType import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent @@ -49,7 +50,7 @@ class NotifiableEventProcessor @Inject constructor( else -> ProcessedEvent.Type.KEEP } is SimpleNotifiableEvent -> when (it.type) { - /*EventType.REDACTION*/ "m.room.redaction" -> ProcessedEvent.Type.REMOVE + EventType.REDACTION -> ProcessedEvent.Type.REMOVE else -> ProcessedEvent.Type.KEEP } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt index add172e44f..d7af528ce3 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId +import io.element.android.libraries.matrix.api.timeline.item.event.EventType import io.element.android.services.appnavstate.api.AppNavigationState import io.element.android.services.appnavstate.api.currentRoomId import io.element.android.services.appnavstate.api.currentSessionId @@ -52,7 +53,7 @@ data class NotifiableMessageEvent( override val isUpdated: Boolean = false ) : NotifiableEvent { - val type: String = /* EventType.MESSAGE */ "m.room.message" + val type: String = EventType.MESSAGE val description: String = body ?: "" val title: String = senderName ?: "" diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventProcessorTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventProcessorTest.kt index 80246abb14..38f2edd476 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventProcessorTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventProcessorTest.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.push.impl.notifications import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.timeline.item.event.EventType import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 import io.element.android.libraries.matrix.test.A_ROOM_ID @@ -60,7 +61,7 @@ class NotifiableEventProcessorTest { @Test fun `given redacted simple event when processing then remove redaction event`() { - val events = listOf(aSimpleNotifiableEvent(eventId = AN_EVENT_ID, type = "m.room.redaction")) + val events = listOf(aSimpleNotifiableEvent(eventId = AN_EVENT_ID, type = EventType.REDACTION)) val result = eventProcessor.process(events, appNavigationState = NOT_VIEWING_A_ROOM, renderedEvents = emptyList()) From f1d2f566bcdc2304b5659f0be438040faca67fb3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Apr 2023 21:24:07 +0200 Subject: [PATCH 25/75] Add OIDC support --- docs/oidc.md | 45 +++++++ .../features/login/impl/LoginFlowNode.kt | 14 ++ .../features/login/impl/oidc/OidcEvents.kt | 23 ++++ .../features/login/impl/oidc/OidcNode.kt | 58 +++++++++ .../features/login/impl/oidc/OidcPresenter.kt | 97 ++++++++++++++ .../features/login/impl/oidc/OidcState.kt | 26 ++++ .../login/impl/oidc/OidcStateProvider.kt | 39 ++++++ .../features/login/impl/oidc/OidcUrlParser.kt | 47 +++++++ .../features/login/impl/oidc/OidcView.kt | 121 ++++++++++++++++++ .../login/impl/oidc/OidcWebViewClient.kt | 42 ++++++ .../login/impl/oidc/WebViewEventListener.kt | 29 +++++ .../features/login/impl/root/LoginRootNode.kt | 11 +- .../login/impl/root/LoginRootPresenter.kt | 28 +++- .../login/impl/root/LoginRootState.kt | 9 +- .../login/impl/root/LoginRootStateProvider.kt | 8 +- .../features/login/impl/root/LoginRootView.kt | 65 ++++++---- .../login/impl/util/LoginConstants.kt | 3 +- .../api/auth/AuthenticationException.kt | 1 + .../api/auth/MatrixAuthenticationService.kt | 19 +++ .../api/auth/MatrixHomeServerDetails.kt | 2 +- .../libraries/matrix/api/auth/OidcConfig.kt | 21 +++ .../libraries/matrix/api/auth/OidcDetails.kt | 25 ++++ .../impl/auth/AuthenticationException.kt | 7 + .../matrix/impl/auth/HomeserverDetails.kt | 2 +- .../libraries/matrix/impl/auth/OidcConfig.kt | 29 +++++ .../auth/RustMatrixAuthenticationService.kt | 62 ++++++++- 26 files changed, 789 insertions(+), 44 deletions(-) create mode 100644 docs/oidc.md create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcState.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcStateProvider.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcWebViewClient.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/WebViewEventListener.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcConfig.kt create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcDetails.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt diff --git a/docs/oidc.md b/docs/oidc.md new file mode 100644 index 0000000000..7feae3ce83 --- /dev/null +++ b/docs/oidc.md @@ -0,0 +1,45 @@ +[ios implementation](https://github.com/vector-im/element-x-ios/compare/develop...doug/oidc-temp) + +Rust sdk branch: https://github.com/matrix-org/matrix-rust-sdk/tree/oidc-ffi + +Figma https://www.figma.com/file/o9p34zmiuEpZRyvZXJZAYL/FTUE?node-id=133-5426&t=yQXKeANatk6keoZF-0 + +Server list: https://github.com/vector-im/oidc-playground + +Metadata iOS: (from https://github.com/vector-im/element-x-ios/blob/5f9d07377cebc4f21d9668b1a25f6e3bb22f64a1/ElementX/Sources/Services/Authentication/AuthenticationServiceProxy.swift#L28) + +clientName: InfoPlistReader.main.bundleDisplayName, +redirectUri: "io.element:/callback", +clientUri: "https://element.io", +tosUri: "https://element.io/user-terms-of-service", +policyUri: "https://element.io/privacy" + + +Android: +clientName = "Element", +redirectUri = "io.element:/callback", +clientUri = "https://element.io", +tosUri = "https://element.io/user-terms-of-service", +policyUri = "https://element.io/privacy" + + +Example of OidcData (from presentUrl callback): +url: https://auth-oidc.lab.element.dev/authorize?response_type=code&client_id=01GYCAGG3PA70CJ97ZVP0WFJY3&redirect_uri=io.element%3A%2Fcallback&scope=openid+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Aapi%3A*+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Adevice%3AYAgcPW4mcG&state=ex6mNJVFZ5jn9wL8&nonce=NZ93DOyIGQd9exPQ&code_challenge_method=S256&code_challenge=FFRcPALNSPCh-ZgpyTRFu_h8NZJVncfvihbfT9CyX8U&prompt=consent + +Formatted url: +https://auth-oidc.lab.element.dev/authorize? + response_type=code& + client_id=01GYCAGG3PA70CJ97ZVP0WFJY3& + redirect_uri=io.element%3A%2Fcallback& + scope=openid+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Aapi%3A*+urn%3Amatrix%3Aorg.matrix.msc2967.client%3Adevice%3AYAgcPW4mcG& + state=ex6mNJVFZ5jn9wL8& + nonce=NZ93DOyIGQd9exPQ& + code_challenge_method=S256& + code_challenge=FFRcPALNSPCh-ZgpyTRFu_h8NZJVncfvihbfT9CyX8U& + prompt=consent + +state: ex6mNJVFZ5jn9wL8 + + +Oidc client example: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/examples/oidc_cli/src/main.rs +Oidc sdk doc: https://github.com/matrix-org/matrix-rust-sdk/blob/39ad8a46801fb4317a777ebf895822b3675b709c/crates/matrix-sdk/src/oidc.rs diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index dcdaf1a347..37cfec229b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -29,11 +29,13 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.login.impl.changeserver.ChangeServerNode +import io.element.android.features.login.impl.oidc.OidcNode import io.element.android.features.login.impl.root.LoginRootNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.auth.OidcDetails import kotlinx.parcelize.Parcelize @ContributesNode(AppScope::class) @@ -55,6 +57,9 @@ class LoginFlowNode @AssistedInject constructor( @Parcelize object ChangeServer : NavTarget + + @Parcelize + data class OidcView(val oidcDetails: OidcDetails) : NavTarget } override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { @@ -64,10 +69,19 @@ class LoginFlowNode @AssistedInject constructor( override fun onChangeHomeServer() { backstack.push(NavTarget.ChangeServer) } + + override fun onOidcDetails(oidcDetails: OidcDetails) { + backstack.push(NavTarget.OidcView(oidcDetails)) + } } createNode(buildContext, plugins = listOf(callback)) } + NavTarget.ChangeServer -> createNode(buildContext) + is NavTarget.OidcView -> { + val input = OidcNode.Inputs(navTarget.oidcDetails) + createNode(buildContext, plugins = listOf(input)) + } } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt new file mode 100644 index 0000000000..0c40f457bf --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +sealed interface OidcEvents { + object Cancel : OidcEvents + data class OidcActionEvent(val oidcAction: OidcAction): OidcEvents + object ClearError : OidcEvents +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt new file mode 100644 index 0000000000..0122ead10c --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.architecture.inputs +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.auth.OidcDetails + +/** + * TODO Transmit back press to the webview + */ +@ContributesNode(AppScope::class) +class OidcNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + presenterFactory: OidcPresenter.Factory, +) : Node(buildContext, plugins = plugins) { + + data class Inputs( + val oidcDetails: OidcDetails, + ) : NodeInputs + + private val inputs: Inputs = inputs() + private val presenter = presenterFactory.create(inputs.oidcDetails) + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + OidcView( + state = state, + modifier = modifier, + onNavigateBack = ::navigateUp, + ) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt new file mode 100644 index 0000000000..76faa090d1 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService +import io.element.android.libraries.matrix.api.auth.OidcDetails +import kotlinx.coroutines.launch + +class OidcPresenter @AssistedInject constructor( + @Assisted private val oidcDetails: OidcDetails, + private val authenticationService: MatrixAuthenticationService, +) : Presenter { + + @AssistedFactory + interface Factory { + fun create(oidcDetails: OidcDetails): OidcPresenter + } + + @Composable + override fun present(): OidcState { + var requestState: Async by remember { + mutableStateOf(Async.Uninitialized) + } + val localCoroutineScope = rememberCoroutineScope() + + fun handleCancel() { + requestState = Async.Loading() + localCoroutineScope.launch { + requestState = try { + authenticationService.cancelOidcLogin() + // Then go back + Async.Success(Unit) + } catch (throwable: Throwable) { + Async.Failure(throwable) + } + } + } + + fun handleSuccess(url: String) { + requestState = Async.Loading() + localCoroutineScope.launch { + try { + authenticationService.loginWithOidc(url) + // Then the node tree will be updated, there is nothing to do + } catch (throwable: Throwable) { + requestState = Async.Failure(throwable) + } + } + } + + fun handleAction(action: OidcAction) { + when (action) { + OidcAction.GoBack -> handleCancel() + is OidcAction.Success -> handleSuccess(action.url) + } + } + + fun handleEvents(event: OidcEvents) { + when (event) { + OidcEvents.Cancel -> handleCancel() + is OidcEvents.OidcActionEvent -> handleAction(event.oidcAction) + OidcEvents.ClearError -> requestState = Async.Uninitialized + } + } + + return OidcState( + oidcDetails = oidcDetails, + requestState = requestState, + eventSink = ::handleEvents + ) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcState.kt new file mode 100644 index 0000000000..e9b2ac2355 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcState.kt @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.auth.OidcDetails + +data class OidcState( + val oidcDetails: OidcDetails, + val requestState: Async, + val eventSink: (OidcEvents) -> Unit +) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcStateProvider.kt new file mode 100644 index 0000000000..7a5552e719 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcStateProvider.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.auth.OidcDetails + +open class OidcStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aOidcState(), + aOidcState().copy(requestState = Async.Loading()), + ) +} + +fun aOidcState() = OidcState( + oidcDetails = aOidcDetails(), + requestState = Async.Uninitialized, + eventSink = {} +) + +fun aOidcDetails() = OidcDetails( + url = "aUrl", +) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt new file mode 100644 index 0000000000..eb55368f47 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import io.element.android.libraries.matrix.api.auth.OidcConfig + +/** + * Simple parser for oidc url interception. + * TODO Find documentation about the format. + */ +class OidcUrlParser { + + // When user press button "Cancel", we get the url: + // `io.element:/callback?error=access_denied&state=IFF1UETGye2ZA8pO` + // On success, we get: + // `io.element:/callback?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB` + /** + * Return a OidcAction, or null if the url is not a OidcUrl + */ + fun parse(url: String): OidcAction? { + if (!url.startsWith(OidcConfig.redirectUri)) return null + if (url.contains("error=access_denied")) return OidcAction.GoBack + if (url.contains("code=")) return OidcAction.Success(url) + + // Other case not supported, let's crash the app for now + error("Not supported: $url") + } +} + +sealed interface OidcAction { + object GoBack : OidcAction + data class Success(val url: String) : OidcAction +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt new file mode 100644 index 0000000000..06591409c9 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import android.webkit.WebView +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.viewinterop.AndroidView +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.core.bool.orFalse +import io.element.android.libraries.core.bool.orTrue +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator + +@Composable +fun OidcView( + state: OidcState, + onNavigateBack: () -> Unit, + modifier: Modifier = Modifier, +) { + val oidcUrlParser = remember { OidcUrlParser() } + var webView: WebView? = null + fun shouldOverrideUrl(url: String): Boolean { + val action = oidcUrlParser.parse(url) + if (action != null) { + state.eventSink.invoke(OidcEvents.OidcActionEvent(action)) + return true + } + return false + } + + val oidcWebViewClient = remember { + OidcWebViewClient(eventListener = object : WebViewEventListener { + override fun shouldOverrideUrlLoading(url: String): Boolean { + return shouldOverrideUrl(url) + } + }) + } + + BackHandler { + if (webView?.canGoBack().orFalse()) { + webView?.goBack() + } else { + // To properly cancel Oidc login + state.eventSink.invoke(OidcEvents.Cancel) + } + } + + Box(modifier = modifier.statusBarsPadding()) { + AndroidView( + modifier = modifier + .statusBarsPadding(), + factory = { context -> + WebView(context).apply { + webViewClient = oidcWebViewClient + loadUrl(state.oidcDetails.url) + }.also { + webView = it + } + } + ) + + when (state.requestState) { + Async.Uninitialized -> Unit + is Async.Failure -> { + ErrorDialog( + content = state.requestState.error.toString(), + onDismiss = { state.eventSink(OidcEvents.ClearError) } + ) + } + is Async.Loading -> { + // Indeterminate indicator, to avoid the freeze effect if the connection takes time to initialize. + CircularProgressIndicator( + modifier = Modifier.align(Alignment.Center) + ) + } + is Async.Success -> onNavigateBack() + } + } +} + +@Preview +@Composable +fun OidcViewLightPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) = + ElementPreviewLight { ContentToPreview(state) } + +@Preview +@Composable +fun OidcViewDarkPreview(@PreviewParameter(OidcStateProvider::class) state: OidcState) = + ElementPreviewDark { ContentToPreview(state) } + +@Composable +private fun ContentToPreview(state: OidcState) { + OidcView( + state = state, + onNavigateBack = { }, + ) +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcWebViewClient.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcWebViewClient.kt new file mode 100644 index 0000000000..bd7ab99ad6 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcWebViewClient.kt @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import android.annotation.TargetApi +import android.os.Build +import android.webkit.WebResourceRequest +import android.webkit.WebView +import android.webkit.WebViewClient +import timber.log.Timber + +// TODO Move to a dedicated module +class OidcWebViewClient(private val eventListener: WebViewEventListener) : WebViewClient() { + @TargetApi(Build.VERSION_CODES.N) + override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { + return shouldOverrideUrl(request.url.toString()) + } + + @Deprecated("Deprecated in Java") + override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { + return shouldOverrideUrl(url) + } + + private fun shouldOverrideUrl(url: String): Boolean { + Timber.d("shouldOverrideUrl: $url") + return eventListener.shouldOverrideUrlLoading(url) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/WebViewEventListener.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/WebViewEventListener.kt new file mode 100644 index 0000000000..91fde6c311 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/WebViewEventListener.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +interface WebViewEventListener { + /** + * Triggered when a Webview loads an url. + * + * @param url The url about to be rendered. + * @return true if the method needs to manage some custom handling + */ + fun shouldOverrideUrlLoading(url: String): Boolean { + return false + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootNode.kt index fce214f2e4..787f5d0b48 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootNode.kt @@ -26,6 +26,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.di.AppScope +import io.element.android.libraries.matrix.api.auth.OidcDetails @ContributesNode(AppScope::class) class LoginRootNode @AssistedInject constructor( @@ -36,20 +37,26 @@ class LoginRootNode @AssistedInject constructor( interface Callback : Plugin { fun onChangeHomeServer() + fun onOidcDetails(oidcDetails: OidcDetails) } private fun onChangeHomeServer() { plugins().forEach { it.onChangeHomeServer() } } + private fun onOidcDetails(oidcDetails: OidcDetails) { + plugins().forEach { it.onOidcDetails(oidcDetails) } + } + @Composable override fun View(modifier: Modifier) { val state = presenter.present() LoginRootView( state = state, modifier = modifier, - onChangeServer = this::onChangeHomeServer, - onBackPressed = this::navigateUp + onChangeServer = ::onChangeHomeServer, + onOidcDetails = ::onOidcDetails, + onBackPressed = ::navigateUp ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt index c9a75365f1..fd3d293870 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt @@ -33,7 +33,11 @@ import javax.inject.Inject class LoginRootPresenter @Inject constructor(private val authenticationService: MatrixAuthenticationService) : Presenter { - private val defaultHomeserver = MatrixHomeServerDetails(LoginConstants.DEFAULT_HOMESERVER_URL, true, null) + private val defaultHomeserver = MatrixHomeServerDetails( + url = LoginConstants.DEFAULT_HOMESERVER_URL, + supportsPasswordLogin = true, + supportsOidc = false, + ) @Composable override fun present(): LoginRootState { @@ -54,7 +58,12 @@ class LoginRootPresenter @Inject constructor(private val authenticationService: is LoginRootEvents.SetPassword -> updateFormState(formState) { copy(password = event.password) } - LoginRootEvents.Submit -> localCoroutineScope.submit(homeserver.url, formState.value, loggedInState) + LoginRootEvents.Submit -> { + when { + homeserver.supportsOidc -> localCoroutineScope.submitOidc(homeserver.url, loggedInState) + homeserver.supportsPasswordLogin -> localCoroutineScope.submit(homeserver.url, formState.value, loggedInState) + } + } LoginRootEvents.ClearError -> loggedInState.value = LoggedInState.NotLoggedIn } } @@ -67,9 +76,22 @@ class LoginRootPresenter @Inject constructor(private val authenticationService: ) } + private fun CoroutineScope.submitOidc(homeserver: String, loggedInState: MutableState) = launch { + loggedInState.value = LoggedInState.LoggingIn + // TODO rework the setHomeserver flow + authenticationService.setHomeserver(homeserver) + authenticationService.getOidcUrl() + .onSuccess { + loggedInState.value = LoggedInState.OidcStarted(it) + } + .onFailure { failure -> + loggedInState.value = LoggedInState.ErrorLoggingIn(failure) + } + } + private fun CoroutineScope.submit(homeserver: String, formState: LoginFormState, loggedInState: MutableState) = launch { loggedInState.value = LoggedInState.LoggingIn - //TODO rework the setHomeserver flow + // TODO rework the setHomeserver flow authenticationService.setHomeserver(homeserver) authenticationService.login(formState.login.trim(), formState.password) .onSuccess { sessionId -> diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt index 34e903dd0e..884a3037df 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt @@ -18,6 +18,7 @@ package io.element.android.features.login.impl.root import android.os.Parcelable import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails +import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.parcelize.Parcelize @@ -27,13 +28,17 @@ data class LoginRootState( val formState: LoginFormState, val eventSink: (LoginRootEvents) -> Unit ) { - val submitEnabled: Boolean get() = - formState.login.isNotEmpty() && formState.password.isNotEmpty() && loggedInState !is LoggedInState.ErrorLoggingIn + val supportPasswordLogin = homeserverDetails.supportsPasswordLogin + val supportOidcLogin = homeserverDetails.supportsOidc + val submitEnabled: Boolean + get() = loggedInState !is LoggedInState.ErrorLoggingIn && + ((formState.login.isNotEmpty() && formState.password.isNotEmpty()) || supportOidcLogin) } sealed interface LoggedInState { object NotLoggedIn : LoggedInState object LoggingIn : LoggedInState + data class OidcStarted(val oidcDetail: OidcDetails) : LoggedInState data class ErrorLoggingIn(val failure: Throwable) : LoggedInState data class LoggedIn(val sessionId: SessionId) : LoggedInState } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt index 9864bf2380..cdb92bcfaf 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt @@ -24,16 +24,20 @@ open class LoginRootStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLoginRootState(), - aLoginRootState().copy(homeserverDetails = MatrixHomeServerDetails("some-custom-server.com", true, null)), + aLoginRootState().copy(homeserverDetails = MatrixHomeServerDetails("some-custom-server.com", supportsPasswordLogin = true, supportsOidc = false)), aLoginRootState().copy(formState = LoginFormState("user", "pass")), aLoginRootState().copy(formState = LoginFormState("user", "pass"), loggedInState = LoggedInState.LoggingIn), aLoginRootState().copy(formState = LoginFormState("user", "pass"), loggedInState = LoggedInState.ErrorLoggingIn(Throwable())), aLoginRootState().copy(formState = LoginFormState("user", "pass"), loggedInState = LoggedInState.LoggedIn(SessionId("@user:domain"))), + // Oidc + aLoginRootState().copy(homeserverDetails = MatrixHomeServerDetails("server-with-oidc.org", supportsPasswordLogin = false, supportsOidc = true)), + // No password, no oidc support + aLoginRootState().copy(homeserverDetails = MatrixHomeServerDetails("wrong.org", supportsPasswordLogin = false, supportsOidc = false)), ) } fun aLoginRootState() = LoginRootState( - homeserverDetails = MatrixHomeServerDetails("matrix.org", true, null), + homeserverDetails = MatrixHomeServerDetails("matrix.org", supportsPasswordLogin = true, supportsOidc = false), loggedInState = LoggedInState.NotLoggedIn, formState = LoginFormState.Default, eventSink = {} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootView.kt index b72122c914..c8c85ed50f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootView.kt @@ -83,7 +83,7 @@ import io.element.android.libraries.designsystem.theme.components.TextField import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.theme.components.autofill import io.element.android.libraries.designsystem.theme.components.onTabOrEnterKeyFocusNext -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.testtags.TestTags import io.element.android.libraries.testtags.testTag import io.element.android.libraries.ui.strings.R as StringR @@ -94,7 +94,7 @@ fun LoginRootView( state: LoginRootState, modifier: Modifier = Modifier, onChangeServer: () -> Unit = {}, - onLoginWithSuccess: (SessionId) -> Unit = {}, + onOidcDetails: (OidcDetails) -> Unit = {}, onBackPressed: () -> Unit, ) { val isLoading by remember(state.loggedInState) { @@ -102,6 +102,15 @@ fun LoginRootView( state.loggedInState == LoggedInState.LoggingIn } } + val focusManager = LocalFocusManager.current + + fun submit() { + // Clear focus to prevent keyboard issues with textfields + focusManager.clearFocus(force = true) + + state.eventSink(LoginRootEvents.Submit) + } + Scaffold( topBar = { TopAppBar( @@ -143,13 +152,37 @@ fun LoginRootView( Spacer(Modifier.height(32.dp)) - LoginForm(state = state, isLoading = isLoading) + when { + state.supportOidcLogin -> { + // Oidc, in this case, just display a Spacer and the submit button + Spacer(Modifier.height(28.dp)) + } + state.supportPasswordLogin -> { + LoginForm(state = state, isLoading = isLoading, onSubmit = ::submit) + } + else -> { + Text(text = "No supported login flow") + } + } - Spacer(modifier = Modifier.height(32.dp)) + Spacer(Modifier.height(28.dp)) + if (state.supportOidcLogin || state.supportPasswordLogin) { + // Submit + ButtonWithProgress( + text = stringResource(R.string.screen_login_submit), + showProgress = isLoading, + onClick = ::submit, + enabled = state.submitEnabled, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.loginContinue) + ) + Spacer(modifier = Modifier.height(32.dp)) + } } when (val loggedInState = state.loggedInState) { - is LoggedInState.LoggedIn -> onLoginWithSuccess(loggedInState.sessionId) + is LoggedInState.OidcStarted -> onOidcDetails(loggedInState.oidcDetail) else -> Unit } } @@ -217,6 +250,7 @@ internal fun ChangeServerSection( internal fun LoginForm( state: LoginRootState, isLoading: Boolean, + onSubmit: () -> Unit, modifier: Modifier = Modifier ) { var loginFieldState by textFieldState(stateValue = state.formState.login) @@ -225,13 +259,6 @@ internal fun LoginForm( val focusManager = LocalFocusManager.current val eventSink = state.eventSink - fun submit() { - // Clear focus to prevent keyboard issues with textfields - focusManager.clearFocus(force = true) - - eventSink(LoginRootEvents.Submit) - } - Column(modifier) { Text( text = stringResource(R.string.screen_login_form_header), @@ -318,23 +345,11 @@ internal fun LoginForm( imeAction = ImeAction.Done, ), keyboardActions = KeyboardActions( - onDone = { submit() } + onDone = { onSubmit() } ), singleLine = true, maxLines = 1, ) - Spacer(Modifier.height(28.dp)) - - // Submit - ButtonWithProgress( - text = stringResource(R.string.screen_login_submit), - showProgress = isLoading, - onClick = ::submit, - enabled = state.submitEnabled, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginContinue) - ) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt index 8e2c37904f..c481fdf927 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt @@ -18,7 +18,6 @@ package io.element.android.features.login.impl.util object LoginConstants { - const val DEFAULT_HOMESERVER_URL = "matrix.org" + const val DEFAULT_HOMESERVER_URL = "synapse-oidc.lab.element.dev" // "matrix.org" const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md" - } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt index 4c943db0d6..e670e02f11 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/AuthenticationException.kt @@ -22,4 +22,5 @@ sealed class AuthenticationException(message: String) : Exception(message) { class SlidingSyncNotAvailable(message: String) : AuthenticationException(message) class SessionMissing(message: String) : AuthenticationException(message) class Generic(message: String) : AuthenticationException(message) + class OidcError(type: String, message: String) : AuthenticationException(message) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt index 3414c4fb6e..c15153876c 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixAuthenticationService.kt @@ -28,4 +28,23 @@ interface MatrixAuthenticationService { fun getHomeserverDetails(): StateFlow suspend fun setHomeserver(homeserver: String): Result suspend fun login(username: String, password: String): Result + + /* + * OIDC part. + */ + + /** + * Get the Oidc url to display to the user. + */ + suspend fun getOidcUrl(): Result + + /** + * Cancel Oidc login sequence. + */ + suspend fun cancelOidcLogin(): Result + + /** + * Attempt to login using the [callbackUrl] provided by the Oidc page. + */ + suspend fun loginWithOidc(callbackUrl: String): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt index 7f2b074c67..90c54cfe2e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt @@ -23,5 +23,5 @@ import kotlinx.parcelize.Parcelize data class MatrixHomeServerDetails( val url: String, val supportsPasswordLogin: Boolean, - val authenticationIssuer: String? + val supportsOidc: Boolean, ): Parcelable diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcConfig.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcConfig.kt new file mode 100644 index 0000000000..ae473885da --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcConfig.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.api.auth + +object OidcConfig { + const val redirectUri = "io.element:/callback" +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcDetails.kt new file mode 100644 index 0000000000..34c926c8e6 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/OidcDetails.kt @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.api.auth + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class OidcDetails( + val url: String, +) : Parcelable diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt index 96e3a367cb..b98ecc193f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt @@ -26,6 +26,13 @@ fun Throwable.mapAuthenticationException(): Throwable { is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(this.message!!) is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(this.message!!) is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(this.message!!) + + is RustAuthenticationException.OidcException -> AuthenticationException.OidcError("OidcException", message!!) + is RustAuthenticationException.OidcMetadataInvalid -> AuthenticationException.OidcError("OidcMetadataInvalid", message!!) + is RustAuthenticationException.OidcMetadataMissing -> AuthenticationException.OidcError("OidcMetadataMissing", message!!) + is RustAuthenticationException.OidcNotStarted -> AuthenticationException.OidcError("OidcNotStarted", message!!) + is RustAuthenticationException.OidcNotSupported -> AuthenticationException.OidcError("OidcNotSupported", message!!) + else -> this } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt index e2f3cfe676..9ca3d78456 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt @@ -23,6 +23,6 @@ fun HomeserverLoginDetails.map(): MatrixHomeServerDetails = use { MatrixHomeServerDetails( url = url(), supportsPasswordLogin = supportsPasswordLogin(), - authenticationIssuer = authenticationIssuer() + supportsOidc = supportsOidcLogin(), ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt new file mode 100644 index 0000000000..774c22781b --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.impl.auth + +import io.element.android.libraries.matrix.api.auth.OidcConfig +import org.matrix.rustcomponents.sdk.OidcClientMetadata + +val oidcClientMetadata: OidcClientMetadata = OidcClientMetadata( + clientName = "Element", + redirectUri = OidcConfig.redirectUri, + clientUri = "https://element.io", + tosUri = "https://element.io/user-terms-of-service", + policyUri = "https://element.io/privacy" +) + diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index aff83c3b5a..b0409cfba3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -24,8 +24,8 @@ import io.element.android.libraries.di.SingleIn import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails +import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.impl.RustMatrixClient import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.libraries.sessionstorage.api.SessionStore @@ -36,6 +36,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder +import org.matrix.rustcomponents.sdk.OidcAuthenticationUrl import org.matrix.rustcomponents.sdk.Session import org.matrix.rustcomponents.sdk.use import java.io.File @@ -51,7 +52,12 @@ class RustMatrixAuthenticationService @Inject constructor( private val sessionStore: SessionStore, ) : MatrixAuthenticationService { - private val authService: RustAuthenticationService = RustAuthenticationService(baseDirectory.absolutePath, null, null) + private val authService: RustAuthenticationService = RustAuthenticationService( + basePath = baseDirectory.absolutePath, + passphrase = null, + oidcClientMetadata = oidcClientMetadata, + customSlidingSyncProxy = null + ) private var currentHomeserver = MutableStateFlow(null) override fun isLoggedIn(): Flow { @@ -91,9 +97,9 @@ class RustMatrixAuthenticationService @Inject constructor( if (homeServerDetails != null) { currentHomeserver.value = homeServerDetails.copy(url = homeserver) } + }.mapFailure { failure -> + failure.mapAuthenticationException() } - }.mapFailure { failure -> - failure.mapAuthenticationException() } override suspend fun login(username: String, password: String): Result = @@ -103,11 +109,55 @@ class RustMatrixAuthenticationService @Inject constructor( val sessionData = client.use { it.session().toSessionData() } sessionStore.storeData(sessionData) SessionId(sessionData.userId) + }.mapFailure { failure -> + failure.mapAuthenticationException() } - }.mapFailure { failure -> - failure.mapAuthenticationException() } + private var pendingUrlForOidcLogin: OidcAuthenticationUrl? = null + + override suspend fun getOidcUrl(): Result { + return withContext(coroutineDispatchers.io) { + runCatching { + val urlForOidcLogin = authService.urlForOidcLogin() + val url = urlForOidcLogin.loginUrl() + pendingUrlForOidcLogin = urlForOidcLogin + OidcDetails(url) + }.mapFailure { failure -> + failure.mapAuthenticationException() + } + } + } + + override suspend fun cancelOidcLogin(): Result { + return withContext(coroutineDispatchers.io) { + runCatching { + pendingUrlForOidcLogin?.close() + pendingUrlForOidcLogin = null + }.mapFailure { failure -> + failure.mapAuthenticationException() + } + } + } + + /** + * callbackUrl should be the uriRedirect from OidcClientMetadata (with all the parameters) + */ + override suspend fun loginWithOidc(callbackUrl: String): Result { + return withContext(coroutineDispatchers.io) { + runCatching { + val urlForOidcLogin = pendingUrlForOidcLogin ?: error("You need to call `getOidcUrl()` first") + val client = authService.loginWithOidcCallback(urlForOidcLogin, callbackUrl) + val sessionData = client.use { it.session().toSessionData() } + pendingUrlForOidcLogin = null + sessionStore.storeData(sessionData) + SessionId(sessionData.userId) + }.mapFailure { failure -> + failure.mapAuthenticationException() + } + } + } + private fun createMatrixClient(client: Client): MatrixClient { return RustMatrixClient( client = client, From 9551a5e6f8fe8f1d4d27c9e83b3e822f113891d3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Apr 2023 16:13:02 +0200 Subject: [PATCH 26/75] Rework the set homeserver part: get the info, instead of hard-coded value, and implement retry in case of error. --- .../login/impl/root/LoginRootEvents.kt | 1 + .../login/impl/root/LoginRootPresenter.kt | 58 ++++++++++---- .../login/impl/root/LoginRootState.kt | 9 ++- .../login/impl/root/LoginRootStateProvider.kt | 40 +++++++++- .../features/login/impl/root/LoginRootView.kt | 77 ++++++++++++------- .../components/async/AsyncFailure.kt | 70 +++++++++++++++++ .../components/async/AsyncLoading.kt | 54 +++++++++++++ ...lurePreviewDark_0_null,NEXUS_5,1.0,en].png | 3 + ...urePreviewLight_0_null,NEXUS_5,1.0,en].png | 3 + ...dingPreviewDark_0_null,NEXUS_5,1.0,en].png | 3 + ...ingPreviewLight_0_null,NEXUS_5,1.0,en].png | 3 + 11 files changed, 270 insertions(+), 51 deletions(-) create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt create mode 100644 libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncLoading.kt create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncLoadingPreviewDark_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncLoadingPreviewLight_0_null,NEXUS_5,1.0,en].png diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootEvents.kt index d3f8738dc7..5aa5071876 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootEvents.kt @@ -17,6 +17,7 @@ package io.element.android.features.login.impl.root sealed interface LoginRootEvents { + object RetryFetchServerInfo : LoginRootEvents data class SetLogin(val login: String) : LoginRootEvents data class SetPassword(val password: String) : LoginRootEvents object Submit : LoginRootEvents diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt index fd3d293870..0d1b8de7fd 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt @@ -17,6 +17,7 @@ package io.element.android.features.login.impl.root import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState import androidx.compose.runtime.collectAsState import androidx.compose.runtime.mutableStateOf @@ -24,25 +25,38 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.features.login.impl.util.LoginConstants +import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.execute import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch import javax.inject.Inject -class LoginRootPresenter @Inject constructor(private val authenticationService: MatrixAuthenticationService) : Presenter { - - private val defaultHomeserver = MatrixHomeServerDetails( - url = LoginConstants.DEFAULT_HOMESERVER_URL, - supportsPasswordLogin = true, - supportsOidc = false, - ) +class LoginRootPresenter @Inject constructor( + private val authenticationService: MatrixAuthenticationService, +) : Presenter { @Composable override fun present(): LoginRootState { val localCoroutineScope = rememberCoroutineScope() - val homeserver = authenticationService.getHomeserverDetails().collectAsState().value ?: defaultHomeserver + val currentHomeServerDetails = authenticationService.getHomeserverDetails().collectAsState().value + val homeserver = currentHomeServerDetails?.url ?: LoginConstants.DEFAULT_HOMESERVER_URL + val getHomeServerDetailsAction: MutableState> = remember { + if (currentHomeServerDetails != null) { + mutableStateOf(Async.Success(currentHomeServerDetails)) + } else { + mutableStateOf(Async.Uninitialized) + } + } + + LaunchedEffect(Unit) { + if (currentHomeServerDetails == null) { + getHomeServerDetails(homeserver, getHomeServerDetailsAction) + } + } + val loggedInState: MutableState = remember { mutableStateOf(LoggedInState.NotLoggedIn) } @@ -52,6 +66,7 @@ class LoginRootPresenter @Inject constructor(private val authenticationService: fun handleEvents(event: LoginRootEvents) { when (event) { + LoginRootEvents.RetryFetchServerInfo -> localCoroutineScope.getHomeServerDetails(homeserver, getHomeServerDetailsAction) is LoginRootEvents.SetLogin -> updateFormState(formState) { copy(login = event.login) } @@ -59,9 +74,10 @@ class LoginRootPresenter @Inject constructor(private val authenticationService: copy(password = event.password) } LoginRootEvents.Submit -> { + val homeServerDetails = getHomeServerDetailsAction.value.dataOrNull() ?: return when { - homeserver.supportsOidc -> localCoroutineScope.submitOidc(homeserver.url, loggedInState) - homeserver.supportsPasswordLogin -> localCoroutineScope.submit(homeserver.url, formState.value, loggedInState) + homeServerDetails.supportsOidc -> localCoroutineScope.submitOidc(loggedInState) + homeServerDetails.supportsPasswordLogin -> localCoroutineScope.submit(formState.value, loggedInState) } } LoginRootEvents.ClearError -> loggedInState.value = LoggedInState.NotLoggedIn @@ -69,17 +85,27 @@ class LoginRootPresenter @Inject constructor(private val authenticationService: } return LoginRootState( - homeserverDetails = homeserver, + homeserverUrl = homeserver, + homeserverDetails = getHomeServerDetailsAction.value, loggedInState = loggedInState.value, formState = formState.value, eventSink = ::handleEvents ) } - private fun CoroutineScope.submitOidc(homeserver: String, loggedInState: MutableState) = launch { + private fun CoroutineScope.getHomeServerDetails( + homeserver: String, + state: MutableState>, + ) = launch { + state.value = Async.Loading() + suspend { + authenticationService.setHomeserver(homeserver) + authenticationService.getHomeserverDetails().value!! + }.execute(state) + } + + private fun CoroutineScope.submitOidc(loggedInState: MutableState) = launch { loggedInState.value = LoggedInState.LoggingIn - // TODO rework the setHomeserver flow - authenticationService.setHomeserver(homeserver) authenticationService.getOidcUrl() .onSuccess { loggedInState.value = LoggedInState.OidcStarted(it) @@ -89,10 +115,8 @@ class LoginRootPresenter @Inject constructor(private val authenticationService: } } - private fun CoroutineScope.submit(homeserver: String, formState: LoginFormState, loggedInState: MutableState) = launch { + private fun CoroutineScope.submit(formState: LoginFormState, loggedInState: MutableState) = launch { loggedInState.value = LoggedInState.LoggingIn - // TODO rework the setHomeserver flow - authenticationService.setHomeserver(homeserver) authenticationService.login(formState.login.trim(), formState.password) .onSuccess { sessionId -> loggedInState.value = LoggedInState.LoggedIn(sessionId) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt index 884a3037df..ccf7523b1b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt @@ -17,19 +17,22 @@ package io.element.android.features.login.impl.root import android.os.Parcelable +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.core.SessionId import kotlinx.parcelize.Parcelize data class LoginRootState( - val homeserverDetails: MatrixHomeServerDetails, + val homeserverUrl: String, + val homeserverDetails: Async, val loggedInState: LoggedInState, val formState: LoginFormState, val eventSink: (LoginRootEvents) -> Unit ) { - val supportPasswordLogin = homeserverDetails.supportsPasswordLogin - val supportOidcLogin = homeserverDetails.supportsOidc + val supportPasswordLogin = (homeserverDetails as? Async.Success)?.state?.supportsPasswordLogin.orFalse() + val supportOidcLogin = (homeserverDetails as? Async.Success)?.state?.supportsOidc.orFalse() val submitEnabled: Boolean get() = loggedInState !is LoggedInState.ErrorLoggingIn && ((formState.login.isNotEmpty() && formState.password.isNotEmpty()) || supportOidcLogin) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt index cdb92bcfaf..cffa395ea0 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt @@ -17,6 +17,7 @@ package io.element.android.features.login.impl.root import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.core.SessionId @@ -24,20 +25,51 @@ open class LoginRootStateProvider : PreviewParameterProvider { override val values: Sequence get() = sequenceOf( aLoginRootState(), - aLoginRootState().copy(homeserverDetails = MatrixHomeServerDetails("some-custom-server.com", supportsPasswordLogin = true, supportsOidc = false)), + aLoginRootState().copy( + homeserverDetails = Async.Success( + MatrixHomeServerDetails( + "some-custom-server.com", + supportsPasswordLogin = true, + supportsOidc = false + ) + ) + ), aLoginRootState().copy(formState = LoginFormState("user", "pass")), aLoginRootState().copy(formState = LoginFormState("user", "pass"), loggedInState = LoggedInState.LoggingIn), aLoginRootState().copy(formState = LoginFormState("user", "pass"), loggedInState = LoggedInState.ErrorLoggingIn(Throwable())), aLoginRootState().copy(formState = LoginFormState("user", "pass"), loggedInState = LoggedInState.LoggedIn(SessionId("@user:domain"))), // Oidc - aLoginRootState().copy(homeserverDetails = MatrixHomeServerDetails("server-with-oidc.org", supportsPasswordLogin = false, supportsOidc = true)), + aLoginRootState().copy( + homeserverUrl = "server-with-oidc.org", + homeserverDetails = Async.Success( + MatrixHomeServerDetails( + "server-with-oidc.org", + supportsPasswordLogin = false, + supportsOidc = true + ) + ) + ), // No password, no oidc support - aLoginRootState().copy(homeserverDetails = MatrixHomeServerDetails("wrong.org", supportsPasswordLogin = false, supportsOidc = false)), + aLoginRootState().copy( + homeserverUrl = "wrong.org", + homeserverDetails = Async.Success( + MatrixHomeServerDetails( + "wrong.org", + supportsPasswordLogin = false, + supportsOidc = false + ) + ) + ), + // Loading + aLoginRootState().copy(homeserverDetails = Async.Loading()), + //Error + aLoginRootState().copy(homeserverDetails = Async.Failure(Exception("An error occurred"))), ) } fun aLoginRootState() = LoginRootState( - homeserverDetails = MatrixHomeServerDetails("matrix.org", supportsPasswordLogin = true, supportsOidc = false), + homeserverUrl = "matrix.org", + homeserverDetails = Async.Success(MatrixHomeServerDetails("matrix.org", supportsPasswordLogin = true, supportsOidc = false)), loggedInState = LoggedInState.NotLoggedIn, formState = LoginFormState.Default, eventSink = {} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootView.kt index c8c85ed50f..2444418725 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootView.kt @@ -68,7 +68,10 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.login.impl.R import io.element.android.features.login.impl.error.loginError +import io.element.android.libraries.architecture.Async import io.element.android.libraries.designsystem.ElementTextStyles +import io.element.android.libraries.designsystem.components.async.AsyncFailure +import io.element.android.libraries.designsystem.components.async.AsyncLoading import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.button.ButtonWithProgress import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog @@ -146,39 +149,22 @@ fun LoginRootView( ChangeServerSection( interactionEnabled = !isLoading, - homeserver = state.homeserverDetails.url, + homeserver = state.homeserverUrl, onChangeServer = onChangeServer ) Spacer(Modifier.height(32.dp)) - when { - state.supportOidcLogin -> { - // Oidc, in this case, just display a Spacer and the submit button - Spacer(Modifier.height(28.dp)) - } - state.supportPasswordLogin -> { - LoginForm(state = state, isLoading = isLoading, onSubmit = ::submit) - } - else -> { - Text(text = "No supported login flow") - } - } - - Spacer(Modifier.height(28.dp)) - - if (state.supportOidcLogin || state.supportPasswordLogin) { - // Submit - ButtonWithProgress( - text = stringResource(R.string.screen_login_submit), - showProgress = isLoading, - onClick = ::submit, - enabled = state.submitEnabled, - modifier = Modifier - .fillMaxWidth() - .testTag(TestTags.loginContinue) + when (state.homeserverDetails) { + Async.Uninitialized, + is Async.Loading -> AsyncLoading() + is Async.Failure -> AsyncFailure( + throwable = state.homeserverDetails.error, + onRetry = { + state.eventSink.invoke(LoginRootEvents.RetryFetchServerInfo) + } ) - Spacer(modifier = Modifier.height(32.dp)) + is Async.Success -> ServerDetailForm(state, isLoading, ::submit) } } when (val loggedInState = state.loggedInState) { @@ -195,6 +181,43 @@ fun LoginRootView( } } +@Composable +fun ServerDetailForm( + state: LoginRootState, + isLoading: Boolean, + submit: () -> Unit, + modifier: Modifier = Modifier, +) { + when { + state.supportOidcLogin -> { + // Oidc, in this case, just display a Spacer and the submit button + Spacer(modifier.height(28.dp)) + } + state.supportPasswordLogin -> { + LoginForm(state = state, isLoading = isLoading, onSubmit = submit, modifier = modifier) + } + else -> { + Text(modifier = modifier, text = "No supported login flow") + } + } + + Spacer(Modifier.height(28.dp)) + + if (state.supportOidcLogin || state.supportPasswordLogin) { + // Submit + ButtonWithProgress( + text = stringResource(R.string.screen_login_submit), + showProgress = isLoading, + onClick = submit, + enabled = state.submitEnabled, + modifier = Modifier + .fillMaxWidth() + .testTag(TestTags.loginContinue) + ) + Spacer(modifier = Modifier.height(32.dp)) + } +} + @Composable internal fun ChangeServerSection( interactionEnabled: Boolean, diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt new file mode 100644 index 0000000000..5863f4c80c --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncFailure.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.components.async + +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.Button +import io.element.android.libraries.designsystem.theme.components.Text + +@Composable +fun AsyncFailure( + throwable: Throwable, + onRetry: (() -> Unit)?, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + .padding(vertical = 32.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Text(text = throwable.message ?: "An error occurred") + if (onRetry != null) { + Spacer(modifier = Modifier.height(24.dp)) + Button(onClick = onRetry) { + Text(text = "Retry") + } + } + } +} + +@Preview +@Composable +internal fun AsyncFailurePreviewLight() = ElementPreviewLight { ContentToPreview() } + +@Preview +@Composable +internal fun AsyncFailurePreviewDark() = ElementPreviewDark { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + AsyncFailure( + throwable = IllegalStateException("An error occurred"), + onRetry = {} + ) +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncLoading.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncLoading.kt new file mode 100644 index 0000000000..f63d34fd9b --- /dev/null +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/async/AsyncLoading.kt @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.designsystem.components.async + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator + +@Composable +fun AsyncLoading(modifier: Modifier = Modifier) { + Box( + modifier = modifier + .fillMaxWidth() + .height(120.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } +} + +@Preview +@Composable +internal fun AsyncLoadingPreviewLight() = ElementPreviewLight { ContentToPreview() } + +@Preview +@Composable +internal fun AsyncLoadingPreviewDark() = ElementPreviewDark { ContentToPreview() } + +@Composable +private fun ContentToPreview() { + AsyncLoading() +} diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..dea5ca7bfc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewDark_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ee096c4234d5f983e84113058518af756ff03696ed5a733ec7540a842ff59ba4 +size 11435 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8356990393 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncFailurePreviewLight_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2e2f7614737130cb1a23105faab7e46b39580eadcd05b6d7f811ddef525bef11 +size 10640 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncLoadingPreviewDark_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncLoadingPreviewDark_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..54cbdbcbed --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncLoadingPreviewDark_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:226b5056efcf7371c76729caf4105d94f1d945f40f1d6715d538ed19154ea0f0 +size 4971 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncLoadingPreviewLight_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncLoadingPreviewLight_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..29817c3dd2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components.async_null_DefaultGroup_AsyncLoadingPreviewLight_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59580eab9625ce67761ab8674f2adb0d9e4f0d2b27bbc132f60ae021828fd558 +size 4644 From 6ad14aa223c61993d43314c00c77842d435c7384 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Tue, 30 May 2023 14:57:03 +0100 Subject: [PATCH 27/75] Fix flaky media tests. (#479) Creating a mock during the test run seems to sometimes take a large amount of time when running the tests in parallel (possibly some kind of resource contention?) Instead, perform the mocking in the test class so it's part of the setup, not the actual test runs. --- .../android/features/messages/MessagesPresenterTest.kt | 7 ++++++- .../attachments/AttachmentsPreviewPresenterTest.kt | 8 +++++++- .../element/android/features/messages/fixtures/media.kt | 2 +- .../features/messages/media/FakeLocalMediaFactory.kt | 4 ++-- .../messages/media/viewer/MediaViewerPresenterTest.kt | 5 ++++- .../messages/textcomposer/MessageComposerPresenterTest.kt | 4 +++- 6 files changed, 23 insertions(+), 7 deletions(-) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt index 3f6e4e2c79..05345a4e3c 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt @@ -16,6 +16,7 @@ package io.element.android.features.messages +import android.net.Uri import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test @@ -40,11 +41,15 @@ import io.element.android.libraries.mediapickers.test.FakePickerProvider import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor import io.element.android.libraries.textcomposer.MessageComposerMode +import io.mockk.mockk import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest import org.junit.Test class MessagesPresenterTest { + + private val mockMediaUrl: Uri = mockk("localMediaUri") + @Test fun `present - initial state`() = runTest { val presenter = createMessagePresenter() @@ -135,7 +140,7 @@ class MessagesPresenterTest { room = matrixRoom, mediaPickerProvider = FakePickerProvider(), featureFlagService = FakeFeatureFlagService(), - localMediaFactory = FakeLocalMediaFactory(), + localMediaFactory = FakeLocalMediaFactory(mockMediaUrl), mediaSender = MediaSender(FakeMediaPreProcessor(),matrixRoom), snackbarDispatcher = SnackbarDispatcher(), ) diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt index 36983979fc..7d7316b290 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/attachments/AttachmentsPreviewPresenterTest.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.attachments +import android.net.Uri import androidx.media3.common.MimeTypes import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow @@ -35,6 +36,7 @@ import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.mediaupload.api.MediaPreProcessor import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor +import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test @@ -42,6 +44,7 @@ import org.junit.Test class AttachmentsPreviewPresenterTest { private val mediaPreProcessor = FakeMediaPreProcessor() + private val mockMediaUrl: Uri = mockk("localMediaUri") @Test fun `present - send media success scenario`() = runTest { @@ -87,7 +90,10 @@ class AttachmentsPreviewPresenterTest { } private fun anAttachmentsPreviewPresenter( - localMedia: LocalMedia = aLocalMedia(mimeType = MimeTypes.IMAGE_JPEG), + localMedia: LocalMedia = aLocalMedia( + uri = mockMediaUrl, + mimeType = MimeTypes.IMAGE_JPEG + ), room: MatrixRoom = FakeMatrixRoom() ): AttachmentsPreviewPresenter { return AttachmentsPreviewPresenter( diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt index ce5847f559..60a01a76d4 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/fixtures/media.kt @@ -23,7 +23,7 @@ import io.element.android.features.messages.impl.media.local.LocalMedia import io.mockk.mockk fun aLocalMedia( - uri: Uri = mockk("localMediaUri"), + uri: Uri, mimeType: String = MimeTypes.IMAGE_JPEG, name: String = "a media", size: Long = 1000, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaFactory.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaFactory.kt index 0382941d87..89f4e96173 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaFactory.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/FakeLocalMediaFactory.kt @@ -23,12 +23,12 @@ import io.element.android.features.messages.impl.media.local.LocalMediaFactory import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.media.MediaFile -class FakeLocalMediaFactory : LocalMediaFactory { +class FakeLocalMediaFactory(private val localMediaUri: Uri) : LocalMediaFactory { var fallbackMimeType: String = MimeTypes.OctetStream override fun createFromMediaFile(mediaFile: MediaFile, mimeType: String?): LocalMedia { - return aLocalMedia(mimeType = mimeType ?: fallbackMimeType) + return aLocalMedia(uri = localMediaUri, mimeType = mimeType ?: fallbackMimeType) } override fun createFromUri(uri: Uri, mimeType: String?): LocalMedia { diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt index fb5d8edf72..1ef8097d4a 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/media/viewer/MediaViewerPresenterTest.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.media.viewer +import android.net.Uri import androidx.media3.common.MimeTypes import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow @@ -31,6 +32,7 @@ import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS import io.element.android.libraries.matrix.test.media.FakeMediaLoader import io.element.android.libraries.matrix.test.media.aMediaSource +import io.mockk.mockk import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test @@ -40,7 +42,8 @@ private const val TESTED_MEDIA_NAME = "MediaName" class MediaViewerPresenterTest { - private val localMediaFactory = FakeLocalMediaFactory() + private val mockMediaUrl: Uri = mockk("localMediaUri") + private val localMediaFactory = FakeLocalMediaFactory(mockMediaUrl) private val mediaLoader = FakeMediaLoader() @Test diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt index 01c6e0eb49..bfef3b4011 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/textcomposer/MessageComposerPresenterTest.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.textcomposer +import android.net.Uri import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.ReceiveTurbine @@ -69,7 +70,8 @@ class MessageComposerPresenterTest { ) private val mediaPreProcessor = FakeMediaPreProcessor() private val snackbarDispatcher = SnackbarDispatcher() - private val localMediaFactory = FakeLocalMediaFactory() + private val mockMediaUrl: Uri = mockk("localMediaUri") + private val localMediaFactory = FakeLocalMediaFactory(mockMediaUrl) @Test fun `present - initial state`() = runTest { From f8dbd31c117d3daf5978114eb175f6d5769dc531 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Apr 2023 21:15:46 +0200 Subject: [PATCH 28/75] Test for Oidc --- .../features/login/impl/oidc/OidcPresenter.kt | 28 ++-- .../features/login/impl/oidc/OidcUrlParser.kt | 2 +- .../login/impl/root/LoginRootPresenter.kt | 6 +- .../changeserver/ChangeServerPresenterTest.kt | 4 +- .../login/impl/oidc/OidcPresenterTest.kt | 145 ++++++++++++++++++ .../login/impl/oidc/OidcUrlParserTest.kt | 59 +++++++ .../login/impl/root/LoginRootPresenterTest.kt | 116 +++++++++++++- .../android/libraries/matrix/test/TestData.kt | 3 +- .../test/auth/FakeAuthenticationService.kt | 26 ++++ 9 files changed, 365 insertions(+), 24 deletions(-) create mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt create mode 100644 features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParserTest.kt diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt index 76faa090d1..1c7debfd6f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt @@ -51,25 +51,27 @@ class OidcPresenter @AssistedInject constructor( fun handleCancel() { requestState = Async.Loading() localCoroutineScope.launch { - requestState = try { - authenticationService.cancelOidcLogin() - // Then go back - Async.Success(Unit) - } catch (throwable: Throwable) { - Async.Failure(throwable) - } + authenticationService.cancelOidcLogin() + .fold( + onSuccess = { + // Then go back + requestState = Async.Success(Unit) + }, + onFailure = { + requestState = Async.Failure(it) + } + ) } } fun handleSuccess(url: String) { requestState = Async.Loading() localCoroutineScope.launch { - try { - authenticationService.loginWithOidc(url) - // Then the node tree will be updated, there is nothing to do - } catch (throwable: Throwable) { - requestState = Async.Failure(throwable) - } + authenticationService.loginWithOidc(url) + .onFailure { + requestState = Async.Failure(it) + } + // On success, the node tree will be updated, there is nothing to do } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt index eb55368f47..a28e63ede8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt @@ -32,7 +32,7 @@ class OidcUrlParser { * Return a OidcAction, or null if the url is not a OidcUrl */ fun parse(url: String): OidcAction? { - if (!url.startsWith(OidcConfig.redirectUri)) return null + if (url.startsWith(OidcConfig.redirectUri).not()) return null if (url.contains("error=access_denied")) return OidcAction.GoBack if (url.contains("code=")) return OidcAction.Success(url) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt index 0d1b8de7fd..cf00075c4e 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt @@ -97,10 +97,12 @@ class LoginRootPresenter @Inject constructor( homeserver: String, state: MutableState>, ) = launch { - state.value = Async.Loading() suspend { authenticationService.setHomeserver(homeserver) - authenticationService.getHomeserverDetails().value!! + .map { + authenticationService.getHomeserverDetails().value!! + } + .getOrThrow() }.execute(state) } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt index cc216cc9dd..a30bd9449c 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/changeserver/ChangeServerPresenterTest.kt @@ -20,9 +20,9 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.login.impl.util.LoginConstants import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.test.A_HOMESERVER -import io.element.android.libraries.matrix.test.A_HOMESERVER_URL import io.element.android.libraries.matrix.test.A_HOMESERVER_URL_2 import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService @@ -39,7 +39,7 @@ class ChangeServerPresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.homeserver).isEqualTo(A_HOMESERVER_URL) + assertThat(initialState.homeserver).isEqualTo(LoginConstants.DEFAULT_HOMESERVER_URL) assertThat(initialState.submitEnabled).isTrue() } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt new file mode 100644 index 0000000000..0fba87f2e3 --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class) + +package io.element.android.features.login.impl.oidc + +import app.cash.molecule.RecompositionClock +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.test.A_THROWABLE +import io.element.android.libraries.matrix.test.auth.A_OIDC_DATA +import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class OidcPresenterTest { + @Test + fun `present - initial state`() = runTest { + val presenter = OidcPresenter( + A_OIDC_DATA, + FakeAuthenticationService(), + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.oidcDetails).isEqualTo(A_OIDC_DATA) + assertThat(initialState.requestState).isEqualTo(Async.Uninitialized) + } + } + + @Test + fun `present - go back`() = runTest { + val presenter = OidcPresenter( + A_OIDC_DATA, + FakeAuthenticationService(), + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(OidcEvents.Cancel) + val loadingState = awaitItem() + assertThat(loadingState.requestState).isEqualTo(Async.Loading()) + val finalState = awaitItem() + assertThat(finalState.requestState).isEqualTo(Async.Success(Unit)) + } + } + + @Test + fun `present - go back with failure`() = runTest { + val authenticationService = FakeAuthenticationService() + val presenter = OidcPresenter( + A_OIDC_DATA, + authenticationService, + ) + authenticationService.givenOidcCancelError(A_THROWABLE) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(OidcEvents.Cancel) + val loadingState = awaitItem() + assertThat(loadingState.requestState).isEqualTo(Async.Loading()) + val finalState = awaitItem() + assertThat(finalState.requestState).isEqualTo(Async.Failure(A_THROWABLE)) + // Note: in real life I do not think this can happen, and the app should not block the user. + } + } + + @Test + fun `present - user cancels from webview`() = runTest { + val presenter = OidcPresenter( + A_OIDC_DATA, + FakeAuthenticationService(), + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(OidcEvents.OidcActionEvent(OidcAction.GoBack)) + val loadingState = awaitItem() + assertThat(loadingState.requestState).isEqualTo(Async.Loading()) + val finalState = awaitItem() + assertThat(finalState.requestState).isEqualTo(Async.Success(Unit)) + } + } + + @Test + fun `present - login success`() = runTest { + val presenter = OidcPresenter( + A_OIDC_DATA, + FakeAuthenticationService(), + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(OidcEvents.OidcActionEvent(OidcAction.Success("A_URL"))) + val loadingState = awaitItem() + assertThat(loadingState.requestState).isEqualTo(Async.Loading()) + // In this case, no success, the session is created and the node get destroyed. + } + } + + @Test + fun `present - login error`() = runTest { + val authenticationService = FakeAuthenticationService() + val presenter = OidcPresenter( + A_OIDC_DATA, + authenticationService, + ) + authenticationService.givenLoginError(A_THROWABLE) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + initialState.eventSink.invoke(OidcEvents.OidcActionEvent(OidcAction.Success("A_URL"))) + val loadingState = awaitItem() + assertThat(loadingState.requestState).isEqualTo(Async.Loading()) + val errorState = awaitItem() + assertThat(errorState.requestState).isEqualTo(Async.Failure(A_THROWABLE)) + errorState.eventSink.invoke(OidcEvents.ClearError) + val finalState = awaitItem() + assertThat(finalState.requestState).isEqualTo(Async.Uninitialized) + } + } +} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParserTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParserTest.kt new file mode 100644 index 0000000000..d13f24c885 --- /dev/null +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParserTest.kt @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.api.auth.OidcConfig +import org.junit.Assert +import org.junit.Test + +class OidcUrlParserTest { + @Test + fun `test empty url`() { + val sut = OidcUrlParser() + assertThat(sut.parse("")).isNull() + } + + @Test + fun `test regular url`() { + val sut = OidcUrlParser() + assertThat(sut.parse("https://matrix.org")).isNull() + } + + @Test + fun `test cancel url`() { + val sut = OidcUrlParser() + val aCancelUrl = OidcConfig.redirectUri + "?error=access_denied&state=IFF1UETGye2ZA8pO" + assertThat(sut.parse(aCancelUrl)).isEqualTo(OidcAction.GoBack) + } + + @Test + fun `test success url`() { + val sut = OidcUrlParser() + val aSuccessUrl = OidcConfig.redirectUri + "?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB" + assertThat(sut.parse(aSuccessUrl)).isEqualTo(OidcAction.Success(aSuccessUrl)) + } + + @Test + fun `test unknown url`() { + val sut = OidcUrlParser() + val anUnknownUrl = OidcConfig.redirectUri + "?state=IFF1UETGye2ZA8pO&goat=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB" + Assert.assertThrows(IllegalStateException::class.java) { + assertThat(sut.parse(anUnknownUrl)) + } + } +} diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt index be940feacf..c072656e36 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt @@ -20,11 +20,16 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.login.impl.util.LoginConstants +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.test.A_HOMESERVER +import io.element.android.libraries.matrix.test.A_HOMESERVER_OIDC import io.element.android.libraries.matrix.test.A_PASSWORD import io.element.android.libraries.matrix.test.A_SESSION_ID import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.A_USER_NAME +import io.element.android.libraries.matrix.test.auth.A_OIDC_DATA import io.element.android.libraries.matrix.test.auth.FakeAuthenticationService import kotlinx.coroutines.test.runTest import org.junit.Test @@ -39,18 +44,79 @@ class LoginRootPresenterTest { presenter.present() }.test { val initialState = awaitItem() - assertThat(initialState.homeserverDetails).isEqualTo(A_HOMESERVER) + assertThat(initialState.homeserverUrl).isEqualTo(LoginConstants.DEFAULT_HOMESERVER_URL) + assertThat(initialState.homeserverDetails).isEqualTo(Async.Uninitialized) assertThat(initialState.loggedInState).isEqualTo(LoggedInState.NotLoggedIn) assertThat(initialState.formState).isEqualTo(LoginFormState.Default) assertThat(initialState.submitEnabled).isFalse() + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - initial state server load`() = runTest { + val authenticationService = FakeAuthenticationService() + val presenter = LoginRootPresenter( + authenticationService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.homeserverUrl).isEqualTo(LoginConstants.DEFAULT_HOMESERVER_URL) + assertThat(initialState.homeserverDetails).isEqualTo(Async.Uninitialized) + assertThat(initialState.loggedInState).isEqualTo(LoggedInState.NotLoggedIn) + assertThat(initialState.formState).isEqualTo(LoginFormState.Default) + assertThat(initialState.submitEnabled).isFalse() + val loadingState = awaitItem() + assertThat(loadingState.homeserverDetails).isEqualTo(Async.Loading()) + authenticationService.givenHomeserver(A_HOMESERVER) + skipItems(1) + val loadedState = awaitItem() + assertThat(loadedState.homeserverDetails).isEqualTo(Async.Success(A_HOMESERVER)) + } + } + + @Test + fun `present - initial state server load error and retry`() = runTest { + val authenticationService = FakeAuthenticationService() + val presenter = LoginRootPresenter( + authenticationService, + ) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.homeserverUrl).isEqualTo(LoginConstants.DEFAULT_HOMESERVER_URL) + assertThat(initialState.homeserverDetails).isEqualTo(Async.Uninitialized) + assertThat(initialState.loggedInState).isEqualTo(LoggedInState.NotLoggedIn) + assertThat(initialState.formState).isEqualTo(LoginFormState.Default) + assertThat(initialState.submitEnabled).isFalse() + val loadingState = awaitItem() + assertThat(loadingState.homeserverDetails).isEqualTo(Async.Loading()) + val aThrowable = Throwable("Error") + authenticationService.givenChangeServerError(aThrowable) + val errorState = awaitItem() + assertThat(errorState.homeserverDetails).isEqualTo(Async.Failure(aThrowable)) + // Retry + errorState.eventSink.invoke(LoginRootEvents.RetryFetchServerInfo) + val loadingState2 = awaitItem() + assertThat(loadingState2.homeserverDetails).isEqualTo(Async.Loading()) + authenticationService.givenChangeServerError(null) + authenticationService.givenHomeserver(A_HOMESERVER) + skipItems(1) + val loadedState = awaitItem() + assertThat(loadedState.homeserverDetails).isEqualTo(Async.Success(A_HOMESERVER)) } } @Test fun `present - enter login and password`() = runTest { + val authenticationService = FakeAuthenticationService() val presenter = LoginRootPresenter( - FakeAuthenticationService(), + authenticationService, ) + authenticationService.givenHomeserver(A_HOMESERVER) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -67,10 +133,49 @@ class LoginRootPresenterTest { } @Test - fun `present - submit`() = runTest { + fun `present - oidc login`() = runTest { + val authenticationService = FakeAuthenticationService() val presenter = LoginRootPresenter( - FakeAuthenticationService(), + authenticationService, ) + authenticationService.givenHomeserver(A_HOMESERVER_OIDC) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.submitEnabled).isTrue() + initialState.eventSink.invoke(LoginRootEvents.Submit) + val oidcState = awaitItem() + assertThat(oidcState.loggedInState).isEqualTo(LoggedInState.OidcStarted(A_OIDC_DATA)) + } + } + + @Test + fun `present - oidc login error`() = runTest { + val authenticationService = FakeAuthenticationService() + val presenter = LoginRootPresenter( + authenticationService, + ) + authenticationService.givenHomeserver(A_HOMESERVER_OIDC) + authenticationService.givenOidcError(A_THROWABLE) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.submitEnabled).isTrue() + initialState.eventSink.invoke(LoginRootEvents.Submit) + val oidcState = awaitItem() + assertThat(oidcState.loggedInState).isEqualTo(LoggedInState.ErrorLoggingIn(A_THROWABLE)) + } + } + + @Test + fun `present - submit`() = runTest { + val authenticationService = FakeAuthenticationService() + val presenter = LoginRootPresenter( + authenticationService, + ) + authenticationService.givenHomeserver(A_HOMESERVER) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -93,6 +198,7 @@ class LoginRootPresenterTest { val presenter = LoginRootPresenter( authenticationService, ) + authenticationService.givenHomeserver(A_HOMESERVER) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { @@ -116,11 +222,11 @@ class LoginRootPresenterTest { val presenter = LoginRootPresenter( authenticationService, ) + authenticationService.givenHomeserver(A_HOMESERVER) moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { val initialState = awaitItem() - // Submit will return an error authenticationService.givenLoginError(A_THROWABLE) initialState.eventSink(LoginRootEvents.Submit) diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index d9322ed2ae..be9f41dd6c 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -47,7 +47,8 @@ const val ANOTHER_MESSAGE = "Hello universe!" const val A_HOMESERVER_URL = "matrix.org" const val A_HOMESERVER_URL_2 = "matrix-client.org" -val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, true, null) +val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = true, supportsOidc = false) +val A_HOMESERVER_OIDC = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = false, supportsOidc = true) const val AN_AVATAR_URL = "mxc://data" diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt index 886eac95ef..5b6b8b1e28 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt @@ -19,6 +19,7 @@ package io.element.android.libraries.matrix.test.auth import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails +import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.A_USER_ID import kotlinx.coroutines.delay @@ -27,8 +28,12 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.flowOf +val A_OIDC_DATA = OidcDetails(url = "a-url") + class FakeAuthenticationService : MatrixAuthenticationService { private var homeserver = MutableStateFlow(null) + private var oidcError: Throwable? = null + private var oidcCancelError: Throwable? = null private var loginError: Throwable? = null private var changeServerError: Throwable? = null @@ -62,6 +67,27 @@ class FakeAuthenticationService : MatrixAuthenticationService { return loginError?.let { Result.failure(it) } ?: Result.success(A_USER_ID) } + override suspend fun getOidcUrl(): Result { + return oidcError?.let { Result.failure(it) } ?: Result.success(A_OIDC_DATA) + } + + override suspend fun cancelOidcLogin(): Result { + return oidcCancelError?.let { Result.failure(it) } ?: Result.success(Unit) + } + + override suspend fun loginWithOidc(callbackUrl: String): Result { + delay(100) + return loginError?.let { Result.failure(it) } ?: Result.success(A_USER_ID) + } + + fun givenOidcError(throwable: Throwable?) { + oidcError = throwable + } + + fun givenOidcCancelError(throwable: Throwable?) { + oidcCancelError = throwable + } + fun givenLoginError(throwable: Throwable?) { loginError = throwable } From 4cc305087175d46970ca22fda1010c03dd6ed504 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 20 Apr 2023 21:38:45 +0200 Subject: [PATCH 29/75] Cleanup --- .../element/android/features/login/impl/oidc/OidcNode.kt | 3 --- .../element/android/features/login/impl/oidc/OidcView.kt | 1 - .../features/login/impl/root/LoginRootPresenter.kt | 2 +- .../android/features/login/impl/root/LoginRootState.kt | 2 +- .../features/login/impl/root/LoginRootStateProvider.kt | 8 ++++---- .../libraries/matrix/api/auth/MatrixHomeServerDetails.kt | 2 +- .../libraries/matrix/impl/auth/HomeserverDetails.kt | 2 +- .../io/element/android/libraries/matrix/test/TestData.kt | 4 ++-- 8 files changed, 10 insertions(+), 14 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt index 0122ead10c..b1f9b45237 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt @@ -29,9 +29,6 @@ import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.auth.OidcDetails -/** - * TODO Transmit back press to the webview - */ @ContributesNode(AppScope::class) class OidcNode @AssistedInject constructor( @Assisted buildContext: BuildContext, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt index 06591409c9..110678a708 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt @@ -92,7 +92,6 @@ fun OidcView( ) } is Async.Loading -> { - // Indeterminate indicator, to avoid the freeze effect if the connection takes time to initialize. CircularProgressIndicator( modifier = Modifier.align(Alignment.Center) ) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt index cf00075c4e..5dec3b6537 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt @@ -76,7 +76,7 @@ class LoginRootPresenter @Inject constructor( LoginRootEvents.Submit -> { val homeServerDetails = getHomeServerDetailsAction.value.dataOrNull() ?: return when { - homeServerDetails.supportsOidc -> localCoroutineScope.submitOidc(loggedInState) + homeServerDetails.supportsOidcLogin -> localCoroutineScope.submitOidc(loggedInState) homeServerDetails.supportsPasswordLogin -> localCoroutineScope.submit(formState.value, loggedInState) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt index ccf7523b1b..45eafa744c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootState.kt @@ -32,7 +32,7 @@ data class LoginRootState( val eventSink: (LoginRootEvents) -> Unit ) { val supportPasswordLogin = (homeserverDetails as? Async.Success)?.state?.supportsPasswordLogin.orFalse() - val supportOidcLogin = (homeserverDetails as? Async.Success)?.state?.supportsOidc.orFalse() + val supportOidcLogin = (homeserverDetails as? Async.Success)?.state?.supportsOidcLogin.orFalse() val submitEnabled: Boolean get() = loggedInState !is LoggedInState.ErrorLoggingIn && ((formState.login.isNotEmpty() && formState.password.isNotEmpty()) || supportOidcLogin) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt index cffa395ea0..5f6d7c1f3a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootStateProvider.kt @@ -30,7 +30,7 @@ open class LoginRootStateProvider : PreviewParameterProvider { MatrixHomeServerDetails( "some-custom-server.com", supportsPasswordLogin = true, - supportsOidc = false + supportsOidcLogin = false ) ) ), @@ -45,7 +45,7 @@ open class LoginRootStateProvider : PreviewParameterProvider { MatrixHomeServerDetails( "server-with-oidc.org", supportsPasswordLogin = false, - supportsOidc = true + supportsOidcLogin = true ) ) ), @@ -56,7 +56,7 @@ open class LoginRootStateProvider : PreviewParameterProvider { MatrixHomeServerDetails( "wrong.org", supportsPasswordLogin = false, - supportsOidc = false + supportsOidcLogin = false ) ) ), @@ -69,7 +69,7 @@ open class LoginRootStateProvider : PreviewParameterProvider { fun aLoginRootState() = LoginRootState( homeserverUrl = "matrix.org", - homeserverDetails = Async.Success(MatrixHomeServerDetails("matrix.org", supportsPasswordLogin = true, supportsOidc = false)), + homeserverDetails = Async.Success(MatrixHomeServerDetails("matrix.org", supportsPasswordLogin = true, supportsOidcLogin = false)), loggedInState = LoggedInState.NotLoggedIn, formState = LoginFormState.Default, eventSink = {} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt index 90c54cfe2e..f5fc38eb16 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/auth/MatrixHomeServerDetails.kt @@ -23,5 +23,5 @@ import kotlinx.parcelize.Parcelize data class MatrixHomeServerDetails( val url: String, val supportsPasswordLogin: Boolean, - val supportsOidc: Boolean, + val supportsOidcLogin: Boolean, ): Parcelable diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt index 9ca3d78456..f1d3e34bf8 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt @@ -23,6 +23,6 @@ fun HomeserverLoginDetails.map(): MatrixHomeServerDetails = use { MatrixHomeServerDetails( url = url(), supportsPasswordLogin = supportsPasswordLogin(), - supportsOidc = supportsOidcLogin(), + supportsOidcLogin = supportsOidcLogin(), ) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt index be9f41dd6c..7e54d8e851 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/TestData.kt @@ -47,8 +47,8 @@ const val ANOTHER_MESSAGE = "Hello universe!" const val A_HOMESERVER_URL = "matrix.org" const val A_HOMESERVER_URL_2 = "matrix-client.org" -val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = true, supportsOidc = false) -val A_HOMESERVER_OIDC = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = false, supportsOidc = true) +val A_HOMESERVER = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = true, supportsOidcLogin = false) +val A_HOMESERVER_OIDC = MatrixHomeServerDetails(A_HOMESERVER_URL, supportsPasswordLogin = false, supportsOidcLogin = true) const val AN_AVATAR_URL = "mxc://data" From d2b73dd6bc2f29a18f72f785f5c6dbcae47cf986 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Fri, 21 Apr 2023 09:49:11 +0200 Subject: [PATCH 30/75] Quality checks --- .../element/android/features/login/impl/oidc/OidcUrlParser.kt | 2 +- .../io/element/android/features/login/impl/oidc/OidcView.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt index a28e63ede8..090fd62501 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt @@ -29,7 +29,7 @@ class OidcUrlParser { // On success, we get: // `io.element:/callback?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB` /** - * Return a OidcAction, or null if the url is not a OidcUrl + * Return a OidcAction, or null if the url is not a OidcUrl. */ fun parse(url: String): OidcAction? { if (url.startsWith(OidcConfig.redirectUri).not()) return null diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt index 110678a708..10834f5afa 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt @@ -71,7 +71,7 @@ fun OidcView( Box(modifier = modifier.statusBarsPadding()) { AndroidView( - modifier = modifier + modifier = Modifier .statusBarsPadding(), factory = { context -> WebView(context).apply { From d2f969252dc99e6dd51be82f49167703c8d36ee6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 12:50:35 +0200 Subject: [PATCH 31/75] Oidc with CustomTab --- app/src/main/AndroidManifest.xml | 8 ++ .../io/element/android/appnav/RootFlowNode.kt | 19 +++- .../android/appnav/intent/IntentResolver.kt | 47 ++++++++++ .../features/login/api/oidc/OidcAction.kt | 22 +++++ .../features/login/api/oidc/OidcActionFlow.kt | 21 +++++ .../login/api/oidc/OidcIntentResolver.kt | 23 +++++ features/login/impl/build.gradle.kts | 1 + .../login/impl/src/main/AndroidManifest.xml | 26 ++++++ .../features/login/impl/LoginFlowNode.kt | 10 ++- .../login/impl/oidc/CustomTabHandler.kt | 87 +++++++++++++++++++ .../impl/oidc/DefaultOidcIntentResolver.kt | 33 +++++++ .../features/login/impl/oidc/OidcEvents.kt | 2 + .../features/login/impl/oidc/OidcPresenter.kt | 1 + .../features/login/impl/oidc/OidcUrlParser.kt | 9 +- .../features/login/impl/oidc/web/CustomTab.kt | 65 ++++++++++++++ .../impl/oidc/web/DefaultOidcActionFlow.kt | 39 +++++++++ .../login/impl/root/LoginRootPresenter.kt | 36 ++++++++ .../login/impl/oidc/OidcPresenterTest.kt | 1 + .../login/impl/oidc/OidcUrlParserTest.kt | 1 + gradle/libs.versions.toml | 2 + libraries/deeplink/build.gradle.kts | 1 + tools/adb/oidc.sh | 24 +++++ 22 files changed, 466 insertions(+), 12 deletions(-) create mode 100644 appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt create mode 100644 features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcAction.kt create mode 100644 features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcActionFlow.kt create mode 100644 features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcIntentResolver.kt create mode 100644 features/login/impl/src/main/AndroidManifest.xml create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabHandler.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/DefaultOidcIntentResolver.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/CustomTab.kt create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/DefaultOidcActionFlow.kt create mode 100755 tools/adb/oidc.sh diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 22a63b60c9..875f9f0435 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -49,6 +49,14 @@ android:host="open" android:scheme="elementx" /> + + + + + + + + ( backstack = BackStack( @@ -204,8 +208,11 @@ class RootFlowNode @AssistedInject constructor( } suspend fun handleIntent(intent: Intent) { - deeplinkParser.getFromIntent(intent) - ?.let { navigateTo(it) } + val resolvedIntent = intentResolver.resolve(intent) ?: return + when (resolvedIntent) { + is ResolvedIntent.Navigation -> navigateTo(resolvedIntent.deeplinkData) + is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction) + } } private suspend fun navigateTo(deeplinkData: DeeplinkData) { @@ -223,6 +230,10 @@ class RootFlowNode @AssistedInject constructor( } } + private fun onOidcAction(oidcAction: OidcAction) { + oidcActionFlow.post(oidcAction) + } + private suspend fun attachSession(sessionId: SessionId): LoggedInFlowNode { return attachChild { backstack.newRoot(NavTarget.LoggedInFlow(sessionId)) diff --git a/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt new file mode 100644 index 0000000000..b567395c1e --- /dev/null +++ b/appnav/src/main/kotlin/io/element/android/appnav/intent/IntentResolver.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.appnav.intent + +import android.content.Intent +import io.element.android.features.login.api.oidc.OidcAction +import io.element.android.features.login.api.oidc.OidcIntentResolver +import io.element.android.libraries.deeplink.DeeplinkData +import io.element.android.libraries.deeplink.DeeplinkParser +import timber.log.Timber +import javax.inject.Inject + +sealed interface ResolvedIntent { + data class Navigation(val deeplinkData: DeeplinkData) : ResolvedIntent + data class Oidc(val oidcAction: OidcAction) : ResolvedIntent +} + +class IntentResolver @Inject constructor( + private val deeplinkParser: DeeplinkParser, + private val oidcIntentResolver: OidcIntentResolver +) { + fun resolve(intent: Intent): ResolvedIntent? { + val deepLinkData = deeplinkParser.getFromIntent(intent) + if (deepLinkData != null) return ResolvedIntent.Navigation(deepLinkData) + + val oidcAction = oidcIntentResolver.resolve(intent) + if (oidcAction != null) return ResolvedIntent.Oidc(oidcAction) + + // Unknown intent + Timber.w("Unknown intent") + return null + } +} diff --git a/features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcAction.kt b/features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcAction.kt new file mode 100644 index 0000000000..6e90a390c4 --- /dev/null +++ b/features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcAction.kt @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.api.oidc + +sealed interface OidcAction { + object GoBack : OidcAction + data class Success(val url: String) : OidcAction +} diff --git a/features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcActionFlow.kt b/features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcActionFlow.kt new file mode 100644 index 0000000000..004e7c8a51 --- /dev/null +++ b/features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcActionFlow.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.api.oidc + +interface OidcActionFlow { + fun post(oidcAction: OidcAction) +} diff --git a/features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcIntentResolver.kt b/features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcIntentResolver.kt new file mode 100644 index 0000000000..a6ecf26fca --- /dev/null +++ b/features/login/api/src/main/kotlin/io/element/android/features/login/api/oidc/OidcIntentResolver.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.api.oidc + +import android.content.Intent + +interface OidcIntentResolver { + fun resolve(intent: Intent): OidcAction? +} diff --git a/features/login/impl/build.gradle.kts b/features/login/impl/build.gradle.kts index 5666fc1179..c43a5c2cd3 100644 --- a/features/login/impl/build.gradle.kts +++ b/features/login/impl/build.gradle.kts @@ -45,6 +45,7 @@ dependencies { implementation(projects.libraries.elementresources) implementation(projects.libraries.testtags) implementation(projects.libraries.uiStrings) + implementation(libs.androidx.browser) api(projects.features.login.api) ksp(libs.showkase.processor) diff --git a/features/login/impl/src/main/AndroidManifest.xml b/features/login/impl/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..172e8645c1 --- /dev/null +++ b/features/login/impl/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index 37cfec229b..f1f56888bb 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -29,6 +29,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.login.impl.changeserver.ChangeServerNode +import io.element.android.features.login.impl.oidc.CustomTabHandler import io.element.android.features.login.impl.oidc.OidcNode import io.element.android.features.login.impl.root.LoginRootNode import io.element.android.libraries.architecture.BackstackNode @@ -42,6 +43,7 @@ import kotlinx.parcelize.Parcelize class LoginFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, + private val customTabHandler: CustomTabHandler, ) : BackstackNode( backstack = BackStack( initialElement = NavTarget.Root, @@ -50,7 +52,6 @@ class LoginFlowNode @AssistedInject constructor( buildContext = buildContext, plugins = plugins, ) { - sealed interface NavTarget : Parcelable { @Parcelize object Root : NavTarget @@ -71,7 +72,12 @@ class LoginFlowNode @AssistedInject constructor( } override fun onOidcDetails(oidcDetails: OidcDetails) { - backstack.push(NavTarget.OidcView(oidcDetails)) + if (customTabHandler.supportCustomTab()) { + customTabHandler.open(oidcDetails.url) + } else { + // Fallback to WebView mode + backstack.push(NavTarget.OidcView(oidcDetails)) + } } } createNode(buildContext, plugins = listOf(callback)) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabHandler.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabHandler.kt new file mode 100644 index 0000000000..593469a12d --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabHandler.kt @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import android.content.ComponentName +import android.content.Context +import android.net.Uri +import androidx.browser.customtabs.CustomTabsClient +import androidx.browser.customtabs.CustomTabsServiceConnection +import androidx.browser.customtabs.CustomTabsSession +import io.element.android.features.login.impl.oidc.web.openUrlInChromeCustomTab +import io.element.android.libraries.di.ApplicationContext +import javax.inject.Inject + +class CustomTabHandler @Inject constructor( + @ApplicationContext private val context: Context, +) { + private var customTabsSession: CustomTabsSession? = null + private var customTabsClient: CustomTabsClient? = null + private var customTabsServiceConnection: CustomTabsServiceConnection? = null + + /** + * Return true if the device supports Custom tab, i.e. there is an third party app with + * CustomTab support (ex: Chrome, Firefox, etc.). + */ + fun supportCustomTab(): Boolean { + val packageName = CustomTabsClient.getPackageName(context, null) + return packageName != null + } + + fun prepareCustomTab(url: String) { + val packageName = CustomTabsClient.getPackageName(context, null) + + // packageName can be null if there are 0 or several CustomTabs compatible browsers installed on the device + if (packageName != null) { + customTabsServiceConnection = object : CustomTabsServiceConnection() { + override fun onCustomTabsServiceConnected(name: ComponentName, client: CustomTabsClient) { + customTabsClient = client + .also { it.warmup(0L) } + prefetchUrl(url) + } + + override fun onServiceDisconnected(name: ComponentName?) { + } + } + .also { + CustomTabsClient.bindCustomTabsService( + context, + // Despite the API, packageName cannot be null + packageName, + it + ) + } + } + } + + private fun prefetchUrl(url: String) { + if (customTabsSession == null) { + customTabsSession = customTabsClient?.newSession(null) + } + + customTabsSession?.mayLaunchUrl(Uri.parse(url), null, null) + } + + fun disposeCustomTab() { + customTabsServiceConnection?.let { context.unbindService(it) } + customTabsServiceConnection = null + } + + fun open(url: String) { + openUrlInChromeCustomTab(context, customTabsSession, false, url) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/DefaultOidcIntentResolver.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/DefaultOidcIntentResolver.kt new file mode 100644 index 0000000000..8b6844e0f3 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/DefaultOidcIntentResolver.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import android.content.Intent +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.login.api.oidc.OidcAction +import io.element.android.features.login.api.oidc.OidcIntentResolver +import io.element.android.libraries.di.AppScope +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +class DefaultOidcIntentResolver @Inject constructor( + private val oidcUrlParser: OidcUrlParser, +) : OidcIntentResolver { + override fun resolve(intent: Intent): OidcAction? { + return oidcUrlParser.parse(intent.dataString.orEmpty()) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt index 0c40f457bf..4f62c6476d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt @@ -16,6 +16,8 @@ package io.element.android.features.login.impl.oidc +import io.element.android.features.login.api.oidc.OidcAction + sealed interface OidcEvents { object Cancel : OidcEvents data class OidcActionEvent(val oidcAction: OidcAction): OidcEvents diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt index 1c7debfd6f..f66caef5b2 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.setValue import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject +import io.element.android.features.login.api.oidc.OidcAction import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt index 090fd62501..487df70253 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParser.kt @@ -16,13 +16,15 @@ package io.element.android.features.login.impl.oidc +import io.element.android.features.login.api.oidc.OidcAction import io.element.android.libraries.matrix.api.auth.OidcConfig +import javax.inject.Inject /** * Simple parser for oidc url interception. * TODO Find documentation about the format. */ -class OidcUrlParser { +class OidcUrlParser @Inject constructor() { // When user press button "Cancel", we get the url: // `io.element:/callback?error=access_denied&state=IFF1UETGye2ZA8pO` @@ -40,8 +42,3 @@ class OidcUrlParser { error("Not supported: $url") } } - -sealed interface OidcAction { - object GoBack : OidcAction - data class Success(val url: String) : OidcAction -} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/CustomTab.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/CustomTab.kt new file mode 100644 index 0000000000..d6c9de7e68 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/CustomTab.kt @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc.web + +import android.content.ActivityNotFoundException +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.browser.customtabs.CustomTabColorSchemeParams +import androidx.browser.customtabs.CustomTabsIntent +import androidx.browser.customtabs.CustomTabsSession + +/** + * Open url in custom tab or, if not available, in the default browser. + * If several compatible browsers are installed, the user will be proposed to choose one. + * Ref: https://developer.chrome.com/multidevice/android/customtabs. + */ +fun openUrlInChromeCustomTab( + context: Context, + session: CustomTabsSession?, + darkTheme: Boolean, + url: String +) { + try { + CustomTabsIntent.Builder() + .setDefaultColorSchemeParams( + CustomTabColorSchemeParams.Builder() + // TODO .setToolbarColor(ThemeUtils.getColor(context, android.R.attr.colorBackground)) + // TODO .setNavigationBarColor(ThemeUtils.getColor(context, android.R.attr.colorBackground)) + .build() + ) + .setColorScheme( + when (darkTheme) { + false -> CustomTabsIntent.COLOR_SCHEME_LIGHT + true -> CustomTabsIntent.COLOR_SCHEME_DARK + } + ) + // Note: setting close button icon does not work + // .setCloseButtonIcon(BitmapFactory.decodeResource(context.resources, R.drawable.ic_back_24dp)) + // .setStartAnimations(context, R.anim.enter_fade_in, R.anim.exit_fade_out) + // .setExitAnimations(context, R.anim.enter_fade_in, R.anim.exit_fade_out) + .apply { session?.let { setSession(it) } } + .build() + .apply { + intent.flags += Intent.FLAG_ACTIVITY_NEW_TASK + } + .launchUrl(context, Uri.parse(url)) + } catch (activityNotFoundException: ActivityNotFoundException) { + // TODO context.toast(R.string.error_no_external_application_found) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/DefaultOidcActionFlow.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/DefaultOidcActionFlow.kt new file mode 100644 index 0000000000..be4ec88ca2 --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/DefaultOidcActionFlow.kt @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc.web + +import com.squareup.anvil.annotations.ContributesBinding +import io.element.android.features.login.api.oidc.OidcAction +import io.element.android.features.login.api.oidc.OidcActionFlow +import io.element.android.libraries.di.AppScope +import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.flow.MutableStateFlow +import javax.inject.Inject + +@ContributesBinding(AppScope::class) +@SingleIn(AppScope::class) +class DefaultOidcActionFlow @Inject constructor() : OidcActionFlow { + private val mutableStateFlow = MutableStateFlow(null) + + override fun post(oidcAction: OidcAction) { + mutableStateFlow.value = oidcAction + } + + suspend fun collect(lambda: suspend (OidcAction?) -> Unit) { + mutableStateFlow.collect(lambda) + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt index 5dec3b6537..5bf9f2ad05 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt @@ -24,6 +24,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable +import io.element.android.features.login.api.oidc.OidcAction +import io.element.android.features.login.impl.oidc.web.DefaultOidcActionFlow import io.element.android.features.login.impl.util.LoginConstants import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter @@ -36,6 +38,7 @@ import javax.inject.Inject class LoginRootPresenter @Inject constructor( private val authenticationService: MatrixAuthenticationService, + private val defaultOidcActionFlow: DefaultOidcActionFlow, ) : Presenter { @Composable @@ -64,6 +67,14 @@ class LoginRootPresenter @Inject constructor( mutableStateOf(LoginFormState.Default) } + LaunchedEffect(Unit) { + launch { + defaultOidcActionFlow.collect { + onOidcAction(it, loggedInState) + } + } + } + fun handleEvents(event: LoginRootEvents) { when (event) { LoginRootEvents.RetryFetchServerInfo -> localCoroutineScope.getHomeServerDetails(homeserver, getHomeServerDetailsAction) @@ -131,4 +142,29 @@ class LoginRootPresenter @Inject constructor( private fun updateFormState(formState: MutableState, updateLambda: LoginFormState.() -> LoginFormState) { formState.value = updateLambda(formState.value) } + + private suspend fun onOidcAction(oidcAction: OidcAction?, loggedInState: MutableState) { + oidcAction ?: return + loggedInState.value = LoggedInState.LoggingIn + when (oidcAction) { + OidcAction.GoBack -> { + authenticationService.cancelOidcLogin() + .onSuccess { + loggedInState.value = LoggedInState.NotLoggedIn + } + .onFailure { failure -> + loggedInState.value = LoggedInState.ErrorLoggingIn(failure) + } + } + is OidcAction.Success -> { + authenticationService.loginWithOidc(oidcAction.url) + .onSuccess { sessionId -> + loggedInState.value = LoggedInState.LoggedIn(sessionId) + } + .onFailure { failure -> + loggedInState.value = LoggedInState.ErrorLoggingIn(failure) + } + } + } + } } diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt index 0fba87f2e3..f69cecd0f2 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt @@ -22,6 +22,7 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.login.api.oidc.OidcAction import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.test.A_THROWABLE import io.element.android.libraries.matrix.test.auth.A_OIDC_DATA diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParserTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParserTest.kt index d13f24c885..a0275f8f47 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParserTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcUrlParserTest.kt @@ -17,6 +17,7 @@ package io.element.android.features.login.impl.oidc import com.google.common.truth.Truth.assertThat +import io.element.android.features.login.api.oidc.OidcAction import io.element.android.libraries.matrix.api.auth.OidcConfig import org.junit.Assert import org.junit.Test diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9783cb1cf7..8e3522055c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -18,6 +18,7 @@ lifecycle = "2.6.1" activity = "1.7.2" startup = "1.1.1" media3 = "1.0.2" +browser = "1.5.0" # Compose compose_bom = "2023.05.01" @@ -70,6 +71,7 @@ androidx_datastore_datastore = { module = "androidx.datastore:datastore", versio androidx_exifinterface = "androidx.exifinterface:exifinterface:1.3.6" androidx_constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "constraintlayout" } androidx_recyclerview = { module = "androidx.recyclerview:recyclerview", version.ref = "recyclerview" } +androidx_browser = { module = "androidx.browser:browser", version.ref = "browser" } androidx_lifecycle_runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } androidx_lifecycle_process = { module = "androidx.lifecycle:lifecycle-process", version.ref = "lifecycle" } androidx_splash = "androidx.core:core-splashscreen:1.0.1" diff --git a/libraries/deeplink/build.gradle.kts b/libraries/deeplink/build.gradle.kts index d850074d66..08ec84c227 100644 --- a/libraries/deeplink/build.gradle.kts +++ b/libraries/deeplink/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { implementation(libs.dagger) implementation(libs.androidx.corektx) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.architecture) testImplementation(libs.test.junit) testImplementation(libs.test.truth) diff --git a/tools/adb/oidc.sh b/tools/adb/oidc.sh new file mode 100755 index 0000000000..52be556cbb --- /dev/null +++ b/tools/adb/oidc.sh @@ -0,0 +1,24 @@ +#! /bin/bash +# +# Copyright (c) 2023 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# Format is: + +# Error +# adb shell am start -a android.intent.action.VIEW -d io.element:/callback?error=access_denied&state=IFF1UETGye2ZA8pO + +# Success +adb shell am start -a android.intent.action.VIEW -d io.element:/callback?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB From 4951a94af59c128f3cc70b44d734b0edeb1a3b87 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 14:49:15 +0200 Subject: [PATCH 32/75] Create sub packages. --- .../features/login/impl/LoginFlowNode.kt | 9 +++-- .../impl/oidc/CustomTabAvailabilityChecker.kt | 35 +++++++++++++++++++ .../oidc/{ => customtab}/CustomTabHandler.kt | 14 ++------ .../DefaultOidcActionFlow.kt | 2 +- .../CustomTab.kt => customtab/Extensions.kt} | 7 ++-- .../impl/oidc/{ => webview}/OidcEvents.kt | 2 +- .../login/impl/oidc/{ => webview}/OidcNode.kt | 2 +- .../impl/oidc/{ => webview}/OidcPresenter.kt | 2 +- .../impl/oidc/{ => webview}/OidcState.kt | 2 +- .../oidc/{ => webview}/OidcStateProvider.kt | 2 +- .../login/impl/oidc/{ => webview}/OidcView.kt | 4 +-- .../oidc/{ => webview}/OidcWebViewClient.kt | 3 +- .../{ => webview}/WebViewEventListener.kt | 2 +- .../login/impl/root/LoginRootPresenter.kt | 2 +- .../oidc/{ => webview}/OidcPresenterTest.kt | 2 +- 15 files changed, 58 insertions(+), 32 deletions(-) create mode 100644 features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabAvailabilityChecker.kt rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{ => customtab}/CustomTabHandler.kt (83%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{web => customtab}/DefaultOidcActionFlow.kt (95%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{web/CustomTab.kt => customtab/Extensions.kt} (94%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{ => webview}/OidcEvents.kt (93%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{ => webview}/OidcNode.kt (96%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{ => webview}/OidcPresenter.kt (98%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{ => webview}/OidcState.kt (93%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{ => webview}/OidcStateProvider.kt (95%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{ => webview}/OidcView.kt (96%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{ => webview}/OidcWebViewClient.kt (94%) rename features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/{ => webview}/WebViewEventListener.kt (93%) rename features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/{ => webview}/OidcPresenterTest.kt (98%) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index f1f56888bb..833bf4367b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -29,8 +29,9 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.login.impl.changeserver.ChangeServerNode -import io.element.android.features.login.impl.oidc.CustomTabHandler -import io.element.android.features.login.impl.oidc.OidcNode +import io.element.android.features.login.impl.oidc.CustomTabAvailabilityChecker +import io.element.android.features.login.impl.oidc.customtab.CustomTabHandler +import io.element.android.features.login.impl.oidc.webview.OidcNode import io.element.android.features.login.impl.root.LoginRootNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler @@ -43,6 +44,7 @@ import kotlinx.parcelize.Parcelize class LoginFlowNode @AssistedInject constructor( @Assisted buildContext: BuildContext, @Assisted plugins: List, + private val customTabAvailabilityChecker: CustomTabAvailabilityChecker, private val customTabHandler: CustomTabHandler, ) : BackstackNode( backstack = BackStack( @@ -72,7 +74,8 @@ class LoginFlowNode @AssistedInject constructor( } override fun onOidcDetails(oidcDetails: OidcDetails) { - if (customTabHandler.supportCustomTab()) { + if (customTabAvailabilityChecker.supportCustomTab()) { + // In this case open a Chrome Custom tab customTabHandler.open(oidcDetails.url) } else { // Fallback to WebView mode diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabAvailabilityChecker.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabAvailabilityChecker.kt new file mode 100644 index 0000000000..424e9f13bc --- /dev/null +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabAvailabilityChecker.kt @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.login.impl.oidc + +import android.content.Context +import androidx.browser.customtabs.CustomTabsClient +import io.element.android.libraries.di.ApplicationContext +import javax.inject.Inject + +class CustomTabAvailabilityChecker @Inject constructor( + @ApplicationContext private val context: Context, +) { + /** + * Return true if the device supports Custom tab, i.e. there is an third party app with + * CustomTab support (ex: Chrome, Firefox, etc.). + */ + fun supportCustomTab(): Boolean { + val packageName = CustomTabsClient.getPackageName(context, null) + return packageName != null + } +} diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabHandler.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt similarity index 83% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabHandler.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt index 593469a12d..4a84dc76a7 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/CustomTabHandler.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.customtab import android.content.ComponentName import android.content.Context @@ -22,7 +22,6 @@ import android.net.Uri import androidx.browser.customtabs.CustomTabsClient import androidx.browser.customtabs.CustomTabsServiceConnection import androidx.browser.customtabs.CustomTabsSession -import io.element.android.features.login.impl.oidc.web.openUrlInChromeCustomTab import io.element.android.libraries.di.ApplicationContext import javax.inject.Inject @@ -33,15 +32,6 @@ class CustomTabHandler @Inject constructor( private var customTabsClient: CustomTabsClient? = null private var customTabsServiceConnection: CustomTabsServiceConnection? = null - /** - * Return true if the device supports Custom tab, i.e. there is an third party app with - * CustomTab support (ex: Chrome, Firefox, etc.). - */ - fun supportCustomTab(): Boolean { - val packageName = CustomTabsClient.getPackageName(context, null) - return packageName != null - } - fun prepareCustomTab(url: String) { val packageName = CustomTabsClient.getPackageName(context, null) @@ -82,6 +72,6 @@ class CustomTabHandler @Inject constructor( } fun open(url: String) { - openUrlInChromeCustomTab(context, customTabsSession, false, url) + context.openUrlInChromeCustomTab(customTabsSession, false, url) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/DefaultOidcActionFlow.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt similarity index 95% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/DefaultOidcActionFlow.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt index be4ec88ca2..87c22629a0 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/DefaultOidcActionFlow.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc.web +package io.element.android.features.login.impl.oidc.customtab import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.login.api.oidc.OidcAction diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/CustomTab.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/Extensions.kt similarity index 94% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/CustomTab.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/Extensions.kt index d6c9de7e68..3321c8979a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/web/CustomTab.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/Extensions.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc.web +package io.element.android.features.login.impl.oidc.customtab import android.content.ActivityNotFoundException import android.content.Context @@ -29,8 +29,7 @@ import androidx.browser.customtabs.CustomTabsSession * If several compatible browsers are installed, the user will be proposed to choose one. * Ref: https://developer.chrome.com/multidevice/android/customtabs. */ -fun openUrlInChromeCustomTab( - context: Context, +fun Context.openUrlInChromeCustomTab( session: CustomTabsSession?, darkTheme: Boolean, url: String @@ -58,7 +57,7 @@ fun openUrlInChromeCustomTab( .apply { intent.flags += Intent.FLAG_ACTIVITY_NEW_TASK } - .launchUrl(context, Uri.parse(url)) + .launchUrl(this, Uri.parse(url)) } catch (activityNotFoundException: ActivityNotFoundException) { // TODO context.toast(R.string.error_no_external_application_found) } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcEvents.kt similarity index 93% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcEvents.kt index 4f62c6476d..6265cfc85a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcEvents.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcEvents.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.webview import io.element.android.features.login.api.oidc.OidcAction diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcNode.kt similarity index 96% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcNode.kt index b1f9b45237..dd16b5e57b 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcNode.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.webview import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenter.kt similarity index 98% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenter.kt index f66caef5b2..66926b3734 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenter.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.webview import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcState.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcState.kt similarity index 93% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcState.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcState.kt index e9b2ac2355..fc9507a89d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcState.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcState.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.webview import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.auth.OidcDetails diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcStateProvider.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcStateProvider.kt similarity index 95% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcStateProvider.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcStateProvider.kt index 7a5552e719..80878cf8f8 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcStateProvider.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcStateProvider.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.webview import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.architecture.Async diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt similarity index 96% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt index 10834f5afa..47dd4f7a28 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.webview import android.webkit.WebView import androidx.activity.compose.BackHandler @@ -27,9 +27,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.viewinterop.AndroidView +import io.element.android.features.login.impl.oidc.OidcUrlParser import io.element.android.libraries.architecture.Async import io.element.android.libraries.core.bool.orFalse -import io.element.android.libraries.core.bool.orTrue import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcWebViewClient.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt similarity index 94% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcWebViewClient.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt index bd7ab99ad6..78c44dcf27 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/OidcWebViewClient.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.webview import android.annotation.TargetApi import android.os.Build @@ -23,7 +23,6 @@ import android.webkit.WebView import android.webkit.WebViewClient import timber.log.Timber -// TODO Move to a dedicated module class OidcWebViewClient(private val eventListener: WebViewEventListener) : WebViewClient() { @TargetApi(Build.VERSION_CODES.N) override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/WebViewEventListener.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/WebViewEventListener.kt similarity index 93% rename from features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/WebViewEventListener.kt rename to features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/WebViewEventListener.kt index 91fde6c311..acb5c082dc 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/WebViewEventListener.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/WebViewEventListener.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.webview interface WebViewEventListener { /** diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt index 5bf9f2ad05..ecf6533929 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt @@ -25,7 +25,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.saveable.rememberSaveable import io.element.android.features.login.api.oidc.OidcAction -import io.element.android.features.login.impl.oidc.web.DefaultOidcActionFlow +import io.element.android.features.login.impl.oidc.customtab.DefaultOidcActionFlow import io.element.android.features.login.impl.util.LoginConstants import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt similarity index 98% rename from features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt rename to features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt index f69cecd0f2..5756cd13d2 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/OidcPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/oidc/webview/OidcPresenterTest.kt @@ -16,7 +16,7 @@ @file:OptIn(ExperimentalCoroutinesApi::class) -package io.element.android.features.login.impl.oidc +package io.element.android.features.login.impl.oidc.webview import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow From 0e9c7bc15a5a906c50b981e974665c0e29871b47 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 14:58:25 +0200 Subject: [PATCH 33/75] Start CustomTab from Activity --- .../android/features/login/impl/LoginFlowNode.kt | 13 ++++++++++++- .../login/impl/oidc/customtab/CustomTabHandler.kt | 5 +++-- .../login/impl/oidc/customtab/Extensions.kt | 8 ++------ 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index 833bf4367b..d8f05832f9 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -16,9 +16,12 @@ package io.element.android.features.login.impl +import android.app.Activity import android.os.Parcelable import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.composable.Children import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node @@ -54,6 +57,8 @@ class LoginFlowNode @AssistedInject constructor( buildContext = buildContext, plugins = plugins, ) { + private var activity: Activity? = null + sealed interface NavTarget : Parcelable { @Parcelize object Root : NavTarget @@ -76,7 +81,7 @@ class LoginFlowNode @AssistedInject constructor( override fun onOidcDetails(oidcDetails: OidcDetails) { if (customTabAvailabilityChecker.supportCustomTab()) { // In this case open a Chrome Custom tab - customTabHandler.open(oidcDetails.url) + activity?.let { customTabHandler.open(it, oidcDetails.url) } } else { // Fallback to WebView mode backstack.push(NavTarget.OidcView(oidcDetails)) @@ -96,6 +101,12 @@ class LoginFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { + activity = LocalContext.current as? Activity + DisposableEffect(lifecycle) { + onDispose { + activity = null + } + } Children( navModel = backstack, modifier = modifier, diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt index 4a84dc76a7..059757657d 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt @@ -16,6 +16,7 @@ package io.element.android.features.login.impl.oidc.customtab +import android.app.Activity import android.content.ComponentName import android.content.Context import android.net.Uri @@ -71,7 +72,7 @@ class CustomTabHandler @Inject constructor( customTabsServiceConnection = null } - fun open(url: String) { - context.openUrlInChromeCustomTab(customTabsSession, false, url) + fun open(activity: Activity, url: String) { + activity.openUrlInChromeCustomTab(customTabsSession, false, url) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/Extensions.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/Extensions.kt index 3321c8979a..be98566e7c 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/Extensions.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/Extensions.kt @@ -16,9 +16,8 @@ package io.element.android.features.login.impl.oidc.customtab +import android.app.Activity import android.content.ActivityNotFoundException -import android.content.Context -import android.content.Intent import android.net.Uri import androidx.browser.customtabs.CustomTabColorSchemeParams import androidx.browser.customtabs.CustomTabsIntent @@ -29,7 +28,7 @@ import androidx.browser.customtabs.CustomTabsSession * If several compatible browsers are installed, the user will be proposed to choose one. * Ref: https://developer.chrome.com/multidevice/android/customtabs. */ -fun Context.openUrlInChromeCustomTab( +fun Activity.openUrlInChromeCustomTab( session: CustomTabsSession?, darkTheme: Boolean, url: String @@ -54,9 +53,6 @@ fun Context.openUrlInChromeCustomTab( // .setExitAnimations(context, R.anim.enter_fade_in, R.anim.exit_fade_out) .apply { session?.let { setSession(it) } } .build() - .apply { - intent.flags += Intent.FLAG_ACTIVITY_NEW_TASK - } .launchUrl(this, Uri.parse(url)) } catch (activityNotFoundException: ActivityNotFoundException) { // TODO context.toast(R.string.error_no_external_application_found) From 0766ec456ce1a9bc53de3ca4d078e57d92256eb0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 15:08:56 +0200 Subject: [PATCH 34/75] Oidc custom tab: avoid replay. --- .../login/impl/oidc/customtab/DefaultOidcActionFlow.kt | 4 ++++ .../android/features/login/impl/root/LoginRootPresenter.kt | 1 + 2 files changed, 5 insertions(+) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt index 87c22629a0..41ec484298 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt @@ -36,4 +36,8 @@ class DefaultOidcActionFlow @Inject constructor() : OidcActionFlow { suspend fun collect(lambda: suspend (OidcAction?) -> Unit) { mutableStateFlow.collect(lambda) } + + fun reset() { + mutableStateFlow.value = null + } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt index ecf6533929..f55c2030e7 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/root/LoginRootPresenter.kt @@ -166,5 +166,6 @@ class LoginRootPresenter @Inject constructor( } } } + defaultOidcActionFlow.reset() } } From 47af53049e9994282b43f0c7b7e373b7af1cdb12 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 15:21:39 +0200 Subject: [PATCH 35/75] Avoid Custom Chrome tab to appear as recent activity. --- app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 875f9f0435..f5eacd03a4 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,7 +33,7 @@ android:name=".MainActivity" android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|uiMode" android:exported="true" - android:launchMode="singleInstance" + android:launchMode="singleTop" android:theme="@style/Theme.ElementX.Splash" android:windowSoftInputMode="adjustResize"> From 4ecd7a241712952e9e6b96ed9761628cfef0597d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 15:26:03 +0200 Subject: [PATCH 36/75] Fix test script --- tools/adb/oidc.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/adb/oidc.sh b/tools/adb/oidc.sh index 52be556cbb..bcc519f313 100755 --- a/tools/adb/oidc.sh +++ b/tools/adb/oidc.sh @@ -18,7 +18,7 @@ # Format is: # Error -# adb shell am start -a android.intent.action.VIEW -d io.element:/callback?error=access_denied&state=IFF1UETGye2ZA8pO +# adb shell am start -a android.intent.action.VIEW -d "io.element:/callback?error=access_denied\\&state=IFF1UETGye2ZA8pO" # Success -adb shell am start -a android.intent.action.VIEW -d io.element:/callback?state=IFF1UETGye2ZA8pO&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB +adb shell am start -a android.intent.action.VIEW -d "io.element:/callback?state=IFF1UETGye2ZA8pO\\&code=y6X1GZeqA3xxOWcTeShgv8nkgFJXyzWB" From 142c459861aa64123ce5d8842b33ff401338f1af Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 15:37:37 +0200 Subject: [PATCH 37/75] Fix compilation and test --- .../login/impl/root/LoginRootPresenterTest.kt | 18 ++++++++++++++++++ .../android/samples/minimal/LoginScreen.kt | 6 +++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt index c072656e36..7548d50c57 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt @@ -20,6 +20,7 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.login.impl.oidc.customtab.DefaultOidcActionFlow import io.element.android.features.login.impl.util.LoginConstants import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails @@ -39,6 +40,7 @@ class LoginRootPresenterTest { fun `present - initial state`() = runTest { val presenter = LoginRootPresenter( FakeAuthenticationService(), + DefaultOidcActionFlow(), ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -56,8 +58,10 @@ class LoginRootPresenterTest { @Test fun `present - initial state server load`() = runTest { val authenticationService = FakeAuthenticationService() + val oidcActionFlow = DefaultOidcActionFlow() val presenter = LoginRootPresenter( authenticationService, + oidcActionFlow, ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -80,8 +84,10 @@ class LoginRootPresenterTest { @Test fun `present - initial state server load error and retry`() = runTest { val authenticationService = FakeAuthenticationService() + val oidcActionFlow = DefaultOidcActionFlow() val presenter = LoginRootPresenter( authenticationService, + oidcActionFlow, ) moleculeFlow(RecompositionClock.Immediate) { presenter.present() @@ -113,8 +119,10 @@ class LoginRootPresenterTest { @Test fun `present - enter login and password`() = runTest { val authenticationService = FakeAuthenticationService() + val oidcActionFlow = DefaultOidcActionFlow() val presenter = LoginRootPresenter( authenticationService, + oidcActionFlow, ) authenticationService.givenHomeserver(A_HOMESERVER) moleculeFlow(RecompositionClock.Immediate) { @@ -135,8 +143,10 @@ class LoginRootPresenterTest { @Test fun `present - oidc login`() = runTest { val authenticationService = FakeAuthenticationService() + val oidcActionFlow = DefaultOidcActionFlow() val presenter = LoginRootPresenter( authenticationService, + oidcActionFlow, ) authenticationService.givenHomeserver(A_HOMESERVER_OIDC) moleculeFlow(RecompositionClock.Immediate) { @@ -153,8 +163,10 @@ class LoginRootPresenterTest { @Test fun `present - oidc login error`() = runTest { val authenticationService = FakeAuthenticationService() + val oidcActionFlow = DefaultOidcActionFlow() val presenter = LoginRootPresenter( authenticationService, + oidcActionFlow, ) authenticationService.givenHomeserver(A_HOMESERVER_OIDC) authenticationService.givenOidcError(A_THROWABLE) @@ -172,8 +184,10 @@ class LoginRootPresenterTest { @Test fun `present - submit`() = runTest { val authenticationService = FakeAuthenticationService() + val oidcActionFlow = DefaultOidcActionFlow() val presenter = LoginRootPresenter( authenticationService, + oidcActionFlow, ) authenticationService.givenHomeserver(A_HOMESERVER) moleculeFlow(RecompositionClock.Immediate) { @@ -195,8 +209,10 @@ class LoginRootPresenterTest { @Test fun `present - submit with error`() = runTest { val authenticationService = FakeAuthenticationService() + val oidcActionFlow = DefaultOidcActionFlow() val presenter = LoginRootPresenter( authenticationService, + oidcActionFlow, ) authenticationService.givenHomeserver(A_HOMESERVER) moleculeFlow(RecompositionClock.Immediate) { @@ -219,8 +235,10 @@ class LoginRootPresenterTest { @Test fun `present - clear error`() = runTest { val authenticationService = FakeAuthenticationService() + val oidcActionFlow = DefaultOidcActionFlow() val presenter = LoginRootPresenter( authenticationService, + oidcActionFlow, ) authenticationService.givenHomeserver(A_HOMESERVER) moleculeFlow(RecompositionClock.Immediate) { diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt index e7ea426a65..9d9971a842 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/LoginScreen.kt @@ -19,6 +19,7 @@ package io.element.android.samples.minimal import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import io.element.android.features.login.impl.oidc.customtab.DefaultOidcActionFlow import io.element.android.features.login.impl.root.LoginRootPresenter import io.element.android.features.login.impl.root.LoginRootView import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService @@ -28,7 +29,10 @@ class LoginScreen(private val authenticationService: MatrixAuthenticationService @Composable fun Content(modifier: Modifier = Modifier) { val presenter = remember { - LoginRootPresenter(authenticationService = authenticationService) + LoginRootPresenter( + authenticationService = authenticationService, + DefaultOidcActionFlow() + ) } val state = presenter.present() LoginRootView( From 5ae2a199dc1465e33b405b2e543cf4b2a1071f84 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 16:17:46 +0200 Subject: [PATCH 38/75] Add test for oidc with custom tab. --- .../login/impl/root/LoginRootPresenterTest.kt | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt index 7548d50c57..0dee8d47c0 100644 --- a/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt +++ b/features/login/impl/src/test/kotlin/io/element/android/features/login/impl/root/LoginRootPresenterTest.kt @@ -20,6 +20,7 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.features.login.api.oidc.OidcAction import io.element.android.features.login.impl.oidc.customtab.DefaultOidcActionFlow import io.element.android.features.login.impl.util.LoginConstants import io.element.android.libraries.architecture.Async @@ -181,6 +182,50 @@ class LoginRootPresenterTest { } } + @Test + fun `present - oidc custom tab login`() = runTest { + val authenticationService = FakeAuthenticationService() + val oidcActionFlow = DefaultOidcActionFlow() + val presenter = LoginRootPresenter( + authenticationService, + oidcActionFlow, + ) + authenticationService.givenHomeserver(A_HOMESERVER_OIDC) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.submitEnabled).isTrue() + initialState.eventSink.invoke(LoginRootEvents.Submit) + val oidcState = awaitItem() + assertThat(oidcState.loggedInState).isEqualTo(LoggedInState.OidcStarted(A_OIDC_DATA)) + // Oidc cancel, sdk error + authenticationService.givenOidcCancelError(A_THROWABLE) + oidcActionFlow.post(OidcAction.GoBack) + val stateCancelSdkError = awaitItem() + assertThat(stateCancelSdkError.loggedInState).isEqualTo(LoggedInState.ErrorLoggingIn(A_THROWABLE)) + // Oidc cancel, sdk OK + authenticationService.givenOidcCancelError(null) + oidcActionFlow.post(OidcAction.GoBack) + val stateCancel = awaitItem() + assertThat(stateCancel.loggedInState).isEqualTo(LoggedInState.NotLoggedIn) + // Oidc success, sdk error + authenticationService.givenLoginError(A_THROWABLE) + oidcActionFlow.post(OidcAction.Success(A_OIDC_DATA.url)) + val stateSuccessSdkErrorLoading = awaitItem() + assertThat(stateSuccessSdkErrorLoading.loggedInState).isEqualTo(LoggedInState.LoggingIn) + val stateSuccessSdkError = awaitItem() + assertThat(stateSuccessSdkError.loggedInState).isEqualTo(LoggedInState.ErrorLoggingIn(A_THROWABLE)) + // Oidc success + authenticationService.givenLoginError(null) + oidcActionFlow.post(OidcAction.Success(A_OIDC_DATA.url)) + val stateSuccess = awaitItem() + assertThat(stateSuccess.loggedInState).isEqualTo(LoggedInState.LoggingIn) + val stateSuccessLoggedIn = awaitItem() + assertThat(stateSuccessLoggedIn.loggedInState).isEqualTo(LoggedInState.LoggedIn(A_SESSION_ID)) + } + } + @Test fun `present - submit`() = runTest { val authenticationService = FakeAuthenticationService() From f4809100d072077479d4d31062d8e7c1fbb71e7d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 17:36:17 +0200 Subject: [PATCH 39/75] Quality --- .../matrix/impl/auth/RustMatrixAuthenticationService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index b0409cfba3..aaf85ce8fc 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -141,7 +141,7 @@ class RustMatrixAuthenticationService @Inject constructor( } /** - * callbackUrl should be the uriRedirect from OidcClientMetadata (with all the parameters) + * callbackUrl should be the uriRedirect from OidcClientMetadata (with all the parameters). */ override suspend fun loginWithOidc(callbackUrl: String): Result { return withContext(coroutineDispatchers.io) { From e3c781ad766a0998119687b9576bd9537cc76624 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 18:00:53 +0200 Subject: [PATCH 40/75] Custom tab: dark theme support. --- .../io/element/android/features/login/impl/LoginFlowNode.kt | 5 ++++- .../features/login/impl/oidc/customtab/CustomTabHandler.kt | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index d8f05832f9..3b9f374bc4 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -39,6 +39,7 @@ import io.element.android.features.login.impl.root.LoginRootNode import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.createNode +import io.element.android.libraries.designsystem.theme.ElementTheme import io.element.android.libraries.di.AppScope import io.element.android.libraries.matrix.api.auth.OidcDetails import kotlinx.parcelize.Parcelize @@ -58,6 +59,7 @@ class LoginFlowNode @AssistedInject constructor( plugins = plugins, ) { private var activity: Activity? = null + private var darkTheme: Boolean = false sealed interface NavTarget : Parcelable { @Parcelize @@ -81,7 +83,7 @@ class LoginFlowNode @AssistedInject constructor( override fun onOidcDetails(oidcDetails: OidcDetails) { if (customTabAvailabilityChecker.supportCustomTab()) { // In this case open a Chrome Custom tab - activity?.let { customTabHandler.open(it, oidcDetails.url) } + activity?.let { customTabHandler.open(it, darkTheme, oidcDetails.url) } } else { // Fallback to WebView mode backstack.push(NavTarget.OidcView(oidcDetails)) @@ -102,6 +104,7 @@ class LoginFlowNode @AssistedInject constructor( @Composable override fun View(modifier: Modifier) { activity = LocalContext.current as? Activity + darkTheme = !ElementTheme.colors.isLight DisposableEffect(lifecycle) { onDispose { activity = null diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt index 059757657d..407459c5bf 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/CustomTabHandler.kt @@ -72,7 +72,7 @@ class CustomTabHandler @Inject constructor( customTabsServiceConnection = null } - fun open(activity: Activity, url: String) { - activity.openUrlInChromeCustomTab(customTabsSession, false, url) + fun open(activity: Activity, darkTheme: Boolean, url: String) { + activity.openUrlInChromeCustomTab(customTabsSession, darkTheme, url) } } From ad4141fcc989344268d95f038ee16cfbb3bac1d8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Mon, 24 Apr 2023 18:04:06 +0200 Subject: [PATCH 41/75] Cleanup --- .../io/element/android/features/login/impl/LoginFlowNode.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt index 3b9f374bc4..36153a33ba 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/LoginFlowNode.kt @@ -20,6 +20,7 @@ import android.app.Activity import android.os.Parcelable import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import com.bumble.appyx.core.composable.Children @@ -105,7 +106,7 @@ class LoginFlowNode @AssistedInject constructor( override fun View(modifier: Modifier) { activity = LocalContext.current as? Activity darkTheme = !ElementTheme.colors.isLight - DisposableEffect(lifecycle) { + DisposableEffect(Unit) { onDispose { activity = null } From f6302e4ed3dbf3504de26dbb0b8826ce336782a9 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 25 Apr 2023 16:02:38 +0200 Subject: [PATCH 42/75] better api --- .../login/impl/oidc/customtab/DefaultOidcActionFlow.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt index 41ec484298..17dfa8418f 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/customtab/DefaultOidcActionFlow.kt @@ -21,6 +21,7 @@ import io.element.android.features.login.api.oidc.OidcAction import io.element.android.features.login.api.oidc.OidcActionFlow import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.SingleIn +import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.MutableStateFlow import javax.inject.Inject @@ -33,8 +34,8 @@ class DefaultOidcActionFlow @Inject constructor() : OidcActionFlow { mutableStateFlow.value = oidcAction } - suspend fun collect(lambda: suspend (OidcAction?) -> Unit) { - mutableStateFlow.collect(lambda) + suspend fun collect(collector: FlowCollector) { + mutableStateFlow.collect(collector) } fun reset() { From d9831cb8ab88593bcbcc7b8538c9a9ac78646a5d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 15:50:25 +0200 Subject: [PATCH 43/75] Make the application compile with a SDK with no support for Oidc. --- .../features/login/impl/util/LoginConstants.kt | 2 +- .../impl/auth/AuthenticationException.kt | 2 ++ .../matrix/impl/auth/HomeserverDetails.kt | 2 +- .../libraries/matrix/impl/auth/OidcConfig.kt | 5 ++++- .../auth/RustMatrixAuthenticationService.kt | 18 +++++++++++++++--- 5 files changed, 23 insertions(+), 6 deletions(-) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt index c481fdf927..cb01f8095a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/util/LoginConstants.kt @@ -18,6 +18,6 @@ package io.element.android.features.login.impl.util object LoginConstants { - const val DEFAULT_HOMESERVER_URL = "synapse-oidc.lab.element.dev" // "matrix.org" + const val DEFAULT_HOMESERVER_URL = "matrix.org" // TODO Oidc "synapse-oidc.lab.element.dev" const val SLIDING_SYNC_READ_MORE_URL = "https://github.com/matrix-org/sliding-sync/blob/main/docs/Landing.md" } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt index b98ecc193f..c264a95f67 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/AuthenticationException.kt @@ -27,11 +27,13 @@ fun Throwable.mapAuthenticationException(): Throwable { is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(this.message!!) is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(this.message!!) + /* TODO Oidc is RustAuthenticationException.OidcException -> AuthenticationException.OidcError("OidcException", message!!) is RustAuthenticationException.OidcMetadataInvalid -> AuthenticationException.OidcError("OidcMetadataInvalid", message!!) is RustAuthenticationException.OidcMetadataMissing -> AuthenticationException.OidcError("OidcMetadataMissing", message!!) is RustAuthenticationException.OidcNotStarted -> AuthenticationException.OidcError("OidcNotStarted", message!!) is RustAuthenticationException.OidcNotSupported -> AuthenticationException.OidcError("OidcNotSupported", message!!) + */ else -> this } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt index f1d3e34bf8..a3d277c6da 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/HomeserverDetails.kt @@ -23,6 +23,6 @@ fun HomeserverLoginDetails.map(): MatrixHomeServerDetails = use { MatrixHomeServerDetails( url = url(), supportsPasswordLogin = supportsPasswordLogin(), - supportsOidcLogin = supportsOidcLogin(), + supportsOidcLogin = false // TODO Oidc supportsOidcLogin(), ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt index 774c22781b..1ba5063df9 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/OidcConfig.kt @@ -17,8 +17,10 @@ package io.element.android.libraries.matrix.impl.auth import io.element.android.libraries.matrix.api.auth.OidcConfig -import org.matrix.rustcomponents.sdk.OidcClientMetadata +// TODO Oidc +// import org.matrix.rustcomponents.sdk.OidcClientMetadata +/* val oidcClientMetadata: OidcClientMetadata = OidcClientMetadata( clientName = "Element", redirectUri = OidcConfig.redirectUri, @@ -26,4 +28,5 @@ val oidcClientMetadata: OidcClientMetadata = OidcClientMetadata( tosUri = "https://element.io/user-terms-of-service", policyUri = "https://element.io/privacy" ) + */ diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index aaf85ce8fc..4aea938e42 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -36,7 +36,8 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.ClientBuilder -import org.matrix.rustcomponents.sdk.OidcAuthenticationUrl +// TODO Oidc +// import org.matrix.rustcomponents.sdk.OidcAuthenticationUrl import org.matrix.rustcomponents.sdk.Session import org.matrix.rustcomponents.sdk.use import java.io.File @@ -55,7 +56,8 @@ class RustMatrixAuthenticationService @Inject constructor( private val authService: RustAuthenticationService = RustAuthenticationService( basePath = baseDirectory.absolutePath, passphrase = null, - oidcClientMetadata = oidcClientMetadata, + // TODO Oidc + // oidcClientMetadata = oidcClientMetadata, customSlidingSyncProxy = null ) private var currentHomeserver = MutableStateFlow(null) @@ -114,9 +116,12 @@ class RustMatrixAuthenticationService @Inject constructor( } } - private var pendingUrlForOidcLogin: OidcAuthenticationUrl? = null + // TODO Oidc + // private var pendingUrlForOidcLogin: OidcAuthenticationUrl? = null override suspend fun getOidcUrl(): Result { + TODO("Oidc") + /* return withContext(coroutineDispatchers.io) { runCatching { val urlForOidcLogin = authService.urlForOidcLogin() @@ -127,9 +132,12 @@ class RustMatrixAuthenticationService @Inject constructor( failure.mapAuthenticationException() } } + */ } override suspend fun cancelOidcLogin(): Result { + TODO("Oidc") + /* return withContext(coroutineDispatchers.io) { runCatching { pendingUrlForOidcLogin?.close() @@ -138,12 +146,15 @@ class RustMatrixAuthenticationService @Inject constructor( failure.mapAuthenticationException() } } + */ } /** * callbackUrl should be the uriRedirect from OidcClientMetadata (with all the parameters). */ override suspend fun loginWithOidc(callbackUrl: String): Result { + TODO("Oidc") + /* return withContext(coroutineDispatchers.io) { runCatching { val urlForOidcLogin = pendingUrlForOidcLogin ?: error("You need to call `getOidcUrl()` first") @@ -156,6 +167,7 @@ class RustMatrixAuthenticationService @Inject constructor( failure.mapAuthenticationException() } } + */ } private fun createMatrixClient(client: Client): MatrixClient { From 44bc6f5ef55188df5ec2307d5e4c6e659b7e1a01 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 16:00:37 +0200 Subject: [PATCH 44/75] Record screenshots --- ...aultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...aultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...ultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...aultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...aultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...ultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png | 3 +++ ...ultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 +++ ...up_LoginRootScreenDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...up_LoginRootScreenDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 3 +++ ...up_LoginRootScreenDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 3 +++ ...up_LoginRootScreenDarkPreview_0_null_8,NEXUS_5,1.0,en].png | 3 +++ ...up_LoginRootScreenDarkPreview_0_null_9,NEXUS_5,1.0,en].png | 3 +++ ...p_LoginRootScreenLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 ++-- ...p_LoginRootScreenLightPreview_0_null_6,NEXUS_5,1.0,en].png | 3 +++ ...p_LoginRootScreenLightPreview_0_null_7,NEXUS_5,1.0,en].png | 3 +++ ...p_LoginRootScreenLightPreview_0_null_8,NEXUS_5,1.0,en].png | 3 +++ ...p_LoginRootScreenLightPreview_0_null_9,NEXUS_5,1.0,en].png | 3 +++ 18 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_7,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_8,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_9,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_7,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_8,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_9,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..94817469d3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49e2768b2a111af737a30038913481113f759a20d3cf5df0166964c75926ec1f +size 6545 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..40f9c06277 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eac9030993d92ef7f57ae5e622d566cd88555a60c462c0299f2a16a0c3e5daa8 +size 6853 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..94817469d3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49e2768b2a111af737a30038913481113f759a20d3cf5df0166964c75926ec1f +size 6545 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7ae58a8744 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc.webview_null_DefaultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74a1a3ac6ae62c3573ce5834f5ae17952049ee77a8be76bfcb99e78b8fc6cb97 +size 6675 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..94817469d3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49e2768b2a111af737a30038913481113f759a20d3cf5df0166964c75926ec1f +size 6545 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..40f9c06277 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eac9030993d92ef7f57ae5e622d566cd88555a60c462c0299f2a16a0c3e5daa8 +size 6853 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..94817469d3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:49e2768b2a111af737a30038913481113f759a20d3cf5df0166964c75926ec1f +size 6545 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7ae58a8744 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.oidc_null_DefaultGroup_OidcViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:74a1a3ac6ae62c3573ce5834f5ae17952049ee77a8be76bfcb99e78b8fc6cb97 +size 6675 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 5f158821fc..3ace44456c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9d4aefe56727b0752db5a45f3e4444be134ef0ae1dcbc833acd9c914e726421b -size 34253 +oid sha256:db157015f3b440738db961d5152c693383457562158563fb2beb8ebb6e41feba +size 31570 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..5197332f89 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:03c71b78e0e64989d64526a245efbf7881a47c5d270e754811575e36d3520f33 +size 25303 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1536711dde --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:670769b82c2405be6b8360cf36ebab06551f6088899e9e0dc1e63d4102c904cd +size 24543 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..63690986c3 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:42121a3c5151b222724a05bbd6d2c6b624d5211a9365f4c7a2de21a31f0650de +size 20264 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8f27ef74ed --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenDarkPreview_0_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ccf9cbb300a4d0c45c4be54124e6e506ca1f53e7fa9c33db3544ab85110d3d35 +size 26052 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_1,NEXUS_5,1.0,en].png index 1db3e3234c..b20f7ad12e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65aad7d493d9bae55dda4ca2783c0f0a8ed5fbcf67835957b6ede1e8f2d8293e -size 33210 +oid sha256:e2ef182ba3721fa37c014dff57b49cbb7ee23d6becbc4ee60d81d44aff11a198 +size 30523 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..49257acefc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5cda5716e850b31997b49dafb975112b3a0578391cb39044ca0057de6379530f +size 24636 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_7,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..b20d358a12 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e338c8c2d3f89f3b7fdeafc5afffb36399855155e2b85231349e13e381e4cf1f +size 23617 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4313439757 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dea12dfe1c1799b1d2d765214b1bd911154a19a7794bf37c6663d179cec76c81 +size 19556 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ed81ad1335 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.login.impl.root_null_DefaultGroup_LoginRootScreenLightPreview_0_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1dc7d39a9b4e0f3eee8157b0290146b6bae5e6d0ea280296ddfc7051f31b95df +size 24786 From 31e0120f46f972d57c7a0b5681f68a0a8e4e56d8 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 16:38:46 +0200 Subject: [PATCH 45/75] Ignore temporary error. --- .../features/login/impl/oidc/webview/OidcWebViewClient.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt index 78c44dcf27..2f3a0aee9a 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt @@ -16,6 +16,7 @@ package io.element.android.features.login.impl.oidc.webview +import android.annotation.SuppressLint import android.annotation.TargetApi import android.os.Build import android.webkit.WebResourceRequest @@ -24,6 +25,8 @@ import android.webkit.WebViewClient import timber.log.Timber class OidcWebViewClient(private val eventListener: WebViewEventListener) : WebViewClient() { + // We will revert to API 23, in the mean time ignore the warning here. + @SuppressLint("ObsoleteSdkInt") @TargetApi(Build.VERSION_CODES.N) override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean { return shouldOverrideUrl(request.url.toString()) From ca59960e17b3525d7bcafa46c5f5884cbbd6ba4f Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Tue, 30 May 2023 19:11:42 +0000 Subject: [PATCH 46/75] Update dependency com.google.truth:truth to v1.1.4 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9783cb1cf7..150f35c885 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -119,7 +119,7 @@ test_barista = "com.adevinta.android:barista:4.3.0" test_hamcrest = "org.hamcrest:hamcrest:2.2" test_orchestrator = "androidx.test:orchestrator:1.4.2" test_turbine = "app.cash.turbine:turbine:0.13.0" -test_truth = "com.google.truth:truth:1.1.3" +test_truth = "com.google.truth:truth:1.1.4" test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.12" test_robolectric = "org.robolectric:robolectric:4.10.3" test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" } From bbacda4b451d5ccb616660ee87b6335172839b18 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 31 May 2023 10:34:03 +0200 Subject: [PATCH 47/75] Cleanup after PR review. --- docs/oidc.md | 2 ++ .../features/login/impl/oidc/webview/OidcView.kt | 13 +++++-------- .../login/impl/oidc/webview/OidcWebViewClient.kt | 7 ++++--- .../login/impl/oidc/webview/WebViewEventListener.kt | 6 ++---- .../matrix/test/auth/FakeAuthenticationService.kt | 7 ++++--- 5 files changed, 17 insertions(+), 18 deletions(-) diff --git a/docs/oidc.md b/docs/oidc.md index 7feae3ce83..5f9e70268d 100644 --- a/docs/oidc.md +++ b/docs/oidc.md @@ -1,3 +1,5 @@ +This file contains some rough notes about Oidc implementation, with some examples of actual data. + [ios implementation](https://github.com/vector-im/element-x-ios/compare/develop...doug/oidc-temp) Rust sdk branch: https://github.com/matrix-org/matrix-rust-sdk/tree/oidc-ffi diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt index 47dd4f7a28..c1235b76c5 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcView.kt @@ -21,7 +21,10 @@ import androidx.activity.compose.BackHandler import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview @@ -42,7 +45,7 @@ fun OidcView( modifier: Modifier = Modifier, ) { val oidcUrlParser = remember { OidcUrlParser() } - var webView: WebView? = null + var webView by remember { mutableStateOf(null) } fun shouldOverrideUrl(url: String): Boolean { val action = oidcUrlParser.parse(url) if (action != null) { @@ -53,11 +56,7 @@ fun OidcView( } val oidcWebViewClient = remember { - OidcWebViewClient(eventListener = object : WebViewEventListener { - override fun shouldOverrideUrlLoading(url: String): Boolean { - return shouldOverrideUrl(url) - } - }) + OidcWebViewClient(::shouldOverrideUrl) } BackHandler { @@ -71,8 +70,6 @@ fun OidcView( Box(modifier = modifier.statusBarsPadding()) { AndroidView( - modifier = Modifier - .statusBarsPadding(), factory = { context -> WebView(context).apply { webViewClient = oidcWebViewClient diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt index 2f3a0aee9a..7d8e789715 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/OidcWebViewClient.kt @@ -22,9 +22,10 @@ import android.os.Build import android.webkit.WebResourceRequest import android.webkit.WebView import android.webkit.WebViewClient -import timber.log.Timber -class OidcWebViewClient(private val eventListener: WebViewEventListener) : WebViewClient() { +class OidcWebViewClient( + private val eventListener: WebViewEventListener, +) : WebViewClient() { // We will revert to API 23, in the mean time ignore the warning here. @SuppressLint("ObsoleteSdkInt") @TargetApi(Build.VERSION_CODES.N) @@ -38,7 +39,7 @@ class OidcWebViewClient(private val eventListener: WebViewEventListener) : WebVi } private fun shouldOverrideUrl(url: String): Boolean { - Timber.d("shouldOverrideUrl: $url") + // Timber.d("shouldOverrideUrl: $url") return eventListener.shouldOverrideUrlLoading(url) } } diff --git a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/WebViewEventListener.kt b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/WebViewEventListener.kt index acb5c082dc..446754aced 100644 --- a/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/WebViewEventListener.kt +++ b/features/login/impl/src/main/kotlin/io/element/android/features/login/impl/oidc/webview/WebViewEventListener.kt @@ -16,14 +16,12 @@ package io.element.android.features.login.impl.oidc.webview -interface WebViewEventListener { +fun interface WebViewEventListener { /** * Triggered when a Webview loads an url. * * @param url The url about to be rendered. * @return true if the method needs to manage some custom handling */ - fun shouldOverrideUrlLoading(url: String): Boolean { - return false - } + fun shouldOverrideUrlLoading(url: String): Boolean } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt index 5b6b8b1e28..2b34a158a4 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/auth/FakeAuthenticationService.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.auth.OidcDetails import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.test.A_USER_ID +import io.element.android.libraries.matrix.test.FAKE_DELAY_IN_MS import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -58,12 +59,12 @@ class FakeAuthenticationService : MatrixAuthenticationService { } override suspend fun setHomeserver(homeserver: String): Result { - delay(100) + delay(FAKE_DELAY_IN_MS) return changeServerError?.let { Result.failure(it) } ?: Result.success(Unit) } override suspend fun login(username: String, password: String): Result { - delay(100) + delay(FAKE_DELAY_IN_MS) return loginError?.let { Result.failure(it) } ?: Result.success(A_USER_ID) } @@ -76,7 +77,7 @@ class FakeAuthenticationService : MatrixAuthenticationService { } override suspend fun loginWithOidc(callbackUrl: String): Result { - delay(100) + delay(FAKE_DELAY_IN_MS) return loginError?.let { Result.failure(it) } ?: Result.success(A_USER_ID) } From 1da242f63fc8c4b5d8f8803ebd16dc6ff0b9d3ca Mon Sep 17 00:00:00 2001 From: Marco Romano Date: Wed, 31 May 2023 11:24:01 +0200 Subject: [PATCH 48/75] Update pull_request.md (#490) Grammar nits. --- docs/pull_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/pull_request.md b/docs/pull_request.md index 323734bcaa..6144dd0d92 100644 --- a/docs/pull_request.md +++ b/docs/pull_request.md @@ -79,7 +79,7 @@ Exceptions can occur: **Important notice 1:** Releases are created from the `develop` branch. So `develop` branch should always contain a "releasable" source code. So when a feature is being implemented with several PRs, it has to be disabled by default (using a feature flag for instance), until the feature is fully implemented. A last PR to enable the feature can then be created. -**Important notice 2:** Database migration: some developers and some people from the community are using the nightly build from `develop`. Multiple database migrations should be properly handled for them. This is OK to have multiple migrations between 2 releases, this is not OK to add steps to the pending database migration on `develop`. So for instance `develop` users will migrate from version 11 to version 12, then 13, then 14, and `main` users will do all those steps after they get the app upgrade. +**Important notice 2:** Database migration: some developers and some people from the community are using the nightly build from `develop`. Multiple database migrations should be properly handled for them. It is OK to have multiple migrations between 2 releases, It is not OK to add steps to existing database migrations on `develop`. So for instance `develop` users will migrate from version 11 to version 12, then 13, then 14, and `main` users will do all those steps after they get the app upgrade. ##### PR Review Assignment From b83dc0fd61c8b2e3834308a731e6f91e82223c3d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 31 May 2023 11:40:32 +0200 Subject: [PATCH 49/75] Add codeStyles generated file from Android Studio to VCS. --- .idea/codeStyles/Project.xml | 123 +++++++++++++++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 ++ 2 files changed, 128 insertions(+) create mode 100644 .idea/codeStyles/Project.xml create mode 100644 .idea/codeStyles/codeStyleConfig.xml diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000000..7643783a82 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,123 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000000..79ee123c2b --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file From fccf194e7bab7507c66875a42b3a19c18bbe18e2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 31 May 2023 11:46:01 +0200 Subject: [PATCH 50/75] Formatting: set LINE_BREAK_AFTER_MULTILINE_WHEN_ENTRY to `false`. New default value for Android Studio is `true`. --- .idea/codeStyles/Project.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 7643783a82..cdef735570 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,6 +1,7 @@ + From f49e2dd4e387774ea93cc01159f341dac9ca6ef3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 31 May 2023 11:48:26 +0200 Subject: [PATCH 51/75] Add a shared dictionary to the project. --- .gitignore | 1 - .idea/dictionaries/shared.xml | 8 ++++++++ 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 .idea/dictionaries/shared.xml diff --git a/.gitignore b/.gitignore index 345da6fb97..cde20b0085 100644 --- a/.gitignore +++ b/.gitignore @@ -48,7 +48,6 @@ captures/ .idea/navEditor.xml .idea/tasks.xml .idea/workspace.xml -.idea/dictionaries .idea/libraries # Android Studio 3 in .gitignore file. .idea/caches diff --git a/.idea/dictionaries/shared.xml b/.idea/dictionaries/shared.xml new file mode 100644 index 0000000000..216a8cbd20 --- /dev/null +++ b/.idea/dictionaries/shared.xml @@ -0,0 +1,8 @@ + + + + backstack + textfields + + + From 7f71b2d81df8e24f6406297f26042dda14a75bb5 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 31 May 2023 15:18:42 +0200 Subject: [PATCH 52/75] Search for forbidden patterns in Kotlin files. --- .github/workflows/quality.yml | 8 ++ tools/check/check_code_quality.sh | 55 +++++++++ tools/check/forbidden_strings_in_code.txt | 131 ++++++++++++++++++++++ 3 files changed, 194 insertions(+) create mode 100755 tools/check/check_code_quality.sh create mode 100755 tools/check/forbidden_strings_in_code.txt diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml index 86e823f335..c945559c8d 100644 --- a/.github/workflows/quality.yml +++ b/.github/workflows/quality.yml @@ -12,6 +12,14 @@ env: CI_GRADLE_ARG_PROPERTIES: --stacktrace -PpreDexEnable=false --max-workers 2 --no-daemon --warn jobs: + checkScript: + name: Search for forbidden patterns + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Run code quality check suite + run: ./tools/check/check_code_quality.sh + check: name: Project Check Suite runs-on: ubuntu-latest diff --git a/tools/check/check_code_quality.sh b/tools/check/check_code_quality.sh new file mode 100755 index 0000000000..9e8c964499 --- /dev/null +++ b/tools/check/check_code_quality.sh @@ -0,0 +1,55 @@ +#!/usr/bin/env bash + +# +# Copyright 2023 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +####################################################################################################################### +# Search forbidden pattern +####################################################################################################################### + +searchForbiddenStringsScript=./tmp/search_forbidden_strings.pl + +if [[ -f ${searchForbiddenStringsScript} ]]; then + echo "${searchForbiddenStringsScript} already there" +else + mkdir tmp + echo "Get the script" + wget https://raw.githubusercontent.com/matrix-org/matrix-dev-tools/develop/bin/search_forbidden_strings.pl -O ${searchForbiddenStringsScript} +fi + +if [[ -x ${searchForbiddenStringsScript} ]]; then + echo "${searchForbiddenStringsScript} is already executable" +else + echo "Make the script executable" + chmod u+x ${searchForbiddenStringsScript} +fi + +echo +echo "Search for forbidden patterns in code..." + +# list all Kotlin folders of the project. +allKotlinDirs=`find . -type d |grep -v build |grep -v \.git |grep -v \.gradle |grep kotlin$` + +${searchForbiddenStringsScript} ./tools/check/forbidden_strings_in_code.txt $allKotlinDirs + +resultForbiddenStringInCode=$? + +if [[ ${resultForbiddenStringInCode} -eq 0 ]]; then + echo "MAIN OK" +else + echo "❌ MAIN ERROR" + exit 1 +fi diff --git a/tools/check/forbidden_strings_in_code.txt b/tools/check/forbidden_strings_in_code.txt new file mode 100755 index 0000000000..ae346c170d --- /dev/null +++ b/tools/check/forbidden_strings_in_code.txt @@ -0,0 +1,131 @@ +# +# Copyright 2023 New Vector Ltd +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +# This file list String which are not allowed in source code. +# Use Perl regex to write forbidden strings +# Note: line cannot start with a space. Use \s instead. +# It is possible to specify an authorized number of occurrence with === suffix. Default is 0 +# Example: +# AuthorizedStringThreeTimes===3 + +# Extension:kt + +### No import static: use full class name +import static + +### Rubbish from merge. Please delete those lines (sometimes in comment) +<<<<<<< +>>>>>>> + +### carry return before "}". Please remove empty lines. +\n\s*\n\s*\} + +### typo detected. +formated +abtract +Succes[^s] +succes[^s] + +### Use int instead of Integer +protected Integer + +### Use the interface declaration. Example: use type "Map" instead of type "HashMap" to declare variable or parameter. For Kotlin, use mapOf, setOf, ... +(private|public|protected| ) (static )?(final )?(HashMap|HashSet|ArrayList)< + +### Use int instead of short +Short\.parseShort +\(short\) +private short +final short + +### Line length is limited to 160 chars. Please split long lines +#[^─]{161} + +### "DO NOT COMMIT" has been committed +DO NOT COMMIT + +### invalid formatting +\s{8}/\*\n \* +# Now checked by ktlint +# [^\w]if\( +# while\( +# for\( + +# Add space after // +# DISABLED To re-enable when code will be formatted globally +#^\s*//[^\s] + +### invalid formatting (too many space char) +^ /\* + +### unnecessary parenthesis around numbers, example: " (0)" + \(\d+\) + +### import the package, do not use long class name with package +android\.os\.Build\. + +### Tab char is forbidden. Use only spaces +\t + +# Empty lines and trailing space +# DISABLED To re-enable when code will be formatted globally +#[ ]$ + +### Deprecated, use retrofit2.HttpException +import retrofit2\.adapter\.rxjava\.HttpException + +### This is generally not necessary, no need to reset the padding if there is no drawable +setCompoundDrawablePadding\(0\) + +# Change thread with Rx +# DISABLED +#runOnUiThread + +### Bad formatting of chain (missing new line) +\w\.flatMap\( + +### In Kotlin, Void has to be null safe, i.e. use 'Void?' instead of 'Void' +\: Void\) + +### Kotlin conversion tools introduce this, but is can be replace by trim() +trim \{ it \<\= \' \' \} + +### Put the operator at the beginning of next line + ==$ + +### Use JsonUtils.getBasicGson() +new Gson\(\) + +### Use TextUtils.formatFileSize +Formatter\.formatFileSize===1 + +### Use TextUtils.formatFileSize with short format param to true +Formatter\.formatShortFileSize===1 + +### Use `Context#getSystemService` extension function provided by `core-ktx` +getSystemService\(Context + +### Use DefaultSharedPreferences.getInstance() instead for better performance +PreferenceManager\.getDefaultSharedPreferences==2 + +### Use the Clock interface, or use `measureTimeMillis` +System\.currentTimeMillis\(\)===1 + +### Remove extra space between the name and the description +\* @\w+ \w+ + + +### Suspicious String template. Please check that the string template will behave as expected, i.e. the class field and not the whole object will be used. For instance `Timber.d("$event.type")` is not correct, you should write `Timber.d("${event.type}")`. In the former the whole event content will be logged, since it's a data class. If this is expected (i.e. to fix false positive), please add explicit curly braces (`{` and `}`) around the variable, for instance `"elementLogs.${i}.txt"` +\$[a-zA-Z_]\w*\??\.[a-zA-Z_] From becbc6607d256a9b107cf51d2ecc91f856189d79 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 31 May 2023 15:31:05 +0200 Subject: [PATCH 53/75] Fix detected forbidden pattern. --- libraries/matrix/impl/build.gradle.kts | 1 + .../libraries/matrix/impl/RustMatrixClient.kt | 3 ++ .../auth/RustMatrixAuthenticationService.kt | 4 +- .../matrix/impl/room/RustMatrixRoom.kt | 6 ++- .../libraries/push/impl/PushersManager.kt | 3 +- .../notifications/NotifiableEventResolver.kt | 50 +++++++++---------- .../impl/di/SessionStorageModule.kt | 4 +- 7 files changed, 39 insertions(+), 32 deletions(-) diff --git a/libraries/matrix/impl/build.gradle.kts b/libraries/matrix/impl/build.gradle.kts index 6b76a17985..1ac5b4b507 100644 --- a/libraries/matrix/impl/build.gradle.kts +++ b/libraries/matrix/impl/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { // api(projects.libraries.rustsdk) implementation(libs.matrix.sdk) implementation(projects.libraries.di) + implementation(projects.services.toolbox.api) api(projects.libraries.matrix.api) implementation(libs.dagger) implementation(projects.libraries.core) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index bac98dd852..ce76980a6f 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -44,6 +44,7 @@ import io.element.android.libraries.matrix.impl.usersearch.UserProfileMapper import io.element.android.libraries.matrix.impl.usersearch.UserSearchResultMapper import io.element.android.libraries.matrix.impl.verification.RustSessionVerificationService import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -77,6 +78,7 @@ class RustMatrixClient constructor( private val coroutineScope: CoroutineScope, private val dispatchers: CoroutineDispatchers, private val baseDirectory: File, + private val clock: SystemClock, ) : MatrixClient { override val sessionId: UserId = UserId(client.userId()) @@ -215,6 +217,7 @@ class RustMatrixClient constructor( innerRoom = fullRoom, coroutineScope = coroutineScope, coroutineDispatchers = dispatchers, + clock = clock, ) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index aff83c3b5a..f03ba4562c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -25,10 +25,10 @@ import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.impl.RustMatrixClient import io.element.android.libraries.sessionstorage.api.SessionData import io.element.android.libraries.sessionstorage.api.SessionStore +import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -49,6 +49,7 @@ class RustMatrixAuthenticationService @Inject constructor( private val coroutineScope: CoroutineScope, private val coroutineDispatchers: CoroutineDispatchers, private val sessionStore: SessionStore, + private val clock: SystemClock, ) : MatrixAuthenticationService { private val authService: RustAuthenticationService = RustAuthenticationService(baseDirectory.absolutePath, null, null) @@ -115,6 +116,7 @@ class RustMatrixAuthenticationService @Inject constructor( coroutineScope = coroutineScope, dispatchers = coroutineDispatchers, baseDirectory = baseDirectory, + clock = clock, ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index 55576c7b96..c21ceb5076 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.impl.media.map import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline +import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -54,6 +55,7 @@ class RustMatrixRoom( private val innerRoom: Room, private val coroutineScope: CoroutineScope, private val coroutineDispatchers: CoroutineDispatchers, + private val clock: SystemClock, ) : MatrixRoom { override val membersStateFlow: StateFlow @@ -77,9 +79,9 @@ class RustMatrixRoom( it.rooms.contains(roomId.value) } .map { - System.currentTimeMillis() + clock.epochMillis() } - .onStart { emit(System.currentTimeMillis()) } + .onStart { emit(clock.epochMillis()) } } override fun timeline(): MatrixTimeline { diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt index 04d2875328..8bb1ddf20c 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/PushersManager.kt @@ -115,8 +115,7 @@ class PushersManager @Inject constructor( appDisplayName = appName, deviceDisplayName = currentSession.sessionParams.deviceId ?: "MOBILE" ) - - */ + */ } fun getPusherForCurrentSession() {}/*: Pusher? { diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index 82e3e43d50..de168090f4 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -71,32 +71,32 @@ class NotifiableEventResolver @Inject constructor( return notificationData.asNotifiableEvent(sessionId) } -} -private fun NotificationData.asNotifiableEvent(userId: SessionId): NotifiableEvent { - return NotifiableMessageEvent( - sessionId = userId, - roomId = roomId, - eventId = eventId, - editedEventId = null, - canBeReplaced = true, - noisy = isNoisy, - timestamp = System.currentTimeMillis(), - senderName = senderDisplayName, - senderId = senderId.value, - body = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", - imageUriString = null, - threadId = null, - roomName = null, - roomIsDirect = false, - roomAvatarPath = roomAvatarUrl, - senderAvatarPath = senderAvatarUrl, - soundName = null, - outGoingMessage = false, - outGoingMessageFailed = false, - isRedacted = false, - isUpdated = false - ) + private fun NotificationData.asNotifiableEvent(userId: SessionId): NotifiableEvent { + return NotifiableMessageEvent( + sessionId = userId, + roomId = roomId, + eventId = eventId, + editedEventId = null, + canBeReplaced = true, + noisy = isNoisy, + timestamp = clock.epochMillis(), + senderName = senderDisplayName, + senderId = senderId.value, + body = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", + imageUriString = null, + threadId = null, + roomName = null, + roomIsDirect = false, + roomAvatarPath = roomAvatarUrl, + senderAvatarPath = senderAvatarUrl, + soundName = null, + outGoingMessage = false, + outGoingMessageFailed = false, + isRedacted = false, + isUpdated = false + ) + } } /** diff --git a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt index 052943388e..323aa93bb7 100644 --- a/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt +++ b/libraries/session-storage/impl/src/main/kotlin/io/element/android/libraries/sessionstorage/impl/di/SessionStorageModule.kt @@ -34,10 +34,10 @@ object SessionStorageModule { @SingleIn(AppScope::class) fun provideMatrixDatabase(@ApplicationContext context: Context): SessionDatabase { val name = "session_database" - val secretFile = context.getDatabasePath("$name.key") + val secretFile = context.getDatabasePath("${name}.key") val passphraseProvider = RandomSecretPassphraseProvider(context, secretFile) val driver = SqlCipherDriverFactory(passphraseProvider) - .create(SessionDatabase.Schema, "$name.db", context) + .create(SessionDatabase.Schema, "${name}.db", context) return SessionDatabase(driver) } } From d4284f167ac2af3aa4c7f40114186be5f4f1d773 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 31 May 2023 15:47:24 +0200 Subject: [PATCH 54/75] Fix compilation issue. --- .../kotlin/io/element/android/samples/minimal/MainActivity.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt index ef776c613c..12481b81e1 100644 --- a/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt +++ b/samples/minimal/src/main/kotlin/io/element/android/samples/minimal/MainActivity.kt @@ -30,6 +30,7 @@ import io.element.android.libraries.designsystem.theme.ElementTheme import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.impl.auth.RustMatrixAuthenticationService import io.element.android.libraries.sessionstorage.impl.memory.InMemorySessionStore +import io.element.android.services.toolbox.impl.systemclock.DefaultSystemClock import kotlinx.coroutines.runBlocking import java.io.File @@ -43,6 +44,7 @@ class MainActivity : ComponentActivity() { coroutineScope = Singleton.appScope, coroutineDispatchers = Singleton.coroutineDispatchers, sessionStore = InMemorySessionStore(), + clock = DefaultSystemClock() ) } From c74978867721a007588748958ce8a8cdaf6763a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Wed, 31 May 2023 16:46:58 +0200 Subject: [PATCH 55/75] Enable gif support for Coil --- gradle/libs.versions.toml | 1 + libraries/matrixui/build.gradle.kts | 1 + .../libraries/matrix/ui/media/ImageLoaderFactories.kt | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 150f35c885..7663a06c78 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -127,6 +127,7 @@ test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = " # Others coil = { module = "io.coil-kt:coil", version.ref = "coil" } coil_compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } +coil_gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "datetime" } serialization_json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "serialization_json" } showkase = { module = "com.airbnb.android:showkase", version.ref = "showkase" } diff --git a/libraries/matrixui/build.gradle.kts b/libraries/matrixui/build.gradle.kts index 380f19f792..7302eeddfb 100644 --- a/libraries/matrixui/build.gradle.kts +++ b/libraries/matrixui/build.gradle.kts @@ -39,6 +39,7 @@ dependencies { implementation(projects.libraries.core) implementation(projects.libraries.uiStrings) implementation(libs.coil.compose) + implementation(libs.coil.gif) ksp(libs.showkase.processor) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt index d1fab05544..9038e03611 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/ImageLoaderFactories.kt @@ -17,8 +17,11 @@ package io.element.android.libraries.matrix.ui.media import android.content.Context +import android.os.Build import coil.ImageLoader import coil.ImageLoaderFactory +import coil.decode.GifDecoder +import coil.decode.ImageDecoderDecoder import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.MatrixClient import okhttp3.OkHttpClient @@ -34,6 +37,12 @@ class LoggedInImageLoaderFactory @Inject constructor( .Builder(context) .okHttpClient(okHttpClient) .components { + // Add gif support + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + add(ImageDecoderDecoder.Factory()) + } else { + add(GifDecoder.Factory()) + } add(AvatarDataKeyer()) add(MediaRequestDataKeyer()) add(CoilMediaFetcher.AvatarFactory(matrixClient)) From 6bc48084b83c3fbac3fc02ccf902e5c34320e1ad Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Wed, 31 May 2023 17:57:50 +0000 Subject: [PATCH 56/75] Update plugin sonarqube to v4.2.0.3129 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 43d18e320c..6e949900a0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -180,6 +180,6 @@ ktlint = "org.jlleitschuh.gradle.ktlint:11.3.2" dependencygraph = { id = "com.savvasdalkitsis.module-dependency-graph", version.ref = "dependencygraph" } dependencycheck = { id = "org.owasp.dependencycheck", version.ref = "dependencycheck" } paparazzi = "app.cash.paparazzi:1.2.0" -sonarqube = "org.sonarqube:4.1.0.3113" +sonarqube = "org.sonarqube:4.2.0.3129" kover = "org.jetbrains.kotlinx.kover:0.6.1" sqldelight = { id = "com.squareup.sqldelight", version.ref = "sqldelight" } From 473bfd1e23dc59a5888a68063315e84ef8d24745 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 1 Jun 2023 09:40:45 +0100 Subject: [PATCH 57/75] Display a notice if Matrix ID isn't resolved (#461) Display a notice if Matrix ID isn't resolved If we can't get the profile of a user after an mxid was searched for, show a warning under their ID to say the invite probably won't be delivered. Closes #424 --- changelog.d/424.feature | 1 + .../SearchMultipleUsersResultItem.kt | 45 +++--- .../components/SearchSingleUserResultItem.kt | 39 +++-- .../impl/components/SearchUserBar.kt | 19 +-- .../impl/userlist/DefaultUserListPresenter.kt | 4 +- .../createroom/impl/userlist/UserListState.kt | 3 +- .../impl/userlist/UserListStateProvider.kt | 5 +- .../src/main/res/values-es/translations.xml | 2 +- .../src/main/res/values-it/translations.xml | 2 +- .../src/main/res/values-ro/translations.xml | 2 +- .../userlist/DefaultUserListPresenterTests.kt | 9 +- .../roomdetails/impl/RoomDetailsView.kt | 5 +- .../impl/invite/RoomInviteMembersPresenter.kt | 9 +- .../impl/invite/RoomInviteMembersState.kt | 1 + .../invite/RoomInviteMembersStateProvider.kt | 14 ++ .../impl/invite/RoomInviteMembersView.kt | 43 +++-- .../invite/RoomInviteMembersPresenterTest.kt | 57 ++++++- .../matrix/ui/components/UnresolvedUserRow.kt | 150 ++++++++++++++++++ .../usersearch/api/UserRepository.kt | 3 +- .../usersearch/api/UserSearchResult.kt | 24 +++ .../usersearch/impl/MatrixUserRepository.kt | 17 +- .../impl/MatrixUserRepositoryTest.kt | 17 +- .../usersearch/test/FakeUserRepository.kt | 8 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ItemDarkPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...temLightPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...sultItemPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...ItemDarkPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...temLightPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...sultItemPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...onDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...onDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...onDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...onDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...nLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ViewDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...iewLightPreview_0_null,NEXUS_5,1.0,en].png | 2 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wDarkPreview_0_null_10,NEXUS_5,1.0,en].png | 2 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 2 +- ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 2 +- ...ewDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 2 +- ...ewDarkPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_8,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_9,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...LightPreview_0_null_10,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_8,NEXUS_5,1.0,en].png | 2 +- ...wLightPreview_0_null_9,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...reenDarkPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...eenLightPreview_0_null,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 2 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 3 + ...sLightPreview_0_null_6,NEXUS_5,1.0,en].png | 3 + ...dUserRowPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...dUserRowPreview_0_null,NEXUS_5,1.0,en].png | 3 + 89 files changed, 503 insertions(+), 216 deletions(-) create mode 100644 changelog.d/424.feature create mode 100644 libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt create mode 100644 libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserSearchResult.kt delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemLightPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemLightPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png diff --git a/changelog.d/424.feature b/changelog.d/424.feature new file mode 100644 index 0000000000..1fb267318e --- /dev/null +++ b/changelog.d/424.feature @@ -0,0 +1 @@ +[Create and join rooms] Show a notice for MXIDs that don't resolve when searching for users to invite diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchMultipleUsersResultItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchMultipleUsersResultItem.kt index cf13768a82..7dd0ce3e3c 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchMultipleUsersResultItem.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchMultipleUsersResultItem.kt @@ -22,40 +22,49 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.preview.ElementPreviewDark -import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.matrix.ui.components.CheckableMatrixUserRow +import io.element.android.libraries.matrix.ui.components.CheckableUnresolvedUserRow import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.usersearch.api.UserSearchResult @Composable fun SearchMultipleUsersResultItem( - matrixUser: MatrixUser, + searchResult: UserSearchResult, isUserSelected: Boolean, modifier: Modifier = Modifier, onCheckedChange: (Boolean) -> Unit = {}, ) { - CheckableMatrixUserRow( - checked = isUserSelected, - modifier = modifier, - matrixUser = matrixUser, - avatarSize = AvatarSize.Custom(36.dp), - onCheckedChange = onCheckedChange, - ) + if (searchResult.isUnresolved) { + CheckableUnresolvedUserRow( + checked = isUserSelected, + modifier = modifier, + avatarData = searchResult.matrixUser.getAvatarData(AvatarSize.Custom(36.dp)), + id = searchResult.matrixUser.userId.value, + onCheckedChange = onCheckedChange, + ) + } else { + CheckableMatrixUserRow( + checked = isUserSelected, + modifier = modifier, + matrixUser = searchResult.matrixUser, + avatarSize = AvatarSize.Custom(36.dp), + onCheckedChange = onCheckedChange, + ) + } } @Preview @Composable -internal fun SearchMultipleUsersResultItemLightPreview() = ElementPreviewLight { ContentToPreview() } - -@Preview -@Composable -internal fun SearchMultipleUsersResultItemDarkPreview() = ElementPreviewDark { ContentToPreview() } +internal fun SearchMultipleUsersResultItemPreview() = ElementThemedPreview { ContentToPreview() } @Composable private fun ContentToPreview() { Column { - SearchMultipleUsersResultItem(matrixUser = aMatrixUser(), isUserSelected = true) - SearchMultipleUsersResultItem(matrixUser = aMatrixUser(), isUserSelected = false) + SearchMultipleUsersResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = false), isUserSelected = false) + SearchMultipleUsersResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = false), isUserSelected = true) + SearchMultipleUsersResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = true), isUserSelected = false) + SearchMultipleUsersResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = true), isUserSelected = true) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchSingleUserResultItem.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchSingleUserResultItem.kt index 8dcaeb2bdf..f1279cfdec 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchSingleUserResultItem.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchSingleUserResultItem.kt @@ -17,39 +17,48 @@ package io.element.android.features.createroom.impl.components import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.element.android.libraries.designsystem.components.avatar.AvatarSize -import io.element.android.libraries.designsystem.preview.ElementPreviewDark -import io.element.android.libraries.designsystem.preview.ElementPreviewLight -import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.designsystem.preview.ElementThemedPreview import io.element.android.libraries.matrix.ui.components.MatrixUserRow +import io.element.android.libraries.matrix.ui.components.UnresolvedUserRow import io.element.android.libraries.matrix.ui.components.aMatrixUser +import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.usersearch.api.UserSearchResult @Composable fun SearchSingleUserResultItem( - matrixUser: MatrixUser, + searchResult: UserSearchResult, modifier: Modifier = Modifier, onClick: () -> Unit = {}, ) { - MatrixUserRow( - modifier = modifier.clickable(onClick = onClick), - matrixUser = matrixUser, - avatarSize = AvatarSize.Custom(36.dp), - ) + if (searchResult.isUnresolved) { + UnresolvedUserRow( + modifier = modifier.clickable(onClick = onClick), + avatarData = searchResult.matrixUser.getAvatarData(AvatarSize.Custom(36.dp)), + id = searchResult.matrixUser.userId.value, + ) + } else { + MatrixUserRow( + modifier = modifier.clickable(onClick = onClick), + matrixUser = searchResult.matrixUser, + avatarSize = AvatarSize.Custom(36.dp), + ) + } } @Preview @Composable -internal fun SearchSingleUserResultItemLightPreview() = ElementPreviewLight { ContentToPreview() } - -@Preview -@Composable -internal fun SearchSingleUserResultItemDarkPreview() = ElementPreviewDark { ContentToPreview() } +internal fun SearchSingleUserResultItemPreview() = ElementThemedPreview{ ContentToPreview() } @Composable private fun ContentToPreview() { - SearchSingleUserResultItem(matrixUser = aMatrixUser()) + Column { + SearchSingleUserResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = false)) + SearchSingleUserResultItem(searchResult = UserSearchResult(aMatrixUser(), isUnresolved = true)) + } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchUserBar.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchUserBar.kt index be87e1cf51..bbbcd715e5 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchUserBar.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/SearchUserBar.kt @@ -29,12 +29,13 @@ import io.element.android.libraries.designsystem.theme.components.SearchBarResul import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.ui.components.SelectedUsersList import io.element.android.libraries.ui.strings.R +import io.element.android.libraries.usersearch.api.UserSearchResult import kotlinx.collections.immutable.ImmutableList @Composable fun SearchUserBar( query: String, - state: SearchBarResultState>, + state: SearchBarResultState>, selectedUsers: ImmutableList, active: Boolean, isMultiSelectionEnabled: Boolean, @@ -68,26 +69,26 @@ fun SearchUserBar( resultHandler = { users -> LazyColumn { if (isMultiSelectionEnabled) { - items(users) { matrixUser -> + items(users) { searchResult -> SearchMultipleUsersResultItem( modifier = Modifier.fillMaxWidth(), - matrixUser = matrixUser, - isUserSelected = selectedUsers.find { it.userId == matrixUser.userId } != null, + searchResult = searchResult, + isUserSelected = selectedUsers.find { it.userId == searchResult.matrixUser.userId } != null, onCheckedChange = { checked -> if (checked) { - onUserSelected(matrixUser) + onUserSelected(searchResult.matrixUser) } else { - onUserDeselected(matrixUser) + onUserDeselected(searchResult.matrixUser) } } ) } } else { - items(users) { matrixUser -> + items(users) { searchResult -> SearchSingleUserResultItem( modifier = Modifier.fillMaxWidth(), - matrixUser = matrixUser, - onClick = { onUserSelected(matrixUser) } + searchResult = searchResult, + onClick = { onUserSelected(searchResult.matrixUser) } ) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenter.kt index 2efbdb7b9a..867fdc9a60 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenter.kt @@ -30,8 +30,8 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.di.SessionScope -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.usersearch.api.UserRepository +import io.element.android.libraries.usersearch.api.UserSearchResult import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList @@ -56,7 +56,7 @@ class DefaultUserListPresenter @AssistedInject constructor( var isSearchActive by rememberSaveable { mutableStateOf(false) } val selectedUsers by userListDataStore.selectedUsers().collectAsState(emptyList()) var searchQuery by rememberSaveable { mutableStateOf("") } - var searchResults: SearchBarResultState> by remember { + var searchResults: SearchBarResultState> by remember { mutableStateOf(SearchBarResultState.NotSearching()) } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/UserListState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/UserListState.kt index d8ff391163..60a5bea506 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/UserListState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/UserListState.kt @@ -18,11 +18,12 @@ package io.element.android.features.createroom.impl.userlist import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.usersearch.api.UserSearchResult import kotlinx.collections.immutable.ImmutableList data class UserListState( val searchQuery: String, - val searchResults: SearchBarResultState>, + val searchResults: SearchBarResultState>, val selectedUsers: ImmutableList, val isSearchActive: Boolean, val selectionMode: SelectionMode, diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/UserListStateProvider.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/UserListStateProvider.kt index cb8bf284b0..31d1f6953a 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/UserListStateProvider.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/userlist/UserListStateProvider.kt @@ -19,6 +19,7 @@ package io.element.android.features.createroom.impl.userlist import androidx.compose.ui.tooling.preview.PreviewParameterProvider import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import io.element.android.libraries.usersearch.api.UserSearchResult import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList @@ -38,14 +39,14 @@ open class UserListStateProvider : PreviewParameterProvider { isSearchActive = true, searchQuery = "@someone:matrix.org", selectedUsers = aListOfSelectedUsers(), - searchResults = SearchBarResultState.Results(aMatrixUserList().toImmutableList()), + searchResults = SearchBarResultState.Results(aMatrixUserList().map { UserSearchResult(it) }.toImmutableList()), ), aUserListState().copy( isSearchActive = true, searchQuery = "@someone:matrix.org", selectionMode = SelectionMode.Multiple, selectedUsers = aListOfSelectedUsers(), - searchResults = SearchBarResultState.Results(aMatrixUserList().toImmutableList()), + searchResults = SearchBarResultState.Results(aMatrixUserList().map { UserSearchResult(it) }.toImmutableList()), ), aUserListState().copy( isSearchActive = true, diff --git a/features/createroom/impl/src/main/res/values-es/translations.xml b/features/createroom/impl/src/main/res/values-es/translations.xml index 9a3030ba71..9a5d672fd4 100644 --- a/features/createroom/impl/src/main/res/values-es/translations.xml +++ b/features/createroom/impl/src/main/res/values-es/translations.xml @@ -5,4 +5,4 @@ "Añadir personas" "Se ha producido un error al intentar iniciar un chat" "Crear una sala" - \ No newline at end of file + diff --git a/features/createroom/impl/src/main/res/values-it/translations.xml b/features/createroom/impl/src/main/res/values-it/translations.xml index 214cd9406d..ceddb71154 100644 --- a/features/createroom/impl/src/main/res/values-it/translations.xml +++ b/features/createroom/impl/src/main/res/values-it/translations.xml @@ -5,4 +5,4 @@ "Aggiungi persone" "Si è verificato un errore durante il tentativo di avviare una chat" "Crea una stanza" - \ No newline at end of file + diff --git a/features/createroom/impl/src/main/res/values-ro/translations.xml b/features/createroom/impl/src/main/res/values-ro/translations.xml index 55b9f6d692..e546e55587 100644 --- a/features/createroom/impl/src/main/res/values-ro/translations.xml +++ b/features/createroom/impl/src/main/res/values-ro/translations.xml @@ -14,4 +14,4 @@ "Despre ce este această cameră?" "A apărut o eroare la încercarea începerii conversației" "Creați o cameră" - \ No newline at end of file + diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt index 40b7e6549b..745bdf74f9 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/userlist/DefaultUserListPresenterTests.kt @@ -23,6 +23,7 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.designsystem.theme.components.SearchBarResultState import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import io.element.android.libraries.usersearch.api.UserSearchResult import io.element.android.libraries.usersearch.test.FakeUserRepository import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.test.runTest @@ -130,18 +131,18 @@ class DefaultUserListPresenterTests { skipItems(2) // When the user repository emits a result, it's copied to the state - userRepository.emitResult(listOf(aMatrixUser())) + userRepository.emitResult(listOf(UserSearchResult(aMatrixUser()))) assertThat(awaitItem().searchResults).isEqualTo( SearchBarResultState.Results( - persistentListOf(aMatrixUser()) + persistentListOf(UserSearchResult(aMatrixUser())) ) ) // When the user repository emits another result, it replaces the previous value - userRepository.emitResult(aMatrixUserList()) + userRepository.emitResult(aMatrixUserList().map { UserSearchResult(it) }) assertThat(awaitItem().searchResults).isEqualTo( SearchBarResultState.Results( - aMatrixUserList() + aMatrixUserList().map { UserSearchResult(it) } ) ) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 7d90342f65..78c26c4ae5 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -45,9 +45,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp +import io.element.android.features.leaveroom.api.LeaveRoomView import io.element.android.features.roomdetails.impl.blockuser.BlockUserDialogs import io.element.android.features.roomdetails.impl.blockuser.BlockUserSection -import io.element.android.features.leaveroom.api.LeaveRoomView import io.element.android.features.roomdetails.impl.members.details.RoomMemberHeaderSection import io.element.android.features.roomdetails.impl.members.details.RoomMemberMainActionsSection import io.element.android.libraries.architecture.isLoading @@ -67,6 +67,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.ui.strings.R as StringR @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) @Composable @@ -185,7 +186,7 @@ internal fun RoomHeaderSection( @Composable internal fun TopicSection(roomTopic: String, modifier: Modifier = Modifier) { - PreferenceCategory(title = stringResource(R.string.screen_room_details_topic_title), modifier = modifier) { + PreferenceCategory(title = stringResource(StringR.string.common_topic), modifier = modifier) { Text( roomTopic, modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 12.dp), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt index 07b3bb1708..ef882c2f40 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenter.kt @@ -126,15 +126,16 @@ class RoomInviteMembersPresenter @Inject constructor( userRepository.search(searchQuery).collect { searchResults.value = when { it.isEmpty() -> SearchBarResultState.NoResults() - else -> SearchBarResultState.Results(it.map { user -> - val existingMembership = joinedMembers.firstOrNull { j -> j.userId == user.userId }?.membership + else -> SearchBarResultState.Results(it.map { result -> + val existingMembership = joinedMembers.firstOrNull { j -> j.userId == result.matrixUser.userId }?.membership val isJoined = existingMembership == RoomMembershipState.JOIN val isInvited = existingMembership == RoomMembershipState.INVITE InvitableUser( - matrixUser = user, - isSelected = selectedUsers.value.contains(user) || isJoined || isInvited, + matrixUser = result.matrixUser, + isSelected = selectedUsers.value.contains(result.matrixUser) || isJoined || isInvited, isAlreadyJoined = isJoined, isAlreadyInvited = isInvited, + isUnresolved = result.isUnresolved, ) }.toImmutableList()) } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersState.kt index b96cc30935..9a2ceb7c4b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersState.kt @@ -35,4 +35,5 @@ data class InvitableUser( val isSelected: Boolean = false, val isAlreadyJoined: Boolean = false, val isAlreadyInvited: Boolean = false, + val isUnresolved: Boolean = false, ) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersStateProvider.kt index 137d47b660..00e9496c2a 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersStateProvider.kt @@ -48,5 +48,19 @@ internal class RoomInviteMembersStateProvider : PreviewParameterProvider - CheckableUserRow( - checked = invitableUser.isSelected, - enabled = !invitableUser.isAlreadyInvited && !invitableUser.isAlreadyJoined, - avatarData = invitableUser.matrixUser.getAvatarData(AvatarSize.MEDIUM), - name = invitableUser.matrixUser.getBestName(), - subtext = when { - // If they're already invited or joined we show that information - invitableUser.isAlreadyJoined -> stringResource(R.string.screen_room_details_already_a_member) - invitableUser.isAlreadyInvited -> stringResource(R.string.screen_room_details_already_invited) - // Otherwise show the ID, unless that's already used for their name - invitableUser.matrixUser.displayName.isNullOrEmpty().not() -> invitableUser.matrixUser.userId.value - else -> null - }, - onCheckedChange = { onUserToggled(invitableUser.matrixUser) }, - modifier = Modifier.fillMaxWidth() - ) + if (invitableUser.isUnresolved && !invitableUser.isAlreadyInvited && !invitableUser.isAlreadyJoined) { + CheckableUnresolvedUserRow( + checked = invitableUser.isSelected, + avatarData = invitableUser.matrixUser.getAvatarData(AvatarSize.MEDIUM), + id = invitableUser.matrixUser.userId.value, + onCheckedChange = { onUserToggled(invitableUser.matrixUser) }, + modifier = Modifier.fillMaxWidth() + ) + } else { + CheckableUserRow( + checked = invitableUser.isSelected, + enabled = !invitableUser.isAlreadyInvited && !invitableUser.isAlreadyJoined, + avatarData = invitableUser.matrixUser.getAvatarData(AvatarSize.MEDIUM), + name = invitableUser.matrixUser.getBestName(), + subtext = when { + // If they're already invited or joined we show that information + invitableUser.isAlreadyJoined -> stringResource(R.string.screen_room_details_already_a_member) + invitableUser.isAlreadyInvited -> stringResource(R.string.screen_room_details_already_invited) + // Otherwise show the ID, unless that's already used for their name + invitableUser.matrixUser.displayName.isNullOrEmpty().not() -> invitableUser.matrixUser.userId.value + else -> null + }, + onCheckedChange = { onUserToggled(invitableUser.matrixUser) }, + modifier = Modifier.fillMaxWidth() + ) + } } } }, diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt index 49ecff7d3b..3baea96990 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/impl/invite/RoomInviteMembersPresenterTest.kt @@ -29,10 +29,12 @@ import io.element.android.libraries.designsystem.theme.components.SearchBarResul import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.ui.components.aMatrixUser import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import io.element.android.libraries.usersearch.api.UserSearchResult import io.element.android.libraries.usersearch.test.FakeUserRepository import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.collections.immutable.ImmutableList @@ -130,7 +132,7 @@ internal class RoomInviteMembersPresenterTest { skipItems(1) assertThat(repository.providedQuery).isEqualTo("some query") - repository.emitResult(aMatrixUserList()) + repository.emitResult(aMatrixUserList().map { UserSearchResult(it) }) skipItems(1) val resultState = awaitItem() @@ -175,7 +177,7 @@ internal class RoomInviteMembersPresenterTest { skipItems(1) assertThat(repository.providedQuery).isEqualTo("some query") - repository.emitResult(aMatrixUserList()) + repository.emitResult(aMatrixUserList().map { UserSearchResult(it) }) skipItems(1) val resultState = awaitItem() @@ -202,6 +204,53 @@ internal class RoomInviteMembersPresenterTest { } } + @Test + fun `present - performs search and handles unresolved results`() = runTest { + val userList = aMatrixUserList() + val joinedUser = userList[0] + val invitedUser = userList[1] + + val repository = FakeUserRepository() + val presenter = RoomInviteMembersPresenter( + userRepository = repository, + roomMemberListDataSource = createDataSource(FakeMatrixRoom().apply { + givenRoomMembersState(MatrixRoomMembersState.Ready(listOf( + aRoomMember(userId = joinedUser.userId, membership = RoomMembershipState.JOIN), + aRoomMember(userId = invitedUser.userId, membership = RoomMembershipState.INVITE), + ))) + }), + coroutineDispatchers = testCoroutineDispatchers() + ) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + skipItems(1) + + initialState.eventSink(RoomInviteMembersEvents.UpdateSearchQuery("some query")) + skipItems(1) + + assertThat(repository.providedQuery).isEqualTo("some query") + + val unresolvedUser = UserSearchResult(aMatrixUser(id = A_USER_ID.value), isUnresolved = true) + repository.emitResult(listOf(unresolvedUser) + aMatrixUserList().map { UserSearchResult(it) }) + skipItems(1) + + val resultState = awaitItem() + assertThat(resultState.searchResults).isInstanceOf(SearchBarResultState.Results::class.java) + + val users = resultState.searchResults.users() + + val userWhoShouldBeUnresolved = users.first() + assertThat(userWhoShouldBeUnresolved.isUnresolved).isTrue() + + // All other users are neither joined nor invited + val otherUsers = users.minus(userWhoShouldBeUnresolved) + assertThat(otherUsers.none { it.isUnresolved }).isTrue() + } + } + @Test fun `present - toggle users updates selected user state`() = runTest { val repository = FakeUserRepository() @@ -254,7 +303,7 @@ internal class RoomInviteMembersPresenterTest { skipItems(1) assertThat(repository.providedQuery).isEqualTo("some query") - repository.emitResult(aMatrixUserList() + selectedUser) + repository.emitResult((aMatrixUserList() + selectedUser).map { UserSearchResult(it) }) skipItems(2) val resultState = awaitItem() @@ -296,7 +345,7 @@ internal class RoomInviteMembersPresenterTest { skipItems(1) assertThat(repository.providedQuery).isEqualTo("some query") - repository.emitResult(aMatrixUserList() + selectedUser) + repository.emitResult((aMatrixUserList() + selectedUser).map { UserSearchResult(it) }) skipItems(2) // And then a user is toggled diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt new file mode 100644 index 0000000000..c195d42685 --- /dev/null +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnresolvedUserRow.kt @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.ui.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.IntrinsicSize +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Error +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.element.android.libraries.designsystem.components.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.preview.ElementThemedPreview +import io.element.android.libraries.designsystem.theme.components.Checkbox +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.ui.model.getAvatarData +import io.element.android.libraries.ui.strings.R + +@Composable +fun UnresolvedUserRow( + avatarData: AvatarData, + id: String, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .fillMaxWidth() + .padding(start = 16.dp, top = 8.dp, end = 16.dp, bottom = 8.dp) + .height(IntrinsicSize.Min), + verticalAlignment = Alignment.CenterVertically + ) { + Avatar(avatarData) + Column( + modifier = Modifier + .padding(start = 12.dp), + ) { + // ID + Text( + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + text = id, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + color = MaterialTheme.colorScheme.primary, + ) + + // Warning + Row(modifier = Modifier.fillMaxWidth()) { + Icon( + imageVector = Icons.Filled.Error, + contentDescription = "", + modifier = Modifier + .size(18.dp) + .align(Alignment.Top) + .padding(2.dp), + tint = MaterialTheme.colorScheme.error, + ) + + Text( + text = stringResource(R.string.common_invite_unknown_profile), + color = MaterialTheme.colorScheme.secondary, + fontSize = 12.sp, + lineHeight = 16.sp, + ) + } + } + } +} + +@Composable +fun CheckableUnresolvedUserRow( + checked: Boolean, + avatarData: AvatarData, + id: String, + modifier: Modifier = Modifier, + onCheckedChange: (Boolean) -> Unit = {}, + enabled: Boolean = true, +) { + Row( + modifier = modifier + .fillMaxWidth() + .clickable(role = Role.Checkbox, enabled = enabled) { + onCheckedChange(!checked) + }, + verticalAlignment = Alignment.CenterVertically, + ) { + UnresolvedUserRow( + modifier = Modifier.weight(1f), + avatarData = avatarData, + id = id, + ) + + Checkbox( + checked = checked, + onCheckedChange = onCheckedChange, + enabled = enabled, + ) + } +} + +@Preview +@Composable +internal fun UnresolvedUserRowPreview() = + ElementThemedPreview { + val matrixUser = aMatrixUser() + UnresolvedUserRow(matrixUser.getAvatarData(), matrixUser.userId.value) + } + +@Preview +@Composable +internal fun CheckableUnresolvedUserRowPreview() = + ElementThemedPreview { + val matrixUser = aMatrixUser() + Column { + CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(), matrixUser.userId.value) + CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(), matrixUser.userId.value) + CheckableUnresolvedUserRow(false, matrixUser.getAvatarData(), matrixUser.userId.value, enabled = false) + CheckableUnresolvedUserRow(true, matrixUser.getAvatarData(), matrixUser.userId.value, enabled = false) + } + } diff --git a/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserRepository.kt b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserRepository.kt index bbb124c3c5..03e9952c92 100644 --- a/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserRepository.kt +++ b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserRepository.kt @@ -16,10 +16,9 @@ package io.element.android.libraries.usersearch.api -import io.element.android.libraries.matrix.api.user.MatrixUser import kotlinx.coroutines.flow.Flow interface UserRepository { - suspend fun search(query: String): Flow> + suspend fun search(query: String): Flow> } diff --git a/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserSearchResult.kt b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserSearchResult.kt new file mode 100644 index 0000000000..e67a7af46f --- /dev/null +++ b/libraries/usersearch/api/src/main/kotlin/io/element/android/libraries/usersearch/api/UserSearchResult.kt @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.usersearch.api + +import io.element.android.libraries.matrix.api.user.MatrixUser + +data class UserSearchResult( + val matrixUser: MatrixUser, + val isUnresolved: Boolean = false, +) diff --git a/libraries/usersearch/impl/src/main/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserRepository.kt b/libraries/usersearch/impl/src/main/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserRepository.kt index 7577e8f6e8..fff9218556 100644 --- a/libraries/usersearch/impl/src/main/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserRepository.kt +++ b/libraries/usersearch/impl/src/main/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserRepository.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.usersearch.api.UserListDataSource import io.element.android.libraries.usersearch.api.UserRepository +import io.element.android.libraries.usersearch.api.UserSearchResult import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -33,24 +34,26 @@ class MatrixUserRepository @Inject constructor( private val dataSource: UserListDataSource ) : UserRepository { - override suspend fun search(query: String): Flow> = flow { + override suspend fun search(query: String): Flow> = flow { // Manually add a fake result with the matrixId, if any val isUserId = MatrixPatterns.isUserId(query) if (isUserId) { - emit(listOf(MatrixUser(UserId(query)))) + emit(listOf(UserSearchResult(MatrixUser(UserId(query))))) } if (query.length >= MINIMUM_SEARCH_LENGTH) { // Debounce delay(DEBOUNCE_TIME_MILLIS) - val results = dataSource.search(query).toMutableList() + val results = dataSource.search(query).map { UserSearchResult(it) }.toMutableList() // If the query is a user ID and the result doesn't contain that user ID, query the profile information explicitly - if (isUserId && results.none { it.userId.value == query }) { - val getProfileResult: MatrixUser? = dataSource.getProfile(UserId(query)) - val profile = getProfileResult ?: MatrixUser(UserId(query)) - results.add(0, profile) + if (isUserId && results.none { it.matrixUser.userId.value == query }) { + results.add( + 0, + dataSource.getProfile(UserId(query)) + ?.let { UserSearchResult(it) } + ?: UserSearchResult(MatrixUser(UserId(query)), isUnresolved = true)) } emit(results) diff --git a/libraries/usersearch/impl/src/test/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserRepositoryTest.kt b/libraries/usersearch/impl/src/test/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserRepositoryTest.kt index e2eab067e6..b2327ef750 100644 --- a/libraries/usersearch/impl/src/test/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserRepositoryTest.kt +++ b/libraries/usersearch/impl/src/test/kotlin/io/element/android/libraries/usersearch/impl/MatrixUserRepositoryTest.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_NAME import io.element.android.libraries.matrix.ui.components.aMatrixUserList +import io.element.android.libraries.usersearch.api.UserSearchResult import io.element.android.libraries.usersearch.test.FakeUserListDataSource import kotlinx.coroutines.test.runTest import org.junit.Test @@ -63,7 +64,7 @@ internal class MatrixUserRepositoryTest { val result = repository.search("some query") result.test { - assertThat(awaitItem()).isEqualTo(aMatrixUserList()) + assertThat(awaitItem()).isEqualTo(aMatrixUserList().toUserSearchResults()) awaitComplete() } } @@ -76,7 +77,7 @@ internal class MatrixUserRepositoryTest { val result = repository.search(A_USER_ID.value) result.test { - assertThat(awaitItem()).isEqualTo(listOf(MatrixUser(userId = A_USER_ID))) + assertThat(awaitItem()).isEqualTo(listOf(placeholderResult())) skipItems(1) awaitComplete() } @@ -93,7 +94,7 @@ internal class MatrixUserRepositoryTest { result.test { skipItems(1) - assertThat(awaitItem()).isEqualTo(searchResults) + assertThat(awaitItem()).isEqualTo(searchResults.toUserSearchResults()) awaitComplete() } } @@ -112,13 +113,13 @@ internal class MatrixUserRepositoryTest { result.test { skipItems(1) - assertThat(awaitItem()).isEqualTo(listOf(userProfile) + searchResults) + assertThat(awaitItem()).isEqualTo((listOf(userProfile) + searchResults).toUserSearchResults()) awaitComplete() } } @Test - fun `search - just shows id if profile can't be loaded`() = runTest { + fun `search - returns unresolved user if profile can't be loaded`() = runTest { val searchResults = aMatrixUserListWithoutUserId(A_USER_ID) val dataSource = FakeUserListDataSource() @@ -130,11 +131,15 @@ internal class MatrixUserRepositoryTest { result.test { skipItems(1) - assertThat(awaitItem()).isEqualTo(listOf(MatrixUser(userId = A_USER_ID)) + searchResults) + assertThat(awaitItem()).isEqualTo(listOf(placeholderResult(isUnresolved = true)) + searchResults.toUserSearchResults()) awaitComplete() } } private fun aMatrixUserListWithoutUserId(userId: UserId) = aMatrixUserList().filterNot { it.userId == userId } + private fun List.toUserSearchResults() = map { UserSearchResult(it) } + + private fun placeholderResult(id: UserId = A_USER_ID, isUnresolved: Boolean = false) = UserSearchResult(MatrixUser(id), isUnresolved = isUnresolved) + } diff --git a/libraries/usersearch/test/src/main/kotlin/io/element/android/libraries/usersearch/test/FakeUserRepository.kt b/libraries/usersearch/test/src/main/kotlin/io/element/android/libraries/usersearch/test/FakeUserRepository.kt index ea0d93a197..0911d86b36 100644 --- a/libraries/usersearch/test/src/main/kotlin/io/element/android/libraries/usersearch/test/FakeUserRepository.kt +++ b/libraries/usersearch/test/src/main/kotlin/io/element/android/libraries/usersearch/test/FakeUserRepository.kt @@ -16,8 +16,8 @@ package io.element.android.libraries.usersearch.test -import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.usersearch.api.UserRepository +import io.element.android.libraries.usersearch.api.UserSearchResult import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow @@ -26,14 +26,14 @@ class FakeUserRepository : UserRepository { var providedQuery: String? = null private set - private val flow = MutableSharedFlow>() + private val flow = MutableSharedFlow>() - override suspend fun search(query: String): Flow> { + override suspend fun search(query: String): Flow> { providedQuery = query return flow } - suspend fun emitResult(result: List) { + suspend fun emitResult(result: List) { flow.emit(result) } diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index ebc59dec6a..fca921c50b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:439a2d9e9497486e86b63fe42402f1c2ee33b15695e47312e18867530d041dad -size 96539 +oid sha256:c1c1eedbab868e0c2501220293608572850e052f685f7076ec939b3f1a9abf27 +size 4464 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index dd593bdc3e..665c8811ac 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:255b99dea5c1da9d466533d510d7c0abe39a79cdc42b579cfa186176e9c6a49d -size 91991 +oid sha256:bb0d3bfcfd75cbd75fd9270ff1dc27090e5dbac79ca8db8a46d91a4c12bc966b +size 4457 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index a076ab948b..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4bd925ebe0257b400ed0b1cb956848622d1b42d8afcc71f915522c4878aaf94b -size 23447 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemLightPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 050457dd05..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemLightPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:12b569ad80bb4a4df89d70d9ae4a92da0d9e96deb477e1e437efbef5ec4a1fdd -size 22302 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2066a90b22 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchMultipleUsersResultItemPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e7352789223e57f85c597a591ada117c903e06ed2df49719a98e793d128cd7b +size 106579 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 7833fbac38..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemDarkPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:91de9d54216aac2bf2c4721e5e8ff5e74be43105846bdac16230cb7bea7427af -size 13777 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemLightPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 6b4f02c4d6..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemLightPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:38fbef57e6fb2860d05ab2b864bab16a30ebc98199eb18b19e422f946c52e163 -size 13096 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e992def7cc --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_SearchSingleUserResultItemPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:815864d4c929c5f123bc830d367b4f983851412891a23329d03e58e7726e4280 +size 54198 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 2a7a10f2c1..7abe9f914a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:56c23bd0880524cc56b713c93c5358e9f3771291c20f7a149bad00465f3f987a -size 20648 +oid sha256:b13d46300c7cdd3632ee39a6bfce92a5b9bfa44917b32928da313741965addc7 +size 8677 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 74acb423d6..5e201cb8cf 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b7b91a2d05b975d568116615c286568f376ebead49e25ff17f5aae8b75be0e1f -size 28876 +oid sha256:4d63e57cea65fd8617f19f2775c6cacb58fb1fd933ac26895445b49c8d8638d4 +size 17363 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 69d05b132c..309acc5b39 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f12591531cfdce24378003dda48940c20d8c9cf7bfac73ac1e7713e925bfac4d -size 20214 +oid sha256:255e7caab6e7133239a186a421cf669649133704b15cddebe2dbc4ca9baca372 +size 8731 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index bb7d1036e4..99c67749fc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44a35f28b3a59cc28937fee16eda26c26ef7b7622f929218e40f7537e096b2e8 -size 28120 +oid sha256:4d62a75d2faea119344221414f4442bb0682bf74a1bd71ca1cfc5ddfcaea4635 +size 17342 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index cd4deb3d0b..792ec762a7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:372f239e351215dadf0d5c451b8105e93bc86e338f4e564aa4726d482028ae9c -size 396027 +oid sha256:6e30e5a9ef2371748af9a77806168d5e49185b2e565308f31edb62b94f8060b0 +size 396024 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 5183011e59..90f76a4d29 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:087ef6e2e489e63e1a30793b183915984a32265676fd5a95f02d7ca02821c84b -size 132174 +oid sha256:036b362d18c690ff516de13f45aa5561902772bb4ff5a5c48383e22b27eb2999 +size 132172 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 62f356b12c..7db0c02d08 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.attachments.preview_null_DefaultGroup_AttachmentsPreviewViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f5e03b31f020201e8a8bd7ad5962022337441c4e83d15e03e95f83c9fa10eaaf -size 98743 +oid sha256:e4c14c033b2d4961bad5d05ae7eb61e72fc542e497bb09770271980321832607 +size 98742 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 4afab509fc..d400660085 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd0b34e2f0b0c8e7267b4029fb7755c707ebf0e018d085d7374ebd459fbe7479 -size 393620 +oid sha256:bc6306a6c951e34884603f71ceae6c6fdbf32889146b60049f41f9451972dd2e +size 393618 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 4afab509fc..d400660085 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd0b34e2f0b0c8e7267b4029fb7755c707ebf0e018d085d7374ebd459fbe7479 -size 393620 +oid sha256:bc6306a6c951e34884603f71ceae6c6fdbf32889146b60049f41f9451972dd2e +size 393618 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 4afab509fc..d400660085 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd0b34e2f0b0c8e7267b4029fb7755c707ebf0e018d085d7374ebd459fbe7479 -size 393620 +oid sha256:bc6306a6c951e34884603f71ceae6c6fdbf32889146b60049f41f9451972dd2e +size 393618 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 4afab509fc..d400660085 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd0b34e2f0b0c8e7267b4029fb7755c707ebf0e018d085d7374ebd459fbe7479 -size 393620 +oid sha256:bc6306a6c951e34884603f71ceae6c6fdbf32889146b60049f41f9451972dd2e +size 393618 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 4afab509fc..d400660085 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.media.viewer_null_DefaultGroup_MediaViewerViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dd0b34e2f0b0c8e7267b4029fb7755c707ebf0e018d085d7374ebd459fbe7479 -size 393620 +oid sha256:bc6306a6c951e34884603f71ceae6c6fdbf32889146b60049f41f9451972dd2e +size 393618 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 5dcb0e4633..5c15cd7380 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:712e7422e6f0ae9d6093619b156d982b0d4a861dc04c04473d3d0eb5751bd097 -size 6302 +oid sha256:8db2a0def5cf23917ababd10121dda386ccc44aa0172810e9ca916fbb63b08d0 +size 6303 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_1,NEXUS_5,1.0,en].png index ce4149128c..f45e646ed2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e3db033f79ac2c77abb8c41f6331e17ca89702fe00e42fa0b3c3f68b0d331de -size 7036 +oid sha256:417e4325a43fd13637f54fa71536441f2c4b5652e3bb25e2d579ef7d713c4aa9 +size 7035 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 5dcb0e4633..5c15cd7380 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:712e7422e6f0ae9d6093619b156d982b0d4a861dc04c04473d3d0eb5751bd097 -size 6302 +oid sha256:8db2a0def5cf23917ababd10121dda386ccc44aa0172810e9ca916fbb63b08d0 +size 6303 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_3,NEXUS_5,1.0,en].png index ce4149128c..f45e646ed2 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7e3db033f79ac2c77abb8c41f6331e17ca89702fe00e42fa0b3c3f68b0d331de -size 7036 +oid sha256:417e4325a43fd13637f54fa71536441f2c4b5652e3bb25e2d579ef7d713c4aa9 +size 7035 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_0,NEXUS_5,1.0,en].png index 4e6eae60dc..7a56bba7ba 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cdecfdb401b135159f3a4f9001a7739e4ba9a8fd80371b4a68278a07a8834c0 -size 6161 +oid sha256:593546b9837061cc2f0e27b94fe8a6fae80c35db62767cd43b97cc77c942c71b +size 6163 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_1,NEXUS_5,1.0,en].png index 7b52cf5f6a..639720a366 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:310058557ae80956fe325fc8a58df7b62c45040d707a0f7569f87cbb74cede07 -size 6836 +oid sha256:93805e6e1a168295b4263152f569b3b7065e890c11870f7e21f5b0f5cc1b07a6 +size 6834 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_2,NEXUS_5,1.0,en].png index 4e6eae60dc..7a56bba7ba 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7cdecfdb401b135159f3a4f9001a7739e4ba9a8fd80371b4a68278a07a8834c0 -size 6161 +oid sha256:593546b9837061cc2f0e27b94fe8a6fae80c35db62767cd43b97cc77c942c71b +size 6163 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_3,NEXUS_5,1.0,en].png index 7b52cf5f6a..639720a366 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_MessagesReactionButtonLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:310058557ae80956fe325fc8a58df7b62c45040d707a0f7569f87cbb74cede07 -size 6836 +oid sha256:93805e6e1a168295b4263152f569b3b7065e890c11870f7e21f5b0f5cc1b07a6 +size 6834 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewDarkPreview_0_null,NEXUS_5,1.0,en].png index 0006c267ea..f4f6a627b5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3dc781e3f17f09eb7e0cb8bf753a68ebde850b0b618e8d6bff7c780e365ee778 -size 11853 +oid sha256:ff1f9f65252e855037d6008aea0d884ccaec6419412b26e4ad12a32ab583f5ca +size 11848 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewLightPreview_0_null,NEXUS_5,1.0,en].png index 0dcb849d60..02c073d5ad 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline.components_null_DefaultGroup_TimelineItemReactionsViewLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6d14b21a4723db7ac5ceda97eee44681d5d236007262e0e9e8b5a8d5abf8022b +oid sha256:37c370bc41527d7b407679c971a35aee051da93c033c2f195b6c3d6c7b2d885a size 11514 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 105b94a19e..b2a3fa0b56 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:121e9eb7013123e5647e351eda7d331ade8032ae5aa56bd1be943ee6c6acad53 -size 41444 +oid sha256:f99c5fd192b3d56188dc0a14a522b1ea4a05cdbe9b18f63c4a4c99f0914c0df5 +size 41443 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index b579039d11..123b94deab 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e19a24fa27305822af8ec3a4dfac727eb1297ccbbeffec726664c6bc29b1a2a4 -size 53421 +oid sha256:8a524ff4463d72a0adca6a13907d95c6fb2cfe8afeba541cfe03c8fb44202d4f +size 53427 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png index 2a39d3fa2d..dce22380c4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9ab84b59725483b7187e91f3df18d1d55decbef7da37e186099027c6c98e3430 +oid sha256:6c7090f345c722c0c5d5d415723e1770843d36fef48dc750a893320830b9dfd4 size 46068 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index ec8687992b..e88ba038ca 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5b388b6664ea0e5a86d6506a954cbcdc36c39054204739365eb0228b2e1d4fd4 -size 197778 +oid sha256:b876f9b49579215a6f0cbb73627e6c5ad19e7b71131bee02a2fe4b7f46432a67 +size 197787 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png index 9467cabc80..f923e1c9b4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dbeca10cc6a489dd20390bbc3bf5f2fa4f726342083621b8161a2a4448622ffc -size 198122 +oid sha256:6d358680e2b08ed4d4f709b9f4d908f5cad84b888a7aa74b49609b09297e416e +size 198126 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png index 22b7e81257..e6567f92c5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23ed23fbc4a47152c4d9b6373111c7de5497900e9ccb3382f99364bde2444032 +oid sha256:f8f54ea6a7d158e8f7da56648aabf05c8dbc761db8698891d2d3f1ff863b76fb size 51724 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png index c71a28244b..3451b5ba36 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8a715939660ec1a2bea4c5a3e9d0a3b4b0bc45f137fae4c3210476145346a3a9 +oid sha256:4c2845cd6ef914a8062d3685d62d6b861a3963e09aad13efd5a9ac5e2de1a3ea size 73507 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 2fbafcf338..85e76e3cb1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f3094dab56dd2c46a5c34162e717afcf674823be080959ec5e852227946237ff +oid sha256:8dbf4c20504f68324ab8606d84f41390bef28479f5e0f9d76d34c358403b0de8 size 42954 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png index d89fe8b6dd..63a6318693 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:44534d8513114e666e7faa341a81184420fd84e8c3723c2d17236949f31e2fdb -size 55118 +oid sha256:3defc06e2e3d791261e87048fb019d2da7e8372d35550bcb17aacb2bb62370eb +size 55127 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png index fc9cacdcf3..4d1821d97b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f988cd33ae74aa292f26f0c6f11b5fcec8ff298005423161921e912a5e416c97 -size 39341 +oid sha256:824b93fd466fff749da6f6314fecbc1a15d316b3cac201adac0137c28660e121 +size 39342 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png index 61e274ef18..e666ac5893 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewDarkPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a65842cd12943594ed28ffba0a958ff076d6c6492533bfc5ffd395b915b2b30 -size 56467 +oid sha256:8233a3eafef97e18dcd6b846d78af3e3a6a4fb79b89af276a1a41067f9e9af45 +size 56472 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index d33815af64..d328d59d1a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f7822ba6ecc9e7cf7bfdb4446b6796dce685d2417f6ed29b6ef5015438e09ace -size 40935 +oid sha256:fbe93775a53dc0e06a019d2fd3c72612dfbf9000709f48b4cdcc14ef72c914ad +size 40934 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index ff256d7186..d462c390a8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3d8fae3d49c61850eef15c762d1f9615a735ca1943e78aa4dc116eaa830c02a -size 53182 +oid sha256:4703dc63b3015c2a2c96e731dbabe55f2cff07c5508853f370d4eeb67e67740a +size 53186 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png index b2207d8ccb..f6c6b2f55e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_10,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ebc4e13d35c918274f35445db57955780bfdc037d5bd85a98eddb9b3bac42d5e -size 45846 +oid sha256:70a1848c08f7e3ed4fcc2cd0b05344234b2c4a2c26139c256344b102b49d98a1 +size 45847 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index dabf581ac8..35a2f47fac 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ed3f266ce286216deca3747ded2b46bd7ef37c707145d1ae676e8afe227850b2 -size 199651 +oid sha256:4fe993ac7679fef189bcbfdf9e82abfb0263686721fcabb4ddeca8d2a3d4e209 +size 199655 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png index ca627ffc7f..8a508845da 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:685a607ef2c644b8844a1a83bc9427040f3890b4b9f777aa80592c98f0a96d87 -size 199963 +oid sha256:fa118c2f28fe2c7bd3a194cf80cb25ece8c61fd15edd407d93ee76816c362fe1 +size 199975 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png index 1ddc9bb4b0..f29c6b9fdd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d5975a89ee4cf263317079be95b6f6cc199bfbbcf75ac88d622989bfd859c813 -size 51801 +oid sha256:022ca7f2b5ce17e0079346778ee3b99b1e235439f88885a2d1bf545cc11864e6 +size 51800 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png index 213b36fafb..fcbfd4f52b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0375a01da0c470b8379d2edcfb282b4b55f5cee86dc9da3efbe66fbadf7019a5 -size 74079 +oid sha256:c347c51252c8e1305385c6c92936941403c481c792ba3fe10ca4949a1accd051 +size 74080 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png index 4360de3629..db3b41535b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f085dfb5b0147374af21b8df3f4cd2b9930e638188add6f3a4fa94008747c7ee -size 42553 +oid sha256:51120566fc91498097a2e68232f04342ade84d4aee702d5cc06ee5ff4bc17460 +size 42551 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png index 0f0e1ba1aa..5edaa2c19f 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7ec8c64bb4500bce08725d74753f45db93a369095626064ecdf9a83122aea326 -size 55285 +oid sha256:548566862bbb0da3e13b43aee882b28656ea544b9341d6152d4d1f5c526ed3f2 +size 55292 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png index ecf8b2e64c..ad1af8bb53 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_8,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54046d58d89dd8f44e8e2fdb83306682fd6305a32ba91c06c9bcd042e99bbdd3 +oid sha256:092598b99ed8d53381f80352c357b13b159dd65cb23ef54d277bfa862de28263 size 38928 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png index 6d8f83611f..05f925aee9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl.timeline_null_DefaultGroup_TimelineViewLightPreview_0_null_9,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a96004c7012bd1b83eb395511472a6c93f410f9504656f4e3621d7809eccca9f -size 56483 +oid sha256:0dd943e910de80c7a36db12acb17b0ee116228181ddb58a45c8ec9252e85e435 +size 56491 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index f1bc47d485..10be1fb5e7 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:49759fbb46a966d0e0f1c793545e320e7588ac482399a12ac45762474e6781f8 -size 45579 +oid sha256:74d763ff8d44e686ee3a10dcadc0e2c1f02e3332c486d62d00e2333d0c3b60cf +size 45578 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index c28a1d3ac7..dac883dcdb 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cdca9e94c071c20753aa2076f6c3a7ed931a2c1e90a78c29e00f27f7af99dfe8 -size 45758 +oid sha256:09b29e2e7bb8c38c978d3b56b04a97fc96d752154bb78fddd13a5cde8051a048 +size 45757 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 6864f509ba..fd526958c5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4e47966820a6a77efece51aaac5adbe3dd1dad1880b1feb7da2e86e36b386f7e -size 43815 +oid sha256:a936d98080f35c838adab660ef9b9287fc380d1358335c54e3da56230476721f +size 43810 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 7009f86be7..c8b1900e58 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bc3fc4c97a43c5786a75328e35828ed0e0cfd74ffab673832053ecc11db9b5e0 -size 44852 +oid sha256:43042ccb540b5fa3b8d5d9e273bd1155258d0dc12875b95c7a364492b3985da2 +size 44854 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 823b19bf1c..a07fbf9be9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f82a51b6b6ed4ac030c4602da9da2629010169e998aaba16f9e05c652f6010d -size 45283 +oid sha256:16cf470ec6ff50ee469db7bdc2cbfc1e32c5bcd7835c12c7313aafa495a44825 +size 45282 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png index 49d46e6e23..8d97ed2ac1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.messages.impl_null_DefaultGroup_MessagesViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:5d18843f61267ae7385c23113be5c5c3d4376d95a843eef126ed670877f69b80 -size 42281 +oid sha256:e6cc29403f3b3e146e9a637973320f5bd6bf73feb7b042f8844337817e551113 +size 42280 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenDarkPreview_0_null,NEXUS_5,1.0,en].png index 9749477ebd..b1f708d57c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenDarkPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c820bd324df729db08710d1a6c17ca34a451a3bb94da2206fc57d6b4efe91e2f -size 60019 +oid sha256:b53d55b5085673ac3a0a7663f7ff68e7d510fe352f3931b49c7e75e4c3767b93 +size 60007 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenLightPreview_0_null,NEXUS_5,1.0,en].png index f24edcc4a1..d020b1e521 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenLightPreview_0_null,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.onboarding.impl_null_DefaultGroup_OnBoardingScreenLightPreview_0_null,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da2f9f87d89382b4b18d39a477a8e86f98067e2326adae4def8374b5bf09f316 -size 57588 +oid sha256:a761318f3dfbc2ce6e777cfabc19eeb2f89100ae7631660f4b4d7550cd947c84 +size 57580 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 07451a0330..01ec8ec0fc 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:628f1f00dca9d15faabd8288a3c54b1b8581a380cf14edf364f0dee9ecc187d5 +oid sha256:147786f2cfcf7674147253cca5e41b4af96587a27216076a1c0802c81b0b1b46 size 180117 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 8146c241cb..a49d11835b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.rageshake.impl.bugreport_null_DefaultGroup_BugReportViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:827bccb0fdd3106fb24a95d665b4da2cfc15de48e8f508ae809c9f75d6d1bbf0 -size 178915 +oid sha256:789a979f7207531b4b9ba488ae2de52e6046809e788baf6690e1383a933cf624 +size 178916 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..84abdcb0e5 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cb1178f063eafb21ba01b93310d84335afbc4e6ad1fc99d1776578fe3f7eaa07 +size 49237 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..4d09e6d3d2 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca9a896e2dd3cf47792467f052f2d45b555d99ccbd91436ef4c03dfca66836ab +size 47542 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..fd84b77b2d --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_CheckableUnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33e3ba31ada4522ce7c9b6adafd4c1c37df1927017e4bba8bf3f418a352a0cc9 +size 129286 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..2b2903bdba --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnresolvedUserRowPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4bd094880b88662dd76409e4a728eda3736700c59c0721b87ba2568ee4bc0782 +size 39296 From 9efbe52c0d8fb286b1c8646fdf2a81f8a5108e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Mart=C3=ADn?= Date: Thu, 1 Jun 2023 07:35:20 +0200 Subject: [PATCH 58/75] Remove duplicate issue reporting by Danger --- tools/danger/dangerfile-lint.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/danger/dangerfile-lint.js b/tools/danger/dangerfile-lint.js index b0531fef9b..8d704ef5a8 100644 --- a/tools/danger/dangerfile-lint.js +++ b/tools/danger/dangerfile-lint.js @@ -26,4 +26,9 @@ schedule(reporter.scan({ * This can be useful if there are multiple reports being parsed to make them distinguishable. */ // outputPrefix?: "" + + /** + * Optional: If set to true, it will remove duplicate violations. + */ + removeDuplicates: true, })) From a36f4b794748338a832b3379283ba9083e02b893 Mon Sep 17 00:00:00 2001 From: Jorge Martin Espinosa Date: Thu, 1 Jun 2023 12:03:27 +0200 Subject: [PATCH 59/75] [Room details] Open room member details when clicking on user data in timeline (#482) --- .../io/element/android/appnav/RoomFlowNode.kt | 15 ++++++++++++++- .../element/android/appnav/RoomFlowNodeTest.kt | 7 ++++++- changelog.d/480.feature | 1 + .../features/messages/api/MessagesEntryPoint.kt | 2 ++ .../features/messages/impl/MessagesFlowNode.kt | 5 +++++ .../features/messages/impl/MessagesNode.kt | 7 +++++++ .../features/messages/impl/MessagesView.kt | 8 +++++++- .../messages/impl/timeline/TimelineView.kt | 14 ++++++++++++++ features/roomdetails/api/build.gradle.kts | 1 + .../roomdetails/api/RoomDetailsEntryPoint.kt | 17 ++++++++++++++++- .../impl/DefaultRoomDetailsEntryPoint.kt | 16 ++++++++++++++-- .../roomdetails/impl/RoomDetailsFlowNode.kt | 6 ++++-- .../members/details/RoomMemberDetailsNode.kt | 6 +++--- .../details/RoomMemberDetailsPresenter.kt | 4 ++-- .../matrix/ui/room/MatrixRoomMembers.kt | 6 +++--- 15 files changed, 99 insertions(+), 16 deletions(-) create mode 100644 changelog.d/480.feature diff --git a/appnav/src/main/kotlin/io/element/android/appnav/RoomFlowNode.kt b/appnav/src/main/kotlin/io/element/android/appnav/RoomFlowNode.kt index 0493ae7db1..fb3a8d566e 100644 --- a/appnav/src/main/kotlin/io/element/android/appnav/RoomFlowNode.kt +++ b/appnav/src/main/kotlin/io/element/android/appnav/RoomFlowNode.kt @@ -39,6 +39,7 @@ import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.inputs import io.element.android.libraries.di.SessionScope +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.services.appnavstate.api.AppNavigationStateService @@ -119,11 +120,20 @@ class RoomFlowNode @AssistedInject constructor( override fun onRoomDetailsClicked() { backstack.push(NavTarget.RoomDetails) } + + override fun onUserDataClicked(userId: UserId) { + backstack.push(NavTarget.RoomMemberDetails(userId)) + } } messagesEntryPoint.createNode(this, buildContext, callback) } NavTarget.RoomDetails -> { - roomDetailsEntryPoint.createNode(this, buildContext, emptyList()) + val inputs = RoomDetailsEntryPoint.Inputs(RoomDetailsEntryPoint.InitialTarget.RoomDetails) + roomDetailsEntryPoint.createNode(this, buildContext, inputs, emptyList()) + } + is NavTarget.RoomMemberDetails -> { + val inputs = RoomDetailsEntryPoint.Inputs(RoomDetailsEntryPoint.InitialTarget.RoomMemberDetails(navTarget.userId)) + roomDetailsEntryPoint.createNode(this, buildContext, inputs, emptyList()) } } } @@ -134,6 +144,9 @@ class RoomFlowNode @AssistedInject constructor( @Parcelize object RoomDetails : NavTarget + + @Parcelize + data class RoomMemberDetails(val userId: UserId) : NavTarget } private val timeline = inputs.room.timeline() diff --git a/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt b/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt index a151be665c..daff06af16 100644 --- a/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt +++ b/appnav/src/test/kotlin/io/element/android/appnav/RoomFlowNodeTest.kt @@ -60,7 +60,12 @@ class RoomFlowNodeTest { var nodeId: String? = null - override fun createNode(parentNode: Node, buildContext: BuildContext, plugins: List): Node { + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + inputs: RoomDetailsEntryPoint.Inputs, + plugins: List + ): Node { return node(buildContext) {}.also { nodeId = it.id } diff --git a/changelog.d/480.feature b/changelog.d/480.feature new file mode 100644 index 0000000000..e64dcc1f33 --- /dev/null +++ b/changelog.d/480.feature @@ -0,0 +1 @@ +Open room member details when tapping on a user in the timeline diff --git a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt index 9f15a77f4c..f1ed5c18dd 100644 --- a/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt +++ b/features/messages/api/src/main/kotlin/io/element/android/features/messages/api/MessagesEntryPoint.kt @@ -20,6 +20,7 @@ import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.matrix.api.core.UserId interface MessagesEntryPoint : FeatureEntryPoint { fun createNode( @@ -30,5 +31,6 @@ interface MessagesEntryPoint : FeatureEntryPoint { interface Callback : Plugin { fun onRoomDetailsClicked() + fun onUserDataClicked(userId: UserId) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt index 1e10a553f1..00e74b026c 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesFlowNode.kt @@ -39,6 +39,7 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.media.MediaSource import kotlinx.collections.immutable.ImmutableList import kotlinx.parcelize.Parcelize @@ -89,6 +90,10 @@ class MessagesFlowNode @AssistedInject constructor( override fun onPreviewAttachments(attachments: ImmutableList) { backstack.push(NavTarget.AttachmentPreview(attachments.first())) } + + override fun onUserDataClicked(userId: UserId) { + callback?.onUserDataClicked(userId) + } } createNode(buildContext, listOf(callback)) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt index 626b3bd682..96f76d3a34 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt @@ -28,6 +28,7 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.features.messages.impl.attachments.Attachment import io.element.android.features.messages.impl.timeline.model.TimelineItem import io.element.android.libraries.di.RoomScope +import io.element.android.libraries.matrix.api.core.UserId import kotlinx.collections.immutable.ImmutableList @ContributesNode(RoomScope::class) @@ -43,6 +44,7 @@ class MessagesNode @AssistedInject constructor( fun onRoomDetailsClicked() fun onEventClicked(event: TimelineItem.Event) fun onPreviewAttachments(attachments: ImmutableList) + fun onUserDataClicked(userId: UserId) } private fun onRoomDetailsClicked() { @@ -57,6 +59,10 @@ class MessagesNode @AssistedInject constructor( callback?.onPreviewAttachments(attachments) } + private fun onUserDataClicked(userId: UserId) { + callback?.onUserDataClicked(userId) + } + @Composable override fun View(modifier: Modifier) { val state = presenter.present() @@ -66,6 +72,7 @@ class MessagesNode @AssistedInject constructor( onRoomDetailsClicked = this::onRoomDetailsClicked, onEventClicked = this::onEventClicked, onPreviewAttachments = this::onPreviewAttachments, + onUserDataClicked = this::onUserDataClicked, modifier = modifier, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt index 8d08b77a49..3aca34ee2b 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt @@ -85,6 +85,7 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.designsystem.utils.LogCompositions +import io.element.android.libraries.matrix.api.core.UserId import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.launch import timber.log.Timber @@ -97,6 +98,7 @@ fun MessagesView( onBackPressed: () -> Unit, onRoomDetailsClicked: () -> Unit, onEventClicked: (event: TimelineItem.Event) -> Unit, + onUserDataClicked: (UserId) -> Unit, onPreviewAttachments: (ImmutableList) -> Unit, modifier: Modifier = Modifier, ) { @@ -203,6 +205,7 @@ fun MessagesView( .consumeWindowInsets(padding), onMessageClicked = ::onMessageClicked, onMessageLongClicked = ::onMessageLongClicked, + onUserDataClicked = onUserDataClicked, ) }, snackbarHost = { @@ -240,6 +243,7 @@ fun MessagesViewContent( state: MessagesState, modifier: Modifier = Modifier, onMessageClicked: (TimelineItem.Event) -> Unit = {}, + onUserDataClicked: (UserId) -> Unit = {}, onMessageLongClicked: (TimelineItem.Event) -> Unit = {}, ) { Column( @@ -255,6 +259,7 @@ fun MessagesViewContent( modifier = Modifier.weight(1f), onMessageClicked = onMessageClicked, onMessageLongClicked = onMessageLongClicked, + onUserDataClicked = onUserDataClicked, ) } MessageComposerView( @@ -354,6 +359,7 @@ private fun ContentToPreview(state: MessagesState) { onBackPressed = {}, onRoomDetailsClicked = {}, onEventClicked = {}, - onPreviewAttachments = {} + onPreviewAttachments = {}, + onUserDataClicked = {}, ) } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt index cf7c3c9bf1..f5ebdcc796 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt @@ -18,6 +18,7 @@ package io.element.android.features.messages.impl.timeline import androidx.compose.animation.animateContentSize import androidx.compose.foundation.background +import androidx.compose.foundation.clickable import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -87,6 +88,7 @@ import io.element.android.libraries.designsystem.theme.LocalColors import io.element.android.libraries.designsystem.theme.components.FloatingActionButton import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.matrix.api.core.UserId import kotlinx.collections.immutable.ImmutableList import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch @@ -95,6 +97,7 @@ import kotlinx.coroutines.launch fun TimelineView( state: TimelineState, modifier: Modifier = Modifier, + onUserDataClicked: (UserId) -> Unit = {}, onMessageClicked: (TimelineItem.Event) -> Unit = {}, onMessageLongClicked: (TimelineItem.Event) -> Unit = {}, ) { @@ -120,6 +123,7 @@ fun TimelineView( highlightedItem = state.highlightedEventId?.value, onClick = onMessageClicked, onLongClick = onMessageLongClicked, + onUserDataClick = onUserDataClicked, ) if (index == state.timelineItems.lastIndex) { onReachedLoadMore() @@ -139,6 +143,7 @@ fun TimelineView( fun TimelineItemRow( timelineItem: TimelineItem, highlightedItem: String?, + onUserDataClick: (UserId) -> Unit, onClick: (TimelineItem.Event) -> Unit, onLongClick: (TimelineItem.Event) -> Unit, modifier: Modifier = Modifier @@ -173,6 +178,7 @@ fun TimelineItemRow( isHighlighted = highlightedItem == timelineItem.identifier(), onClick = ::onClick, onLongClick = ::onLongClick, + onUserDataClick = onUserDataClick, modifier = modifier, ) } @@ -203,6 +209,7 @@ fun TimelineItemRow( highlightedItem = highlightedItem, onClick = onClick, onLongClick = onLongClick, + onUserDataClick = onUserDataClick, ) } } @@ -230,15 +237,21 @@ fun TimelineItemEventRow( isHighlighted: Boolean, onClick: () -> Unit, onLongClick: () -> Unit, + onUserDataClick: (UserId) -> Unit, modifier: Modifier = Modifier ) { val interactionSource = remember { MutableInteractionSource() } + fun onUserDataClicked() { + onUserDataClick(event.senderId) + } + val (parentAlignment, contentAlignment) = if (event.isMine) { Pair(Alignment.CenterEnd, Alignment.End) } else { Pair(Alignment.CenterStart, Alignment.Start) } + Box( modifier = modifier .fillMaxWidth() @@ -257,6 +270,7 @@ fun TimelineItemEventRow( Modifier .zIndex(1f) .offset(y = 12.dp) + .clickable(onClick = ::onUserDataClicked) ) } val bubbleState = BubbleState( diff --git a/features/roomdetails/api/build.gradle.kts b/features/roomdetails/api/build.gradle.kts index c93ec69f89..ddc062cb3b 100644 --- a/features/roomdetails/api/build.gradle.kts +++ b/features/roomdetails/api/build.gradle.kts @@ -16,6 +16,7 @@ plugins { id("io.element.android-library") + id("kotlin-parcelize") } android { diff --git a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt index 560e9d5dd7..e73d63f38c 100644 --- a/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt +++ b/features/roomdetails/api/src/main/kotlin/io/element/android/features/roomdetails/api/RoomDetailsEntryPoint.kt @@ -16,11 +16,26 @@ package io.element.android.features.roomdetails.api +import android.os.Parcelable import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.architecture.FeatureEntryPoint +import io.element.android.libraries.architecture.NodeInputs +import io.element.android.libraries.matrix.api.core.UserId +import kotlinx.parcelize.Parcelize interface RoomDetailsEntryPoint : FeatureEntryPoint { - fun createNode(parentNode: Node, buildContext: BuildContext, plugins: List): Node + + sealed interface InitialTarget : Parcelable { + @Parcelize + object RoomDetails : InitialTarget + + @Parcelize + data class RoomMemberDetails(val roomMemberId: UserId) : InitialTarget + } + + data class Inputs(val initialElement: InitialTarget) : NodeInputs + + fun createNode(parentNode: Node, buildContext: BuildContext, inputs: Inputs, plugins: List): Node } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt index eebdbea062..be6b915212 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/DefaultRoomDetailsEntryPoint.kt @@ -21,13 +21,25 @@ import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.plugin.Plugin import com.squareup.anvil.annotations.ContributesBinding import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint +import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint.InitialTarget +import io.element.android.features.roomdetails.impl.RoomDetailsFlowNode.NavTarget import io.element.android.libraries.architecture.createNode import io.element.android.libraries.di.AppScope import javax.inject.Inject @ContributesBinding(AppScope::class) class DefaultRoomDetailsEntryPoint @Inject constructor() : RoomDetailsEntryPoint { - override fun createNode(parentNode: Node, buildContext: BuildContext, plugins: List): Node { - return parentNode.createNode(buildContext, plugins) + override fun createNode( + parentNode: Node, + buildContext: BuildContext, + inputs: RoomDetailsEntryPoint.Inputs, + plugins: List + ): Node { + return parentNode.createNode(buildContext, plugins + inputs) } } + +internal fun InitialTarget.toNavTarget() = when (this) { + is InitialTarget.RoomDetails -> NavTarget.RoomDetails + is InitialTarget.RoomMemberDetails -> NavTarget.RoomMemberDetails(roomMemberId) +} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 97fb3311cb..02783775bf 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -28,6 +28,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode +import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.features.roomdetails.impl.invite.RoomInviteMembersNode import io.element.android.features.roomdetails.impl.members.RoomMemberListNode import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode @@ -44,7 +45,7 @@ class RoomDetailsFlowNode @AssistedInject constructor( @Assisted plugins: List, ) : BackstackNode( backstack = BackStack( - initialElement = NavTarget.RoomDetails, + initialElement = plugins.filterIsInstance().first().initialElement.toNavTarget(), savedStateMap = buildContext.savedStateMap, ), buildContext = buildContext, @@ -95,7 +96,8 @@ class RoomDetailsFlowNode @AssistedInject constructor( createNode(buildContext) } is NavTarget.RoomMemberDetails -> { - createNode(buildContext, listOf(RoomMemberDetailsNode.Inputs(navTarget.roomMemberId))) + val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId)) + createNode(buildContext, plugins) } } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt index 7fd4dd3876..bd4258ac88 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsNode.kt @@ -42,11 +42,11 @@ class RoomMemberDetailsNode @AssistedInject constructor( presenterFactory: RoomMemberDetailsPresenter.Factory, ) : Node(buildContext, plugins = plugins) { - data class Inputs( - val roomMemberId: UserId, + data class RoomMemberDetailsInput( + val roomMemberId: UserId ) : NodeInputs - private val inputs = inputs() + private val inputs = inputs() private val presenter = presenterFactory.create(inputs.roomMemberId) @Composable diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt index 594152e241..7ef6cd4aa4 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/details/RoomMemberDetailsPresenter.kt @@ -33,7 +33,7 @@ import io.element.android.libraries.core.bool.orFalse import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom -import io.element.android.libraries.matrix.ui.room.getRoomMember +import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch @@ -51,7 +51,7 @@ class RoomMemberDetailsPresenter @AssistedInject constructor( override fun present(): RoomMemberDetailsState { val coroutineScope = rememberCoroutineScope() var confirmationDialog by remember { mutableStateOf(null) } - val roomMember by room.getRoomMember(roomMemberId) + val roomMember by room.getRoomMemberAsState(roomMemberId) // the room member is not really live... val isBlocked = remember { mutableStateOf(roomMember?.isIgnored.orFalse()) diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt index 061ce365eb..5980bb138f 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomMembers.kt @@ -29,13 +29,13 @@ import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.roomMembers @Composable -fun MatrixRoom.getRoomMember(userId: UserId): State { +fun MatrixRoom.getRoomMemberAsState(userId: UserId): State { val roomMembersState by membersStateFlow.collectAsState() - return getRoomMember(roomMembersState = roomMembersState, userId = userId) + return getRoomMemberAsState(roomMembersState = roomMembersState, userId = userId) } @Composable -fun getRoomMember(roomMembersState: MatrixRoomMembersState, userId: UserId): State { +fun getRoomMemberAsState(roomMembersState: MatrixRoomMembersState, userId: UserId): State { val roomMembers = roomMembersState.roomMembers() return remember(roomMembers) { derivedStateOf { From 31afed144864d9edeef18ab872744e30836e195c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 23 May 2023 18:53:22 +0200 Subject: [PATCH 60/75] Map some info for the notifications. --- .../api/notification/NotificationData.kt | 18 ++- .../impl/notification/NotificationMapper.kt | 8 +- .../notification/RustNotificationService.kt | 5 - .../impl/notification/TimelineEventMapper.kt | 111 ++++++++++++++++++ .../notifications/NotifiableEventResolver.kt | 24 ++-- 5 files changed, 147 insertions(+), 19 deletions(-) create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt index 991f8dd117..60d889d046 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt @@ -20,15 +20,25 @@ import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.UserId -//TODO add content data class NotificationData( val senderId: UserId, val eventId: EventId, val roomId: RoomId, - val senderAvatarUrl: String? = null, - val senderDisplayName: String? = null, - val roomAvatarUrl: String? = null, + val senderAvatarUrl: String?, + val senderDisplayName: String?, + val roomAvatarUrl: String?, + val roomDisplayName: String?, val isDirect: Boolean, val isEncrypted: Boolean, val isNoisy: Boolean, + val event: NotificationEvent, +) + +data class NotificationEvent( + val eventId: EventId, + val senderId: UserId, + val timestamp: Long, + val content: String, + // For images for instance + val contentUrl: String? ) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt index 4b121db9bf..e6125cf69b 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt @@ -23,9 +23,9 @@ import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notification.NotificationData import org.matrix.rustcomponents.sdk.NotificationItem import org.matrix.rustcomponents.sdk.use -import javax.inject.Inject -class NotificationMapper @Inject constructor() { +class NotificationMapper { + private val timelineEventMapper = TimelineEventMapper() fun map(notificationItem: NotificationItem): NotificationData { return notificationItem.use { @@ -36,9 +36,11 @@ class NotificationMapper @Inject constructor() { senderAvatarUrl = it.senderAvatarUrl, senderDisplayName = it.senderDisplayName, roomAvatarUrl = it.roomAvatarUrl, + roomDisplayName = it.roomDisplayName, isDirect = it.isDirect, isEncrypted = it.isEncrypted.orFalse(), - isNoisy = it.isNoisy + isNoisy = it.isNoisy, + event = it.event.use { event -> timelineEventMapper.map(event) } ) } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt index bd94de21fc..8b630cd64a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/RustNotificationService.kt @@ -16,18 +16,13 @@ package io.element.android.libraries.matrix.impl.notification -import io.element.android.libraries.core.coroutine.CoroutineDispatchers -import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId -import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.notification.NotificationService -import kotlinx.coroutines.withContext import org.matrix.rustcomponents.sdk.Client import org.matrix.rustcomponents.sdk.use -import java.io.File class RustNotificationService( private val client: Client, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt new file mode 100644 index 0000000000..3d2759c6d1 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.impl.notification + +import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.notification.NotificationEvent +import org.matrix.rustcomponents.sdk.MessageLikeEventContent +import org.matrix.rustcomponents.sdk.MessageType +import org.matrix.rustcomponents.sdk.StateEventContent +import org.matrix.rustcomponents.sdk.TimelineEvent +import org.matrix.rustcomponents.sdk.TimelineEventType +import org.matrix.rustcomponents.sdk.use +import javax.inject.Inject + +class TimelineEventMapper @Inject constructor() { + + fun map(timelineEvent: TimelineEvent): NotificationEvent { + return timelineEvent.use { + NotificationEvent( + eventId = EventId(it.eventId()), + senderId = UserId(it.senderId()), + timestamp = it.timestamp().toLong(), + content = it.eventType().toContent(), + contentUrl = null // TODO it.eventType().toContentUrl(), + ) + } + } +} + +private fun TimelineEventType.toContent(): String { + return when (this) { + is TimelineEventType.MessageLike -> content.toContent() + is TimelineEventType.State -> content.toContent() + } +} + +private fun StateEventContent.toContent(): String { + return when (this) { + StateEventContent.PolicyRuleRoom -> "PolicyRuleRoom" + StateEventContent.PolicyRuleServer -> "PolicyRuleServer" + StateEventContent.PolicyRuleUser -> "PolicyRuleUser" + StateEventContent.RoomAliases -> "RoomAliases" + StateEventContent.RoomAvatar -> "RoomAvatar" + StateEventContent.RoomCanonicalAlias -> "RoomCanonicalAlias" + StateEventContent.RoomCreate -> "RoomCreate" + StateEventContent.RoomEncryption -> "RoomEncryption" + StateEventContent.RoomGuestAccess -> "RoomGuestAccess" + StateEventContent.RoomHistoryVisibility -> "RoomHistoryVisibility" + StateEventContent.RoomJoinRules -> "RoomJoinRules" + is StateEventContent.RoomMemberContent -> "$userId is now $membershipState" + StateEventContent.RoomName -> "RoomName" + StateEventContent.RoomPinnedEvents -> "RoomPinnedEvents" + StateEventContent.RoomPowerLevels -> "RoomPowerLevels" + StateEventContent.RoomServerAcl -> "RoomServerAcl" + StateEventContent.RoomThirdPartyInvite -> "RoomThirdPartyInvite" + StateEventContent.RoomTombstone -> "RoomTombstone" + StateEventContent.RoomTopic -> "RoomTopic" + StateEventContent.SpaceChild -> "SpaceChild" + StateEventContent.SpaceParent -> "SpaceParent" + } +} + +private fun MessageLikeEventContent.toContent(): String { + return use { + when (it) { + MessageLikeEventContent.CallAnswer -> "CallAnswer" + MessageLikeEventContent.CallCandidates -> "CallCandidates" + MessageLikeEventContent.CallHangup -> "CallHangup" + MessageLikeEventContent.CallInvite -> "CallInvite" + MessageLikeEventContent.KeyVerificationAccept -> "KeyVerificationAccept" + MessageLikeEventContent.KeyVerificationCancel -> "KeyVerificationCancel" + MessageLikeEventContent.KeyVerificationDone -> "KeyVerificationDone" + MessageLikeEventContent.KeyVerificationKey -> "KeyVerificationKey" + MessageLikeEventContent.KeyVerificationMac -> "KeyVerificationMac" + MessageLikeEventContent.KeyVerificationReady -> "KeyVerificationReady" + MessageLikeEventContent.KeyVerificationStart -> "KeyVerificationStart" + is MessageLikeEventContent.ReactionContent -> "Reacted to ${it.relatedEventId.take(8)}…" + MessageLikeEventContent.RoomEncrypted -> "RoomEncrypted" + is MessageLikeEventContent.RoomMessage -> it.messageType.toContent() + MessageLikeEventContent.RoomRedaction -> "RoomRedaction" + MessageLikeEventContent.Sticker -> "Sticker" + } + } +} + +private fun MessageType.toContent(): String { + return when (this) { + is MessageType.Audio -> content.use { it.body } + is MessageType.Emote -> content.body + is MessageType.File -> content.use { it.body } + is MessageType.Image -> content.use { it.body } + is MessageType.Notice -> content.body + is MessageType.Text -> content.body + is MessageType.Video -> content.use { it.body } + } +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index de168090f4..840a38350e 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -24,11 +24,11 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.notification.NotificationData +import io.element.android.libraries.matrix.api.notification.NotificationEvent import io.element.android.libraries.push.impl.log.pushLoggerTag import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.services.toolbox.api.strings.StringProvider -import io.element.android.services.toolbox.api.systemclock.SystemClock import timber.log.Timber import javax.inject.Inject @@ -44,7 +44,6 @@ class NotifiableEventResolver @Inject constructor( private val stringProvider: StringProvider, // private val noticeEventFormatter: NoticeEventFormatter, // private val displayableEventFormatter: DisplayableEventFormatter, - private val clock: SystemClock, private val matrixAuthenticationService: MatrixAuthenticationService, private val buildMeta: BuildMeta, ) { @@ -80,13 +79,13 @@ class NotifiableEventResolver @Inject constructor( editedEventId = null, canBeReplaced = true, noisy = isNoisy, - timestamp = clock.epochMillis(), + timestamp = event.timestamp, senderName = senderDisplayName, senderId = senderId.value, - body = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", - imageUriString = null, + body = event.content, + imageUriString = event.contentUrl, threadId = null, - roomName = null, + roomName = roomDisplayName, roomIsDirect = false, roomAvatarPath = roomAvatarUrl, senderAvatarPath = senderAvatarUrl, @@ -107,8 +106,19 @@ private fun NotificationData?.orDefault(roomId: RoomId, eventId: EventId): Notif eventId = eventId, senderId = UserId("@user:domain"), roomId = roomId, + senderAvatarUrl = null, + senderDisplayName = null, + roomAvatarUrl = null, + roomDisplayName = null, isNoisy = false, isEncrypted = false, - isDirect = false + isDirect = false, + event = NotificationEvent( + eventId = eventId, + senderId = UserId("@user:domain"), + timestamp = System.currentTimeMillis(), + content = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", + contentUrl = null + ) ) } From c91e8e5050b55823de6c6f325c61541285ca076d Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 10:28:39 +0200 Subject: [PATCH 61/75] Notifications: render room and user icons. --- libraries/push/impl/build.gradle.kts | 1 + .../notifications/NotificationBitmapLoader.kt | 50 ++++++++++--------- 2 files changed, 27 insertions(+), 24 deletions(-) diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index 2951ca0e25..da9122ef52 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -35,6 +35,7 @@ dependencies { implementation(libs.androidx.security.crypto) implementation(libs.network.retrofit) implementation(libs.serialization.json) + implementation(libs.coil) implementation(projects.libraries.architecture) implementation(projects.libraries.core) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index 7bd76f9f42..9ceed4296c 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -21,7 +21,13 @@ import android.graphics.Bitmap import android.os.Build import androidx.annotation.WorkerThread import androidx.core.graphics.drawable.IconCompat +import androidx.core.graphics.drawable.toBitmap +import coil.imageLoader +import coil.request.ImageRequest +import coil.transform.CircleCropTransformation import io.element.android.libraries.di.ApplicationContext +import io.element.android.libraries.matrix.api.media.MediaResolver +import kotlinx.coroutines.runBlocking import timber.log.Timber import javax.inject.Inject @@ -31,6 +37,7 @@ class NotificationBitmapLoader @Inject constructor( /** * Get icon of a room. + * @param path mxc url */ @WorkerThread fun getRoomBitmap(path: String?): Bitmap? { @@ -43,18 +50,15 @@ class NotificationBitmapLoader @Inject constructor( @WorkerThread private fun loadRoomBitmap(path: String): Bitmap? { return try { - null - /* TODO Notification - Glide.with(context) - .asBitmap() - .load(path) - .format(DecodeFormat.PREFER_ARGB_8888) - .signature(ObjectKey("room-icon-notification")) - .submit() - .get() - */ + val imageRequest = ImageRequest.Builder(context) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(128))) + .build() + runBlocking { + val result = context.imageLoader.execute(imageRequest) + result.drawable?.toBitmap() + } } catch (e: Exception) { - Timber.e(e, "decodeFile failed") + Timber.e(e, "Unable to load room bitmap") null } } @@ -62,6 +66,7 @@ class NotificationBitmapLoader @Inject constructor( /** * Get icon of a user. * Before Android P, this does nothing because the icon won't be used + * @param path mxc url */ @WorkerThread fun getUserIcon(path: String?): IconCompat? { @@ -75,20 +80,17 @@ class NotificationBitmapLoader @Inject constructor( @WorkerThread private fun loadUserIcon(path: String): IconCompat? { return try { - null - /* TODO Notification - val bitmap = Glide.with(context) - .asBitmap() - .load(path) - .transform(CircleCrop()) - .format(DecodeFormat.PREFER_ARGB_8888) - .signature(ObjectKey("user-icon-notification")) - .submit() - .get() - IconCompat.createWithBitmap(bitmap) - */ + val imageRequest = ImageRequest.Builder(context) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(128))) + .transformations(CircleCropTransformation()) + .build() + val bitmap = runBlocking { + val result = context.imageLoader.execute(imageRequest) + result.drawable?.toBitmap() + } + return bitmap?.let { IconCompat.createWithBitmap(it) } } catch (e: Exception) { - Timber.e(e, "decodeFile failed") + Timber.e(e, "Unable to load user bitmap") null } } From bc9ec5a28a67250441d13a2cb840c3412fef0d48 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 10:59:11 +0200 Subject: [PATCH 62/75] Notifications: render current user name and avatar. --- .../NotificationDrawerManager.kt | 45 ++++++++++------ .../impl/notifications/NotificationFactory.kt | 14 ++--- .../notifications/NotificationRenderer.kt | 54 +++++++++++-------- .../notifications/RoomGroupMessageCreator.kt | 12 ++--- .../SummaryGroupMessageCreator.kt | 12 ++--- .../factories/NotificationFactory.kt | 12 ++--- 6 files changed, 83 insertions(+), 66 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt index cf0307fbd9..c3c5275eb5 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt @@ -16,21 +16,21 @@ package io.element.android.libraries.push.impl.notifications -import android.content.Context import android.os.Handler import android.os.HandlerThread import androidx.annotation.WorkerThread import io.element.android.libraries.androidutils.throttler.FirstThrottler import io.element.android.libraries.core.cache.CircularCache +import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope -import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.api.store.PushDataStore -import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreMessageEventInRoom @@ -38,6 +38,7 @@ import io.element.android.services.appnavstate.api.AppNavigationState import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import timber.log.Timber import javax.inject.Inject @@ -48,7 +49,6 @@ import javax.inject.Inject */ @SingleIn(AppScope::class) class NotificationDrawerManager @Inject constructor( - @ApplicationContext context: Context, private val pushDataStore: PushDataStore, private val notifiableEventProcessor: NotifiableEventProcessor, private val notificationRenderer: NotificationRenderer, @@ -57,6 +57,7 @@ class NotificationDrawerManager @Inject constructor( private val appNavigationStateService: AppNavigationStateService, private val coroutineScope: CoroutineScope, private val buildMeta: BuildMeta, + private val matrixAuthenticationService: MatrixAuthenticationService, ) { private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY) @@ -66,7 +67,6 @@ class NotificationDrawerManager @Inject constructor( * Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events. */ private val notificationState by lazy { createInitialNotificationState() } - private val avatarSize = context.resources.getDimensionPixelSize(R.dimen.profile_avatar_size) private var currentAppNavigationState: AppNavigationState? = null private val firstThrottler = FirstThrottler(200) @@ -239,6 +239,7 @@ class NotificationDrawerManager @Inject constructor( } } + @WorkerThread private fun renderEvents(eventsToRender: List>) { // Group by sessionId val eventsForSessions = eventsToRender.groupBy { @@ -246,17 +247,29 @@ class NotificationDrawerManager @Inject constructor( } eventsForSessions.forEach { (sessionId, notifiableEvents) -> - // TODO EAx val user = session.getUserOrDefault(session.myUserId) - // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash - val myUserDisplayName = "Todo display name" // user.toMatrixItem().getBestName() - // TODO EAx avatar URL - val myUserAvatarUrl = null // session.contentUrlResolver().resolveThumbnail( - // contentUrl = user.avatarUrl, - // width = avatarSize, - // height = avatarSize, - // method = ContentUrlResolver.ThumbnailMethod.SCALE - //) - notificationRenderer.render(sessionId, myUserDisplayName, myUserAvatarUrl, useCompleteNotificationFormat, notifiableEvents) + val currentUser = tryOrNull( + onError = { Timber.e(it, "Unable to retrieve info for user ${sessionId.value}") }, + operation = { + runBlocking { + val client = matrixAuthenticationService.restoreSession(sessionId).getOrNull() + + // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash + val myUserDisplayName = client?.loadUserDisplayName()?.getOrNull() ?: sessionId.value + val userAvatarUrl = client?.loadUserAvatarURLString()?.getOrNull() + MatrixUser( + userId = sessionId, + displayName = myUserDisplayName, + avatarUrl = userAvatarUrl + ) + } + } + ) ?: MatrixUser( + userId = sessionId, + displayName = sessionId.value, + avatarUrl = null + ) + + notificationRenderer.render(currentUser, useCompleteNotificationFormat, notifiableEvents) } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt index 4bb49e168f..96f5e5c81b 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt @@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent @@ -35,9 +35,7 @@ class NotificationFactory @Inject constructor( ) { fun Map.toNotifications( - sessionId: SessionId, - myUserDisplayName: String, - myUserAvatarUrl: String? + currentUser: MatrixUser, ): List { return map { (roomId, events) -> when { @@ -45,11 +43,9 @@ class NotificationFactory @Inject constructor( else -> { val messageEvents = events.onlyKeptEvents().filterNot { it.isRedacted } roomGroupMessageCreator.createRoomMessage( - sessionId = sessionId, + currentUser = currentUser, events = messageEvents, roomId = roomId, - userDisplayName = myUserDisplayName, - userAvatarUrl = myUserAvatarUrl ) } } @@ -99,7 +95,7 @@ class NotificationFactory @Inject constructor( } fun createSummaryNotification( - sessionId: SessionId, + currentUser: MatrixUser, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, @@ -112,7 +108,7 @@ class NotificationFactory @Inject constructor( roomMeta.isEmpty() && invitationMeta.isEmpty() && simpleMeta.isEmpty() -> SummaryNotification.Removed else -> SummaryNotification.Update( summaryGroupMessageCreator.createSummaryNotification( - sessionId = sessionId, + currentUser = currentUser, roomNotifications = roomMeta, invitationNotifications = invitationMeta, simpleNotifications = simpleMeta, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 277dc3b822..33e15510f1 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.notifications import androidx.annotation.WorkerThread import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent @@ -34,19 +34,17 @@ class NotificationRenderer @Inject constructor( @WorkerThread fun render( - sessionId: SessionId, - myUserDisplayName: String, - myUserAvatarUrl: String?, + currentUser: MatrixUser, useCompleteNotificationFormat: Boolean, eventsToProcess: List> ) { val (roomEvents, simpleEvents, invitationEvents) = eventsToProcess.groupByType() with(notificationFactory) { - val roomNotifications = roomEvents.toNotifications(sessionId, myUserDisplayName, myUserAvatarUrl) + val roomNotifications = roomEvents.toNotifications(currentUser) val invitationNotifications = invitationEvents.toNotifications() val simpleNotifications = simpleEvents.toNotifications() val summaryNotification = createSummaryNotification( - sessionId = sessionId, + currentUser = currentUser, roomNotifications = roomNotifications, invitationNotifications = invitationNotifications, simpleNotifications = simpleNotifications, @@ -56,21 +54,27 @@ class NotificationRenderer @Inject constructor( // Remove summary first to avoid briefly displaying it after dismissing the last notification if (summaryNotification == SummaryNotification.Removed) { Timber.d("Removing summary notification") - notificationDisplayer.cancelNotificationMessage(null, notificationIdProvider.getSummaryNotificationId(sessionId)) + notificationDisplayer.cancelNotificationMessage( + tag = null, + id = notificationIdProvider.getSummaryNotificationId(currentUser.userId) + ) } roomNotifications.forEach { wrapper -> when (wrapper) { is RoomNotification.Removed -> { Timber.d("Removing room messages notification ${wrapper.roomId}") - notificationDisplayer.cancelNotificationMessage(wrapper.roomId.value, notificationIdProvider.getRoomMessagesNotificationId(sessionId)) + notificationDisplayer.cancelNotificationMessage( + tag = wrapper.roomId.value, + id = notificationIdProvider.getRoomMessagesNotificationId(currentUser.userId) + ) } is RoomNotification.Message -> if (useCompleteNotificationFormat) { Timber.d("Updating room messages notification ${wrapper.meta.roomId}") notificationDisplayer.showNotificationMessage( - wrapper.meta.roomId.value, - notificationIdProvider.getRoomMessagesNotificationId(sessionId), - wrapper.notification + tag = wrapper.meta.roomId.value, + id = notificationIdProvider.getRoomMessagesNotificationId(currentUser.userId), + notification = wrapper.notification ) } } @@ -80,14 +84,17 @@ class NotificationRenderer @Inject constructor( when (wrapper) { is OneShotNotification.Removed -> { Timber.d("Removing invitation notification ${wrapper.key}") - notificationDisplayer.cancelNotificationMessage(wrapper.key, notificationIdProvider.getRoomInvitationNotificationId(sessionId)) + notificationDisplayer.cancelNotificationMessage( + tag = wrapper.key, + id = notificationIdProvider.getRoomInvitationNotificationId(currentUser.userId) + ) } is OneShotNotification.Append -> if (useCompleteNotificationFormat) { Timber.d("Updating invitation notification ${wrapper.meta.key}") notificationDisplayer.showNotificationMessage( - wrapper.meta.key, - notificationIdProvider.getRoomInvitationNotificationId(sessionId), - wrapper.notification + tag = wrapper.meta.key, + id = notificationIdProvider.getRoomInvitationNotificationId(currentUser.userId), + notification = wrapper.notification ) } } @@ -97,14 +104,17 @@ class NotificationRenderer @Inject constructor( when (wrapper) { is OneShotNotification.Removed -> { Timber.d("Removing simple notification ${wrapper.key}") - notificationDisplayer.cancelNotificationMessage(wrapper.key, notificationIdProvider.getRoomEventNotificationId(sessionId)) + notificationDisplayer.cancelNotificationMessage( + tag = wrapper.key, + id = notificationIdProvider.getRoomEventNotificationId(currentUser.userId) + ) } is OneShotNotification.Append -> if (useCompleteNotificationFormat) { Timber.d("Updating simple notification ${wrapper.meta.key}") notificationDisplayer.showNotificationMessage( - wrapper.meta.key, - notificationIdProvider.getRoomEventNotificationId(sessionId), - wrapper.notification + tag = wrapper.meta.key, + id = notificationIdProvider.getRoomEventNotificationId(currentUser.userId), + notification = wrapper.notification ) } } @@ -114,9 +124,9 @@ class NotificationRenderer @Inject constructor( if (summaryNotification is SummaryNotification.Update) { Timber.d("Updating summary notification") notificationDisplayer.showNotificationMessage( - null, - notificationIdProvider.getSummaryNotificationId(sessionId), - summaryNotification.notification + tag = null, + id = notificationIdProvider.getSummaryNotificationId(currentUser.userId), + notification = summaryNotification.notification ) } } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index 00222728bf..8024e0c6cb 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -20,7 +20,7 @@ import android.graphics.Bitmap import androidx.core.app.NotificationCompat import androidx.core.app.Person import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent @@ -37,19 +37,17 @@ class RoomGroupMessageCreator @Inject constructor( ) { fun createRoomMessage( - sessionId: SessionId, + currentUser: MatrixUser, events: List, roomId: RoomId, - userDisplayName: String, - userAvatarUrl: String? ): RoomNotification.Message { val lastKnownRoomEvent = events.last() val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderName ?: "Room name (${roomId.value.take(8)}…)" val roomIsGroup = !lastKnownRoomEvent.roomIsDirect val style = NotificationCompat.MessagingStyle( Person.Builder() - .setName(userDisplayName) - .setIcon(bitmapLoader.getUserIcon(userAvatarUrl)) + .setName(currentUser.displayName) + .setIcon(bitmapLoader.getUserIcon(currentUser.avatarUrl)) .setKey(lastKnownRoomEvent.sessionId.value) .build() ).also { @@ -80,7 +78,7 @@ class RoomGroupMessageCreator @Inject constructor( notificationFactory.createMessagesListNotification( style, RoomEventGroupInfo( - sessionId = sessionId, + sessionId = currentUser.userId, roomId = roomId, roomDisplayName = roomName, isDirect = !roomIsGroup, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt index a400c2b7a3..78c6ab3ee0 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt @@ -18,7 +18,7 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification import androidx.core.app.NotificationCompat -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory import io.element.android.services.toolbox.api.strings.StringProvider @@ -44,7 +44,7 @@ class SummaryGroupMessageCreator @Inject constructor( ) { fun createSummaryNotification( - sessionId: SessionId, + currentUser: MatrixUser, roomNotifications: List, invitationNotifications: List, simpleNotifications: List, @@ -74,7 +74,7 @@ class SummaryGroupMessageCreator @Inject constructor( .setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents)) return if (useCompleteNotificationFormat) { notificationFactory.createSummaryListNotification( - sessionId, + currentUser, summaryInboxStyle, sumTitle, noisy = summaryIsNoisy, @@ -82,7 +82,7 @@ class SummaryGroupMessageCreator @Inject constructor( ) } else { processSimpleGroupSummary( - sessionId, + currentUser, summaryIsNoisy, messageCount, simpleNotifications.size, @@ -94,7 +94,7 @@ class SummaryGroupMessageCreator @Inject constructor( } private fun processSimpleGroupSummary( - sessionId: SessionId, + currentUser: MatrixUser, summaryIsNoisy: Boolean, messageEventsCount: Int, simpleEventsCount: Int, @@ -167,7 +167,7 @@ class SummaryGroupMessageCreator @Inject constructor( } } return notificationFactory.createSummaryListNotification( - sessionId = sessionId, + currentUser = currentUser, style = null, compatSummary = privacyTitle, noisy = summaryIsNoisy, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt index 5795ea5f5f..18bfdd8292 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt @@ -26,8 +26,8 @@ import androidx.core.content.ContextCompat import androidx.core.content.res.ResourcesCompat import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.ApplicationContext -import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.ThreadId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.RoomEventGroupInfo import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels @@ -226,7 +226,7 @@ class NotificationFactory @Inject constructor( * Create the summary notification. */ fun createSummaryListNotification( - sessionId: SessionId, + currentUser: MatrixUser, style: NotificationCompat.InboxStyle?, compatSummary: String, noisy: Boolean, @@ -240,12 +240,12 @@ class NotificationFactory @Inject constructor( // used in compat < N, after summary is built based on child notifications .setWhen(lastMessageTimestamp) .setStyle(style) - .setContentTitle(sessionId.value) + .setContentTitle(currentUser.userId.value) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setSmallIcon(smallIcon) // set content text to support devices running API level < 24 .setContentText(compatSummary) - .setGroup(sessionId.value) + .setGroup(currentUser.userId.value) // set this notification as the summary for the group .setGroupSummary(true) .setColor(accentColor) @@ -264,8 +264,8 @@ class NotificationFactory @Inject constructor( priority = NotificationCompat.PRIORITY_LOW } } - .setContentIntent(pendingIntentFactory.createOpenSessionPendingIntent(sessionId)) - .setDeleteIntent(pendingIntentFactory.createDismissSummaryPendingIntent(sessionId)) + .setContentIntent(pendingIntentFactory.createOpenSessionPendingIntent(currentUser.userId)) + .setDeleteIntent(pendingIntentFactory.createDismissSummaryPendingIntent(currentUser.userId)) .build() } From fee97ff735185733a6d1848a8beb9e9f3f87141b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 14:47:32 +0200 Subject: [PATCH 63/75] Notifications: add prefix to debug notification display. --- .../notifications/NotifiableEventResolver.kt | 2 +- .../notifications/NotificationBitmapLoader.kt | 8 +++---- .../notifications/RoomGroupMessageCreator.kt | 13 ++++++++---- .../SummaryGroupMessageCreator.kt | 16 +++++++------- .../notifications/debug/DebugNotification.kt | 21 +++++++++++++++++++ .../factories/NotificationFactory.kt | 21 ++++++++++--------- 6 files changed, 55 insertions(+), 26 deletions(-) create mode 100644 libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index 840a38350e..16abf69874 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -86,7 +86,7 @@ class NotifiableEventResolver @Inject constructor( imageUriString = event.contentUrl, threadId = null, roomName = roomDisplayName, - roomIsDirect = false, + roomIsDirect = isDirect, roomAvatarPath = roomAvatarUrl, senderAvatarPath = senderAvatarUrl, soundName = null, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index 9ceed4296c..a965c6aaa5 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -51,13 +51,13 @@ class NotificationBitmapLoader @Inject constructor( private fun loadRoomBitmap(path: String): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(128))) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(512))) .build() runBlocking { val result = context.imageLoader.execute(imageRequest) result.drawable?.toBitmap() } - } catch (e: Exception) { + } catch (e: Throwable) { Timber.e(e, "Unable to load room bitmap") null } @@ -81,7 +81,7 @@ class NotificationBitmapLoader @Inject constructor( private fun loadUserIcon(path: String): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(128))) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(512))) .transformations(CircleCropTransformation()) .build() val bitmap = runBlocking { @@ -89,7 +89,7 @@ class NotificationBitmapLoader @Inject constructor( result.drawable?.toBitmap() } return bitmap?.let { IconCompat.createWithBitmap(it) } - } catch (e: Exception) { + } catch (e: Throwable) { Timber.e(e, "Unable to load user bitmap") null } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index 8024e0c6cb..a03fd00dc1 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -22,6 +22,7 @@ import androidx.core.app.Person import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R +import io.element.android.libraries.push.impl.notifications.debug.annotateForDebug import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.services.toolbox.api.strings.StringProvider @@ -46,12 +47,12 @@ class RoomGroupMessageCreator @Inject constructor( val roomIsGroup = !lastKnownRoomEvent.roomIsDirect val style = NotificationCompat.MessagingStyle( Person.Builder() - .setName(currentUser.displayName) + .setName(currentUser.displayName?.annotateForDebug(50)) .setIcon(bitmapLoader.getUserIcon(currentUser.avatarUrl)) .setKey(lastKnownRoomEvent.sessionId.value) .build() ).also { - it.conversationTitle = roomName.takeIf { roomIsGroup } + it.conversationTitle = roomName.takeIf { roomIsGroup }?.annotateForDebug(51) it.isGroupConversation = roomIsGroup it.addMessagesFromEvents(events) } @@ -103,7 +104,7 @@ class RoomGroupMessageCreator @Inject constructor( null } else { Person.Builder() - .setName(event.senderName) + .setName(event.senderName?.annotateForDebug(70)) .setIcon(bitmapLoader.getUserIcon(event.senderAvatarPath)) .setKey(event.senderId) .build() @@ -115,7 +116,11 @@ class RoomGroupMessageCreator @Inject constructor( senderPerson ) else -> { - val message = NotificationCompat.MessagingStyle.Message(event.body, event.timestamp, senderPerson).also { message -> + val message = NotificationCompat.MessagingStyle.Message( + event.body?.annotateForDebug(71), + event.timestamp, + senderPerson + ).also { message -> event.imageUri?.let { message.setData("image/", it) } diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt index 78c6ab3ee0..5a7f3d36e8 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/SummaryGroupMessageCreator.kt @@ -20,6 +20,7 @@ import android.app.Notification import androidx.core.app.NotificationCompat import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R +import io.element.android.libraries.push.impl.notifications.debug.annotateForDebug import io.element.android.libraries.push.impl.notifications.factories.NotificationFactory import io.element.android.services.toolbox.api.strings.StringProvider import javax.inject.Inject @@ -40,7 +41,7 @@ import javax.inject.Inject */ class SummaryGroupMessageCreator @Inject constructor( private val stringProvider: StringProvider, - private val notificationFactory: NotificationFactory + private val notificationFactory: NotificationFactory, ) { fun createSummaryNotification( @@ -51,9 +52,9 @@ class SummaryGroupMessageCreator @Inject constructor( useCompleteNotificationFormat: Boolean ): Notification { val summaryInboxStyle = NotificationCompat.InboxStyle().also { style -> - roomNotifications.forEach { style.addLine(it.summaryLine) } - invitationNotifications.forEach { style.addLine(it.summaryLine) } - simpleNotifications.forEach { style.addLine(it.summaryLine) } + roomNotifications.forEach { style.addLine(it.summaryLine.annotateForDebug(40)) } + invitationNotifications.forEach { style.addLine(it.summaryLine.annotateForDebug(41)) } + simpleNotifications.forEach { style.addLine(it.summaryLine.annotateForDebug(42)) } } val summaryIsNoisy = roomNotifications.any { it.shouldBing } || @@ -69,9 +70,10 @@ class SummaryGroupMessageCreator @Inject constructor( // FIXME roomIdToEventMap.size is not correct, this is the number of rooms val nbEvents = roomNotifications.size + simpleNotifications.size val sumTitle = stringProvider.getQuantityString(R.plurals.notification_compat_summary_title, nbEvents, nbEvents) - summaryInboxStyle.setBigContentTitle(sumTitle) - // TODO get latest event? - .setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents)) + summaryInboxStyle.setBigContentTitle(sumTitle.annotateForDebug(43)) + //.setSummaryText(stringProvider.getQuantityString(R.plurals.notification_unread_notified_messages, nbEvents, nbEvents).annotateForDebug(44)) + // Use account name now, for multi-session + .setSummaryText(currentUser.userId.value.annotateForDebug(44)) return if (useCompleteNotificationFormat) { notificationFactory.createSummaryListNotification( currentUser, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt new file mode 100644 index 0000000000..eca99394f6 --- /dev/null +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.push.impl.notifications.debug + +fun CharSequence.annotateForDebug(prefix: Int): CharSequence { + return "$prefix-$this" +} diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt index 18bfdd8292..9da47a6569 100755 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationFactory.kt @@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.R import io.element.android.libraries.push.impl.notifications.RoomEventGroupInfo import io.element.android.libraries.push.impl.notifications.channels.NotificationChannels +import io.element.android.libraries.push.impl.notifications.debug.annotateForDebug import io.element.android.libraries.push.impl.notifications.factories.action.AcceptInvitationActionFactory import io.element.android.libraries.push.impl.notifications.factories.action.MarkAsReadActionFactory import io.element.android.libraries.push.impl.notifications.factories.action.QuickReplyActionFactory @@ -84,16 +85,16 @@ class NotificationFactory @Inject constructor( // ID of the corresponding shortcut, for conversation features under API 30+ .setShortcutId(roomInfo.roomId.value) // Title for API < 16 devices. - .setContentTitle(roomInfo.roomDisplayName) + .setContentTitle(roomInfo.roomDisplayName.annotateForDebug(1)) // Content for API < 16 devices. - .setContentText(stringProvider.getString(R.string.notification_new_messages)) + .setContentText(stringProvider.getString(R.string.notification_new_messages).annotateForDebug(2)) // Number of new notifications for API <24 (M and below) devices. .setSubText( stringProvider.getQuantityString( R.plurals.notification_new_messages_for_room, messageStyle.messages.size, messageStyle.messages.size - ) + ).annotateForDebug(3) ) // Auto-bundling is enabled for 4 or more notifications on API 24+ (N+) // devices and all Wear devices. But we want a custom grouping, so we specify the groupID @@ -135,7 +136,7 @@ class NotificationFactory @Inject constructor( } setDeleteIntent(pendingIntentFactory.createDismissRoomPendingIntent(roomInfo.sessionId, roomInfo.roomId)) } - .setTicker(tickerText) + .setTicker(tickerText.annotateForDebug(4)) .build() } @@ -147,8 +148,8 @@ class NotificationFactory @Inject constructor( val channelId = notificationChannels.getChannelIdForMessage(inviteNotifiableEvent.noisy) return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) - .setContentTitle(inviteNotifiableEvent.roomName ?: buildMeta.applicationName) - .setContentText(inviteNotifiableEvent.description) + .setContentTitle((inviteNotifiableEvent.roomName ?: buildMeta.applicationName).annotateForDebug(5)) + .setContentText(inviteNotifiableEvent.description.annotateForDebug(6)) .setGroup(inviteNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) .setSmallIcon(smallIcon) @@ -196,8 +197,8 @@ class NotificationFactory @Inject constructor( val channelId = notificationChannels.getChannelIdForMessage(simpleNotifiableEvent.noisy) return NotificationCompat.Builder(context, channelId) .setOnlyAlertOnce(true) - .setContentTitle(buildMeta.applicationName) - .setContentText(simpleNotifiableEvent.description) + .setContentTitle(buildMeta.applicationName.annotateForDebug(7)) + .setContentText(simpleNotifiableEvent.description.annotateForDebug(8)) .setGroup(simpleNotifiableEvent.sessionId.value) .setGroupAlertBehavior(NotificationCompat.GROUP_ALERT_ALL) .setSmallIcon(smallIcon) @@ -240,11 +241,11 @@ class NotificationFactory @Inject constructor( // used in compat < N, after summary is built based on child notifications .setWhen(lastMessageTimestamp) .setStyle(style) - .setContentTitle(currentUser.userId.value) + .setContentTitle(currentUser.userId.value.annotateForDebug(9)) .setCategory(NotificationCompat.CATEGORY_MESSAGE) .setSmallIcon(smallIcon) // set content text to support devices running API level < 24 - .setContentText(compatSummary) + .setContentText(compatSummary.annotateForDebug(10)) .setGroup(currentUser.userId.value) // set this notification as the summary for the group .setGroupSummary(true) From e02ba42ea081aa7ad31b0432ff0f448870d35fbd Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 16:34:24 +0200 Subject: [PATCH 64/75] Bigger image - WIP --- .../push/impl/notifications/NotificationBitmapLoader.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index a965c6aaa5..0302a9ced6 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -51,7 +51,7 @@ class NotificationBitmapLoader @Inject constructor( private fun loadRoomBitmap(path: String): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(512))) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) .build() runBlocking { val result = context.imageLoader.execute(imageRequest) @@ -81,7 +81,7 @@ class NotificationBitmapLoader @Inject constructor( private fun loadUserIcon(path: String): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(512))) + .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() val bitmap = runBlocking { From 1ac5f8e788bbc672fcfba9e1170f75f6664e1a35 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 16:35:06 +0200 Subject: [PATCH 65/75] Disable debugging of notification --- .../push/impl/notifications/debug/DebugNotification.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt index eca99394f6..37f33e1188 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/debug/DebugNotification.kt @@ -16,6 +16,6 @@ package io.element.android.libraries.push.impl.notifications.debug -fun CharSequence.annotateForDebug(prefix: Int): CharSequence { - return "$prefix-$this" +fun CharSequence.annotateForDebug(@Suppress("UNUSED_PARAMETER") prefix: Int): CharSequence { + return this // "$prefix-$this" } From 183dd153ffe94c542c16f13b4880123c20c4aaa0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 17:13:24 +0200 Subject: [PATCH 66/75] Fix test --- .../notifications/NotificationFactoryTest.kt | 23 ++++++++++++------- .../notifications/NotificationRendererTest.kt | 9 +++----- .../fake/FakeNotificationFactory.kt | 10 ++++---- .../fake/FakeRoomGroupMessageCreator.kt | 14 +++++++---- 4 files changed, 31 insertions(+), 25 deletions(-) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt index 9fbd723071..016d90952e 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt @@ -18,6 +18,7 @@ package io.element.android.libraries.push.impl.notifications import com.google.common.truth.Truth.assertThat import io.element.android.libraries.matrix.api.core.EventId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -124,11 +125,13 @@ class NotificationFactoryTest { fun `given room with message when mapping to notification then delegates to room group message creator`() = testWith(notificationFactory) { val events = listOf(A_MESSAGE_EVENT) val expectedNotification = roomGroupMessageCreator.givenCreatesRoomMessageFor( - A_SESSION_ID, events, A_ROOM_ID, A_SESSION_ID.value, MY_AVATAR_URL + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), events, A_ROOM_ID ) val roomWithMessage = mapOf(A_ROOM_ID to listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, A_MESSAGE_EVENT))) - val result = roomWithMessage.toNotifications(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + val result = roomWithMessage.toNotifications( + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + ) assertThat(result).isEqualTo(listOf(expectedNotification)) } @@ -138,7 +141,9 @@ class NotificationFactoryTest { val events = listOf(ProcessedEvent(ProcessedEvent.Type.REMOVE, A_MESSAGE_EVENT)) val emptyRoom = mapOf(A_ROOM_ID to events) - val result = emptyRoom.toNotifications(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + val result = emptyRoom.toNotifications( + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + ) assertThat(result).isEqualTo( listOf( @@ -153,7 +158,9 @@ class NotificationFactoryTest { fun `given a room with only redacted events when mapping to notification then is Empty`() = testWith(notificationFactory) { val redactedRoom = mapOf(A_ROOM_ID to listOf(ProcessedEvent(ProcessedEvent.Type.KEEP, A_MESSAGE_EVENT.copy(isRedacted = true)))) - val result = redactedRoom.toNotifications(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + val result = redactedRoom.toNotifications( + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + ) assertThat(result).isEqualTo( listOf( @@ -176,14 +183,14 @@ class NotificationFactoryTest { ) val withRedactedRemoved = listOf(A_MESSAGE_EVENT.copy(eventId = EventId("\$not-redacted"))) val expectedNotification = roomGroupMessageCreator.givenCreatesRoomMessageFor( - A_SESSION_ID, + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL), withRedactedRemoved, A_ROOM_ID, - A_SESSION_ID.value, - MY_AVATAR_URL ) - val result = roomWithRedactedMessage.toNotifications(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + val result = roomWithRedactedMessage.toNotifications( + MatrixUser(A_SESSION_ID, A_SESSION_ID.value, MY_AVATAR_URL) + ) assertThat(result).isEqualTo(listOf(expectedNotification)) } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index 79c6dfdb02..a94ef3f02d 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -17,6 +17,7 @@ package io.element.android.libraries.push.impl.notifications import android.app.Notification +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -193,9 +194,7 @@ class NotificationRendererTest { private fun renderEventsAsNotifications() { notificationRenderer.render( - sessionId = A_SESSION_ID, - myUserDisplayName = MY_USER_DISPLAY_NAME, - myUserAvatarUrl = MY_USER_AVATAR_URL, + MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL), useCompleteNotificationFormat = USE_COMPLETE_NOTIFICATION_FORMAT, eventsToProcess = AN_EVENT_LIST ) @@ -214,9 +213,7 @@ class NotificationRendererTest { ) { notificationFactory.givenNotificationsFor( groupedEvents = A_PROCESSED_EVENTS, - sessionId = A_SESSION_ID, - myUserDisplayName = MY_USER_DISPLAY_NAME, - myUserAvatarUrl = MY_USER_AVATAR_URL, + matrixUser = MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL), useCompleteNotificationFormat = useCompleteNotificationFormat, roomNotifications = roomNotifications, invitationNotifications = invitationNotifications, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt index 7d7812e6cb..999cd6f54a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt @@ -16,7 +16,7 @@ package io.element.android.libraries.push.impl.notifications.fake -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.GroupedNotificationEvents import io.element.android.libraries.push.impl.notifications.NotificationFactory import io.element.android.libraries.push.impl.notifications.OneShotNotification @@ -30,9 +30,7 @@ class FakeNotificationFactory { fun givenNotificationsFor( groupedEvents: GroupedNotificationEvents, - sessionId: SessionId, - myUserDisplayName: String, - myUserAvatarUrl: String?, + matrixUser: MatrixUser, useCompleteNotificationFormat: Boolean, roomNotifications: List, invitationNotifications: List, @@ -40,13 +38,13 @@ class FakeNotificationFactory { summaryNotification: SummaryNotification ) { with(instance) { - every { groupedEvents.roomEvents.toNotifications(sessionId, myUserDisplayName, myUserAvatarUrl) } returns roomNotifications + every { groupedEvents.roomEvents.toNotifications(matrixUser) } returns roomNotifications every { groupedEvents.invitationEvents.toNotifications() } returns invitationNotifications every { groupedEvents.simpleEvents.toNotifications() } returns simpleNotifications every { createSummaryNotification( - sessionId, + matrixUser, roomNotifications, invitationNotifications, simpleNotifications, diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt index df0b5ad42b..bba9fde202 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt @@ -17,7 +17,7 @@ package io.element.android.libraries.push.impl.notifications.fake import io.element.android.libraries.matrix.api.core.RoomId -import io.element.android.libraries.matrix.api.core.SessionId +import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.RoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.RoomNotification import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent @@ -29,14 +29,18 @@ class FakeRoomGroupMessageCreator { val instance = mockk() fun givenCreatesRoomMessageFor( - sessionId: SessionId, + matrixUser: MatrixUser, events: List, roomId: RoomId, - userDisplayName: String, - userAvatarUrl: String? ): RoomNotification.Message { val mockMessage = mockk() - every { instance.createRoomMessage(sessionId, events, roomId, userDisplayName, userAvatarUrl) } returns mockMessage + every { + instance.createRoomMessage( + currentUser = matrixUser, + events = events, + roomId = roomId, + ) + } returns mockMessage return mockMessage } } From 685b034977211d97928c778429ff25aa7916809a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 17:32:33 +0200 Subject: [PATCH 67/75] Cleanup --- .../libraries/matrix/api/notification/NotificationData.kt | 2 -- .../libraries/matrix/impl/notification/TimelineEventMapper.kt | 2 -- .../push/impl/notifications/NotifiableEventResolver.kt | 2 -- .../push/impl/notifications/model/NotifiableMessageEvent.kt | 3 +++ 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt index 60d889d046..eb6e9998ac 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt @@ -35,8 +35,6 @@ data class NotificationData( ) data class NotificationEvent( - val eventId: EventId, - val senderId: UserId, val timestamp: Long, val content: String, // For images for instance diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt index 3d2759c6d1..adb9dcce72 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/TimelineEventMapper.kt @@ -32,8 +32,6 @@ class TimelineEventMapper @Inject constructor() { fun map(timelineEvent: TimelineEvent): NotificationEvent { return timelineEvent.use { NotificationEvent( - eventId = EventId(it.eventId()), - senderId = UserId(it.senderId()), timestamp = it.timestamp().toLong(), content = it.eventType().toContent(), contentUrl = null // TODO it.eventType().toContentUrl(), diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index 16abf69874..8e274b2124 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -114,8 +114,6 @@ private fun NotificationData?.orDefault(roomId: RoomId, eventId: EventId): Notif isEncrypted = false, isDirect = false, event = NotificationEvent( - eventId = eventId, - senderId = UserId("@user:domain"), timestamp = System.currentTimeMillis(), content = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", contentUrl = null diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt index d7af528ce3..1216e0fe12 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt @@ -57,6 +57,9 @@ data class NotifiableMessageEvent( val description: String = body ?: "" val title: String = senderName ?: "" + // TODO EAx The image has to be downloaded and expose using the file provider. + // Example of value from Element Android: + // content://im.vector.app.debug.mx-sdk.fileprovider/downloads/downloads/816abf76d806c768760568952b1862c8/F/72c33edd23dee3b95f4d5a18aa25fa54/image.png val imageUri: Uri? get() = imageUriString?.let { Uri.parse(it) } } From c7e68e5d813ac08626458a5c9a4f59d1d782b5c2 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 24 May 2023 17:58:16 +0200 Subject: [PATCH 68/75] Use coroutine dispatcher instead of WorkerThread --- .../notifications/NotificationBitmapLoader.kt | 26 +++----- .../NotificationDrawerManager.kt | 66 ++++++++----------- .../impl/notifications/NotificationFactory.kt | 2 +- .../notifications/NotificationRenderer.kt | 4 +- .../notifications/RoomGroupMessageCreator.kt | 6 +- .../notifications/NotificationFactoryTest.kt | 7 +- .../fake/FakeNotificationFactory.kt | 3 +- .../fake/FakeRoomGroupMessageCreator.kt | 4 +- 8 files changed, 48 insertions(+), 70 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index 0302a9ced6..a3644e737f 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -19,7 +19,6 @@ package io.element.android.libraries.push.impl.notifications import android.content.Context import android.graphics.Bitmap import android.os.Build -import androidx.annotation.WorkerThread import androidx.core.graphics.drawable.IconCompat import androidx.core.graphics.drawable.toBitmap import coil.imageLoader @@ -27,7 +26,6 @@ import coil.request.ImageRequest import coil.transform.CircleCropTransformation import io.element.android.libraries.di.ApplicationContext import io.element.android.libraries.matrix.api.media.MediaResolver -import kotlinx.coroutines.runBlocking import timber.log.Timber import javax.inject.Inject @@ -39,24 +37,20 @@ class NotificationBitmapLoader @Inject constructor( * Get icon of a room. * @param path mxc url */ - @WorkerThread - fun getRoomBitmap(path: String?): Bitmap? { + suspend fun getRoomBitmap(path: String?): Bitmap? { if (path == null) { return null } return loadRoomBitmap(path) } - @WorkerThread - private fun loadRoomBitmap(path: String): Bitmap? { + private suspend fun loadRoomBitmap(path: String): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) .build() - runBlocking { - val result = context.imageLoader.execute(imageRequest) - result.drawable?.toBitmap() - } + val result = context.imageLoader.execute(imageRequest) + result.drawable?.toBitmap() } catch (e: Throwable) { Timber.e(e, "Unable to load room bitmap") null @@ -68,8 +62,7 @@ class NotificationBitmapLoader @Inject constructor( * Before Android P, this does nothing because the icon won't be used * @param path mxc url */ - @WorkerThread - fun getUserIcon(path: String?): IconCompat? { + suspend fun getUserIcon(path: String?): IconCompat? { if (path == null || Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { return null } @@ -77,17 +70,14 @@ class NotificationBitmapLoader @Inject constructor( return loadUserIcon(path) } - @WorkerThread - private fun loadUserIcon(path: String): IconCompat? { + private suspend fun loadUserIcon(path: String): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() - val bitmap = runBlocking { - val result = context.imageLoader.execute(imageRequest) - result.drawable?.toBitmap() - } + val result = context.imageLoader.execute(imageRequest) + val bitmap = result.drawable?.toBitmap() return bitmap?.let { IconCompat.createWithBitmap(it) } } catch (e: Throwable) { Timber.e(e, "Unable to load user bitmap") diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt index c3c5275eb5..87d37e7e33 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDrawerManager.kt @@ -16,11 +16,9 @@ package io.element.android.libraries.push.impl.notifications -import android.os.Handler -import android.os.HandlerThread -import androidx.annotation.WorkerThread import io.element.android.libraries.androidutils.throttler.FirstThrottler import io.element.android.libraries.core.cache.CircularCache +import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.data.tryOrNull import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.di.AppScope @@ -37,8 +35,9 @@ import io.element.android.libraries.push.impl.notifications.model.shouldIgnoreMe import io.element.android.services.appnavstate.api.AppNavigationState import io.element.android.services.appnavstate.api.AppNavigationStateService import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.delay import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withContext import timber.log.Timber import javax.inject.Inject @@ -56,13 +55,10 @@ class NotificationDrawerManager @Inject constructor( private val filteredEventDetector: FilteredEventDetector, private val appNavigationStateService: AppNavigationStateService, private val coroutineScope: CoroutineScope, + private val dispatchers: CoroutineDispatchers, private val buildMeta: BuildMeta, private val matrixAuthenticationService: MatrixAuthenticationService, ) { - - private val handlerThread: HandlerThread = HandlerThread("NotificationDrawerManager", Thread.MIN_PRIORITY) - private var backgroundHandler: Handler - /** * Lazily initializes the NotificationState as we rely on having a current session in order to fetch the persisted queue of events. */ @@ -74,8 +70,6 @@ class NotificationDrawerManager @Inject constructor( private var useCompleteNotificationFormat = true init { - handlerThread.start() - backgroundHandler = Handler(handlerThread.looper) // Observe application state coroutineScope.launch { appNavigationStateService.appNavigationStateFlow @@ -193,30 +187,25 @@ class NotificationDrawerManager @Inject constructor( notificationState.updateQueuedEvents(this) { queuedEvents, _ -> action(queuedEvents) } - refreshNotificationDrawer() + coroutineScope.refreshNotificationDrawer() } - private fun refreshNotificationDrawer() { + private fun CoroutineScope.refreshNotificationDrawer() = launch { // Implement last throttler val canHandle = firstThrottler.canHandle() Timber.v("refreshNotificationDrawer(), delay: ${canHandle.waitMillis()} ms") - backgroundHandler.removeCallbacksAndMessages(null) - - backgroundHandler.postDelayed( - { - try { - refreshNotificationDrawerBg() - } catch (throwable: Throwable) { - // It can happen if for instance session has been destroyed. It's a bit ugly to try catch like this, but it's safer - Timber.w(throwable, "refreshNotificationDrawerBg failure") - } - }, - canHandle.waitMillis() - ) + withContext(dispatchers.io) { + delay(canHandle.waitMillis()) + try { + refreshNotificationDrawerBg() + } catch (throwable: Throwable) { + // It can happen if for instance session has been destroyed. It's a bit ugly to try catch like this, but it's safer + Timber.w(throwable, "refreshNotificationDrawerBg failure") + } + } } - @WorkerThread - private fun refreshNotificationDrawerBg() { + private suspend fun refreshNotificationDrawerBg() { Timber.v("refreshNotificationDrawerBg()") val eventsToRender = notificationState.updateQueuedEvents(this) { queuedEvents, renderedEvents -> notifiableEventProcessor.process(queuedEvents.rawEvents(), currentAppNavigationState, renderedEvents).also { @@ -239,8 +228,7 @@ class NotificationDrawerManager @Inject constructor( } } - @WorkerThread - private fun renderEvents(eventsToRender: List>) { + private suspend fun renderEvents(eventsToRender: List>) { // Group by sessionId val eventsForSessions = eventsToRender.groupBy { it.event.sessionId @@ -250,18 +238,16 @@ class NotificationDrawerManager @Inject constructor( val currentUser = tryOrNull( onError = { Timber.e(it, "Unable to retrieve info for user ${sessionId.value}") }, operation = { - runBlocking { - val client = matrixAuthenticationService.restoreSession(sessionId).getOrNull() + val client = matrixAuthenticationService.restoreSession(sessionId).getOrNull() - // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash - val myUserDisplayName = client?.loadUserDisplayName()?.getOrNull() ?: sessionId.value - val userAvatarUrl = client?.loadUserAvatarURLString()?.getOrNull() - MatrixUser( - userId = sessionId, - displayName = myUserDisplayName, - avatarUrl = userAvatarUrl - ) - } + // myUserDisplayName cannot be empty else NotificationCompat.MessagingStyle() will crash + val myUserDisplayName = client?.loadUserDisplayName()?.getOrNull() ?: sessionId.value + val userAvatarUrl = client?.loadUserAvatarURLString()?.getOrNull() + MatrixUser( + userId = sessionId, + displayName = myUserDisplayName, + avatarUrl = userAvatarUrl + ) } ) ?: MatrixUser( userId = sessionId, diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt index 96f5e5c81b..79173611dc 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactory.kt @@ -34,7 +34,7 @@ class NotificationFactory @Inject constructor( private val summaryGroupMessageCreator: SummaryGroupMessageCreator ) { - fun Map.toNotifications( + suspend fun Map.toNotifications( currentUser: MatrixUser, ): List { return map { (roomId, events) -> diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt index 33e15510f1..428420211b 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRenderer.kt @@ -16,7 +16,6 @@ package io.element.android.libraries.push.impl.notifications -import androidx.annotation.WorkerThread import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.model.InviteNotifiableEvent @@ -32,8 +31,7 @@ class NotificationRenderer @Inject constructor( private val notificationFactory: NotificationFactory, ) { - @WorkerThread - fun render( + suspend fun render( currentUser: MatrixUser, useCompleteNotificationFormat: Boolean, eventsToProcess: List> diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt index a03fd00dc1..5656b81dd9 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt @@ -37,7 +37,7 @@ class RoomGroupMessageCreator @Inject constructor( private val notificationFactory: NotificationFactory ) { - fun createRoomMessage( + suspend fun createRoomMessage( currentUser: MatrixUser, events: List, roomId: RoomId, @@ -98,7 +98,7 @@ class RoomGroupMessageCreator @Inject constructor( ) } - private fun NotificationCompat.MessagingStyle.addMessagesFromEvents(events: List) { + private suspend fun NotificationCompat.MessagingStyle.addMessagesFromEvents(events: List) { events.forEach { event -> val senderPerson = if (event.outGoingMessage) { null @@ -171,7 +171,7 @@ class RoomGroupMessageCreator @Inject constructor( } } - private fun getRoomBitmap(events: List): Bitmap? { + private suspend fun getRoomBitmap(events: List): Bitmap? { // Use the last event (most recent?) return events.lastOrNull() ?.roomAvatarPath diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt index 016d90952e..18d8870ac3 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationFactoryTest.kt @@ -28,6 +28,7 @@ import io.element.android.libraries.push.impl.notifications.fake.FakeSummaryGrou import io.element.android.libraries.push.impl.notifications.fixtures.aNotifiableMessageEvent import io.element.android.libraries.push.impl.notifications.fixtures.aSimpleNotifiableEvent import io.element.android.libraries.push.impl.notifications.fixtures.anInviteNotifiableEvent +import kotlinx.coroutines.test.runTest import org.junit.Test private val MY_AVATAR_URL: String? = null @@ -196,6 +197,8 @@ class NotificationFactoryTest { } } -fun testWith(receiver: T, block: T.() -> Unit) { - receiver.block() +fun testWith(receiver: T, block: suspend T.() -> Unit) { + runTest { + receiver.block() + } } diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt index 999cd6f54a..09957e2cf2 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeNotificationFactory.kt @@ -22,6 +22,7 @@ import io.element.android.libraries.push.impl.notifications.NotificationFactory import io.element.android.libraries.push.impl.notifications.OneShotNotification import io.element.android.libraries.push.impl.notifications.RoomNotification import io.element.android.libraries.push.impl.notifications.SummaryNotification +import io.mockk.coEvery import io.mockk.every import io.mockk.mockk @@ -38,7 +39,7 @@ class FakeNotificationFactory { summaryNotification: SummaryNotification ) { with(instance) { - every { groupedEvents.roomEvents.toNotifications(matrixUser) } returns roomNotifications + coEvery { groupedEvents.roomEvents.toNotifications(matrixUser) } returns roomNotifications every { groupedEvents.invitationEvents.toNotifications() } returns invitationNotifications every { groupedEvents.simpleEvents.toNotifications() } returns simpleNotifications diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt index bba9fde202..b896737e6f 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fake/FakeRoomGroupMessageCreator.kt @@ -21,7 +21,7 @@ import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.push.impl.notifications.RoomGroupMessageCreator import io.element.android.libraries.push.impl.notifications.RoomNotification import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent -import io.mockk.every +import io.mockk.coEvery import io.mockk.mockk class FakeRoomGroupMessageCreator { @@ -34,7 +34,7 @@ class FakeRoomGroupMessageCreator { roomId: RoomId, ): RoomNotification.Message { val mockMessage = mockk() - every { + coEvery { instance.createRoomMessage( currentUser = matrixUser, events = events, From 881c8fd97b7f421bd24f65ef1a19fd2e9b7da84e Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 30 May 2023 14:24:42 +0200 Subject: [PATCH 69/75] Fix test --- .../notifications/NotificationRendererTest.kt | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt index a94ef3f02d..c109edb40a 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationRendererTest.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.push.impl.notifications.fake.FakeNotificatio import io.element.android.libraries.push.impl.notifications.fake.FakeNotificationFactory import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.mockk.mockk +import kotlinx.coroutines.test.runTest import org.junit.Test private const val MY_USER_DISPLAY_NAME = "display-name" @@ -54,7 +55,7 @@ class NotificationRendererTest { ) @Test - fun `given no notifications when rendering then cancels summary notification`() { + fun `given no notifications when rendering then cancels summary notification`() = runTest { givenNoNotifications() renderEventsAsNotifications() @@ -64,7 +65,7 @@ class NotificationRendererTest { } @Test - fun `given last room message group notification is removed when rendering then remove the summary and then remove message notification`() { + fun `given last room message group notification is removed when rendering then remove the summary and then remove message notification`() = runTest { givenNotifications(roomNotifications = listOf(RoomNotification.Removed(A_ROOM_ID)), summaryNotification = A_REMOVE_SUMMARY_NOTIFICATION) renderEventsAsNotifications() @@ -76,7 +77,7 @@ class NotificationRendererTest { } @Test - fun `given a room message group notification is removed when rendering then remove the message notification and update summary`() { + fun `given a room message group notification is removed when rendering then remove the message notification and update summary`() = runTest { givenNotifications(roomNotifications = listOf(RoomNotification.Removed(A_ROOM_ID))) renderEventsAsNotifications() @@ -88,7 +89,7 @@ class NotificationRendererTest { } @Test - fun `given a room message group notification is added when rendering then show the message notification and update summary`() { + fun `given a room message group notification is added when rendering then show the message notification and update summary`() = runTest { givenNotifications( roomNotifications = listOf( RoomNotification.Message( @@ -107,7 +108,7 @@ class NotificationRendererTest { } @Test - fun `given last simple notification is removed when rendering then remove the summary and then remove simple notification`() { + fun `given last simple notification is removed when rendering then remove the summary and then remove simple notification`() = runTest { givenNotifications(simpleNotifications = listOf(OneShotNotification.Removed(AN_EVENT_ID.value)), summaryNotification = A_REMOVE_SUMMARY_NOTIFICATION) renderEventsAsNotifications() @@ -119,7 +120,7 @@ class NotificationRendererTest { } @Test - fun `given a simple notification is removed when rendering then remove the simple notification and update summary`() { + fun `given a simple notification is removed when rendering then remove the simple notification and update summary`() = runTest { givenNotifications(simpleNotifications = listOf(OneShotNotification.Removed(AN_EVENT_ID.value))) renderEventsAsNotifications() @@ -131,7 +132,7 @@ class NotificationRendererTest { } @Test - fun `given a simple notification is added when rendering then show the simple notification and update summary`() { + fun `given a simple notification is added when rendering then show the simple notification and update summary`() = runTest { givenNotifications( simpleNotifications = listOf( OneShotNotification.Append( @@ -150,7 +151,7 @@ class NotificationRendererTest { } @Test - fun `given last invitation notification is removed when rendering then remove the summary and then remove invitation notification`() { + fun `given last invitation notification is removed when rendering then remove the summary and then remove invitation notification`() = runTest { givenNotifications(invitationNotifications = listOf(OneShotNotification.Removed(A_ROOM_ID.value)), summaryNotification = A_REMOVE_SUMMARY_NOTIFICATION) renderEventsAsNotifications() @@ -162,7 +163,7 @@ class NotificationRendererTest { } @Test - fun `given an invitation notification is removed when rendering then remove the invitation notification and update summary`() { + fun `given an invitation notification is removed when rendering then remove the invitation notification and update summary`() = runTest { givenNotifications(invitationNotifications = listOf(OneShotNotification.Removed(A_ROOM_ID.value))) renderEventsAsNotifications() @@ -174,7 +175,7 @@ class NotificationRendererTest { } @Test - fun `given an invitation notification is added when rendering then show the invitation notification and update summary`() { + fun `given an invitation notification is added when rendering then show the invitation notification and update summary`() = runTest { givenNotifications( simpleNotifications = listOf( OneShotNotification.Append( @@ -192,7 +193,7 @@ class NotificationRendererTest { } } - private fun renderEventsAsNotifications() { + private suspend fun renderEventsAsNotifications() { notificationRenderer.render( MatrixUser(A_SESSION_ID, MY_USER_DISPLAY_NAME, MY_USER_AVATAR_URL), useCompleteNotificationFormat = USE_COMPLETE_NOTIFICATION_FORMAT, From c46692eb20be2b9eed8eb876eaf4905b81f6a197 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Jun 2023 12:27:11 +0200 Subject: [PATCH 70/75] Do not use System.currentTimeMillis() --- .../notifications/NotifiableEventResolver.kt | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt index 8e274b2124..fb3fcfc61f 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotifiableEventResolver.kt @@ -29,6 +29,7 @@ import io.element.android.libraries.push.impl.log.pushLoggerTag import io.element.android.libraries.push.impl.notifications.model.NotifiableEvent import io.element.android.libraries.push.impl.notifications.model.NotifiableMessageEvent import io.element.android.services.toolbox.api.strings.StringProvider +import io.element.android.services.toolbox.api.systemclock.SystemClock import timber.log.Timber import javax.inject.Inject @@ -46,6 +47,7 @@ class NotifiableEventResolver @Inject constructor( // private val displayableEventFormatter: DisplayableEventFormatter, private val matrixAuthenticationService: MatrixAuthenticationService, private val buildMeta: BuildMeta, + private val clock: SystemClock, ) { suspend fun resolveEvent(sessionId: SessionId, roomId: RoomId, eventId: EventId): NotifiableEvent? { @@ -96,27 +98,27 @@ class NotifiableEventResolver @Inject constructor( isUpdated = false ) } -} -/** - * TODO This is a temporary method for EAx. - */ -private fun NotificationData?.orDefault(roomId: RoomId, eventId: EventId): NotificationData { - return this ?: NotificationData( - eventId = eventId, - senderId = UserId("@user:domain"), - roomId = roomId, - senderAvatarUrl = null, - senderDisplayName = null, - roomAvatarUrl = null, - roomDisplayName = null, - isNoisy = false, - isEncrypted = false, - isDirect = false, - event = NotificationEvent( - timestamp = System.currentTimeMillis(), - content = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", - contentUrl = null + /** + * TODO This is a temporary method for EAx. + */ + private fun NotificationData?.orDefault(roomId: RoomId, eventId: EventId): NotificationData { + return this ?: NotificationData( + eventId = eventId, + senderId = UserId("@user:domain"), + roomId = roomId, + senderAvatarUrl = null, + senderDisplayName = null, + roomAvatarUrl = null, + roomDisplayName = null, + isNoisy = false, + isEncrypted = false, + isDirect = false, + event = NotificationEvent( + timestamp = clock.epochMillis(), + content = "Message ${eventId.value.take(8)}… in room ${roomId.value.take(8)}…", + contentUrl = null + ) ) - ) + } } From f9e75ee4f7817275d9accfd14a3bb69efa66ac72 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 10:17:14 +0000 Subject: [PATCH 71/75] Update android.gradle.plugin to v8.0.2 --- gradle/libs.versions.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 5aa00f991a..2f84422fce 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -3,7 +3,7 @@ [versions] # Project -android_gradle_plugin = "8.0.1" +android_gradle_plugin = "8.0.2" kotlin = "1.8.21" ksp = "1.8.21-1.0.11" molecule = "0.9.0" From 82031beaa527ce312ba17a8f0b6014496bf4905a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Jun 2023 15:34:45 +0200 Subject: [PATCH 72/75] Rename job - try to fix conflict. --- .github/workflows/danger.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/danger.yml b/.github/workflows/danger.yml index 8358eae14b..223a273b68 100644 --- a/.github/workflows/danger.yml +++ b/.github/workflows/danger.yml @@ -5,7 +5,7 @@ on: [pull_request] jobs: build: runs-on: ubuntu-latest - name: Danger + name: Danger main check steps: - uses: actions/checkout@v3 - run: | From 5de90c387112c47db62b6096370e24b4012c461a Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 1 Jun 2023 15:02:28 +0100 Subject: [PATCH 73/75] Danger: don't complain about reviewers (#509) Now we have a CODEOWNERS file a reviewer is assigned automatically, so there's no need for this. It also misfires a bunch (maybe if there are no _pending_ reviewers because they've already reviewed?), so removing it fixes that noise. --- tools/danger/dangerfile.js | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/danger/dangerfile.js b/tools/danger/dangerfile.js index 88e721febb..263c394e25 100644 --- a/tools/danger/dangerfile.js +++ b/tools/danger/dangerfile.js @@ -171,11 +171,6 @@ if (hasPngs) { warn("You seem to have made changes to some images. Please consider using an vector drawable.") } -// Check for reviewers -if (github.requested_reviewers.users.length == 0 && !pr.draft) { - warn("Please add a reviewer to your PR.") -} - // Check that translations have not been modified by developers const translationAllowList = [ "RiotTranslateBot", From a1e0a2c08dd5123ff29b43467f148401394e3339 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Jun 2023 17:02:48 +0200 Subject: [PATCH 74/75] Fix compilation issue after rebase. --- libraries/push/impl/build.gradle.kts | 1 + .../push/impl/notifications/NotificationBitmapLoader.kt | 7 ++++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/push/impl/build.gradle.kts b/libraries/push/impl/build.gradle.kts index da9122ef52..725961a248 100644 --- a/libraries/push/impl/build.gradle.kts +++ b/libraries/push/impl/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.network) implementation(projects.libraries.matrix.api) + implementation(projects.libraries.matrixui) api(projects.libraries.pushproviders.api) api(projects.libraries.pushstore.api) api(projects.libraries.push.api) diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt index a3644e737f..c2cdfc5677 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBitmapLoader.kt @@ -25,7 +25,8 @@ import coil.imageLoader import coil.request.ImageRequest import coil.transform.CircleCropTransformation import io.element.android.libraries.di.ApplicationContext -import io.element.android.libraries.matrix.api.media.MediaResolver +import io.element.android.libraries.matrix.api.media.MediaSource +import io.element.android.libraries.matrix.ui.media.MediaRequestData import timber.log.Timber import javax.inject.Inject @@ -47,7 +48,7 @@ class NotificationBitmapLoader @Inject constructor( private suspend fun loadRoomBitmap(path: String): Bitmap? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) + .data(MediaRequestData(MediaSource(path), MediaRequestData.Kind.Thumbnail(1024))) .build() val result = context.imageLoader.execute(imageRequest) result.drawable?.toBitmap() @@ -73,7 +74,7 @@ class NotificationBitmapLoader @Inject constructor( private suspend fun loadUserIcon(path: String): IconCompat? { return try { val imageRequest = ImageRequest.Builder(context) - .data(MediaResolver.Meta(path, MediaResolver.Kind.Thumbnail(1024))) + .data(MediaRequestData(MediaSource(path), MediaRequestData.Kind.Thumbnail(1024))) .transformations(CircleCropTransformation()) .build() val result = context.imageLoader.execute(imageRequest) From 5d0fb45ff674a4b2d535cde91131513a65ae00fe Mon Sep 17 00:00:00 2001 From: Florian Renaud Date: Thu, 1 Jun 2023 17:10:29 +0200 Subject: [PATCH 75/75] Update room properties from room details (#439) - Add the edit action in the room details - Add "Add topic" button in room details - Add the screen behind that action to edit some room properties: avatar, name, topic - Handle the save button action - enable the button only if changes are detected - display a loader "updating room" - display an error dialog if any request has failed - Check user has the right power level to change various attributes - "Add topic" is only shown if there's no topic and they are able to set on - Edit menu is only shown if they can change topic, name or avatar - On the edit page, any fields they can't change are uneditable Co-authored-by: Chris Smith --- changelog.d/242.feature | 1 + features/createroom/impl/build.gradle.kts | 2 +- .../impl/configureroom/ConfigureRoomEvents.kt | 2 +- .../configureroom/ConfigureRoomPresenter.kt | 2 +- .../impl/configureroom/ConfigureRoomState.kt | 2 +- .../impl/configureroom/ConfigureRoomView.kt | 96 ++- .../impl/src/main/res/values/localazy.xml | 4 +- .../ConfigureRoomPresenterTests.kt | 2 +- .../impl/src/main/res/values/localazy.xml | 2 +- .../impl/src/main/res/values/localazy.xml | 4 + features/roomdetails/impl/build.gradle.kts | 5 + .../roomdetails/impl/RoomDetailsAction.kt | 23 + .../roomdetails/impl/RoomDetailsFlowNode.kt | 15 + .../roomdetails/impl/RoomDetailsNode.kt | 13 + .../roomdetails/impl/RoomDetailsPresenter.kt | 26 +- .../roomdetails/impl/RoomDetailsState.kt | 9 +- .../impl/RoomDetailsStateProvider.kt | 17 +- .../roomdetails/impl/RoomDetailsView.kt | 135 +++- .../impl/edit/RoomDetailsEditEvents.kt | 27 + .../impl/edit/RoomDetailsEditNode.kt | 46 ++ .../impl/edit/RoomDetailsEditPresenter.kt | 166 +++++ .../impl/edit/RoomDetailsEditState.kt | 36 + .../impl/edit/RoomDetailsEditStateProvider.kt | 49 ++ .../impl/edit/RoomDetailsEditView.kt | 306 +++++++++ .../impl/src/main/res/values/localazy.xml | 5 +- .../roomdetails/RoomDetailsPresenterTests.kt | 137 +++- .../edit/RoomDetailsEditPresenterTest.kt | 618 ++++++++++++++++++ .../android/libraries/designsystem/Color.kt | 3 + .../components/LabelledTextField.kt | 24 +- .../designsystem/theme/ColorsDark.kt | 2 + .../designsystem/theme/ColorsLight.kt | 6 +- .../designsystem/theme/ElementColors.kt | 7 + .../libraries/matrix/api/room/MatrixRoom.kt | 10 + .../matrix/api/room/StateEventType.kt | 41 ++ .../matrix/impl/room/RustMatrixRoom.kt | 37 ++ .../matrix/impl/room/StateEventType.kt | 68 ++ .../matrix/test/room/FakeMatrixRoom.kt | 63 ++ .../ui/components/AvatarActionBottomSheet.kt | 15 +- .../matrix/ui/components/UnsavedAvatar.kt | 20 +- .../matrix/ui/media}/AvatarAction.kt | 2 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ieldDarkPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...eldLightPreview_0_null,NEXUS_5,1.0,en].png | 3 - ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...ewDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 3 + ...ewDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 3 + ...ewDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 3 + ...ewDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 3 + ...ewDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 3 + ...ewDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 3 + ...ewDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 3 + ...wLightPreview_0_null_0,NEXUS_5,1.0,en].png | 3 + ...wLightPreview_0_null_1,NEXUS_5,1.0,en].png | 3 + ...wLightPreview_0_null_2,NEXUS_5,1.0,en].png | 3 + ...wLightPreview_0_null_3,NEXUS_5,1.0,en].png | 3 + ...wLightPreview_0_null_4,NEXUS_5,1.0,en].png | 3 + ...wLightPreview_0_null_5,NEXUS_5,1.0,en].png | 3 + ...wLightPreview_0_null_6,NEXUS_5,1.0,en].png | 3 + ...rsDarkPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...rsDarkPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_0,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_1,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...sLightPreview_0_null_6,NEXUS_5,1.0,en].png | 4 +- ...arkPreview--1_1_null_2,NEXUS_5,1.0,en].png | 4 +- ...arkPreview--1_1_null_3,NEXUS_5,1.0,en].png | 4 +- ...arkPreview--1_1_null_4,NEXUS_5,1.0,en].png | 4 +- ...arkPreview--1_1_null_5,NEXUS_5,1.0,en].png | 4 +- ...arkPreview--1_1_null_7,NEXUS_5,1.0,en].png | 4 +- ...arkPreview--1_1_null_8,NEXUS_5,1.0,en].png | 3 + ...arkPreview--1_1_null_9,NEXUS_5,1.0,en].png | 3 + ...ghtPreview--0_0_null_2,NEXUS_5,1.0,en].png | 4 +- ...ghtPreview--0_0_null_3,NEXUS_5,1.0,en].png | 4 +- ...ghtPreview--0_0_null_4,NEXUS_5,1.0,en].png | 4 +- ...ghtPreview--0_0_null_5,NEXUS_5,1.0,en].png | 4 +- ...ghtPreview--0_0_null_7,NEXUS_5,1.0,en].png | 4 +- ...ghtPreview--0_0_null_8,NEXUS_5,1.0,en].png | 3 + ...ghtPreview--0_0_null_9,NEXUS_5,1.0,en].png | 3 + ...ieldDarkPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...eldLightPreview_0_null,NEXUS_5,1.0,en].png | 3 + ...eetDarkPreview_0_null,NEXUS_5,1.0,en].png} | 0 ...etLightPreview_0_null,NEXUS_5,1.0,en].png} | 0 ...tarDarkPreview_0_null,NEXUS_5,1.0,en].png} | 0 ...arLightPreview_0_null,NEXUS_5,1.0,en].png} | 0 100 files changed, 2031 insertions(+), 219 deletions(-) create mode 100644 changelog.d/242.feature create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsAction.kt create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditEvents.kt create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt create mode 100644 features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt create mode 100644 features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt rename {features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl => libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem}/components/LabelledTextField.kt (77%) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/avatar/AvatarActionListView.kt => libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt (92%) rename features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/Avatar.kt => libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt (86%) rename {features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/avatar => libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media}/AvatarAction.kt (95%) delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png delete mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_9,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_9,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png create mode 100644 tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom.avatar_null_DefaultGroup_SheetContentDarkPreview_0_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_AvatarActionBottomSheetDarkPreview_0_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom.avatar_null_DefaultGroup_SheetContentLightPreview_0_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_AvatarActionBottomSheetLightPreview_0_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_AvatarDarkPreview_0_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnsavedAvatarDarkPreview_0_null,NEXUS_5,1.0,en].png} (100%) rename tests/uitests/src/test/snapshots/images/{io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_AvatarLightPreview_0_null,NEXUS_5,1.0,en].png => io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnsavedAvatarLightPreview_0_null,NEXUS_5,1.0,en].png} (100%) diff --git a/changelog.d/242.feature b/changelog.d/242.feature new file mode 100644 index 0000000000..fd82c425d3 --- /dev/null +++ b/changelog.d/242.feature @@ -0,0 +1 @@ +[Create and join rooms] Update room properties from room details diff --git a/features/createroom/impl/build.gradle.kts b/features/createroom/impl/build.gradle.kts index 65c43e8fc3..f203501315 100644 --- a/features/createroom/impl/build.gradle.kts +++ b/features/createroom/impl/build.gradle.kts @@ -48,8 +48,8 @@ dependencies { implementation(projects.libraries.androidutils) implementation(projects.libraries.mediapickers.api) implementation(projects.libraries.mediaupload.api) - implementation(libs.coil.compose) implementation(projects.libraries.usersearch.impl) + implementation(libs.coil.compose) api(projects.features.createroom.api) testImplementation(libs.test.junit) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt index 9082849954..a020b387cb 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomEvents.kt @@ -17,8 +17,8 @@ package io.element.android.features.createroom.impl.configureroom import io.element.android.features.createroom.impl.CreateRoomConfig -import io.element.android.features.createroom.impl.configureroom.avatar.AvatarAction import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.media.AvatarAction sealed interface ConfigureRoomEvents { data class RoomNameChanged(val name: String) : ConfigureRoomEvents diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt index df0013972d..ca714b1e59 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenter.kt @@ -27,7 +27,6 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore -import io.element.android.features.createroom.impl.configureroom.avatar.AvatarAction import io.element.android.libraries.architecture.Async import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.architecture.execute @@ -37,6 +36,7 @@ import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters import io.element.android.libraries.matrix.api.createroom.RoomPreset import io.element.android.libraries.matrix.api.createroom.RoomVisibility +import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediaupload.api.MediaPreProcessor import kotlinx.collections.immutable.toImmutableList diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt index b99d70bb13..2e34f3bda2 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomState.kt @@ -16,8 +16,8 @@ package io.element.android.features.createroom.impl.configureroom +import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.features.createroom.impl.CreateRoomConfig -import io.element.android.features.createroom.impl.configureroom.avatar.AvatarAction import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.core.RoomId import kotlinx.collections.immutable.ImmutableList diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt index 53d46b03c9..4aae1936a9 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt +++ b/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomView.kt @@ -17,6 +17,7 @@ package io.element.android.features.createroom.impl.configureroom import android.net.Uri +import androidx.compose.foundation.clickable import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -24,9 +25,11 @@ import androidx.compose.foundation.layout.ExperimentalLayoutApi import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.selection.selectableGroup +import androidx.compose.foundation.verticalScroll import androidx.compose.material.ExperimentalMaterialApi import androidx.compose.material.ModalBottomSheetValue import androidx.compose.material.rememberModalBottomSheetState @@ -46,11 +49,9 @@ import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.element.android.features.createroom.impl.R -import io.element.android.features.createroom.impl.components.Avatar -import io.element.android.features.createroom.impl.components.LabelledTextField import io.element.android.features.createroom.impl.components.RoomPrivacyOption -import io.element.android.features.createroom.impl.configureroom.avatar.AvatarActionListView import io.element.android.libraries.architecture.Async +import io.element.android.libraries.designsystem.components.LabelledTextField import io.element.android.libraries.designsystem.components.ProgressDialog import io.element.android.libraries.designsystem.components.button.BackButton import io.element.android.libraries.designsystem.components.dialogs.RetryDialog @@ -61,7 +62,9 @@ import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TextButton import io.element.android.libraries.matrix.api.core.RoomId +import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet import io.element.android.libraries.matrix.ui.components.SelectedUsersList +import io.element.android.libraries.matrix.ui.components.UnsavedAvatar import kotlinx.coroutines.launch import io.element.android.libraries.ui.strings.R as StringR @@ -105,54 +108,48 @@ fun ConfigureRoomView( ) } ) { padding -> - LazyColumn( + Column( modifier = Modifier .padding(padding) + .imePadding() + .verticalScroll(rememberScrollState()) .consumeWindowInsets(padding), verticalArrangement = Arrangement.spacedBy(24.dp), ) { - item { - RoomNameWithAvatar( - modifier = Modifier.padding(horizontal = 16.dp), - avatarUri = state.config.avatarUri, - roomName = state.config.roomName.orEmpty(), - onAvatarClick = ::onAvatarClicked, - onRoomNameChanged = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) }, - ) - } - item { - RoomTopic( - modifier = Modifier.padding(horizontal = 16.dp), - topic = state.config.topic.orEmpty(), - onTopicChanged = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) }, - ) - } + RoomNameWithAvatar( + modifier = Modifier.padding(horizontal = 16.dp), + avatarUri = state.config.avatarUri, + roomName = state.config.roomName.orEmpty(), + onAvatarClick = ::onAvatarClicked, + onRoomNameChanged = { state.eventSink(ConfigureRoomEvents.RoomNameChanged(it)) }, + ) + RoomTopic( + modifier = Modifier.padding(horizontal = 16.dp), + topic = state.config.topic.orEmpty(), + onTopicChanged = { state.eventSink(ConfigureRoomEvents.TopicChanged(it)) }, + ) if (state.config.invites.isNotEmpty()) { - item { - SelectedUsersList( - contentPadding = PaddingValues(horizontal = 24.dp), - selectedUsers = state.config.invites, - onUserRemoved = { - focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.RemoveFromSelection(it)) - }, - ) - } - } - item { - RoomPrivacyOptions( - modifier = Modifier.padding(bottom = 40.dp), - selected = state.config.privacy, - onOptionSelected = { + SelectedUsersList( + contentPadding = PaddingValues(horizontal = 24.dp), + selectedUsers = state.config.invites, + onUserRemoved = { focusManager.clearFocus() - state.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(it.privacy)) + state.eventSink(ConfigureRoomEvents.RemoveFromSelection(it)) }, ) } + RoomPrivacyOptions( + modifier = Modifier.padding(bottom = 40.dp), + selected = state.config.privacy, + onOptionSelected = { + focusManager.clearFocus() + state.eventSink(ConfigureRoomEvents.RoomPrivacyChanged(it.privacy)) + }, + ) } } - AvatarActionListView( + AvatarActionBottomSheet( actions = state.avatarActions, modalBottomSheetState = itemActionsBottomSheetState, onActionSelected = { state.eventSink(ConfigureRoomEvents.HandleAvatarAction(it)) } @@ -221,16 +218,17 @@ fun RoomNameWithAvatar( horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically, ) { - Avatar( + UnsavedAvatar( avatarUri = avatarUri, - onClick = onAvatarClick, + modifier = Modifier.clickable(onClick = onAvatarClick), ) LabelledTextField( label = stringResource(R.string.screen_create_room_room_name_label), value = roomName, placeholder = stringResource(R.string.screen_create_room_room_name_placeholder), - onValueChange = onRoomNameChanged + singleLine = true, + onValueChange = onRoomNameChanged, ) } } @@ -269,6 +267,13 @@ fun RoomPrivacyOptions( } } +private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = + pointerInput(Unit) { + detectTapGestures(onTap = { + focusManager.clearFocus() + }) + } + @Preview @Composable fun ConfigureRoomViewLightPreview(@PreviewParameter(ConfigureRoomStateProvider::class) state: ConfigureRoomState) = @@ -286,10 +291,3 @@ private fun ContentToPreview(state: ConfigureRoomState) { ) } -private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = - pointerInput(Unit) { - detectTapGestures(onTap = { - focusManager.clearFocus() - }) - } - diff --git a/features/createroom/impl/src/main/res/values/localazy.xml b/features/createroom/impl/src/main/res/values/localazy.xml index 128855932a..b3c0a6e618 100644 --- a/features/createroom/impl/src/main/res/values/localazy.xml +++ b/features/createroom/impl/src/main/res/values/localazy.xml @@ -1,8 +1,8 @@ "New room" - "Invite people" - "Add people" + "Invite friends to Element" + "Invite people" "An error occurred when creating the room" "Messages in this room are encrypted. Encryption can’t be disabled afterwards." "Private room (invite only)" diff --git a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt index ed44cc1983..7f36b9e2b9 100644 --- a/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt +++ b/features/createroom/impl/src/test/kotlin/io/element/android/features/createroom/impl/configureroom/ConfigureRoomPresenterTests.kt @@ -21,9 +21,9 @@ import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.ui.media.AvatarAction import io.element.android.features.createroom.impl.CreateRoomConfig import io.element.android.features.createroom.impl.CreateRoomDataStore -import io.element.android.features.createroom.impl.configureroom.avatar.AvatarAction import io.element.android.features.createroom.impl.userlist.UserListDataStore import io.element.android.libraries.architecture.Async import io.element.android.libraries.matrix.api.core.RoomId diff --git a/features/invitelist/impl/src/main/res/values/localazy.xml b/features/invitelist/impl/src/main/res/values/localazy.xml index 44f6d91267..56163ec3a8 100644 --- a/features/invitelist/impl/src/main/res/values/localazy.xml +++ b/features/invitelist/impl/src/main/res/values/localazy.xml @@ -6,4 +6,4 @@ "Decline chat" "No Invites" "%1$s invited you" - \ No newline at end of file + diff --git a/features/onboarding/impl/src/main/res/values/localazy.xml b/features/onboarding/impl/src/main/res/values/localazy.xml index 3baebbaa6b..54d86ba247 100644 --- a/features/onboarding/impl/src/main/res/values/localazy.xml +++ b/features/onboarding/impl/src/main/res/values/localazy.xml @@ -1,5 +1,9 @@ + "Sign in manually" + "Sign in with QR code" + "Create account" + "Communicate and collaborate securely" "Welcome to the %1$s Beta. Supercharged, for speed and simplicity." "Be in your Element" \ No newline at end of file diff --git a/features/roomdetails/impl/build.gradle.kts b/features/roomdetails/impl/build.gradle.kts index bcd618eccd..2ad93a95f4 100644 --- a/features/roomdetails/impl/build.gradle.kts +++ b/features/roomdetails/impl/build.gradle.kts @@ -41,6 +41,8 @@ dependencies { implementation(projects.libraries.elementresources) implementation(projects.libraries.uiStrings) implementation(projects.libraries.androidutils) + implementation(projects.libraries.mediapickers.api) + implementation(projects.libraries.mediaupload.api) api(projects.features.roomdetails.api) api(projects.libraries.usersearch.api) api(projects.services.apperror.api) @@ -52,7 +54,10 @@ dependencies { testImplementation(libs.molecule.runtime) testImplementation(libs.test.truth) testImplementation(libs.test.turbine) + testImplementation(libs.test.mockk) testImplementation(projects.libraries.matrix.test) + testImplementation(projects.libraries.mediaupload.test) + testImplementation(projects.libraries.mediapickers.test) testImplementation(projects.libraries.usersearch.test) testImplementation(projects.tests.testutils) testImplementation(projects.features.leaveroom.fake) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsAction.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsAction.kt new file mode 100644 index 0000000000..61b3da21f9 --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsAction.kt @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.roomdetails.impl + +sealed interface RoomDetailsAction { + object Edit : RoomDetailsAction + + object AddTopic : RoomDetailsAction +} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt index 02783775bf..7298e0eda6 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsFlowNode.kt @@ -29,6 +29,7 @@ import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.element.android.anvilannotations.ContributesNode import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint +import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditNode import io.element.android.features.roomdetails.impl.invite.RoomInviteMembersNode import io.element.android.features.roomdetails.impl.members.RoomMemberListNode import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsNode @@ -59,6 +60,9 @@ class RoomDetailsFlowNode @AssistedInject constructor( @Parcelize object RoomMemberList : NavTarget + @Parcelize + object RoomDetailsEdit : NavTarget + @Parcelize object InviteMembers : NavTarget @@ -74,12 +78,17 @@ class RoomDetailsFlowNode @AssistedInject constructor( backstack.push(NavTarget.RoomMemberList) } + override fun editRoomDetails() { + backstack.push(NavTarget.RoomDetailsEdit) + } + override fun openInviteMembers() { backstack.push(NavTarget.InviteMembers) } } createNode(buildContext, listOf(roomDetailsCallback)) } + NavTarget.RoomMemberList -> { val roomMemberListCallback = object : RoomMemberListNode.Callback { override fun openRoomMemberDetails(roomMemberId: UserId) { @@ -92,9 +101,15 @@ class RoomDetailsFlowNode @AssistedInject constructor( } createNode(buildContext, listOf(roomMemberListCallback)) } + + NavTarget.RoomDetailsEdit -> { + createNode(buildContext) + } + NavTarget.InviteMembers -> { createNode(buildContext) } + is NavTarget.RoomMemberDetails -> { val plugins = listOf(RoomMemberDetailsNode.RoomMemberDetailsInput(navTarget.roomMemberId)) createNode(buildContext, plugins) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt index 8fe4f774d7..77153ad62d 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsNode.kt @@ -46,6 +46,7 @@ class RoomDetailsNode @AssistedInject constructor( interface Callback : Plugin { fun openRoomMemberList() fun openInviteMembers() + fun editRoomDetails() } private val callbacks = plugins() @@ -90,6 +91,10 @@ class RoomDetailsNode @AssistedInject constructor( } } + private fun onEditRoomDetails() { + callbacks.forEach { it.editRoomDetails() } + } + @Composable override fun View(modifier: Modifier) { val context = LocalContext.current @@ -103,10 +108,18 @@ class RoomDetailsNode @AssistedInject constructor( this.onShareMember(context, roomMember) } + fun onActionClicked(action: RoomDetailsAction) { + when (action) { + RoomDetailsAction.Edit -> onEditRoomDetails() + RoomDetailsAction.AddTopic -> onEditRoomDetails() + } + } + RoomDetailsView( state = state, modifier = modifier, goBack = this::navigateUp, + onActionClicked = ::onActionClicked, onShareRoom = ::onShareRoom, onShareMember = ::onShareMember, openRoomMemberList = ::openRoomMemberList, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt index 4e7d88b18e..f016b38cb3 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt @@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.ui.room.getDirectRoomMember import javax.inject.Inject @@ -52,10 +53,23 @@ class RoomDetailsPresenter @Inject constructor( val membersState by room.membersStateFlow.collectAsState() val memberCount by getMemberCount(membersState) val canInvite by getCanInvite(membersState) + val canEditName by getCanSendStateEvent(membersState, StateEventType.ROOM_NAME) + val canEditAvatar by getCanSendStateEvent(membersState, StateEventType.ROOM_AVATAR) + val canEditTopic by getCanSendStateEvent(membersState, StateEventType.ROOM_TOPIC) val dmMember by room.getDirectRoomMember(membersState) val roomMemberDetailsPresenter = roomMemberDetailsPresenter(dmMember) val roomType = getRoomType(dmMember) + val topicState = remember(canEditTopic, room.topic) { + val topic = room.topic + + when { + !topic.isNullOrBlank() -> RoomTopicState.ExistingTopic(topic) + canEditTopic -> RoomTopicState.CanAddTopic + else -> RoomTopicState.Hidden + } + } + fun handleEvents(event: RoomDetailsEvent) { when (event) { is RoomDetailsEvent.LeaveRoom -> @@ -70,10 +84,11 @@ class RoomDetailsPresenter @Inject constructor( roomName = room.name ?: room.displayName, roomAlias = room.alias, roomAvatarUrl = room.avatarUrl, - roomTopic = room.topic, + roomTopic = topicState, memberCount = memberCount, isEncrypted = room.isEncrypted, canInvite = canInvite, + canEdit = canEditAvatar || canEditName || canEditTopic, roomType = roomType.value, roomMemberDetailsState = roomMemberDetailsState, leaveRoomState = leaveRoomState, @@ -108,6 +123,15 @@ class RoomDetailsPresenter @Inject constructor( return canInvite } + @Composable + private fun getCanSendStateEvent(membersState: MatrixRoomMembersState, type: StateEventType): State { + val canSendEvent = remember(membersState) { mutableStateOf(false) } + LaunchedEffect(membersState) { + canSendEvent.value = room.canSendStateEvent(type).getOrElse { false } + } + return canSendEvent + } + @Composable private fun getMemberCount(membersState: MatrixRoomMembersState): State> { return remember(membersState) { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt index a046548b19..4554e7aec9 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsState.kt @@ -26,11 +26,12 @@ data class RoomDetailsState( val roomName: String, val roomAlias: String?, val roomAvatarUrl: String?, - val roomTopic: String?, + val roomTopic: RoomTopicState, val memberCount: Async, val isEncrypted: Boolean, val roomType: RoomDetailsType, val roomMemberDetailsState: RoomMemberDetailsState?, + val canEdit: Boolean, val canInvite: Boolean, val leaveRoomState: LeaveRoomState, val eventSink: (RoomDetailsEvent) -> Unit @@ -40,3 +41,9 @@ sealed interface RoomDetailsType { object Room : RoomDetailsType data class Dm(val roomMember: RoomMember) : RoomDetailsType } + +sealed interface RoomTopicState { + object Hidden : RoomTopicState + object CanAddTopic : RoomTopicState + data class ExistingTopic(val topic: String) : RoomTopicState +} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt index 9cb5b925fa..82fd0ae0aa 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsStateProvider.kt @@ -28,13 +28,15 @@ open class RoomDetailsStateProvider : PreviewParameterProvider override val values: Sequence get() = sequenceOf( aRoomDetailsState(), - aRoomDetailsState().copy(roomTopic = null), + aRoomDetailsState().copy(roomTopic = RoomTopicState.Hidden), + aRoomDetailsState().copy(roomTopic = RoomTopicState.CanAddTopic), aRoomDetailsState().copy(isEncrypted = false), aRoomDetailsState().copy(roomAlias = null), aRoomDetailsState().copy(memberCount = Async.Failure(Throwable())), aDmRoomDetailsState().copy(roomName = "Daniel"), aDmRoomDetailsState(isDmMemberIgnored = true).copy(roomName = "Daniel"), aRoomDetailsState().copy(canInvite = true), + aRoomDetailsState().copy(canEdit = true), // Add other state here ) } @@ -64,14 +66,17 @@ fun aRoomDetailsState() = RoomDetailsState( roomName = "Marketing", roomAlias = "#marketing:domain.com", roomAvatarUrl = null, - roomTopic = "Welcome to #marketing, home of the Marketing team " + - "|| WIKI PAGE: https://domain.org/wiki/Marketing " + - "|| MAIL iki/Marketing " + - "|| MAI iki/Marketing " + - "|| MAI iki/Marketing...", + roomTopic = RoomTopicState.ExistingTopic( + "Welcome to #marketing, home of the Marketing team " + + "|| WIKI PAGE: https://domain.org/wiki/Marketing " + + "|| MAIL iki/Marketing " + + "|| MAI iki/Marketing " + + "|| MAI iki/Marketing..." + ), memberCount = Async.Success(32), isEncrypted = true, canInvite = false, + canEdit = false, roomType = RoomDetailsType.Room, roomMemberDetailsState = null, leaveRoomState = LeaveRoomState(), diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt index 78c26c4ae5..491c1eb22b 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsView.kt @@ -28,16 +28,25 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.outlined.Add import androidx.compose.material.icons.outlined.Lock import androidx.compose.material.icons.outlined.Person import androidx.compose.material.icons.outlined.PersonAddAlt import androidx.compose.material.icons.outlined.Share +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.vector.ImageVector @@ -63,24 +72,26 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.preview.LargeHeightPreview import io.element.android.libraries.designsystem.theme.LocalColors +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.IconButton import io.element.android.libraries.designsystem.theme.components.Scaffold import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.ui.strings.R as StringR -@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) +@OptIn(ExperimentalLayoutApi::class) @Composable fun RoomDetailsView( state: RoomDetailsState, goBack: () -> Unit, + onActionClicked: (RoomDetailsAction) -> Unit, onShareRoom: () -> Unit, onShareMember: (RoomMember) -> Unit, openRoomMemberList: () -> Unit, invitePeople: () -> Unit, modifier: Modifier = Modifier, ) { - fun onShareMember() { onShareMember((state.roomType as RoomDetailsType.Dm).roomMember) } @@ -88,13 +99,18 @@ fun RoomDetailsView( Scaffold( modifier = modifier, topBar = { - TopAppBar(title = { }, navigationIcon = { BackButton(onClick = goBack) }) + RoomDetailsTopBar( + goBack = goBack, + showEdit = state.canEdit, + onActionClicked = onActionClicked + ) }, ) { padding -> - Column(modifier = Modifier - .padding(padding) - .consumeWindowInsets(padding) - .verticalScroll(rememberScrollState()) + Column( + modifier = Modifier + .padding(padding) + .verticalScroll(rememberScrollState()) + .consumeWindowInsets(padding) ) { LeaveRoomView(state = state.leaveRoomState) @@ -108,6 +124,7 @@ fun RoomDetailsView( ) MainActionsSection(onShareRoom = onShareRoom) } + is RoomDetailsType.Dm -> { val member = state.roomType.roomMember RoomMemberHeaderSection( @@ -120,8 +137,11 @@ fun RoomDetailsView( } Spacer(Modifier.height(26.dp)) - if (state.roomTopic != null) { - TopicSection(roomTopic = state.roomTopic) + if (state.roomTopic !is RoomTopicState.Hidden) { + TopicSection( + roomTopic = state.roomTopic, + onActionClicked = onActionClicked, + ) } if (state.roomType is RoomDetailsType.Room) { @@ -129,10 +149,14 @@ fun RoomDetailsView( MembersSection( memberCount = memberCount, isLoading = state.memberCount.isLoading(), - showInvite = state.canInvite, openRoomMemberList = openRoomMemberList, - invitePeople = invitePeople, ) + + if (state.canInvite) { + InviteSection( + invitePeople = invitePeople + ) + } } if (state.isEncrypted) { @@ -152,6 +176,45 @@ fun RoomDetailsView( } } +@OptIn(ExperimentalMaterial3Api::class) +@Composable +internal fun RoomDetailsTopBar( + goBack: () -> Unit, + onActionClicked: (RoomDetailsAction) -> Unit, + showEdit: Boolean, + modifier: Modifier = Modifier, +) { + var showMenu by remember { mutableStateOf(false) } + + TopAppBar( + modifier = modifier, + title = { }, + navigationIcon = { BackButton(onClick = goBack) }, + actions = { + if (showEdit) { + IconButton(onClick = { showMenu = !showMenu }) { + Icon(Icons.Default.MoreVert, "") + } + DropdownMenu( + modifier = Modifier.widthIn(200.dp), + expanded = showMenu, + onDismissRequest = { showMenu = false }, + ) { + DropdownMenuItem( + text = { Text(stringResource(id = StringR.string.action_edit)) }, + onClick = { + // Explicitly close the menu before handling the action, as otherwise it stays open during the + // transition and renders really badly. + showMenu = false + onActionClicked(RoomDetailsAction.Edit) + }, + ) + } + } + }, + ) +} + @Composable internal fun MainActionsSection(onShareRoom: () -> Unit, modifier: Modifier = Modifier) { Row(modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { @@ -185,14 +248,26 @@ internal fun RoomHeaderSection( } @Composable -internal fun TopicSection(roomTopic: String, modifier: Modifier = Modifier) { +internal fun TopicSection( + roomTopic: RoomTopicState, + onActionClicked: (RoomDetailsAction) -> Unit, + modifier: Modifier = Modifier +) { PreferenceCategory(title = stringResource(StringR.string.common_topic), modifier = modifier) { - Text( - roomTopic, - modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 12.dp), - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.tertiary - ) + if (roomTopic is RoomTopicState.CanAddTopic) { + PreferenceText( + title = stringResource(R.string.screen_room_details_add_topic_title), + icon = Icons.Outlined.Add, + onClick = { onActionClicked(RoomDetailsAction.AddTopic) }, + ) + } else if (roomTopic is RoomTopicState.ExistingTopic) { + Text( + roomTopic.topic, + modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 12.dp), + style = MaterialTheme.typography.bodySmall, + color = MaterialTheme.colorScheme.tertiary + ) + } } } @@ -200,8 +275,6 @@ internal fun TopicSection(roomTopic: String, modifier: Modifier = Modifier) { internal fun MembersSection( memberCount: Int?, isLoading: Boolean, - showInvite: Boolean, - invitePeople: () -> Unit, openRoomMemberList: () -> Unit, modifier: Modifier = Modifier, ) { @@ -213,13 +286,20 @@ internal fun MembersSection( onClick = openRoomMemberList, loadingCurrentValue = isLoading, ) - if (showInvite) { - PreferenceText( - title = stringResource(R.string.screen_room_details_invite_people_title), - icon = Icons.Outlined.PersonAddAlt, - onClick = invitePeople, - ) - } + } +} + +@Composable +internal fun InviteSection( + invitePeople: () -> Unit, + modifier: Modifier = Modifier, +) { + PreferenceCategory(modifier = modifier) { + PreferenceText( + title = stringResource(R.string.screen_room_details_invite_people_title), + icon = Icons.Outlined.PersonAddAlt, + onClick = invitePeople, + ) } } @@ -261,6 +341,7 @@ private fun ContentToPreview(state: RoomDetailsState) { RoomDetailsView( state = state, goBack = {}, + onActionClicked = {}, onShareRoom = {}, onShareMember = {}, openRoomMemberList = {}, diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditEvents.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditEvents.kt new file mode 100644 index 0000000000..b4bc348b8a --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditEvents.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.roomdetails.impl.edit + +import io.element.android.libraries.matrix.ui.media.AvatarAction + +sealed interface RoomDetailsEditEvents { + data class HandleAvatarAction(val action: AvatarAction) : RoomDetailsEditEvents + data class UpdateRoomName(val name: String) : RoomDetailsEditEvents + data class UpdateRoomTopic(val topic: String) : RoomDetailsEditEvents + object Save : RoomDetailsEditEvents + object CancelSaveChanges : RoomDetailsEditEvents +} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt new file mode 100644 index 0000000000..af05b547b2 --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditNode.kt @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.roomdetails.impl.edit + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import com.bumble.appyx.core.modality.BuildContext +import com.bumble.appyx.core.node.Node +import com.bumble.appyx.core.plugin.Plugin +import dagger.assisted.Assisted +import dagger.assisted.AssistedInject +import io.element.android.anvilannotations.ContributesNode +import io.element.android.libraries.di.RoomScope + +@ContributesNode(RoomScope::class) +class RoomDetailsEditNode @AssistedInject constructor( + @Assisted buildContext: BuildContext, + @Assisted plugins: List, + private val presenter: RoomDetailsEditPresenter, +) : Node(buildContext, plugins = plugins) { + + @Composable + override fun View(modifier: Modifier) { + val state = presenter.present() + RoomDetailsEditView( + state = state, + onBackPressed = ::navigateUp, + onRoomEdited = ::navigateUp, + modifier = modifier, + ) + } +} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt new file mode 100644 index 0000000000..a6726f9105 --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditPresenter.kt @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.roomdetails.impl.edit + +import android.net.Uri +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import androidx.core.net.toUri +import io.element.android.libraries.matrix.ui.media.AvatarAction +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.architecture.Presenter +import io.element.android.libraries.architecture.execute +import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.StateEventType +import io.element.android.libraries.mediapickers.api.PickerProvider +import io.element.android.libraries.mediaupload.api.MediaPreProcessor +import io.element.android.libraries.mediaupload.api.MediaUploadInfo +import kotlinx.collections.immutable.toImmutableList +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import javax.inject.Inject + +class RoomDetailsEditPresenter @Inject constructor( + private val room: MatrixRoom, + private val mediaPickerProvider: PickerProvider, + private val mediaPreProcessor: MediaPreProcessor, +) : Presenter { + + @Composable + override fun present(): RoomDetailsEditState { + val roomSyncUpdateFlow = room.syncUpdateFlow().collectAsState(0L) + + // Since there is no way to obtain the new avatar uri after uploading a new avatar, + // just erase the local value when the room field has changed + var roomAvatarUri by rememberSaveable(room.avatarUrl) { mutableStateOf(room.avatarUrl?.toUri()) } + + var roomName by rememberSaveable { mutableStateOf((room.name ?: room.displayName).trim()) } + var roomTopic by rememberSaveable { mutableStateOf(room.topic?.trim()) } + + val saveButtonEnabled by remember( + roomSyncUpdateFlow.value, + roomName, + roomTopic, + roomAvatarUri, + ) { + derivedStateOf { + roomAvatarUri?.toString()?.trim() != room.avatarUrl?.toUri()?.toString()?.trim() + || roomName.trim() != (room.name ?: room.displayName).trim() + || roomTopic.orEmpty().trim() != room.topic.orEmpty().trim() + } + } + + var canChangeName by remember { mutableStateOf(false) } + var canChangeTopic by remember { mutableStateOf(false) } + var canChangeAvatar by remember { mutableStateOf(false) } + + LaunchedEffect(Unit) { + canChangeName = room.canSendStateEvent(StateEventType.ROOM_NAME).getOrElse { false } + canChangeTopic = room.canSendStateEvent(StateEventType.ROOM_TOPIC).getOrElse { false } + canChangeAvatar = room.canSendStateEvent(StateEventType.ROOM_AVATAR).getOrElse { false } + } + + val cameraPhotoPicker = mediaPickerProvider.registerCameraPhotoPicker( + onResult = { uri -> if (uri != null) roomAvatarUri = uri } + ) + val galleryImagePicker = mediaPickerProvider.registerGalleryImagePicker( + onResult = { uri -> if (uri != null) roomAvatarUri = uri } + ) + + val avatarActions by remember(roomAvatarUri) { + derivedStateOf { + listOfNotNull( + AvatarAction.TakePhoto, + AvatarAction.ChoosePhoto, + AvatarAction.Remove.takeIf { roomAvatarUri != null }, + ).toImmutableList() + } + } + + val saveAction: MutableState> = remember { mutableStateOf(Async.Uninitialized) } + val localCoroutineScope = rememberCoroutineScope() + fun handleEvents(event: RoomDetailsEditEvents) { + when (event) { + is RoomDetailsEditEvents.Save -> localCoroutineScope.saveChanges(roomName, roomTopic, roomAvatarUri, saveAction) + is RoomDetailsEditEvents.HandleAvatarAction -> { + when (event.action) { + AvatarAction.ChoosePhoto -> galleryImagePicker.launch() + AvatarAction.TakePhoto -> cameraPhotoPicker.launch() + AvatarAction.Remove -> roomAvatarUri = null + } + } + + is RoomDetailsEditEvents.UpdateRoomName -> roomName = event.name + is RoomDetailsEditEvents.UpdateRoomTopic -> roomTopic = event.topic.takeUnless { it.isEmpty() } + RoomDetailsEditEvents.CancelSaveChanges -> saveAction.value = Async.Uninitialized + } + } + + return RoomDetailsEditState( + roomId = room.roomId.value, + roomName = roomName, + canChangeName = canChangeName, + roomTopic = roomTopic.orEmpty(), + canChangeTopic = canChangeTopic, + roomAvatarUrl = roomAvatarUri, + canChangeAvatar = canChangeAvatar, + avatarActions = avatarActions, + saveButtonEnabled = saveButtonEnabled, + saveAction = saveAction.value, + eventSink = ::handleEvents, + ) + } + + private fun CoroutineScope.saveChanges(name: String, topic: String?, avatarUri: Uri?, action: MutableState>) = launch { + val results = mutableListOf>() + suspend { + if (topic.orEmpty().trim() != room.topic.orEmpty().trim()) { + results.add(room.setTopic(topic.orEmpty())) + } + if (name.isNotEmpty() && name.trim() != room.name.orEmpty().trim()) { + results.add(room.setName(name)) + } + if (avatarUri?.toString()?.trim() != room.avatarUrl?.trim()) { + results.add(updateAvatar(avatarUri)) + } + if (results.all { it.isSuccess }) Unit else results.first { it.isFailure }.getOrThrow() + }.execute(action) + } + + private suspend fun updateAvatar(avatarUri: Uri?): Result { + return runCatching { + val result = if (avatarUri != null) { + val preprocessed = mediaPreProcessor.process(avatarUri, MimeTypes.Jpeg, compressIfPossible = false).getOrThrow() as? MediaUploadInfo.Image + val byteArray = preprocessed?.file?.readBytes() + byteArray?.let { room.updateAvatar(MimeTypes.Jpeg, it) } ?: error("Could not process the given uri ($avatarUri)") + } else { + room.removeAvatar() + } + result.getOrThrow() + } + } +} diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt new file mode 100644 index 0000000000..ceb87b6f27 --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditState.kt @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.roomdetails.impl.edit + +import android.net.Uri +import io.element.android.libraries.matrix.ui.media.AvatarAction +import io.element.android.libraries.architecture.Async +import kotlinx.collections.immutable.ImmutableList + +data class RoomDetailsEditState( + val roomId: String, + val roomName: String, + val canChangeName: Boolean, + val roomTopic: String, + val canChangeTopic: Boolean, + val roomAvatarUrl: Uri?, + val canChangeAvatar: Boolean, + val avatarActions: ImmutableList, + val saveButtonEnabled: Boolean, + val saveAction: Async, + val eventSink: (RoomDetailsEditEvents) -> Unit +) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt new file mode 100644 index 0000000000..62a363aeac --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditStateProvider.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.roomdetails.impl.edit + +import android.net.Uri +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.libraries.architecture.Async +import kotlinx.collections.immutable.persistentListOf + +open class RoomDetailsEditStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + aRoomDetailsEditState(), + aRoomDetailsEditState().copy(roomTopic = ""), + aRoomDetailsEditState().copy(roomAvatarUrl = Uri.EMPTY), + aRoomDetailsEditState().copy(canChangeName = true, canChangeTopic = false, canChangeAvatar = true, saveButtonEnabled = false), + aRoomDetailsEditState().copy(canChangeName = false, canChangeTopic = true, canChangeAvatar = false, saveButtonEnabled = false), + aRoomDetailsEditState().copy(saveAction = Async.Loading()), + aRoomDetailsEditState().copy(saveAction = Async.Failure(Throwable("Whelp"))) + ) +} + +fun aRoomDetailsEditState() = RoomDetailsEditState( + roomId = "a room id", + roomName = "Marketing", + canChangeName = true, + roomTopic = "a room topic that is quite long so should wrap onto multiple lines", + canChangeTopic = true, + roomAvatarUrl = null, + canChangeAvatar = true, + avatarActions = persistentListOf(), + saveButtonEnabled = true, + saveAction = Async.Uninitialized, + eventSink = {} +) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt new file mode 100644 index 0000000000..414eaf9831 --- /dev/null +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/edit/RoomDetailsEditView.kt @@ -0,0 +1,306 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class) + +package io.element.android.features.roomdetails.impl.edit + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.ModalBottomSheetValue +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.AddAPhoto +import androidx.compose.material.rememberModalBottomSheetState +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusManager +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import io.element.android.features.roomdetails.impl.R +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.designsystem.components.LabelledTextField +import io.element.android.libraries.designsystem.components.ProgressDialog +import io.element.android.libraries.designsystem.components.avatar.Avatar +import io.element.android.libraries.designsystem.components.avatar.AvatarData +import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.designsystem.components.button.BackButton +import io.element.android.libraries.designsystem.components.dialogs.ErrorDialog +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight +import io.element.android.libraries.designsystem.theme.LocalColors +import io.element.android.libraries.designsystem.theme.components.CenterAlignedTopAppBar +import io.element.android.libraries.designsystem.theme.components.Icon +import io.element.android.libraries.designsystem.theme.components.Scaffold +import io.element.android.libraries.designsystem.theme.components.Text +import io.element.android.libraries.designsystem.theme.components.TextButton +import io.element.android.libraries.matrix.ui.components.AvatarActionBottomSheet +import io.element.android.libraries.matrix.ui.components.UnsavedAvatar +import kotlinx.coroutines.launch +import io.element.android.libraries.ui.strings.R as StringR + +@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) +@Composable +fun RoomDetailsEditView( + state: RoomDetailsEditState, + onBackPressed: () -> Unit, + onRoomEdited: () -> Unit, + modifier: Modifier = Modifier, +) { + val coroutineScope = rememberCoroutineScope() + val focusManager = LocalFocusManager.current + val itemActionsBottomSheetState = rememberModalBottomSheetState( + initialValue = ModalBottomSheetValue.Hidden, + ) + + fun onAvatarClicked() { + focusManager.clearFocus() + coroutineScope.launch { + itemActionsBottomSheetState.show() + } + } + + Scaffold( + modifier = modifier.clearFocusOnTap(focusManager), + topBar = { + CenterAlignedTopAppBar( + title = { + Text( + text = stringResource(id = R.string.screen_room_details_edit_room_title), + fontSize = 16.sp, + fontWeight = FontWeight.SemiBold, + ) + }, + navigationIcon = { BackButton(onClick = onBackPressed) }, + actions = { + TextButton( + enabled = state.saveButtonEnabled, + onClick = { + focusManager.clearFocus() + state.eventSink(RoomDetailsEditEvents.Save) + }, + ) { + Text( + text = stringResource(StringR.string.action_save), + fontSize = 16.sp, + ) + } + } + ) + }, + ) { padding -> + Column( + modifier = Modifier + .padding(padding) + .padding(horizontal = 16.dp) + .navigationBarsPadding() + .imePadding() + .verticalScroll(rememberScrollState()) + ) { + Spacer(modifier = Modifier.height(24.dp)) + EditableAvatarView(state, ::onAvatarClicked) + Spacer(modifier = Modifier.height(60.dp)) + + if (state.canChangeName) { + LabelledTextField( + label = stringResource(id = R.string.screen_room_details_room_name_label), + value = state.roomName, + placeholder = stringResource(id = R.string.screen_room_details_room_name_placeholder), + singleLine = true, + onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomName(it)) }, + ) + } else { + LabelledReadOnlyField( + title = stringResource(R.string.screen_room_details_room_name_label), + value = state.roomName + ) + } + + Spacer(modifier = Modifier.height(28.dp)) + + if (state.canChangeTopic) { + LabelledTextField( + label = stringResource(id = StringR.string.common_topic), + value = state.roomTopic, + placeholder = stringResource(id = R.string.screen_room_details_topic_placeholder), + maxLines = 10, + onValueChange = { state.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(it)) }, + ) + } else { + LabelledReadOnlyField( + title = stringResource(R.string.screen_room_details_topic_title), + value = state.roomTopic + ) + } + } + } + + AvatarActionBottomSheet( + actions = state.avatarActions, + modalBottomSheetState = itemActionsBottomSheetState, + onActionSelected = { state.eventSink(RoomDetailsEditEvents.HandleAvatarAction(it)) } + ) + + when (state.saveAction) { + is Async.Loading -> { + ProgressDialog(text = stringResource(R.string.screen_room_details_updating_room)) + } + + is Async.Failure -> { + ErrorDialog( + content = stringResource(R.string.screen_room_details_edition_error), + onDismiss = { state.eventSink(RoomDetailsEditEvents.CancelSaveChanges) }, + ) + } + + is Async.Success -> { + LaunchedEffect(state.saveAction) { + onRoomEdited() + } + } + + else -> Unit + } +} + +@Composable +private fun EditableAvatarView( + state: RoomDetailsEditState, + onAvatarClicked: () -> Unit, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { + Box( + modifier = Modifier + .size(70.dp) + .clickable(onClick = onAvatarClicked, enabled = state.canChangeAvatar) + ) { + // TODO this might be able to be simplified into a single component once send/receive media is done + when (state.roomAvatarUrl?.scheme) { + null, "mxc" -> { + Avatar( + avatarData = AvatarData(state.roomId, state.roomName, state.roomAvatarUrl?.toString(), size = AvatarSize.HUGE), + modifier = Modifier.fillMaxSize(), + ) + } + + else -> { + UnsavedAvatar( + avatarUri = state.roomAvatarUrl, + modifier = Modifier.fillMaxSize(), + ) + } + } + + if (state.canChangeAvatar) { + Box( + modifier = Modifier + .align(Alignment.BottomEnd) + .clip(CircleShape) + .background(LocalColors.current.gray1400) + .size(24.dp), + contentAlignment = Alignment.Center, + ) { + Icon( + modifier = Modifier.size(16.dp), + imageVector = Icons.Outlined.AddAPhoto, + contentDescription = "", + tint = MaterialTheme.colorScheme.onPrimary, + ) + } + } + } + } +} + +@Composable +private fun LabelledReadOnlyField( + title: String, + value: String, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier, + verticalArrangement = Arrangement.spacedBy(8.dp), + ) { + Text( + modifier = Modifier.padding(horizontal = 16.dp), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, + text = title, + ) + + Text( + modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 8.dp, bottom = 12.dp), + style = MaterialTheme.typography.bodyLarge, + color = MaterialTheme.colorScheme.primary, + text = value, + ) + } +} + +private fun Modifier.clearFocusOnTap(focusManager: FocusManager): Modifier = + pointerInput(Unit) { + detectTapGestures(onTap = { + focusManager.clearFocus() + }) + } + +@Preview +@Composable +fun RoomDetailsEditViewLightPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = + ElementPreviewLight { ContentToPreview(state) } + +@Preview +@Composable +fun RoomDetailsEditViewDarkPreview(@PreviewParameter(RoomDetailsEditStateProvider::class) state: RoomDetailsEditState) = + ElementPreviewDark { ContentToPreview(state) } + +@Composable +private fun ContentToPreview(state: RoomDetailsEditState) { + RoomDetailsEditView( + state = state, + onBackPressed = {}, + onRoomEdited = {}, + ) +} diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index ec907c6078..4f45bb6b49 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -12,7 +12,10 @@ "Unable to update room" "Messages are secured with locks. Only you and the recipients have the unique keys to unlock them." "Message encryption enabled" + "Room name" + "e.g. Product Sprint" "Share room" + "What is this room about?" "Updating room…" "Pending" "Room members" @@ -22,7 +25,7 @@ "Unblock" "On unblocking the user, you will be able to see all messages by them again." "Unblock user" - "Invite people" + "Invite friends to Element" "Leave room" "People" "Security" diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt index 0c950071ca..bcaa824104 100644 --- a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/RoomDetailsPresenterTests.kt @@ -19,10 +19,11 @@ package io.element.android.features.roomdetails import app.cash.molecule.RecompositionClock import app.cash.molecule.moleculeFlow import app.cash.turbine.test -import com.google.common.truth.Truth +import com.google.common.truth.Truth.assertThat import io.element.android.features.leaveroom.fake.LeaveRoomPresenterFake import io.element.android.features.roomdetails.impl.RoomDetailsPresenter import io.element.android.features.roomdetails.impl.RoomDetailsType +import io.element.android.features.roomdetails.impl.RoomTopicState import io.element.android.features.roomdetails.impl.members.aRoomMember import io.element.android.features.roomdetails.impl.members.details.RoomMemberDetailsPresenter import io.element.android.libraries.architecture.Async @@ -31,8 +32,8 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState -import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -40,7 +41,6 @@ import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.A_USER_ID_2 import io.element.android.libraries.matrix.test.FakeMatrixClient import io.element.android.libraries.matrix.test.room.FakeMatrixRoom -import io.element.android.tests.testutils.testCoroutineDispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import org.junit.Test @@ -48,9 +48,6 @@ import org.junit.Test @ExperimentalCoroutinesApi class RoomDetailsPresenterTests { - private val roomMembershipObserver = RoomMembershipObserver() - private val testCoroutineDispatchers = testCoroutineDispatchers() - private fun aRoomDetailsPresenter(room: MatrixRoom): RoomDetailsPresenter { val roomMemberDetailsPresenterFactory = object : RoomMemberDetailsPresenter.Factory { override fun create(roomMemberId: UserId): RoomMemberDetailsPresenter { @@ -68,12 +65,12 @@ class RoomDetailsPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.roomId).isEqualTo(room.roomId.value) - Truth.assertThat(initialState.roomName).isEqualTo(room.name) - Truth.assertThat(initialState.roomAvatarUrl).isEqualTo(room.avatarUrl) - Truth.assertThat(initialState.roomTopic).isEqualTo(room.topic) - Truth.assertThat(initialState.memberCount).isEqualTo(Async.Uninitialized) - Truth.assertThat(initialState.isEncrypted).isEqualTo(room.isEncrypted) + assertThat(initialState.roomId).isEqualTo(room.roomId.value) + assertThat(initialState.roomName).isEqualTo(room.name) + assertThat(initialState.roomAvatarUrl).isEqualTo(room.avatarUrl) + assertThat(initialState.roomTopic).isEqualTo(RoomTopicState.ExistingTopic(room.topic!!)) + assertThat(initialState.memberCount).isEqualTo(Async.Uninitialized) + assertThat(initialState.isEncrypted).isEqualTo(room.isEncrypted) cancelAndIgnoreRemainingEvents() } @@ -93,22 +90,22 @@ class RoomDetailsPresenterTests { }.test { room.givenRoomMembersState(MatrixRoomMembersState.Unknown) val initialState = awaitItem() - Truth.assertThat(initialState.memberCount).isEqualTo(Async.Uninitialized) + assertThat(initialState.memberCount).isEqualTo(Async.Uninitialized) skipItems(1) room.givenRoomMembersState(MatrixRoomMembersState.Pending(null)) val loadingState = awaitItem() - Truth.assertThat(loadingState.memberCount).isEqualTo(Async.Loading(null)) + assertThat(loadingState.memberCount).isEqualTo(Async.Loading(null)) room.givenRoomMembersState(MatrixRoomMembersState.Error(error)) skipItems(1) val failureState = awaitItem() - Truth.assertThat(failureState.memberCount).isEqualTo(Async.Failure(error, null)) + assertThat(failureState.memberCount).isEqualTo(Async.Failure(error, null)) room.givenRoomMembersState(MatrixRoomMembersState.Ready(roomMembers)) skipItems(1) val successState = awaitItem() - Truth.assertThat(successState.memberCount).isEqualTo(Async.Success(1)) + assertThat(successState.memberCount).isEqualTo(Async.Success(1)) cancelAndIgnoreRemainingEvents() } @@ -122,7 +119,7 @@ class RoomDetailsPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.roomName).isEqualTo(room.displayName) + assertThat(initialState.roomName).isEqualTo(room.displayName) cancelAndIgnoreRemainingEvents() } @@ -144,7 +141,7 @@ class RoomDetailsPresenterTests { presenter.present() }.test { val initialState = awaitItem() - Truth.assertThat(initialState.roomType).isEqualTo(RoomDetailsType.Dm(otherRoomMember)) + assertThat(initialState.roomType).isEqualTo(RoomDetailsType.Dm(otherRoomMember)) cancelAndIgnoreRemainingEvents() } @@ -160,9 +157,9 @@ class RoomDetailsPresenterTests { presenter.present() }.test { // Initially false - Truth.assertThat(awaitItem().canInvite).isFalse() + assertThat(awaitItem().canInvite).isFalse() // Then the asynchronous check completes and it becomes true - Truth.assertThat(awaitItem().canInvite).isTrue() + assertThat(awaitItem().canInvite).isTrue() cancelAndIgnoreRemainingEvents() } @@ -177,7 +174,7 @@ class RoomDetailsPresenterTests { moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { - Truth.assertThat(awaitItem().canInvite).isFalse() + assertThat(awaitItem().canInvite).isFalse() } } @@ -190,7 +187,103 @@ class RoomDetailsPresenterTests { moleculeFlow(RecompositionClock.Immediate) { presenter.present() }.test { - Truth.assertThat(awaitItem().canInvite).isFalse() + assertThat(awaitItem().canInvite).isFalse() + } + } + + @Test + fun `present - initial state when user can edit one attribute`() = runTest { + val room = aMatrixRoom().apply { + givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) + givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false)) + givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Whelp"))) + givenCanInviteResult(Result.success(false)) + } + val presenter = aRoomDetailsPresenter(room) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + // Initially false + assertThat(awaitItem().canEdit).isFalse() + // Then the asynchronous check completes and it becomes true + assertThat(awaitItem().canEdit).isTrue() + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - initial state when user can edit all attributes`() = runTest { + val room = aMatrixRoom().apply { + givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) + givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true)) + givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true)) + givenCanInviteResult(Result.success(false)) + } + val presenter = aRoomDetailsPresenter(room) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + // Initially false + assertThat(awaitItem().canEdit).isFalse() + // Then the asynchronous check completes and it becomes true + assertThat(awaitItem().canEdit).isTrue() + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - initial state when user can edit no attributes`() = runTest { + val room = aMatrixRoom().apply { + givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false)) + givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false)) + givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false)) + givenCanInviteResult(Result.success(false)) + } + val presenter = aRoomDetailsPresenter(room) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + // Initially false, and no further events + assertThat(awaitItem().canEdit).isFalse() + } + } + + @Test + fun `present - topic state is hidden when no topic and user has no permission`() = runTest { + val room = aMatrixRoom(topic = null).apply { + givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(false)) + givenCanInviteResult(Result.success(false)) + } + + val presenter = aRoomDetailsPresenter(room) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + // The initial state is "hidden" and no further state changes happen + assertThat(awaitItem().roomTopic).isEqualTo(RoomTopicState.Hidden) + } + } + + @Test + fun `present - topic state is 'can add topic' when no topic and user has permission`() = runTest { + val room = aMatrixRoom(topic = null).apply { + givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) + givenCanInviteResult(Result.success(false)) + } + + val presenter = aRoomDetailsPresenter(room) + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + // Ignore the initial state + skipItems(1) + + // When the async permission check finishes, the topic state will be updated + assertThat(awaitItem().roomTopic).isEqualTo(RoomTopicState.CanAddTopic) + + cancelAndIgnoreRemainingEvents() } } } diff --git a/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt new file mode 100644 index 0000000000..adbec76ae7 --- /dev/null +++ b/features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/edit/RoomDetailsEditPresenterTest.kt @@ -0,0 +1,618 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.roomdetails.edit + +import android.net.Uri +import app.cash.molecule.RecompositionClock +import app.cash.molecule.moleculeFlow +import app.cash.turbine.test +import com.google.common.truth.Truth.assertThat +import io.element.android.libraries.matrix.ui.media.AvatarAction +import io.element.android.features.roomdetails.aMatrixRoom +import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditEvents +import io.element.android.features.roomdetails.impl.edit.RoomDetailsEditPresenter +import io.element.android.libraries.architecture.Async +import io.element.android.libraries.matrix.api.room.MatrixRoom +import io.element.android.libraries.matrix.api.room.StateEventType +import io.element.android.libraries.matrix.test.AN_AVATAR_URL +import io.element.android.libraries.mediapickers.test.FakePickerProvider +import io.element.android.libraries.mediaupload.api.MediaUploadInfo +import io.element.android.libraries.mediaupload.api.ThumbnailProcessingInfo +import io.element.android.libraries.mediaupload.test.FakeMediaPreProcessor +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkAll +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Test +import java.io.File + +@ExperimentalCoroutinesApi +class RoomDetailsEditPresenterTest { + + private lateinit var fakePickerProvider: FakePickerProvider + private lateinit var fakeMediaPreProcessor: FakeMediaPreProcessor + + private val roomAvatarUri: Uri = mockk() + private val anotherAvatarUri: Uri = mockk() + + private val fakeFileContents = ByteArray(2) + + @Before + fun setup() { + fakePickerProvider = FakePickerProvider() + fakeMediaPreProcessor = FakeMediaPreProcessor() + mockkStatic(Uri::class) + + every { Uri.parse(AN_AVATAR_URL) } returns roomAvatarUri + every { Uri.parse(ANOTHER_AVATAR_URL) } returns anotherAvatarUri + } + + @After + fun tearDown() { + unmockkAll() + } + + private fun aRoomDetailsEditPresenter(room: MatrixRoom): RoomDetailsEditPresenter { + return RoomDetailsEditPresenter( + room = room, + mediaPickerProvider = fakePickerProvider, + mediaPreProcessor = fakeMediaPreProcessor, + ) + } + + @Test + fun `present - initial state is created from room info`() = runTest { + val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL) + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.roomId).isEqualTo(room.roomId.value) + assertThat(initialState.roomName).isEqualTo(room.name) + assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) + assertThat(initialState.roomTopic).isEqualTo(room.topic.orEmpty()) + assertThat(initialState.avatarActions).containsExactly( + AvatarAction.ChoosePhoto, + AvatarAction.TakePhoto, + AvatarAction.Remove + ) + assertThat(initialState.saveButtonEnabled).isEqualTo(false) + assertThat(initialState.saveAction).isInstanceOf(Async.Uninitialized::class.java) + } + } + + @Test + fun `present - sets canChangeName if user has permission`() = runTest { + val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply { + givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(true)) + givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(false)) + givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops"))) } + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + // Initially false + val initialState = awaitItem() + assertThat(initialState.canChangeName).isFalse() + assertThat(initialState.canChangeAvatar).isFalse() + assertThat(initialState.canChangeTopic).isFalse() + + // When the asynchronous check completes, the single field we can edit is true + val settledState = awaitItem() + assertThat(settledState.canChangeName).isTrue() + assertThat(settledState.canChangeAvatar).isFalse() + assertThat(settledState.canChangeTopic).isFalse() + } + } + + @Test + fun `present - sets canChangeAvatar if user has permission`() = runTest { + val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply { + givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false)) + givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.success(true)) + givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.failure(Throwable("Oops"))) + } + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + // Initially false + val initialState = awaitItem() + assertThat(initialState.canChangeName).isFalse() + assertThat(initialState.canChangeAvatar).isFalse() + assertThat(initialState.canChangeTopic).isFalse() + + // When the asynchronous check completes, the single field we can edit is true + val settledState = awaitItem() + assertThat(settledState.canChangeName).isFalse() + assertThat(settledState.canChangeAvatar).isTrue() + assertThat(settledState.canChangeTopic).isFalse() + } + } + + @Test + fun `present - sets canChangeTopic if user has permission`() = runTest { + val room = aMatrixRoom(avatarUrl = AN_AVATAR_URL).apply { + givenCanSendStateResult(StateEventType.ROOM_NAME, Result.success(false)) + givenCanSendStateResult(StateEventType.ROOM_AVATAR, Result.failure(Throwable("Oops"))) + givenCanSendStateResult(StateEventType.ROOM_TOPIC, Result.success(true)) + } + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + // Initially false + val initialState = awaitItem() + assertThat(initialState.canChangeName).isFalse() + assertThat(initialState.canChangeAvatar).isFalse() + assertThat(initialState.canChangeTopic).isFalse() + + // When the asynchronous check completes, the single field we can edit is true + val settledState = awaitItem() + assertThat(settledState.canChangeName).isFalse() + assertThat(settledState.canChangeAvatar).isFalse() + assertThat(settledState.canChangeTopic).isTrue() + } + } + + @Test + fun `present - updates state in response to changes`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.roomTopic).isEqualTo("My topic") + assertThat(initialState.roomName).isEqualTo("Name") + assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) + + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name II")) + awaitItem().apply { + assertThat(roomTopic).isEqualTo("My topic") + assertThat(roomName).isEqualTo("Name II") + assertThat(roomAvatarUrl).isEqualTo(roomAvatarUri) + } + + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name III")) + awaitItem().apply { + assertThat(roomTopic).isEqualTo("My topic") + assertThat(roomName).isEqualTo("Name III") + assertThat(roomAvatarUrl).isEqualTo(roomAvatarUri) + } + + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("Another topic")) + awaitItem().apply { + assertThat(roomTopic).isEqualTo("Another topic") + assertThat(roomName).isEqualTo("Name III") + assertThat(roomAvatarUrl).isEqualTo(roomAvatarUri) + } + + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) + awaitItem().apply { + assertThat(roomTopic).isEqualTo("Another topic") + assertThat(roomName).isEqualTo("Name III") + assertThat(roomAvatarUrl).isNull() + } + } + } + + @Test + fun `present - obtains avatar uris from gallery`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) + + fakePickerProvider.givenResult(anotherAvatarUri) + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) + + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + awaitItem().apply { + assertThat(roomAvatarUrl).isEqualTo(anotherAvatarUri) + } + } + } + + @Test + fun `present - obtains avatar uris from camera`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) + + fakePickerProvider.givenResult(anotherAvatarUri) + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.roomAvatarUrl).isEqualTo(roomAvatarUri) + + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.TakePhoto)) + awaitItem().apply { + assertThat(roomAvatarUrl).isEqualTo(anotherAvatarUri) + } + } + } + + @Test + fun `present - updates save button state`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) + + fakePickerProvider.givenResult(roomAvatarUri) + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.saveButtonEnabled).isEqualTo(false) + + // Once a change is made, the save button is enabled + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name II")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // If it's reverted then the save disables again + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + + // Make a change... + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("Another topic")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // Revert it... + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("My topic")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + + // Make a change... + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // Revert it... + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + } + } + + @Test + fun `present - updates save button state when initial values are null`() = runTest { + val room = aMatrixRoom(topic = null, name = null, displayName = "fallback", avatarUrl = null) + + fakePickerProvider.givenResult(roomAvatarUri) + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + assertThat(initialState.saveButtonEnabled).isEqualTo(false) + + // Once a change is made, the save button is enabled + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("Name II")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // If it's reverted then the save disables again + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("fallback")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + + // Make a change... + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("Another topic")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // Revert it... + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("")) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + + // Make a change... + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(true) + } + + // Revert it... + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) + awaitItem().apply { + assertThat(saveButtonEnabled).isEqualTo(false) + } + } + } + + @Test + fun `present - save changes room details if different`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("New name")) + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("New topic")) + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) + initialState.eventSink(RoomDetailsEditEvents.Save) + + assertThat(room.newName).isEqualTo("New name") + assertThat(room.newTopic).isEqualTo("New topic") + assertThat(room.newAvatarData).isNull() + assertThat(room.removedAvatar).isTrue() + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - save doesn't change room details if they're the same trimmed`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName(" Name ")) + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic(" My topic ")) + initialState.eventSink(RoomDetailsEditEvents.Save) + + assertThat(room.newName).isNull() + assertThat(room.newTopic).isNull() + assertThat(room.newAvatarData).isNull() + assertThat(room.removedAvatar).isFalse() + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - save doesn't change topic if it was unset and is now blank`() = runTest { + val room = aMatrixRoom(topic = null, name = "Name", avatarUrl = AN_AVATAR_URL) + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("")) + initialState.eventSink(RoomDetailsEditEvents.Save) + + assertThat(room.newName).isNull() + assertThat(room.newTopic).isNull() + assertThat(room.newAvatarData).isNull() + assertThat(room.removedAvatar).isFalse() + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - save doesn't change name if it's now empty`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomName("")) + initialState.eventSink(RoomDetailsEditEvents.Save) + + assertThat(room.newName).isNull() + assertThat(room.newTopic).isNull() + assertThat(room.newAvatarData).isNull() + assertThat(room.removedAvatar).isFalse() + + cancelAndIgnoreRemainingEvents() + } + } + + @Test + fun `present - save processes and sets avatar when processor returns successfully`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) + + givenPickerReturnsFile() + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + initialState.eventSink(RoomDetailsEditEvents.Save) + skipItems(2) + + assertThat(room.newName).isNull() + assertThat(room.newTopic).isNull() + assertThat(room.newAvatarData).isSameInstanceAs(fakeFileContents) + assertThat(room.removedAvatar).isFalse() + } + } + + @Test + fun `present - save does not set avatar data if processor fails`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL) + + fakePickerProvider.givenResult(anotherAvatarUri) + fakeMediaPreProcessor.givenResult(Result.failure(Throwable("Oh no"))) + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + initialState.eventSink(RoomDetailsEditEvents.Save) + skipItems(1) + + assertThat(room.newName).isNull() + assertThat(room.newTopic).isNull() + assertThat(room.newAvatarData).isNull() + assertThat(room.removedAvatar).isFalse() + + assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java) + } + } + + @Test + fun `present - sets save action to failure if name update fails`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL).apply { + givenSetNameResult(Result.failure(Throwable("!"))) + } + + saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomName("New name")) + } + + @Test + fun `present - sets save action to failure if topic update fails`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL).apply { + givenSetTopicResult(Result.failure(Throwable("!"))) + } + + saveAndAssertFailure(room, RoomDetailsEditEvents.UpdateRoomTopic("New topic")) + } + + @Test + fun `present - sets save action to failure if removing avatar fails`() = runTest { + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL).apply { + givenRemoveAvatarResult(Result.failure(Throwable("!"))) + } + + saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.Remove)) + } + + @Test + fun `present - sets save action to failure if setting avatar fails`() = runTest { + givenPickerReturnsFile() + + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL).apply { + givenUpdateAvatarResult(Result.failure(Throwable("!"))) + } + + saveAndAssertFailure(room, RoomDetailsEditEvents.HandleAvatarAction(AvatarAction.ChoosePhoto)) + } + + @Test + fun `present - CancelSaveChanges resets save action state`() = runTest { + givenPickerReturnsFile() + + val room = aMatrixRoom(topic = "My topic", name = "Name", avatarUrl = AN_AVATAR_URL).apply { + givenSetTopicResult(Result.failure(Throwable("!"))) + } + + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(RoomDetailsEditEvents.UpdateRoomTopic("foo")) + initialState.eventSink(RoomDetailsEditEvents.Save) + skipItems(1) + + assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java) + + initialState.eventSink(RoomDetailsEditEvents.CancelSaveChanges) + assertThat(awaitItem().saveAction).isInstanceOf(Async.Uninitialized::class.java) + } + } + + private suspend fun saveAndAssertFailure(room: MatrixRoom, event: RoomDetailsEditEvents) { + val presenter = aRoomDetailsEditPresenter(room) + + moleculeFlow(RecompositionClock.Immediate) { + presenter.present() + }.test { + val initialState = awaitItem() + + initialState.eventSink(event) + initialState.eventSink(RoomDetailsEditEvents.Save) + skipItems(1) + + assertThat(awaitItem().saveAction).isInstanceOf(Async.Failure::class.java) + } + } + + private fun givenPickerReturnsFile() { + mockkStatic(File::readBytes) + val processedFile: File = mockk { + every { readBytes() } returns fakeFileContents + } + + fakePickerProvider.givenResult(anotherAvatarUri) + fakeMediaPreProcessor.givenResult(Result.success(MediaUploadInfo.Image( + file = processedFile, + info = mockk(), + thumbnailInfo = ThumbnailProcessingInfo( + file = processedFile, + info = mockk(), + blurhash = "", + ) + ))) + } + + companion object { + private const val ANOTHER_AVATAR_URL = "example://camera/foo.jpg" + } + +} diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/Color.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/Color.kt index 50585f9dba..e61b2e66a3 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/Color.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/Color.kt @@ -79,3 +79,6 @@ val Compound_Gray_300_Light = Color(0xFFF0F2F5) val Compound_Gray_300_Dark = Color(0xFF1D1F24) val Compound_Gray_400_Light = Color(0xFFE1E6EC) val Compound_Gray_400_Dark = Color(0xFF26282D) + +val Gray_1400_Light = Color(0xFF1B1D22) +val Gray_1400_Dark = Color(0xFFEBEEF2) diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/LabelledTextField.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt similarity index 77% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/LabelledTextField.kt rename to libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt index 2a28c56253..0f0ada5497 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/LabelledTextField.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/components/LabelledTextField.kt @@ -14,18 +14,17 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.components +package io.element.android.libraries.designsystem.components import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import io.element.android.features.createroom.impl.R import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.Text @@ -36,8 +35,9 @@ fun LabelledTextField( label: String, value: String, modifier: Modifier = Modifier, - placeholder: String = "", - maxLines: Int = 1, + placeholder: String? = null, + maxLines: Int = Int.MAX_VALUE, + singleLine: Boolean = false, onValueChange: (String) -> Unit = {}, ) { Column( @@ -46,15 +46,17 @@ fun LabelledTextField( ) { Text( modifier = Modifier.padding(horizontal = 16.dp), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.primary, text = label ) TextField( modifier = Modifier.fillMaxWidth(), value = value, - placeholder = { Text(placeholder) }, + placeholder = placeholder?.let { { Text(placeholder) } }, onValueChange = onValueChange, - singleLine = maxLines == 1, + singleLine = singleLine, maxLines = maxLines, ) } @@ -72,14 +74,14 @@ fun LabelledTextFieldDarkPreview() = ElementPreviewDark { ContentToPreview() } private fun ContentToPreview() { Column { LabelledTextField( - label = stringResource(R.string.screen_create_room_room_name_label), + label = "Room name", value = "", - placeholder = stringResource(R.string.screen_create_room_room_name_placeholder), + placeholder = "e.g. Product Sprint", ) LabelledTextField( - label = stringResource(R.string.screen_create_room_room_name_label), + label = "Room name", value = "a room name", - placeholder = stringResource(R.string.screen_create_room_room_name_placeholder), + placeholder = "e.g. Product Sprint", ) } } diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsDark.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsDark.kt index 166a2f3f79..56a287a9e9 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsDark.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsDark.kt @@ -25,6 +25,7 @@ import io.element.android.libraries.designsystem.Black_800 import io.element.android.libraries.designsystem.Black_950 import io.element.android.libraries.designsystem.Compound_Gray_300_Dark import io.element.android.libraries.designsystem.DarkGrey +import io.element.android.libraries.designsystem.Gray_1400_Dark import io.element.android.libraries.designsystem.Gray_300 import io.element.android.libraries.designsystem.Gray_400 import io.element.android.libraries.designsystem.Compound_Gray_400_Dark @@ -42,6 +43,7 @@ fun elementColorsDark() = ElementColors( quinary = Gray_450, gray300 = Compound_Gray_300_Dark, gray400 = Compound_Gray_400_Dark, + gray1400 = Gray_1400_Dark, textActionCritical = TextColorCriticalDark, isLight = false, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsLight.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsLight.kt index 085dd534cd..696baca384 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsLight.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ColorsLight.kt @@ -22,12 +22,13 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import io.element.android.libraries.designsystem.Azure import io.element.android.libraries.designsystem.Black_900 +import io.element.android.libraries.designsystem.Compound_Gray_300_Light +import io.element.android.libraries.designsystem.Compound_Gray_400_Light import io.element.android.libraries.designsystem.Gray_100 +import io.element.android.libraries.designsystem.Gray_1400_Light import io.element.android.libraries.designsystem.Gray_150 import io.element.android.libraries.designsystem.Gray_200 import io.element.android.libraries.designsystem.Gray_25 -import io.element.android.libraries.designsystem.Compound_Gray_300_Light -import io.element.android.libraries.designsystem.Compound_Gray_400_Light import io.element.android.libraries.designsystem.Gray_50 import io.element.android.libraries.designsystem.SystemGrey5Light import io.element.android.libraries.designsystem.SystemGrey6Light @@ -42,6 +43,7 @@ fun elementColorsLight() = ElementColors( quinary = Gray_50, gray300 = Compound_Gray_300_Light, gray400 = Compound_Gray_400_Light, + gray1400 = Gray_1400_Light, textActionCritical = TextColorCriticalLight, isLight = true, ) diff --git a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementColors.kt b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementColors.kt index 2643e678d3..9f7139b446 100644 --- a/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementColors.kt +++ b/libraries/designsystem/src/main/kotlin/io/element/android/libraries/designsystem/theme/ElementColors.kt @@ -31,6 +31,7 @@ class ElementColors( quinary: Color, gray300: Color, gray400: Color, + gray1400: Color, textActionCritical: Color, isLight: Boolean ) { @@ -53,6 +54,9 @@ class ElementColors( var gray400 by mutableStateOf(gray400) private set + var gray1400 by mutableStateOf(gray1400) + private set + var textActionCritical by mutableStateOf(textActionCritical) private set @@ -67,6 +71,7 @@ class ElementColors( quinary: Color = this.quinary, gray300: Color = this.gray300, gray400: Color = this.gray400, + gray1400: Color = this.gray1400, textActionCritical: Color = this.textActionCritical, isLight: Boolean = this.isLight, ) = ElementColors( @@ -77,6 +82,7 @@ class ElementColors( quinary = quinary, gray300 = gray300, gray400 = gray400, + gray1400 = gray1400, textActionCritical = textActionCritical, isLight = isLight, ) @@ -89,6 +95,7 @@ class ElementColors( quinary = other.quinary gray300 = other.gray300 gray400 = other.gray400 + gray1400 = other.gray1400 textActionCritical = other.textActionCritical isLight = other.isLight } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt index 5cf9375e0a..edf85cf471 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt @@ -89,4 +89,14 @@ interface MatrixRoom : Closeable { suspend fun inviteUserById(id: UserId): Result suspend fun canInvite(): Result + + suspend fun canSendStateEvent(type: StateEventType): Result + + suspend fun updateAvatar(mimeType: String, data: ByteArray): Result + + suspend fun removeAvatar(): Result + + suspend fun setName(name: String): Result + + suspend fun setTopic(topic: String): Result } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt new file mode 100644 index 0000000000..50cde59b37 --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/StateEventType.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.api.room + +enum class StateEventType { + POLICY_RULE_ROOM, + POLICY_RULE_SERVER, + POLICY_RULE_USER, + ROOM_ALIASES, + ROOM_AVATAR, + ROOM_CANONICAL_ALIAS, + ROOM_CREATE, + ROOM_ENCRYPTION, + ROOM_GUEST_ACCESS, + ROOM_HISTORY_VISIBILITY, + ROOM_JOIN_RULES, + ROOM_MEMBER_EVENT, + ROOM_NAME, + ROOM_PINNED_EVENTS, + ROOM_POWER_LEVELS, + ROOM_SERVER_ACL, + ROOM_THIRD_PARTY_INVITE, + ROOM_TOMBSTONE, + ROOM_TOPIC, + SPACE_CHILD, + SPACE_PARENT; +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt index c21ceb5076..383710873a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt @@ -27,12 +27,14 @@ import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState +import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.impl.media.map import io.element.android.libraries.matrix.impl.timeline.RustMatrixTimeline import io.element.android.services.toolbox.api.systemclock.SystemClock import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -224,6 +226,12 @@ class RustMatrixRoom( } } + override suspend fun canSendStateEvent(type: StateEventType): Result = withContext(coroutineDispatchers.io) { + runCatching { + innerRoom.member(sessionId.value).use { it.canSendState(type.map()) } + } + } + override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo): Result = withContext(coroutineDispatchers.io) { runCatching { innerRoom.sendImage(file.path, thumbnailFile.path, imageInfo.map()) @@ -247,4 +255,33 @@ class RustMatrixRoom( innerRoom.sendFile(file.path, fileInfo.map()) } } + + @OptIn(ExperimentalUnsignedTypes::class) + override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result = + withContext(Dispatchers.IO) { + runCatching { + innerRoom.uploadAvatar(mimeType, data.toUByteArray().toList()) + } + } + + override suspend fun removeAvatar(): Result = + withContext(Dispatchers.IO) { + runCatching { + innerRoom.removeAvatar() + } + } + + override suspend fun setName(name: String): Result = + withContext(Dispatchers.IO) { + runCatching { + innerRoom.setName(name) + } + } + + override suspend fun setTopic(topic: String): Result = + withContext(Dispatchers.IO) { + runCatching { + innerRoom.setTopic(topic) + } + } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt new file mode 100644 index 0000000000..2cd09e213c --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/StateEventType.kt @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.libraries.matrix.impl.room + +import io.element.android.libraries.matrix.api.room.StateEventType +import org.matrix.rustcomponents.sdk.StateEventType as RustStateEventType + +fun StateEventType.map(): RustStateEventType = when (this) { + StateEventType.POLICY_RULE_ROOM -> RustStateEventType.POLICY_RULE_ROOM + StateEventType.POLICY_RULE_SERVER -> RustStateEventType.POLICY_RULE_SERVER + StateEventType.POLICY_RULE_USER -> RustStateEventType.POLICY_RULE_USER + StateEventType.ROOM_ALIASES -> RustStateEventType.ROOM_ALIASES + StateEventType.ROOM_AVATAR -> RustStateEventType.ROOM_AVATAR + StateEventType.ROOM_CANONICAL_ALIAS -> RustStateEventType.ROOM_CANONICAL_ALIAS + StateEventType.ROOM_CREATE -> RustStateEventType.ROOM_CREATE + StateEventType.ROOM_ENCRYPTION -> RustStateEventType.ROOM_ENCRYPTION + StateEventType.ROOM_GUEST_ACCESS -> RustStateEventType.ROOM_GUEST_ACCESS + StateEventType.ROOM_HISTORY_VISIBILITY -> RustStateEventType.ROOM_HISTORY_VISIBILITY + StateEventType.ROOM_JOIN_RULES -> RustStateEventType.ROOM_JOIN_RULES + StateEventType.ROOM_MEMBER_EVENT -> RustStateEventType.ROOM_MEMBER_EVENT + StateEventType.ROOM_NAME -> RustStateEventType.ROOM_NAME + StateEventType.ROOM_PINNED_EVENTS -> RustStateEventType.ROOM_PINNED_EVENTS + StateEventType.ROOM_POWER_LEVELS -> RustStateEventType.ROOM_POWER_LEVELS + StateEventType.ROOM_SERVER_ACL -> RustStateEventType.ROOM_SERVER_ACL + StateEventType.ROOM_THIRD_PARTY_INVITE -> RustStateEventType.ROOM_THIRD_PARTY_INVITE + StateEventType.ROOM_TOMBSTONE -> RustStateEventType.ROOM_TOMBSTONE + StateEventType.ROOM_TOPIC -> RustStateEventType.ROOM_TOPIC + StateEventType.SPACE_CHILD -> RustStateEventType.SPACE_CHILD + StateEventType.SPACE_PARENT -> RustStateEventType.SPACE_PARENT +} + +fun RustStateEventType.map(): StateEventType = when (this) { + RustStateEventType.POLICY_RULE_ROOM -> StateEventType.POLICY_RULE_ROOM + RustStateEventType.POLICY_RULE_SERVER -> StateEventType.POLICY_RULE_SERVER + RustStateEventType.POLICY_RULE_USER -> StateEventType.POLICY_RULE_USER + RustStateEventType.ROOM_ALIASES -> StateEventType.ROOM_ALIASES + RustStateEventType.ROOM_AVATAR -> StateEventType.ROOM_AVATAR + RustStateEventType.ROOM_CANONICAL_ALIAS -> StateEventType.ROOM_CANONICAL_ALIAS + RustStateEventType.ROOM_CREATE -> StateEventType.ROOM_CREATE + RustStateEventType.ROOM_ENCRYPTION -> StateEventType.ROOM_ENCRYPTION + RustStateEventType.ROOM_GUEST_ACCESS -> StateEventType.ROOM_GUEST_ACCESS + RustStateEventType.ROOM_HISTORY_VISIBILITY -> StateEventType.ROOM_HISTORY_VISIBILITY + RustStateEventType.ROOM_JOIN_RULES -> StateEventType.ROOM_JOIN_RULES + RustStateEventType.ROOM_MEMBER_EVENT -> StateEventType.ROOM_MEMBER_EVENT + RustStateEventType.ROOM_NAME -> StateEventType.ROOM_NAME + RustStateEventType.ROOM_PINNED_EVENTS -> StateEventType.ROOM_PINNED_EVENTS + RustStateEventType.ROOM_POWER_LEVELS -> StateEventType.ROOM_POWER_LEVELS + RustStateEventType.ROOM_SERVER_ACL -> StateEventType.ROOM_SERVER_ACL + RustStateEventType.ROOM_THIRD_PARTY_INVITE -> StateEventType.ROOM_THIRD_PARTY_INVITE + RustStateEventType.ROOM_TOMBSTONE -> StateEventType.ROOM_TOMBSTONE + RustStateEventType.ROOM_TOPIC -> StateEventType.ROOM_TOPIC + RustStateEventType.SPACE_CHILD -> StateEventType.SPACE_CHILD + RustStateEventType.SPACE_PARENT -> StateEventType.SPACE_PARENT +} diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt index ff91db14b3..8769d9ddfb 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt @@ -26,6 +26,7 @@ import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.room.MatrixRoom import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState +import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.timeline.MatrixTimeline import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_SESSION_ID @@ -62,7 +63,13 @@ class FakeMatrixRoom( private var rejectInviteResult = Result.success(Unit) private var inviteUserResult = Result.success(Unit) private var canInviteResult = Result.success(true) + private val canSendStateResults = mutableMapOf>() private var sendMediaResult = Result.success(Unit) + private var setNameResult = Result.success(Unit) + private var setTopicResult = Result.success(Unit) + private var updateAvatarResult = Result.success(Unit) + private var removeAvatarResult = Result.success(Unit) + var sendMediaCount = 0 private set @@ -75,6 +82,18 @@ class FakeMatrixRoom( var invitedUserId: UserId? = null private set + var newTopic: String? = null + private set + + var newName: String? = null + private set + + var newAvatarData: ByteArray? = null + private set + + var removedAvatar: Boolean = false + private set + private var leaveRoomError: Throwable? = null override val membersStateFlow: MutableStateFlow = MutableStateFlow(MatrixRoomMembersState.Unknown) @@ -151,6 +170,10 @@ class FakeMatrixRoom( return canInviteResult } + override suspend fun canSendStateEvent(type: StateEventType): Result { + return canSendStateResults[type] ?: Result.failure(IllegalStateException("No fake answer")) + } + override suspend fun sendImage(file: File, thumbnailFile: File, imageInfo: ImageInfo): Result = fakeSendMedia() override suspend fun sendVideo(file: File, thumbnailFile: File, videoInfo: VideoInfo): Result = fakeSendMedia() @@ -166,6 +189,26 @@ class FakeMatrixRoom( } } + override suspend fun updateAvatar(mimeType: String, data: ByteArray): Result { + newAvatarData = data + return updateAvatarResult + } + + override suspend fun removeAvatar(): Result { + removedAvatar = true + return removeAvatarResult + } + + override suspend fun setName(name: String): Result { + newName = name + return setNameResult + } + + override suspend fun setTopic(topic: String): Result { + newTopic = topic + return setTopicResult + } + override fun close() = Unit fun givenLeaveRoomError(throwable: Throwable?) { @@ -204,6 +247,10 @@ class FakeMatrixRoom( canInviteResult = result } + fun givenCanSendStateResult(type: StateEventType, result: Result) { + canSendStateResults[type] = result + } + fun givenIgnoreResult(result: Result) { ignoreResult = result } @@ -215,4 +262,20 @@ class FakeMatrixRoom( fun givenSendMediaResult(result: Result) { sendMediaResult = result } + + fun givenUpdateAvatarResult(result: Result) { + updateAvatarResult = result + } + + fun givenRemoveAvatarResult(result: Result) { + removeAvatarResult = result + } + + fun givenSetNameResult(result: Result) { + setNameResult = result + } + + fun givenSetTopicResult(result: Result) { + setTopicResult = result + } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/avatar/AvatarActionListView.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt similarity index 92% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/avatar/AvatarActionListView.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt index 422a187290..59178d3a45 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/avatar/AvatarActionListView.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/AvatarActionBottomSheet.kt @@ -16,7 +16,7 @@ @file:OptIn(ExperimentalMaterialApi::class) -package io.element.android.features.createroom.impl.configureroom.avatar +package io.element.android.libraries.matrix.ui.components import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.fillMaxWidth @@ -39,12 +39,13 @@ import io.element.android.libraries.designsystem.preview.ElementPreviewDark import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.components.Icon import io.element.android.libraries.designsystem.theme.components.ModalBottomSheetLayout +import io.element.android.libraries.matrix.ui.media.AvatarAction import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.launch @Composable -fun AvatarActionListView( +fun AvatarActionBottomSheet( actions: ImmutableList, modalBottomSheetState: ModalBottomSheetState, modifier: Modifier = Modifier, @@ -62,7 +63,7 @@ fun AvatarActionListView( modifier = modifier, sheetState = modalBottomSheetState, sheetContent = { - SheetContent( + AvatarActionBottomSheetContent( actions = actions, onActionClicked = ::onItemActionClicked, modifier = Modifier @@ -74,7 +75,7 @@ fun AvatarActionListView( } @Composable -private fun SheetContent( +private fun AvatarActionBottomSheetContent( actions: ImmutableList, modifier: Modifier = Modifier, onActionClicked: (AvatarAction) -> Unit = { }, @@ -107,17 +108,17 @@ private fun SheetContent( @Preview @Composable -fun SheetContentLightPreview() = +fun AvatarActionBottomSheetLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun SheetContentDarkPreview() = +fun AvatarActionBottomSheetDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable private fun ContentToPreview() { - AvatarActionListView( + AvatarActionBottomSheet( actions = persistentListOf(AvatarAction.TakePhoto, AvatarAction.ChoosePhoto, AvatarAction.Remove), modalBottomSheetState = ModalBottomSheetState( initialValue = ModalBottomSheetValue.Expanded diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/Avatar.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt similarity index 86% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/Avatar.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt index bbaf5c46e5..e5816ac014 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/components/Avatar.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/components/UnsavedAvatar.kt @@ -14,11 +14,10 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.components +package io.element.android.libraries.matrix.ui.components import android.net.Uri import androidx.compose.foundation.background -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.size @@ -43,16 +42,19 @@ import io.element.android.libraries.designsystem.preview.debugPlaceholderBackgro import io.element.android.libraries.designsystem.theme.LocalColors import io.element.android.libraries.designsystem.theme.components.Icon +/** + * An avatar that the user has selected, but which has not yet been uploaded to Matrix. + * + * The image is loaded from a local resource instead of from a MXC URI. + */ @Composable -fun Avatar( +fun UnsavedAvatar( avatarUri: Uri?, modifier: Modifier = Modifier, - onClick: () -> Unit = {}, ) { val commonModifier = modifier .size(70.dp) .clip(CircleShape) - .clickable(onClick = onClick) if (avatarUri != null) { val context = LocalContext.current @@ -82,16 +84,16 @@ fun Avatar( @Preview @Composable -fun AvatarLightPreview() = ElementPreviewLight { ContentToPreview() } +fun UnsavedAvatarLightPreview() = ElementPreviewLight { ContentToPreview() } @Preview @Composable -fun AvatarDarkPreview() = ElementPreviewDark { ContentToPreview() } +fun UnsavedAvatarDarkPreview() = ElementPreviewDark { ContentToPreview() } @Composable private fun ContentToPreview() { Row { - Avatar(null) - Avatar(Uri.EMPTY) + UnsavedAvatar(null) + UnsavedAvatar(Uri.EMPTY) } } diff --git a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/avatar/AvatarAction.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt similarity index 95% rename from features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/avatar/AvatarAction.kt rename to libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt index 25ecc2b3db..624eb7c607 100644 --- a/features/createroom/impl/src/main/kotlin/io/element/android/features/createroom/impl/configureroom/avatar/AvatarAction.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/media/AvatarAction.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -package io.element.android.features.createroom.impl.configureroom.avatar +package io.element.android.libraries.matrix.ui.media import androidx.annotation.StringRes import androidx.compose.material.icons.Icons diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 525854695a..5c4f9b2afa 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55d774d5e61f8832162859ec9d22299efa5b0728cfc3308b13fb11df31c5130e -size 13790 +oid sha256:20e9edff7567936627718427349a2ca7bf318a909b6ca206304deab0149a796d +size 13934 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index 596f01f6a6..274045391e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b96cf938377ee0f02e0cf2d5896eb83935d9416a5b87849ed81caed4db5e90f2 -size 41264 +oid sha256:ab9f1e15c5a071be9c2f63aa28e6fbd57c360960899f808cead275ef67a54ae6 +size 41444 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 8ea3902e3a..419dd3269a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2ed229820d7cbd6b550e98116ec3bf688c52957e2a57ab296bfc5abd212345b1 -size 13784 +oid sha256:0b2350c880e73ce285bfeafafc851c6973e0372330ab6f4daa16d078ed6a81c9 +size 13884 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index 339b2adf16..85e99f70b1 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.addpeople_null_DefaultGroup_AddPeopleViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:92fe30f0927b8be4ffc384548e6731e7d5e347dc5caa1c84251f2a932ee1873c -size 38848 +oid sha256:71d10fb1a2102466ab759442a1e006a3f5f85cb80f9f885cb5d18c8ded570bef +size 38945 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 508f754a17..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fb3c154cf9fac2bc17b0ac3575bd2ce7745781cc1f2b9f28c220f98fb359c74d -size 16284 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png deleted file mode 100644 index 4bd0d46417..0000000000 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1d932221affde9bf53b06ea0e2b9c4b45dffe6adad7ff92f75775d9fe1d15b77 -size 16024 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index 734464a3ef..a12b50f256 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9da3afa82783ded1e686a36eb969b8e097de853e9c6dadea24884f7975c0720 -size 63729 +oid sha256:533ccd5b4e27b98ae4155e70d8cdde8a3c263ca613cc3e0ce31c2fc1d52758b8 +size 63361 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png index dc5306778e..24c6168c3a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2c200874d6fa8f4fe07da69a7879eb264fb8941c1ab9654162cb76586c52936 -size 103326 +oid sha256:7a036b609372a8c2f20307ca9f13b619973da8da45e81a019af1fdafadda64de +size 102275 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 3983e46caf..2a7e793b29 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e48bb979a1d328871157847862723b8342cd8c2704750045bbf24592e143a5e0 -size 57874 +oid sha256:4032529e222fb9d14d4dc1fd215c8aae5d63326809df7cec449011d7904a60d2 +size 58355 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png index bcd8ebf134..8a33e96fa4 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom_null_DefaultGroup_ConfigureRoomViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4320fd1900a120a737ba2a56e6c1f55cf671eb0e8c1791dc605994d333feb0c2 -size 96888 +oid sha256:2bf49968f5b07ca6798c4c8670a9e173ef3a511c6c60ed8d55d652bb042b8a8b +size 96370 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png index de4377ecc9..4c9bab34c8 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6f36c2b4f4266048d5295df08f380e4128630d11cce7ac11cf3a0eaaa5594d61 -size 19924 +oid sha256:4fddc08d41da556719424efbc75a1a80a57202095ce082124bc0bca08e54c74b +size 21415 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_0,NEXUS_5,1.0,en].png index 0bdbed7968..11d198b819 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.root_null_DefaultGroup_CreateRoomRootViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:23b9e996b8f0cc2efcb3adb6294bfa7b4a53fefbb7b6ee07add4105da9b9d40e -size 18984 +oid sha256:2639e6662a1c78ff0f6091fb72b5fc06b486aa08e3beed78e4877b9282ce98d6 +size 20309 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..9b2f7eb04e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ec2051ec47f43a88aeb0d56078094fca784c948ce2b3e1a450343943ab7ee951 +size 35408 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..487980822c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85d54c4c049c16435c4e6bcc8bf6d25dbc3fccc90a0278c2c330c97334eb27d0 +size 28119 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..8209d5b40a --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6caa0bdf8897ab6d8d73b8dfeac5c8c3cece5945e87123a1f5a1c96de477e623 +size 32262 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..16e677ee95 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:df9d8b28c6e98632204e90bccfddcf1e0d0ca6ce1a19af03f5bc491ade32fa36 +size 34793 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..c6c6003d29 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bfed133c28a48fb90aeda89de0154b4ab5c209d08a75147065dd8cc126f955eb +size 34416 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d1bbb6763c --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d3e1f45de5ab8d0bd953bd2a0a0014b7dce3315a82d993ceb633304f2b8c2622 +size 28517 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..0c632d8d45 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:02d7335bab9fd2bd753086ceeed886e57e0800b4d36a2cacd136c37bd41ebb9d +size 27152 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..a1307b1cf9 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c61f9803c3313f6e2cceb966942bec7885fcb2efd87e94babdac8e37b95d4da5 +size 35380 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..7422209943 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a1a6d6b7fb6f2d97d63773dae499ed096dbf38d16cd34cafcee3b46571717bcb +size 27828 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..ee9c9329b8 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:31d763b42ffc618bbf1c3272cbd4484edd7ab687f0ba25391955edf8f57c3208 +size 32443 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..077fc03e17 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca9b7a692c9d5bfa0ab0789f1d33a2f66c6244b1f4b672fab1aa18a71b3c9d0a +size 33897 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..cda549b019 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bdbeb867e5e009206a55eb1dfb0c478499301baa317a979cb2ffe9b1198871e8 +size 33931 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..e866646102 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dcf0472ccdfd1256eca966c6d29b4aace86f9ddab37164bb865633349a4377f3 +size 28519 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..d3cccbf61e --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.edit_null_DefaultGroup_RoomDetailsEditViewLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3733e12a63cd72d529f9359f555cda4da65384bd7631f8450451b27095691193 +size 26945 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png index ee9f0fb113..38aaa95764 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:516e972069292f31625847ca39de06ea402ce1606c97d84f930c18eaa2448cfa -size 13759 +oid sha256:9ba1f70268339a290e35e2127d1a3fa03840e69dc143631ef2fe29f22ddf646a +size 15363 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png index a1094a6fa6..e40d7e6d34 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b387f10903926ba9ed30b7d213314388eebf23743cbbdbcf4b613cf3136f64aa -size 41524 +oid sha256:01b7d6a33e3591a54bcf602bac34155b85056ebdf600c511858efba55ea43cf4 +size 43034 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png index 79b48dd535..7c469f37db 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0af8c7149fd4266c68c0e4b8b9a699bc0b3fc0b05fea3f2e2544abc884f70c5b -size 11988 +oid sha256:cdb5e42bc94ca908c76b5da491ed712169876e8f2ea80534f2e722b7280ba72d +size 13602 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png index ff95c9ed19..9648a14380 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8eb2fcdb57b7f1ba28990f9576fad6dce156fe0c5c9ae491797040fc22ae4f7b -size 39516 +oid sha256:656cec478d23350ed5c6e08b6cd9579e497999d6a7a8aa879af7dd223fefa98b +size 41088 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png index f7f16c2b8a..3f7583050a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1e4d3bc53e6a86957218247b614be6bbc78cfb35ce49565f2243f50d6ef94fe3 -size 14216 +oid sha256:456b9c09e0ff314543fa6a0e54d7e6e41f346e3fd2ff33f9ba1ea3ae3168d629 +size 15839 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png index 0cd4f9c3e0..a604fa2859 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:213777e4ace6f9c33209db37bffff2ca6dba13e540e9d07be82239d261ee8e3f -size 63951 +oid sha256:d76042bbb94307f96dc99d5077b58204b8f30a195408739271321570c218d0da +size 65441 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png index 84abdcb0e5..7041e4be75 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersDarkPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb1178f063eafb21ba01b93310d84335afbc4e6ad1fc99d1776578fe3f7eaa07 -size 49237 +oid sha256:22bda941040d3ad60d709517f07a27933ae25a5a69464e492fe1d0f9c4d7c405 +size 50488 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png index 57c7799447..29dd68cf13 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_0,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8ddec11fd636d345c49cf981ce7498ac2d94027d4b223b257b64288bb0254766 -size 13328 +oid sha256:bcc75c47150e577714221b5d574a67c69051cf2f4439f8b7c929620509d87562 +size 14668 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png index 1db9b42586..7a5b1f7a23 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_1,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:da5ba4637d999c8654e38a86ad0b26c75a6fe10a6988eb821b24cf91da2274e5 -size 39070 +oid sha256:1df29f2bd4090162dcbf7336971c366b53b49390f5f41cc6df6c6f506f01ab30 +size 40464 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png index 6648389b0e..79171ae80b 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c57c20fe79d0dbb0ca25571fa2ab3ba6e06f3a9c6634e96fac3ed0155c2ea106 -size 11160 +oid sha256:d488d43188f491cc578d30a70d05c90d6c436ca8fad88a36b85a1ef5bc90e88b +size 12564 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png index 5b6a14ea66..29762f9bf5 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e43279132a7536516cf267599f877f7c729f76be51d6319944f74c57af0e282 -size 36493 +oid sha256:900065642b4eb9a90a3a5d1bbbb5f03e9057b08e5e8289e535fd9237ac15e497 +size 37824 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png index fd948f750d..0a329a78cd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:933c5ac4478699e9e713af559025ab675a8bdc93b15c42ff27fe5525fa3b668a -size 13166 +oid sha256:b457fe4bc8ff8a26612819abbbac6440b539e61a8f00fb4171ad38dfb131840a +size 14553 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png index a35148eff1..168be9c78c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:3a66a39bf18ba35abfc87dea22ca019eea98727bf246142cb034649c5b1772bd -size 61041 +oid sha256:8384acb8fef843e0f06108d230c40bda5db7a95932568d37cbd610eaf09acc23 +size 62392 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png index 4d09e6d3d2..53795c55fd 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl.invite_null_DefaultGroup_RoomInviteMembersLightPreview_0_null_6,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ca9a896e2dd3cf47792467f052f2d45b555d99ccbd91436ef4c03dfca66836ab -size 47542 +oid sha256:307bfb8ffa9088d7d474c0d51e5e8d54bd6598b527357fe67380c755c2af75f0 +size 48893 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png index d2b6b99b87..eef6fe679e 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3e225aea9018c660e16ce5ee6e1e343b439b96264b274c5d722b05c6c9a5d48 -size 52064 +oid sha256:53015a9106ad2c3a24b9073e142db6fb3ba0e40ef3e87b1ba2142ec04066eba6 +size 53644 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png index f056965480..d2b6b99b87 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2372fe63e9fcc2c6529622270592437b880211acdcf7ef8be36dfc9fe41e0e5b -size 66807 +oid sha256:c3e225aea9018c660e16ce5ee6e1e343b439b96264b274c5d722b05c6c9a5d48 +size 52064 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_4,NEXUS_5,1.0,en].png index da20925ed9..f056965480 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:dce5d8c8dacb7c095cd22d590fc4fd9bc2cb9b13e0d4edbdd52387226b37b4ec -size 70253 +oid sha256:2372fe63e9fcc2c6529622270592437b880211acdcf7ef8be36dfc9fe41e0e5b +size 66807 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png index dd6890cc46..da20925ed9 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d3a1afc10771257294d9f22d26ed8a81d04cd4b1f31d9fb9fc1065a8216230a9 -size 67658 +oid sha256:dce5d8c8dacb7c095cd22d590fc4fd9bc2cb9b13e0d4edbdd52387226b37b4ec +size 70253 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png index 29b02fac10..dd6890cc46 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7a3b23895b133b7bba7300fbf31fd70daa83f558e29e8c09dfc7789ce0bc04d6 -size 61861 +oid sha256:d3a1afc10771257294d9f22d26ed8a81d04cd4b1f31d9fb9fc1065a8216230a9 +size 67658 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..3f1e6f2204 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8c3317c3c03399d5575a089029e237c766b912bb80a96a8feccb005f4e7247f5 +size 63166 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..98ca67f5aa --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsDarkPreview--1_1_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f3dd0fc6e0024160c03749948e4d4c6b353e3b5162b6ecc219c8bd16c2ad9848 +size 71157 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png index 66e16dc49c..aa5bcb484a 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_2,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b22c207ac5f185c1c7eaa30ffe43acb2a1e8da06379f15f66ff96797b8ea9158 -size 48297 +oid sha256:7045a9ced4691ec52451b6a31d2454ddc82c04b767c96a3dd4c3f81addcb8f0c +size 50010 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png index e58e13b108..66e16dc49c 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_3,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a7a98c0125cc58a130b8ebfbd5f2b8fcfa65e02b0c6a151c0b594208a8801c96 -size 60894 +oid sha256:b22c207ac5f185c1c7eaa30ffe43acb2a1e8da06379f15f66ff96797b8ea9158 +size 48297 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_4,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_4,NEXUS_5,1.0,en].png index b8abed4961..e58e13b108 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_4,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_4,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:998819b0aeb15712bb53e982e3756cf2e4a4069a2a13f7955e10b873e3e736bd -size 63698 +oid sha256:a7a98c0125cc58a130b8ebfbd5f2b8fcfa65e02b0c6a151c0b594208a8801c96 +size 60894 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png index 5130e63f43..b8abed4961 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_5,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cb8dc0ca3fa34ffa40ea43934ee00d0c48c9b7ce721274763c093eee83d8cff9 -size 62187 +oid sha256:998819b0aeb15712bb53e982e3756cf2e4a4069a2a13f7955e10b873e3e736bd +size 63698 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png index 81d82fd9d8..5130e63f43 100644 --- a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_7,NEXUS_5,1.0,en].png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e403d076b9d28e7fd06493399d4b7c405d99f329fb1a298c41fb445a1e9ec466 -size 57215 +oid sha256:cb8dc0ca3fa34ffa40ea43934ee00d0c48c9b7ce721274763c093eee83d8cff9 +size 62187 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..451dc8f651 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_8,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:da8ee4d42e3fcc4b68e06406bee859634c245baeb9ab7850427fffc04f41f1f8 +size 58493 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_9,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_9,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..be582845d0 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.roomdetails.impl_null_DefaultGroup_RoomDetailsLightPreview--0_0_null_9,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1797f9c90a79fc9bcbb1aa8d8ae32452c76d17b12e8ef7f441e1c93e9ac95c3f +size 64593 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..1de39b0f80 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_LabelledTextFieldDarkPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7cc0a54fec11686b8ffcba432562cfc1115973fab77693f8983a5b81a64e5d78 +size 15683 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png new file mode 100644 index 0000000000..f40b7dae38 --- /dev/null +++ b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.designsystem.components_null_DefaultGroup_LabelledTextFieldLightPreview_0_null,NEXUS_5,1.0,en].png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:738fd87b80036a040ceaddbad06ed4657a0d8d9d8c6052933300e5c194c17f24 +size 15724 diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom.avatar_null_DefaultGroup_SheetContentDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_AvatarActionBottomSheetDarkPreview_0_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom.avatar_null_DefaultGroup_SheetContentDarkPreview_0_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_AvatarActionBottomSheetDarkPreview_0_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom.avatar_null_DefaultGroup_SheetContentLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_AvatarActionBottomSheetLightPreview_0_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.configureroom.avatar_null_DefaultGroup_SheetContentLightPreview_0_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_AvatarActionBottomSheetLightPreview_0_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_AvatarDarkPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnsavedAvatarDarkPreview_0_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_AvatarDarkPreview_0_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnsavedAvatarDarkPreview_0_null,NEXUS_5,1.0,en].png diff --git a/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_AvatarLightPreview_0_null,NEXUS_5,1.0,en].png b/tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnsavedAvatarLightPreview_0_null,NEXUS_5,1.0,en].png similarity index 100% rename from tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.features.createroom.impl.components_null_DefaultGroup_AvatarLightPreview_0_null,NEXUS_5,1.0,en].png rename to tests/uitests/src/test/snapshots/images/io.element.android.tests.uitests_ScreenshotTest_preview_tests[io.element.android.libraries.matrix.ui.components_null_DefaultGroup_UnsavedAvatarLightPreview_0_null,NEXUS_5,1.0,en].png