Release 1.9.10 (#7019)

* spacing improved further

* Use unstable prefixes for QR code login

* Changelog

* Force update client information

* Changelog

* Fix incorrect Task creation for processing scanned qr codes

* Check and mark the received MSK as trusted before locally verifying the existing device

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

* Translations update from Weblate (#6908)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translation: Element iOS/Element iOS (Dialogs)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

* Translations update from Weblate (#6909)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (49 of 49 strings)

Translation: Element iOS/Element iOS (Push)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

* Translations update from Weblate (#6908)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translation: Element iOS/Element iOS (Dialogs)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

* Translations update from Weblate (#6910)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translations update from Weblate (#6908)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translation: Element iOS/Element iOS (Dialogs)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

* Translations update from Weblate (#6909)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (49 of 49 strings)

Translation: Element iOS/Element iOS (Push)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

* Translations update from Weblate (#6908)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translation: Element iOS/Element iOS (Dialogs)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

* Translations update from Weblate (#6911)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translation: Element iOS/Element iOS (Dialogs)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/bg/

* Translations update from Weblate (#6909)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (49 of 49 strings)

Translation: Element iOS/Element iOS (Push)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

* Translations update from Weblate (#6908)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translation: Element iOS/Element iOS (Dialogs)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

* Translations update from Weblate (#6910)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translations update from Weblate (#6908)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translation: Element iOS/Element iOS (Dialogs)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

* Translations update from Weblate (#6909)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (49 of 49 strings)

Translation: Element iOS/Element iOS (Push)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

* Translations update from Weblate (#6908)

* Translated using Weblate (Bulgarian)

Currently translated at 100.0% (8 of 8 strings)

Translation: Element iOS/Element iOS (Dialogs)
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-dialogs/bg/

* Translations update from Weblate (#6907)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>

* Translations update from Weblate (#6915)

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.2% (1870 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Bulgarian)

Currently translated at 66.4% (1529 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/bg/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 97.5% (2245 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2301 of 2301 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>

* Missing change from fix

* changelog

* Only running alpha builds when PR labeled with `Trigger-PR-Build`

* Configure codecov flags and have them be carried forward

* Add pull request change types for triggering alpha builds

* Updated templates readme.md file

* Add private var for avatar menu

* Cleanup createAvatarButtonItem method

* Add multiple fallbacks in AvatarViewDataProtocol

* Changelog

* Prepare for new sprint

* Add changelog.d file

* Remove space

* Init voice broadcast playing service

* Add some comments

* Add chunks in TimelineVoiceBroadcastDetails

* Check user id to prevent fake ckunk

* Rename TimelineVoiceBroadcastCoordinator to controller

It has nothing todo with a coordinator. Start to follow the same naming as VoiceMessage.

Remove SwiftUI VoiceBroadcastChunk to make it build

* Removed VoiceBroadcastProtocol

We do not need to abstract it

* Simplify TimelineVoiceBroadcastDetails struct

* Rename some existing voice broadcast files to VoiceBroadcastPlayback

Record will happen in separate files

* Renamed back to VoiceBroadcastPlaybackCoordinator

The logic will be moved to the view model. This file will just serve the SwiftUI view

* Fix text view height issue

* Add changelog.d file

* Aggregate chunks in voice broacast

* VB: Move view logic to the view model

* Add device_id and record tag

* VB: Playback starts to work but only the first chunk if it is ogg

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2302 of 2302 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* updated the package

* Remove clips to bounds for text views inside bubbles

* Improve http url interaction ux

* Edit mode

* Add changelog.d file

* VB: Improve playback states

* added placeholder to the viewModel

* Expose better broadcast details to the view

Starting from the sender name but we can add more things. This is up to the design expectation

* Session selection state

* Update the Voice Broadcast Labs flag description

* Device-to-device verification

* rich text composer placeholder text implemented using the same logic of the normal composer

* Support mp4 audio file format

* added a simple test

* improving code

* VB: Make the view model aware of every chunk new coming

this reactive approach will help to cache and reorder them by sequence

* fixing a legacy issue that sometime removed the placeholder

* improved old code

* improved old code further

* - Fix the RoomBubbleCellData tag management (Record/Playback/NoDisplay)
- Force the VB display even if the Labs flag is disabled. The Labs flag is only used now to block VB recording and sending
- Fix: Release VBService when the user stops the broadcast

* VoiceMessageAudioPlayer: Add support of URLs queue playback

To be used for voice broadcast

* VB: Support multi chunks playback

* Display info dialogs when we prevent the user from starting a new voice broadcast
- Update the existing implementation used to start/stop a voice broadcast in order to handle the different cases where voice broadcast is denied
- Add the optional Voice broadcast action to the new wysiwyg composer

* Init voice broadcast playing service

* Add some comments

* Add chunks in TimelineVoiceBroadcastDetails

* Check user id to prevent fake ckunk

* Rename TimelineVoiceBroadcastCoordinator to controller

It has nothing todo with a coordinator. Start to follow the same naming as VoiceMessage.

Remove SwiftUI VoiceBroadcastChunk to make it build

* Removed VoiceBroadcastProtocol

We do not need to abstract it

* Simplify TimelineVoiceBroadcastDetails struct

* Rename some existing voice broadcast files to VoiceBroadcastPlayback

Record will happen in separate files

* Renamed back to VoiceBroadcastPlaybackCoordinator

The logic will be moved to the view model. This file will just serve the SwiftUI view

* Aggregate chunks in voice broacast

* VB: Move view logic to the view model

* Add device_id and record tag

* VB: Playback starts to work but only the first chunk if it is ogg

* VB: Improve playback states

* Expose better broadcast details to the view

Starting from the sender name but we can add more things. This is up to the design expectation

* Update the Voice Broadcast Labs flag description

* Support mp4 audio file format

* VB: Make the view model aware of every chunk new coming

this reactive approach will help to cache and reorder them by sequence

* VoiceMessageAudioPlayer: Add support of URLs queue playback

To be used for voice broadcast

* VB: Support multi chunks playback

* - Fix the RoomBubbleCellData tag management (Record/Playback/NoDisplay)
- Force the VB display even if the Labs flag is disabled. The Labs flag is only used now to block VB recording and sending
- Fix: Release VBService when the user stops the broadcast

* Fixes after rebase on develop
Update voice broadcast playback UI

* Add voice broadcast error view

* Remove matrixsdk import

* VB: Manage playback completion properly

Make VoiceManager audioPlayerDidFinishPlaying called when the last item of the playlist has been played

* VB: Introduce VoiceBroadcastState for the UI

* Add issue automation for PS features teams

* Update issue automation for design

Put only high priority issues in front of the design team, all of which the design team will aim to action to keep the queue at zero

* Clarify issue automation conditions

Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>

* Removed sections

* VB: Started live playback

* VB: Support live playback

from the beginning or with go live

* VB: Own code review

* UI fixes

* Fix infinite layout loops on timeline (#6942)

* Remove `forceZeroSageAreaInsets` from `VectorHostingController`

* Fix layout issues when adding hosting views into the content view

* Use the new api when adding views into the content

* Add changelog

* VB: Remove a done TODO

* Display live voice broadcast

* Fix crash

* VB: Fix playLive after pause

* Fixes after rebase from develop

* VB: Hack to make the project build for SwiftUI tests

We need to rework the view model to remove its dependency on MatrixSDK

* Update UI

* VB: Moved the VM temporary under a MatrixSDK

to avoid to use it on the SwiftUI build

* the voice message toolbar now appears

* First part of the voice broadcast recording feature

* Fix missing parts on Voice broadcast recorder service

* Update recorder view and content

* Add sequence value when sending chunk files

* Update recorder service

* Fix recorder service for audio node after stopping a record

* Fix UI status at record startup and depending on the recording status

* Send the last chunk file after stopping or pausing a record

* Update recording state after stopping voice broadcast on every cases

* BF Ignore redacted voice broadcast state event (their content is empty)

* Update first chunk number

* Remove temporary code for stopping recording

* Update sequence number associated to chunk file sending

* Add AAC to M4A converter and now convert chunks before sending them

* Remove useless log in VoiceBroadcastRecorderService

* Fix a potential crash when trying to send a nil chunk file (in case of stop immediately after pause)

* Set voice broadcast recording to pause when the app goes in background and when the RVC will disappear

* Enhance RoomBubbleCellData handling
- detect correctly an actual live recording
- update the tile display at the end of a live recording

* Add missing update state after switching to resumed state

* Fix API change for VectorHostingController

* Update record UI to be in sync with other platforms

* Remove useless imports

* voice messages implemented

* fix

* Clean code

* Enhance chunk sending mecanism in recorder service

* voice message support added to the rich text composer

* Select All

* fix for tests

* new pushed mic asset

* Unit tests

* should fix the CI UI tests failing

* Changelog

* reimplemented but the animation and the spacing needs some fixing

* fixing view not returning in place

* animation

* More UI tests

* improving anim, however it only works with swiftui build

* Code review fixes

* minor adjustments

* Stop running UI tests on pushes to develop, they already run on PRs

* Fixes #6879 - Xcode 14 resource bundle signing errors

* Translated using Weblate (German)

Currently translated at 100.0% (2307 of 2307 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Persian)

Currently translated at 45.1% (1042 of 2307 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/fa/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2307 of 2307 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (German)

Currently translated at 100.0% (2308 of 2308 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Russian)

Currently translated at 81.0% (1870 of 2308 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ru/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2308 of 2308 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2308 of 2308 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2308 of 2308 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* Translated using Weblate (Estonian)

Currently translated at 100.0% (2308 of 2308 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2308 of 2308 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2308 of 2308 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2308 of 2308 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Translated using Weblate (German)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/

* Translated using Weblate (Polish)

Currently translated at 94.1% (2175 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pl/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/

* Translated using Weblate (Italian)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/

* Define MXCrypto and MXCrossSigning as protocols

* Translated using Weblate (Ukrainian)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/

* speeding the animation a bit

* tests and identifier improvements

* fix

* changelog

* removed unused code

* comment

* Curate MXCrypto protocol methods

* Complete MXCryptoV2 implementation

* Switch the CI to code 14 and the iOS 14 simulator, fix UI tests

* Fixes #6987 - Prevent ZXing from unnecessarily requesting camera access

* Fixes #6988 - Prevent actor switching when tearing down the rendezvous

* add Z-Labs tag or rich text editor and update to the new label naming

* changelog

* Hide old sessions list when the new dm is enabled

* Add changelog.d file

* CryptoV2 changes

* Display crypto version

* Add issue automation for the VoIP team

* Fix typo in issue automation

* Translations update from Weblate (#7017)

* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/

* Translated using Weblate (Estonian)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/

* Translated using Weblate (German)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (German)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/de/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (2311 of 2311 strings)

Translation: Element iOS/Element iOS
Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/nl/

Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johan Smits <johan@smitsmail.net>

* changelog.d: Upgrade MatrixSDK version ([v0.24.2](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.24.2)).

* version++

Co-authored-by: Mauro Romito <mauro.romito@element.io>
Co-authored-by: Alfonso Grillo <alfogrillo@element.io>
Co-authored-by: David Langley <langley.dave@gmail.com>
Co-authored-by: Velin92 <34335419+Velin92@users.noreply.github.com>
Co-authored-by: Hugh Nimmo-Smith <hughns@element.io>
Co-authored-by: Aleksandrs Proskurins <paleksandrs@gmail.com>
Co-authored-by: Aleksandrs Proskurins <aleksandrsp@element.io>
Co-authored-by: Stefan Ceriu <stefanc@matrix.org>
Co-authored-by: Vri <element@vrifox.cc>
Co-authored-by: Johannes Marbach <n0-0ne+github@mailbox.org>
Co-authored-by: Nui Harime <harime.nui@yandex.ru>
Co-authored-by: Szimszon <github@oregpreshaz.eu>
Co-authored-by: Slavi Pantaleev <slavi@devture.com>
Co-authored-by: random <dictionary@tutamail.com>
Co-authored-by: lvre <7uu3qrbvm@relay.firefox.com>
Co-authored-by: Ihor Hordiichuk <igor_ck@outlook.com>
Co-authored-by: Priit Jõerüüt <riot@joeruut.com>
Co-authored-by: Linerly <linerly@protonmail.com>
Co-authored-by: Jozef Gaal <preklady@mayday.sk>
Co-authored-by: Weblate <noreply@weblate.org>
Co-authored-by: Element Translate Bot <admin@riot.im>
Co-authored-by: Alfonso Grillo <alfogrillo@gmail.com>
Co-authored-by: yostyle <y.pintas@gmail.com>
Co-authored-by: manuroe <manu@matrix.org>
Co-authored-by: Giom Foret <giom@matrix.org>
Co-authored-by: Andy Uhnak <andyuhnak@gmail.com>
Co-authored-by: giomfo <gforet@matrix.org>
Co-authored-by: Kat Gerasimova <ekaterinag@element.io>
Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com>
Co-authored-by: Doug <douglase@element.io>
Co-authored-by: Philippe Loriaux <philippel@element.io>
Co-authored-by: manuroe <manuroe@users.noreply.github.com>
Co-authored-by: mmehdishafiee <mmhdishafiee@gmail.com>
Co-authored-by: Bartosz <barpaw@gmail.com>
Co-authored-by: Johan Smits <johan@smitsmail.net>
Co-authored-by: gulekismail <ismailgulek0@gmail.com>
This commit is contained in:
ismailgulek
2022-11-01 14:14:46 +03:00
committed by GitHub
parent c74efb00e6
commit 2cf68dd262
202 changed files with 4329 additions and 1268 deletions

View File

@@ -1,9 +1,6 @@
name: UI Tests CI
on:
push:
branches: [ develop ]
pull_request:
workflow_dispatch:

View File

@@ -17,7 +17,8 @@ jobs:
contains(github.event.issue.labels.*.name, 'Z-IA') ||
contains(github.event.issue.labels.*.name, 'A-Themes-Custom') ||
contains(github.event.issue.labels.*.name, 'A-E2EE-Dehydration') ||
contains(github.event.issue.labels.*.name, 'A-Tags')
contains(github.event.issue.labels.*.name, 'A-Tags') ||
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor')
steps:
- uses: actions/github-script@v5
with:
@@ -44,7 +45,13 @@ jobs:
name: P1 X-Needs-Design to Design project board
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'X-Needs-Design')
contains(github.event.issue.labels.*.name, 'X-Needs-Design') &&
(contains(github.event.issue.labels.*.name, 'S-Critical') &&
(contains(github.event.issue.labels.*.name, 'O-Frequent') ||
contains(github.event.issue.labels.*.name, 'O-Occasional')) ||
(contains(github.event.issue.labels.*.name, 'S-Major') &&
contains(github.event.issue.labels.*.name, 'O-Frequent')) ||
contains(github.event.issue.labels.*.name, 'A11y'))
steps:
- uses: octokit/graphql-action@v2.x
id: add_to_project
@@ -202,3 +209,105 @@ jobs:
env:
PROJECT_ID: "PN_kwDOAM0swc4AArk0"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
ps_features1:
name: Add labelled issues to PS features team 1
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Polls') ||
contains(github.event.issue.labels.*.name, 'A-Location-Sharing') ||
(contains(github.event.issue.labels.*.name, 'A-Voice-Messages') &&
!contains(github.event.issue.labels.*.name, 'A-Broadcast')) ||
(contains(github.event.issue.labels.*.name, 'A-Session-Mgmt') &&
contains(github.event.issue.labels.*.name, 'A-User-Settings'))
steps:
- uses: octokit/graphql-action@v2.x
id: add_to_project
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
item {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PVT_kwDOAM0swc4AHJKF"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
ps_features2:
name: Add labelled issues to PS features team 2
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-DM-Start') ||
contains(github.event.issue.labels.*.name, 'A-Broadcast')
steps:
- uses: octokit/graphql-action@v2.x
id: add_to_project
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
item {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PVT_kwDOAM0swc4AHJKd"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
ps_features3:
name: Add labelled issues to PS features team 3
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'A-Rich-Text-Editor')
steps:
- uses: octokit/graphql-action@v2.x
id: add_to_project
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
item {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PVT_kwDOAM0swc4AHJKW"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}
voip:
name: Add labelled issues to VoIP project board
runs-on: ubuntu-latest
if: >
contains(github.event.issue.labels.*.name, 'Team: VoIP')
steps:
- uses: octokit/graphql-action@v2.x
id: add_to_project
with:
headers: '{"GraphQL-Features": "projects_next_graphql"}'
query: |
mutation add_to_project($projectid:ID!,$contentid:ID!) {
addProjectV2ItemById(input: {projectId: $projectid contentId: $contentid}) {
item {
id
}
}
}
projectid: ${{ env.PROJECT_ID }}
contentid: ${{ github.event.issue.node_id }}
env:
PROJECT_ID: "PVT_kwDOAM0swc4ABMIk"
GITHUB_TOKEN: ${{ secrets.ELEMENT_BOT_TOKEN }}

View File

@@ -1,3 +1,40 @@
## Changes in 1.9.10 (2022-11-01)
✨ Features
- Changed the info in the background audio message player. ([#6870](https://github.com/vector-im/element-ios/pull/6870))
- Added voice message support to the Rich Text Composer ([#6941](https://github.com/vector-im/element-ios/issues/6941))
🙌 Improvements
- Improves external links interaction UX. ([#6936](https://github.com/vector-im/element-ios/pull/6936))
- Verification: Deprecate legacy device-to-device verification ([#6937](https://github.com/vector-im/element-ios/pull/6937))
- Crypto: Define MXCrypto and MXCrossSigning as protocols ([#6943](https://github.com/vector-im/element-ios/pull/6943))
- Hide the old session list when the new device manager is enabled. ([#6999](https://github.com/vector-im/element-ios/pull/6999))
- Upgrade MatrixSDK version ([v0.24.2](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.24.2)).
- Added a responsive placeholder text to the Rich Text Composer ([#6935](https://github.com/vector-im/element-ios/issues/6935))
- Added the maximise/minimise toggle button to the Rich Text Composer ([#6954](https://github.com/vector-im/element-ios/issues/6954))
🐛 Bugfixes
- Timeline: Fix layout for SwiftUI content views. ([#5326](https://github.com/vector-im/element-ios/issues/5326))
- Updates the avatar image loading logics. ([#6847](https://github.com/vector-im/element-ios/issues/6847))
- Fixes input text view height when containing multiple lines of text. ([#6849](https://github.com/vector-im/element-ios/issues/6849))
- Fixed the placeholder flickering in the input toolbar when there is an height change. ([#6949](https://github.com/vector-im/element-ios/issues/6949))
🧱 Build
- Add Z-Labs tag for rich text editor and update to the new label naming. ([#6996](https://github.com/vector-im/element-ios/pull/6996))
🚧 In development 🚧
- Device Manager: Multi-session selection. ([#6928](https://github.com/vector-im/element-ios/issues/6928))
Others
- Updated templates readme file. ([#6925](https://github.com/vector-im/element-ios/issues/6925))
## Changes in 1.9.9 (2022-10-18)
✨ Features

View File

@@ -33,7 +33,7 @@ class AppConfiguration: CommonConfiguration {
// Get additional events (modular widget, voice broadcast...)
MXKAppSettings.standard()?.addSupportedEventTypes([kWidgetMatrixEventTypeString,
kWidgetModularEventTypeString,
VoiceBroadcastSettings.eventType])
VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType])
// Hide undecryptable messages that were sent while the user was not in the room
MXKAppSettings.standard()?.hidePreJoinedUndecryptableEvents = true

View File

@@ -15,5 +15,5 @@
//
// Version
MARKETING_VERSION = 1.9.9
CURRENT_PROJECT_VERSION = 1.9.9
MARKETING_VERSION = 1.9.10
CURRENT_PROJECT_VERSION = 1.9.10

View File

@@ -234,6 +234,8 @@ final class BuildSettings: NSObject {
static let allowInviteExernalUsers: Bool = true
static let allowBackgroundAudioMessagePlayback: Bool = true
// MARK: - Side Menu
static let enableSideMenu: Bool = true && !newAppLayoutEnabled
static let sideMenuShowInviteFriends: Bool = true
@@ -406,7 +408,7 @@ final class BuildSettings: NSObject {
static let locationSharingEnabled = true
// MARK: - Voice Broadcast
static let voiceBroadcastChunkLength: Int = 600
static let voiceBroadcastChunkLength: Int = 120
static let voiceBroadcastMaxLength: UInt64 = 144000
// MARK: - MXKAppSettings

View File

@@ -172,7 +172,7 @@ class CommonConfiguration: NSObject, Configurable {
func setupSettingsWhenLoaded(for matrixSession: MXSession) {
// Do not warn for unknown devices. We have cross-signing now
matrixSession.crypto?.warnOnUnknowDevices = false
(matrixSession.crypto as? MXLegacyCrypto)?.warnOnUnknowDevices = false
}
}

11
Podfile
View File

@@ -16,7 +16,7 @@ use_frameworks!
# - `{ :specHash => {sdk spec hash}` to depend on specific pod options (:git => …, :podspec => …) for MatrixSDK repo. Used by Fastfile during CI
#
# Warning: our internal tooling depends on the name of this variable name, so be sure not to change it
$matrixSDKVersion = '= 0.24.1'
$matrixSDKVersion = '= 0.24.2'
# $matrixSDKVersion = :local
# $matrixSDKVersion = { :branch => 'develop'}
# $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } }
@@ -154,5 +154,14 @@ post_install do |installer|
config.build_settings['WARNING_CFLAGS'] ||= ['$(inherited)','-Wno-nullability-completeness']
config.build_settings['OTHER_SWIFT_FLAGS'] ||= ['$(inherited)', '-Xcc', '-Wno-nullability-completeness']
end
# Fix Xcode 14 resource bundle signing issues
# https://github.com/CocoaPods/CocoaPods/issues/11402#issuecomment-1259231655
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
target.build_configurations.each do |config|
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
end
end
end
end

View File

@@ -243,4 +243,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 82fb79d0a6b074f77950ec73304a749eb0329d12
COCOAPODS: 1.11.2
COCOAPODS: 1.11.3

View File

@@ -23,7 +23,7 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift",
"state" : {
"revision" : "11dad16e3e589dba423f6cc5707e9df8aace89b0"
"revision" : "d5ef7054fb43924d5b92d5d627347ca2bc333717"
}
},
{

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "user_session_list_item_not_selected.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,3 @@
<svg width="20" height="21" viewBox="0 0 20 21" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0.5C4.5 0.5 0 5 0 10.5C0 16 4.5 20.5 10 20.5C15.5 20.5 20 16 20 10.5C20 5 15.5 0.5 10 0.5ZM10 18.5C5.6 18.5 2 14.9 2 10.5C2 6.1 5.6 2.5 10 2.5C14.4 2.5 18 6.1 18 10.5C18 14.9 14.4 18.5 10 18.5Z" fill="#C1C6CD"/>
</svg>

After

Width:  |  Height:  |  Size: 368 B

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "user_session_list_item_selected.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,4 @@
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0C4.5 0 0 4.5 0 10C0 15.5 4.5 20 10 20C15.5 20 20 15.5 20 10C20 4.5 15.5 0 10 0Z" fill="#0DBD8B"/>
<path d="M4.54541 10.8887L7.80299 14.243L15.4545 6.36426" stroke="white" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 400 B

View File

@@ -1,17 +1,17 @@
{
"images" : [
{
"filename" : "action_voice_message.png",
"filename" : "Microphone icon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "action_voice_message@2x.png",
"filename" : "Microphone icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "action_voice_message@3x.png",
"filename" : "Microphone icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 818 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -1,17 +1,17 @@
{
"images" : [
{
"filename" : "voice_message_record_button_recording.png",
"filename" : "Microphone asset.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "voice_message_record_button_recording@2x.png",
"filename" : "Microphone asset@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "voice_message_record_button_recording@3x.png",
"filename" : "Microphone asset@3x.png",
"idiom" : "universal",
"scale" : "3x"
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "voice_broadcast_live.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,7 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.4589 2.79061C13.2328 2.49998 12.814 2.44762 12.5234 2.67367C12.233 2.89946 12.1805 3.31764 12.4057 3.60823L12.4062 3.60888L12.4068 3.60975L12.4159 3.62185C12.4248 3.63372 12.439 3.65309 12.4578 3.67965C12.4956 3.7328 12.5517 3.8145 12.6202 3.9221C12.7574 4.13766 12.9427 4.45508 13.1285 4.85325C13.502 5.65361 13.866 6.75551 13.866 7.9999C13.866 9.24429 13.502 10.3462 13.1285 11.1465C12.9427 11.5447 12.7574 11.8621 12.6202 12.0777C12.5517 12.1853 12.4956 12.267 12.4578 12.3201C12.439 12.3467 12.4248 12.3661 12.4159 12.3779L12.4068 12.3901L12.4062 12.3909L12.4056 12.3917C12.1805 12.6823 12.2331 13.1004 12.5234 13.3261C12.814 13.5522 13.2328 13.4998 13.4589 13.2092L12.962 12.8228C13.4589 13.2092 13.4589 13.2092 13.4589 13.2092L13.4602 13.2075L13.4621 13.205L13.4677 13.1978L13.4853 13.1743C13.4999 13.1547 13.5201 13.1272 13.5449 13.0921C13.5947 13.0221 13.6636 12.9216 13.7451 12.7935C13.9079 12.5377 14.1226 12.1694 14.3368 11.7104C14.7633 10.7965 15.1993 9.49837 15.1993 7.9999C15.1993 6.50143 14.7633 5.20333 14.3368 4.28941C14.1226 3.83043 13.9079 3.46214 13.7451 3.20627C13.6636 3.07816 13.5947 2.97772 13.5449 2.90765C13.5201 2.8726 13.4999 2.8451 13.4853 2.8255L13.4677 2.80202L13.4621 2.79479L13.4602 2.79232L13.4595 2.79137C13.4595 2.79137 13.4589 2.79061 12.9326 3.1999L13.4589 2.79061Z" fill="white"/>
<path d="M11.7261 5.19075C11.5001 4.90012 11.0812 4.84777 10.7906 5.07381C10.5007 5.29927 10.4479 5.71652 10.6719 6.00706L10.6742 6.01011C10.6772 6.01411 10.6828 6.02177 10.6907 6.03292C10.7066 6.05526 10.7315 6.09143 10.7625 6.14011C10.8246 6.23781 10.91 6.3838 10.9958 6.56768C11.1693 6.93947 11.3332 7.44137 11.3332 8.00005C11.3332 8.55872 11.1693 9.06062 10.9958 9.43241C10.91 9.61629 10.8246 9.76229 10.7625 9.85999C10.7315 9.90866 10.7066 9.94483 10.6907 9.96717C10.6828 9.97832 10.6772 9.98598 10.6742 9.98998L10.6719 9.99304C10.4479 10.2836 10.5007 10.7008 10.7906 10.9263C11.0812 11.1523 11.5001 11.1 11.7261 10.8093L11.1999 10.4C11.7261 10.8093 11.7261 10.8093 11.7261 10.8093L11.7273 10.8078L11.7288 10.8059L11.7326 10.8009L11.7436 10.7863C11.7523 10.7746 11.7639 10.7588 11.7778 10.7392C11.8057 10.6999 11.8433 10.645 11.8873 10.5758C11.9752 10.4378 12.0898 10.2409 12.204 9.99625C12.4305 9.5109 12.6666 8.8128 12.6666 8.00005C12.6666 7.18729 12.4305 6.48919 12.204 6.00384C12.0898 5.75915 11.9752 5.56229 11.8873 5.42427C11.8433 5.35509 11.8057 5.30019 11.7778 5.26092C11.7639 5.24127 11.7523 5.22549 11.7436 5.21376L11.7326 5.19918L11.7288 5.19419L11.7273 5.19228L11.7267 5.19147C11.7267 5.19147 11.7261 5.19075 11.1999 5.60005L11.7261 5.19075Z" fill="white"/>
<path d="M2.40733 13.2094C2.63337 13.5 3.05223 13.5524 3.34286 13.3263C3.63317 13.1005 3.68572 12.6824 3.46054 12.3918L3.46004 12.3911L3.45939 12.3903L3.45029 12.3781C3.44145 12.3663 3.42722 12.3469 3.40836 12.3203C3.37062 12.2672 3.31448 12.1855 3.246 12.0779C3.10883 11.8623 2.9235 11.5449 2.73768 11.1467C2.36418 10.3464 2.00023 9.24449 2.00023 8.0001C2.00023 6.75571 2.36418 5.65381 2.73768 4.85345C2.9235 4.45528 3.10883 4.13786 3.246 3.92231C3.31448 3.8147 3.37062 3.733 3.40836 3.67985C3.42722 3.65329 3.44145 3.63392 3.45029 3.62205L3.45939 3.60995L3.46004 3.60909L3.46064 3.60831C3.68571 3.31772 3.63313 2.89963 3.34286 2.67387C3.05223 2.44782 2.63337 2.50018 2.40733 2.79081L2.90417 3.17724C2.40732 2.79082 2.40733 2.79081 2.40733 2.79081L2.406 2.79252L2.40409 2.795L2.39856 2.80223L2.3809 2.8257C2.3663 2.84531 2.34615 2.8728 2.32126 2.90785C2.2715 2.97792 2.20265 3.07836 2.12112 3.20647C1.95829 3.46234 1.74363 3.83063 1.52944 4.28961C1.10294 5.20353 0.666896 6.50163 0.666896 8.0001C0.666896 9.49857 1.10294 10.7967 1.52944 11.7106C1.74363 12.1696 1.95829 12.5379 2.12112 12.7937C2.20265 12.9218 2.2715 13.0223 2.32126 13.0923C2.34615 13.1274 2.3663 13.1549 2.3809 13.1745L2.39856 13.198L2.40409 13.2052L2.406 13.2077L2.40674 13.2086C2.40674 13.2086 2.40733 13.2094 2.93356 12.8001L2.40733 13.2094Z" fill="white"/>
<path d="M4.14008 10.8092C4.36612 11.0999 4.78497 11.1522 5.0756 10.9262C5.36548 10.7007 5.41832 10.2835 5.19431 9.99294L5.19202 9.98989C5.18904 9.98589 5.18341 9.97823 5.17549 9.96708C5.15962 9.94474 5.13473 9.90857 5.10375 9.85989C5.04158 9.76219 4.95625 9.6162 4.87043 9.43232C4.69693 9.06053 4.53298 8.55863 4.53298 7.99995C4.53298 7.44128 4.69693 6.93938 4.87043 6.56759C4.95625 6.38371 5.04158 6.23771 5.10375 6.14001C5.13473 6.09134 5.15962 6.05517 5.17549 6.03283C5.18341 6.02168 5.18904 6.01402 5.19202 6.01002L5.19432 6.00696C5.41832 5.71642 5.36547 5.29917 5.0756 5.07372C4.78497 4.84767 4.36612 4.90003 4.14008 5.19066L4.66631 5.59996C4.14008 5.19066 4.14008 5.19066 4.14008 5.19066L4.13889 5.19218L4.13742 5.1941L4.1336 5.19909L4.12263 5.21366C4.11389 5.2254 4.10234 5.24118 4.08838 5.26083C4.0605 5.3001 4.02289 5.355 3.97887 5.42418C3.89104 5.56219 3.77638 5.75906 3.66219 6.00375C3.43569 6.4891 3.19964 7.1872 3.19964 7.99995C3.19964 8.81271 3.43569 9.51081 3.66219 9.99616C3.77638 10.2409 3.89104 10.4377 3.97887 10.5757C4.02289 10.6449 4.0605 10.6998 4.08838 10.7391C4.10234 10.7587 4.11389 10.7745 4.12263 10.7862L4.1336 10.8008L4.13742 10.8058L4.13889 10.8077L4.13952 10.8085C4.13952 10.8085 4.14008 10.8092 4.66631 10.4L4.14008 10.8092Z" fill="white"/>
<circle cx="8.00033" cy="8.00033" r="1.33333" fill="white"/>
</svg>

After

Width:  |  Height:  |  Size: 5.2 KiB

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "voice_broadcast_pause.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,5 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="16" cy="16" r="16" fill="white"/>
<path d="M11.833 11C11.833 10.4477 12.2061 10 12.6663 10H13.4997C13.9599 10 14.333 10.4477 14.333 11V21C14.333 21.5523 13.9599 22 13.4997 22H12.6663C12.2061 22 11.833 21.5523 11.833 21V11Z" fill="#737D8C"/>
<path d="M17.6663 11C17.6663 10.4477 18.0394 10 18.4997 10H19.333C19.7932 10 20.1663 10.4477 20.1663 11V21C20.1663 21.5523 19.7932 22 19.333 22H18.4997C18.0394 22 17.6663 21.5523 17.6663 21V11Z" fill="#737D8C"/>
</svg>

After

Width:  |  Height:  |  Size: 568 B

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "voice_broadcast_play.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,4 @@
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="16" cy="16" r="16" fill="white"/>
<path d="M11 22.2819V9.71812C11 8.95143 11.8273 8.46993 12.4939 8.84862L23.5526 15.1305C24.2274 15.5138 24.2274 16.4862 23.5526 16.8695L12.4939 23.1514C11.8273 23.5301 11 23.0486 11 22.2819Z" fill="#737D8C"/>
</svg>

After

Width:  |  Height:  |  Size: 358 B

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "voice_broadcast_record.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,6 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.25 16.5C16.25 15.6716 16.8096 15 17.5 15H18.75C19.4404 15 20 15.6716 20 16.5V31.5C20 32.3284 19.4404 33 18.75 33H17.5C16.8096 33 16.25 32.3284 16.25 31.5V16.5Z" fill="#737D8C"/>
<path d="M25 16.5C25 15.6716 25.5596 15 26.25 15H27.5C28.1904 15 28.75 15.6716 28.75 16.5V31.5C28.75 32.3284 28.1904 33 27.5 33H26.25C25.5596 33 25 32.3284 25 31.5V16.5Z" fill="#737D8C"/>
<circle cx="24" cy="24" r="24" fill="#F4F6FA"/>
<circle cx="24" cy="24" r="7.5" fill="#FF5B55"/>
</svg>

After

Width:  |  Height:  |  Size: 579 B

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "voice_broadcast_record_pause.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,5 @@
<svg width="49" height="48" viewBox="0 0 49 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="24.5" cy="24" r="24" fill="#F4F6FA"/>
<path d="M18.25 16.5002C18.25 15.6718 18.8096 15.0002 19.5 15.0002H20.75C21.4404 15.0002 22 15.6718 22 16.5002V31.5002C22 32.3287 21.4404 33.0002 20.75 33.0002H19.5C18.8096 33.0002 18.25 32.3287 18.25 31.5002V16.5002Z" fill="#737D8C"/>
<path d="M27 16.5002C27 15.6718 27.5596 15.0002 28.25 15.0002H29.5C30.1904 15.0002 30.75 15.6718 30.75 16.5002V31.5002C30.75 32.3287 30.1904 33.0002 29.5 33.0002H28.25C27.5596 33.0002 27 32.3287 27 31.5002V16.5002Z" fill="#737D8C"/>
</svg>

After

Width:  |  Height:  |  Size: 622 B

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "voice_broadcast_stop.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,6 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.249 16.5C16.249 15.6716 16.8087 15 17.499 15H18.749C19.4394 15 19.999 15.6716 19.999 16.5V31.5C19.999 32.3284 19.4394 33 18.749 33H17.499C16.8087 33 16.249 32.3284 16.249 31.5V16.5Z" fill="#737D8C"/>
<path d="M24.999 16.5C24.999 15.6716 25.5587 15 26.249 15H27.499C28.1894 15 28.749 15.6716 28.749 16.5V31.5C28.749 32.3284 28.1894 33 27.499 33H26.249C25.5587 33 24.999 32.3284 24.999 31.5V16.5Z" fill="#737D8C"/>
<circle cx="24" cy="24" r="24" fill="#F4F6FA"/>
<rect x="15" y="15" width="18" height="18" rx="1.5" fill="#737D8C"/>
</svg>

After

Width:  |  Height:  |  Size: 646 B

View File

@@ -153,15 +153,15 @@
"room_two_users_are_typing" = "%@ und %@ tippen…";
"room_many_users_are_typing" = "%@, %@ und andere tippen…";
"room_message_placeholder" = "Nachricht senden (unverschlüsselt)…";
"encrypted_room_message_placeholder" = "Verschlüsselte Nachricht…";
"room_message_short_placeholder" = "Sende eine Nachricht…";
"encrypted_room_message_placeholder" = "Verschlüsselte Nachricht senden …";
"room_message_short_placeholder" = "Nachricht senden …";
"room_offline_notification" = "Verbindung zum Server wurde unterbrochen.";
"room_unsent_messages_notification" = "Nachrichten wurden nicht gesendet.";
"room_unsent_messages_unknown_devices_notification" = "Nachrichten wurden nicht gesendet, da unbekannte Sitzungen vorhanden waren.";
"room_unsent_messages_notification" = "Senden der Nachrichten fehlgeschlagen.";
"room_unsent_messages_unknown_devices_notification" = "Senden der Nachrichten aufgrund unbekannter Sitzungen fehlgeschlagen.";
"room_prompt_resend" = "Alle erneut senden";
"room_prompt_cancel" = "Alles abbrechen";
"room_prompt_cancel" = "Alle abbrechen";
"room_resend_unsent_messages" = "Ungesendete Nachrichten erneut senden";
"room_delete_unsent_messages" = "Lösche ungesendete Nachrichten";
"room_delete_unsent_messages" = "Nicht gesendete Nachrichten löschen";
"room_event_action_copy" = "Kopieren";
"room_event_action_quote" = "Zitieren";
"room_event_action_more" = "Mehr";
@@ -301,7 +301,7 @@
"room_participants_action_unban" = "Entsperren";
"room_participants_action_set_default_power_level" = "Besondere Berechtigungen entziehen";
"room_participants_action_start_voice_call" = "Starte Sprach-Anruf";
"room_ongoing_conference_call" = "Laufender Konferenz-Anruf. Trete bei als %@ oder %@.";
"room_ongoing_conference_call" = "Laufender Konferenzanruf. Tritt als %@ oder %@ bei.";
"room_event_action_redact" = "Entfernen";
"room_warning_about_encryption" = "Ende-zu-Ende-Verschlüsselung ist in Beta und ist evtl. nicht zuverlässig.\n\nMan sollte noch nicht darauf vertrauen, dass die Daten sicher sind.\n\nGeräte werden Nachrichten von vor dem Beitritt des Raumes nicht entschlüsseln können.\n\nVerschlüsselte Nachrichten sind nicht lesbar in Anwendungen, die die Verschlüsselung noch nicht implementiert haben.";
"unknown_devices_alert" = "Dieser Raum enthält unbekannte Sitzungen, die nicht verifiziert wurden.\nDas bedeutet, es gibt keine Garantie, dass sie den angegebenen Benutzern gehört.\nWir empfehlen eine Überprüfung für jedes Gerät, bevor du weitermachst. Du kannst die Nachricht auch ohne Verifizierung erneut senden.";
@@ -411,14 +411,14 @@
"auth_home_server_placeholder" = "URL (z.B. https://matrix.org)";
"auth_identity_server_placeholder" = "URL (z. B. https://vector.im)";
"room_ongoing_conference_call_close" = "Schließen";
"room_conference_call_no_power" = "Du brauchst die Berechtigung Konferenzgespräche in diesem Raum zu verwalten";
"room_conference_call_no_power" = "Du bist nicht berechtigt, Konferenzgespräche in diesem Raum zu verwalten";
"settings_labs_create_conference_with_jitsi" = "Erstelle Konferenzgespräche mit Jitsi";
"call_already_displayed" = "Es existiert bereits ein Gespräch.";
"call_jitsi_error" = "Konferenzgespräch konnte nicht betreten werden.";
// Widget
"widget_no_power_to_manage" = "Du brauchst die Berechtigung um Widgets in diesem Raum zu verwalten";
"widget_creation_failure" = "Widget-Erstellung fehlgeschlagen";
"room_ongoing_conference_call_with_close" = "Laufendes Konferenzgespräch. Trete mit %@ oder %@ bei. %@ es.";
"room_ongoing_conference_call_with_close" = "Laufendes Konferenzgespräch. Tritt als %@ oder %@ bei. %@ es.";
"settings_ui_theme" = "Thema";
"settings_ui_theme_auto" = "Auto";
"settings_ui_theme_light" = "Hell";
@@ -436,13 +436,13 @@
"call_incoming_voice" = "Eingehender Anruf…";
"call_incoming_video" = "Eingehender Videoanruf…";
// Widget Integration Manager
"widget_integration_need_to_be_able_to_invite" = "Du musst Benutzer einladen können um das zu tun.";
"widget_integration_need_to_be_able_to_invite" = "Du musst Benutzer einladen dürfen, um dies zu tun.";
"widget_integration_unable_to_create" = "Erstellen des Widgets nicht möglich.";
"widget_integration_failed_to_send_request" = "Senden der Anfrage fehlgeschlagen.";
"widget_integration_room_not_recognised" = "Dieser Raum wurde nicht erkannt.";
"widget_integration_positive_power_level" = "Berechtigungslevel muss eine positive Zahl sein.";
"widget_integration_must_be_in_room" = "Du bist nicht in diesem Raum.";
"widget_integration_no_permission_in_room" = "Du hast keine Berechtigung dies in diesem Raum zu tun.";
"widget_integration_no_permission_in_room" = "Du bist nicht berechtigt, dies in diesem Raum zu tun.";
"widget_integration_missing_room_id" = "room_id fehlt in der Anfrage.";
"widget_integration_missing_user_id" = "user_id fehlt in der Anfrage.";
"widget_integration_room_not_visible" = "Raum %@ ist nicht sichtbar.";
@@ -502,7 +502,7 @@
// Group rooms
"group_rooms_filter_rooms" = "Filtere Community-Räume";
"e2e_room_key_request_message_new_device" = "Du hast die neue Sitzung '%@' hinzugefügt, welche Verschlüsselungs-Schlüssel anfordert.";
"room_do_not_have_permission_to_post" = "Du hast keine Berechtigung Nachrichten in diesem Raum zu senden";
"room_do_not_have_permission_to_post" = "Du bist nicht berechtigt, Nachrichten in diesem Raum zu senden";
"room_event_action_kick_prompt_reason" = "Grund für das Entfernen des Benutzers";
"room_event_action_ban_prompt_reason" = "Grund für die Verbannung der Person";
"room_action_send_photo_or_video" = "Foto oder Video senden";
@@ -532,8 +532,8 @@
"rerequest_keys_alert_title" = "Anfrage gesendet";
"rerequest_keys_alert_message" = "Bitte %@ auf einem anderen Gerät öffnen, das die Nachricht entschlüsseln kann, damit es die Schlüssel an diese Sitzung senden kann.";
"room_message_reply_to_placeholder" = "Antwort senden (unverschlüsselt)…";
"encrypted_room_message_reply_to_placeholder" = "Sende eine verschlüsselte Antwort…";
"room_message_reply_to_short_placeholder" = "Sende eine Antwort…";
"encrypted_room_message_reply_to_placeholder" = "Verschlüsselte Antwort senden …";
"room_message_reply_to_short_placeholder" = "Antwort senden …";
"room_replacement_information" = "Dieser Raum wurde ersetzt und ist nicht länger aktiv.";
"room_replacement_link" = "Die Konversation wird hier fortgesetzt.";
"room_predecessor_information" = "Dieser Raum ist die Fortsetzung einer anderen Konversation.";
@@ -743,8 +743,8 @@
"room_action_send_file" = "Datei senden";
"room_message_edits_history_title" = "Bearbeitungsverlauf";
// Widget
"widget_no_integrations_server_configured" = "Kein Integrationsserver konfiguriert";
"widget_integrations_server_failed_to_connect" = "Verbindung zum Integrationsserver fehlgeschlagen";
"widget_no_integrations_server_configured" = "Kein Integrations-Server konfiguriert";
"widget_integrations_server_failed_to_connect" = "Verbindung zum Integrations-Server fehlgeschlagen";
"device_verification_security_advice" = "Für maximale Sicherheit empfehlen wir, dies persönlich zu tun oder ein anderes vertrauenswürdiges Kommunikationsmittel zu verwenden";
"device_verification_incoming_description_1" = "Überprüfe diese Sitzung, um sie als vertrauenswürdig zu markieren. Sitzungen von Partnern zu vertrauen gibt dir zusätzliche Sicherheit bei der Verwendung von Ende-zu-Ende verschlüsselten Nachrichten.";
"device_verification_incoming_description_2" = "Wenn du diese Sitzung verifizierst, wird sie für dich und für dein Gegenüber als vertrauenswürdig gekennzeichnet.";
@@ -821,7 +821,7 @@
"media_type_accessibility_video" = "Video";
"media_type_accessibility_location" = "Standort";
"media_type_accessibility_file" = "Datei";
"media_type_accessibility_sticker" = "Aufkleber";
"media_type_accessibility_sticker" = "Sticker";
"settings_identity_server_settings" = "IDENTITÄTSERVER";
"settings_three_pids_management_information_part1" = "Verwalte hier, mit welchen E-Mail-Adressen oder Telefonnummern du dich anmeldest, oder dein Konto wiederherstellen kannst. Kontrolliere, wer dich finden kann ";
"settings_three_pids_management_information_part3" = ".";
@@ -902,7 +902,7 @@
"room_participants_security_loading" = "Lade…";
"room_participants_security_information_room_not_encrypted" = "Nachrichten in diesem Raum sind nicht Ende-zu-Ende verschlüsselt.";
"settings_security" = "SICHERHEIT";
"settings_integrations_allow_description" = "Benutze einen Integrationsmanager (%@), um Bots, Bridges, Widgets und Aufkleberpakete zu verwalten.\n\nIntegrationsmanager erhalten Konfigurationsdaten und können Widgets verändern, Raum-Einladungen versenden sowie Berechtigungen in deinem Namen einstellen.";
"settings_integrations_allow_description" = "Nutze einen Integrationsassistenten (%@), um Bots, Brücken, Widgets und Sticker-Pakete zu verwalten.\n\nIntegrationsassistenten erhalten Konfigurationsdaten und können Widgets verändern, Raumeinladungen versenden sowie Berechtigungen in deinem Namen einstellen.";
"settings_labs_enable_cross_signing" = "Aktiviere Cross-Signing, um deinen Gesprächspartner anstatt dessen Gerät zu verifizieren (in Entwicklung)";
// Security settings
"security_settings_title" = "Sicherheit";
@@ -1301,8 +1301,8 @@
"settings_show_NSFW_public_rooms" = "Öffentliche Räume mit anstößigen Inhalte anzeigen";
"room_open_dialpad" = "Wähltastatur";
"room_place_voice_call" = "Sprachanruf";
"room_unsent_messages_cancel_message" = "Bist du dir sicher alle nicht gesendete Nachrichten in diesem Raum zu löschen?";
"room_unsent_messages_cancel_title" = "Lösche nicht gesendete Nachrichten";
"room_unsent_messages_cancel_message" = "Bist du dir sicher, dass du alle nicht gesendeten Nachrichten in diesem Raum löschen möchtest?";
"room_unsent_messages_cancel_title" = "Nicht gesendete Nachrichten löschen";
"callbar_return" = "Zurück";
"callbar_only_multiple_paused" = "%@ pausierte Anrufe";
"callbar_only_single_paused" = "Pausierter Anruf";
@@ -1483,7 +1483,7 @@
// Alert explaining what an identity server / integration manager is.
"service_terms_modal_information_title_identity_server" = "Indentitätsserver";
"service_terms_modal_description_integration_manager" = "Das erlaubt dir Bots, Bridges und Stickerpacks zu verwenden.";
"service_terms_modal_description_integration_manager" = "Dies wird dir die Verwendung von Bots, Brücken und Sticker-Paketen ermöglichen.";
"service_terms_modal_description_identity_server" = "Dies erlaubt Personen, die deine Telefonnummer oder E-Mail in ihren Kontakten hat, dich zu finden.";
"service_terms_modal_table_header_identity_server" = "NUTZUNGSBEDINGUNGEN IDENTITÄTSSERVER";
"service_terms_modal_table_header_integration_manager" = "NUTZUNGSBEDINGUNGEN INTEGRATIONSMANAGER";
@@ -1506,12 +1506,12 @@
"poll_edit_form_add_option" = "Option hinzufügen";
"poll_edit_form_option_number" = "Option %lu";
"poll_edit_form_question_or_topic" = "Frage oder Thematik";
"room_event_action_end_poll" = "Umfrage beenden";
"room_event_action_remove_poll" = "Umfrage entfernen";
"room_event_action_end_poll" = "Abstimmung beenden";
"room_event_action_remove_poll" = "Abstimmung entfernen";
// Mark: - Polls
"poll_edit_form_create_poll" = "Umfrage erstellen";
"poll_edit_form_create_poll" = "Abstimmung erstellen";
"settings_labs_enabled_polls" = "Umfragen";
"share_extension_send_now" = "Jetzt senden";
"accessibility_button_label" = "Knopf";
@@ -1538,7 +1538,7 @@
"poll_edit_form_poll_question_or_topic" = "Frage oder Thema der Umfrage";
"poll_edit_form_input_placeholder" = "Schreib etwas";
"analytics_prompt_terms_link_upgrade" = "hier";
"poll_timeline_not_closed_title" = "Fehler beim Beenden der Abstimmung";
"poll_timeline_not_closed_title" = "Beenden der Abstimmung fehlgeschlagen";
"poll_timeline_vote_not_registered_subtitle" = "Wir konnten deine Stimme leider nicht erfassen. Versuche es bitte erneut";
"poll_timeline_total_final_results" = "Es wurden %lu Stimmen abgegeben";
"poll_timeline_total_final_results_one_vote" = "Es wurde 1 Stimme abgegeben";
@@ -1547,7 +1547,7 @@
"poll_timeline_not_closed_subtitle" = "Versuche es bitte erneut";
"poll_timeline_vote_not_registered_title" = "Stimme nicht erfasst";
"poll_edit_form_post_failure_subtitle" = "Versuche es bitte erneut";
"poll_edit_form_post_failure_title" = "Fehler beim Senden der Abstimmung";
"poll_edit_form_post_failure_title" = "Absenden der Abstimmung fehlgeschlagen";
"share_extension_low_quality_video_message" = "Für eine bessere Qualität sende es in %@ oder sende es in niedriger Qualität.";
"share_extension_low_quality_video_title" = "Das Video wird in niedriger Qualität gesendet werden";
"analytics_prompt_stop" = "Teilen beenden";
@@ -1588,11 +1588,11 @@
"onboarding_splash_register_button_title" = "Konto erstellen";
"settings_enable_room_message_bubbles" = "Nachrichtenblasen";
"poll_edit_form_update_failure_subtitle" = "Bitte erneut versuchen";
"poll_edit_form_poll_type" = "Umfragetyp";
"poll_edit_form_poll_type_closed_description" = "Ergebnisse werden erst angezeigt, wenn du die Umfrage beendest";
"poll_edit_form_poll_type_closed" = "Geschlossene Umfrage";
"poll_edit_form_poll_type_open_description" = "Ergebnisse werden direkt nach Stimmabgabe angezeigt";
"poll_edit_form_poll_type_open" = "Offene Umfrage";
"poll_edit_form_poll_type" = "Abstimmungsart";
"poll_edit_form_poll_type_closed_description" = "Die Ergebnisse werden erst sichtbar, sobald du die Umfrage beendest";
"poll_edit_form_poll_type_closed" = "Abgeschlossene Abstimmung";
"poll_edit_form_poll_type_open_description" = "Abstimmende können die Ergebnisse nach Stimmabgabe sehen";
"poll_edit_form_poll_type_open" = "Laufende Abstimmung";
"poll_edit_form_update_failure_title" = "Aktualisierung der Umfrage fehlgeschlagen";
"threads_empty_tip" = "Hinweis: Tippe auf eine Nachricht und wähle „Thread“ um einen neuen zu starten.";
"threads_empty_info_my" = "Antworte auf einen laufenden Thread oder tippe auf eine Nachricht und wähle „Thread“ um einen neuen zu starten.";
@@ -1766,7 +1766,7 @@
"notice_room_history_visible_to_members_from_joined_point_for_dm" = "%@ hat den zukünftigen Verlauf für alle Raumteilnehmer ab deren Einladung sichtbar gemacht.";
"notice_crypto_unable_to_decrypt" = "** Entschlüsselung nicht möglich: %@ **";
"notice_crypto_error_unknown_inbound_session_id" = "Die absendende Sitzung hat uns keine Schlüssel für diese Nachricht gesendet.";
"notice_sticker" = "Aufkleber";
"notice_sticker" = "Sticker";
"notice_in_reply_to" = "Als Antwort auf";
// room display name
"room_displayname_empty_room" = "Leerer Raum";
@@ -2365,7 +2365,7 @@
"spaces_explore_rooms_room_number" = "%@ Räume";
"spaces_create_space_title" = "Einen Space erstellen";
"spaces_add_space_title" = "Space erstellen";
"space_invite_not_enough_permission" = "Du hast keine Berechtigung, Personen zu diesem Space einzuladen";
"space_invite_not_enough_permission" = "Du hast keine Berechtigung, Personen in diesen Space einzuladen";
"room_invite_not_enough_permission" = "Du hast keine Berechtigung, Personen zu diesem Raum einzuladen";
"room_invite_to_room_option_detail" = "Sie werden kein Teil von %@ sein.";
"room_invite_to_room_option_title" = "Nur zu diesem Raum";
@@ -2639,3 +2639,15 @@
// Send Media Actions
"wysiwyg_composer_start_action_media_picker" = "Fotobibliothek";
"settings_labs_enable_wysiwyg_composer" = "Probiere den Rich-Text-Editor aus (bald auch mit Plain-Text-Modus)";
"wysiwyg_composer_start_action_voice_broadcast" = "Sprachübertragung";
"voice_broadcast_already_in_progress_message" = "Du zeichnest bereits eine Sprachübertragung auf. Bitte beende die laufende Übertragung, um eine neue zu beginnen.";
"voice_broadcast_blocked_by_someone_else_message" = "Jemand anderes nimmt bereits eine Sprachübertragung auf. Warte auf das Ende der Übertragung, bevor du eine neue startest.";
"voice_broadcast_permission_denied_message" = "Du hast nicht die nötigen Berechtigungen, um eine Sprachübertragung in diesem Raum zu starten. Kontaktiere einen Raumadministrator, um deine Berechtigungen anzupassen.";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Sprachübertragung kann nicht gestartet werden";
"settings_labs_enable_voice_broadcast" = "Sprachübertragung (in aktiver Entwicklung)";
"voice_broadcast_playback_loading_error" = "Wiedergabe der Sprachübertragung nicht möglich.";
"deselect_all" = "Alle abwählen";
"user_other_session_menu_select_sessions" = "Sitzungen auswählen";
"user_other_session_selected_count" = "%@ ausgewählt";

View File

@@ -20,4 +20,3 @@
"image_picker_action_files" = "Choose from files";
"voice_broadcast_in_timeline_title" = "Voice broadcast detected (under active development)";
"voice_broadcast_in_timeline_body" = "We currently only detect voice broadcast in the room timeline, this is not possible to send or listen an actual voice broadcast";

View File

@@ -798,7 +798,7 @@ Tap the + to start adding people.";
"settings_labs_enable_new_client_info_feature" = "Record the client name, version, and url to recognise sessions more easily in session manager";
"settings_labs_enable_new_app_layout" = "New Application Layout";
"settings_labs_enable_wysiwyg_composer" = "Try out the rich text editor (plain text mode coming soon)";
"settings_labs_enable_voice_broadcast" = "Voice broadcast (under active development). We currently only detect voice broadcast in the room timeline, this is not possible to send or listen an actual voice broadcast";
"settings_labs_enable_voice_broadcast" = "Voice broadcast (under active development)";
"settings_version" = "Version %@";
"settings_olm_version" = "Olm Version %@";
@@ -2189,6 +2189,13 @@ Tap the + to start adding people.";
"voice_message_stop_locked_mode_recording" = "Tap on your recording to stop or listen";
"voice_message_lock_screen_placeholder" = "Voice message";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Can't start a new voice broadcast";
"voice_broadcast_permission_denied_message" = "You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.";
"voice_broadcast_blocked_by_someone_else_message" = "Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.";
"voice_broadcast_already_in_progress_message" = "You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.";
"voice_broadcast_playback_loading_error" = "Unable to play this voice broadcast.";
// Mark: - Version check
"version_check_banner_title_supported" = "Were ending support for iOS %@";
@@ -2453,6 +2460,8 @@ To enable access, tap Settings> Location and select Always";
"user_other_session_no_verified_sessions" = "No verified sessions found.";
"user_other_session_no_unverified_sessions" = "No unverified sessions found.";
"user_other_session_clear_filter" = "Clear filter";
"user_other_session_selected_count" = "%@ selected";
"user_other_session_menu_select_sessions" = "Select sessions";
// First item is client name and second item is session display name
"user_session_name" = "%@: %@";
@@ -2503,6 +2512,7 @@ To enable access, tap Settings> Location and select Always";
"wysiwyg_composer_start_action_location" = "Location";
"wysiwyg_composer_start_action_camera" = "Camera";
"wysiwyg_composer_start_action_text_formatting" = "Text Formatting";
"wysiwyg_composer_start_action_voice_broadcast" = "Voice broadcast";
// Formatting Actions
"wysiwyg_composer_format_action_bold" = "Apply bold format";
@@ -2582,6 +2592,7 @@ To enable access, tap Settings> Location and select Always";
"reset_to_default" = "Reset to default";
"resend_message" = "Resend the message";
"select_all" = "Select All";
"deselect_all" = "Deselect All";
"cancel_upload" = "Cancel Upload";
"cancel_download" = "Cancel Download";
"show_details" = "Show Details";

View File

@@ -2506,3 +2506,86 @@
"authentication_qr_login_start_subtitle" = "Kasuta selle seadme kaamerat ja logi sisse teises seadmes kuvatud QR-koodi alusel:";
"authentication_qr_login_start_title" = "Loe QR-koodi";
"authentication_login_with_qr" = "Logi sisse QR-koodi abil";
"wysiwyg_composer_format_action_strikethrough" = "Kasuta allajoonitud kirja";
"wysiwyg_composer_format_action_underline" = "Kasuta läbijoonitud kirja";
"wysiwyg_composer_format_action_italic" = "Kasuta kaldkirja";
// Formatting Actions
"wysiwyg_composer_format_action_bold" = "Kasuta paksu kirja";
"wysiwyg_composer_start_action_voice_broadcast" = "Ringhäälingukõne";
"wysiwyg_composer_start_action_text_formatting" = "Tekstivorming";
"wysiwyg_composer_start_action_camera" = "Kaamera";
"wysiwyg_composer_start_action_location" = "Asukoht";
"wysiwyg_composer_start_action_polls" = "Küsitlused";
"wysiwyg_composer_start_action_attachments" = "Manused";
"wysiwyg_composer_start_action_stickers" = "Kleepsud";
// Mark: - WYSIWYG Composer
// Send Media Actions
"wysiwyg_composer_start_action_media_picker" = "Fotode kogu";
"user_session_details_last_activity" = "Viimati kasutusel";
"device_type_name_unknown" = "Tundmatu seadmetüüp";
"device_type_name_mobile" = "Mobiiltelefon";
"device_type_name_web" = "Veebiliides";
"device_type_name_desktop" = "Töölauarakendus";
"user_inactive_session_item_with_date" = "Pole olnud kasutusel üle 90 päeva (%@)";
"user_inactive_session_item" = "Pole olnud kasutusel üle 90 päeva";
"user_session_item_details_last_activity" = "Viimati kasutusel %@";
"user_other_session_clear_filter" = "Eemalda filter";
"user_other_session_no_unverified_sessions" = "Verifitseerimata sessioone ei leidu.";
"user_other_session_no_verified_sessions" = "Verifitseeritud sessioone ei leidu.";
"user_other_session_no_inactive_sessions" = "Ei leidu sessioone, mis pole aktiivses kasutuses.";
"user_other_session_filter_menu_inactive" = "Pole pidevas kasutuses";
"user_other_session_filter_menu_unverified" = "Verifitseerimata";
"user_other_session_filter_menu_verified" = "Verifitseeritud";
"user_other_session_filter_menu_all" = "Kõik sessioonid";
"user_other_session_filter" = "Filtreeri";
"user_other_session_verified_sessions_header_subtitle" = "Parima turvalisuse nimel logi välja neist sessioonidest, mida sa enam ei kasuta või ei tunne ära.";
"user_other_session_current_session_details" = "Sinu praegune sessioon";
"user_other_session_unverified_sessions_header_subtitle" = "Turvalise sõnumvahetuse nimel verifitseeri kõik oma sessioonid ning logi neist välja, mida sa enam ei kasuta või ei tunne enam ära.";
"user_other_session_security_recommendation_title" = "Turvalisusega seotud soovitused";
"user_other_session_verified_additional_info" = "See sessioon on valmis turvaliseks sõnumivahetuseks.";
"user_other_session_unverified_additional_info" = "Parima turvalisuse ja töökindluse nimel verifitseeri see sessioon või logi ta võrgust välja.";
"user_session_verification_unknown_additional_info" = "Selle sessiooni olekut ei saa tuvastada enne kui oled ta verifitseerinud.";
"user_session_verification_unknown_short" = "Teadmata olek";
"user_session_verification_unknown" = "Verifitseerimise olek on määratlemata";
"user_sessions_overview_link_device" = "Seo teise seadmega";
// MARK: User sessions management
// Parameter is the application display name (e.g. "Element")
"user_sessions_default_session_display_name" = "%@ iOS";
"voice_broadcast_playback_loading_error" = "Selle ringhäälingukõne esitamine ei õnnestu.";
"voice_broadcast_already_in_progress_message" = "Sa juba salvestad ringhäälingukõnet. Uue alustamiseks palun lõpeta eelmine salvestus.";
"voice_broadcast_blocked_by_someone_else_message" = "Keegi juba salvestab ringhäälingukõnet. Uue ringhäälingukõne salvestamiseks palun oota, kuni see teine ringhäälingukõne on lõppenud.";
"voice_broadcast_permission_denied_message" = "Sul pole piisavalt õigusi selles jututoas ringhäälingukõne algatamiseks. Õiguste lisamiseks palun võta ühendust jututoa haldajaga.";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Uue ringhäälingukõne alustamine pole võimalik";
"sign_out_confirmation_message" = "Kas sa oled kindel et soovid välja logida?";
// MARK: Sign out warning
"sign_out" = "Logi välja";
"manage_session_rename" = "Muuda sessiooni nime";
"manage_session_name_info_link" = "Lisateave";
/* The placeholder will be replaces with manage_session_name_info_link */
"manage_session_name_info" = "Palun arvesta, et sessioonide nimed on näha ka kõikidele osapooltele, kellega sa suhtled. %@";
"manage_session_name_hint" = "Sinu enda kirjutatud sessiooninimede alusel on sul oma seadmeid lihtsam ära tunda.";
"settings_labs_enable_voice_broadcast" = "Ringhäälingukõne (aktiivses arenduses)";
"settings_labs_enable_wysiwyg_composer" = "Proovi vormindatud teksti alusel töötavat tekstitoimetit (varsti lisandub ka vormindamata teksti režiim)";
"authentication_qr_login_failure_retry" = "Proovi uuesti";
"authentication_qr_login_failure_request_timed_out" = "Sidumine ei lõppenud etteantud aja jooksul.";
"authentication_qr_login_failure_request_denied" = "Teine seade lükkas päringu tagasi.";
"authentication_qr_login_failure_invalid_qr" = "QR-kood on vigane.";
"authentication_qr_login_failure_title" = "Seose loomine ei õnenstunud";
"authentication_qr_login_loading_signed_in" = "Sa oled oma teises seadmes sisse loginud Matrix'i võrku.";
"authentication_qr_login_loading_waiting_signin" = "Ootame, et teine seade logiks võrku.";
"authentication_qr_login_loading_connecting_device" = "Loon ühendust seadmega";
"authentication_qr_login_confirm_alert" = "Palun vaata, et sa kindlasti tead, kust see QR-kood kuvatakse. Sellisel viisil seadmete sidumisel sa annad oma kasutajakontole täiemahulise ligipääsu.";
"authentication_qr_login_confirm_subtitle" = "Kontrolli, et järgnev kood klapib teises seadmes kuvatava koodiga:";
"deselect_all" = "Eemalda kõik valikud";
"user_other_session_menu_select_sessions" = "Vali sessioonid";
"user_other_session_selected_count" = "%@ valitud";

View File

@@ -1270,3 +1270,23 @@
"microphone_access_not_granted_for_voice_message" = "جهت ارسال پیام صوتی نیاز به دسترسی به میکروفون وجود دارد اما %@ دسترسی استفاده از آن را ندارد";
"e2e_passphrase_too_short" = "کلمه عبور بیش از حد کوتاه است (حداقل می‌بایست %d کاراکتر باشد)";
"message_reply_to_sender_sent_a_voice_message" = "یک پیام صوتی ارسال کنید.";
"onboarding_splash_page_1_title" = "صاحب گفتگوهای خود شوید.";
"onboarding_splash_login_button_title" = "من از قبل حساب کاربری دارم";
// MARK: Onboarding
"onboarding_splash_register_button_title" = "ساخت حساب کاربری";
"accessibility_button_label" = "دکمه";
"saving" = "در حال ذخیره";
// Activities
"loading" = "در حال بارگزاری";
"invite_to" = "دعوت به %@";
"confirm" = "تأیید";
"edit" = "ویرایش";
"suggest" = "پیشنهاد";
"add" = "افزودن";
"existing" = "خروج";
"new_word" = "جدید";
"stop" = "توقف";
"joining" = "پیوستن";
"enable" = "فعال";

View File

@@ -2625,3 +2625,15 @@
"authentication_qr_login_start_subtitle" = "Használd a kamerát ezen az eszközön a másik eszközödön megjelenő QR kód beolvasására:";
"authentication_qr_login_start_title" = "QR kód beolvasása";
"authentication_login_with_qr" = "Belépés QR kóddal";
"settings_labs_enable_voice_broadcast" = "Hang közvetítés (aktív fejlesztés alatt)";
"wysiwyg_composer_start_action_voice_broadcast" = "Hang közvetítés";
"voice_broadcast_playback_loading_error" = "A hang közvetítés nem játszható le.";
"voice_broadcast_already_in_progress_message" = "Egy hang közvetítés már folyamatban van. Először fejezd be a jelenlegi közvetítést egy új indításához.";
"voice_broadcast_blocked_by_someone_else_message" = "Valaki már elindított egy hang közvetítést. Várd meg a közvetítés végét az új indításához.";
"voice_broadcast_permission_denied_message" = "Nincs jogosultságod hang közvetítést indítani ebben a szobában. Vedd fel a kapcsolatot a szoba adminisztrátorával a szükséges jogosultság megszerzéséhez.";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Az új hang közvetítés nem indítható el";
"deselect_all" = "Semmit nem jelöl ki";
"user_other_session_menu_select_sessions" = "Munkamenetek kiválasztása";
"user_other_session_selected_count" = "%@ kiválasztva";

View File

@@ -2832,3 +2832,15 @@
"manage_session_name_info" = "Harap diketahui bahwa nama sesi juga terlihat ke orang-orang yang Anda berkomunikasi. %@";
"manage_session_name_hint" = "Nama sesi khusus dapat membantu Anda mengenal perangkat Anda dengan lebih mudah.";
"settings_labs_enable_wysiwyg_composer" = "Coba editor teks kaya (mode teks biasa akan datang)";
"wysiwyg_composer_start_action_voice_broadcast" = "Siaran suara";
"voice_broadcast_playback_loading_error" = "Tidak dapat memainkan siaran suara ini.";
"voice_broadcast_already_in_progress_message" = "Anda saat ini merekam sebuah siaran suara. Mohon akhiri siaran suara Anda saat ini untuk memulai yang baru.";
"voice_broadcast_blocked_by_someone_else_message" = "Ada orang lain yang saat ini merekam sebuah siaran suara. Tunggu siaran suaranya berakhir untuk memulai yang baru.";
"voice_broadcast_permission_denied_message" = "Anda tidak memiliki izin untuk memulai sebuah siaran suara di ruangan ini. Hubungi sebuah administrator ruangan untuk meningkatkan izin Anda.";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Tidak dapat memulai sebuah siaran suara baru";
"settings_labs_enable_voice_broadcast" = "Siaran suara (dalam pengembangan aktif)";
"deselect_all" = "Batalkan Semua Pilihan";
"user_other_session_menu_select_sessions" = "Pilih sesi";
"user_other_session_selected_count" = "%@ dipilih";

View File

@@ -2605,4 +2605,15 @@
"manage_session_name_info" = "Ricorda che i nomi di sessione sono anche visibili alle persone con cui comunichi. %@";
"manage_session_name_hint" = "I nomi di sessione personalizzati possono aiutarti a riconoscere i tuoi dispositivi più facilmente.";
"settings_labs_enable_wysiwyg_composer" = "Prova l'editor in rich text (il testo semplice è in arrivo)";
"settings_labs_enable_voice_broadcast" = "Broadcast voce (in sviluppo attivo). Attualmente rileviamo solo il broadcast vocale nella linea temporale della stanza, non è possibile inviare o ascoltare un vero broadcast vocale";
"settings_labs_enable_voice_broadcast" = "Trasmissione vocale (in sviluppo attivo)";
"wysiwyg_composer_start_action_voice_broadcast" = "Trasmissione vocale";
"voice_broadcast_playback_loading_error" = "Impossibile avviare questa trasmissione vocale.";
"voice_broadcast_already_in_progress_message" = "Stai già registrando una trasmissione vocale. Termina quella in corso per iniziarne una nuova.";
"voice_broadcast_blocked_by_someone_else_message" = "Qualcun altro sta già registrando una trasmissione vocale. Aspetta che finisca prima di iniziarne una nuova.";
"voice_broadcast_permission_denied_message" = "Non hai l'autorizzazione necessaria per iniziare un broadcast vocale in questa stanza. Contatta un amministratore della stanza per aggiornare le tue autorizzazioni.";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Impossibile iniziare una nuova trasmissione vocale";
"deselect_all" = "Deseleziona tutti";
"user_other_session_menu_select_sessions" = "Seleziona sessioni";
"user_other_session_selected_count" = "%@ selezionate";

View File

@@ -2463,7 +2463,7 @@
"room_access_settings_screen_upgrade_alert_note" = "Houd er rekening mee dat bij het upgraden een nieuwe versie van de kamer wordt gemaakt. Alle huidige berichten blijven in deze gearchiveerde ruimte.";
"room_access_settings_screen_upgrade_alert_message_no_param" = "Iedereen in een bovenliggende space kan deze ruimte vinden en er lid van worden. Het is niet nodig om iedereen handmatig uit te nodigen. U kunt dit op elk moment wijzigen in de kamerinstellingen.";
"room_access_settings_screen_upgrade_alert_message" = "Iedereen in %@ kan deze ruimte vinden en er lid van worden - het is niet nodig om iedereen handmatig uit te nodigen. U kunt dit op elk moment wijzigen in de kamerinstellingen.";
"settings_presence_offline_mode_description" = "Indien ingeschakeld, verschijnt u altijd offline voor andere personen, zelfs wanneer u de applicatie gebruikt.";
"settings_presence_offline_mode_description" = "Indien ingeschakeld, verschijnt u altijd offline voor andere personen, zelfs wanneer u de toepassing gebruikt.";
"settings_presence_offline_mode" = "Offline modus";
"settings_presence" = "Aanwezigheid";
"threads_discourage_information_2" = "\n\nWilt u toch threads inschakelen?";
@@ -2652,3 +2652,156 @@
// User sessions management
"user_sessions_settings" = "Beheer sessies";
"invite_to" = "Uitnodigen %@";
"room_event_encryption_info_key_authenticity_not_guaranteed" = "De authenticiteit van dit versleutelde bericht kan niet worden gegarandeerd op dit apparaat.";
"deselect_all" = "Deselecteer alles";
"wysiwyg_composer_format_action_strikethrough" = "Onderstrepen formaat toepassen";
"wysiwyg_composer_format_action_underline" = "Doorstrepen formaat toepassen";
"wysiwyg_composer_format_action_italic" = "Cursief formaat toepassen";
// Formatting Actions
"wysiwyg_composer_format_action_bold" = "Vet formaat toepassen";
"wysiwyg_composer_start_action_voice_broadcast" = "Spraakuitzending";
"wysiwyg_composer_start_action_text_formatting" = "Tekst opmaak";
"wysiwyg_composer_start_action_camera" = "Camera";
"wysiwyg_composer_start_action_location" = "Locatie";
"wysiwyg_composer_start_action_polls" = "Peilingen";
"wysiwyg_composer_start_action_attachments" = "Bijlagen";
"wysiwyg_composer_start_action_stickers" = "Stikkers";
// Mark: - WYSIWYG Composer
// Send Media Actions
"wysiwyg_composer_start_action_media_picker" = "Fotobibliotheek";
"user_session_overview_session_details_button_title" = "Sessie details";
"user_session_overview_session_title" = "Sessie";
"user_session_overview_current_session_title" = "Huidige sessie";
"user_session_details_application_url" = "URL";
"user_session_details_application_version" = "Versie";
"user_session_details_application_name" = "Naam";
"user_session_details_device_os" = "Besturingssysteem";
"user_session_details_device_browser" = "Browser";
"user_session_details_device_model" = "Model";
"user_session_details_device_ip_location" = "IP locatie";
"user_session_details_device_ip_address" = "IP adres";
"user_session_details_last_activity" = "Laatste activiteit";
"user_session_details_session_section_footer" = "Kopieer alle gegevens door erop te tikken en ingedrukt te houden.";
"user_session_details_session_id" = "Sessie ID";
"user_session_details_session_name" = "Sessie naam";
"user_session_details_device_section_header" = "Apparaat";
"device_name_unknown" = "Onbekende toepassing";
"settings_labs_enable_new_app_layout" = "Nieuwe toepassing-indeling";
"settings_labs_enable_new_client_info_feature" = "Noteer de naam, versie en url van de toepassing om sessies gemakkelijker te herkennen in sessiebeheer";
"user_session_details_application_section_header" = "Toepassing";
"user_session_details_session_section_header" = "Sessie";
"user_session_details_title" = "Toon details";
"device_type_name_unknown" = "Onbekend";
"device_type_name_mobile" = "Mobiel";
"device_type_name_web" = "Web";
"device_type_name_desktop" = "Desktop";
"device_name_mobile" = "%@ Mobiel";
"device_name_web" = "%@ Web";
"device_name_desktop" = "%@ Desktop";
"user_inactive_session_item_with_date" = "Meer dan 90 dagen inactief (%@)";
"user_inactive_session_item" = "90+ dagen inactief";
"user_session_item_details_last_activity" = "Laatste activiteit %@";
/* %1$@ will be the verification state and %2$@ will be user_session_item_details_verification_unknown or user_other_session_current_session_details */
"user_session_item_details" = "%1$@ · %2$@";
// First item is client name and second item is session display name
"user_session_name" = "%@: %@";
"user_other_session_menu_select_sessions" = "Selecteer sessies";
"user_other_session_selected_count" = "%@ geselecteerd";
"user_other_session_clear_filter" = "Leeg filter";
"user_other_session_no_unverified_sessions" = "Geen niet geverifieerde sessies gevonden.";
"user_other_session_no_verified_sessions" = "Geen geverifieerde sessies gevonden.";
"user_other_session_no_inactive_sessions" = "Geen inactieve sessies gevonden.";
"user_other_session_filter_menu_inactive" = "Inactief";
"user_other_session_filter_menu_unverified" = "Niet geverifieerd";
"user_other_session_filter_menu_verified" = "Geverifieerd";
"user_other_session_filter_menu_all" = "Alle sessies";
"user_other_session_filter" = "Filter";
"user_other_session_verified_sessions_header_subtitle" = "Voor de beste beveiliging logt u uit bij elke sessie die u niet meer herkent of gebruikt.";
"user_other_session_current_session_details" = "Uw huidige sessie";
"user_other_session_unverified_sessions_header_subtitle" = "Verifieer uw sessies voor verbeterde beveiligde berichtenuitwisseling of meld u af bij sessies die u niet meer herkent of gebruikt.";
"user_other_session_security_recommendation_title" = "Beveiligingsaanbeveling";
"user_session_push_notifications_message" = "Indien ingeschakeld, ontvangt deze sessie pushmeldingen.";
"user_session_push_notifications" = "Pushmeldingen";
"user_other_session_verified_additional_info" = "Deze sessie is klaar voor beveiligde berichtenuitwisseling.";
"user_other_session_unverified_additional_info" = "Verifieer of meld u af bij deze sessie voor de beste beveiliging en betrouwbaarheid.";
"user_session_verification_unknown_additional_info" = "Verifieer uw huidige sessie om de verificatiestatus van deze sessie weer te geven.";
"user_session_unverified_additional_info" = "Verifieer uw huidige sessie voor verbeterde beveiligde berichtenuitwisseling.";
"user_session_verified_additional_info" = "Uw huidige sessie is klaar voor beveiligde berichtenuitwisseling.";
"user_session_learn_more" = "Meer lezen";
"user_session_view_details" = "Bekijk details";
"user_session_verify_action" = "Sessie verifiëren";
"user_session_verification_unknown_short" = "Onbekend";
"user_session_unverified_short" = "Niet geverifieerd";
"user_session_verified_short" = "Geverifieerd";
"user_session_verification_unknown" = "Onbekende verificatiestatus";
"user_session_unverified" = "Niet geverifieerde sessie";
"user_session_verified" = "Geverifieerde sessie";
"user_sessions_view_all_action" = "Alles bekijken (%d)";
"user_sessions_overview_link_device" = "Een apparaat koppelen";
"user_sessions_overview_current_session_section_title" = "Huidige sessie";
"user_sessions_overview_other_sessions_section_info" = "Voor de beste beveiliging verifieert u uw sessies en meldt u zich af bij elke sessie die u niet meer herkent of gebruikt.";
"user_sessions_overview_other_sessions_section_title" = "Andere sessies";
"user_sessions_overview_security_recommendations_inactive_info" = "Overweeg om u af te melden bij oude sessies (90 dagen of ouder) die u niet meer gebruikt.";
"user_sessions_overview_security_recommendations_inactive_title" = "Inactieve sessies";
"user_sessions_overview_security_recommendations_unverified_info" = "Verifieer of meld u af bij niet geverifieerde sessies.";
"user_sessions_overview_security_recommendations_unverified_title" = "Niet geverifieerde sessies";
"user_sessions_overview_security_recommendations_section_info" = "Verbeter uw accountbeveiliging door deze aanbevelingen op te volgen.";
"user_sessions_overview_security_recommendations_section_title" = "Beveiligingsaanbevelingen";
// MARK: User sessions management
// Parameter is the application display name (e.g. "Element")
"user_sessions_default_session_display_name" = "%@ iOS";
"all_chats_user_menu_accessibility_label" = "Gebruikersmenu";
"voice_broadcast_playback_loading_error" = "Kan deze spraakuitzending niet afspelen.";
"voice_broadcast_already_in_progress_message" = "U neemt al een spraakuitzending op. Beëindig uw huidige spraakuitzending om een nieuwe te starten.";
"voice_broadcast_blocked_by_someone_else_message" = "Iemand anders neemt al een spraakuitzending op. Wacht tot hun spraakuitzending is afgelopen om een nieuwe te starten.";
"voice_broadcast_permission_denied_message" = "U heeft niet de vereiste rechten om een spraakuitzending in deze kamer te starten. Neem contact op met een kamer beheerder om uw machtigingen te upgraden.";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Kan geen nieuwe spraakuitzending starten";
"sign_out_confirmation_message" = "Weet u zeker dat u zich wilt afmelden?";
// MARK: Sign out warning
"sign_out" = "Afmelden";
"manage_session_rename" = "Sessie hernoemen";
"manage_session_name_info_link" = "Lees meer";
/* The placeholder will be replaces with manage_session_name_info_link */
"manage_session_name_info" = "Houd er rekening mee dat sessienamen ook zichtbaar zijn voor mensen met wie u communiceert. %@";
"manage_session_name_hint" = "Met aangepaste sessienamen kunt u uw apparaten gemakkelijker herkennen.";
"settings_labs_enable_voice_broadcast" = "Voice-uitzending (in actieve ontwikkeling)";
"settings_labs_enable_wysiwyg_composer" = "Probeer de rich-text-editor (platte tekst-modus komt binnenkort)";
"settings_labs_enable_new_session_manager" = "Nieuwe sessiemanager";
"room_first_message_placeholder" = "Stuur uw eerste bericht…";
"authentication_qr_login_failure_retry" = "Probeer het nog eens";
"authentication_qr_login_failure_request_timed_out" = "De koppeling is niet binnen de vereiste tijd voltooid.";
"authentication_qr_login_failure_request_denied" = "Het verzoek is geweigerd op het andere apparaat.";
"authentication_qr_login_failure_invalid_qr" = "QR-code is ongeldig.";
"authentication_qr_login_failure_title" = "Koppelen mislukt";
"authentication_qr_login_loading_signed_in" = "U bent nu aangemeld op uw andere apparaat.";
"authentication_qr_login_loading_waiting_signin" = "Wachten tot het apparaat zich aanmeldt.";
"authentication_qr_login_loading_connecting_device" = "Verbinden met apparaat";
"authentication_qr_login_confirm_alert" = "Zorg ervoor dat u de herkomst van deze code kent. Door apparaten te koppelen, geeft u iemand volledige toegang tot uw account.";
"authentication_qr_login_confirm_subtitle" = "Controleer of de onderstaande code overeenkomt met uw andere apparaat:";
"authentication_qr_login_confirm_title" = "Beveiligde verbinding tot stand gebracht";
"authentication_qr_login_scan_subtitle" = "Positioneer de QR-code in het vierkant hieronder";
"authentication_qr_login_scan_title" = "Scan QR-code";
"authentication_qr_login_display_step2" = "Selecteer 'Aanmelden met QR-code'";
"authentication_qr_login_display_step1" = "Open Element op uw andere apparaat";
"authentication_qr_login_display_subtitle" = "Scan de onderstaande QR-code met uw apparaat dat is uitgelogd.";
"authentication_qr_login_display_title" = "Een apparaat koppelen";
"authentication_qr_login_start_display_qr" = "QR-code weergeven op dit apparaat";
"authentication_qr_login_start_need_alternative" = "Een alternatieve methode nodig?";
"authentication_qr_login_start_step4" = "Selecteer 'Toon QR-code op dit apparaat'";
"authentication_qr_login_start_step3" = "Selecteer 'Een apparaat koppelen'";
"authentication_qr_login_start_step2" = "Ga naar Instellingen -> Beveiliging en privacy";
"authentication_qr_login_start_step1" = "Open Element op uw andere apparaat";
"authentication_qr_login_start_subtitle" = "Gebruik de camera op dit apparaat om de QR-code te scannen die op uw andere apparaat wordt weergegeven:";
"authentication_qr_login_start_title" = "Scan QR-code";
"authentication_login_with_qr" = "Log in met QR-code";

View File

@@ -751,7 +751,7 @@
"group_participants_invited_section" = "ZAPROSZONY";
"receipt_status_read" = "Odczytano: ";
// Media picker
"media_picker_title" = "Selektor mediów";
"media_picker_title" = "Biblioteka mediów";
// Image picker
"image_picker_action_camera" = "Zrób zdjęcie";
"image_picker_action_library" = "Wybierz z biblioteki";
@@ -2569,7 +2569,7 @@
// Mark: - All Chats
"all_chats_title" = "Wszystkie rozmowy";
"all_chats_title" = "Rozmowy";
"spaces_subspace_creation_visibility_message" = "Utworzona przestrzeń zostanie dodana do %@.";
"spaces_subspace_creation_visibility_title" = "Jakiego rodzaju podprzestrzeń chcesz utworzyć?";
"spaces_explore_rooms_format" = "Przeglądaj %@";

View File

@@ -1688,7 +1688,7 @@
"invite_user" = "Convidar Usuária(o) matrix";
"reset_to_default" = "Resettar para default";
"resend_message" = "Reenviar a mensagem";
"select_all" = "Selecionar Todas";
"select_all" = "Selecionar Todas(os)";
"cancel_upload" = "Cancelar Upload";
"cancel_download" = "Cancelar Download";
"show_details" = "Mostrar Detalhes";
@@ -2606,3 +2606,15 @@
"manage_session_name_info" = "Por favor esteja ciente que nomes de sessões também são visíveis a pessoas com quem você se comunica. %@";
"manage_session_name_hint" = "Nomes de sessões personalizados podem ajudar você a reconhecer seus dispositivos mais facilmente.";
"settings_labs_enable_wysiwyg_composer" = "Experimente o editor de texto rico (modo de texto puro vindo em breve)";
"wysiwyg_composer_start_action_voice_broadcast" = "Broadcast de voz";
"voice_broadcast_playback_loading_error" = "Incapaz de tocar este broadcast de voz.";
"voice_broadcast_already_in_progress_message" = "Você já está gravando um broadcast de voz. Por favor termine seu broadcast de voz atual para começar um novo.";
"voice_broadcast_blocked_by_someone_else_message" = "Alguma outra pessoa já está gravando um broadcast de voz. Espere que o broadcast de voz dela termine para começar um novo.";
"voice_broadcast_permission_denied_message" = "Você não tem as permissões requeridas para começar um broadcast de voz nesta sala. Contacte um(a) administrador(a) da sala para fazer upgrade de suas permissões.";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Não dá para começar um novo broadcast de voz";
"settings_labs_enable_voice_broadcast" = "Broadcast de voz (sob desenvolvimento ativo)";
"deselect_all" = "Desselecionar Todas(os)";
"user_other_session_menu_select_sessions" = "Selecionar sessões";
"user_other_session_selected_count" = "%@ selecionadas";

View File

@@ -603,7 +603,7 @@
"key_backup_recover_from_passphrase_passphrase_title" = "Ввод";
"key_backup_recover_from_passphrase_passphrase_placeholder" = "Введите секретную фразу";
"key_backup_recover_from_passphrase_recover_action" = "Разблокировать историю";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Не знаете вашу секретную фразу для восстановления? Вы можете ";
"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Не помните свою мнемоническую фразу? Вы можете ";
"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "использовать ключ безопасности";
"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = ".";
"key_backup_recover_from_recovery_key_info" = "Используйте ключ безопасности для разблокировки истории безопасных сообщений";
@@ -624,7 +624,7 @@
"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Ключ безопасности";
"key_backup_setup_success_from_recovery_key_make_copy_action" = "Сделать копию";
"key_backup_setup_success_from_recovery_key_made_copy_action" = "Я сделал копию";
"key_backup_recover_invalid_passphrase_title" = "Неверная секретная фраза для восстановления";
"key_backup_recover_invalid_passphrase_title" = "Неверная мнемоническая фраза";
"key_backup_recover_invalid_recovery_key_title" = "Несоответствующий ключ безопасности";
"key_backup_setup_banner_title" = "Не теряйте зашифрованные сообщения";
"key_backup_setup_banner_subtitle" = "Начать использовать ключ восстановления";
@@ -641,7 +641,7 @@
"key_backup_setup_intro_setup_action_with_existing_backup" = "Использовать ключ восстановления";
"settings_key_backup_info" = "Зашифрованные сообщения защищены сквозным шифрованием. Только вы и получатель(и) имеют ключи для чтения этих сообщений.";
"settings_key_backup_info_signout_warning" = "Сделайте резервную копию ключей перед выходом, чтобы не потерять их.";
"key_backup_setup_passphrase_title" = "Защитите резервную копию секретной фразой";
"key_backup_setup_passphrase_title" = "Защитите резервную копию мнемонической фразой";
"key_backup_setup_passphrase_setup_recovery_key_info" = "Или защитите свою резервную копию с помощью ключа безопасности, сохранив ее в безопасном месте.";
"key_backup_setup_passphrase_setup_recovery_key_action" = "(Расширенный) Настройка с ключом безопасности";
// Success from passphrase
@@ -654,7 +654,7 @@
"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Зашифрованные сообщения будут утеряны";
"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Мне не нужны мои зашифрованные сообщения";
"sign_out_non_existing_key_backup_alert_title" = "Вы потеряете доступ к зашифрованным сообщениям если выйдете сейчас";
"key_backup_recover_invalid_passphrase" = "Невозможно расшифровать резервную копию с помощью этой секретной фразы: убедитесь, что вы ввели верную секретную фразу для восстановления.";
"key_backup_recover_invalid_passphrase" = "Невозможно расшифровать резервную копию с помощью этой фразы: убедитесь, что вы ввели верную мнемоническую фразу.";
"key_backup_recover_invalid_recovery_key" = "Невозможно расшифровать резервную копию с помощью этого ключа: убедитесь, что вы ввели верный ключ безопасности.";
"e2e_key_backup_wrong_version_button_settings" = "Настройки";
"key_backup_setup_intro_manual_export_info" = "(Расширенный)";
@@ -986,7 +986,7 @@
"secure_key_backup_setup_intro_info" = "Защитите себя от потери доступа к зашифрованным сообщениям и данным, создав резервную копию ключей шифрования на своём сервере.";
"secure_key_backup_setup_intro_use_security_key_title" = "Используйте ключ безопасности";
"secure_key_backup_setup_intro_use_security_key_info" = "Создайте ключ безопасности для хранения в надежном месте, например в менеджере паролей или сейфе.";
"secure_key_backup_setup_intro_use_security_passphrase_title" = "Использовать секретную фразу";
"secure_key_backup_setup_intro_use_security_passphrase_title" = "Использовать мнемоническую фразу";
"secure_key_backup_setup_intro_use_security_passphrase_info" = "Введите секретную фразу, известную только вам, и создайте ключ для резервного копирования.";
"secure_key_backup_setup_existing_backup_error_title" = "Резервная копия сообщений уже существует";
"secure_key_backup_setup_existing_backup_error_info" = "Разблокируйте его для повторного использования в защищенной резервной копии или удалите для создания новой резервной копии сообщений в защищенной резервной копии.";
@@ -1024,7 +1024,7 @@
"device_verification_self_verify_wait_information" = "Подтвердите этот сеанс на одном из других ваших сеансов, предоставив ему доступ к зашифрованным сообщениям.\n\nИспользуйте последнюю версию %@ на других ваших устройствах:";
"device_verification_self_verify_wait_additional_information" = "Это работает с %@ и другими клиентами Matrix с поддержкой кросс-подписи.";
"device_verification_self_verify_wait_recover_secrets_without_passphrase" = "Используйте ключ безопасности";
"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Используйте секретную фразу или ключ безопасности";
"device_verification_self_verify_wait_recover_secrets_with_passphrase" = "Используйте мнемоническую фразу или бумажный ключ";
"device_verification_self_verify_wait_recover_secrets_additional_information" = "Если вы не можете получить доступ к существующему сеансу";
"key_verification_verify_sas_title_emoji" = "Сравните смайлы";
"key_verification_verify_sas_title_number" = "Сравните числа";
@@ -1102,17 +1102,17 @@
"user_verification_session_details_verify_action_current_user" = "Интерактивная проверка";
"user_verification_session_details_verify_action_current_user_manually" = "Ручная проверка с помощью текста";
"user_verification_session_details_verify_action_other_user" = "Подтверждение вручную";
"secrets_recovery_with_passphrase_title" = "Секретная фраза";
"secrets_recovery_with_passphrase_title" = "Мнемоническая фраза";
"secrets_recovery_with_passphrase_information_default" = "Получите доступ к своей защищённой истории сообщений и вашей личности с кросс-подписью для проверки других сеансов, введя секретную фразу.";
"secrets_recovery_with_passphrase_information_verify_device" = "Используйте секретную фразу, чтобы проверить это устройство.";
"secrets_recovery_with_passphrase_information_verify_device" = "Используйте свою мнемоническую фразу, чтобы заверить эту сессию.";
"secrets_recovery_with_passphrase_passphrase_title" = "Ввод";
"secrets_recovery_with_passphrase_passphrase_placeholder" = "Введите секретную фразу";
"secrets_recovery_with_passphrase_passphrase_placeholder" = "Введите мнемоническую фразу";
"secrets_recovery_with_passphrase_recover_action" = "Использовать секретную фразу";
"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Не знаете вашу секретную фразу? Вы можете ";
"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "использовать ключ безопасности";
"secrets_recovery_with_passphrase_lost_passphrase_action_part1" = "Не помните свою мнемоническую фразу? Вы можете ";
"secrets_recovery_with_passphrase_lost_passphrase_action_part2" = "использовать бумажный ключ";
"secrets_recovery_with_passphrase_lost_passphrase_action_part3" = ".";
"secrets_recovery_with_passphrase_invalid_passphrase_title" = "Невозможно получить доступ к секретному хранилищу";
"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Убедитесь, что вы ввели правильную секретную фразу.";
"secrets_recovery_with_passphrase_invalid_passphrase_message" = "Убедитесь, что вы ввели верную мнемоническую фразу.";
"secrets_recovery_with_key_title" = "Ключ безопасности";
"secrets_recovery_with_key_information_default" = "Получите доступ к своей защищённой истории сообщений и вашей личности с кросс-подписью для проверки других сеансов, введя ключ безопасности.";
"secrets_recovery_with_key_information_verify_device" = "Используйте ключ безопасности, чтобы проверить это устройство.";
@@ -1128,11 +1128,11 @@
"secrets_setup_recovery_key_done_action" = "Готово";
"secrets_setup_recovery_key_storage_alert_title" = "Храните его в безопасности";
"secrets_setup_recovery_key_storage_alert_message" = "✓ Распечатайте и храните в безопасном месте\n✓ Сохраните его на USB-носителе или резервном носителе\n✓ Скопируйте его в свое личное облачное хранилище";
"secrets_setup_recovery_passphrase_title" = "Задайте секретную фразу";
"secrets_setup_recovery_passphrase_title" = "Задайте мнемоническую фразу";
"secrets_setup_recovery_passphrase_information" = "Введите секретную фразу, известную только вам, для защиты данных на вашем сервере.";
"secrets_setup_recovery_passphrase_additional_information" = "Не используйте пароль своей учетной записи.";
"secrets_setup_recovery_passphrase_validate_action" = "Готово";
"secrets_setup_recovery_passphrase_confirm_information" = "Для подтверждения введите вашу секретную фразу ещё раз.";
"secrets_setup_recovery_passphrase_confirm_information" = "Введите мнемоническую фразу ещё раз, чтобы подтвердить её.";
"secrets_setup_recovery_passphrase_confirm_passphrase_title" = "Подтвердить";
"secrets_setup_recovery_passphrase_confirm_passphrase_placeholder" = "Подтвердить секретную фразу";
"cross_signing_setup_banner_title" = "Настройка шифрования";
@@ -1238,8 +1238,8 @@
// MARK: - Home
"home_empty_view_title" = "Добро пожаловать в %@,\n%@";
"secrets_setup_recovery_passphrase_summary_information" = "Запомните свою секретную фразу. Её можно использовать для разблокировки ваших зашифрованных сообщений и данных.";
"secrets_setup_recovery_passphrase_summary_title" = "Сохраните вашу секретную фразу";
"secrets_setup_recovery_passphrase_summary_information" = "Запомните свою мнемоническую фразу. Её можно использовать для разблокировки ваших зашифрованных сообщений и данных.";
"secrets_setup_recovery_passphrase_summary_title" = "Сохраните свою мнемоническую фразу";
"favourites_empty_view_information" = "Вы можете добавить в избранное несколькими способами - самый быстрый - просто нажать и удерживать. Нажмите на звёздочку, и они автоматически появятся здесь, и вы их навсегда сохраните.";
// MARK: - Favourites
@@ -1355,7 +1355,7 @@
"space_feature_unavailable_title" = "Пространств ещё нет";
"secrets_recovery_with_key_information_unlock_secure_backup_with_key" = "Введите свой ключ безопасности, чтобы продолжить.";
"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Введите секретную фразу, чтобы продолжить.";
"secrets_recovery_with_key_information_unlock_secure_backup_with_phrase" = "Введите мнемоническую фразу, чтобы продолжить.";
"key_verification_verify_qr_code_scan_code_other_device_action" = "Сканирование с помощью этого устройства";
// Success from secure backup

View File

@@ -2828,3 +2828,15 @@
"manage_session_name_info" = "Uvedomte si, že názvy relácií sú viditeľné aj pre ľudí, s ktorými komunikujete. %@";
"manage_session_name_hint" = "Vlastné názvy relácií vám pomôžu ľahšie rozpoznať vaše zariadenia.";
"settings_labs_enable_wysiwyg_composer" = "Vyskúšajte rozšírený textový editor (čistý textový režim sa objaví čoskoro)";
"wysiwyg_composer_start_action_voice_broadcast" = "Hlasové vysielanie";
"voice_broadcast_already_in_progress_message" = "Už nahrávate hlasové vysielanie. Ukončite aktuálne hlasové vysielanie a spustite nové.";
"voice_broadcast_blocked_by_someone_else_message" = "Niekto iný už nahráva hlasové vysielanie. Počkajte, kým sa skončí jeho hlasové vysielanie, a potom spustite nové.";
"voice_broadcast_permission_denied_message" = "Nemáte požadované oprávnenia na spustenie hlasového vysielania v tejto miestnosti. Obráťte sa na správcu miestnosti, aby vám rozšíril oprávnenia.";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Nie je možné spustiť nové hlasové vysielanie";
"settings_labs_enable_voice_broadcast" = "Hlasové vysielanie (v štádiu aktívneho vývoja)";
"voice_broadcast_playback_loading_error" = "Toto hlasové vysielanie nie je možné prehrať.";
"deselect_all" = "Zrušiť výber všetkých";
"user_other_session_selected_count" = "%@ vybratých";
"user_other_session_menu_select_sessions" = "Vyberte relácie";

View File

@@ -2830,3 +2830,15 @@
"manage_session_name_info" = "Зауважте, що назви сеансів також видно людям, з якими ви спілкуєтесь. %@";
"manage_session_name_hint" = "Власні назви сеансів допоможуть вам легше розпізнавати ваші пристрої.";
"settings_labs_enable_wysiwyg_composer" = "Спробуйте розширений текстовий редактор (незабаром з'явиться режим звичайного тексту)";
"wysiwyg_composer_start_action_voice_broadcast" = "Голосові повідомлення";
"voice_broadcast_playback_loading_error" = "Неможливо відтворити це голосове повідомлення.";
"voice_broadcast_already_in_progress_message" = "Ви вже записуєте голосове повідомлення. Завершіть поточну трансляцію, щоб розпочати нову.";
"voice_broadcast_blocked_by_someone_else_message" = "Хтось інший вже записує голосове повідомлення. Зачекайте, поки закінчиться трансляція, щоб розпочати нову.";
"voice_broadcast_permission_denied_message" = "Ви не маєте необхідних дозволів для початку трансляції голосового повідомлення в цій кімнаті. Зверніться до адміністратора кімнати, щоб оновити ваші дозволи.";
// Mark: - Voice broadcast
"voice_broadcast_unauthorized_title" = "Не вдалося розпочати трансляцію нового голосового повідомлення";
"settings_labs_enable_voice_broadcast" = "Голосові повідомлення (в активній розробці)";
"deselect_all" = "Скасувати вибір усіх";
"user_other_session_menu_select_sessions" = "Вибрати сеанси";
"user_other_session_selected_count" = "Вибрано %@";

View File

@@ -329,7 +329,7 @@
{
if (self.mxSession.crypto)
{
[self.mxSession.crypto trustLevelSummaryForUserIds:@[userId] onComplete:^(MXUsersTrustLevelSummary *usersTrustLevelSummary) {
[self.mxSession.crypto trustLevelSummaryForUserIds:@[userId] forceDownload:NO success:^(MXUsersTrustLevelSummary *usersTrustLevelSummary) {
UserEncryptionTrustLevel userEncryptionTrustLevel;
double trustedDevicesPercentage = usersTrustLevelSummary.trustedDevicesProgress.fractionCompleted;
@@ -341,7 +341,7 @@
else if (trustedDevicesPercentage == 0.0)
{
// Verify if the user has the user has cross-signing enabled
if ([self.mxSession.crypto crossSigningKeysForUser:userId])
if ([self.mxSession.crypto.crossSigning crossSigningKeysForUser:userId])
{
userEncryptionTrustLevel = UserEncryptionTrustLevelNotVerified;
}
@@ -357,6 +357,9 @@
onComplete(userEncryptionTrustLevel);
} failure:^(NSError *error) {
MXLogErrorDetails(@"[MXRoom+Riot] Error fetching trust level summary", error);
onComplete(UserEncryptionTrustLevelUnknown);
}];
}
else

View File

@@ -51,5 +51,16 @@ extension UITableViewCell {
@objc func vc_setAccessoryDisclosureIndicatorWithCurrentTheme() {
self.vc_setAccessoryDisclosureIndicator(withTheme: ThemeService.shared().theme)
}
@objc var vc_parentViewController: UIViewController? {
var parent: UIResponder? = self
while parent != nil {
parent = parent?.next
if let viewController = parent as? UIViewController {
return viewController
}
}
return nil
}
}

View File

@@ -22,7 +22,10 @@ extension UITextView {
self.attributedText.enumerateAttribute(
.attachment,
in: NSRange(location: 0, length: self.attributedText.length),
options: []) { _, range, _ in
options: []) { value, range, _ in
guard value != nil else {
return
}
self.layoutManager.invalidateDisplay(forCharacterRange: range)
}
}

View File

@@ -127,6 +127,8 @@ internal class Asset: NSObject {
internal static let userOtherSessionsUnverified = ImageAsset(name: "user_other_sessions_unverified")
internal static let userOtherSessionsVerified = ImageAsset(name: "user_other_sessions_verified")
internal static let userSessionListItemInactiveSession = ImageAsset(name: "user_session_list_item_inactive_session")
internal static let userSessionListItemNotSelected = ImageAsset(name: "user_session_list_item_not_selected")
internal static let userSessionListItemSelected = ImageAsset(name: "user_session_list_item_selected")
internal static let userSessionUnverified = ImageAsset(name: "user_session_unverified")
internal static let userSessionVerificationUnknown = ImageAsset(name: "user_session_verification_unknown")
internal static let userSessionVerified = ImageAsset(name: "user_session_verified")
@@ -335,6 +337,12 @@ internal class Asset: NSObject {
internal static let tabHome = ImageAsset(name: "tab_home")
internal static let tabPeople = ImageAsset(name: "tab_people")
internal static let tabRooms = ImageAsset(name: "tab_rooms")
internal static let voiceBroadcastLive = ImageAsset(name: "voice_broadcast_live")
internal static let voiceBroadcastPause = ImageAsset(name: "voice_broadcast_pause")
internal static let voiceBroadcastPlay = ImageAsset(name: "voice_broadcast_play")
internal static let voiceBroadcastRecord = ImageAsset(name: "voice_broadcast_record")
internal static let voiceBroadcastRecordPause = ImageAsset(name: "voice_broadcast_record_pause")
internal static let voiceBroadcastStop = ImageAsset(name: "voice_broadcast_stop")
internal static let launchScreenLogo = ImageAsset(name: "launch_screen_logo")
}
@objcMembers

View File

@@ -1523,6 +1523,10 @@ public class VectorL10n: NSObject {
public static var delete: String {
return VectorL10n.tr("Vector", "delete")
}
/// Deselect All
public static var deselectAll: String {
return VectorL10n.tr("Vector", "deselect_all")
}
/// This operation requires additional authentication.\nTo continue, please enter your password.
public static var deviceDetailsDeletePromptMessage: String {
return VectorL10n.tr("Vector", "device_details_delete_prompt_message")
@@ -7535,7 +7539,7 @@ public class VectorL10n: NSObject {
public static var settingsLabsEnableThreads: String {
return VectorL10n.tr("Vector", "settings_labs_enable_threads")
}
/// Voice broadcast (under active development). We currently only detect voice broadcast in the room timeline, this is not possible to send or listen an actual voice broadcast
/// Voice broadcast (under active development)
public static var settingsLabsEnableVoiceBroadcast: String {
return VectorL10n.tr("Vector", "settings_labs_enable_voice_broadcast")
}
@@ -8671,6 +8675,10 @@ public class VectorL10n: NSObject {
public static var userOtherSessionFilterMenuVerified: String {
return VectorL10n.tr("Vector", "user_other_session_filter_menu_verified")
}
/// Select sessions
public static var userOtherSessionMenuSelectSessions: String {
return VectorL10n.tr("Vector", "user_other_session_menu_select_sessions")
}
/// No inactive sessions found.
public static var userOtherSessionNoInactiveSessions: String {
return VectorL10n.tr("Vector", "user_other_session_no_inactive_sessions")
@@ -8687,6 +8695,10 @@ public class VectorL10n: NSObject {
public static var userOtherSessionSecurityRecommendationTitle: String {
return VectorL10n.tr("Vector", "user_other_session_security_recommendation_title")
}
/// %@ selected
public static func userOtherSessionSelectedCount(_ p1: String) -> String {
return VectorL10n.tr("Vector", "user_other_session_selected_count", p1)
}
/// Verify or sign out from this session for best security and reliability.
public static var userOtherSessionUnverifiedAdditionalInfo: String {
return VectorL10n.tr("Vector", "user_other_session_unverified_additional_info")
@@ -9051,6 +9063,26 @@ public class VectorL10n: NSObject {
public static var voice: String {
return VectorL10n.tr("Vector", "voice")
}
/// You are already recording a voice broadcast. Please end your current voice broadcast to start a new one.
public static var voiceBroadcastAlreadyInProgressMessage: String {
return VectorL10n.tr("Vector", "voice_broadcast_already_in_progress_message")
}
/// Someone else is already recording a voice broadcast. Wait for their voice broadcast to end to start a new one.
public static var voiceBroadcastBlockedBySomeoneElseMessage: String {
return VectorL10n.tr("Vector", "voice_broadcast_blocked_by_someone_else_message")
}
/// You don't have the required permissions to start a voice broadcast in this room. Contact a room administrator to upgrade your permissions.
public static var voiceBroadcastPermissionDeniedMessage: String {
return VectorL10n.tr("Vector", "voice_broadcast_permission_denied_message")
}
/// Unable to play this voice broadcast.
public static var voiceBroadcastPlaybackLoadingError: String {
return VectorL10n.tr("Vector", "voice_broadcast_playback_loading_error")
}
/// Can't start a new voice broadcast
public static var voiceBroadcastUnauthorizedTitle: String {
return VectorL10n.tr("Vector", "voice_broadcast_unauthorized_title")
}
/// Voice message
public static var voiceMessageLockScreenPlaceholder: String {
return VectorL10n.tr("Vector", "voice_message_lock_screen_placeholder")
@@ -9207,6 +9239,10 @@ public class VectorL10n: NSObject {
public static var wysiwygComposerStartActionTextFormatting: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_text_formatting")
}
/// Voice broadcast
public static var wysiwygComposerStartActionVoiceBroadcast: String {
return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_voice_broadcast")
}
/// Yes
public static var yes: String {
return VectorL10n.tr("Vector", "yes")

View File

@@ -14,10 +14,6 @@ public extension VectorL10n {
static var imagePickerActionFiles: String {
return VectorL10n.tr("Untranslated", "image_picker_action_files")
}
/// We currently only detect voice broadcast in the room timeline, this is not possible to send or listen an actual voice broadcast
static var voiceBroadcastInTimelineBody: String {
return VectorL10n.tr("Untranslated", "voice_broadcast_in_timeline_body")
}
/// Voice broadcast detected (under active development)
static var voiceBroadcastInTimelineTitle: String {
return VectorL10n.tr("Untranslated", "voice_broadcast_in_timeline_title")

View File

@@ -195,7 +195,9 @@ UINavigationControllerDelegate
- (BOOL)presentIncomingKeyVerificationRequest:(id<MXKeyVerificationRequest>)incomingKeyVerificationRequest
inSession:(MXSession*)session;
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember session:(MXSession*)mxSession;
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember
session:(MXSession*)mxSession
completion:(void (^)(void))completion;
- (BOOL)presentCompleteSecurityForSession:(MXSession*)mxSession;

View File

@@ -128,6 +128,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
If any the currently displayed key verification dialog
*/
KeyVerificationCoordinatorBridgePresenter *keyVerificationCoordinatorBridgePresenter;
/**
Completion block for the requester of key verification
*/
void (^keyVerificationCompletionBlock)(void);
/**
Currently displayed secure backup setup
@@ -610,6 +615,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Analytics: Force to send the pending actions
[[DecryptionFailureTracker sharedInstance] dispatch];
[Analytics.shared forceUpload];
// Pause Voice Broadcast recording if needed
[VoiceBroadcastRecorderProvider.shared pauseRecording];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
@@ -2280,9 +2288,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Stay in launching during the first server sync if the store is empty.
isLaunching = (mainSession.rooms.count == 0 && launchAnimationContainerView);
if (mainSession.crypto.crossSigning && mainSession.crypto.crossSigning.state == MXCrossSigningStateCrossSigningExists)
if (mainSession.crypto.crossSigning && mainSession.crypto.crossSigning.state == MXCrossSigningStateCrossSigningExists && [mainSession.crypto isKindOfClass:[MXLegacyCrypto class]])
{
[mainSession.crypto setOutgoingKeyRequestsEnabled:NO onComplete:nil];
[(MXLegacyCrypto *)mainSession.crypto setOutgoingKeyRequestsEnabled:NO onComplete:nil];
}
break;
case MXSessionStateRunning:
@@ -2495,6 +2503,12 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
- (void)checkLocalPrivateKeysInSession:(MXSession*)mxSession
{
if (![mxSession.crypto isKindOfClass:[MXLegacyCrypto class]])
{
return;
}
MXLegacyCrypto *crypto = (MXLegacyCrypto *)mxSession.crypto;
MXRecoveryService *recoveryService = mxSession.crypto.recoveryService;
NSUInteger keysCount = 0;
if ([recoveryService hasSecretWithSecretId:MXSecretId.keyBackup])
@@ -2515,7 +2529,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
{
// We should have 3 of them. If not, request them again as mitigation
MXLogDebug(@"[AppDelegate] checkLocalPrivateKeysInSession: request keys because keysCount = %@", @(keysCount));
[mxSession.crypto requestAllPrivateKeys];
[crypto requestAllPrivateKeys];
}
}
@@ -3475,17 +3489,24 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
MXLogDebug(@"[AppDelegate] checkPendingRoomKeyRequestsInSession called while the app is not active. Ignore it.");
return;
}
if (![mxSession.crypto isKindOfClass:[MXLegacyCrypto class]])
{
MXLogDebug(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: Only legacy crypto allows manually accepting/rejecting key requests");
return;
}
MXLegacyCrypto *crypto = (MXLegacyCrypto *)mxSession.crypto;
MXWeakify(self);
[mxSession.crypto pendingKeyRequests:^(MXUsersDevicesMap<NSArray<MXIncomingRoomKeyRequest *> *> *pendingKeyRequests) {
[crypto pendingKeyRequests:^(MXUsersDevicesMap<NSArray<MXIncomingRoomKeyRequest *> *> *pendingKeyRequests) {
MXStrongifyAndReturnIfNil(self);
MXLogDebug(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: cross-signing state: %ld, pendingKeyRequests.count: %@. Already displayed: %@",
mxSession.crypto.crossSigning.state,
crypto.crossSigning.state,
@(pendingKeyRequests.count),
self->roomKeyRequestViewController ? @"YES" : @"NO");
if (!mxSession.crypto.crossSigning || mxSession.crypto.crossSigning.state == MXCrossSigningStateNotBootstrapped)
if (!crypto.crossSigning || crypto.crossSigning.state == MXCrossSigningStateNotBootstrapped)
{
if (self->roomKeyRequestViewController)
{
@@ -3515,13 +3536,13 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// Give the client a chance to refresh the device list
MXWeakify(self);
[mxSession.crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
[crypto downloadKeys:@[userId] forceDownload:NO success:^(MXUsersDevicesMap<MXDeviceInfo *> *usersDevicesInfoMap, NSDictionary<NSString *,MXCrossSigningInfo *> *crossSigningKeysMap) {
MXStrongifyAndReturnIfNil(self);
MXDeviceInfo *deviceInfo = [usersDevicesInfoMap objectForDevice:deviceId forUser:userId];
if (deviceInfo)
{
if (!mxSession.crypto.crossSigning || mxSession.crypto.crossSigning.state == MXCrossSigningStateNotBootstrapped)
if (!crypto.crossSigning || crypto.crossSigning.state == MXCrossSigningStateNotBootstrapped)
{
BOOL wasNewDevice = (deviceInfo.trustLevel.localVerificationStatus == MXDeviceUnknown);
@@ -3529,7 +3550,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
{
MXLogDebug(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: Open dialog for %@", deviceInfo);
self->roomKeyRequestViewController = [[RoomKeyRequestViewController alloc] initWithDeviceInfo:deviceInfo wasNewDevice:wasNewDevice andMatrixSession:mxSession onComplete:^{
self->roomKeyRequestViewController = [[RoomKeyRequestViewController alloc] initWithDeviceInfo:deviceInfo wasNewDevice:wasNewDevice andMatrixSession:mxSession crypto:crypto onComplete:^{
self->roomKeyRequestViewController = nil;
@@ -3543,7 +3564,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
// If the device was new before, it's not any more.
if (wasNewDevice)
{
[mxSession.crypto setDeviceVerification:MXDeviceUnverified forDevice:deviceId ofUser:userId success:openDialog failure:nil];
[crypto setDeviceVerification:MXDeviceUnverified forDevice:deviceId ofUser:userId success:openDialog failure:nil];
}
else
{
@@ -3552,13 +3573,13 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
}
else if (deviceInfo.trustLevel.isVerified)
{
[mxSession.crypto acceptAllPendingKeyRequestsFromUser:userId andDevice:deviceId onComplete:^{
[crypto acceptAllPendingKeyRequestsFromUser:userId andDevice:deviceId onComplete:^{
[self checkPendingRoomKeyRequests];
}];
}
else
{
[mxSession.crypto ignoreAllPendingKeyRequestsFromUser:userId andDevice:deviceId onComplete:^{
[crypto ignoreAllPendingKeyRequestsFromUser:userId andDevice:deviceId onComplete:^{
[self checkPendingRoomKeyRequests];
}];
}
@@ -3566,7 +3587,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
else
{
MXLogDebug(@"[AppDelegate] checkPendingRoomKeyRequestsInSession: No details found for device %@:%@", userId, deviceId);
[mxSession.crypto ignoreAllPendingKeyRequestsFromUser:userId andDevice:deviceId onComplete:^{
[crypto ignoreAllPendingKeyRequestsFromUser:userId andDevice:deviceId onComplete:^{
[self checkPendingRoomKeyRequests];
}];
}
@@ -3697,7 +3718,9 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
return presented;
}
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember session:(MXSession*)mxSession
- (BOOL)presentUserVerificationForRoomMember:(MXRoomMember*)roomMember
session:(MXSession*)mxSession
completion:(void (^)(void))completion;
{
MXLogDebug(@"[AppDelegate][MXKeyVerification] presentUserVerificationForRoomMember: %@", roomMember);
@@ -3710,6 +3733,8 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
[keyVerificationCoordinatorBridgePresenter presentFrom:self.presentedViewController roomMember:roomMember animated:YES];
presented = YES;
keyVerificationCompletionBlock = completion;
}
else
{
@@ -3741,11 +3766,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
- (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
{
MXCrypto *crypto = coordinatorBridgePresenter.session.crypto;
if (!crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled)
id<MXCrypto> crypto = coordinatorBridgePresenter.session.crypto;
if ([crypto isKindOfClass:[MXLegacyCrypto class]] && (!crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled))
{
MXLogDebug(@"[AppDelegate][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys");
[crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil];
[(MXLegacyCrypto *)crypto setOutgoingKeyRequestsEnabled:YES onComplete:nil];
}
[self dismissKeyVerificationCoordinatorBridgePresenter];
}
@@ -3762,6 +3787,11 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
}];
keyVerificationCoordinatorBridgePresenter = nil;
if (keyVerificationCompletionBlock) {
keyVerificationCompletionBlock();
}
keyVerificationCompletionBlock = nil;
}
#pragma mark - New request
@@ -3981,7 +4011,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
- (void)registerUserDidSignInOnNewDeviceNotificationForSession:(MXSession*)session
{
MXCrossSigning *crossSigning = session.crypto.crossSigning;
id<MXCrossSigning> crossSigning = session.crypto.crossSigning;
if (!crossSigning)
{
@@ -4072,7 +4102,7 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni
- (void)registerDidChangeCrossSigningKeysNotificationForSession:(MXSession*)session
{
MXCrossSigning *crossSigning = session.crypto.crossSigning;
id<MXCrossSigning> crossSigning = session.crypto.crossSigning;
if (!crossSigning)
{

View File

@@ -758,8 +758,8 @@ extension AuthenticationCoordinator: AuthenticationServiceDelegate {
// MARK: - KeyVerificationCoordinatorDelegate
extension AuthenticationCoordinator: KeyVerificationCoordinatorDelegate {
func keyVerificationCoordinatorDidComplete(_ coordinator: KeyVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) {
if let crypto = session?.crypto,
!crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled {
if let crypto = session?.crypto as? MXLegacyCrypto, let backup = crypto.backup,
!backup.hasPrivateKeyInCryptoStore || !backup.enabled {
MXLog.debug("[AuthenticationCoordinator][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys")
crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
}
@@ -810,5 +810,4 @@ extension AuthenticationCoordinator: AuthFallBackViewControllerDelegate {
func authFallBackViewControllerDidClose(_ authFallBackViewController: AuthFallBackViewController) {
dismissFallback()
}
}

View File

@@ -219,8 +219,8 @@ extension LegacyAuthenticationCoordinator: AuthenticationViewControllerDelegate
// MARK: - KeyVerificationCoordinatorDelegate
extension LegacyAuthenticationCoordinator: KeyVerificationCoordinatorDelegate {
func keyVerificationCoordinatorDidComplete(_ coordinator: KeyVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) {
if let crypto = session?.crypto,
!crypto.backup.hasPrivateKeyInCryptoStore || !crypto.backup.enabled {
if let crypto = session?.crypto as? MXLegacyCrypto, let backup = crypto.backup,
!backup.hasPrivateKeyInCryptoStore || !backup.enabled {
MXLog.debug("[LegacyAuthenticationCoordinator][MXKeyVerification] requestAllPrivateKeys: Request key backup private keys")
crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
}

View File

@@ -69,7 +69,7 @@ class SessionVerificationListener {
}
if session.state == .storeDataReady {
if let crypto = session.crypto, crypto.crossSigning != nil {
if let crypto = session.crypto as? MXLegacyCrypto {
// Do not make key share requests while the "Complete security" is not complete.
// If the device is self-verified, the SDK will restore the existing key backup.
// Then, it will re-enable outgoing key share requests
@@ -78,7 +78,8 @@ class SessionVerificationListener {
} else if session.state == .running {
unregisterSessionStateChangeNotification()
if let crypto = session.crypto, let crossSigning = crypto.crossSigning {
if let crypto = session.crypto {
let crossSigning = crypto.crossSigning
crossSigning.refreshState { [weak self] stateUpdated in
guard let self = self else { return }
@@ -100,7 +101,7 @@ class SessionVerificationListener {
self.completion?(.authenticationIsComplete)
} failure: { error in
MXLog.error("[SessionVerificationListener] sessionStateDidChange: Bootstrap failed", context: error)
crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
(crypto as? MXLegacyCrypto)?.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
self.completion?(.authenticationIsComplete)
}
} else {
@@ -110,12 +111,12 @@ class SessionVerificationListener {
self.completion?(.authenticationIsComplete)
} failure: { error in
MXLog.error("[SessionVerificationListener] sessionStateDidChange: Do not know how to bootstrap cross-signing. Skip it.")
crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
(crypto as? MXLegacyCrypto)?.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
self.completion?(.authenticationIsComplete)
}
}
} else {
crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
(crypto as? MXLegacyCrypto)?.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
self.completion?(.authenticationIsComplete)
}
case .crossSigningExists:
@@ -124,12 +125,12 @@ class SessionVerificationListener {
default:
MXLog.debug("[SessionVerificationListener] sessionStateDidChange: Nothing to do")
crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
(crypto as? MXLegacyCrypto)?.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
self.completion?(.authenticationIsComplete)
}
} failure: { [weak self] error in
MXLog.error("[SessionVerificationListener] sessionStateDidChange: Fail to refresh crypto state", context: error)
crypto.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
(crypto as? MXLegacyCrypto)?.setOutgoingKeyRequestsEnabled(true, onComplete: nil)
self?.completion?(.authenticationIsComplete)
}
} else {

View File

@@ -373,7 +373,12 @@ CallAudioRouteMenuViewDelegate>
// Acknowledge the existence of all devices
[self startActivityIndicator];
[self.mainSession.crypto setDevicesKnown:unknownDevices complete:^{
if (![self.mainSession.crypto isKindOfClass:[MXLegacyCrypto class]])
{
MXLogFailure(@"[CallViewController] call: Only legacy crypto supports manual setting of known devices");
return;
}
[(MXLegacyCrypto *)self.mainSession.crypto setDevicesKnown:unknownDevices complete:^{
[self stopActivityIndicator];

View File

@@ -106,19 +106,9 @@ class AvatarView: UIView, Themable {
return
}
let defaultAvatarImage: UIImage?
var defaultAvatarImageContentMode: UIView.ContentMode = .scaleAspectFill
let (defaultAvatarImage, defaultAvatarImageContentMode) = viewData.fallbackImageParameters() ?? (nil, .scaleAspectFill)
updateAvatarImageView(image: defaultAvatarImage, contentMode: defaultAvatarImageContentMode)
switch viewData.fallbackImage {
case .matrixItem(let matrixItemId, let matrixItemDisplayName):
defaultAvatarImage = AvatarGenerator.generateAvatar(forMatrixItem: matrixItemId, withDisplayName: matrixItemDisplayName)
case .image(let image, let contentMode):
defaultAvatarImage = image
defaultAvatarImageContentMode = contentMode ?? .scaleAspectFill
case .none:
defaultAvatarImage = nil
}
if let avatarUrl = viewData.avatarUrl {
avatarImageView.setImageURI(avatarUrl,
withType: nil,
@@ -127,12 +117,9 @@ class AvatarView: UIView, Themable {
with: MXThumbnailingMethodScale,
previewImage: defaultAvatarImage,
mediaManager: viewData.mediaManager)
avatarImageView.contentMode = .scaleAspectFill
avatarImageView.imageView?.contentMode = .scaleAspectFill
updateAvatarContentMode(contentMode: .scaleAspectFill)
} else {
avatarImageView.image = defaultAvatarImage
avatarImageView.contentMode = defaultAvatarImageContentMode
avatarImageView.imageView?.contentMode = defaultAvatarImageContentMode
updateAvatarImageView(image: defaultAvatarImage, contentMode: defaultAvatarImageContentMode)
}
}
@@ -148,6 +135,16 @@ class AvatarView: UIView, Themable {
gestureRecognizer.minimumPressDuration = 0
self.addGestureRecognizer(gestureRecognizer)
}
private func updateAvatarImageView(image: UIImage?, contentMode: UIView.ContentMode) {
avatarImageView?.image = image
updateAvatarContentMode(contentMode: contentMode)
}
private func updateAvatarContentMode(contentMode: UIView.ContentMode) {
avatarImageView?.contentMode = contentMode
avatarImageView?.imageView?.contentMode = contentMode
}
// MARK: - Actions

View File

@@ -29,6 +29,21 @@ struct AvatarViewData: AvatarViewDataProtocol {
/// Matrix media handler if exists
var mediaManager: MXMediaManager?
/// Fallback image used when avatarUrl is nil
var fallbackImage: AvatarFallbackImage?
/// Fallback images used when avatarUrl is nil
var fallbackImages: [AvatarFallbackImage]?
}
extension AvatarViewData {
init(matrixItemId: String,
displayName: String? = nil,
avatarUrl: String? = nil,
mediaManager: MXMediaManager? = nil,
fallbackImage: AvatarFallbackImage?) {
self.matrixItemId = matrixItemId
self.displayName = displayName
self.avatarUrl = avatarUrl
self.mediaManager = mediaManager
self.fallbackImages = fallbackImage.map { [$0] }
}
}

View File

@@ -41,6 +41,24 @@ protocol AvatarViewDataProtocol: AvatarProtocol {
/// Matrix media handler
var mediaManager: MXMediaManager? { get }
/// Fallback image used when avatarUrl is nil
var fallbackImage: AvatarFallbackImage? { get }
/// Fallback images used when avatarUrl is nil
var fallbackImages: [AvatarFallbackImage]? { get }
}
extension AvatarViewDataProtocol {
func fallbackImageParameters() -> (UIImage?, UIView.ContentMode)? {
fallbackImages?
.lazy
.map { fallbackImage in
switch fallbackImage {
case .matrixItem(let matrixItemId, let matrixItemDisplayName):
return (AvatarGenerator.generateAvatar(forMatrixItem: matrixItemId, withDisplayName: matrixItemDisplayName), .scaleAspectFill)
case .image(let image, let contentMode):
return (image, contentMode ?? .scaleAspectFill)
}
}
.first { (image, contentMode) in
image != nil
}
}
}

View File

@@ -25,8 +25,7 @@ import Combine
class VectorHostingController: UIHostingController<AnyView> {
// MARK: Private
private let forceZeroSafeAreaInsets: Bool
private var theme: Theme
private var heightSubject = CurrentValueSubject<CGFloat, Never>(0)
@@ -55,11 +54,8 @@ class VectorHostingController: UIHostingController<AnyView> {
}
/// Initializer
/// - Parameter rootView: Root view for the controller.
/// - Parameter forceZeroSafeAreaInsets: Whether to force-set the hosting view's safe area insets to zero. Useful when the view is used as part of a table view.
init<Content>(rootView: Content,
forceZeroSafeAreaInsets: Bool = false) where Content: View {
init<Content>(rootView: Content) where Content: View {
self.theme = ThemeService.shared().theme
self.forceZeroSafeAreaInsets = forceZeroSafeAreaInsets
super.init(rootView: AnyView(rootView.vectorContent()))
}
@@ -116,22 +112,6 @@ class VectorHostingController: UIHostingController<AnyView> {
heightSubject.send(height)
}
}
override func viewSafeAreaInsetsDidChange() {
super.viewSafeAreaInsetsDidChange()
guard forceZeroSafeAreaInsets else {
return
}
let counterSafeAreaInsets = UIEdgeInsets(top: -view.safeAreaInsets.top,
left: -view.safeAreaInsets.left,
bottom: -view.safeAreaInsets.bottom,
right: -view.safeAreaInsets.right)
if additionalSafeAreaInsets != counterSafeAreaInsets, counterSafeAreaInsets != .zero {
additionalSafeAreaInsets = counterSafeAreaInsets
}
}
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)

View File

@@ -85,7 +85,7 @@ final class CrossSigningService: NSObject {
@discardableResult
func setupCrossSigningWithoutAuthentication(for session: MXSession, success: @escaping (() -> Void), failure: @escaping ((Error) -> Void)) -> MXHTTPOperation? {
guard let crossSigning = session.crypto.crossSigning else {
guard let crossSigning = session.crypto?.crossSigning else {
failure(CrossSigningServiceError.unknown)
return nil
}

View File

@@ -72,7 +72,7 @@ final class CrossSigningSetupCoordinator: CrossSigningSetupCoordinatorType {
}
private func setupCrossSigning(with authenticationParameters: [String: Any]) {
guard let crossSigning = self.parameters.session.crypto.crossSigning else {
guard let crossSigning = self.parameters.session.crypto?.crossSigning else {
return
}

View File

@@ -332,7 +332,7 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol {
createAvatarButtonItem(for: viewController)
}
private func createAvatarButtonItem(for viewController: UIViewController) {
private var avatarMenu: UIMenu {
var actions: [UIMenuElement] = []
actions.append(UIAction(title: VectorL10n.allChatsUserMenuSettings, image: UIImage(systemName: "gearshape")) { [weak self] action in
@@ -358,32 +358,30 @@ class AllChatsCoordinator: NSObject, SplitViewMasterCoordinatorProtocol {
}
]))
let menu = UIMenu(options: .displayInline, children: actions)
return UIMenu(options: .displayInline, children: actions)
}
private func createAvatarButtonItem(for viewController: UIViewController) {
let view = UIView(frame: CGRect(x: 0, y: 0, width: 36, height: 36))
view.backgroundColor = .clear
let button: UIButton = UIButton(frame: view.bounds.inset(by: UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7)))
let avatarInsets: UIEdgeInsets = .init(top: 7, left: 7, bottom: 7, right: 7)
let button: UIButton = .init(frame: view.bounds.inset(by: avatarInsets))
button.setImage(Asset.Images.tabPeople.image, for: .normal)
button.menu = menu
button.menu = avatarMenu
button.showsMenuAsPrimaryAction = true
button.autoresizingMask = [.flexibleHeight, .flexibleWidth]
button.accessibilityLabel = VectorL10n.allChatsUserMenuAccessibilityLabel
view.addSubview(button)
self.avatarMenuButton = button
let avatarView = UserAvatarView(frame: view.bounds.inset(by: UIEdgeInsets(top: 7, left: 7, bottom: 7, right: 7)))
let avatarView = UserAvatarView(frame: view.bounds.inset(by: avatarInsets))
avatarView.isUserInteractionEnabled = false
avatarView.update(theme: ThemeService.shared().theme)
avatarView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
view.addSubview(avatarView)
self.avatarMenuView = avatarView
if let avatar = userAvatarViewData(from: currentMatrixSession) {
avatarView.fill(with: avatar)
button.setImage(nil, for: .normal)
}
updateAvatarButtonItem()
viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: view)
}

View File

@@ -22,7 +22,7 @@ final class KeyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType {
// MARK: Private
private let session: MXSession
private let keyBackup: MXKeyBackup
private let navigationRouter: NavigationRouterType
private let keyBackupVersion: MXKeyBackupVersion
@@ -34,8 +34,8 @@ final class KeyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType {
// MARK: - Setup
init(session: MXSession, keyBackupVersion: MXKeyBackupVersion, navigationRouter: NavigationRouterType? = nil) {
self.session = session
init(keyBackup: MXKeyBackup, keyBackupVersion: MXKeyBackupVersion, navigationRouter: NavigationRouterType? = nil) {
self.keyBackup = keyBackup
self.keyBackupVersion = keyBackupVersion
if let navigationRouter = navigationRouter {
@@ -52,7 +52,7 @@ final class KeyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType {
let rootCoordinator: Coordinator & Presentable
// Check if we have the private key locally
if self.session.crypto.backup.hasPrivateKeyInCryptoStore {
if keyBackup.hasPrivateKeyInCryptoStore {
rootCoordinator = self.createRecoverFromPrivateKeyCoordinator()
} else {
rootCoordinator = self.createRecoverWithUserInteractionCoordinator()
@@ -93,19 +93,19 @@ final class KeyBackupRecoverCoordinator: KeyBackupRecoverCoordinatorType {
}
private func createRecoverFromPrivateKeyCoordinator() -> KeyBackupRecoverFromPrivateKeyCoordinator {
let coordinator = KeyBackupRecoverFromPrivateKeyCoordinator(keyBackup: self.session.crypto.backup, keyBackupVersion: self.keyBackupVersion)
let coordinator = KeyBackupRecoverFromPrivateKeyCoordinator(keyBackup: keyBackup, keyBackupVersion: self.keyBackupVersion)
coordinator.delegate = self
return coordinator
}
private func createRecoverFromPassphraseCoordinator() -> KeyBackupRecoverFromPassphraseCoordinator {
let coordinator = KeyBackupRecoverFromPassphraseCoordinator(keyBackup: self.session.crypto.backup, keyBackupVersion: self.keyBackupVersion)
let coordinator = KeyBackupRecoverFromPassphraseCoordinator(keyBackup: keyBackup, keyBackupVersion: self.keyBackupVersion)
coordinator.delegate = self
return coordinator
}
private func createRecoverFromRecoveryKeyCoordinator() -> KeyBackupRecoverFromRecoveryKeyCoordinator {
let coordinator = KeyBackupRecoverFromRecoveryKeyCoordinator(keyBackup: self.session.crypto.backup, keyBackupVersion: self.keyBackupVersion)
let coordinator = KeyBackupRecoverFromRecoveryKeyCoordinator(keyBackup: keyBackup, keyBackupVersion: self.keyBackupVersion)
coordinator.delegate = self
return coordinator
}

View File

@@ -49,7 +49,12 @@ final class KeyBackupRecoverCoordinatorBridgePresenter: NSObject {
// MARK: - Public
func present(from viewController: UIViewController, animated: Bool) {
let keyBackupSetupCoordinator = KeyBackupRecoverCoordinator(session: self.session, keyBackupVersion: keyBackupVersion)
guard let keyBackup = session.crypto?.backup else {
MXLog.failure("[KeyBackupRecoverCoordinatorBridgePresenter] Cannot setup backups without backup module")
return
}
let keyBackupSetupCoordinator = KeyBackupRecoverCoordinator(keyBackup: keyBackup, keyBackupVersion: keyBackupVersion)
keyBackupSetupCoordinator.delegate = self
viewController.present(keyBackupSetupCoordinator.toPresentable(), animated: animated, completion: nil)
keyBackupSetupCoordinator.start()
@@ -58,12 +63,16 @@ final class KeyBackupRecoverCoordinatorBridgePresenter: NSObject {
}
func push(from navigationController: UINavigationController, animated: Bool) {
guard let keyBackup = session.crypto?.backup else {
MXLog.failure("[KeyBackupRecoverCoordinatorBridgePresenter] Cannot setup backups without backup module")
return
}
MXLog.debug("[KeyBackupRecoverCoordinatorBridgePresenter] Push complete security from \(navigationController)")
let navigationRouter = NavigationRouterStore.shared.navigationRouter(for: navigationController)
let keyBackupSetupCoordinator = KeyBackupRecoverCoordinator(session: self.session, keyBackupVersion: keyBackupVersion, navigationRouter: navigationRouter)
let keyBackupSetupCoordinator = KeyBackupRecoverCoordinator(keyBackup: keyBackup, keyBackupVersion: keyBackupVersion, navigationRouter: navigationRouter)
keyBackupSetupCoordinator.delegate = self
keyBackupSetupCoordinator.start() // Will trigger view controller push

View File

@@ -66,7 +66,7 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType {
private func createSetupIntroViewController() -> KeyBackupSetupIntroViewController {
let backupState = self.session.crypto.backup?.state ?? MXKeyBackupStateUnknown
let backupState = self.session.crypto?.backup?.state ?? MXKeyBackupStateUnknown
let isABackupAlreadyExists: Bool
switch backupState {
@@ -99,7 +99,12 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType {
}
private func showSetupPassphrase(animated: Bool) {
let keyBackupSetupPassphraseCoordinator = KeyBackupSetupPassphraseCoordinator(session: self.session)
guard let keyBackup = self.session.crypto?.backup else {
MXLog.failure("[KeyBackupSetupCoordinator] Cannot setup backups without backup module")
return
}
let keyBackupSetupPassphraseCoordinator = KeyBackupSetupPassphraseCoordinator(keyBackup: keyBackup)
keyBackupSetupPassphraseCoordinator.delegate = self
keyBackupSetupPassphraseCoordinator.start()
@@ -130,7 +135,7 @@ final class KeyBackupSetupCoordinator: KeyBackupSetupCoordinatorType {
}
private func createKeyBackupUsingSecureBackup(privateKey: Data, completion: @escaping (Result<Void, Error>) -> Void) {
guard let keyBackup = session.crypto.backup, let recoveryService = session.crypto.recoveryService else {
guard let keyBackup = session.crypto?.backup, let recoveryService = session.crypto?.recoveryService else {
return
}

View File

@@ -23,7 +23,6 @@ final class KeyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordin
// MARK: Private
private let session: MXSession
private var keyBackupSetupPassphraseViewModel: KeyBackupSetupPassphraseViewModelType
private let keyBackupSetupPassphraseViewController: KeyBackupSetupPassphraseViewController
@@ -35,10 +34,8 @@ final class KeyBackupSetupPassphraseCoordinator: KeyBackupSetupPassphraseCoordin
// MARK: - Setup
init(session: MXSession) {
self.session = session
let keyBackupSetupPassphraseViewModel = KeyBackupSetupPassphraseViewModel(keyBackup: self.session.crypto.backup)
init(keyBackup: MXKeyBackup) {
let keyBackupSetupPassphraseViewModel = KeyBackupSetupPassphraseViewModel(keyBackup: keyBackup)
let keyBackupSetupPassphraseViewController = KeyBackupSetupPassphraseViewController.instantiate(with: keyBackupSetupPassphraseViewModel)
self.keyBackupSetupPassphraseViewModel = keyBackupSetupPassphraseViewModel
self.keyBackupSetupPassphraseViewController = keyBackupSetupPassphraseViewController

View File

@@ -324,12 +324,8 @@ extension KeyVerificationCoordinator: KeyVerificationDataLoadingCoordinatorDeleg
// MARK: - DeviceVerificationStartCoordinatorDelegate
extension KeyVerificationCoordinator: DeviceVerificationStartCoordinatorDelegate {
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) {
self.showVerifyBySAS(transaction: transaction, animated: true)
}
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction) {
self.didCancel()
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, otherDidAcceptRequest request: MXKeyVerificationRequest) {
self.showVerifyByScanning(keyVerificationRequest: request, animated: true)
}
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType) {

View File

@@ -19,7 +19,6 @@
import Foundation
enum KeyVerificationDataLoadingViewModelError: Error {
case unknown
case transactionCancelled
case transactionCancelledByMe(reason: MXTransactionCancelCode)
}
@@ -137,9 +136,7 @@ final class KeyVerificationDataLoadingViewModel: KeyVerificationDataLoadingViewM
return
}
let finalError = error ?? KeyVerificationDataLoadingViewModelError.unknown
sself.update(viewState: .error(finalError))
sself.update(viewState: .error(error))
})
} else {

View File

@@ -92,21 +92,29 @@ final class KeyVerificationSelfVerifyWaitViewModel: KeyVerificationSelfVerifyWai
// be sure that session has completed its first sync
if session.state >= .running {
// Always send request instead of waiting for an incoming one as per recent EW changes
MXLog.debug("[KeyVerificationSelfVerifyWaitViewModel] loadData: Send a verification request to all devices instead of waiting")
let keyVerificationService = KeyVerificationService()
self.verificationManager.requestVerificationByToDevice(withUserId: self.session.myUserId, deviceIds: nil, methods: keyVerificationService.supportedKeyVerificationMethods(), success: { [weak self] (keyVerificationRequest) in
guard let self = self else {
return
}
if let existingRequest = verificationManager.pendingRequests.first(where: { $0.isFromMyUser && !$0.isFromMyDevice && $0.state == MXKeyVerificationRequestStatePending }) {
MXLog.debug("[KeyVerificationSelfVerifyWaitViewModel] loadData: Accepting an existing self-verification request instead of starting a new one")
self.keyVerificationRequest = keyVerificationRequest
registerTransactionDidStateChangeNotification()
acceptKeyVerificationRequest(existingRequest)
} else {
}, failure: { [weak self] error in
self?.update(viewState: .error(error))
})
continueLoadData()
// Always send request instead of waiting for an incoming one as per recent EW changes
MXLog.debug("[KeyVerificationSelfVerifyWaitViewModel] loadData: Send a verification request to all devices instead of waiting")
let keyVerificationService = KeyVerificationService()
self.verificationManager.requestVerificationByToDevice(withUserId: self.session.myUserId, deviceIds: nil, methods: keyVerificationService.supportedKeyVerificationMethods(), success: { [weak self] (keyVerificationRequest) in
guard let self = self else {
return
}
self.keyVerificationRequest = keyVerificationRequest
}, failure: { [weak self] error in
self?.update(viewState: .error(error))
})
continueLoadData()
}
} else {
// show loader
self.update(viewState: .secretsRecoveryCheckingAvailability(VectorL10n.deviceVerificationSelfVerifyWaitRecoverSecretsCheckingAvailability))

View File

@@ -63,13 +63,9 @@ extension DeviceVerificationStartCoordinator: DeviceVerificationStartViewModelCo
func deviceVerificationStartViewModelDidUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType) {
self.delegate?.deviceVerificationStartCoordinatorDidCancel(self)
}
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction) {
self.delegate?.deviceVerificationStartCoordinator(self, didCompleteWithOutgoingTransaction: transaction)
}
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction) {
self.delegate?.deviceVerificationStartCoordinator(self, didTransactionCancelled: transaction)
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest) {
self.delegate?.deviceVerificationStartCoordinator(self, otherDidAcceptRequest: request)
}
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType) {

View File

@@ -19,8 +19,7 @@
import Foundation
protocol DeviceVerificationStartCoordinatorDelegate: AnyObject {
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, didTransactionCancelled transaction: MXSASTransaction)
func deviceVerificationStartCoordinator(_ coordinator: DeviceVerificationStartCoordinatorType, otherDidAcceptRequest request: MXKeyVerificationRequest)
func deviceVerificationStartCoordinatorDidCancel(_ coordinator: DeviceVerificationStartCoordinatorType)
}

View File

@@ -29,7 +29,7 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
private let otherUser: MXUser
private let otherDevice: MXDeviceInfo
private var transaction: MXSASTransaction!
private var request: MXKeyVerificationRequest?
// MARK: Public
@@ -52,12 +52,12 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
case .beginVerifying:
self.beginVerifying()
case .verifyUsingLegacy:
self.cancelTransaction()
self.cancelRequest()
self.update(viewState: .verifyUsingLegacy(self.session, self.otherDevice))
case .verifiedUsingLegacy:
self.coordinatorDelegate?.deviceVerificationStartViewModelDidUseLegacyVerification(self)
case .cancel:
self.cancelTransaction()
self.cancelRequest()
self.coordinatorDelegate?.deviceVerificationStartViewModelDidCancel(self)
}
}
@@ -67,30 +67,22 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
private func beginVerifying() {
self.update(viewState: .loading)
self.verificationManager.beginKeyVerification(withUserId: self.otherUser.userId, andDeviceId: self.otherDevice.deviceId, method: MXKeyVerificationMethodSAS, success: { [weak self] (transaction) in
guard let sself = self else {
return
}
guard let sasTransaction = transaction as? MXSASTransaction, !sasTransaction.isIncoming else {
self.verificationManager.requestVerificationByToDevice(withUserId: otherUser.userId, deviceIds: [otherDevice.deviceId], methods: [MXKeyVerificationMethodSAS], success: { [weak self] request in
guard let self = self else {
return
}
sself.transaction = sasTransaction
self.request = request
sself.update(viewState: .loaded)
sself.registerTransactionDidStateChangeNotification(transaction: sasTransaction)
self.update(viewState: .loaded)
self.registerKeyVerificationRequestDidChangeNotification(for: request)
}, failure: {[weak self] error in
self?.update(viewState: .error(error))
})
}
private func cancelTransaction() {
guard let transaction = self.transaction else {
return
}
transaction.cancel(with: MXTransactionCancelCode.user())
private func cancelRequest() {
request?.cancel(with: MXTransactionCancelCode.user(), success: nil)
}
private func update(viewState: DeviceVerificationStartViewState) {
@@ -98,37 +90,41 @@ final class DeviceVerificationStartViewModel: DeviceVerificationStartViewModelTy
}
// MARK: - MXKeyVerificationTransactionDidChange
// MARK: - MXKeyVerificationRequestDidChange
private func registerTransactionDidStateChangeNotification(transaction: MXSASTransaction) {
NotificationCenter.default.addObserver(self, selector: #selector(transactionDidStateChange(notification:)), name: NSNotification.Name.MXKeyVerificationTransactionDidChange, object: transaction)
private func registerKeyVerificationRequestDidChangeNotification(for request: MXKeyVerificationRequest) {
NotificationCenter.default.addObserver(self, selector: #selector(requestDidStateChange(notification:)), name: .MXKeyVerificationRequestDidChange, object: request)
}
private func unregisterTransactionDidStateChangeNotification() {
NotificationCenter.default.removeObserver(self, name: .MXKeyVerificationTransactionDidChange, object: nil)
private func unregisterKeyVerificationRequestDidChangeNotification() {
NotificationCenter.default.removeObserver(self, name: .MXKeyVerificationRequestDidChange, object: nil)
}
@objc private func transactionDidStateChange(notification: Notification) {
guard let transaction = notification.object as? MXSASTransaction, !transaction.isIncoming else {
@objc private func requestDidStateChange(notification: Notification) {
guard let request = notification.object as? MXKeyVerificationRequest, request.requestId == self.request?.requestId else {
return
}
switch transaction.state {
case MXSASTransactionStateShowSAS:
self.unregisterTransactionDidStateChangeNotification()
self.coordinatorDelegate?.deviceVerificationStartViewModel(self, didCompleteWithOutgoingTransaction: transaction)
case MXSASTransactionStateCancelled:
guard let reason = transaction.reasonCancelCode else {
switch request.state {
case MXKeyVerificationRequestStateAccepted, MXKeyVerificationRequestStateReady:
self.unregisterKeyVerificationRequestDidChangeNotification()
self.coordinatorDelegate?.deviceVerificationStartViewModel(self, otherDidAcceptRequest: request)
case MXKeyVerificationRequestStateCancelled:
guard let reason = request.reasonCancelCode else {
return
}
self.unregisterTransactionDidStateChangeNotification()
self.unregisterKeyVerificationRequestDidChangeNotification()
self.update(viewState: .cancelled(reason))
case MXSASTransactionStateCancelledByMe:
guard let reason = transaction.reasonCancelCode else {
case MXKeyVerificationRequestStateCancelledByMe:
guard let reason = request.reasonCancelCode else {
return
}
self.unregisterTransactionDidStateChangeNotification()
self.unregisterKeyVerificationRequestDidChangeNotification()
self.update(viewState: .cancelledByMe(reason))
case MXKeyVerificationRequestStateExpired:
self.unregisterKeyVerificationRequestDidChangeNotification()
self.update(viewState: .error(UserVerificationStartViewModelError.keyVerificationRequestExpired))
default:
break
}

View File

@@ -25,8 +25,7 @@ protocol DeviceVerificationStartViewModelViewDelegate: AnyObject {
protocol DeviceVerificationStartViewModelCoordinatorDelegate: AnyObject {
func deviceVerificationStartViewModelDidUseLegacyVerification(_ viewModel: DeviceVerificationStartViewModelType)
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didCompleteWithOutgoingTransaction transaction: MXSASTransaction)
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, didTransactionCancelled transaction: MXSASTransaction)
func deviceVerificationStartViewModel(_ viewModel: DeviceVerificationStartViewModelType, otherDidAcceptRequest request: MXKeyVerificationRequest)
func deviceVerificationStartViewModelDidCancel(_ viewModel: DeviceVerificationStartViewModelType)
}

View File

@@ -18,10 +18,6 @@
import Foundation
enum UserVerificationSessionsStatusViewModelError: Error {
case unknown
}
final class UserVerificationSessionsStatusViewModel: UserVerificationSessionsStatusViewModelType {
// MARK: - Properties
@@ -103,7 +99,7 @@ final class UserVerificationSessionsStatusViewModel: UserVerificationSessionsSta
}
private func getDevicesFromCache(for userId: String) -> [MXDeviceInfo] {
guard let deviceInfoMap = self.session.crypto.devices(forUser: self.userId) else {
guard let deviceInfoMap = self.session.crypto?.devices(forUser: self.userId) else {
return []
}
return Array(deviceInfoMap.values)
@@ -128,9 +124,7 @@ final class UserVerificationSessionsStatusViewModel: UserVerificationSessionsSta
completion(.success(sessionsViewData))
}, failure: { error in
let finalError = error ?? UserVerificationSessionsStatusViewModelError.unknown
completion(.failure(finalError))
completion(.failure(error))
})
return httpOperation

View File

@@ -189,6 +189,7 @@ extension UserVerificationCoordinator: KeyVerificationCoordinatorDelegate {
func keyVerificationCoordinatorDidComplete(_ coordinator: KeyVerificationCoordinatorType, otherUserId: String, otherDeviceId: String) {
dismissPresenter(coordinator: coordinator)
delegate?.userVerificationCoordinatorDidComplete(self)
}
func keyVerificationCoordinatorDidCancel(_ coordinator: KeyVerificationCoordinatorType) {

View File

@@ -952,7 +952,10 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
{
// Force a reload of device keys at the next session start.
// This will fix potential UISIs other peoples receive for our messages.
[mxSession.crypto resetDeviceKeys];
if ([mxSession.crypto isKindOfClass:[MXLegacyCrypto class]])
{
[(MXLegacyCrypto *)mxSession.crypto resetDeviceKeys];
}
// Clean other stores
[mxSession.scanManager deleteAllAntivirusScans];
@@ -1743,8 +1746,18 @@ static NSArray<NSNumber*> *initialSyncSilentErrorsHTTPStatusCodes;
return;
}
if (![mxSession.crypto.crossSigning isKindOfClass:[MXLegacyCrossSigning class]]) {
MXLogFailure(@"Device dehydratation is currently only supported by legacy cross signing, add support to all implementations");
if (failure)
{
failure(nil);
}
return;
}
MXLegacyCrossSigning *crossSigning = (MXLegacyCrossSigning *)mxSession.crypto.crossSigning;;
MXLogDebug(@"[MXKAccount] attemptDeviceDehydrationWithRetry: starting device dehydration");
[[MXKAccountManager sharedManager].dehydrationService dehydrateDeviceWithMatrixRestClient:mxRestClient crypto:mxSession.crypto dehydrationKey:keyData success:^(NSString *deviceId) {
[[MXKAccountManager sharedManager].dehydrationService dehydrateDeviceWithMatrixRestClient:mxRestClient crossSigning:crossSigning dehydrationKey:keyData success:^(NSString *deviceId) {
MXLogDebug(@"[MXKAccount] attemptDeviceDehydrationWithRetry: device successfully dehydrated");
if (success)

View File

@@ -65,9 +65,12 @@
_event = event;
_displayFix = MXKRoomBubbleComponentDisplayFixNone;
if ([event.content[@"format"] isEqualToString:kMXRoomMessageFormatHTML])
NSString *format = event.content[@"format"];
if ([format isKindOfClass:[NSString class]] && [format isEqualToString:kMXRoomMessageFormatHTML])
{
if ([((NSString*)event.content[@"formatted_body"]) containsString:@"<blockquote"])
NSString *formattedBody = (NSString*)event.content[@"formatted_body"];
if ([formattedBody isKindOfClass:[NSString class]] && [formattedBody containsString:@"<blockquote"])
{
_displayFix |= MXKRoomBubbleComponentDisplayFixHtmlBlockquote;
}

View File

@@ -37,7 +37,9 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag)
RoomBubbleCellDataTagPoll,
RoomBubbleCellDataTagLocation,
RoomBubbleCellDataTagLiveLocation,
RoomBubbleCellDataTagVoiceBroadcast
RoomBubbleCellDataTagVoiceBroadcastRecord,
RoomBubbleCellDataTagVoiceBroadcastPlayback,
RoomBubbleCellDataTagVoiceBroadcastNoDisplay
};
/**

View File

@@ -183,13 +183,43 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
self.displayTimestampForSelectedComponentOnLeftWhenPossible = NO;
}
}
else if ([event.type isEqualToString:VoiceBroadcastSettings.eventType])
else if ([event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType])
{
self.tag = RoomBubbleCellDataTagVoiceBroadcast;
VoiceBroadcastInfo *voiceBroadcastInfo = [VoiceBroadcastInfo modelFromJSON: event.content];
if ([VoiceBroadcastInfo isStartedFor:voiceBroadcastInfo.state])
{
// This state event corresponds to the beginning of a voice broadcast
// Check whether this is a local live broadcast to display it with the recorder view or not
// Note: Because of race condition, the voiceBroadcastService may be running without id here (the sync response may be received before
// the success of the event sending), in that case, we will display a recorder view by default to let the user be able to stop a potential record.
if ([event.sender isEqualToString: self.mxSession.myUserId] &&
[voiceBroadcastInfo.deviceId isEqualToString:self.mxSession.myDeviceId] &&
self.mxSession.voiceBroadcastService != nil &&
([event.eventId isEqualToString: self.mxSession.voiceBroadcastService.voiceBroadcastInfoEventId] ||
self.mxSession.voiceBroadcastService.voiceBroadcastInfoEventId == nil))
{
self.tag = RoomBubbleCellDataTagVoiceBroadcastRecord;
}
else
{
self.tag = RoomBubbleCellDataTagVoiceBroadcastPlayback;
}
}
else
{
self.tag = RoomBubbleCellDataTagVoiceBroadcastNoDisplay;
if ([VoiceBroadcastInfo isStoppedFor:voiceBroadcastInfo.state])
{
// This state event corresponds to the end of a voice broadcast
// Force the tag of the potential cellData which corresponds to the started event to switch the display from recorder to listener
id<MXKRoomBubbleCellDataStoring> bubbleData = [roomDataSource cellDataOfEventWithEventId:voiceBroadcastInfo.eventId];
bubbleData.tag = RoomBubbleCellDataTagVoiceBroadcastPlayback;
}
}
self.collapsable = NO;
self.collapsed = NO;
MXLogDebug(@"VB incoming initWithEvent")
break;
}
@@ -205,7 +235,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
}
else if (event.content[VoiceBroadcastSettings.voiceBroadcastContentKeyChunkType])
{
self.tag = RoomBubbleCellDataTagVoiceBroadcast;
self.tag = RoomBubbleCellDataTagVoiceBroadcastNoDisplay;
self.collapsable = NO;
self.collapsed = NO;
}
@@ -315,13 +345,11 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
}
break;
case RoomBubbleCellDataTagVoiceBroadcast:
if (RiotSettings.shared.enableVoiceBroadcast == YES &&
[VoiceBroadcastInfo isStartedFor:[VoiceBroadcastInfo modelFromJSON:self.events.lastObject.content].state])
{
hasNoDisplay = NO;
}
case RoomBubbleCellDataTagVoiceBroadcastRecord:
case RoomBubbleCellDataTagVoiceBroadcastPlayback:
hasNoDisplay = NO;
break;
case RoomBubbleCellDataTagVoiceBroadcastNoDisplay:
break;
default:
hasNoDisplay = [super hasNoDisplay];
@@ -1072,7 +1100,9 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
case RoomBubbleCellDataTagLiveLocation:
shouldAddEvent = NO;
break;
case RoomBubbleCellDataTagVoiceBroadcast:
case RoomBubbleCellDataTagVoiceBroadcastRecord:
case RoomBubbleCellDataTagVoiceBroadcastPlayback:
case RoomBubbleCellDataTagVoiceBroadcastNoDisplay:
shouldAddEvent = NO;
break;
default:
@@ -1143,7 +1173,7 @@ NSString *const URLPreviewDidUpdateNotification = @"URLPreviewDidUpdateNotificat
{
shouldAddEvent = NO;
}
} else if ([event.type isEqualToString:VoiceBroadcastSettings.eventType]) {
} else if ([event.type isEqualToString:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType]) {
shouldAddEvent = NO;
}
break;

View File

@@ -73,11 +73,6 @@ typedef NS_ENUM(NSUInteger, MXKRoomViewControllerJoinRoomResult) {
*/
MXKAttachment *currentSharedAttachment;
/**
The potential text input placeholder is saved when it is replaced temporarily
*/
NSString *savedInputToolbarPlaceholder;
/**
Tell whether the input toolbar required to run an animation indicator.
*/

View File

@@ -440,7 +440,9 @@
- (void)startUserVerification
{
[[AppDelegate theDelegate] presentUserVerificationForRoomMember:self.mxRoomMember session:self.mainSession];
[[AppDelegate theDelegate] presentUserVerificationForRoomMember:self.mxRoomMember session:self.mainSession completion:^{
[self refreshUserEncryptionTrustLevel];
}];
}
- (void)presentUserVerification
@@ -1332,6 +1334,7 @@
- (void)keyVerificationCoordinatorBridgePresenterDelegateDidComplete:(KeyVerificationCoordinatorBridgePresenter *)coordinatorBridgePresenter otherUserId:(NSString * _Nonnull)otherUserId otherDeviceId:(NSString * _Nonnull)otherDeviceId
{
[self refreshUserEncryptionTrustLevel];
[self dismissKeyVerificationCoordinatorBridgePresenter];
}

View File

@@ -25,7 +25,7 @@ class RoomNotificationSettingsAvatarView: UIView {
func configure(viewData: AvatarViewDataProtocol) {
avatarView.fill(with: viewData)
switch viewData.fallbackImage {
switch viewData.fallbackImages?.first {
case .matrixItem(_, let matrixItemDisplayName):
nameLabel.text = matrixItemDisplayName
default:

View File

@@ -92,7 +92,8 @@ final class RoomCoordinator: NSObject, RoomCoordinatorProtocol {
self.roomViewController.parentSpaceId = parameters.parentSpaceId
TimelinePollProvider.shared.session = parameters.session
TimelineVoiceBroadcastProvider.shared.session = parameters.session
VoiceBroadcastPlaybackProvider.shared.session = parameters.session
VoiceBroadcastRecorderProvider.shared.session = parameters.session
super.init()
}

View File

@@ -598,6 +598,7 @@ static CGSize kThreadListBarButtonItemImageSize;
isAppeared = NO;
[VoiceMessageMediaServiceProvider.sharedProvider pauseAllServices];
[VoiceBroadcastRecorderProvider.shared pauseRecording];
// Stop the loading indicator even if the session is still in progress
[self stopLoadingUserIndicator];
@@ -1775,15 +1776,20 @@ static CGSize kThreadListBarButtonItemImageSize;
|| self.customizedRoomDataSource.jitsiWidget;
}
- (BOOL)canSendStateEventWithType:(MXEventTypeString)eventTypeString
{
MXRoomPowerLevels *powerLevels = [self.roomDataSource.roomState powerLevels];
NSInteger requiredPower = [powerLevels minimumPowerLevelForSendingEventAsStateEvent:eventTypeString];
NSInteger myPower = [powerLevels powerLevelOfUserWithUserID:self.roomDataSource.mxSession.myUserId];
return myPower >= requiredPower;
}
/**
Returns a flag for the current user whether it's privileged to add/remove Jitsi widgets to this room.
*/
- (BOOL)canEditJitsiWidget
{
MXRoomPowerLevels *powerLevels = [self.roomDataSource.roomState powerLevels];
NSInteger requiredPower = [powerLevels minimumPowerLevelForSendingEventAsStateEvent:kWidgetModularEventTypeString];
NSInteger myPower = [powerLevels powerLevelOfUserWithUserID:self.roomDataSource.mxSession.myUserId];
return myPower >= requiredPower;
return [self canSendStateEventWithType:kWidgetModularEventTypeString];
}
- (void)registerURLPreviewNotifications
@@ -1993,9 +1999,9 @@ static CGSize kThreadListBarButtonItemImageSize;
[self updateInputToolBarVisibility];
// Check whether the input toolbar is ready before updating it.
if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:RoomInputToolbarView.class])
if (self.inputToolbarView && [self inputToolbarConformsToToolbarViewProtocol])
{
RoomInputToolbarView *roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView;
id<RoomInputToolbarViewProtocol> roomInputToolbarView = (id<RoomInputToolbarViewProtocol>) self.inputToolbarView;
// Update encryption decoration if needed
[self updateEncryptionDecorationForRoomInputToolbar:roomInputToolbarView];
@@ -2115,9 +2121,9 @@ static CGSize kThreadListBarButtonItemImageSize;
- (void)updateInputToolbarEncryptionDecoration
{
if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:RoomInputToolbarView.class])
if (self.inputToolbarView && [self inputToolbarConformsToToolbarViewProtocol])
{
RoomInputToolbarView *roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView;
id<RoomInputToolbarViewProtocol> roomInputToolbarView = (id<RoomInputToolbarViewProtocol>)self.inputToolbarView;
[self updateEncryptionDecorationForRoomInputToolbar:roomInputToolbarView];
}
}
@@ -2133,7 +2139,7 @@ static CGSize kThreadListBarButtonItemImageSize;
roomTitleView.badgeImageView.image = self.roomEncryptionBadgeImage;
}
- (void)updateEncryptionDecorationForRoomInputToolbar:(RoomInputToolbarView*)roomInputToolbarView
- (void)updateEncryptionDecorationForRoomInputToolbar:(id<RoomInputToolbarViewProtocol>)roomInputToolbarView
{
roomInputToolbarView.isEncryptionEnabled = self.isEncryptionEnabled;
}
@@ -2290,6 +2296,16 @@ static CGSize kThreadListBarButtonItemImageSize;
[self roomInputToolbarViewDidTapFileUpload];
}]];
}
if (RiotSettings.shared.enableVoiceBroadcast && !self.isNewDirectChat)
{
[actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionLive.image andAction:^{
MXStrongifyAndReturnIfNil(self);
if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) {
((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO;
}
[self roomInputToolbarViewDidTapVoiceBroadcast];
}]];
}
if (BuildSettings.pollsEnabled && self.displayConfiguration.sendingPollsEnabled && !self.isNewDirectChat)
{
[actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionPoll.image andAction:^{
@@ -2320,35 +2336,6 @@ static CGSize kThreadListBarButtonItemImageSize;
[self showCameraControllerAnimated:YES];
}]];
}
if (RiotSettings.shared.enableVoiceBroadcast && !self.isNewDirectChat)
{
[actionItems addObject:[[RoomActionItem alloc] initWithImage:AssetImages.actionLive.image andAction:^{
MXStrongifyAndReturnIfNil(self);
if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) {
((RoomInputToolbarView *) self.inputToolbarView).actionMenuOpened = NO;
}
// TODO: Init and start voice broadcast
MXSession* session = self.roomDataSource.mxSession;
[session getOrCreateVoiceBroadcastServiceFor:self.roomDataSource.room completion:^(VoiceBroadcastService *voiceBroadcastService) {
if (voiceBroadcastService) {
if ([VoiceBroadcastInfo isStoppedFor:[voiceBroadcastService getState]]) {
[session.voiceBroadcastService startVoiceBroadcastWithSuccess:^(NSString * _Nullable success) {
} failure:^(NSError * _Nonnull error) {
}];
} else {
[session.voiceBroadcastService stopVoiceBroadcastWithSuccess:^(NSString * _Nullable success) {
} failure:^(NSError * _Nonnull error) {
}];
}
}
}];
}]];
}
roomInputView.actionsBar.actionItems = actionItems;
}
@@ -2436,6 +2423,39 @@ static CGSize kThreadListBarButtonItemImageSize;
self.documentPickerPresenter = documentPickerPresenter;
}
- (void)roomInputToolbarViewDidTapVoiceBroadcast
{
// Check first the room permission
if (![self canSendStateEventWithType:VoiceBroadcastSettings.voiceBroadcastInfoContentKeyType])
{
[self showAlertWithTitle:[VectorL10n voiceBroadcastUnauthorizedTitle] message:[VectorL10n voiceBroadcastPermissionDeniedMessage]];
return;
}
MXSession* session = self.roomDataSource.mxSession;
// Check whether the user is not already broadcasting here or in another room
if (session.voiceBroadcastService)
{
[self showAlertWithTitle:[VectorL10n voiceBroadcastUnauthorizedTitle] message:[VectorL10n voiceBroadcastAlreadyInProgressMessage]];
return;
}
// Request the voice broadcast service to start recording - No service is returned if someone else is already broadcasting in the room
[session getOrCreateVoiceBroadcastServiceFor:self.roomDataSource.room completion:^(VoiceBroadcastService *voiceBroadcastService) {
if (voiceBroadcastService) {
[voiceBroadcastService startVoiceBroadcastWithSuccess:^(NSString * _Nullable success) {
} failure:^(NSError * _Nonnull error) {
}];
}
else
{
[self showAlertWithTitle:[VectorL10n voiceBroadcastUnauthorizedTitle] message:[VectorL10n voiceBroadcastBlockedBySomeoneElseMessage]];
}
}];
}
/**
Send a video asset via the room input toolbar prompting the user for the conversion preset to use
if the `showMediaCompressionPrompt` setting has been enabled.
@@ -3211,7 +3231,7 @@ static CGSize kThreadListBarButtonItemImageSize;
}
}
}
else if (bubbleData.tag == RoomBubbleCellDataTagVoiceBroadcast)
else if (bubbleData.tag == RoomBubbleCellDataTagVoiceBroadcastPlayback)
{
if (bubbleData.isIncoming)
{
@@ -3244,6 +3264,22 @@ static CGSize kThreadListBarButtonItemImageSize;
}
}
}
else if (bubbleData.tag == RoomBubbleCellDataTagVoiceBroadcastRecord)
{
if (bubbleData.isPaginationFirstBubble)
{
cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorderWithPaginationTitle;
}
else if (bubbleData.shouldHideSenderInformation)
{
cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorderWithoutSenderInfo;
}
else
{
cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorder;
}
}
else if (roomBubbleCellData.getFirstBubbleComponentWithDisplay.event.isEmote)
{
if (bubbleData.isIncoming)
@@ -4565,6 +4601,9 @@ static CGSize kThreadListBarButtonItemImageSize;
// Do nothing for dummy links
shouldDoAction = NO;
break;
case RoomMessageURLTypeHttp:
shouldDoAction = YES;
break;
default:
{
MXEvent *tappedEvent = userInfo[kMXKRoomBubbleCellEventKey];
@@ -4590,16 +4629,20 @@ static CGSize kThreadListBarButtonItemImageSize;
break;
case UITextItemInteractionPresentActions:
{
// Retrieve the tapped event
MXEvent *tappedEvent = userInfo[kMXKRoomBubbleCellEventKey];
if (tappedEvent)
{
// Long press on link, present room contextual menu.
[self showContextualMenuForEvent:tappedEvent fromSingleTapGesture:NO cell:cell animated:YES];
if (roomMessageURLType == RoomMessageURLTypeHttp) {
shouldDoAction = YES;
} else {
// Retrieve the tapped event
MXEvent *tappedEvent = userInfo[kMXKRoomBubbleCellEventKey];
if (tappedEvent)
{
// Long press on link, present room contextual menu.
[self showContextualMenuForEvent:tappedEvent fromSingleTapGesture:NO cell:cell animated:YES];
}
shouldDoAction = NO;
}
shouldDoAction = NO;
}
break;
case UITextItemInteractionPreview:
@@ -4997,27 +5040,12 @@ static CGSize kThreadListBarButtonItemImageSize;
{
if (self.roomInputToolbarContainerHeightConstraint.constant != height)
{
// Hide temporarily the placeholder to prevent its distortion during height animation
if (!savedInputToolbarPlaceholder)
{
savedInputToolbarPlaceholder = toolbarView.placeholder.length ? toolbarView.placeholder : @"";
}
toolbarView.placeholder = nil;
[super roomInputToolbarView:toolbarView heightDidChanged:height completion:^(BOOL finished) {
if (completion)
{
completion (finished);
}
// Consider here the saved placeholder only if no new placeholder has been defined during the height animation.
if (!toolbarView.placeholder)
{
// Restore the placeholder if any
toolbarView.placeholder = self->savedInputToolbarPlaceholder.length ? self->savedInputToolbarPlaceholder : nil;
}
self->savedInputToolbarPlaceholder = nil;
}];
}
}
@@ -5070,6 +5098,10 @@ static CGSize kThreadListBarButtonItemImageSize;
{
[actionItems addObject:@(ComposerCreateActionAttachments)];
}
if (RiotSettings.shared.enableVoiceBroadcast && !self.isNewDirectChat)
{
[actionItems addObject:@(ComposerCreateActionVoiceBroadcast)];
}
if (BuildSettings.pollsEnabled && self.displayConfiguration.sendingPollsEnabled && !self.isNewDirectChat)
{
[actionItems addObject:@(ComposerCreateActionPolls)];
@@ -6223,7 +6255,13 @@ static CGSize kThreadListBarButtonItemImageSize;
// Acknowledge the existence of all devices
[self startActivityIndicator];
[self.mainSession.crypto setDevicesKnown:self->unknownDevices complete:^{
if (![self.mainSession.crypto isKindOfClass:[MXLegacyCrypto class]])
{
MXLogFailure(@"[RoomVC] eventDidChangeSentState: Only legacy crypto supports manual setting of known devices");
return;
}
[(MXLegacyCrypto *)self.mainSession.crypto setDevicesKnown:self->unknownDevices complete:^{
self->unknownDevices = nil;
[self stopActivityIndicator];
@@ -8007,6 +8045,9 @@ static CGSize kThreadListBarButtonItemImageSize;
case ComposerCreateActionAttachments:
[self roomInputToolbarViewDidTapFileUpload];
break;
case ComposerCreateActionVoiceBroadcast:
[self roomInputToolbarViewDidTapVoiceBroadcast];
break;
case ComposerCreateActionPolls:
[self.delegate roomViewControllerDidRequestPollCreationFormPresentation:self];
break;

View File

@@ -237,6 +237,7 @@ static BOOL _disableLongPressGestureOnEvent;
[tapGesture setDelegate:self];
[self.messageTextView addGestureRecognizer:tapGesture];
self.messageTextView.userInteractionEnabled = YES;
self.messageTextView.clipsToBounds = NO;
// Recognise and make tappable phone numbers, address, etc.
self.messageTextView.dataDetectorTypes = UIDataDetectorTypeAll;
@@ -805,7 +806,7 @@ static BOOL _disableLongPressGestureOnEvent;
mimetype = bubbleData.attachment.contentInfo[@"mimetype"];
}
if ([mimetype isEqualToString:@"image/gif"])
if ([mimetype isKindOfClass:[NSString class]] && [mimetype isEqualToString:@"image/gif"])
{
if (_isAutoAnimatedGif)
{

View File

@@ -178,6 +178,11 @@ typedef NS_ENUM(NSUInteger, RoomTimelineCellIdentifier) {
RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithoutSenderInfo,
RoomTimelineCellIdentifierOutgoingVoiceBroadcastWithPaginationTitle,
// - Voice broadcast recorder
RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorder,
RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorderWithoutSenderInfo,
RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorderWithPaginationTitle,
// - Others
RoomTimelineCellIdentifierEmpty,
RoomTimelineCellIdentifierSelectedSticker,

View File

@@ -16,6 +16,7 @@
import UIKit
import MatrixSDK
import SwiftUI
@objc protocol SizableBaseRoomCellType: BaseRoomCellProtocol {
static func sizingViewHeightHashValue(from bubbleCellData: MXKRoomBubbleCellData) -> Int
@@ -35,6 +36,7 @@ class SizableBaseRoomCell: BaseRoomCell, SizableBaseRoomCellType {
private static let reactionsViewModelBuilder = RoomReactionsViewModelBuilder()
private static let urlPreviewViewSizer = URLPreviewViewSizer()
private var contentVC: UIViewController?
private class var sizingView: SizableBaseRoomCell {
let sizingView: SizableBaseRoomCell
@@ -115,6 +117,10 @@ class SizableBaseRoomCell: BaseRoomCell, SizableBaseRoomCellType {
sizingView.setNeedsLayout()
sizingView.layoutIfNeeded()
if let contentVC = sizingView.contentVC as? UIHostingController<AnyView> {
contentVC.view.invalidateIntrinsicContentSize()
}
let fittingSize = CGSize(width: width, height: UIView.layoutFittingCompressedSize.height)
var height = sizingView.systemLayoutSizeFitting(fittingSize).height
@@ -168,4 +174,24 @@ class SizableBaseRoomCell: BaseRoomCell, SizableBaseRoomCellType {
return height
}
func addContentViewController(_ controller: UIViewController, on contentView: UIView) {
controller.view.invalidateIntrinsicContentSize()
let parent = vc_parentViewController
parent?.addChild(controller)
contentView.vc_addSubViewMatchingParent(controller.view)
controller.didMove(toParent: parent)
contentVC = controller
}
override func prepareForReuse() {
contentVC?.removeFromParent()
contentVC?.view.removeFromSuperview()
contentVC?.didMove(toParent: nil)
contentVC = nil
super.prepareForReuse()
}
}

View File

@@ -143,6 +143,13 @@
[tableView registerClass:VoiceBroadcastOutgoingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:VoiceBroadcastOutgoingWithPaginationTitleBubbleCell.defaultReuseIdentifier];
}
- (void)registerVoiceBroadcastRecorderCellsForTableView:(UITableView*)tableView
{
// Outgoing
[tableView registerClass:VoiceBroadcastRecorderOutgoingWithoutSenderInfoBubbleCell.class forCellReuseIdentifier:VoiceBroadcastRecorderOutgoingWithoutSenderInfoBubbleCell.defaultReuseIdentifier];
[tableView registerClass:VoiceBroadcastRecorderOutgoingWithPaginationTitleBubbleCell.class forCellReuseIdentifier:VoiceBroadcastRecorderOutgoingWithPaginationTitleBubbleCell.defaultReuseIdentifier];
}
#pragma mark - Mapping
- (NSDictionary<NSNumber*, Class>*)incomingTextMessageCellsMapping
@@ -318,4 +325,14 @@
};
}
- (NSDictionary<NSNumber*, Class>*)voiceBroadcastRecorderCellsMapping
{
return @{
// Outgoing
@(RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorder) : VoiceBroadcastRecorderOutgoingWithoutSenderInfoBubbleCell.class,
@(RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorderWithoutSenderInfo) : VoiceBroadcastRecorderOutgoingWithoutSenderInfoBubbleCell.class,
@(RoomTimelineCellIdentifierOutgoingVoiceBroadcastRecorderWithPaginationTitle) : VoiceBroadcastRecorderOutgoingWithPaginationTitleBubbleCell.class,
};
}
@end

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