From 64fe2cacb598196eac525a620c55bd0ed4b2f189 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Tue, 14 Jun 2022 19:02:53 +0000 Subject: [PATCH 01/97] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2047 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ --- Riot/Assets/pt_BR.lproj/Vector.strings | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index 2fce7003e..ea0746bf3 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -347,10 +347,10 @@ "settings_send_crash_report" = "Enviar dados de cash & uso anon"; "settings_enable_rageshake" = "Agitar com raiva para reportar bug"; "settings_clear_cache" = "Limpar cache"; -"settings_change_password" = "Mudar senha de conta Matrix"; -"settings_old_password" = "senha antiga"; -"settings_new_password" = "senha nova"; -"settings_confirm_password" = "confirmar senha"; +"settings_change_password" = "Mudar senha"; +"settings_old_password" = "Senha antiga"; +"settings_new_password" = "Senha nova"; +"settings_confirm_password" = "Confirmar senha"; "settings_fail_to_update_password" = "Falha para atualizar senha de conta Matrix"; "settings_password_updated" = "A senha de sua conta Matrix tem sido atualizada"; "settings_crypto_device_name" = "Nome de sessão: "; @@ -2300,3 +2300,4 @@ "location_sharing_allow_background_location_message" = "Se você gostaria de acessar sua localização Ao Vivo, Element precisa de acesso de localização quando o app está no background. Para habilitar acesso, toque Ajustes > Localização e selecione Sempre"; "location_sharing_allow_background_location_title" = "Permitir acesso"; "settings_labs_enable_live_location_sharing" = "Compartilhamento de localização ao vivo - compartilhar localização atual (desenvolvimento ativo, e temporariamente, localizações persistem em histórico de sala)"; +"settings_ui_show_redactions_in_room_history" = "Mostrar um placeholder para mensagens removidas"; From cacea214e01460a5c6a6b1e0f376f49322bc3c55 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Tue, 14 Jun 2022 16:36:51 +0000 Subject: [PATCH 02/97] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2047 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 2152782e8..a1da0c8ea 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2528,3 +2528,4 @@ "location_sharing_allow_background_location_message" = "Якщо ви хочете ділитися своїм місцеперебуванням наживо, Element потребує доступу до розташування, коли застосунок перебуваю у фоновому режимі. Щоб увімкнути доступ, торкніться Налаштування> Геодані та виберіть завжди"; "location_sharing_allow_background_location_title" = "Дозволити доступ"; "settings_labs_enable_live_location_sharing" = "Поширення місцеперебування наживо - діліться поточним розташуванням (в активній розробці, місця тимчасово зберігаються в історії кімнат)"; +"settings_ui_show_redactions_in_room_history" = "Показувати заповнювач для вилучених повідомлень"; From 184d8acba9e3f4b62059fe44d432a0c01f5de9e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Tue, 14 Jun 2022 19:22:37 +0000 Subject: [PATCH 03/97] Translated using Weblate (Estonian) Currently translated at 100.0% (2047 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 1cbfcbecb..13ad7e2cb 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2271,3 +2271,4 @@ "location_sharing_allow_background_location_validate_action" = "Seadistused"; "location_sharing_allow_background_location_title" = "Luba ligipääs asukohale"; "settings_labs_enable_live_location_sharing" = "Praeguse asukoha jagamine reaalajas (funktsionaalsus on arendamisel ning ajutiselt on asukohad jututoa ajaloos näha)"; +"settings_ui_show_redactions_in_room_history" = "Näita kustutatud sõnumite asemel kohatäidet"; From fdc5c6601e1cdbd576167faff2efd2d9f723671a Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Tue, 14 Jun 2022 20:29:01 +0000 Subject: [PATCH 04/97] Translated using Weblate (Slovak) Currently translated at 100.0% (2047 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index ec376a7e0..eae00e845 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2526,3 +2526,4 @@ "location_sharing_allow_background_location_message" = "Ak chcete zdieľať svoju polohu v reálnom čase, Element potrebuje prístup k polohe, keď beží aplikácia na pozadí.Ak chcete povoliť prístup, ťuknite na položku Nastavenia> Poloha a vyberte položku Vždy"; "location_sharing_allow_background_location_title" = "Povoliť prístup"; "settings_labs_enable_live_location_sharing" = "Zdieľanie polohy v reálnom čase - zdieľanie aktuálnej polohy (v aktívnom vývoji a polohy dočasne pretrvávajú v histórii miestnosti)"; +"settings_ui_show_redactions_in_room_history" = "Zobrazovať náhrady za odstránené správy"; From ccbc183cb97abddd3b1930d53aa90906c1396965 Mon Sep 17 00:00:00 2001 From: random Date: Thu, 16 Jun 2022 13:47:55 +0000 Subject: [PATCH 05/97] Translated using Weblate (Italian) Currently translated at 100.0% (2047 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 54ba451b3..0124a1283 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -318,7 +318,7 @@ "settings_add_email_address" = "Aggiungi indirizzo email"; "settings_phone_number" = "Telefono"; "settings_add_phone_number" = "Aggiungi numero di telefono"; -"settings_change_password" = "Cambia password dell'account Matrix"; +"settings_change_password" = "Cambia password"; "settings_night_mode" = "Modalità notte"; "settings_fail_to_update_profile" = "Errore nell'aggiornamento del profilo"; "settings_enable_push_notif" = "Notifiche per questo dispositivo"; @@ -352,9 +352,9 @@ "settings_third_party_notices" = "Avvisi di terze parti"; "settings_send_crash_report" = "Invia dati di utilizzo anonimi"; "settings_enable_rageshake" = "Per segnalare un errore agita il dispositivo con rabbia"; -"settings_old_password" = "vecchia password"; -"settings_new_password" = "nuova password"; -"settings_confirm_password" = "conferma password"; +"settings_old_password" = "Vecchia password"; +"settings_new_password" = "Nuova password"; +"settings_confirm_password" = "Conferma password"; "settings_fail_to_update_password" = "Aggiornamento password dell'account Matrix fallito"; "settings_password_updated" = "La password del tuo account Matrix è stata aggiornata"; "settings_crypto_device_name" = "Nome sessione: "; @@ -2303,3 +2303,4 @@ "location_sharing_allow_background_location_message" = "Se vuoi condividere la posizione in tempo reale, Element deve accedere alla posizione quando l'app è in secondo piano. Per attivarla, tocca Impostazioni > Posizione e seleziona Sempre"; "location_sharing_allow_background_location_title" = "Permetti accesso"; "settings_labs_enable_live_location_sharing" = "Condivisione posizione in tempo reale - condividi la posizione attuale (in sviluppo attivo e, per ora, le posizioni restano nella cronologia della stanza)"; +"settings_ui_show_redactions_in_room_history" = "Mostra un segnaposto per i messaggi rimossi"; From 364c941a817dd328e7dba26aaf798f45447ab7b9 Mon Sep 17 00:00:00 2001 From: xiao chi Date: Sat, 18 Jun 2022 06:48:02 +0000 Subject: [PATCH 06/97] Translated using Weblate (Japanese) 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/ja/ --- Riot/Assets/ja.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/ja.lproj/InfoPlist.strings b/Riot/Assets/ja.lproj/InfoPlist.strings index 29532a6d2..d99e8eb80 100644 --- a/Riot/Assets/ja.lproj/InfoPlist.strings +++ b/Riot/Assets/ja.lproj/InfoPlist.strings @@ -6,3 +6,4 @@ "NSCalendarsUsageDescription" = "予定されているミーティングをアプリで確認することができます。"; "NSFaceIDUsageDescription" = "Face IDはアプリへのアクセスに使用されます。"; "NSLocationWhenInUseUsageDescription" = "位置情報を共有する際には、地図を表示するためのアクセスをElementに付与する必要があります。"; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "あなたが他の人に位置を共有するとき、Elementは地図をその人に表示するアクセス権が必要です。"; From d975e5375fe7b514858fe16ccd27dd4e34498ce8 Mon Sep 17 00:00:00 2001 From: xiao chi Date: Sat, 18 Jun 2022 06:52:36 +0000 Subject: [PATCH 07/97] Translated using Weblate (Japanese) Currently translated at 97.9% (48 of 49 strings) Translation: Element iOS/Element iOS (Push) Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios-push/ja/ --- Riot/Assets/ja.lproj/Localizable.strings | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/Riot/Assets/ja.lproj/Localizable.strings b/Riot/Assets/ja.lproj/Localizable.strings index 9139c26eb..4a63be21b 100644 --- a/Riot/Assets/ja.lproj/Localizable.strings +++ b/Riot/Assets/ja.lproj/Localizable.strings @@ -104,3 +104,26 @@ /* New image message from a specific person, not referencing a room. */ "PICTURE_FROM_USER" = "%@ さんが写真を送信"; + +/* A user added a Jitsi call to a room */ +"GROUP_CALL_STARTED" = "グループ通話が開始されました"; + +/* A user's membership has updated in an unknown way */ +"USER_MEMBERSHIP_UPDATED" = "%@ がプロフィールを更新しました"; + +/* A user has change their avatar */ +"USER_UPDATED_AVATAR" = "%@ がアバター画像を変更しました"; + +/* A user has change their name to a new name which we don't know */ +"GENERIC_USER_UPDATED_DISPLAYNAME" = "%@ が名前を変更しました"; + +/** Membership Updates **/ + +/* A user has change their name to a new name */ +"USER_UPDATED_DISPLAYNAME" = "%@ が名前を %@ に変更しました"; + +/* New file message from a specific person, not referencing a room. */ +"FILE_FROM_USER" = "%@ がファイルを送信しました: %@"; + +/* New audio message from a specific person, not referencing a room. */ +"AUDIO_FROM_USER" = "%@ が音声ファイルを送信しました: %@"; From c286a4e7fc333960e8afcc574f8966767f0ca731 Mon Sep 17 00:00:00 2001 From: xiao chi Date: Sat, 18 Jun 2022 07:10:49 +0000 Subject: [PATCH 08/97] Translated using Weblate (Japanese) Currently translated at 68.7% (1407 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ja/ --- Riot/Assets/ja.lproj/Vector.strings | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Riot/Assets/ja.lproj/Vector.strings b/Riot/Assets/ja.lproj/Vector.strings index e7e87bb88..a5421f605 100644 --- a/Riot/Assets/ja.lproj/Vector.strings +++ b/Riot/Assets/ja.lproj/Vector.strings @@ -1621,3 +1621,22 @@ "spaces_creation_visibility_title" = "作成するスペースの種類を選択してください"; "space_public_join_rule_detail" = "誰でも参加可能、コミュニティー向け"; "space_private_join_rule_detail" = "招待者のみ参加可能、個人やチーム向け"; +"onboarding_use_case_title" = "誰と話すことが一番多いですか?"; +"onboarding_splash_page_4_message" = "Elementは職場利用にも最適です。世界で最も安全な組織によって信頼されています。"; +"onboarding_splash_page_4_title_no_pun" = "チームのためのメッセージング。"; +"onboarding_splash_page_3_message" = "E2Eで暗号化されており、登録に電話番号は不要です。広告もデータ収集もありません。"; +"onboarding_splash_page_3_title" = "安全なメッセージ。"; +"onboarding_splash_page_2_message" = "データがどこに保存されるかを自分で選び、主導権と独立を手に入れよう。Matrixで接続。"; +"onboarding_splash_page_2_title" = "主導権はあなたにある。"; +"onboarding_splash_page_1_message" = "オンライン上でも対面の会話と同じレベルでプライバシーを守る、安全で独立したコミュニケーション。"; +"saving" = "保存中"; + +// Activities +"loading" = "ロード中"; +"confirm" = "確認"; +"edit" = "編集"; +"suggest" = "サジェスト"; +"add" = "追加"; +"existing" = "既存"; +"new_word" = "新規"; +"stop" = "停止"; From 330098cf6a7b1ea73f4df216de85f9064f1702e8 Mon Sep 17 00:00:00 2001 From: Johan Smits Date: Mon, 20 Jun 2022 09:20:12 +0000 Subject: [PATCH 09/97] Translated using Weblate (Dutch) Currently translated at 100.0% (2047 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/nl/ --- Riot/Assets/nl.lproj/Vector.strings | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index bc8000a8a..19c900719 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -309,9 +309,9 @@ "settings_privacy_policy" = "Privacybeleid"; "settings_third_party_notices" = "Derdepartijmeldingen"; "settings_send_crash_report" = "Anonieme crash- en gebruiksgegevens versturen"; -"settings_old_password" = "oud wachtwoord"; -"settings_new_password" = "nieuw wachtwoord"; -"settings_confirm_password" = "wachtwoord bevestigen"; +"settings_old_password" = "Oud wachtwoord"; +"settings_new_password" = "Nieuw wachtwoord"; +"settings_confirm_password" = "Wachtwoord bevestigen"; "settings_fail_to_update_password" = "Bijwerken van Matrix account wachtwoord is mislukt"; "settings_password_updated" = "Uw Matrix account wachtwoord is bijgewerkt"; "settings_crypto_device_name" = "Apparaatnaam: "; @@ -2490,3 +2490,4 @@ "settings_labs_enable_live_location_sharing" = "Live locatie delen - deel huidige locatie (actieve ontwikkeling, en tijdelijk, locaties blijven bestaan in kamergeschiedenis)"; /* The %@ placeholder will be replaced with the integration manager's URL. */ "settings_integrations_allow_description" = "Gebruik een integratiebeheerder (%@) om bots, bruggen, widgets en stickerpakketten te beheren.\n\nIntegratiebeheerders ontvangen configuratiedata en kunnen widgets aanpassen, kameruitnodigingen versturen en bestuursniveaus instellen namens u."; +"settings_ui_show_redactions_in_room_history" = "Toon een aanduiding voor verwijderde berichten"; From 23511d75702b28ace13b2ef7218ddbf89f1fc88a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sveinn=20=C3=AD=20Felli?= Date: Wed, 22 Jun 2022 13:41:40 +0000 Subject: [PATCH 10/97] Translated using Weblate (Icelandic) 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/is/ --- Riot/Assets/is.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/is.lproj/InfoPlist.strings b/Riot/Assets/is.lproj/InfoPlist.strings index ea0b89629..688960701 100644 --- a/Riot/Assets/is.lproj/InfoPlist.strings +++ b/Riot/Assets/is.lproj/InfoPlist.strings @@ -8,3 +8,4 @@ "NSPhotoLibraryUsageDescription" = "Myndasafnið er notað til að senda myndir og myndskeið."; // Permissions usage explanations "NSCameraUsageDescription" = "Myndavélin er notuð til að taka myndir og myndskeið og fyrir myndsímtöl."; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "Þegar þú deilir staðsetningu þinni til annarra, þarf Element aðgang til að geta birt hana á landakorti."; From fd83254ced43cd45b12b93110b3e6eb21e1bbdb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sveinn=20=C3=AD=20Felli?= Date: Wed, 22 Jun 2022 14:16:37 +0000 Subject: [PATCH 11/97] Translated using Weblate (Icelandic) Currently translated at 83.4% (1708 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/is/ --- Riot/Assets/is.lproj/Vector.strings | 156 +++++++++++++++++++++++++++- 1 file changed, 152 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/is.lproj/Vector.strings b/Riot/Assets/is.lproj/Vector.strings index 21ea08d7a..c2c8af304 100644 --- a/Riot/Assets/is.lproj/Vector.strings +++ b/Riot/Assets/is.lproj/Vector.strings @@ -190,10 +190,10 @@ "settings_third_party_notices" = "Athugasemdir frá þriðja aðila"; "settings_send_crash_report" = "Senda nafnlausar hrunskýrslur og upplýsingar um notkun"; "settings_clear_cache" = "Hreinsa skyndiminni"; -"settings_change_password" = "Breyta Matrix-lykilorði"; -"settings_old_password" = "eldra lykilorð"; -"settings_new_password" = "nýtt lykilorð"; -"settings_confirm_password" = "staðfestu lykilorð"; +"settings_change_password" = "Breyta lykilorði"; +"settings_old_password" = "Eldra lykilorð"; +"settings_new_password" = "Nýtt lykilorð"; +"settings_confirm_password" = "Staðfestu lykilorðið"; "settings_fail_to_update_password" = "Mistókst að uppfæra Matrix-lykilorð"; "settings_password_updated" = "Matrix-lykilorðið þitt hefur verið uppfært"; "settings_crypto_device_name" = "Nafn á setu: "; @@ -1968,3 +1968,151 @@ "settings_labs_enable_ringing_for_group_calls" = "Hringing fyrir hópsímtöl"; "onboarding_use_case_message" = "Við munum hjálpa þér að tengjast."; "callbar_only_single_active_group" = "Ýttu til að taka þátt í hópsímtalinu (%@)"; +"call_video_with_user" = "Myndsímtal við %@"; +"notice_room_history_visible_to_members_from_joined_point_by_you_for_dm" = "Þú gerðir skilaboð héðan í frá sýnileg fyrir alla meðlimi spjallrásarinnar síðan þeir skráðu sig."; +"notice_room_history_visible_to_members_from_joined_point_by_you" = "Þú gerðir ferilskrá spjallrásar héðan í frá sýnilega fyrir alla meðlimi spjallrásarinnar síðan þeir skráðu sig."; +"notice_room_history_visible_to_members_from_invited_point_by_you_for_dm" = "Þú gerðir skilaboð héðan í frá sýnileg fyrir alla meðlimi spjallrásarinnar síðan þeim var boðið."; +"notice_room_history_visible_to_members_from_invited_point_by_you" = "Þú gerðir ferilskrá spjallrásar héðan í frá sýnilega fyrir alla meðlimi spjallrásarinnar síðan þeim var boðið."; +"notice_room_history_visible_to_members_by_you_for_dm" = "Þú gerðir skilaboð héðan í frá sýnileg fyrir alla meðlimi spjallrásarinnar."; +"notice_room_history_visible_to_members_by_you" = "Þú gerðir ferilskrá spjallrásar héðan í frá sýnilega fyrir alla meðlimi spjallrásarinnar."; +"notice_room_history_visible_to_anyone_by_you" = "Þú gerðir ferilskrá spjallrásar héðan í frá sýnilega fyrir hvern sem er."; +"ignore_user" = "Hunsa notanda"; +"location_sharing_live_timer_selector_long" = "í 8 klukkustundir"; +"location_sharing_live_timer_selector_medium" = "í 1 klukkustund"; +"location_sharing_live_timer_selector_short" = "í 15 mínútur"; +"location_sharing_live_list_item_stop_sharing_action" = "Hætta deilingu"; +"location_sharing_live_list_item_current_user_display_name" = "Þú"; +"location_sharing_live_list_item_last_update_invalid" = "Síðasta uppfærsla óþekkt"; +"location_sharing_live_list_item_last_update" = "Uppfært fyrir %@ síðan"; +"location_sharing_live_list_item_sharing_expired" = "Deikling er útrunnin"; +"location_sharing_live_viewer_title" = "Staðsetning"; +"location_sharing_live_map_callout_title" = "Deila staðsetningu"; +"location_sharing_pin_drop_share_title" = "Senda þessa staðsetningu"; +"location_sharing_static_share_title" = "Senda núverandi staðsetningu mína"; +"live_location_sharing_banner_stop" = "Stöðva"; +"live_location_sharing_banner_title" = "Staðsetning í rauntíma virkjuð"; + +// MARK: Live location sharing + +"location_sharing_live_share_title" = "Deila staðsetningu í rauntíma"; +"location_sharing_allow_background_location_cancel_action" = "Ekki núna"; +"location_sharing_allow_background_location_validate_action" = "Stillingar"; +"location_sharing_allow_background_location_title" = "Leyfa aðgang"; +"side_menu_coach_message" = "Strjúktu til hægri eða ýttu til að sjá allar spjallrásir"; +"spaces_add_room_missing_permission_message" = "Þú hefur ekki heimild til að bæta spjallrásum í þetta svæði."; +"spaces_creation_in_one_space" = "í 1 svæði"; +"spaces_creation_in_many_spaces" = "í %@ svæði"; +"spaces_creation_in_spacename_plus_many" = "í %@ + %@ svæði"; +"spaces_creation_in_spacename_plus_one" = "í %@ + 1 svæði"; +"spaces_creation_in_spacename" = "í %@"; +"spaces_creation_post_process_inviting_users" = "Býð %@ notendum"; +"spaces_creation_post_process_adding_rooms" = "Bæti við %@ spjallrásum"; +"spaces_creation_post_process_creating_room" = "Bý til %@"; +"spaces_creation_post_process_uploading_avatar" = "Sendi inn auðkennismynd"; +"spaces_creation_post_process_creating_space_task" = "Bý til %@"; +"spaces_creation_post_process_creating_space" = "Útbý svæði"; +"spaces_creation_invite_by_username_title" = "Bjóddu félögum þínu"; +"spaces_creation_invite_by_username" = "Bjóða með notandanafni"; +"spaces_creation_sharing_type_me_and_teammates_detail" = "Einkasvæði fyrir þig og félaga í teyminu þínu"; +"spaces_creation_sharing_type_me_and_teammates_title" = "Ég og félagar í teyminu mínu"; +"spaces_creation_sharing_type_just_me_detail" = "Einkasvæði til að skipuleggja spjallrásirnar þínar"; +"spaces_creation_sharing_type_just_me_title" = "Bara ég"; +"spaces_creation_sharing_type_title" = "Hverjum ertu að vinna með?"; +"spaces_creation_email_invites_email_title" = "Tölvupóstur"; +"spaces_creation_new_rooms_support" = "Aðstoð"; +"spaces_creation_new_rooms_random" = "Slembið"; +"spaces_creation_new_rooms_general" = "Almennt"; +"spaces_creation_new_rooms_room_name_title" = "Nafn spjallrásar"; +"spaces_creation_private_space_title" = "Einkasvæðið þitt"; +"spaces_creation_public_space_title" = "Opinbera svæðið þitt"; +"spaces_creation_address_already_exists" = "%@\ner þegar til"; +"spaces_creation_address_invalid_characters" = "%@\ner með ógilda stafi"; +"spaces_creation_address" = "Vistfang"; +"spaces_creation_settings_message" = "Bættu við nánari atriðum til að aðgreina þetta frá öðru. Þú getur breytt þessu hvenær sem er."; +"spaces_creation_footer" = "Þú getur breytt þessu síðar"; +"spaces_creation_visibility_message" = "Til að ganga til liðs við fyrirliggjandi svæði þarftu boð."; +"spaces_creation_visibility_title" = "Hvaða tegund af svæði viltu búa til?"; + +// Mark: - Space Creation + +"spaces_creation_hint" = "Svæði eru ný leið til að hópa fólk og spjallrásir."; +"space_topic" = "Lýsing"; +"space_public_join_rule_detail" = "Opið öllum, best fyrir dreifða hópa"; +"spaces_add_space" = "Bæta við svæði"; +"spaces_add_room" = "Bæta við spjallrás"; +"spaces_invite_people" = "Bjóða fólki"; +"space_private_join_rule_detail" = "Einungis gegn boði, best fyrir þig og lítinn hóp"; +"spaces_empty_space_title" = "Þetta svæði er ekki ennþá með neinar spjallrásir)"; +"spaces_explore_rooms_one_room" = "1 spjallrás"; +"spaces_explore_rooms_room_number" = "%@ spjallrásir"; +"spaces_create_space_title" = "Búa til svæði"; +"spaces_add_space_title" = "Búa til svæði"; +"room_invite_to_room_option_detail" = "Þau munu ekki vera hluti af %@."; +"room_invite_to_room_option_title" = "Aðeins í þessa spjallrás"; + +// Mark: - Room invite + +"room_invite_to_space_option_title" = "Í %@"; + +// MARK: - Share invite link + +"share_invite_link_action" = "Deila boðstengli"; +"create_room_processing" = "Bý til spjallrás"; +"create_room_placeholder_address" = "#testroom:matrix.org"; +"create_room_section_footer_type_restricted" = "Hver sem er á svæðinu getur fundið og tekið þátt."; +"create_room_section_footer_type_private" = "Aðeins fólk sem hefur verið boðið getur fundið og tekið þátt."; +"create_room_type_restricted" = "Meðlimir svæðis"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Þú munt tapa dulrituðu skilaboðunum þínum"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "notað öryggislykilinn þinn"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Ertu ekki með öryggisfrasann þinn? Þú getur "; + +// Room suggestion Settings +"room_suggestion_settings_screen_nav_title" = "Stinga upp á spjallrás"; +"room_access_space_chooser_other_spaces_section" = "Önnur svæði eða spjallrásir"; +"room_access_settings_screen_setting_room_access" = "Stilla aðgang að spjallrás"; +"room_access_settings_screen_upgrade_alert_upgrading" = "Uppfæri spjallrás"; +"room_access_settings_screen_upgrade_alert_upgrade_button" = "Uppfæra"; +"room_access_settings_screen_upgrade_alert_message_no_param" = "Hver sem er í yfirsvæði mun geta fundið og tekið þátt í þessari spjallrás - ekki er þörf á að bjóða öllum handvirkt. Þú munt geta breytt þessu í stillingum spjallrásarinnar hvenær sem er."; +"room_access_settings_screen_upgrade_alert_title" = "Uppfæra spjallrás"; +"room_access_settings_screen_public_message" = "Hver sem er getur fundið og tekið þátt."; +"room_access_settings_screen_edit_spaces" = "Breyta svæðum"; +"room_access_settings_screen_upgrade_required" = "Uppfærsla er nauðsynleg"; + +// Room Access Settings +"room_access_settings_screen_nav_title" = "Aðgangur að spjallrás"; +"room_details_access_row_title" = "Aðgangur"; +"settings_presence" = "Viðvera"; +"settings_key_backup_button_connect" = "Tengja þessa setu við öryggisafrit af lykli"; +"settings_labs_enable_live_location_sharing" = "Deiling staðsetninga í rautíma - deildu staðsetningunni þinni í rauntíma (í virkri þróun, tímabundið haldast staðsetningar í ferli spjallrása)"; +"settings_ui_show_redactions_in_room_history" = "Birta frátökutákn fyrir fjarlægð skilaboð"; +"threads_beta_cancel" = "Ekki núna"; +"threads_beta_enable" = "Prófaðu það"; +"threads_beta_information_link" = "Kanna nánar"; +"threads_beta_title" = "Spjallþræðir"; +"threads_notice_done" = "Náði því"; +"onboarding_celebration_button" = "Hefjumst handa"; +"onboarding_celebration_message" = "Kjörstillingarnar þínar hafa verið vistaðar."; +"onboarding_celebration_title" = "Nú ertu tilbúin(n)!"; +"onboarding_avatar_accessibility_label" = "Auðkennismynd"; +"onboarding_avatar_message" = "Þú getur breytt þessu hvenær sem er."; +"onboarding_avatar_title" = "Bættu við auðkennismynd"; +"onboarding_display_name_hint" = "Þú getur breytt þessu síðar"; +"onboarding_display_name_placeholder" = "Birtingarnafn"; +"onboarding_display_name_title" = "Veldu birtingarnafn"; +"onboarding_personalization_skip" = "Sleppa þessu skrefi"; +"onboarding_personalization_save" = "Vista og halda áfram"; +"onboarding_congratulations_title" = "Til hamingju!"; +"saving" = "Vista"; + +// Activities +"loading" = "Hleð inn"; +"confirm" = "Staðfesta"; +"edit" = "Breyta"; +"suggest" = "Stinga upp á"; +"add" = "Bæta við"; +"existing" = "Fyrirliggjandi"; +"new_word" = "Nýtt"; +"stop" = "Stöðva"; +"joining" = "Gengur til liðs við spjallrás"; +// String for App Store +"store_short_description" = "Öruggt dreifvinnsluspjall/VoIP"; From 549a03f5f4d12576c19c3345413b8619e58dabf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Hampa=C3=AF?= Date: Fri, 24 Jun 2022 16:14:39 +0000 Subject: [PATCH 12/97] Translated using Weblate (French) 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/fr/ --- Riot/Assets/fr.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/fr.lproj/InfoPlist.strings b/Riot/Assets/fr.lproj/InfoPlist.strings index c2a08df8d..d4ff9bba6 100644 --- a/Riot/Assets/fr.lproj/InfoPlist.strings +++ b/Riot/Assets/fr.lproj/InfoPlist.strings @@ -6,3 +6,4 @@ "NSCalendarsUsageDescription" = "Voir vos rendez-vous dans l’application."; "NSFaceIDUsageDescription" = "Face ID est utilisé pour accéder à votre application."; "NSLocationWhenInUseUsageDescription" = "Element doit accéder à votre emplacement pour vous permettre de la partager aux autres utilisateurs sur une carte."; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "Element doit accéder à votre localisation afin de pouvoir la partager avec d'autres utilisateurs sur une carte."; From 769135e7299bbf32277b7f1dee6398cb7d283c50 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Sun, 26 Jun 2022 12:11:13 +0000 Subject: [PATCH 13/97] Translated using Weblate (Albanian) 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/sq/ --- Riot/Assets/sq.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/sq.lproj/InfoPlist.strings b/Riot/Assets/sq.lproj/InfoPlist.strings index bfeef7694..740651215 100644 --- a/Riot/Assets/sq.lproj/InfoPlist.strings +++ b/Riot/Assets/sq.lproj/InfoPlist.strings @@ -6,3 +6,4 @@ "NSCalendarsUsageDescription" = "Shihini te aplikacioni takimet tuaja të planifikuara."; "NSFaceIDUsageDescription" = "Face ID përdoret që të hyni në aplikacionin tuaj."; "NSLocationWhenInUseUsageDescription" = "Kur ndani vendndodhjen tuaj me persona, Element-i ka nevojë për hyrje në të, që t’u trgojë atyre një hartë."; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "Kur u tregoni vendndodhjen tuaj të tjerëve, Element-it i duhet hyrje për t’u shfaqur një hartë."; From ac7b55e687070bf147fcfe27348ec46d42b08f2f Mon Sep 17 00:00:00 2001 From: iaiz Date: Sat, 25 Jun 2022 13:56:24 +0000 Subject: [PATCH 14/97] Translated using Weblate (Spanish) 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/es/ --- Riot/Assets/es.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/es.lproj/InfoPlist.strings b/Riot/Assets/es.lproj/InfoPlist.strings index 1f83ea895..6b8c19112 100644 --- a/Riot/Assets/es.lproj/InfoPlist.strings +++ b/Riot/Assets/es.lproj/InfoPlist.strings @@ -6,3 +6,4 @@ "NSFaceIDUsageDescription" = "Face ID se usa para acceder a tu aplicación."; "NSCalendarsUsageDescription" = "Mostrar tus reuniones en la aplicación."; "NSLocationWhenInUseUsageDescription" = "Cuando compartes tu ubicación con otras personas, Element necesita acceso para que puedan verla en el mapa."; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "Cuando compartes tu ubicación con los demás, Element necesita acceso a tu ubicación para mostrársela en un mapa."; From 64740009e12ffa40063b1f2b79076a564428f180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Hampa=C3=AF?= Date: Mon, 27 Jun 2022 22:39:51 +0000 Subject: [PATCH 15/97] Translated using Weblate (French) Currently translated at 94.2% (1930 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 46 +++++++++++++++++++++++------ 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index fb0ec55c9..ac8b93b0a 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -292,10 +292,10 @@ "settings_send_crash_report" = "Envoyer des rapports d’anomalies anonymes et des statistiques d’utilisation"; "settings_enable_rageshake" = "Secouer l’appareil pour signaler un bug"; "settings_clear_cache" = "Vider le cache"; -"settings_change_password" = "Changer le mot de passe de votre compte Matrix"; -"settings_old_password" = "ancien mot de passe"; -"settings_new_password" = "nouveau mot de passe"; -"settings_confirm_password" = "confirmer le mot de passe"; +"settings_change_password" = "Changer mon mot de passe"; +"settings_old_password" = "Ancien mot de passe"; +"settings_new_password" = "Nouveau mot de passe"; +"settings_confirm_password" = "Confirmer le mot de passe"; "settings_fail_to_update_password" = "Échec de la modification du mot de passe du compte Matrix"; "settings_password_updated" = "Le mot de passe de votre compte Matrix a été modifié"; "settings_crypto_device_name" = "Nom de la session : "; @@ -1208,16 +1208,16 @@ "room_info_list_one_member" = "1 membre"; "create_room_placeholder_address" = "#salondetest:matrix.org"; -"create_room_section_header_address" = "Adresse du salon"; +"create_room_section_header_address" = "ADRESSE"; "create_room_show_in_directory" = "Publier le salon dans le répertoire"; "create_room_section_footer_type" = "Les personnes ne rejoignent un salon privé que sur invitation."; -"create_room_type_public" = "Salon public"; -"create_room_type_private" = "Salon privé"; +"create_room_type_public" = "Salon public (tout le monde)"; +"create_room_type_private" = "Salon privé (seulement sur invitation)"; "create_room_section_header_type" = "Type de salon"; "create_room_section_footer_encryption" = "Le chiffrement ne peut pas être désactivé ensuite."; "create_room_enable_encryption" = "Activer le chiffrement"; -"create_room_section_header_encryption" = "Chiffrement du salon"; -"create_room_placeholder_topic" = "Sujet"; +"create_room_section_header_encryption" = "Chiffrement"; +"create_room_placeholder_topic" = "De quoi parle ce salon ?"; "create_room_section_header_topic" = "Sujet du salon (facultatif)"; "create_room_placeholder_name" = "Nom"; "create_room_section_header_name" = "Nom du salon"; @@ -2188,3 +2188,31 @@ "existing" = "Existant"; "new_word" = "Nouveau"; "stop" = "Arrêter"; +"create_room_processing" = "Création du salon"; +"create_room_suggest_room" = "Suggérer aux membres de l'espace"; +"create_room_section_footer_type_restricted" = "Peut être trouvé et rejoins par tous les membres de l'espace."; +"create_room_section_footer_type_private" = "Peut être trouvé et rejoins uniquement par les personnes invitées."; +"create_room_type_restricted" = "Membres de l'espace"; +"call_jitsi_unable_to_start" = "Impossible de démarrer la conférence"; +"room_suggestion_settings_screen_message" = "Les salons suggérés sont indiqués comme intéressant à rejoindre auprès des membres de l'espace."; +"room_suggestion_settings_screen_title" = "Suggérer un salon dans un espace"; + +// Room suggestion Settings +"room_suggestion_settings_screen_nav_title" = "Suggérer un salon"; +"room_access_space_chooser_other_spaces_section" = "Autres espaces ou salons"; +"room_access_space_chooser_known_spaces_section" = "Espaces que vous connaissez contenant %@"; +"room_access_settings_screen_setting_room_access" = "Réglages des permissions d'accès au salon"; +"room_access_settings_screen_upgrade_alert_auto_invite_switch" = "Inviter automatiquement les membres dans les nouveaux salons"; +"room_access_settings_screen_upgrade_alert_message_no_param" = "N'importe qui présent dans l'espace parent pourra trouver et rejoindre ce salon - pas besoin d'inviter tout le monde manuellement. Vous pouvez changer cette option dans les réglages du salon en tout temps."; +"room_access_settings_screen_upgrade_alert_message" = "N'importe qui dans %@ pourra trouver et rejoindre ce salon - pas besoin d'inviter tout le monde manuellement. Vous pouvez changer cette option dans les réglages du salon en tout temps."; +"room_access_settings_screen_public_message" = "Peut être trouvé et rejoint par n'importe qui."; +"room_access_settings_screen_edit_spaces" = "Editer les espaces"; +"room_access_settings_screen_restricted_message" = "Peut être trouvé et rejoint par n'importe qui dans l'espace.\nVous allez devoir préciser depuis quel espace."; +"room_access_settings_screen_private_message" = "Peut être trouvé et rejoint uniquement par les personnes invitées."; +"room_access_settings_screen_message" = "Définir qui peut trouver et rejoindre %@."; +"room_access_settings_screen_title" = "Qui peut accéder à ce salon ?"; + +// Room Access Settings +"room_access_settings_screen_nav_title" = "Permissions du salon"; +"room_details_promote_room_suggest_title" = "Proposer aux membres de l'espace"; +"room_details_promote_room_title" = "Promouvoir le salon"; From 5c2dbf0e780117461b07ce66d9bfd310edeb3301 Mon Sep 17 00:00:00 2001 From: iaiz Date: Sat, 25 Jun 2022 13:55:48 +0000 Subject: [PATCH 16/97] Translated using Weblate (Spanish) Currently translated at 100.0% (2047 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/es/ --- Riot/Assets/es.lproj/Vector.strings | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/es.lproj/Vector.strings b/Riot/Assets/es.lproj/Vector.strings index 897faf8e1..d2afa6093 100644 --- a/Riot/Assets/es.lproj/Vector.strings +++ b/Riot/Assets/es.lproj/Vector.strings @@ -347,10 +347,10 @@ "settings_send_crash_report" = "Enviar datos de uso e informes de fallas anónimos"; "settings_enable_rageshake" = "Agitar con rabia para reportar un error"; "settings_clear_cache" = "Borrar caché"; -"settings_change_password" = "Cambiar contraseña en la cuenta de Matrix"; -"settings_old_password" = "contraseña anterior"; -"settings_new_password" = "contraseña nueva"; -"settings_confirm_password" = "confirmar contraseña"; +"settings_change_password" = "Cambiar contraseña"; +"settings_old_password" = "Contraseña actual"; +"settings_new_password" = "Contraseña nueva"; +"settings_confirm_password" = "Confirmar contraseña"; "settings_fail_to_update_password" = "No se ha podido cambiar la contraseña de Matrix"; "settings_password_updated" = "La contraseña de tu cuenta de Matrix ha sido cambiada"; "settings_crypto_device_name" = "Nombre de la sesión: "; @@ -2423,3 +2423,4 @@ "location_sharing_allow_background_location_cancel_action" = "Ahora no"; "location_sharing_allow_background_location_validate_action" = "Ajustes"; "location_sharing_allow_background_location_title" = "Permitir acceso"; +"settings_ui_show_redactions_in_room_history" = "Mostrar un indicador donde se haya eliminado un mensaje"; From 2732f08608c5265b1ac66d25958346cb085fe3f3 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sun, 26 Jun 2022 17:13:08 +0000 Subject: [PATCH 17/97] Translated using Weblate (Hungarian) Currently translated at 100.0% (2047 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index a28553b36..20e54ce2e 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -338,10 +338,10 @@ "settings_send_crash_report" = "Személytelen összeomlás és felhasználási adatok küldése"; "settings_enable_rageshake" = "Eszköz megrázása a hiba bejelentéséhez"; "settings_clear_cache" = "Gyorsítótár kiürítése"; -"settings_change_password" = "Matrix fiók jelszó megváltoztatása"; -"settings_old_password" = "régi jelszó"; -"settings_new_password" = "új jelszó"; -"settings_confirm_password" = "jelszó megerősítése"; +"settings_change_password" = "Jelszó megváltoztatása"; +"settings_old_password" = "Régi jelszó"; +"settings_new_password" = "Új jelszó"; +"settings_confirm_password" = "Jelszó megerősítése"; "settings_fail_to_update_password" = "A Matrix fiók jelszó frissítése nem sikerült"; "settings_password_updated" = "A Matrix fiók jelszavad frissítve"; "settings_crypto_device_name" = "Munkamenet neve: "; @@ -2323,3 +2323,4 @@ "location_sharing_allow_background_location_message" = "Ha az élő helyzetedet szeretnéd megosztani, Elementnek akkor is hozzáférésre van szüksége a helyadatokhoz ha éppen a háttérben fut. A hozzáférés engedélyezéséhez koppints a Beállítások> Helyadatokra és válaszd a Mindiget"; "location_sharing_allow_background_location_title" = "Hozzáférés engedélyezése"; "settings_labs_enable_live_location_sharing" = "Folyamatos helymeghatározás megosztása - a jelenlegi helyzet megosztása (aktív fejlesztés alatt, átmenetileg a megosztott helyek megmaradnak a szoba idővonalán)"; +"settings_ui_show_redactions_in_room_history" = "Helykitöltő megjelenítése a törölt szövegek helyett"; From 48e5d41ce7a5cbf35663153c8e037f99ce4ce847 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Sun, 26 Jun 2022 12:09:57 +0000 Subject: [PATCH 18/97] Translated using Weblate (Albanian) Currently translated at 99.5% (2038 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 42 ++++++++++++++++++++++++----- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 3feb86e4f..bcb5d1a49 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -115,8 +115,8 @@ "directory_cell_title" = "Shfletoni në drejtori"; "directory_cell_description" = "Dhoma %tu"; "directory_search_results_title" = "Shfletoni përfundime drejtorie"; -"directory_search_results" = "U gjetën %tu përfundime për %@"; -"directory_search_results_more_than" = ">%tu përfundime të gjetura për %@"; +"directory_search_results" = "U gjetën %1$tu përfundime për %2$@"; +"directory_search_results_more_than" = ">U gjetën %1$tu përfundime për %2$@"; "directory_searching_title" = "Po kërkohet në drejtori…"; "directory_search_fail" = "S’u arrit të sillen të dhëna"; // Contacts @@ -257,10 +257,10 @@ "settings_privacy_policy" = "Rregulla Privatësie"; "settings_third_party_notices" = "Njoftime Palësh të Treta"; "settings_clear_cache" = "Pastroje fshehtinën"; -"settings_change_password" = "Ndryshoni fjalëkalim llogarie Matrix"; -"settings_old_password" = "fjalëkalim i vjetër"; -"settings_new_password" = "fjalëkalim i ri"; -"settings_confirm_password" = "ripohoni fjalëkalimin"; +"settings_change_password" = "Ndryshoni fjalëkalimin"; +"settings_old_password" = "Fjalëkalimi i vjetër"; +"settings_new_password" = "Fjalëkalimi i ri"; +"settings_confirm_password" = "Ripohoni frajëkalimin"; "settings_password_updated" = "Fjalëkalimi i llogarisë tuaj Matrix u përditësua"; "settings_crypto_device_name" = "Emër sesioni: "; "settings_crypto_device_id" = "\nID sesioni: "; @@ -2282,3 +2282,33 @@ "new_word" = "E re"; "stop" = "Ndale"; "joining" = "Po hyhet"; +"location_sharing_live_stop_sharing_progress" = "Ndal tregim vendndodhjeje"; +"location_sharing_live_stop_sharing_error" = "S’u arrit të ndalet tregim vendndodhjeje"; +"location_sharing_live_no_user_locations_error_title" = "S’ka vendndodhje përdoruesish gati"; +"location_sharing_live_timer_selector_long" = "për 8 orë"; +"location_sharing_live_timer_selector_medium" = "për 1 orë"; +"location_sharing_live_timer_selector_short" = "për 15 minuta"; +"location_sharing_live_timer_selector_title" = "Zgjidhni për sa kohë do të shohin të tjerët vendndodhjen tuaj të saktë."; +"location_sharing_live_error" = "Gabim vendndodhjeje “live”"; +"location_sharing_live_list_item_stop_sharing_action" = "Resht së ndari"; +"location_sharing_live_list_item_current_user_display_name" = "Ju"; +"location_sharing_live_list_item_last_update_invalid" = "Nuk dihet koha e përditësimit të fundit"; +"location_sharing_live_list_item_last_update" = "Përditësuar %@ më parë"; +"location_sharing_live_viewer_title" = "Vendndodhje"; +"location_sharing_live_map_callout_title" = "Jepe vendndodhjen"; +"live_location_sharing_ended" = "Vendndodhja “live” përfundoi"; +"location_sharing_allow_background_location_cancel_action" = "Jo tani"; +"location_sharing_allow_background_location_validate_action" = "Rregullime"; +"location_sharing_allow_background_location_title" = "Lejoni hyrje"; +"room_invite_to_space_option_detail" = "Mund të eksplorojnë %@, por s’do të jenë anëtarë të %@."; +"room_access_settings_screen_upgrade_alert_note" = "Ju lutemi, kini parasysh se përmirësimi do të prodhojë një version të ri të dhomës. Krejt mesazhet e tanishëm do të mbeten në këtë dhomë të arkivuar."; +"room_access_settings_screen_upgrade_alert_message_no_param" = "Cilido në një hapësirë mëmë do të jetë në gjendje ta gjejë dhe hyjë në këtë dhomë - s’ka nevojë të ftohen dorazi një e nga një. Do të jeni në gjendje ta ndryshoni këtë te rregullimet e dhomës, në çfarëdo kohe."; +"room_access_settings_screen_upgrade_alert_message" = "Cilido te %@ do të jetë në gjendje të gjejë dhe hyjë në këtë dhomë - s’ka nevojë të ftohen dorazi një e nga një. Do të jeni në gjendje ta ndryshoni këtë te rregullimet e kësaj dhome, në çfarëdo kohe."; +"settings_presence_offline_mode_description" = "Në u aktivizoftë, përdoruesve të tjerë do t’u dukeni përherë jo në linjë, madje edhe kur përdoret aplikacioni."; +"settings_presence_offline_mode" = "Mënyra Offline"; +"settings_presence" = "Prani"; +"settings_enable_rageshake" = "“Rage shake” që të njoftoni një të metë"; +"settings_ui_show_redactions_in_room_history" = "Shfaq një vendmbajtëse për mesazhe të hequr"; +"threads_discourage_information_2" = "\n\nDoni të aktivizohen rrjedha, sido qoftë?"; +"threads_discourage_information_1" = "Shërbyesi juaj Home aktualisht s’mbulon rrjedha, ndaj kjo veçori mund të jetë e paqëndrueshme. Disa mesazhe rrjedhash mund të mos jenë të përdorshëm. "; +"confirm" = "Ripohojeni"; From 218b8d7e9162d2d405b4b609c9eab1d36abd28c0 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Sat, 25 Jun 2022 15:38:05 +0000 Subject: [PATCH 19/97] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2047 of 2047 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 40 ++++++++++++++--------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index a1da0c8ea..714cfef89 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -1747,24 +1747,24 @@ "home_context_menu_make_room" = "Перемістити на вкладку «Кімнати»"; "home_context_menu_make_dm" = "Перемістити на вкладку «Люди»"; "event_formatter_message_deleted" = "Повідомлення видалено"; -"settings_labs_enable_threads" = "Треди спілкування"; -"message_from_a_thread" = "З треду"; -"threads_empty_show_all_threads" = "Показати всі треди"; -"threads_empty_tip" = "Порада: Торкніться повідомлення та натисніть «Тред», щоб розпочати його."; -"threads_empty_info_my" = "Відповідайте у поточний тред або торкніться повідомлення та натисніть «Тред», щоб почати новий."; -"threads_empty_info_all" = "Треди допомагають підтримувати розмови за темою та за ними легко стежити."; -"threads_empty_title" = "Спілкуйтеся за темою в тредах"; -"threads_action_my_threads" = "Мої треди"; -"threads_action_all_threads" = "Усі треди"; -"threads_title" = "Треди"; -"thread_copy_link_to_thread" = "Копіювати посилання на тред"; +"settings_labs_enable_threads" = "Гілки повідомлень"; +"message_from_a_thread" = "З гілки"; +"threads_empty_show_all_threads" = "Показати всі гілки"; +"threads_empty_tip" = "Порада: Торкніться повідомлення та натисніть «Гілка», щоб розпочати її."; +"threads_empty_info_my" = "Відповідайте у поточну гілку або торкніться повідомлення та натисніть «Гілка», щоб почати нову."; +"threads_empty_info_all" = "Гілки допомагають підтримувати розмови за темою та за ними легко стежити."; +"threads_empty_title" = "Спілкуйтеся за темою в гілках"; +"threads_action_my_threads" = "Мої гілки"; +"threads_action_all_threads" = "Усі гілки"; +"threads_title" = "Гілки"; +"thread_copy_link_to_thread" = "Копіювати посилання на гілку"; // MARK: Threads -"room_thread_title" = "Тред"; +"room_thread_title" = "Гілка"; "room_accessibility_thread_more" = "Більше"; -"room_accessibility_threads" = "Треди"; +"room_accessibility_threads" = "Гілки"; "room_event_copy_link_info" = "Посилання скопійовано до буфера обміну."; -"room_event_action_reply_in_thread" = "Тред"; +"room_event_action_reply_in_thread" = "Гілка"; "room_event_action_view_in_room" = "Переглянути у кімнаті"; "location_sharing_open_open_street_maps" = "Відкрити у OpenStreetMap"; "search_filter_placeholder" = "Фільтр"; @@ -2455,11 +2455,11 @@ "threads_beta_cancel" = "Не зараз"; "threads_beta_enable" = "Спробувати"; "threads_beta_information_link" = "Докладніше"; -"threads_beta_information" = "Спілкуйтеся у тредах.\n\nТреди допомагають розмежовувати свої розмови за темами та легко стежити за ними. "; -"threads_beta_title" = "Треди"; +"threads_beta_information" = "Спілкуйтеся у гілках.\n\nГілки допомагають розмежовувати свої розмови за темами та легко стежити за ними. "; +"threads_beta_title" = "Гілки"; "threads_notice_done" = "Зрозуміло"; -"threads_notice_information" = "Усі треди, створені в експериментальному режимі, буде показано звичайними відповідями.

Це буде одноразовим переходом, оскільки треди зараз є частиною специфікації Matrix."; -"threads_notice_title" = "Треди більше не експериментальна функція 🎉"; +"threads_notice_information" = "Усі гілки, створені в експериментальному режимі, буде показано звичайними відповідями.

Це буде одноразовим переходом, оскільки на цей час гілки — це частина специфікації Matrix."; +"threads_notice_title" = "Гілки більше не експериментальна функція 🎉"; "room_participants_invite_prompt_to_msg" = "Ви впевнені, що хочете запросити %@ до %@?"; "onboarding_celebration_button" = "Поїхали"; "onboarding_celebration_message" = "Ваші налаштування збережено."; @@ -2507,8 +2507,8 @@ // Room Preview "room_preview_invitation_format" = "%@ запрошує вас приєднатися до цієї кімнати"; -"threads_discourage_information_2" = "\n\nУсе одно увімкнути треди?"; -"threads_discourage_information_1" = "Наразі ваш домашній сервер не підтримує треди, тому ця функція може працювати нестабільно. Деякі повідомлення у тредах можуть бути недоступними. "; +"threads_discourage_information_2" = "\n\nУсе одно ввімкнути гілки?"; +"threads_discourage_information_1" = "Наразі ваш домашній сервер не підтримує гілки, тому ця функція може працювати нестабільно. Деякі повідомлення у гілках можуть бути недоступними. "; "room_ongoing_conference_call_with_close" = "Поточний груповий виклик. Приєднатися як %@ або %@. %@."; "room_ongoing_conference_call" = "Поточний груповий виклик. Приєднатися як %@ або %@."; "location_sharing_live_stop_sharing_progress" = "Припинити надсилати місцеперебування"; From f91ba56593cd5b257259d2646127f4477b4e3d55 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Tue, 28 Jun 2022 21:27:50 +0300 Subject: [PATCH 20/97] Prepare for new sprint --- Config/AppVersion.xcconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Config/AppVersion.xcconfig b/Config/AppVersion.xcconfig index c6678098a..72a69d031 100644 --- a/Config/AppVersion.xcconfig +++ b/Config/AppVersion.xcconfig @@ -15,5 +15,5 @@ // // Version -MARKETING_VERSION = 1.8.20 -CURRENT_PROJECT_VERSION = 1.8.20 +MARKETING_VERSION = 1.8.21 +CURRENT_PROJECT_VERSION = 1.8.21 From d521856f07a623cdde02a75ed815e1df05c49647 Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Tue, 28 Jun 2022 18:11:22 +0100 Subject: [PATCH 21/97] Use ZXing library to generate QR codes --- ...cationVerifyByScanningViewController.swift | 8 +++- Riot/Modules/QRCode/QRCodeGenerator.swift | 43 ++++++++----------- changelog.d/6358.bugfix | 1 + 3 files changed, 26 insertions(+), 26 deletions(-) create mode 100644 changelog.d/6358.bugfix diff --git a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewController.swift b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewController.swift index c68e4f358..c56d0fc0e 100644 --- a/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewController.swift +++ b/Riot/Modules/KeyVerification/Common/Verify/Scanning/KeyVerificationVerifyByScanningViewController.swift @@ -17,6 +17,7 @@ */ import UIKit +import MatrixSDK final class KeyVerificationVerifyByScanningViewController: UIViewController { @@ -215,7 +216,12 @@ final class KeyVerificationVerifyByScanningViewController: UIViewController { private func qrCodeImage(from data: Data) -> UIImage? { let codeGenerator = QRCodeGenerator() - return codeGenerator.generateCode(from: data, with: self.codeImageView.frame.size) + do { + return try codeGenerator.generateCode(from: data, with: codeImageView.frame.size) + } catch { + MXLog.error("[KeyVerificationVerifyByScanningViewController] qrCodeImage: cannot generate QR code - \(error)") + return nil + } } private func presentQRCodeReader(animated: Bool) { diff --git a/Riot/Modules/QRCode/QRCodeGenerator.swift b/Riot/Modules/QRCode/QRCodeGenerator.swift index f3ad32c05..4ac8e6f6e 100644 --- a/Riot/Modules/QRCode/QRCodeGenerator.swift +++ b/Riot/Modules/QRCode/QRCodeGenerator.swift @@ -15,36 +15,29 @@ */ import Foundation +import ZXingObjC final class QRCodeGenerator { - - // MARK: - Constants - - private enum Constants { - static let qrCodeGeneratorFilter = "CIQRCodeGenerator" - static let qrCodeInputCorrectionLevel = "M" + enum Error: Swift.Error { + case cannotCreateImage } - // MARK: - Public - - func generateCode(from data: Data, with size: CGSize) -> UIImage? { - guard let filter = CIFilter(name: Constants.qrCodeGeneratorFilter) else { - return nil + func generateCode(from data: Data, with size: CGSize) throws -> UIImage { + let writer = ZXMultiFormatWriter() + let endodedString = String(data: data, encoding: .isoLatin1) + let scale = UIScreen.main.scale + let bitMatrix = try writer.encode( + endodedString, + format: kBarcodeFormatQRCode, + width: Int32(size.width * scale), + height: Int32(size.height * scale), + hints: ZXEncodeHints() + ) + + guard let cgImage = ZXImage(matrix: bitMatrix).cgimage else { + throw Error.cannotCreateImage } - filter.setValue(data, forKey: "inputMessage") - filter.setValue(Constants.qrCodeInputCorrectionLevel, forKey: "inputCorrectionLevel") // Be sure to use same error resilience level as other platform - - guard let ciImage = filter.outputImage else { - return nil - } - - let scaleX = size.width/ciImage.extent.size.width - let scaleY = size.height/ciImage.extent.size.height - - let transform = CGAffineTransform(scaleX: scaleX, y: scaleY) - - let transformedCIImage = ciImage.transformed(by: transform) - return UIImage(ciImage: transformedCIImage) + return UIImage(cgImage: cgImage) } } diff --git a/changelog.d/6358.bugfix b/changelog.d/6358.bugfix new file mode 100644 index 000000000..a0e99dd20 --- /dev/null +++ b/changelog.d/6358.bugfix @@ -0,0 +1 @@ +Cross-Signing: Use ZXing library to generate QR codes From 260cc22d51bf65d24fe6fed2b0216b78cad7f6e4 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Wed, 29 Jun 2022 16:11:52 +0200 Subject: [PATCH 22/97] Update Files component iOS (#6345) * Update Files component iOS - Done --- .../Utils/EventFormatter/MXKEventFormatter.m | 17 ++++- .../FileWithoutThumbnailBaseBubbleCell.swift | 7 +- .../FileWithoutThumbnailCellContentView.swift | 11 ++- .../FileWithoutThumbnailCellContentView.xib | 57 ++++++++------ .../FileWithoutThumbnailPlainCell.swift | 75 +++++++++++++++++++ ...humbnailWithPaginationTitlePlainCell.swift | 25 +++++++ ...tThumbnailWithoutSenderInfoPlainCell.swift | 25 +++++++ .../Plain/PlainRoomTimelineCellProvider.m | 35 +++++---- .../VoiceMessagePlaybackView.swift | 5 +- .../VoiceMessagePlaybackView.xib | 12 +-- changelog.d/5372.change | 1 + 11 files changed, 221 insertions(+), 49 deletions(-) create mode 100644 Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailPlainCell.swift create mode 100644 Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailWithPaginationTitlePlainCell.swift create mode 100644 Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailWithoutSenderInfoPlainCell.swift create mode 100644 changelog.d/5372.change diff --git a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m index 1aa344af6..0ddecfb08 100644 --- a/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m +++ b/Riot/Modules/MatrixKit/Utils/EventFormatter/MXKEventFormatter.m @@ -1356,9 +1356,22 @@ static NSString *const kHTMLATagRegexPattern = @"( } else if ([msgtype isEqualToString:kMXMessageTypeFile]) { - body = body? body : [VectorL10n noticeFileAttachment]; // Check attachment validity - if (![self isSupportedAttachment:event]) + if ([self isSupportedAttachment:event]) + { + body = body? body : [VectorL10n noticeFileAttachment]; + + NSDictionary *fileInfo = contentToUse[@"info"]; + if (fileInfo) + { + NSNumber *fileSize = fileInfo[@"size"]; + if (fileSize) + { + body = [NSString stringWithFormat:@"%@ (%@)", body, [MXTools fileSizeToString: fileSize.longValue]]; + } + } + } + else { MXLogDebug(@"[MXKEventFormatter] Warning: Unsupported attachment %@", event.description); body = [VectorL10n noticeInvalidAttachment]; diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift index 40b485fa0..83c1b2885 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailBaseBubbleCell.swift @@ -22,8 +22,11 @@ class FileWithoutThumbnailBaseBubbleCell: SizableBaseRoomCell, RoomCellReactions override func render(_ cellData: MXKCellData!) { super.render(cellData) - - self.fileAttachementView?.titleLabel.attributedText = self.suitableAttributedTextMessage + + let attributedText = NSMutableAttributedString(attributedString: self.suitableAttributedTextMessage) + attributedText.addAttributes([.foregroundColor: ThemeService.shared().theme.colors.secondaryContent], + range: NSRange(location: 0, length: attributedText.length)) + self.fileAttachementView?.titleLabel.attributedText = attributedText self.update(theme: ThemeService.shared().theme) } diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift index 1d2058a27..0ca97d522 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.swift @@ -23,9 +23,10 @@ final class FileWithoutThumbnailCellContentView: UIView, NibLoadable { // MARK: Outlets + @IBOutlet private weak var iconBackgroundView: UIView! @IBOutlet private weak var iconImageView: UIImageView! @IBOutlet private(set) weak var titleLabel: UILabel! - + // MARK: Public var badgeImage: UIImage? { @@ -49,17 +50,23 @@ final class FileWithoutThumbnailCellContentView: UIView, NibLoadable { super.awakeFromNib() self.layer.masksToBounds = true + self.iconImageView.image = Asset.Images.fileAttachment.image.withRenderingMode(.alwaysTemplate) + self.iconBackgroundView.layer.masksToBounds = true + + update(theme: ThemeService.shared().theme) } override func layoutSubviews() { super.layoutSubviews() self.layer.cornerRadius = BubbleRoomCellLayoutConstants.bubbleCornerRadius + self.iconBackgroundView.layer.cornerRadius = self.iconBackgroundView.bounds.midX } // MARK: - Public func update(theme: Theme) { - self.titleLabel.textColor = theme.textPrimaryColor + self.iconBackgroundView.backgroundColor = theme.roomCellIncomingBubbleBackgroundColor + self.iconImageView.tintColor = theme.colors.secondaryContent } } diff --git a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.xib b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.xib index 21e33aef7..ebd9e1299 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.xib +++ b/Riot/Modules/Room/TimelineCells/Styles/Bubble/Cells/FileWithoutThumbnail/Common/FileWithoutThumbnailCellContentView.xib @@ -1,9 +1,9 @@ - + - + @@ -11,43 +11,56 @@ - + - - - - - - - - - + + + + + + + + + + + + - - - - + + + + + + + + + + + - + + + + diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailPlainCell.swift new file mode 100644 index 000000000..8dddf30d2 --- /dev/null +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailPlainCell.swift @@ -0,0 +1,75 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +class FileWithoutThumbnailPlainCell: SizableBaseRoomCell, RoomCellReactionsDisplayable, RoomCellReadMarkerDisplayable, RoomCellThreadSummaryDisplayable { + + private(set) var fileAttachementView: FileWithoutThumbnailCellContentView! + + override func render(_ cellData: MXKCellData!) { + super.render(cellData) + + guard let data = cellData as? RoomBubbleCellData else { + return + } + + guard data.attachment.type == .file else { + fatalError("Invalid attachment type passed to a file without thumbnail cell.") + } + + let attributedText = NSMutableAttributedString(attributedString: self.suitableAttributedTextMessage) + attributedText.addAttributes([.foregroundColor: ThemeService.shared().theme.colors.secondaryContent], + range: NSRange(location: 0, length: attributedText.length)) + self.fileAttachementView.titleLabel.attributedText = attributedText + + self.update(theme: ThemeService.shared().theme) + } + + override func setupViews() { + super.setupViews() + + roomCellContentView?.showSenderInfo = true + roomCellContentView?.showPaginationTitle = false + + guard let contentView = roomCellContentView?.innerContentView else { + return + } + + fileAttachementView = FileWithoutThumbnailCellContentView.loadFromNib() + contentView.vc_addSubViewMatchingParent(fileAttachementView) + } + + override func update(theme: Theme) { + super.update(theme: theme) + + guard let fileAttachementView = fileAttachementView else { + return + } + + fileAttachementView.update(theme: theme) + fileAttachementView.backgroundColor = theme.colors.quinaryContent + } + + override func onContentViewTap(_ sender: UITapGestureRecognizer!) { + + if let bubbleData = self.bubbleData, bubbleData.isAttachment { + self.delegate.cell(self, didRecognizeAction: kMXKRoomBubbleCellTapOnAttachmentView, userInfo: nil) + } else { + super.onContentViewTap(sender) + } + } +} diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailWithPaginationTitlePlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailWithPaginationTitlePlainCell.swift new file mode 100644 index 000000000..2fb5b512e --- /dev/null +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailWithPaginationTitlePlainCell.swift @@ -0,0 +1,25 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +class FileWithoutThumbnailWithPaginationTitlePlainCell: FileWithoutThumbnailPlainCell { + override func setupViews() { + super.setupViews() + + roomCellContentView?.showPaginationTitle = true + } +} diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailWithoutSenderInfoPlainCell.swift b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailWithoutSenderInfoPlainCell.swift new file mode 100644 index 000000000..810813ff2 --- /dev/null +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/Cells/FileWithoutThumbnail/FileWithoutThumbnailWithoutSenderInfoPlainCell.swift @@ -0,0 +1,25 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +class FileWithoutThumbnailWithoutSenderInfoPlainCell: FileWithoutThumbnailPlainCell { + override func setupViews() { + super.setupViews() + + roomCellContentView?.showSenderInfo = false + } +} diff --git a/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m index 466a1d665..f016761dd 100644 --- a/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m +++ b/Riot/Modules/Room/TimelineCells/Styles/Plain/PlainRoomTimelineCellProvider.m @@ -110,7 +110,9 @@ [self registerPollCellsForTableView:tableView]; [self registerLocationCellsForTableView:tableView]; - + + [self registerFileWithoutThumbnailCellsForTableView:tableView]; + [tableView registerClass:RoomEmptyBubbleCell.class forCellReuseIdentifier:RoomEmptyBubbleCell.defaultReuseIdentifier]; [tableView registerClass:RoomSelectedStickerBubbleCell.class forCellReuseIdentifier:RoomSelectedStickerBubbleCell.defaultReuseIdentifier]; @@ -261,6 +263,13 @@ [tableView registerClass:LocationWithPaginationTitlePlainCell.class forCellReuseIdentifier:LocationWithPaginationTitlePlainCell.defaultReuseIdentifier]; } +- (void)registerFileWithoutThumbnailCellsForTableView:(UITableView*)tableView +{ + [tableView registerClass:FileWithoutThumbnailPlainCell.class forCellReuseIdentifier:FileWithoutThumbnailPlainCell.defaultReuseIdentifier]; + [tableView registerClass:FileWithoutThumbnailWithoutSenderInfoPlainCell.class forCellReuseIdentifier:FileWithoutThumbnailWithoutSenderInfoPlainCell.defaultReuseIdentifier]; + [tableView registerClass:FileWithoutThumbnailWithPaginationTitlePlainCell.class forCellReuseIdentifier:FileWithoutThumbnailWithPaginationTitlePlainCell.defaultReuseIdentifier]; +} + #pragma mark Cell class association - (NSDictionary*)buildCellClasses @@ -435,13 +444,13 @@ { return @{ // Clear - @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnail) : RoomIncomingTextMsgBubbleCell.class, - @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailWithoutSenderInfo) : RoomIncomingTextMsgWithoutSenderInfoBubbleCell.class, - @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailWithPaginationTitle) : RoomIncomingTextMsgWithPaginationTitleBubbleCell.class, + @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnail) : FileWithoutThumbnailPlainCell.class, + @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailWithoutSenderInfo) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class, + @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailWithPaginationTitle) : FileWithoutThumbnailWithPaginationTitlePlainCell.class, // Encrypted - @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncrypted) : RoomIncomingEncryptedTextMsgBubbleCell.class, - @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncryptedWithoutSenderInfo) : RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.class, - @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncryptedWithPaginationTitle) : RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.class + @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncrypted) : FileWithoutThumbnailPlainCell.class, + @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncryptedWithoutSenderInfo) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class, + @(RoomTimelineCellIdentifierIncomingAttachmentWithoutThumbnailEncryptedWithPaginationTitle) : FileWithoutThumbnailWithPaginationTitlePlainCell.class }; } @@ -449,13 +458,13 @@ { return @{ // Clear - @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnail) : RoomOutgoingTextMsgBubbleCell.class, - @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailWithoutSenderInfo) : RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.class, - @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailWithPaginationTitle) : RoomOutgoingTextMsgWithPaginationTitleBubbleCell.class, + @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnail) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class, + @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailWithoutSenderInfo) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class, + @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailWithPaginationTitle) : FileWithoutThumbnailWithPaginationTitlePlainCell.class, // Encrypted - @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncrypted) : RoomOutgoingEncryptedTextMsgBubbleCell.class, - @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncryptedWithoutSenderInfo) : RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.class, - @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncryptedWithPaginationTitle) : RoomOutgoingEncryptedTextMsgWithPaginationTitleBubbleCell.class + @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncrypted) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class, + @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncryptedWithoutSenderInfo) : FileWithoutThumbnailWithoutSenderInfoPlainCell.class, + @(RoomTimelineCellIdentifierOutgoingAttachmentWithoutThumbnailEncryptedWithPaginationTitle) : FileWithoutThumbnailWithPaginationTitlePlainCell.class }; } diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.swift b/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.swift index c3abeb01f..b5fae41d8 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.swift +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.swift @@ -137,7 +137,7 @@ class VoiceMessagePlaybackView: UIView, NibLoadable, Themable { } self.backgroundColor = theme.colors.background - playButton.backgroundColor = theme.colors.background + playButton.backgroundColor = theme.roomCellIncomingBubbleBackgroundColor playButton.tintColor = theme.colors.secondaryContent let backgroundViewColor = self.customBackgroundViewColor ?? theme.colors.quinaryContent @@ -145,7 +145,8 @@ class VoiceMessagePlaybackView: UIView, NibLoadable, Themable { backgroundView.backgroundColor = backgroundViewColor _waveformView.primaryLineColor = theme.colors.quarterlyContent _waveformView.secondaryLineColor = theme.colors.secondaryContent - elapsedTimeLabel.textColor = theme.colors.tertiaryContent + elapsedTimeLabel.textColor = theme.colors.secondaryContent + elapsedTimeLabel.font = theme.fonts.body } func getRequiredNumberOfSamples() -> Int { diff --git a/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.xib b/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.xib index 943832e99..7b3011954 100644 --- a/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.xib +++ b/Riot/Modules/Room/VoiceMessages/VoiceMessagePlaybackView.xib @@ -1,9 +1,9 @@ - + - + @@ -22,7 +22,7 @@ - + @@ -48,7 +48,7 @@ - + @@ -66,8 +66,8 @@ - - + + diff --git a/changelog.d/5372.change b/changelog.d/5372.change new file mode 100644 index 000000000..cdf6e1433 --- /dev/null +++ b/changelog.d/5372.change @@ -0,0 +1 @@ +Update Files component From 08f8a8a7c446c463501482bb8862badec2d6a2c4 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 Jun 2022 18:09:48 +0200 Subject: [PATCH 23/97] RiotSettings: Send local notification on live location sharing activation changes. --- Riot/Managers/Settings/RiotSettings.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 634919814..f2dfc96cc 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -154,7 +154,11 @@ final class RiotSettings: NSObject { /// Indicates if live location sharing is enabled @UserDefault(key: UserDefaultsKeys.enableLiveLocationSharing, defaultValue: false, storage: defaults) - var enableLiveLocationSharing + var enableLiveLocationSharing { + didSet { + NotificationCenter.default.post(name: RiotSettings.didUpdateLiveLocationSharingActivation, object: self) + } + } // MARK: Calls @@ -375,3 +379,8 @@ final class RiotSettings: NSObject { @UserDefault(key: "lastNumberOfTrackedSpaces", defaultValue: nil, storage: defaults) var lastNumberOfTrackedSpaces: Int? } + +// MARK: - RiotSettings notification constants +extension RiotSettings { + public static let didUpdateLiveLocationSharingActivation = Notification.Name("RiotSettingsDidUpdateLiveLocationSharingActivation") +} From ce0f9be0f75871258d549ab7dd08ee625e00915c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 Jun 2022 18:10:28 +0200 Subject: [PATCH 24/97] UserLocationServiceProvider: Handle live location sharing lab flag activation changes. --- .../UserLocationServiceProvider.swift | 56 +++++++++++++++++-- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/Riot/Modules/LocationSharing/UserLocationServiceProvider.swift b/Riot/Modules/LocationSharing/UserLocationServiceProvider.swift index a9230b181..3ce1dcf60 100644 --- a/Riot/Modules/LocationSharing/UserLocationServiceProvider.swift +++ b/Riot/Modules/LocationSharing/UserLocationServiceProvider.swift @@ -26,16 +26,16 @@ class UserLocationServiceProvider { // MARK: - Properties + // UserLocationService per user id private var locationServices: [String: UserLocationServiceProtocol] = [:] // MARK: - Setup private init() { - guard RiotSettings.shared.enableLiveLocationSharing else { - return - } + self.setupOrTeardownLocationServices() - self.registerUserSessionsServiceNotifications() + // Listen to lab flag changes + self.registerRiotSettingsNotifications() } // MARK: - Public @@ -71,7 +71,7 @@ class UserLocationServiceProvider { MXLog.debug("Start monitoring user live location sharing") } - func setupUserLocationServiceIfNeeded(for userSession: UserSession) { + private func setupUserLocationServiceIfNeeded(for userSession: UserSession) { // Be sure Matrix session has is store setup to access beacon info summaries guard userSession.matrixSession.state.rawValue >= MXSessionState.storeDataReady.rawValue else { @@ -100,6 +100,30 @@ class UserLocationServiceProvider { MXLog.debug("Stop monitoring user live location sharing") } + private func setupOrTeardownLocationServices() { + + self.unregisterUserSessionsServiceNotifications() + + if RiotSettings.shared.enableLiveLocationSharing { + self.setupUserLocationServiceForAllUsers() + self.registerUserSessionsServiceNotifications() + } else { + self.tearDownUserLocationServiceForAllUsers() + } + } + + private func setupUserLocationServiceForAllUsers() { + for userSession in UserSessionsService.shared.userSessions { + self.setupUserLocationService(for: userSession) + } + } + + private func tearDownUserLocationServiceForAllUsers() { + for (userId, _) in self.locationServices { + self.tearDownUserLocationService(for: userId) + } + } + // MARK: UserSessions management private func registerUserSessionsServiceNotifications() { @@ -111,6 +135,15 @@ class UserLocationServiceProvider { NotificationCenter.default.addObserver(self, selector: #selector(userSessionsServiceDidRemoveUserSession(_:)), name: UserSessionsService.didRemoveUserSession, object: nil) } + private func unregisterUserSessionsServiceNotifications() { + + NotificationCenter.default.removeObserver(self, name: UserSessionsService.didAddUserSession, object: nil) + + NotificationCenter.default.removeObserver(self, name: UserSessionsService.userSessionDidChange, object: nil) + + NotificationCenter.default.removeObserver(self, name: UserSessionsService.didRemoveUserSession, object: nil) + } + @objc private func userSessionsServiceDidAddUserSession(_ notification: Notification) { guard let userInfo = notification.userInfo, let userSession = userInfo[UserSessionsService.NotificationUserInfoKey.userSession] as? UserSession else { @@ -137,4 +170,17 @@ class UserLocationServiceProvider { self.tearDownUserLocationService(for: userId) } + + // MARK: - RiotSettings + + private func registerRiotSettingsNotifications() { + + NotificationCenter.default.addObserver(self, selector: #selector(riotSettingsDidUpdateLiveLocationSharingActivation(_:)), name: RiotSettings.didUpdateLiveLocationSharingActivation, object: nil) + } + + @objc private func riotSettingsDidUpdateLiveLocationSharingActivation(_ notification: Notification) { + + // Lab flag value has changed, check if we should enable or disable location services + self.setupOrTeardownLocationServices() + } } From e9445a88ac429dc1d7f0bc530e4f236c05cff314 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 Jun 2022 18:10:58 +0200 Subject: [PATCH 25/97] LocationManager: Fix issue in stop method. --- Riot/Modules/LocationSharing/LocationManager.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/LocationSharing/LocationManager.swift b/Riot/Modules/LocationSharing/LocationManager.swift index a04a5d2b3..fb568f5af 100644 --- a/Riot/Modules/LocationSharing/LocationManager.swift +++ b/Riot/Modules/LocationSharing/LocationManager.swift @@ -112,7 +112,7 @@ class LocationManager: NSObject { switch accuracy { case .full: - self.locationManager.startUpdatingLocation() + self.locationManager.stopUpdatingLocation() case .reduced: self.locationManager.stopMonitoringSignificantLocationChanges() } From 2c5e6481b1b3b18b47df643b6e7929277c2fd66b Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 Jun 2022 18:14:26 +0200 Subject: [PATCH 26/97] Update changes --- changelog.d/6361.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6361.bugfix diff --git a/changelog.d/6361.bugfix b/changelog.d/6361.bugfix new file mode 100644 index 000000000..e514e9699 --- /dev/null +++ b/changelog.d/6361.bugfix @@ -0,0 +1 @@ +Location sharing: Fix live location sharing lab flag activation, no more app relaunch needed. \ No newline at end of file From 12d45570ee7199d6fc2f8a1948400a52d68e1f04 Mon Sep 17 00:00:00 2001 From: random Date: Wed, 29 Jun 2022 12:45:00 +0000 Subject: [PATCH 27/97] Translated using Weblate (Italian) Currently translated at 100.0% (2059 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index 0124a1283..7053448e7 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -2304,3 +2304,21 @@ "location_sharing_allow_background_location_title" = "Permetti accesso"; "settings_labs_enable_live_location_sharing" = "Condivisione posizione in tempo reale - condividi la posizione attuale (in sviluppo attivo e, per ora, le posizioni restano nella cronologia della stanza)"; "settings_ui_show_redactions_in_room_history" = "Mostra un segnaposto per i messaggi rimossi"; + +// MARK: Reactions + +"room_event_action_reaction_more" = "Altre %@"; +"leave_space_selection_no_rooms" = "Non selezionare alcuna stanza"; +"leave_space_selection_all_rooms" = "Seleziona tutte le stanze"; +"leave_space_selection_title" = "SELEZIONA STANZE"; +"leave_space_and_more_rooms" = "Esci dallo spazio e da %@ stanze"; +"leave_space_and_one_room" = "Esci dallo spazio e da 1 stanza"; + +// Mark: Leave space + +"leave_space_action" = "Esci dallo spazio"; +"spaces_feature_not_available" = "Questa funzionalità non è disponibile qui. Per ora puoi farlo con %@ sul tuo computer."; +"home_context_menu_mark_as_read" = "Segna come letto"; +"settings_timeline" = "LINEA TEMPORALE"; +"room_accessibility_record_voice_message_hint" = "Doppio tocco e tieni premuto per registrare."; +"room_accessibility_record_voice_message" = "Registra messaggio vocale"; From 14a08b9cd5681e15001164fcc6c34ea3ce86a3a7 Mon Sep 17 00:00:00 2001 From: lvre <7uu3qrbvm@relay.firefox.com> Date: Thu, 30 Jun 2022 02:59:38 +0000 Subject: [PATCH 28/97] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (2059 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/pt_BR/ --- Riot/Assets/pt_BR.lproj/Vector.strings | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Riot/Assets/pt_BR.lproj/Vector.strings b/Riot/Assets/pt_BR.lproj/Vector.strings index ea0746bf3..e58648ef8 100644 --- a/Riot/Assets/pt_BR.lproj/Vector.strings +++ b/Riot/Assets/pt_BR.lproj/Vector.strings @@ -2301,3 +2301,21 @@ "location_sharing_allow_background_location_title" = "Permitir acesso"; "settings_labs_enable_live_location_sharing" = "Compartilhamento de localização ao vivo - compartilhar localização atual (desenvolvimento ativo, e temporariamente, localizações persistem em histórico de sala)"; "settings_ui_show_redactions_in_room_history" = "Mostrar um placeholder para mensagens removidas"; + +// MARK: Reactions + +"room_event_action_reaction_more" = "Mais %@"; +"leave_space_selection_no_rooms" = "Selecionar nenhuma sala"; +"leave_space_selection_all_rooms" = "Selecionar todas as salas"; +"leave_space_selection_title" = "SELECIONAR SALAS"; +"leave_space_and_more_rooms" = "Sair de espaço e %@ salas"; +"leave_space_and_one_room" = "Sair de espaço e 1 sala"; + +// Mark: Leave space + +"leave_space_action" = "Sair de espaço"; +"spaces_feature_not_available" = "Esta funcionalidade não está disponível aqui. Por enquanto, você pode fazer isto com %@ em seu computador."; +"home_context_menu_mark_as_read" = "Marcar como lida"; +"settings_timeline" = "TIMELINE"; +"room_accessibility_record_voice_message_hint" = "Toque duplo e segure para gravar."; +"room_accessibility_record_voice_message" = "Gravar Mensagem de Voz"; From 5e63e4b0ef6b9ec8d12f1997f9bb9937153b8765 Mon Sep 17 00:00:00 2001 From: Ihor Hordiichuk Date: Wed, 29 Jun 2022 10:44:16 +0000 Subject: [PATCH 29/97] Translated using Weblate (Ukrainian) Currently translated at 100.0% (2059 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/uk/ --- Riot/Assets/uk.lproj/Vector.strings | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Riot/Assets/uk.lproj/Vector.strings b/Riot/Assets/uk.lproj/Vector.strings index 714cfef89..9c754920a 100644 --- a/Riot/Assets/uk.lproj/Vector.strings +++ b/Riot/Assets/uk.lproj/Vector.strings @@ -2529,3 +2529,21 @@ "location_sharing_allow_background_location_title" = "Дозволити доступ"; "settings_labs_enable_live_location_sharing" = "Поширення місцеперебування наживо - діліться поточним розташуванням (в активній розробці, місця тимчасово зберігаються в історії кімнат)"; "settings_ui_show_redactions_in_room_history" = "Показувати заповнювач для вилучених повідомлень"; + +// MARK: Reactions + +"room_event_action_reaction_more" = "Ще %@"; +"leave_space_selection_no_rooms" = "Не вибирати кімнати"; +"leave_space_selection_all_rooms" = "Вибрати всі кімнати"; +"leave_space_selection_title" = "ВИБРАТИ КІМНАТИ"; +"leave_space_and_more_rooms" = "Вийти з простору та %@ кімнат"; +"leave_space_and_one_room" = "Вийти з простору та однієї кімнати"; + +// Mark: Leave space + +"leave_space_action" = "Вийти з простору"; +"spaces_feature_not_available" = "Ця функція тут недоступна. Наразі ви можете це зробити з %@ на своєму комп’ютері."; +"home_context_menu_mark_as_read" = "Позначити прочитаним"; +"settings_timeline" = "СТРІЧКА"; +"room_accessibility_record_voice_message_hint" = "Двічі торкніться й утримуйте для запису."; +"room_accessibility_record_voice_message" = "Записати голосове повідомлення"; From f197549ac30ab7a5714a05b57b3f6c5a8cf34c7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Priit=20J=C3=B5er=C3=BC=C3=BCt?= Date: Wed, 29 Jun 2022 06:00:11 +0000 Subject: [PATCH 30/97] Translated using Weblate (Estonian) Currently translated at 100.0% (2059 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/et/ --- Riot/Assets/et.lproj/Vector.strings | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Riot/Assets/et.lproj/Vector.strings b/Riot/Assets/et.lproj/Vector.strings index 13ad7e2cb..922bd4580 100644 --- a/Riot/Assets/et.lproj/Vector.strings +++ b/Riot/Assets/et.lproj/Vector.strings @@ -2272,3 +2272,21 @@ "location_sharing_allow_background_location_title" = "Luba ligipääs asukohale"; "settings_labs_enable_live_location_sharing" = "Praeguse asukoha jagamine reaalajas (funktsionaalsus on arendamisel ning ajutiselt on asukohad jututoa ajaloos näha)"; "settings_ui_show_redactions_in_room_history" = "Näita kustutatud sõnumite asemel kohatäidet"; + +// MARK: Reactions + +"room_event_action_reaction_more" = "veel %@"; +"leave_space_selection_no_rooms" = "Ära vali ühtegi jututuba"; +"leave_space_selection_all_rooms" = "Vali kõik jututoad"; +"leave_space_selection_title" = "VALI JUTUTUBE"; +"leave_space_and_more_rooms" = "Lahku kogukonnakeskusest ja %@'st jututoast"; +"leave_space_and_one_room" = "Lahku kogukonnakeskusest ja 1'st jututoast"; + +// Mark: Leave space + +"leave_space_action" = "Lahku kogukonnakeskusest"; +"spaces_feature_not_available" = "See funktsionaalsus pole siin rakenduses saadaval. Seni saad vastavat võimalust kasutada %@'i versioonis tavaarvutis."; +"home_context_menu_mark_as_read" = "Märgi loetuks"; +"settings_timeline" = "AJAJOON"; +"room_accessibility_record_voice_message_hint" = "Salvestamiseks klõpsi kaks korda ja hoia."; +"room_accessibility_record_voice_message" = "Salvesta häälsõnum"; From fdb659305217f5f39b8af5eb188716c2dafd2aac Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Fri, 1 Jul 2022 10:32:57 +0200 Subject: [PATCH 31/97] Invite user UI is always hidden by the keyboard (#6348) * Invite user UI is always hidden by the keyboard - fixed --- .../Contacts/ContactsTableViewController.m | 9 +++++++++ .../Contacts/ContactsTableViewController.xib | 17 +++++++---------- .../ContactsPickerViewModel.swift | 1 + changelog.d/5341.bugfix | 1 + 4 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 changelog.d/5341.bugfix diff --git a/Riot/Modules/Contacts/ContactsTableViewController.m b/Riot/Modules/Contacts/ContactsTableViewController.m index 9f97d97fb..d56614b0a 100644 --- a/Riot/Modules/Contacts/ContactsTableViewController.m +++ b/Riot/Modules/Contacts/ContactsTableViewController.m @@ -115,6 +115,15 @@ }]; [self userInterfaceThemeDidChange]; + + if (@available(iOS 15.0, *)) + { + [[_contactsTableView.bottomAnchor constraintEqualToAnchor:self.view.keyboardLayoutGuide.topAnchor] setActive:YES]; + } + else + { + [[_contactsTableView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor] setActive:YES]; + } } - (void)userInterfaceThemeDidChange diff --git a/Riot/Modules/Contacts/ContactsTableViewController.xib b/Riot/Modules/Contacts/ContactsTableViewController.xib index 6d29ed62d..75cdfdcab 100644 --- a/Riot/Modules/Contacts/ContactsTableViewController.xib +++ b/Riot/Modules/Contacts/ContactsTableViewController.xib @@ -1,11 +1,9 @@ - - - - + + - + @@ -21,8 +19,8 @@ - - + + @@ -32,15 +30,14 @@ + - - - + diff --git a/Riot/Modules/Room/ParticipantsInviteModal/ContactsPicker/ContactsPickerViewModel.swift b/Riot/Modules/Room/ParticipantsInviteModal/ContactsPicker/ContactsPickerViewModel.swift index d4939f94c..008b504ee 100644 --- a/Riot/Modules/Room/ParticipantsInviteModal/ContactsPicker/ContactsPickerViewModel.swift +++ b/Riot/Modules/Room/ParticipantsInviteModal/ContactsPicker/ContactsPickerViewModel.swift @@ -126,6 +126,7 @@ class ContactsPickerViewModel: NSObject, ContactsPickerViewModelProtocol { contactsViewController.showSearch(true) contactsViewController.searchBar.placeholder = VectorL10n.roomParticipantsInviteAnotherUser + contactsViewController.searchBar.resignFirstResponder() // Apply the search pattern if any if currentSearchText != nil { diff --git a/changelog.d/5341.bugfix b/changelog.d/5341.bugfix new file mode 100644 index 000000000..bb2b1d244 --- /dev/null +++ b/changelog.d/5341.bugfix @@ -0,0 +1 @@ +Fixed Invite user UI is always hidden by the keyboard From 5f7ca39887ecec3af7b0d1786a159052e730e603 Mon Sep 17 00:00:00 2001 From: iaiz Date: Thu, 30 Jun 2022 22:05:22 +0000 Subject: [PATCH 32/97] Translated using Weblate (Spanish) Currently translated at 99.9% (2058 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/es/ --- Riot/Assets/es.lproj/Vector.strings | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Riot/Assets/es.lproj/Vector.strings b/Riot/Assets/es.lproj/Vector.strings index d2afa6093..bfef13906 100644 --- a/Riot/Assets/es.lproj/Vector.strings +++ b/Riot/Assets/es.lproj/Vector.strings @@ -2424,3 +2424,20 @@ "location_sharing_allow_background_location_validate_action" = "Ajustes"; "location_sharing_allow_background_location_title" = "Permitir acceso"; "settings_ui_show_redactions_in_room_history" = "Mostrar un indicador donde se haya eliminado un mensaje"; +"settings_timeline" = "LÍNEA DE TIEMPO"; +"room_accessibility_record_voice_message_hint" = "Toca dos veces y mantén para grabar."; + +// MARK: Reactions + +"room_event_action_reaction_more" = "%@ más"; +"leave_space_selection_no_rooms" = "No seleccionar ninguna sala"; +"leave_space_selection_all_rooms" = "Seleccionar todas las salas"; +"leave_space_selection_title" = "SELECCIONAR SALAS"; +"leave_space_and_more_rooms" = "Salir del espacio y %@ salas"; +"leave_space_and_one_room" = "Salir del espacio y 1 sala"; + +// Mark: Leave space + +"leave_space_action" = "Salir del espacio"; +"home_context_menu_mark_as_read" = "Marcar como leído"; +"room_accessibility_record_voice_message" = "Grabar mensaje de voz"; From a265345a09ab8b03bcc861dda0917fe1800f41e0 Mon Sep 17 00:00:00 2001 From: Linerly Date: Thu, 30 Jun 2022 12:33:14 +0000 Subject: [PATCH 33/97] Translated using Weblate (Indonesian) Currently translated at 100.0% (2059 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/id/ --- Riot/Assets/id.lproj/Vector.strings | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/Riot/Assets/id.lproj/Vector.strings b/Riot/Assets/id.lproj/Vector.strings index 9310d6f2e..2a3615008 100644 --- a/Riot/Assets/id.lproj/Vector.strings +++ b/Riot/Assets/id.lproj/Vector.strings @@ -2530,3 +2530,22 @@ "location_sharing_allow_background_location_message" = "Jika Anda ingin membagikan lokasi langsung Anda, Element membutuhkan akses lokasi ketika aplikasi berada di latar belakang.Untuk memberikan akses, ketuk Pengaturan> Lokasi dan pilih Selalu"; "location_sharing_allow_background_location_title" = "Perbolehkan akses"; "settings_labs_enable_live_location_sharing" = "Pembagian lokasi langsung — bagikan lokasi saat ini (dalam pengembangan aktif, dan sementara, lokasi tetap di riwayat ruangan)"; + +// MARK: Reactions + +"room_event_action_reaction_more" = "%@ lainnya"; +"leave_space_selection_no_rooms" = "Pilih tidak ada ruangan"; +"leave_space_selection_all_rooms" = "Pilih semua ruangan"; +"leave_space_selection_title" = "PILIH RUANGAN"; +"leave_space_and_more_rooms" = "Tinggalkan space dan %@ ruangan"; +"leave_space_and_one_room" = "Tinggalkan space dan 1 ruangan"; + +// Mark: Leave space + +"leave_space_action" = "Tinggalkan space"; +"spaces_feature_not_available" = "Fitur ini tidak tersedia di sini. Untuk sekarang, Anda dapat melakukannya dengan %@ di komputer Anda."; +"home_context_menu_mark_as_read" = "Tandai sebagai dibaca"; +"settings_ui_show_redactions_in_room_history" = "Tampilkan tampungan untuk pesan yang dihapus"; +"settings_timeline" = "LINIMASA"; +"room_accessibility_record_voice_message_hint" = "Ketuk dua kali dan tekan untuk merekam."; +"room_accessibility_record_voice_message" = "Rekam Pesan Suara"; From d4eb93d2001d68c00ee1bc5f79f42911ce39fbb8 Mon Sep 17 00:00:00 2001 From: Jozef Gaal Date: Thu, 30 Jun 2022 23:03:43 +0000 Subject: [PATCH 34/97] Translated using Weblate (Slovak) Currently translated at 100.0% (2059 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sk/ --- Riot/Assets/sk.lproj/Vector.strings | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Riot/Assets/sk.lproj/Vector.strings b/Riot/Assets/sk.lproj/Vector.strings index eae00e845..e79cfa97a 100644 --- a/Riot/Assets/sk.lproj/Vector.strings +++ b/Riot/Assets/sk.lproj/Vector.strings @@ -2527,3 +2527,21 @@ "location_sharing_allow_background_location_title" = "Povoliť prístup"; "settings_labs_enable_live_location_sharing" = "Zdieľanie polohy v reálnom čase - zdieľanie aktuálnej polohy (v aktívnom vývoji a polohy dočasne pretrvávajú v histórii miestnosti)"; "settings_ui_show_redactions_in_room_history" = "Zobrazovať náhrady za odstránené správy"; + +// MARK: Reactions + +"room_event_action_reaction_more" = "%@ viac"; +"leave_space_selection_no_rooms" = "Nevybrať žiadne miestnosti"; +"leave_space_selection_all_rooms" = "Vybrať všetky miestnosti"; +"leave_space_selection_title" = "VYBRAŤ MIESTNOSTI"; +"leave_space_and_more_rooms" = "Opustiť priestor a %@ miestností"; +"leave_space_and_one_room" = "Opustiť priestor a 1 miestnosť"; + +// Mark: Leave space + +"leave_space_action" = "Opustiť priestor"; +"spaces_feature_not_available" = "Táto funkcia tu nie je k dispozícii. Zatiaľ to môžete urobiť pomocou %@ na vašom počítači."; +"home_context_menu_mark_as_read" = "Označiť ako prečítané"; +"settings_timeline" = "ČASOVÁ OS"; +"room_accessibility_record_voice_message_hint" = "Ak chcete nahrávať, dvakrát ťuknite a podržte."; +"room_accessibility_record_voice_message" = "Nahrať hlasovú správu"; From 13e1c61a066a63929ffe8c5517da291d3436f3d0 Mon Sep 17 00:00:00 2001 From: Kaede Date: Sat, 2 Jul 2022 06:19:31 +0000 Subject: [PATCH 35/97] Translated using Weblate (Japanese) Currently translated at 69.1% (1423 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/ja/ --- Riot/Assets/ja.lproj/Vector.strings | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Riot/Assets/ja.lproj/Vector.strings b/Riot/Assets/ja.lproj/Vector.strings index a5421f605..e2a80f7c2 100644 --- a/Riot/Assets/ja.lproj/Vector.strings +++ b/Riot/Assets/ja.lproj/Vector.strings @@ -1640,3 +1640,19 @@ "existing" = "既存"; "new_word" = "新規"; "stop" = "停止"; +"spaces_creation_post_process_creating_space_task" = "%@を作成中"; +"side_menu_coach_message" = "右にスワイプまたはタップで全てのルームが表示されます"; +"spaces_creation_post_process_creating_space" = "スペースを作成中"; +"spaces_creation_add_rooms_message" = "このスペースはあなた専用のため、他の人に通知されることはありません。この設定は後から変更できます。"; +"spaces_creation_add_rooms_title" = "どれを追加しますか?"; +"spaces_creation_sharing_type_me_and_teammates_detail" = "あなたとチームメイトの非公開のスペース"; +"spaces_creation_sharing_type_me_and_teammates_title" = "自分とチームメイト"; +"spaces_creation_sharing_type_just_me_detail" = "ルームを整理するための非公開のスペース"; +"spaces_creation_sharing_type_just_me_title" = "自分専用"; +"spaces_creation_sharing_type_message" = "参加者を選択してください%@。この設定は後から変更できます。"; +"spaces_creation_settings_message" = "詳細を入力してください。この設定は後から変更できます。"; +"spaces_creation_address_default_message" = "スペースは以下のように表記されます\n%@"; +"space_settings_current_address_message" = "スペースは以下のように表記されます\n%@"; +"space_topic" = "説明文"; +"spaces_creation_cancel_message" = "進捗状況は失われます。"; +"spaces_creation_cancel_title" = "スペースの作成を停止しますか?"; From f484bd7284f0548779c656f8cdc297cd6020c763 Mon Sep 17 00:00:00 2001 From: aringenbach Date: Mon, 4 Jul 2022 15:51:33 +0200 Subject: [PATCH 36/97] Move MatrixKit defaults still in use to BuildSettings and replace register --- Config/BuildSettings.swift | 13 +++++++++ Riot/Modules/Application/LegacyAppDelegate.m | 28 +++++++++++++------- 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Config/BuildSettings.swift b/Config/BuildSettings.swift index 9d307cbef..31f0c737a 100644 --- a/Config/BuildSettings.swift +++ b/Config/BuildSettings.swift @@ -405,4 +405,17 @@ final class BuildSettings: NSObject { static let tileServerMapStyleURL = URL(string: "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx")! static let locationSharingEnabled = true + + // MARK: - MXKAppSettings + static let enableBotCreation: Bool = false + static let maxAllowedMediaCacheSize: Int = 1073741824 + static let presenceColorForOfflineUser: Int = 15020851 + static let presenceColorForOnlineUser: Int = 3401011 + static let presenceColorForUnavailableUser: Int = 15066368 + static let showAllEventsInRoomHistory: Bool = false + static let showLeftMembersInRoomMemberList: Bool = false + static let showRedactionsInRoomHistory: Bool = true + static let showUnsupportedEventsInRoomHistory: Bool = false + static let sortRoomMembersUsingLastSeenTime: Bool = true + static let syncLocalContacts: Bool = false } diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index b592ee311..316f80c18 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -4375,16 +4375,24 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni - (void)setupUserDefaults { - // Register "Riot-Defaults.plist" default values - NSString* userDefaults = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UserDefaults"]; - NSString *defaultsPathFromApp = [[NSBundle mainBundle] pathForResource:userDefaults ofType:@"plist"]; - NSMutableDictionary *defaults = [[NSDictionary dictionaryWithContentsOfFile:defaultsPathFromApp] mutableCopy]; - - // add pusher ids, as they don't belong to plist anymore - defaults[@"pushKitAppIdProd"] = BuildSettings.pushKitAppIdProd; - defaults[@"pushKitAppIdDev"] = BuildSettings.pushKitAppIdDev; - defaults[@"pusherAppIdProd"] = BuildSettings.pusherAppIdProd; - defaults[@"pusherAppIdDev"] = BuildSettings.pusherAppIdDev; + // Register MatrixKit defaults. + NSDictionary *defaults = @{ + @"enableBotCreation": @(BuildSettings.enableBotCreation), + @"maxAllowedMediaCacheSize": @(BuildSettings.maxAllowedMediaCacheSize), + @"presenceColorForOfflineUser": @(BuildSettings.presenceColorForOfflineUser), + @"presenceColorForOnlineUser": @(BuildSettings.presenceColorForOnlineUser), + @"presenceColorForUnavailableUser": @(BuildSettings.presenceColorForUnavailableUser), + @"showAllEventsInRoomHistory": @(BuildSettings.showAllEventsInRoomHistory), + @"showLeftMembersInRoomMemberList": @(BuildSettings.showLeftMembersInRoomMemberList), + @"showRedactionsInRoomHistory": @(BuildSettings.showRedactionsInRoomHistory), + @"showUnsupportedEventsInRoomHistory": @(BuildSettings.showUnsupportedEventsInRoomHistory), + @"sortRoomMembersUsingLastSeenTime": @(BuildSettings.syncLocalContacts), + @"syncLocalContacts": @(BuildSettings.syncLocalContacts), + @"pushKitAppIdProd": BuildSettings.pushKitAppIdProd, + @"pushKitAppIdDev": BuildSettings.pushKitAppIdDev, + @"pusherAppIdProd": BuildSettings.pusherAppIdProd, + @"pusherAppIdDev": BuildSettings.pusherAppIdDev + }; [[NSUserDefaults standardUserDefaults] registerDefaults:defaults]; From 1dbd1b928c0ec7c5a6f563243511241e1df34335 Mon Sep 17 00:00:00 2001 From: aringenbach Date: Mon, 4 Jul 2022 15:54:52 +0200 Subject: [PATCH 37/97] Remove plist and unused SwiftGen config and generated file --- Riot/Assets/Riot-Defaults.plist | 38 ---------------- Riot/Generated/RiotDefaults.swift | 73 ------------------------------ Riot/SupportingFiles/Info.plist | 2 - Tools/SwiftGen/swiftgen-config.yml | 9 +--- 4 files changed, 1 insertion(+), 121 deletions(-) delete mode 100644 Riot/Assets/Riot-Defaults.plist delete mode 100644 Riot/Generated/RiotDefaults.swift diff --git a/Riot/Assets/Riot-Defaults.plist b/Riot/Assets/Riot-Defaults.plist deleted file mode 100644 index 06791225c..000000000 --- a/Riot/Assets/Riot-Defaults.plist +++ /dev/null @@ -1,38 +0,0 @@ - - - - - pinRoomsWithMissedNotif - - pinRoomsWithUnread - - matrixApps - - showAllEventsInRoomHistory - - showRedactionsInRoomHistory - - showUnsupportedEventsInRoomHistory - - sortRoomMembersUsingLastSeenTime - - showLeftMembersInRoomMemberList - - syncLocalContacts - - enableRageShake - - maxAllowedMediaCacheSize - 1073741824 - presenceColorForOnlineUser - 3401011 - presenceColorForUnavailableUser - 15066368 - presenceColorForOfflineUser - 15020851 - enableBotCreation - - enableRingingForGroupCalls - - - diff --git a/Riot/Generated/RiotDefaults.swift b/Riot/Generated/RiotDefaults.swift deleted file mode 100644 index 793d63662..000000000 --- a/Riot/Generated/RiotDefaults.swift +++ /dev/null @@ -1,73 +0,0 @@ -// swiftlint:disable all -// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen - -import Foundation - -// swiftlint:disable superfluous_disable_command -// swiftlint:disable file_length - -// MARK: - Plist Files - -// swiftlint:disable identifier_name line_length type_body_length -internal enum RiotDefaults { - private static let _document = PlistDocument(path: "Riot-Defaults.plist") - - internal static let enableBotCreation: Bool = _document["enableBotCreation"] - internal static let enableRageShake: Bool = _document["enableRageShake"] - internal static let enableRingingForGroupCalls: Bool = _document["enableRingingForGroupCalls"] - internal static let matrixApps: Bool = _document["matrixApps"] - internal static let maxAllowedMediaCacheSize: Int = _document["maxAllowedMediaCacheSize"] - internal static let pinRoomsWithMissedNotif: Bool = _document["pinRoomsWithMissedNotif"] - internal static let pinRoomsWithUnread: Bool = _document["pinRoomsWithUnread"] - internal static let presenceColorForOfflineUser: Int = _document["presenceColorForOfflineUser"] - internal static let presenceColorForOnlineUser: Int = _document["presenceColorForOnlineUser"] - internal static let presenceColorForUnavailableUser: Int = _document["presenceColorForUnavailableUser"] - internal static let showAllEventsInRoomHistory: Bool = _document["showAllEventsInRoomHistory"] - internal static let showLeftMembersInRoomMemberList: Bool = _document["showLeftMembersInRoomMemberList"] - internal static let showRedactionsInRoomHistory: Bool = _document["showRedactionsInRoomHistory"] - internal static let showUnsupportedEventsInRoomHistory: Bool = _document["showUnsupportedEventsInRoomHistory"] - internal static let sortRoomMembersUsingLastSeenTime: Bool = _document["sortRoomMembersUsingLastSeenTime"] - internal static let syncLocalContacts: Bool = _document["syncLocalContacts"] -} -// swiftlint:enable identifier_name line_length type_body_length - -// MARK: - Implementation Details - -private func arrayFromPlist(at path: String) -> [T] { - guard let url = BundleToken.bundle.url(forResource: path, withExtension: nil), - let data = NSArray(contentsOf: url) as? [T] else { - fatalError("Unable to load PLIST at path: \(path)") - } - return data -} - -private struct PlistDocument { - let data: [String: Any] - - init(path: String) { - guard let url = BundleToken.bundle.url(forResource: path, withExtension: nil), - let data = NSDictionary(contentsOf: url) as? [String: Any] else { - fatalError("Unable to load PLIST at path: \(path)") - } - self.data = data - } - - subscript(key: String) -> T { - guard let result = data[key] as? T else { - fatalError("Property '\(key)' is not of type \(T.self)") - } - return result - } -} - -// swiftlint:disable convenience_type -private final class BundleToken { - static let bundle: Bundle = { - #if SWIFT_PACKAGE - return Bundle.module - #else - return Bundle(for: BundleToken.self) - #endif - }() -} -// swiftlint:enable convenience_type diff --git a/Riot/SupportingFiles/Info.plist b/Riot/SupportingFiles/Info.plist index 668798a9a..4eaf0aa24 100644 --- a/Riot/SupportingFiles/Info.plist +++ b/Riot/SupportingFiles/Info.plist @@ -139,8 +139,6 @@ im.vector.app.pills - UserDefaults - ${PRODUCT_NAME}-Defaults applicationGroupIdentifier $(APPLICATION_GROUP_IDENTIFIER) baseBundleIdentifier diff --git a/Tools/SwiftGen/swiftgen-config.yml b/Tools/SwiftGen/swiftgen-config.yml index 957f70fae..7c498ccbc 100755 --- a/Tools/SwiftGen/swiftgen-config.yml +++ b/Tools/SwiftGen/swiftgen-config.yml @@ -29,11 +29,4 @@ xcassets: - Assets/SharedImages.xcassets outputs: templatePath: Templates/Assets/swift4-element.stencil - output: Images.swift -plist: - inputs: Assets/Riot-Defaults.plist - outputs: - templateName: runtime-swift4 - output: RiotDefaults.swift - params: - enumName: RiotDefaults + output: Images.swift \ No newline at end of file From 38ba91fe6831714003b0e6a622c489e935cf5073 Mon Sep 17 00:00:00 2001 From: aringenbach Date: Mon, 4 Jul 2022 15:56:37 +0200 Subject: [PATCH 38/97] Add changelog --- changelog.d/6273.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6273.change diff --git a/changelog.d/6273.change b/changelog.d/6273.change new file mode 100644 index 000000000..28212eb87 --- /dev/null +++ b/changelog.d/6273.change @@ -0,0 +1 @@ +Remove legacy Riot-Defaults property list From 3af0cfbae60ec8c1df18c90c02f06f370bfa1476 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 4 Jul 2022 16:55:30 +0200 Subject: [PATCH 39/97] Strings: Add lab flag promotion strings. --- Riot/Assets/en.lproj/Vector.strings | 4 ++++ Riot/Generated/Strings.swift | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 64b115a23..ea51fbe5f 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2192,6 +2192,10 @@ To enable access, tap Settings> Location and select Always"; "location_sharing_live_stop_sharing_error" = "Fail to stop sharing location"; "location_sharing_live_stop_sharing_progress" = "Stop location sharing"; +"location_sharing_live_lab_promotion_title" = "Live location sharing"; +"location_sharing_live_lab_promotion_text" = "Please note: this is a labs feature using a temporary implementation that allows the history of your shared location to be permanently visible to other people in the room."; +"location_sharing_live_lab_promotion_activation" = "Enable live location sharing"; + // MARK: - MatrixKit diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index a40347d7c..bf20b1740 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2815,6 +2815,18 @@ public class VectorL10n: NSObject { public static var locationSharingLiveError: String { return VectorL10n.tr("Vector", "location_sharing_live_error") } + /// Enable live location sharing + public static var locationSharingLiveLabPromotionActivation: String { + return VectorL10n.tr("Vector", "location_sharing_live_lab_promotion_activation") + } + /// Please note: this is a labs feature using a temporary implementation that allows the history of your shared location to be permanently visible to other people in the room. + public static var locationSharingLiveLabPromotionText: String { + return VectorL10n.tr("Vector", "location_sharing_live_lab_promotion_text") + } + /// Live location sharing + public static var locationSharingLiveLabPromotionTitle: String { + return VectorL10n.tr("Vector", "location_sharing_live_lab_promotion_title") + } /// You public static var locationSharingLiveListItemCurrentUserDisplayName: String { return VectorL10n.tr("Vector", "location_sharing_live_list_item_current_user_display_name") From 000d15e240fcc8793f482720a26454dbe5bc4515 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 4 Jul 2022 17:09:11 +0200 Subject: [PATCH 40/97] Create live location sharing lab flag promotion screen. --- .../LiveLocationLabPromotionModels.swift | 32 +++++++ .../LiveLocationLabPromotionViewModel.swift | 48 +++++++++++ ...ocationLabPromotionViewModelProtocol.swift | 23 +++++ ...kLiveLocationLabPromotionScreenState.swift | 44 ++++++++++ .../UI/LiveLocationLabPromotionUITests.swift | 22 +++++ ...veLocationLabPromotionViewModelTests.swift | 23 +++++ .../View/LiveLocationLabPromotionView.swift | 84 +++++++++++++++++++ 7 files changed, 276 insertions(+) create mode 100644 RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionModels.swift create mode 100644 RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModel.swift create mode 100644 RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModelProtocol.swift create mode 100644 RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/MockLiveLocationLabPromotionScreenState.swift create mode 100644 RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Test/UI/LiveLocationLabPromotionUITests.swift create mode 100644 RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Test/Unit/LiveLocationLabPromotionViewModelTests.swift create mode 100644 RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/View/LiveLocationLabPromotionView.swift diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionModels.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionModels.swift new file mode 100644 index 000000000..266b2d01c --- /dev/null +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionModels.swift @@ -0,0 +1,32 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +// MARK: View + +struct LiveLocationLabPromotionViewState: BindableState { + + var bindings: LiveLocationLabPromotionBindings +} + +struct LiveLocationLabPromotionBindings { + var enableLabFlag: Bool +} + +enum LiveLocationLabPromotionViewAction { + case complete +} diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModel.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModel.swift new file mode 100644 index 000000000..eb4ea189c --- /dev/null +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModel.swift @@ -0,0 +1,48 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +typealias LiveLocationLabPromotionViewModelType = StateStoreViewModel + +class LiveLocationLabPromotionViewModel: LiveLocationLabPromotionViewModelType, LiveLocationLabPromotionViewModelProtocol { + + // MARK: - Properties + + // MARK: Private + + // MARK: Public + + var completion: ((Bool) -> Void)? + + // MARK: - Setup + + init() { + let bindings = LiveLocationLabPromotionBindings(enableLabFlag: false) + super.init(initialViewState: LiveLocationLabPromotionViewState(bindings: bindings)) + } + + // MARK: - Public + + override func process(viewAction: LiveLocationLabPromotionViewAction) { + switch viewAction { + case .complete: + completion?(self.state.bindings.enableLabFlag) + } + } +} diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModelProtocol.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModelProtocol.swift new file mode 100644 index 000000000..358436277 --- /dev/null +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModelProtocol.swift @@ -0,0 +1,23 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +protocol LiveLocationLabPromotionViewModelProtocol { + + var completion: ((Bool) -> Void)? { get set } + var context: LiveLocationLabPromotionViewModelType.Context { get } +} diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/MockLiveLocationLabPromotionScreenState.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/MockLiveLocationLabPromotionScreenState.swift new file mode 100644 index 000000000..f062bfe5f --- /dev/null +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/MockLiveLocationLabPromotionScreenState.swift @@ -0,0 +1,44 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import SwiftUI + +/// Using an enum for the screen allows you define the different state cases with +/// the relevant associated data for each case. +enum MockLiveLocationLabPromotionScreenState: MockScreenState, CaseIterable { + // A case for each state you want to represent + // with specific, minimal associated data that will allow you + // mock that screen. + case labFlagOff + + /// The associated screen + var screenType: Any.Type { + LiveLocationLabPromotionView.self + } + + /// Generate the view struct for the screen state. + var screenView: ([Any], AnyView) { + let viewModel = LiveLocationLabPromotionViewModel() + + // can simulate service and viewModel actions here if needs be. + + return ( + [self, viewModel], + AnyView(LiveLocationLabPromotionView(viewModel: viewModel.context) + )) + } +} diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Test/UI/LiveLocationLabPromotionUITests.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Test/UI/LiveLocationLabPromotionUITests.swift new file mode 100644 index 000000000..20fe9f0a5 --- /dev/null +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Test/UI/LiveLocationLabPromotionUITests.swift @@ -0,0 +1,22 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import RiotSwiftUI + +class LiveLocationLabPromotionUITests: MockScreenTest { + // Nothing to test as the view is completely static +} diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Test/Unit/LiveLocationLabPromotionViewModelTests.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Test/Unit/LiveLocationLabPromotionViewModelTests.swift new file mode 100644 index 000000000..284b27694 --- /dev/null +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Test/Unit/LiveLocationLabPromotionViewModelTests.swift @@ -0,0 +1,23 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest + +@testable import RiotSwiftUI + +class LiveLocationLabPromotionViewModelTests: XCTestCase { + // Nothing to test as there is no mutable state +} diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/View/LiveLocationLabPromotionView.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/View/LiveLocationLabPromotionView.swift new file mode 100644 index 000000000..7ebbda8a7 --- /dev/null +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/View/LiveLocationLabPromotionView.swift @@ -0,0 +1,84 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +struct LiveLocationLabPromotionView: View { + + // MARK: - Properties + + // MARK: Private + + @Environment(\.theme) private var theme + + @ObservedObject var viewModel: LiveLocationLabPromotionViewModel.Context + + // MARK: - View + + var body: some View { + VStack { + VStack { + Image(uiImage: Asset.Images.locationLiveIcon.image) + .resizable() + .frame(width: 60, height: 60) + .padding(.top, 15) + + Text(VectorL10n.locationSharingLiveLabPromotionTitle) + .font(theme.fonts.title2B) + .multilineTextAlignment(.center) + .foregroundColor(theme.colors.primaryContent) + .padding(.top, 15) + + Text(VectorL10n.locationSharingLiveLabPromotionText) + .font(theme.fonts.body) + .multilineTextAlignment(.center) + .foregroundColor(theme.colors.primaryContent) + .padding(.top, 1) + + Toggle(isOn: $viewModel.enableLabFlag) { + + Text(VectorL10n.locationSharingLiveLabPromotionActivation) + .font(theme.fonts.body) + .foregroundColor(theme.colors.primaryContent) + } + .padding(.top) + + Button { + self.viewModel.send(viewAction: .complete) + } label: { + Text(VectorL10n.ok) + .font(theme.fonts.bodySB) + } + .buttonStyle(PrimaryActionButtonStyle()) + .padding(.top) + } + .padding() + } + .frame(maxHeight: .infinity) + .background(theme.colors.background.ignoresSafeArea()) + .accentColor(theme.colors.accent) + } +} + +// MARK: - Previews + +@available(iOS 15.0, *) +struct LiveLocationLabPromotion_Previews: PreviewProvider { + static let stateRenderer = MockLiveLocationLabPromotionScreenState.stateRenderer + static var previews: some View { + stateRenderer.screenGroup() + } +} From 9b78ab4e5659a04efd07c568877fd81440d98dfa Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 4 Jul 2022 17:10:15 +0200 Subject: [PATCH 41/97] MockAppScreens: Add live location lab flag promotion screen. --- RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift index a55633467..c53ee2bdc 100644 --- a/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift +++ b/RiotSwiftUI/Modules/Common/Mock/MockAppScreens.swift @@ -19,6 +19,7 @@ import Foundation /// The static list of mocked screens in RiotSwiftUI enum MockAppScreens { static let appScreens: [MockScreenState.Type] = [ + MockLiveLocationLabPromotionScreenState.self, MockLiveLocationSharingViewerScreenState.self, MockAuthenticationLoginScreenState.self, MockAuthenticationReCaptchaScreenState.self, From ccc199075f080a24845fb3190e9cfa0eeffda5b8 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 4 Jul 2022 17:21:37 +0200 Subject: [PATCH 42/97] Add live location lab flag promotion coordinator. --- .../LiveLocationLabPromotionCoordinator.swift | 61 +++++++++++++++++++ ...ocationLabPromotionViewModelProtocol.swift | 2 + 2 files changed, 63 insertions(+) create mode 100644 RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift new file mode 100644 index 000000000..353125491 --- /dev/null +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift @@ -0,0 +1,61 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +final class LiveLocationLabPromotionCoordinator: Coordinator, Presentable { + + // MARK: - Properties + + // MARK: Private + + private let liveLocationLabPromotionHostingController: VectorHostingController + private var liveLocationLabPromotionViewModel: LiveLocationLabPromotionViewModelProtocol + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + /// Closure called when coordinator completes. Indicates true if the lab flag has been enabled. + var completion: ((Bool) -> Void)? + + // MARK: - Setup + + init() { + let viewModel = LiveLocationLabPromotionViewModel() + let view = LiveLocationLabPromotionView(viewModel: viewModel.context) + liveLocationLabPromotionViewModel = viewModel + liveLocationLabPromotionHostingController = VectorHostingController(rootView: view) + } + + // MARK: - Public + func start() { + MXLog.debug("[LiveLocationLabPromotionCoordinator] did start.") + + self.liveLocationLabPromotionViewModel.completion = { [weak self] enableLiveLocation in + guard let self = self else { return } + + RiotSettings.shared.enableLiveLocationSharing = enableLiveLocation + + self.completion?(enableLiveLocation) + } + } + + func toPresentable() -> UIViewController { + return self.liveLocationLabPromotionHostingController + } +} diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModelProtocol.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModelProtocol.swift index 358436277..a8d741751 100644 --- a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModelProtocol.swift +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/LiveLocationLabPromotionViewModelProtocol.swift @@ -18,6 +18,8 @@ import Foundation protocol LiveLocationLabPromotionViewModelProtocol { + /// Closure called when screen completes. Indicates true if the lab flag has been enabled. var completion: ((Bool) -> Void)? { get set } + var context: LiveLocationLabPromotionViewModelType.Context { get } } From afa90b0270982c63fb1db04bf47fe4492a1ab35c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 4 Jul 2022 17:23:25 +0200 Subject: [PATCH 43/97] LocationSharingService: Ensure to have the latest UserLocationService from MXSession. --- .../Service/MatrixSDK/LocationSharingService.swift | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/Service/MatrixSDK/LocationSharingService.swift b/RiotSwiftUI/Modules/Room/LocationSharing/Service/MatrixSDK/LocationSharingService.swift index 6d40d2ed9..d7692c29e 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/Service/MatrixSDK/LocationSharingService.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/Service/MatrixSDK/LocationSharingService.swift @@ -24,20 +24,26 @@ class LocationSharingService: LocationSharingServiceProtocol { // MARK: Private - private let userLocationService: UserLocationServiceProtocol? + private let session: MXSession + + private var userLocationService: UserLocationServiceProtocol? { + return self.session.userLocationService + } // MARK: Public // MARK: - Setup - init(userLocationService: UserLocationServiceProtocol?) { - self.userLocationService = userLocationService + init(session: MXSession) { + self.session = session } // MARK: - Public func requestAuthorization(_ handler: @escaping LocationAuthorizationHandler) { guard let userLocationService = self.userLocationService else { + + MXLog.error("[LocationSharingService] No userLocationService found for the current session") handler(LocationAuthorizationStatus.unknown) return } From ee58a8be09c4cc833b190e679155d2c318c31966 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 4 Jul 2022 17:39:45 +0200 Subject: [PATCH 44/97] LocationSharingCoordinator: Handle live location sharing lab flag presentation. --- .../LocationSharingCoordinator.swift | 41 +++++++++++++++++-- .../LocationSharingModels.swift | 1 + .../LocationSharingViewModel.swift | 15 ++++++- 3 files changed, 53 insertions(+), 4 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/LocationSharingCoordinator.swift b/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/LocationSharingCoordinator.swift index 63158ebf8..f396c1d88 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/LocationSharingCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/Coordinator/LocationSharingCoordinator.swift @@ -76,12 +76,13 @@ final class LocationSharingCoordinator: Coordinator, Presentable { init(parameters: LocationSharingCoordinatorParameters) { self.parameters = parameters - - let locationSharingService = LocationSharingService(userLocationService: parameters.roomDataSource.mxSession.userLocationService) + let locationSharingService = LocationSharingService(session: parameters.roomDataSource.mxSession) let viewModel = LocationSharingViewModel(mapStyleURL: BuildSettings.tileServerMapStyleURL, avatarData: parameters.avatarData, - isLiveLocationSharingEnabled: RiotSettings.shared.enableLiveLocationSharing, service: locationSharingService) + isLiveLocationSharingEnabled: true, + service: locationSharingService) + let view = LocationSharingView(context: viewModel.context) .addDependency(AvatarService.instantiate(mediaManager: parameters.mediaManager)) @@ -101,6 +102,8 @@ final class LocationSharingCoordinator: Coordinator, Presentable { self.shareStaticLocation(latitude: latitude, longitude: longitude, coordinateType: coordinateType) case .shareLiveLocation(let timeout): self.startLiveLocationSharing(with: timeout) + case .showLabFlagPromotionIfNeeded(let completion): + self.showLabFlagPromotionIfNeeded(completion: completion) } } } @@ -160,6 +163,38 @@ final class LocationSharingCoordinator: Coordinator, Presentable { } } + private func showLabFlagPromotionIfNeeded(completion: @escaping ((Bool) -> Void)) { + guard RiotSettings.shared.enableLiveLocationSharing == false else { + // Live location sharing lab flag is already enabled, do not present lab flag promotion screen + completion(true) + return + } + + self.showLabFlagPromotion(completion: completion) + } + + private func showLabFlagPromotion(completion: @escaping ((Bool) -> Void)) { + + // TODO: Use a NavigationRouter instead of using NavigationView inside LocationSharingView + // In order to use `NavigationRouter.present` + + let coordinator = LiveLocationLabPromotionCoordinator() + coordinator.start() + + coordinator.completion = { [weak self, weak coordinator] enableLiveLocation in + guard let self = self, let coordinator = coordinator else { return } + completion(enableLiveLocation) + + coordinator.toPresentable().dismiss(animated: true) { + self.remove(childCoordinator: coordinator) + } + } + + self.locationSharingHostingController.present(coordinator.toPresentable(), animated: true) + + self.add(childCoordinator: coordinator) + } + // MARK: - Presentable func toPresentable() -> UIViewController { diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingModels.swift b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingModels.swift index 22720eeff..82445ac5e 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingModels.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingModels.swift @@ -46,6 +46,7 @@ enum LocationSharingViewModelResult { case cancel case share(latitude: Double, longitude: Double, coordinateType: LocationSharingCoordinateType) case shareLiveLocation(timeout: TimeInterval) + case showLabFlagPromotionIfNeeded(_ completion: ((Bool) -> Void)) } enum LocationSharingViewError { diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift index f7d1de9d3..91dc1153b 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift @@ -138,7 +138,7 @@ class LocationSharingViewModel: LocationSharingViewModelType, LocationSharingVie } } - private func startLiveLocationSharing() { + private func checkLocationAuthorizationAndPresentTimerSelector() { self.locationSharingService.requestAuthorization { [weak self] authorizationStatus in @@ -165,4 +165,17 @@ class LocationSharingViewModel: LocationSharingViewModelType, LocationSharingVie } } } + + private func startLiveLocationSharing() { + + guard let completion = completion else { + return + } + + completion(.showLabFlagPromotionIfNeeded({ liveLocationEnabled in + if liveLocationEnabled { + self.checkLocationAuthorizationAndPresentTimerSelector() + } + })) + } } From 2ea8268c444e6daf27a1b07aeaebd49e844aaf35 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 4 Jul 2022 17:40:15 +0200 Subject: [PATCH 45/97] LiveLocationLabPromotionCoordinator: Handle interactive dismiss. --- .../LiveLocationLabPromotionCoordinator.swift | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift index 353125491..a3ccb737f 100644 --- a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift @@ -16,7 +16,7 @@ import SwiftUI -final class LiveLocationLabPromotionCoordinator: Coordinator, Presentable { +final class LiveLocationLabPromotionCoordinator: NSObject, Coordinator, Presentable { // MARK: - Properties @@ -35,14 +35,17 @@ final class LiveLocationLabPromotionCoordinator: Coordinator, Presentable { // MARK: - Setup - init() { + override init() { let viewModel = LiveLocationLabPromotionViewModel() let view = LiveLocationLabPromotionView(viewModel: viewModel.context) liveLocationLabPromotionViewModel = viewModel - liveLocationLabPromotionHostingController = VectorHostingController(rootView: view) + liveLocationLabPromotionHostingController = VectorHostingController(rootView: view) + + super.init() } // MARK: - Public + func start() { MXLog.debug("[LiveLocationLabPromotionCoordinator] did start.") @@ -53,9 +56,20 @@ final class LiveLocationLabPromotionCoordinator: Coordinator, Presentable { self.completion?(enableLiveLocation) } + + liveLocationLabPromotionHostingController.presentationController?.delegate = self } func toPresentable() -> UIViewController { return self.liveLocationLabPromotionHostingController } } + +// MARK: - UIAdaptivePresentationControllerDelegate + +extension LiveLocationLabPromotionCoordinator: UIAdaptivePresentationControllerDelegate { + + func presentationControllerDidDismiss(_ presentationController: UIPresentationController) { + self.completion?(RiotSettings.shared.enableLiveLocationSharing) + } +} From 66b9c70ad8a74e50da5d07f7826593e4f4c25f8a Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 4 Jul 2022 17:44:40 +0200 Subject: [PATCH 46/97] Update changes --- changelog.d/6238.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6238.change diff --git a/changelog.d/6238.change b/changelog.d/6238.change new file mode 100644 index 000000000..798c5f25a --- /dev/null +++ b/changelog.d/6238.change @@ -0,0 +1 @@ +Location sharing: Add view to promote live location sharing labs flag on the sharing screen. From 57189a6a30db333e9d4b7e8bcd7b9ef71aca8198 Mon Sep 17 00:00:00 2001 From: Johannes Marbach Date: Tue, 5 Jul 2022 08:18:21 +0200 Subject: [PATCH 47/97] Fix layout issues in timeline poll cells (PSB-125) Fixes: #5326 Signed-off-by: Johannes Marbach --- .../SwiftUI/VectorHostingController.swift | 91 +++++++++++++++++++ .../Coordinator/TimelinePollCoordinator.swift | 4 +- changelog.d/5326.bugfix | 1 + 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 changelog.d/5326.bugfix diff --git a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift index c636350b1..a4577cd75 100644 --- a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift +++ b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift @@ -27,6 +27,16 @@ class VectorHostingController: UIHostingController { var isNavigationBarHidden: Bool = false var hidesBackTitleWhenPushed: Bool = false + + var forceZeroSafeAreaInsets: Bool { + get { + self.view.forceZeroSafeAreaInsets + } + set { + self.view.forceZeroSafeAreaInsets = newValue + } + } + private var theme: Theme // MARK: Public @@ -43,6 +53,7 @@ class VectorHostingController: UIHostingController { init(rootView: Content) where Content: View { self.theme = ThemeService.shared().theme super.init(rootView: AnyView(rootView.vectorContent())) + self.view.swizzleSafeAreaMethodsIfNeeded() } required init?(coder aDecoder: NSCoder) { @@ -103,3 +114,83 @@ class VectorHostingController: UIHostingController { } } } + +// Hack for forcing zero safe area insets on hosting views. This problem occurs when the hosting view is embedded +// in a table view. See https://stackoverflow.com/questions/61552497 for further info. + +private var hasSwizzledSafeAreaMethods = false +private var forceZeroSafeAreaInsetsKey: Void? + +private extension UIView { + + var forceZeroSafeAreaInsets: Bool { + get { + return objc_getAssociatedObject(self, &forceZeroSafeAreaInsetsKey) as? Bool == true + } + set { + objc_setAssociatedObject(self, &forceZeroSafeAreaInsetsKey, newValue, .OBJC_ASSOCIATION_RETAIN) + } + } + + @objc private var _safeAreaInsets: UIEdgeInsets { + return forceZeroSafeAreaInsets ? .zero : self._safeAreaInsets + } + + @objc private var _safeAreaLayoutGuide: UILayoutGuide? { + return forceZeroSafeAreaInsets ? nil : self._safeAreaLayoutGuide + } + + func swizzleSafeAreaMethodsIfNeeded() { + guard !hasSwizzledSafeAreaMethods else { + return + } + hasSwizzledSafeAreaMethods = true + + guard let getSafeAreaInsets = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView.safeAreaInsets)) else { + return + } + + guard let _getSafeAreaInsets = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView._safeAreaInsets)) else { + return + } + + let getSafeAreaInsetsImplementation = method_getImplementation(getSafeAreaInsets) + let _getSafeAreaInsetsImplementation = method_getImplementation(_getSafeAreaInsets) + + class_replaceMethod( + classForCoder, + #selector(getter: UIView.safeAreaInsets), + _getSafeAreaInsetsImplementation, + method_getTypeEncoding(getSafeAreaInsets)) + + class_replaceMethod( + classForCoder, + #selector(getter: UIView._safeAreaInsets), + getSafeAreaInsetsImplementation, + method_getTypeEncoding(_getSafeAreaInsets)) + + guard let getSafeAreaLayoutGuide = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView.safeAreaLayoutGuide)) else { + return + } + + guard let _getSafeAreaLayoutGuide = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView._safeAreaLayoutGuide)) else { + return + } + + let getSafeAreaLayoutGuideImplementation = method_getImplementation(getSafeAreaLayoutGuide) + let _getSafeAreaLayoutGuideImplementation = method_getImplementation(_getSafeAreaLayoutGuide) + + class_replaceMethod( + classForCoder, + #selector(getter: UIView.safeAreaLayoutGuide), + _getSafeAreaLayoutGuideImplementation, + method_getTypeEncoding(getSafeAreaLayoutGuide)) + + class_replaceMethod( + classForCoder, + #selector(getter: UIView._safeAreaLayoutGuide), + getSafeAreaLayoutGuideImplementation, + method_getTypeEncoding(_getSafeAreaLayoutGuide)) + } + +} diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift index aac912d9c..a92a959cb 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift @@ -86,7 +86,9 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel } func toPresentable() -> UIViewController { - return VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context)) + let controller = VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context)) + controller.forceZeroSafeAreaInsets = true + return controller } func canEndPoll() -> Bool { diff --git a/changelog.d/5326.bugfix b/changelog.d/5326.bugfix new file mode 100644 index 000000000..044887f79 --- /dev/null +++ b/changelog.d/5326.bugfix @@ -0,0 +1 @@ +Fix layout issues in timeline poll cells (PSB-125) From f9276434697fe8379d768ba0d78c2ff68e46655b Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 5 Jul 2022 12:18:10 +0200 Subject: [PATCH 48/97] Enhance the VectorHostingController to be presented as a bottom sheet (#6377) * Enhance the VectorHostingController to be presented as a bottom sheet - Done --- .../VectorHostingBottomSheetPreferences.swift | 121 ++++++++++++++++++ .../SwiftUI/VectorHostingController.swift | 13 +- changelog.d/6376.change | 1 + 3 files changed, 132 insertions(+), 3 deletions(-) create mode 100644 Riot/Modules/Common/SwiftUI/VectorHostingBottomSheetPreferences.swift create mode 100644 changelog.d/6376.change diff --git a/Riot/Modules/Common/SwiftUI/VectorHostingBottomSheetPreferences.swift b/Riot/Modules/Common/SwiftUI/VectorHostingBottomSheetPreferences.swift new file mode 100644 index 000000000..cb5afa51a --- /dev/null +++ b/Riot/Modules/Common/SwiftUI/VectorHostingBottomSheetPreferences.swift @@ -0,0 +1,121 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +/// `VectorHostingBottomSheetPreferences` defines the bottom sheet behaviour using the `UISheetPresentationController` of the `UIViewController` +class VectorHostingBottomSheetPreferences { + + // MARK: - Detent + + enum Detent { + case medium + case large + + @available(iOS 15, *) + fileprivate func uiSheetDetent() -> UISheetPresentationController.Detent { + switch self { + case .medium: return .medium() + case .large: return .large() + } + } + + @available(iOS 15, *) + fileprivate func uiSheetDetentId() -> UISheetPresentationController.Detent.Identifier { + switch self { + case .medium: return .medium + case .large: return .large + } + } + } + + // MARK: - Public + + // The array of detents that the sheet may rest at. + // This array must have at least one element. + // Detents must be specified in order from smallest to largest height. + // Default: [.medium, .large] + let detents: [Detent] + + // The default detent. When nil or the identifier is not found in detents, the sheet is displayed at the smallest detent. + // Default: nil + let defaultDetent: Detent? + + // The largest detent that is not dimmed. When nil or the identifier is not found in detents, all detents are dimmed. + // Default: nil + let largestUndimmedDetent: Detent? + let cornerRadius: CGFloat? + + // If there is a larger detent to expand to than the selected detent, and a descendent scroll view is scrolled to top, this controls whether scrolling down will expand to a larger detent. + // Useful to set to NO for non-modal sheets, where scrolling in the sheet should not expand the sheet and obscure the content above. + // Default: YES + let prefersScrollingExpandsWhenScrolledToEdge: Bool + + // Set to YES to show a grabber at the top of the sheet. + // Default: `nil` -> the grabber is shown if more than one detent is configured + let prefersGrabberVisible: Bool? + + // MARK: - Setup + + init(detents: [Detent] = [.medium, .large], + defaultDetent: Detent? = nil, + largestUndimmedDetent: Detent? = nil, + prefersGrabberVisible: Bool? = nil, + cornerRadius: CGFloat? = nil, + prefersScrollingExpandsWhenScrolledToEdge: Bool = true) { + self.detents = detents + self.defaultDetent = defaultDetent + self.largestUndimmedDetent = largestUndimmedDetent + self.prefersGrabberVisible = prefersGrabberVisible + self.cornerRadius = cornerRadius + self.prefersScrollingExpandsWhenScrolledToEdge = prefersScrollingExpandsWhenScrolledToEdge + } + + // MARK: - Public + + func setup(viewController: UIViewController) { + guard #available(iOS 15.0, *) else { return } + + guard let sheetController = viewController.sheetPresentationController else { + MXLog.debug("[VectorHostingBottomSheetPreferences] setup: no sheetPresentationController found") + return + } + + sheetController.detents = self.uiSheetDetents() + if let prefersGrabberVisible = self.prefersGrabberVisible { + sheetController.prefersGrabberVisible = prefersGrabberVisible + } else { + sheetController.prefersGrabberVisible = self.detents.count > 1 + } + sheetController.selectedDetentIdentifier = self.defaultDetent?.uiSheetDetentId() + sheetController.largestUndimmedDetentIdentifier = self.largestUndimmedDetent?.uiSheetDetentId() + sheetController.prefersScrollingExpandsWhenScrolledToEdge = self.prefersScrollingExpandsWhenScrolledToEdge + if let cornerRadius = self.cornerRadius { + sheetController.preferredCornerRadius = cornerRadius + } + } + + // MARK: - Private + + @available(iOS 15, *) + fileprivate func uiSheetDetents() -> [UISheetPresentationController.Detent] { + var uiSheetDetents: [UISheetPresentationController.Detent] = [] + for detent in detents { + uiSheetDetents.append(detent.uiSheetDetent()) + } + return uiSheetDetents + } +} diff --git a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift index c636350b1..26c664023 100644 --- a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift +++ b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift @@ -25,12 +25,17 @@ class VectorHostingController: UIHostingController { // MARK: Private - var isNavigationBarHidden: Bool = false - var hidesBackTitleWhenPushed: Bool = false private var theme: Theme // MARK: Public - + + /// Wether or not the navigation bar should be hidden. Default `false` + var isNavigationBarHidden: Bool = false + /// Wether or not the title of the back item should be hidden. Default `false` + var hidesBackTitleWhenPushed: Bool = false + /// Defines the behaviour of the `VectorHostingController` as a bottom sheet. Default `nil` + var bottomSheetPreferences: VectorHostingBottomSheetPreferences? + /// Whether or not to use the iOS 15 style scroll edge appearance when the controller has a navigation bar. var enableNavigationBarScrollEdgeAppearance = false /// When non-nil, the style will be applied to the status bar. @@ -58,6 +63,8 @@ class VectorHostingController: UIHostingController { self.registerThemeServiceDidChangeThemeNotification() self.update(theme: self.theme) + + bottomSheetPreferences?.setup(viewController: self) } override func viewWillAppear(_ animated: Bool) { diff --git a/changelog.d/6376.change b/changelog.d/6376.change new file mode 100644 index 000000000..ce6c3b487 --- /dev/null +++ b/changelog.d/6376.change @@ -0,0 +1 @@ +Enhance the VectorHostingController to be presented as a bottom sheet From 67d8bb9a6d8b786be7b1a9db8df224b911cd6edb Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Tue, 5 Jul 2022 14:04:52 +0200 Subject: [PATCH 49/97] Server Offline Activity Indicator (#6314) * Server Offline Activity Indicator - implemented --- Riot/Assets/en.lproj/Vector.strings | 2 + Riot/Generated/Strings.swift | 8 ++++ Riot/Modules/Application/AppCoordinator.swift | 10 +++++ .../Common/Toasts/RoundedToastView.swift | 8 +++- .../Common/Toasts/ToastViewState.swift | 2 + .../UserIndicatorPresenter.swift | 34 +++++++++++++++++ .../UserIndicators/UserIndicatorStore.swift | 27 ++++++++++++++ .../ViewPresenters/ToastViewPresenter.swift | 4 +- .../MXKErrorPresentableBuilder.m | 27 ++++++++++---- Riot/Modules/Room/RoomViewController.m | 3 +- .../SplitView/SplitViewCoordinator.swift | 12 ++++++ .../SplitView/SplitViewCoordinatorType.swift | 9 +++++ Riot/Modules/TabBar/TabBarCoordinator.swift | 37 ++++++++++++++++++- .../TabBar/TabBarCoordinatorType.swift | 9 +++++ changelog.d/5607.feature | 1 + 15 files changed, 179 insertions(+), 14 deletions(-) create mode 100644 changelog.d/5607.feature diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 64b115a23..dccecec5d 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1060,6 +1060,8 @@ Tap the + to start adding people."; "today" = "Today"; "yesterday" = "Yesterday"; "network_offline_prompt" = "The Internet connection appears to be offline."; +"network_offline_title" = "You're offline"; +"network_offline_message" = "You're offline, check your connection."; "homeserver_connection_lost" = "Could not connect to the homeserver."; "public_room_section_title" = "Public Rooms (at %@):"; "bug_report_prompt" = "The application has crashed last time. Would you like to submit a crash report?"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index a40347d7c..9ece1d397 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -3239,10 +3239,18 @@ public class VectorL10n: NSObject { public static var networkErrorNotReachable: String { return VectorL10n.tr("Vector", "network_error_not_reachable") } + /// You're offline, check your connection. + public static var networkOfflineMessage: String { + return VectorL10n.tr("Vector", "network_offline_message") + } /// The Internet connection appears to be offline. public static var networkOfflinePrompt: String { return VectorL10n.tr("Vector", "network_offline_prompt") } + /// You're offline + public static var networkOfflineTitle: String { + return VectorL10n.tr("Vector", "network_offline_title") + } /// New public static var newWord: String { return VectorL10n.tr("Vector", "new_word") diff --git a/Riot/Modules/Application/AppCoordinator.swift b/Riot/Modules/Application/AppCoordinator.swift index 82aae24c0..f380cdc44 100755 --- a/Riot/Modules/Application/AppCoordinator.swift +++ b/Riot/Modules/Application/AppCoordinator.swift @@ -94,6 +94,16 @@ final class AppCoordinator: NSObject, AppCoordinatorType { self.addSideMenu() } + NotificationCenter.default.addObserver(forName: NSNotification.Name.appDelegateNetworkStatusDidChange, object: nil, queue: OperationQueue.main) { [weak self] notification in + guard let self = self else { return } + + if AppDelegate.theDelegate().isOffline { + self.splitViewCoordinator?.showAppStateIndicator(with: VectorL10n.networkOfflineTitle, icon: UIImage(systemName: "wifi.slash")) + } else { + self.splitViewCoordinator?.hideAppStateIndicator() + } + } + // NOTE: When split view is shown there can be no Matrix sessions ready. Keep this behavior or use a loading screen before showing the split view. self.showSplitView() MXLog.debug("[AppCoordinator] Showed split view") diff --git a/Riot/Modules/Common/Toasts/RoundedToastView.swift b/Riot/Modules/Common/Toasts/RoundedToastView.swift index 4774eadd0..879a31167 100644 --- a/Riot/Modules/Common/Toasts/RoundedToastView.swift +++ b/Riot/Modules/Common/Toasts/RoundedToastView.swift @@ -96,7 +96,7 @@ class RoundedToastView: UIView, Themable { } func update(theme: Theme) { - backgroundColor = theme.colors.background + backgroundColor = theme.colors.system stackView.arrangedSubviews.first?.tintColor = theme.colors.primaryContent label.font = theme.fonts.subheadline label.textColor = theme.colors.primaryContent @@ -115,6 +115,12 @@ class RoundedToastView: UIView, Themable { case .success: imageView.image = Asset.Images.checkmark.image return imageView + case .failure: + imageView.image = Asset.Images.errorIcon.image + return imageView + case .custom(let icon): + imageView.image = icon?.withRenderingMode(.alwaysTemplate) + return imageView } } } diff --git a/Riot/Modules/Common/Toasts/ToastViewState.swift b/Riot/Modules/Common/Toasts/ToastViewState.swift index e241d4645..c3117b549 100644 --- a/Riot/Modules/Common/Toasts/ToastViewState.swift +++ b/Riot/Modules/Common/Toasts/ToastViewState.swift @@ -20,6 +20,8 @@ struct ToastViewState { enum Style { case loading case success + case failure + case custom(icon: UIImage?) } let style: Style diff --git a/Riot/Modules/Common/UserIndicators/UserIndicatorPresenter.swift b/Riot/Modules/Common/UserIndicators/UserIndicatorPresenter.swift index 36c7c0c6e..a2603e478 100644 --- a/Riot/Modules/Common/UserIndicators/UserIndicatorPresenter.swift +++ b/Riot/Modules/Common/UserIndicators/UserIndicatorPresenter.swift @@ -23,6 +23,8 @@ import UIKit enum UserIndicatorType { case loading(label: String, isInteractionBlocking: Bool) case success(label: String) + case failure(label: String) + case custom(label: String, icon: UIImage?) } /// A presenter which can handle `UserIndicatorType` by creating the underlying `UserIndicator` @@ -75,6 +77,10 @@ class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol { } case .success(let label): return successRequest(label: label) + case .failure(let label): + return failureRequest(label: label) + case .custom(let label, let icon): + return customRequest(label: label, icon: icon) } } @@ -116,4 +122,32 @@ class UserIndicatorTypePresenter: UserIndicatorTypePresenterProtocol { dismissal: .timeout(1.5) ) } + + private func failureRequest(label: String) -> UserIndicatorRequest { + let presenter = ToastViewPresenter( + viewState: .init( + style: .failure, + label: label + ), + presentationContext: presentationContext + ) + return UserIndicatorRequest( + presenter: presenter, + dismissal: .timeout(1.5) + ) + } + + private func customRequest(label: String, icon: UIImage?) -> UserIndicatorRequest { + let presenter = ToastViewPresenter( + viewState: .init( + style: .custom(icon: icon), + label: label + ), + presentationContext: presentationContext + ) + return UserIndicatorRequest( + presenter: presenter, + dismissal: .manual + ) + } } diff --git a/Riot/Modules/Common/UserIndicators/UserIndicatorStore.swift b/Riot/Modules/Common/UserIndicators/UserIndicatorStore.swift index c6bf3ef8b..204edcb66 100644 --- a/Riot/Modules/Common/UserIndicators/UserIndicatorStore.swift +++ b/Riot/Modules/Common/UserIndicators/UserIndicatorStore.swift @@ -24,6 +24,13 @@ import CommonKit private let presenter: UserIndicatorTypePresenterProtocol private var indicators: [UserIndicator] + @objc init(from viewController: UIViewController) { + self.presenter = UserIndicatorTypePresenter(presentingViewController: viewController) + self.indicators = [] + + super.init() + } + init(presenter: UserIndicatorTypePresenterProtocol) { self.presenter = presenter self.indicators = [] @@ -59,4 +66,24 @@ import CommonKit let indicator = presenter.present(.success(label: label)) indicators.append(indicator) } + + /// Present an error message that will be automatically dismissed after a few seconds. + /// + /// Note: This is a convenience function callable by objective-c code + @objc func presentFailure(label: String) { + let indicator = presenter.present(.failure(label: label)) + indicators.append(indicator) + } + + /// Present an custom message + /// To remove the indicator call the returned `UserIndicatorCancel` function + /// + /// Note: This is a convenience function callable by objective-c code + @objc func presentCustom(label: String, icon: UIImage?) -> UserIndicatorCancel { + let indicator = presenter.present(.custom(label: label, icon: icon)) + indicators.append(indicator) + return { + indicator.cancel() + } + } } diff --git a/Riot/Modules/Common/UserIndicators/ViewPresenters/ToastViewPresenter.swift b/Riot/Modules/Common/UserIndicators/ViewPresenters/ToastViewPresenter.swift index a54e37a41..08b3080f7 100644 --- a/Riot/Modules/Common/UserIndicators/ViewPresenters/ToastViewPresenter.swift +++ b/Riot/Modules/Common/UserIndicators/ViewPresenters/ToastViewPresenter.swift @@ -23,7 +23,7 @@ import MatrixSDK /// It is managed by an `UserIndicator`, meaning the `present` and `dismiss` methods will be called when the parent `UserIndicator` starts or completes. class ToastViewPresenter: UserIndicatorViewPresentable { struct Constants { - static let navigationBarPatting = CGFloat(10) + static let navigationBarPatting = CGFloat(12) } private let viewState: ToastViewState @@ -50,7 +50,7 @@ class ToastViewPresenter: UserIndicatorViewPresentable { navigation.view.addSubview(view) NSLayoutConstraint.activate([ view.centerXAnchor.constraint(equalTo: navigation.view.centerXAnchor), - view.topAnchor.constraint(equalTo: navigation.navigationBar.safeAreaLayoutGuide.bottomAnchor, constant: -Constants.navigationBarPatting) + view.topAnchor.constraint(equalTo: navigation.navigationBar.safeAreaLayoutGuide.bottomAnchor, constant: Constants.navigationBarPatting) ]) } else { viewController.view.addSubview(view) diff --git a/Riot/Modules/MatrixKit/Utils/ErrorPresentation/MXKErrorPresentableBuilder.m b/Riot/Modules/MatrixKit/Utils/ErrorPresentation/MXKErrorPresentableBuilder.m index cd8a978b4..077fb7001 100644 --- a/Riot/Modules/MatrixKit/Utils/ErrorPresentation/MXKErrorPresentableBuilder.m +++ b/Riot/Modules/MatrixKit/Utils/ErrorPresentation/MXKErrorPresentableBuilder.m @@ -31,17 +31,28 @@ return nil; } - NSString *title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey]; - NSString *message = [error.userInfo valueForKey:NSLocalizedDescriptionKey]; - - if (!title) + NSString *title; + NSString *message; + + if ([error.domain isEqualToString:NSURLErrorDomain] && error.code == NSURLErrorNotConnectedToInternet) { - title = [VectorL10n error]; + title = [VectorL10n networkOfflineTitle]; + message = [VectorL10n networkOfflineMessage]; } - - if (!message) + else { - message = [VectorL10n errorCommonMessage]; + title = [error.userInfo valueForKey:NSLocalizedFailureReasonErrorKey]; + message = [error.userInfo valueForKey:NSLocalizedDescriptionKey]; + + if (!title) + { + title = [VectorL10n error]; + } + + if (!message) + { + message = [VectorL10n errorCommonMessage]; + } } return [[MXKErrorViewModel alloc] initWithTitle:title message:message]; diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 693f22a9f..d922f182b 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5512,8 +5512,7 @@ static CGSize kThreadListBarButtonItemImageSize; } else if ([AppDelegate theDelegate].isOffline) { - self.activitiesViewExpanded = YES; - [roomActivitiesView displayNetworkErrorNotification:[VectorL10n roomOfflineNotification]]; + // Doing nothing here as the offline notification is now handled by the AppCoordinator } else if (self.customizedRoomDataSource.roomState.isObsolete) { diff --git a/Riot/Modules/SplitView/SplitViewCoordinator.swift b/Riot/Modules/SplitView/SplitViewCoordinator.swift index cf0e09844..eff81d0e5 100644 --- a/Riot/Modules/SplitView/SplitViewCoordinator.swift +++ b/Riot/Modules/SplitView/SplitViewCoordinator.swift @@ -156,6 +156,18 @@ final class SplitViewCoordinator: NSObject, SplitViewCoordinatorType { self.tabBarCoordinator?.popToHome(animated: animated, completion: completion) } + func showErroIndicator(with error: Error) { + tabBarCoordinator?.showErroIndicator(with: error) + } + + func hideAppStateIndicator() { + tabBarCoordinator?.hideAppStateIndicator() + } + + func showAppStateIndicator(with text: String, icon: UIImage?) { + tabBarCoordinator?.showAppStateIndicator(with: text, icon: icon) + } + // MARK: - Private methods private func createPlaceholderDetailsViewController() -> UIViewController { diff --git a/Riot/Modules/SplitView/SplitViewCoordinatorType.swift b/Riot/Modules/SplitView/SplitViewCoordinatorType.swift index 4fd2cfef6..cfd04ed3e 100644 --- a/Riot/Modules/SplitView/SplitViewCoordinatorType.swift +++ b/Riot/Modules/SplitView/SplitViewCoordinatorType.swift @@ -36,4 +36,13 @@ protocol SplitViewCoordinatorType: Coordinator, Presentable { // TODO: Do not expose publicly this method /// Remove detail screens and display placeholder if needed func resetDetails(animated: Bool) + + /// Displays an error using a `UserIndicator`. The messages is dimissed automatically. + func showErroIndicator(with error: Error) + + /// Displays an message related to the application state using a `UserIndicator`. The message must be dimissed by calling the method `hideAppStateIndicator()` + func showAppStateIndicator(with text: String, icon: UIImage?) + + /// Hide the message related to the application state currently displayed. + func hideAppStateIndicator() } diff --git a/Riot/Modules/TabBar/TabBarCoordinator.swift b/Riot/Modules/TabBar/TabBarCoordinator.swift index a35e0429f..73f61b231 100644 --- a/Riot/Modules/TabBar/TabBarCoordinator.swift +++ b/Riot/Modules/TabBar/TabBarCoordinator.swift @@ -29,7 +29,10 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { private let parameters: TabBarCoordinatorParameters private let activityIndicatorPresenter: ActivityIndicatorPresenterType private let indicatorPresenter: UserIndicatorTypePresenterProtocol - + private let userIndicatorStore: UserIndicatorStore + private var appStateIndicatorCancel: UserIndicatorCancel? + private var appSateIndicator: UserIndicator? + // Indicate if the Coordinator has started once private var hasStartedOnce: Bool { return self.masterTabBarController != nil @@ -84,6 +87,7 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { self.masterNavigationController = masterNavigationController self.activityIndicatorPresenter = ActivityIndicatorPresenter() self.indicatorPresenter = UserIndicatorTypePresenter(presentingViewController: masterNavigationController) + self.userIndicatorStore = UserIndicatorStore(presenter: indicatorPresenter) } // MARK: - Public methods @@ -190,6 +194,37 @@ final class TabBarCoordinator: NSObject, TabBarCoordinatorType { } } + func showErroIndicator(with error: Error) { + let error = error as NSError + + // Ignore fake error, or connection cancellation error + guard error.domain != NSURLErrorDomain || error.code != NSURLErrorCancelled else { + return + } + + // Ignore GDPR Consent not given error. Already caught by kMXHTTPClientUserConsentNotGivenErrorNotification observation + let mxError = MXError.isMXError(error) ? MXError(nsError: error) : nil + guard mxError?.errcode != kMXErrCodeStringConsentNotGiven else { + return + } + + let msg = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String + let localizedDescription = error.userInfo[NSLocalizedDescriptionKey] as? String + let title = (error.userInfo[NSLocalizedFailureReasonErrorKey] as? String) ?? (msg ?? (localizedDescription ?? VectorL10n.error)) + + indicators.append(self.indicatorPresenter.present(.failure(label: title))) + } + + func showAppStateIndicator(with text: String, icon: UIImage?) { + hideAppStateIndicator() + appSateIndicator = self.indicatorPresenter.present(.custom(label: text, icon: icon)) + } + + func hideAppStateIndicator() { + appSateIndicator?.cancel() + appSateIndicator = nil + } + // MARK: - SplitViewMasterPresentable var selectedNavigationRouter: NavigationRouterType? { diff --git a/Riot/Modules/TabBar/TabBarCoordinatorType.swift b/Riot/Modules/TabBar/TabBarCoordinatorType.swift index 8d916606a..2d6d290b0 100644 --- a/Riot/Modules/TabBar/TabBarCoordinatorType.swift +++ b/Riot/Modules/TabBar/TabBarCoordinatorType.swift @@ -37,4 +37,13 @@ protocol TabBarCoordinatorType: Coordinator, SplitViewMasterPresentable { // TODO: Remove this method, this implementation detail should not be exposed // Release the current selected item (room/contact/group...). func releaseSelectedItems() + + /// Displays an error using a `UserIndicator`. The messages is dimissed automatically. + func showErroIndicator(with error: Error) + + /// Displays an message related to the application state using a `UserIndicator`. The message must be dimissed by calling the method `hideAppStateIndicator()` + func showAppStateIndicator(with text: String, icon: UIImage?) + + /// Hide the message related to the application state currently displayed. + func hideAppStateIndicator() } diff --git a/changelog.d/5607.feature b/changelog.d/5607.feature new file mode 100644 index 000000000..f17d5c084 --- /dev/null +++ b/changelog.d/5607.feature @@ -0,0 +1 @@ +Server Offline Activity Indicator From 4153164e17807aa691f134c210e2d756bcfedf1f Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 5 Jul 2022 15:47:34 +0200 Subject: [PATCH 50/97] LiveLocationLabPromotionCoordinator: Show screen as a bottom sheet. --- .../Coordinator/LiveLocationLabPromotionCoordinator.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift index a3ccb737f..fbc5c6c9f 100644 --- a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/Coordinator/LiveLocationLabPromotionCoordinator.swift @@ -40,6 +40,7 @@ final class LiveLocationLabPromotionCoordinator: NSObject, Coordinator, Presenta let view = LiveLocationLabPromotionView(viewModel: viewModel.context) liveLocationLabPromotionViewModel = viewModel liveLocationLabPromotionHostingController = VectorHostingController(rootView: view) + liveLocationLabPromotionHostingController.bottomSheetPreferences = VectorHostingBottomSheetPreferences() super.init() } From 7f164d0e77a5cdee266a2d7ea09a0a9fead29563 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 5 Jul 2022 16:16:53 +0200 Subject: [PATCH 51/97] LocationSharingView: Fix background color issue with safe area. --- .../Modules/Room/LocationSharing/View/LocationSharingView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift index 89c86d3ac..70d31f6da 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift @@ -40,6 +40,7 @@ struct LocationSharingView: View { .clipShape(RoundedCornerShape(radius: 8, corners: [.topLeft, .topRight])) } } + .background(theme.colors.background.ignoresSafeArea()) .toolbar { ToolbarItem(placement: .navigationBarLeading) { Button(VectorL10n.cancel, action: { From de33c61a1b4e53e1d0fb6c03f46f8507cac98fff Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 5 Jul 2022 16:33:45 +0200 Subject: [PATCH 52/97] Fix unit tests. --- .../Test/Unit/LocationSharingViewModelTests.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/Test/Unit/LocationSharingViewModelTests.swift b/RiotSwiftUI/Modules/Room/LocationSharing/Test/Unit/LocationSharingViewModelTests.swift index 020021026..a7871fa93 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/Test/Unit/LocationSharingViewModelTests.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/Test/Unit/LocationSharingViewModelTests.swift @@ -50,6 +50,8 @@ class LocationSharingViewModelTests: XCTestCase { expectation.fulfill() case .shareLiveLocation: XCTFail() + case .showLabFlagPromotionIfNeeded: + XCTFail() } } From f1cd8633301887f40b30affd1609033bdf5a859e Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Wed, 15 Jun 2022 17:43:52 +0100 Subject: [PATCH 53/97] Track non-fatal issues via analytics --- Podfile | 1 + Podfile.lock | 8 ++- Riot/Modules/Analytics/Analytics.swift | 17 ++++- .../Analytics/SentryMonitoringClient.swift | 67 +++++++++++++++++++ 4 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 Riot/Modules/Analytics/SentryMonitoringClient.swift diff --git a/Podfile b/Podfile index 0a00d2469..17a98de3b 100644 --- a/Podfile +++ b/Podfile @@ -72,6 +72,7 @@ abstract_target 'RiotPods' do # PostHog for analytics pod 'PostHog', '~> 1.4.4' + pod 'Sentry', '~> 7.15.0' pod 'AnalyticsEvents', :git => 'https://github.com/matrix-org/matrix-analytics-events.git', :branch => 'release/swift', :inhibit_warnings => false # pod 'AnalyticsEvents', :path => '../matrix-analytics-events/AnalyticsEvents.podspec' diff --git a/Podfile.lock b/Podfile.lock index 7e05e4ccd..33e81e347 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -87,6 +87,9 @@ PODS: - Reusable/View (= 4.1.2) - Reusable/Storyboard (4.1.2) - Reusable/View (4.1.2) + - Sentry (7.15.0): + - Sentry/Core (= 7.15.0) + - Sentry/Core (7.15.0) - SideMenu (6.5.0) - SwiftBase32 (0.9.0) - SwiftGen (6.5.1) @@ -126,6 +129,7 @@ DEPENDENCIES: - PostHog (~> 1.4.4) - ReadMoreTextView (~> 3.0.1) - Reusable (~> 4.1) + - Sentry (~> 7.15.0) - SideMenu (~> 6.5) - SwiftBase32 (~> 0.9.0) - SwiftGen (~> 6.3) @@ -169,6 +173,7 @@ SPEC REPOS: - ReadMoreTextView - Realm - Reusable + - Sentry - SideMenu - SwiftBase32 - SwiftGen @@ -223,6 +228,7 @@ SPEC CHECKSUMS: ReadMoreTextView: 19147adf93abce6d7271e14031a00303fe28720d Realm: 9ca328bd7e700cc19703799785e37f77d1a130f2 Reusable: 6bae6a5e8aa793c9c441db0213c863a64bce9136 + Sentry: 63ca44f5e0c8cea0ee5a07686b02e56104f41ef7 SideMenu: f583187d21c5b1dd04c72002be544b555a2627a2 SwiftBase32: 9399c25a80666dc66b51e10076bf591e3bbb8f17 SwiftGen: a6d22010845f08fe18fbdf3a07a8e380fd22e0ea @@ -235,6 +241,6 @@ SPEC CHECKSUMS: zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c ZXingObjC: fdbb269f25dd2032da343e06f10224d62f537bdb -PODFILE CHECKSUM: fdddeaf3f403004b1cc6200add1b6b9e00d40906 +PODFILE CHECKSUM: b3c7c064fc2b74dc937762364faab403fc3fd041 COCOAPODS: 1.11.2 diff --git a/Riot/Modules/Analytics/Analytics.swift b/Riot/Modules/Analytics/Analytics.swift index 9505b5620..030b4463c 100644 --- a/Riot/Modules/Analytics/Analytics.swift +++ b/Riot/Modules/Analytics/Analytics.swift @@ -17,8 +17,12 @@ import PostHog import AnalyticsEvents -/// A class responsible for managing an analytics client -/// and sending events through this client. +/// A class responsible for managing a variety of analytics clients +/// and sending events through these clients. +/// +/// Events may include user activity, or app health data such as crashes, +/// non-fatal issues and performance. `Analytics` class serves as a façade +/// to all these use cases. /// /// ## Creating Analytics Events /// @@ -42,6 +46,9 @@ import AnalyticsEvents /// The analytics client to send events with. private var client: AnalyticsClientProtocol = PostHogAnalyticsClient() + /// The monitoring client to track crashes, issues and performance + private var monitoringClient = SentryMonitoringClient() + /// The service used to interact with account data settings. private var service: AnalyticsService? @@ -106,6 +113,7 @@ import AnalyticsEvents // The order is important here. PostHog ignores the reset if stopped. reset() client.stop() + monitoringClient.stop() MXLog.debug("[Analytics] Stopped.") } @@ -115,6 +123,7 @@ import AnalyticsEvents guard RiotSettings.shared.enableAnalytics, !isRunning else { return } client.start() + monitoringClient.start() // Sanity check in case something went wrong. guard client.isRunning else { return } @@ -163,6 +172,7 @@ import AnalyticsEvents /// Note: **MUST** be called before stopping PostHog or the reset is ignored. func reset() { client.reset() + monitoringClient.reset() MXLog.debug("[Analytics] Reset.") RiotSettings.shared.isIdentifiedForAnalytics = false @@ -374,4 +384,7 @@ extension Analytics: MXAnalyticsDelegate { capture(event: event) } + func trackNonFatalIssue(_ issue: String, details: [String : Any]?) { + monitoringClient.trackNonFatalIssue(issue, details: details) + } } diff --git a/Riot/Modules/Analytics/SentryMonitoringClient.swift b/Riot/Modules/Analytics/SentryMonitoringClient.swift new file mode 100644 index 000000000..286c0fb43 --- /dev/null +++ b/Riot/Modules/Analytics/SentryMonitoringClient.swift @@ -0,0 +1,67 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import Sentry +import MatrixSDK + +/// Sentry client used as part of the Analytics set of tools to track health metrics +/// of the application, such as crashes, non-fatal issues and performance. +/// +/// All analytics tracking, incl. health metrics, is subject to user consent, +/// configurable in user settings. +struct SentryMonitoringClient { + private static let sentryDSN = "https://a5e37731f9b94642a1b93093cacbee4c@sentry.tools.element.io/47" + + func start() { + guard !SentrySDK.isEnabled else { return } + + MXLog.debug("[SentryMonitoringClient] Started") + SentrySDK.start { options in + options.dsn = Self.sentryDSN + options.tracesSampleRate = 1.0 + + options.beforeSend = { event in + MXLog.error("[SentryMonitoringClient] Issue detected: \(event)") + return event + } + + options.onCrashedLastRun = { event in + MXLog.debug("[SentryMonitoringClient] Last run crashed: \(event)") + } + } + } + + func stop() { + MXLog.debug("[SentryMonitoringClient] Stopped") + SentrySDK.close() + } + + func reset() { + MXLog.debug("[SentryMonitoringClient] Reset") + SentrySDK.startSession() + } + + func trackNonFatalIssue(_ issue: String, details: [String: Any]?) { + guard SentrySDK.isEnabled else { return } + + let event = Event() + event.level = .error + event.message = .init(formatted: issue) + event.extra = details + SentrySDK.capture(event: event) + } +} From c26174cf2f791b0410091e7d134d8a8badf95abd Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Thu, 16 Jun 2022 13:06:47 +0100 Subject: [PATCH 54/97] Add changelog --- changelog.d/pr-6308.feature | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-6308.feature diff --git a/changelog.d/pr-6308.feature b/changelog.d/pr-6308.feature new file mode 100644 index 000000000..d288d13bf --- /dev/null +++ b/changelog.d/pr-6308.feature @@ -0,0 +1 @@ +Analytics: Track non-fatal issues if consent provided From d87a213cae991ecfd9782743a199ec96d3d26e42 Mon Sep 17 00:00:00 2001 From: wtimme Date: Wed, 6 Jul 2022 10:39:38 +0200 Subject: [PATCH 55/97] Target `SiriIntents`: Split `IntentHandler` into smaller files (#6203) * Add protocol `ContactResolving` * Let the `IntentHandler` implement `ContactResolving` (#6203) Nothing has changed about the implementation itself; this prepares the separation of this logic into a dedicated unit. * Prepare the separation of the contact resolver from the intents handler (#6203) * Move the implementation of `ContactResolving` to a dedicated class (#6203) * Move `ContactResolver` to a dedicated file (#6203) * Prepare the separation of the `StartAudioCallIntentHandler` from `IntentsHandler` (#6203) * Move the implementation of `INStartAudioCallIntentHandling` to a dedicated class (#6203) * Prepare the separation of the `StartVideoCallIntentHandler` from `IntentsHandler` (#6203) * Move the implementation of `INStartVideoCallIntentHandling` to a dedicated class (#6203) * Prepare the separation of the `SendMessageIntentHandler` from `IntentsHandler` (#6203) * Move the implementation of `INSendMessageIntentHandling` to a dedicated class (#6203) * Remove unused property (#6203) * Return `nil` if the requested intent cannot be handled (#6203) * Initialize the intent handlers _after_ everything else is configured (#6203) In `init()`, there might be some configuration being done that is required for the handlers. * Add changelog entry * Move curly braces in Objective-C to dedicated lines This ensures that the code follows the style that is present in other Objective-C files. Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> * Inject the `ContactResolver` into the intent handlers during initialization In #6365, @pixlwave pointed out that > If the resolver ever gained a cache or stored properties it would > help keep the memory usage down in the extension * Prefer forward-declaration over import in Objective-C header files Per @pixlwave, this helps prevent build failures: > We had random cycle errors while building a while back and it was > eventually solved by removing all imports of `GeneratedInterface-Swift.h` > in every [Objective-C header] file. Co-authored-by: Doug <6060466+pixlwave@users.noreply.github.com> --- SiriIntents/ContactResolver/ContactResolver.h | 26 ++ SiriIntents/ContactResolver/ContactResolver.m | 177 ++++++++ .../ContactResolver/ContactResolving.swift | 22 + SiriIntents/IntentHandler.m | 398 +----------------- .../SendMessage/SendMessageIntentHandler.h | 29 ++ .../SendMessage/SendMessageIntentHandler.m | 161 +++++++ .../StartAudioCallIntentHandler.h | 29 ++ .../StartAudioCallIntentHandler.m | 90 ++++ .../StartVideoCallIntentHandler.h | 29 ++ .../StartVideoCallIntentHandler.m | 90 ++++ changelog.d/6203.build | 1 + 11 files changed, 674 insertions(+), 378 deletions(-) create mode 100644 SiriIntents/ContactResolver/ContactResolver.h create mode 100644 SiriIntents/ContactResolver/ContactResolver.m create mode 100644 SiriIntents/ContactResolver/ContactResolving.swift create mode 100644 SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.h create mode 100644 SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.m create mode 100644 SiriIntents/IntentHandlers/StartAudioCall/StartAudioCallIntentHandler.h create mode 100644 SiriIntents/IntentHandlers/StartAudioCall/StartAudioCallIntentHandler.m create mode 100644 SiriIntents/IntentHandlers/StartVideoCall/StartVideoCallIntentHandler.h create mode 100644 SiriIntents/IntentHandlers/StartVideoCall/StartVideoCallIntentHandler.m create mode 100644 changelog.d/6203.build diff --git a/SiriIntents/ContactResolver/ContactResolver.h b/SiriIntents/ContactResolver/ContactResolver.h new file mode 100644 index 000000000..e2d091e86 --- /dev/null +++ b/SiriIntents/ContactResolver/ContactResolver.h @@ -0,0 +1,26 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +#import "GeneratedInterface-Swift.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ContactResolver: NSObject + +@end + +NS_ASSUME_NONNULL_END diff --git a/SiriIntents/ContactResolver/ContactResolver.m b/SiriIntents/ContactResolver/ContactResolver.m new file mode 100644 index 000000000..42d121261 --- /dev/null +++ b/SiriIntents/ContactResolver/ContactResolver.m @@ -0,0 +1,177 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "ContactResolver.h" +@import Intents; +#import "MXKAccountManager.h" + +@implementation ContactResolver + +- (void)resolveContacts:(nullable NSArray *)contacts + withCompletion:(void (^)(NSArray * _Nonnull))completion +{ + if (contacts.count == 0) + { + completion(@[[INPersonResolutionResult needsValue]]); + return; + } + else + { + // We don't iterate over array of contacts from passed intent + // since it's hard to imagine scenario with several callee + // so we just extract the first one + INPerson *callee = contacts.firstObject; + + // If this method is called after selection of the appropriate user, it will hold userId of an user to whom we must call + NSString *selectedUserId; + + // Check if the user has selected right room among several direct rooms from previous resolution process run + if (callee.customIdentifier.length) + { + // If callee will have the same name as one of the contact in the system contacts app + // Siri will pass us this contact in the intent.contacts array and we must provide the same count of + // resolution results as elements count in the intent.contact. + // So we just pass the same result at all iterations + NSMutableArray *resolutionResults = [NSMutableArray array]; + for (NSInteger i = 0; i < contacts.count; ++i) + [resolutionResults addObject:[INPersonResolutionResult successWithResolvedPerson:callee]]; + completion(resolutionResults); + return; + } + else + { + // This resolution process run after selecting appropriate user among suggested user list + selectedUserId = callee.personHandle.value; + } + + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + if (account) + { + MXFileStore *fileStore = [[MXFileStore alloc] initWithCredentials:account.mxCredentials]; + [fileStore.roomSummaryStore fetchAllSummaries:^(NSArray> * _Nonnull summaries) { + + // Contains userIds of all users with whom the current user has direct chats + // Use set to avoid duplicates + NSMutableSet *directUserIds = [NSMutableSet set]; + + // Contains room summaries for all direct rooms connected with particular userId + NSMutableDictionary> *> *roomSummaries = [NSMutableDictionary dictionary]; + + for (id summary in summaries) + { + // TODO: We also need to check if joined room members count equals 2 + // It is pointlessly to save rooms with 1 joined member or room with more than 2 joined members + if (summary.isDirect) + { + NSString *directUserId = summary.directUserId; + + // Collect room summaries only for specified user + if (selectedUserId && ![directUserId isEqualToString:selectedUserId]) + continue; + + // Save userId + [directUserIds addObject:directUserId]; + + // Save associated with diretUserId room summary + NSMutableArray> *userRoomSummaries = roomSummaries[directUserId]; + if (userRoomSummaries) + [userRoomSummaries addObject:summary]; + else + roomSummaries[directUserId] = [NSMutableArray arrayWithObject:summary]; + } + } + + [fileStore asyncUsersWithUserIds:directUserIds.allObjects success:^(NSArray * _Nonnull users) { + + // Find users whose display name contains string presented us by Siri + NSMutableArray *matchingUsers = [NSMutableArray array]; + for (MXUser *user in users) + { + if (!user.displayname) + continue; + + if (!NSEqualRanges([callee.displayName rangeOfString:user.displayname options:NSCaseInsensitiveSearch], (NSRange){NSNotFound,0})) + { + [matchingUsers addObject:user]; + } + } + + NSMutableArray *persons = [NSMutableArray array]; + + if (matchingUsers.count == 1) + { + MXUser *user = matchingUsers.firstObject; + + // Provide to the user a list of direct rooms to choose from + NSArray> *summaries = roomSummaries[user.userId]; + for (id summary in summaries) + { + INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; + + // For rooms we try to use room display name + NSString *displayName = summary.displayname ? summary.displayname : user.displayname; + + INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle + nameComponents:nil + displayName:displayName + image:nil + contactIdentifier:nil + customIdentifier:summary.roomId]; + + [persons addObject:person]; + } + } + else if (matchingUsers.count > 1) + { + // Provide to the user a list of users to choose from + // This is the case when there are several users with the same name + for (MXUser *user in matchingUsers) + { + INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; + INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle + nameComponents:nil + displayName:user.displayname + image:nil + contactIdentifier:nil + customIdentifier:nil]; + + [persons addObject:person]; + } + } + + if (persons.count == 0) + { + completion(@[[INPersonResolutionResult unsupported]]); + } + else if (persons.count == 1) + { + completion(@[[INPersonResolutionResult successWithResolvedPerson:persons.firstObject]]); + } + else + { + completion(@[[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:persons]]); + } + } failure:nil]; + }]; + } + else + { + completion(@[[INPersonResolutionResult notRequired]]); + } + } +} + +@end diff --git a/SiriIntents/ContactResolver/ContactResolving.swift b/SiriIntents/ContactResolver/ContactResolving.swift new file mode 100644 index 000000000..542e844bb --- /dev/null +++ b/SiriIntents/ContactResolver/ContactResolving.swift @@ -0,0 +1,22 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Intents + +@objc protocol ContactResolving { + func resolveContacts(_ contacts: [INPerson]?, + withCompletion completion: @escaping ([INPersonResolutionResult]) -> Void) +} diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index 65681525a..a18c7a843 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -18,21 +18,23 @@ #import "GeneratedInterface-Swift.h" #import "MXKAccountManager.h" +#import "ContactResolver.h" +#import "StartAudioCallIntentHandler.h" +#import "StartVideoCallIntentHandler.h" +#import "SendMessageIntentHandler.h" #if __has_include() #define CALL_STACK_JINGLE #endif -@interface IntentHandler () +@interface IntentHandler () // Build Settings @property (nonatomic) id configuration; -/** - The room that is currently being used to send a message. This is to ensure a - strong ref is maintained on the `MXRoom` until sending has completed. - */ -@property (nonatomic) MXRoom *selectedRoom; +@property (nonatomic) id startAudioCallIntentHandler; +@property (nonatomic) id startVideoCallIntentHandler; +@property (nonatomic) id sendMessageIntentHandler; @end @@ -65,386 +67,26 @@ Analytics *analytics = Analytics.shared; [MXSDKOptions sharedInstance].analyticsDelegate = analytics; [analytics startIfEnabled]; + + id contactResolver = [[ContactResolver alloc] init]; + _startAudioCallIntentHandler = [[StartAudioCallIntentHandler alloc] initWithContactResolver:contactResolver]; + _startVideoCallIntentHandler = [[StartVideoCallIntentHandler alloc] initWithContactResolver:contactResolver]; + _sendMessageIntentHandler = [[SendMessageIntentHandler alloc] initWithContactResolver:contactResolver]; } return self; } - (id)handlerForIntent:(INIntent *)intent { - return self; -} - -#pragma mark - INStartAudioCallIntentHandling - -- (void)resolveContactsForStartAudioCall:(INStartAudioCallIntent *)intent withCompletion:(void (^)(NSArray * _Nonnull))completion -{ - [self resolveContacts:intent.contacts withCompletion:completion]; -} - -- (void)confirmStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion -{ - INStartAudioCallIntentResponse *response = nil; - - MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; - if (account) - { -#if defined MX_CALL_STACK_OPENWEBRTC || defined MX_CALL_STACK_ENDPOINT || defined CALL_STACK_JINGLE - NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INStartAudioCallIntent class])]; - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeReady userActivity:userActivity]; -#else - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; -#endif - } - else - { - // User hasn't logged in - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureAppConfigurationRequired userActivity:nil]; + if ([intent isKindOfClass:[INStartAudioCallIntent class]]) { + return self.startAudioCallIntentHandler; + } else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) { + return self.startVideoCallIntentHandler; + } else if ([intent isKindOfClass:[INSendMessageIntent class]]) { + return self.sendMessageIntentHandler; } - completion(response); -} - -- (void)handleStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion -{ - INStartAudioCallIntentResponse *response = nil; - - INPerson *person = intent.contacts.firstObject; - if (person && person.customIdentifier) - { - NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; - userActivity.userInfo = @{ @"roomID" : person.customIdentifier }; - - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeContinueInApp - userActivity:userActivity]; - } - else - { - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailure userActivity:nil]; - } - - completion(response); -} - -#pragma mark - INStartVideoCallIntentHandling - -- (void)resolveContactsForStartVideoCall:(INStartVideoCallIntent *)intent withCompletion:(void (^)(NSArray * _Nonnull))completion -{ - [self resolveContacts:intent.contacts withCompletion:completion]; -} - -- (void)confirmStartVideoCall:(INStartVideoCallIntent *)intent completion:(void (^)(INStartVideoCallIntentResponse * _Nonnull))completion -{ - INStartVideoCallIntentResponse *response = nil; - - MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; - if (account) - { -#if defined MX_CALL_STACK_OPENWEBRTC || defined MX_CALL_STACK_ENDPOINT || defined CALL_STACK_JINGLE - NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INStartVideoCallIntent class])]; - response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeReady userActivity:userActivity]; -#else - response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; -#endif - } - else - { - // User hasn't logged in - response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeFailureRequiringAppLaunch userActivity:nil]; - } - - completion(response); -} - -- (void)handleStartVideoCall:(INStartVideoCallIntent *)intent completion:(void (^)(INStartVideoCallIntentResponse * _Nonnull))completion -{ - INStartVideoCallIntentResponse *response = nil; - - INPerson *person = intent.contacts.firstObject; - if (person && person.customIdentifier) - { - NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartVideoCallIntent.class)]; - userActivity.userInfo = @{ @"roomID" : person.customIdentifier }; - - response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeContinueInApp - userActivity:userActivity]; - } - else - { - response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeFailure userActivity:nil]; - } - - completion(response); -} - -#pragma mark - INSendMessageIntentHandling - -- (void)resolveRecipientsForSendMessage:(INSendMessageIntent *)intent completion:(void (^)(NSArray * _Nonnull))completion -{ - [self resolveContacts:intent.recipients withCompletion:completion]; -} - -- (void)resolveContentForSendMessage:(INSendMessageIntent *)intent withCompletion:(void (^)(INStringResolutionResult * _Nonnull))completion -{ - NSString *message = intent.content; - if (message && ![message isEqualToString:@""]) - completion([INStringResolutionResult successWithResolvedString:message]); - else - completion([INStringResolutionResult needsValue]); -} - -- (void)confirmSendMessage:(INSendMessageIntent *)intent completion:(void (^)(INSendMessageIntentResponse * _Nonnull))completion -{ - INSendMessageIntentResponse *response = nil; - - MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; - if (account) - { - NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendMessageIntent class])]; - response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeReady userActivity:userActivity]; - } - else - { - // User hasn't logged in - response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeFailureRequiringAppLaunch userActivity:nil]; - } - - completion(response); -} - -- (void)handleSendMessage:(INSendMessageIntent *)intent completion:(void (^)(INSendMessageIntentResponse * _Nonnull))completion -{ - void (^completeWithCode)(INSendMessageIntentResponseCode) = ^(INSendMessageIntentResponseCode code) { - NSUserActivity *userActivity = nil; - if (code == INSendMessageIntentResponseCodeSuccess) - userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendMessageIntent class])]; - INSendMessageIntentResponse *response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeSuccess - userActivity:userActivity]; - completion(response); - }; - - INPerson *person = intent.recipients.firstObject; - if (person && person.customIdentifier) - { - MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; - MXFileStore *fileStore = [[MXFileStore alloc] initWithCredentials:account.mxCredentials]; - [fileStore.roomSummaryStore fetchAllSummaries:^(NSArray> * _Nonnull summaries) { - NSString *roomID = person.customIdentifier; - - BOOL isEncrypted = NO; - for (id summary in summaries) - { - if ([summary.roomId isEqualToString:roomID]) - { - isEncrypted = summary.isEncrypted; - break; - } - } - - if (isEncrypted) - { - [MXFileStore setPreloadOptions:0]; - - MXSession *session = [[MXSession alloc] initWithMatrixRestClient:account.mxRestClient]; - MXWeakify(session); - [session setStore:fileStore success:^{ - MXStrongifyAndReturnIfNil(session); - - self.selectedRoom = [MXRoom loadRoomFromStore:fileStore withRoomId:roomID matrixSession:session]; - - // Do not warn for unknown devices. We have cross-signing now - session.crypto.warnOnUnknowDevices = NO; - - MXWeakify(self); - [self.selectedRoom sendTextMessage:intent.content - threadId:nil - success:^(NSString *eventId) { - completeWithCode(INSendMessageIntentResponseCodeSuccess); - MXStrongifyAndReturnIfNil(self); - self.selectedRoom = nil; - } failure:^(NSError *error) { - completeWithCode(INSendMessageIntentResponseCodeFailure); - MXStrongifyAndReturnIfNil(self); - self.selectedRoom = nil; - }]; - - } failure:^(NSError *error) { - completeWithCode(INSendMessageIntentResponseCodeFailure); - }]; - - return; - } - - [account.mxRestClient sendTextMessageToRoom:roomID - threadId:nil - text:intent.content - success:^(NSString *eventId) { - completeWithCode(INSendMessageIntentResponseCodeSuccess); - } - failure:^(NSError *error) { - completeWithCode(INSendMessageIntentResponseCodeFailure); - }]; - - }]; - } - else - { - completeWithCode(INSendMessageIntentResponseCodeFailure); - } -} - -#pragma mark - Private - -- (void)resolveContacts:(nullable NSArray *)contacts withCompletion:(void (^)(NSArray * _Nonnull))completion -{ - if (contacts.count == 0) - { - completion(@[[INPersonResolutionResult needsValue]]); - return; - } - else - { - // We don't iterate over array of contacts from passed intent - // since it's hard to imagine scenario with several callee - // so we just extract the first one - INPerson *callee = contacts.firstObject; - - // If this method is called after selection of the appropriate user, it will hold userId of an user to whom we must call - NSString *selectedUserId; - - // Check if the user has selected right room among several direct rooms from previous resolution process run - if (callee.customIdentifier.length) - { - // If callee will have the same name as one of the contact in the system contacts app - // Siri will pass us this contact in the intent.contacts array and we must provide the same count of - // resolution results as elements count in the intent.contact. - // So we just pass the same result at all iterations - NSMutableArray *resolutionResults = [NSMutableArray array]; - for (NSInteger i = 0; i < contacts.count; ++i) - [resolutionResults addObject:[INPersonResolutionResult successWithResolvedPerson:callee]]; - completion(resolutionResults); - return; - } - else - { - // This resolution process run after selecting appropriate user among suggested user list - selectedUserId = callee.personHandle.value; - } - - MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; - if (account) - { - MXFileStore *fileStore = [[MXFileStore alloc] initWithCredentials:account.mxCredentials]; - [fileStore.roomSummaryStore fetchAllSummaries:^(NSArray> * _Nonnull summaries) { - - // Contains userIds of all users with whom the current user has direct chats - // Use set to avoid duplicates - NSMutableSet *directUserIds = [NSMutableSet set]; - - // Contains room summaries for all direct rooms connected with particular userId - NSMutableDictionary> *> *roomSummaries = [NSMutableDictionary dictionary]; - - for (id summary in summaries) - { - // TODO: We also need to check if joined room members count equals 2 - // It is pointlessly to save rooms with 1 joined member or room with more than 2 joined members - if (summary.isDirect) - { - NSString *directUserId = summary.directUserId; - - // Collect room summaries only for specified user - if (selectedUserId && ![directUserId isEqualToString:selectedUserId]) - continue; - - // Save userId - [directUserIds addObject:directUserId]; - - // Save associated with diretUserId room summary - NSMutableArray> *userRoomSummaries = roomSummaries[directUserId]; - if (userRoomSummaries) - [userRoomSummaries addObject:summary]; - else - roomSummaries[directUserId] = [NSMutableArray arrayWithObject:summary]; - } - } - - [fileStore asyncUsersWithUserIds:directUserIds.allObjects success:^(NSArray * _Nonnull users) { - - // Find users whose display name contains string presented us by Siri - NSMutableArray *matchingUsers = [NSMutableArray array]; - for (MXUser *user in users) - { - if (!user.displayname) - continue; - - if (!NSEqualRanges([callee.displayName rangeOfString:user.displayname options:NSCaseInsensitiveSearch], (NSRange){NSNotFound,0})) - { - [matchingUsers addObject:user]; - } - } - - NSMutableArray *persons = [NSMutableArray array]; - - if (matchingUsers.count == 1) - { - MXUser *user = matchingUsers.firstObject; - - // Provide to the user a list of direct rooms to choose from - NSArray> *summaries = roomSummaries[user.userId]; - for (id summary in summaries) - { - INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; - - // For rooms we try to use room display name - NSString *displayName = summary.displayname ? summary.displayname : user.displayname; - - INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle - nameComponents:nil - displayName:displayName - image:nil - contactIdentifier:nil - customIdentifier:summary.roomId]; - - [persons addObject:person]; - } - } - else if (matchingUsers.count > 1) - { - // Provide to the user a list of users to choose from - // This is the case when there are several users with the same name - for (MXUser *user in matchingUsers) - { - INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; - INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle - nameComponents:nil - displayName:user.displayname - image:nil - contactIdentifier:nil - customIdentifier:nil]; - - [persons addObject:person]; - } - } - - if (persons.count == 0) - { - completion(@[[INPersonResolutionResult unsupported]]); - } - else if (persons.count == 1) - { - completion(@[[INPersonResolutionResult successWithResolvedPerson:persons.firstObject]]); - } - else - { - completion(@[[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:persons]]); - } - } failure:nil]; - }]; - } - else - { - completion(@[[INPersonResolutionResult notRequired]]); - } - } + return nil; } @end diff --git a/SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.h b/SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.h new file mode 100644 index 000000000..37d7fee22 --- /dev/null +++ b/SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.h @@ -0,0 +1,29 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +@import Intents; +@protocol ContactResolving; + +NS_ASSUME_NONNULL_BEGIN + +@interface SendMessageIntentHandler : NSObject + +- (instancetype)initWithContactResolver:(id)contactResolver; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.m b/SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.m new file mode 100644 index 000000000..84d9dee63 --- /dev/null +++ b/SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.m @@ -0,0 +1,161 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "SendMessageIntentHandler.h" +#import "ContactResolver.h" +#import "MXKAccountManager.h" +#import "GeneratedInterface-Swift.h" + +@interface SendMessageIntentHandler () + +@property (nonatomic) id contactResolver; + +/** + The room that is currently being used to send a message. This is to ensure a + strong ref is maintained on the `MXRoom` until sending has completed. + */ +@property (nonatomic) MXRoom *selectedRoom; + +@end + +@implementation SendMessageIntentHandler + +- (instancetype)initWithContactResolver:(id)contactResolver +{ + if (self = [super init]) { + _contactResolver = contactResolver; + } + + return self; +} + +#pragma mark - INSendMessageIntentHandling + +- (void)resolveRecipientsForSendMessage:(INSendMessageIntent *)intent completion:(void (^)(NSArray * _Nonnull))completion +{ + [self.contactResolver resolveContacts:intent.recipients withCompletion:completion]; +} + +- (void)resolveContentForSendMessage:(INSendMessageIntent *)intent withCompletion:(void (^)(INStringResolutionResult * _Nonnull))completion +{ + NSString *message = intent.content; + if (message && ![message isEqualToString:@""]) + completion([INStringResolutionResult successWithResolvedString:message]); + else + completion([INStringResolutionResult needsValue]); +} + +- (void)confirmSendMessage:(INSendMessageIntent *)intent completion:(void (^)(INSendMessageIntentResponse * _Nonnull))completion +{ + INSendMessageIntentResponse *response = nil; + + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + if (account) + { + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendMessageIntent class])]; + response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeReady userActivity:userActivity]; + } + else + { + // User hasn't logged in + response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeFailureRequiringAppLaunch userActivity:nil]; + } + + completion(response); +} + +- (void)handleSendMessage:(INSendMessageIntent *)intent completion:(void (^)(INSendMessageIntentResponse * _Nonnull))completion +{ + void (^completeWithCode)(INSendMessageIntentResponseCode) = ^(INSendMessageIntentResponseCode code) { + NSUserActivity *userActivity = nil; + if (code == INSendMessageIntentResponseCodeSuccess) + userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendMessageIntent class])]; + INSendMessageIntentResponse *response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeSuccess + userActivity:userActivity]; + completion(response); + }; + + INPerson *person = intent.recipients.firstObject; + if (person && person.customIdentifier) + { + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + MXFileStore *fileStore = [[MXFileStore alloc] initWithCredentials:account.mxCredentials]; + [fileStore.roomSummaryStore fetchAllSummaries:^(NSArray> * _Nonnull summaries) { + NSString *roomID = person.customIdentifier; + + BOOL isEncrypted = NO; + for (id summary in summaries) + { + if ([summary.roomId isEqualToString:roomID]) + { + isEncrypted = summary.isEncrypted; + break; + } + } + + if (isEncrypted) + { + [MXFileStore setPreloadOptions:0]; + + MXSession *session = [[MXSession alloc] initWithMatrixRestClient:account.mxRestClient]; + MXWeakify(session); + [session setStore:fileStore success:^{ + MXStrongifyAndReturnIfNil(session); + + self.selectedRoom = [MXRoom loadRoomFromStore:fileStore withRoomId:roomID matrixSession:session]; + + // Do not warn for unknown devices. We have cross-signing now + session.crypto.warnOnUnknowDevices = NO; + + MXWeakify(self); + [self.selectedRoom sendTextMessage:intent.content + threadId:nil + success:^(NSString *eventId) { + completeWithCode(INSendMessageIntentResponseCodeSuccess); + MXStrongifyAndReturnIfNil(self); + self.selectedRoom = nil; + } failure:^(NSError *error) { + completeWithCode(INSendMessageIntentResponseCodeFailure); + MXStrongifyAndReturnIfNil(self); + self.selectedRoom = nil; + }]; + + } failure:^(NSError *error) { + completeWithCode(INSendMessageIntentResponseCodeFailure); + }]; + + return; + } + + [account.mxRestClient sendTextMessageToRoom:roomID + threadId:nil + text:intent.content + success:^(NSString *eventId) { + completeWithCode(INSendMessageIntentResponseCodeSuccess); + } + failure:^(NSError *error) { + completeWithCode(INSendMessageIntentResponseCodeFailure); + }]; + + }]; + } + else + { + completeWithCode(INSendMessageIntentResponseCodeFailure); + } +} + +@end diff --git a/SiriIntents/IntentHandlers/StartAudioCall/StartAudioCallIntentHandler.h b/SiriIntents/IntentHandlers/StartAudioCall/StartAudioCallIntentHandler.h new file mode 100644 index 000000000..21f86b2f6 --- /dev/null +++ b/SiriIntents/IntentHandlers/StartAudioCall/StartAudioCallIntentHandler.h @@ -0,0 +1,29 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +@import Intents; +@protocol ContactResolving; + +NS_ASSUME_NONNULL_BEGIN + +@interface StartAudioCallIntentHandler : NSObject + +- (instancetype)initWithContactResolver:(id)contactResolver; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SiriIntents/IntentHandlers/StartAudioCall/StartAudioCallIntentHandler.m b/SiriIntents/IntentHandlers/StartAudioCall/StartAudioCallIntentHandler.m new file mode 100644 index 000000000..7d328e354 --- /dev/null +++ b/SiriIntents/IntentHandlers/StartAudioCall/StartAudioCallIntentHandler.m @@ -0,0 +1,90 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "StartAudioCallIntentHandler.h" +#import "MXKAccountManager.h" +#import "ContactResolver.h" +#import "GeneratedInterface-Swift.h" + +@interface StartAudioCallIntentHandler () + +@property (nonatomic) id contactResolver; + +@end + +@implementation StartAudioCallIntentHandler + +- (instancetype)initWithContactResolver:(id)contactResolver +{ + if (self = [super init]) { + _contactResolver = contactResolver; + } + + return self; +} + +#pragma mark - INStartAudioCallIntentHandling + +- (void)resolveContactsForStartAudioCall:(INStartAudioCallIntent *)intent withCompletion:(void (^)(NSArray * _Nonnull))completion +{ + [self.contactResolver resolveContacts:intent.contacts withCompletion:completion]; +} + +- (void)confirmStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion +{ + INStartAudioCallIntentResponse *response = nil; + + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + if (account) + { +#if defined MX_CALL_STACK_OPENWEBRTC || defined MX_CALL_STACK_ENDPOINT || defined CALL_STACK_JINGLE + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INStartAudioCallIntent class])]; + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeReady userActivity:userActivity]; +#else + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; +#endif + } + else + { + // User hasn't logged in + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureAppConfigurationRequired userActivity:nil]; + } + + completion(response); +} + +- (void)handleStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion +{ + INStartAudioCallIntentResponse *response = nil; + + INPerson *person = intent.contacts.firstObject; + if (person && person.customIdentifier) + { + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; + userActivity.userInfo = @{ @"roomID" : person.customIdentifier }; + + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeContinueInApp + userActivity:userActivity]; + } + else + { + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailure userActivity:nil]; + } + + completion(response); +} + +@end diff --git a/SiriIntents/IntentHandlers/StartVideoCall/StartVideoCallIntentHandler.h b/SiriIntents/IntentHandlers/StartVideoCall/StartVideoCallIntentHandler.h new file mode 100644 index 000000000..496c9fd72 --- /dev/null +++ b/SiriIntents/IntentHandlers/StartVideoCall/StartVideoCallIntentHandler.h @@ -0,0 +1,29 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import +@import Intents; +@protocol ContactResolving; + +NS_ASSUME_NONNULL_BEGIN + +@interface StartVideoCallIntentHandler : NSObject + +- (instancetype)initWithContactResolver:(id)contactResolver; + +@end + +NS_ASSUME_NONNULL_END diff --git a/SiriIntents/IntentHandlers/StartVideoCall/StartVideoCallIntentHandler.m b/SiriIntents/IntentHandlers/StartVideoCall/StartVideoCallIntentHandler.m new file mode 100644 index 000000000..b4fe090d0 --- /dev/null +++ b/SiriIntents/IntentHandlers/StartVideoCall/StartVideoCallIntentHandler.m @@ -0,0 +1,90 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +#import "StartVideoCallIntentHandler.h" +#import "ContactResolver.h" +#import "MXKAccountManager.h" +#import "GeneratedInterface-Swift.h" + +@interface StartVideoCallIntentHandler () + +@property (nonatomic) id contactResolver; + +@end + +@implementation StartVideoCallIntentHandler + +- (instancetype)initWithContactResolver:(id)contactResolver +{ + if (self = [super init]) { + _contactResolver = contactResolver; + } + + return self; +} + +#pragma mark - INStartVideoCallIntentHandling + +- (void)resolveContactsForStartVideoCall:(INStartVideoCallIntent *)intent withCompletion:(void (^)(NSArray * _Nonnull))completion +{ + [self.contactResolver resolveContacts:intent.contacts withCompletion:completion]; +} + +- (void)confirmStartVideoCall:(INStartVideoCallIntent *)intent completion:(void (^)(INStartVideoCallIntentResponse * _Nonnull))completion +{ + INStartVideoCallIntentResponse *response = nil; + + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + if (account) + { +#if defined MX_CALL_STACK_OPENWEBRTC || defined MX_CALL_STACK_ENDPOINT || defined CALL_STACK_JINGLE + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INStartVideoCallIntent class])]; + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeReady userActivity:userActivity]; +#else + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; +#endif + } + else + { + // User hasn't logged in + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeFailureRequiringAppLaunch userActivity:nil]; + } + + completion(response); +} + +- (void)handleStartVideoCall:(INStartVideoCallIntent *)intent completion:(void (^)(INStartVideoCallIntentResponse * _Nonnull))completion +{ + INStartVideoCallIntentResponse *response = nil; + + INPerson *person = intent.contacts.firstObject; + if (person && person.customIdentifier) + { + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartVideoCallIntent.class)]; + userActivity.userInfo = @{ @"roomID" : person.customIdentifier }; + + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeContinueInApp + userActivity:userActivity]; + } + else + { + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeFailure userActivity:nil]; + } + + completion(response); +} + +@end diff --git a/changelog.d/6203.build b/changelog.d/6203.build new file mode 100644 index 000000000..fb5d20f04 --- /dev/null +++ b/changelog.d/6203.build @@ -0,0 +1 @@ +Split `IntentHandler` into smaller, dedicated entities From be92de9bd7f962d6147893d886b69e229137745a Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 6 Jul 2022 13:20:24 +0300 Subject: [PATCH 56/97] Fix the safe area insets issue without method swizzling --- .../SwiftUI/VectorHostingController.swift | 117 ++++-------------- 1 file changed, 24 insertions(+), 93 deletions(-) diff --git a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift index c59dc357f..7a6ccaf7a 100644 --- a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift +++ b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift @@ -40,25 +40,20 @@ class VectorHostingController: UIHostingController { var enableNavigationBarScrollEdgeAppearance = false /// When non-nil, the style will be applied to the status bar. var statusBarStyle: UIStatusBarStyle? - - /// 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. - var forceZeroSafeAreaInsets: Bool { - get { - self.view.forceZeroSafeAreaInsets - } - set { - self.view.forceZeroSafeAreaInsets = newValue - } - } + + private let forceZeroSafeAreaInsets: Bool override var preferredStatusBarStyle: UIStatusBarStyle { statusBarStyle ?? super.preferredStatusBarStyle } - - init(rootView: Content) where Content: View { + /// 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(rootView: Content, + forceZeroSafeAreaInsets: Bool = false) where Content: View { self.theme = ThemeService.shared().theme + self.forceZeroSafeAreaInsets = forceZeroSafeAreaInsets super.init(rootView: AnyView(rootView.vectorContent())) - self.view.swizzleSafeAreaMethodsIfNeeded() } required init?(coder aDecoder: NSCoder) { @@ -106,6 +101,22 @@ class VectorHostingController: UIHostingController { self.view.invalidateIntrinsicContentSize() } } + + 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) @@ -121,83 +132,3 @@ class VectorHostingController: UIHostingController { } } } - -// Hack for forcing zero safe area insets on hosting views. This problem occurs when the hosting view is embedded -// in a table view. See https://stackoverflow.com/questions/61552497 for further info. - -private var hasSwizzledSafeAreaMethods = false -private var forceZeroSafeAreaInsetsKey: Void? - -private extension UIView { - - var forceZeroSafeAreaInsets: Bool { - get { - return objc_getAssociatedObject(self, &forceZeroSafeAreaInsetsKey) as? Bool == true - } - set { - objc_setAssociatedObject(self, &forceZeroSafeAreaInsetsKey, newValue, .OBJC_ASSOCIATION_RETAIN) - } - } - - @objc private var _safeAreaInsets: UIEdgeInsets { - return forceZeroSafeAreaInsets ? .zero : self._safeAreaInsets - } - - @objc private var _safeAreaLayoutGuide: UILayoutGuide? { - return forceZeroSafeAreaInsets ? nil : self._safeAreaLayoutGuide - } - - func swizzleSafeAreaMethodsIfNeeded() { - guard !hasSwizzledSafeAreaMethods else { - return - } - hasSwizzledSafeAreaMethods = true - - guard let getSafeAreaInsets = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView.safeAreaInsets)) else { - return - } - - guard let _getSafeAreaInsets = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView._safeAreaInsets)) else { - return - } - - let getSafeAreaInsetsImplementation = method_getImplementation(getSafeAreaInsets) - let _getSafeAreaInsetsImplementation = method_getImplementation(_getSafeAreaInsets) - - class_replaceMethod( - classForCoder, - #selector(getter: UIView.safeAreaInsets), - _getSafeAreaInsetsImplementation, - method_getTypeEncoding(getSafeAreaInsets)) - - class_replaceMethod( - classForCoder, - #selector(getter: UIView._safeAreaInsets), - getSafeAreaInsetsImplementation, - method_getTypeEncoding(_getSafeAreaInsets)) - - guard let getSafeAreaLayoutGuide = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView.safeAreaLayoutGuide)) else { - return - } - - guard let _getSafeAreaLayoutGuide = class_getInstanceMethod(classForCoder.self, #selector(getter: UIView._safeAreaLayoutGuide)) else { - return - } - - let getSafeAreaLayoutGuideImplementation = method_getImplementation(getSafeAreaLayoutGuide) - let _getSafeAreaLayoutGuideImplementation = method_getImplementation(_getSafeAreaLayoutGuide) - - class_replaceMethod( - classForCoder, - #selector(getter: UIView.safeAreaLayoutGuide), - _getSafeAreaLayoutGuideImplementation, - method_getTypeEncoding(getSafeAreaLayoutGuide)) - - class_replaceMethod( - classForCoder, - #selector(getter: UIView._safeAreaLayoutGuide), - getSafeAreaLayoutGuideImplementation, - method_getTypeEncoding(_getSafeAreaLayoutGuide)) - } - -} From 42cb4a9fbe633e3979e90df1f009fc5b363405b2 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 6 Jul 2022 13:20:41 +0300 Subject: [PATCH 57/97] Update UIHostingController initializer --- .../TimelinePoll/Coordinator/TimelinePollCoordinator.swift | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift index a92a959cb..2ac5741c0 100644 --- a/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift +++ b/RiotSwiftUI/Modules/Room/TimelinePoll/Coordinator/TimelinePollCoordinator.swift @@ -86,9 +86,8 @@ final class TimelinePollCoordinator: Coordinator, Presentable, PollAggregatorDel } func toPresentable() -> UIViewController { - let controller = VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context)) - controller.forceZeroSafeAreaInsets = true - return controller + return VectorHostingController(rootView: TimelinePollView(viewModel: viewModel.context), + forceZeroSafeAreaInsets: true) } func canEndPoll() -> Bool { From 5360a2f537ab01b54c311dde8221149464949166 Mon Sep 17 00:00:00 2001 From: ismailgulek Date: Wed, 6 Jul 2022 13:35:59 +0300 Subject: [PATCH 58/97] Add changelog --- changelog.d/pr-6381.bugfix | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/pr-6381.bugfix diff --git a/changelog.d/pr-6381.bugfix b/changelog.d/pr-6381.bugfix new file mode 100644 index 000000000..f34e363c1 --- /dev/null +++ b/changelog.d/pr-6381.bugfix @@ -0,0 +1 @@ +VectorHostingController: Fix infinite loop due to the safe area insets fix. From dd2c3a86e7405b4d76c6ab07c3871175d2798ac0 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 6 Jul 2022 12:36:51 +0200 Subject: [PATCH 59/97] LiveLocationLabPromotionView: Increase padding beetween toogle and validation button. --- .../View/LiveLocationLabPromotionView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/View/LiveLocationLabPromotionView.swift b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/View/LiveLocationLabPromotionView.swift index 7ebbda8a7..8ca11dd23 100644 --- a/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/View/LiveLocationLabPromotionView.swift +++ b/RiotSwiftUI/Modules/LocationSharing/LiveLocationLabPromotion/View/LiveLocationLabPromotionView.swift @@ -63,7 +63,7 @@ struct LiveLocationLabPromotionView: View { .font(theme.fonts.bodySB) } .buttonStyle(PrimaryActionButtonStyle()) - .padding(.top) + .padding(.top, 20) } .padding() } From 877e53c2b840554ce5109596724868f086dae2ca Mon Sep 17 00:00:00 2001 From: aringenbach Date: Tue, 5 Jul 2022 09:46:36 +0200 Subject: [PATCH 60/97] Add formatter build reply HTML unit tests --- .../MatrixKitTests/MXKEventFormatter+Tests.h | 1 + .../MatrixKitTests/MXKEventFormatterTests.m | 4 +- .../MXKEventFormatterTests.swift | 76 +++++++++++++++++++ .../MatrixKitTests-Bridging-Header.h | 1 + changelog.d/pr-6380.change | 1 + 5 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 RiotTests/MatrixKitTests/MXKEventFormatterTests.swift create mode 100644 changelog.d/pr-6380.change diff --git a/RiotTests/MatrixKitTests/MXKEventFormatter+Tests.h b/RiotTests/MatrixKitTests/MXKEventFormatter+Tests.h index 8aa5a9c1e..e7f3ccb6a 100644 --- a/RiotTests/MatrixKitTests/MXKEventFormatter+Tests.h +++ b/RiotTests/MatrixKitTests/MXKEventFormatter+Tests.h @@ -20,5 +20,6 @@ - (NSString*)userDisplayNameFromContentInEvent:(MXEvent*)event withMembershipFilter:(NSString *)filter; - (NSString*)userAvatarUrlFromContentInEvent:(MXEvent*)event withMembershipFilter:(NSString *)filter; +- (NSString*)buildHTMLStringForEvent:(MXEvent*)event inReplyToEvent:(MXEvent*)repliedEvent; @end diff --git a/RiotTests/MatrixKitTests/MXKEventFormatterTests.m b/RiotTests/MatrixKitTests/MXKEventFormatterTests.m index 089d93810..fbe801665 100644 --- a/RiotTests/MatrixKitTests/MXKEventFormatterTests.m +++ b/RiotTests/MatrixKitTests/MXKEventFormatterTests.m @@ -22,7 +22,7 @@ @import DTCoreText; -@interface MXEventFormatterTests : XCTestCase +@interface MXKEventFormatterTests : XCTestCase { MXKEventFormatter *eventFormatter; MXEvent *anEvent; @@ -31,7 +31,7 @@ @end -@implementation MXEventFormatterTests +@implementation MXKEventFormatterTests - (void)setUp { diff --git a/RiotTests/MatrixKitTests/MXKEventFormatterTests.swift b/RiotTests/MatrixKitTests/MXKEventFormatterTests.swift new file mode 100644 index 000000000..31db30954 --- /dev/null +++ b/RiotTests/MatrixKitTests/MXKEventFormatterTests.swift @@ -0,0 +1,76 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import XCTest +import MatrixSDK + +private enum Constants { + static let roomId = "someRoomId" + static let repliedEventId = "repliedEventId" + static let repliedEventBody = "Test message" + static let repliedEventEditedBody = "Edited message" + static let repliedEventNewContentBody = "New content" + static let replyBody = "> <@alice:matrix.org> Test message\n\nReply" + static let replyFormattedBodyWithItalic = "
In reply to alice
Test message
Reply" + static let expectedHTML = "
In reply to alice
Test message
Reply" + static let expectedEditedHTML = "
In reply to alice
Edited message
Reply" + static let expectedEditedHTMLWithNewContent = "
In reply to alice
New content
Reply" + static let expectedEditedHTMLWithParsedItalic = "
In reply to alice
New content
Reply" +} + +class MXKEventFormatterTests: XCTestCase { + func testBuildHTMLString() { + let formatter = MXKEventFormatter() + let repliedEvent = MXEvent() + let event = MXEvent() + func buildHTML() -> String? { return formatter.buildHTMLString(for: event, inReplyTo: repliedEvent) } + + // Initial setup. + repliedEvent.sender = "alice" + repliedEvent.roomId = Constants.roomId + repliedEvent.eventId = Constants.repliedEventId + repliedEvent.wireType = kMXEventTypeStringRoomMessage + repliedEvent.wireContent = [kMXMessageTypeKey: kMXMessageTypeText, + kMXMessageBodyKey: Constants.repliedEventBody] + event.sender = "bob" + event.wireType = kMXEventTypeStringRoomMessage + event.wireContent = [ + kMXMessageTypeKey: kMXMessageTypeText, + kMXMessageBodyKey: Constants.replyBody, + kMXEventRelationRelatesToKey: [kMXEventContentRelatesToKeyInReplyTo: ["event_id": Constants.repliedEventId]] + ] + + // Default render. + XCTAssertEqual(buildHTML(), Constants.expectedHTML) + + // Render after edition. + repliedEvent.wireContent[kMXMessageBodyKey] = Constants.repliedEventEditedBody + XCTAssertEqual(buildHTML(), Constants.expectedEditedHTML) + + // m.new_content has prioritiy over base content. + repliedEvent.wireContent[kMXMessageContentKeyNewContent] = [kMXMessageBodyKey: Constants.repliedEventNewContentBody] + XCTAssertEqual(buildHTML(), Constants.expectedEditedHTMLWithNewContent) + + // If reply's formatted_body is available it's used to construct a brand new HTML. + event.wireContent["formatted_body"] = Constants.replyFormattedBodyWithItalic + XCTAssertEqual(buildHTML(), Constants.expectedEditedHTMLWithParsedItalic) + + // If content from replied event is missing. Reply can't be constructed (client will use fallback). + repliedEvent.wireContent[kMXMessageBodyKey] = nil + repliedEvent.wireContent[kMXMessageContentKeyNewContent] = nil + XCTAssertNil(buildHTML()) + } +} diff --git a/RiotTests/MatrixKitTests/MatrixKitTests-Bridging-Header.h b/RiotTests/MatrixKitTests/MatrixKitTests-Bridging-Header.h index b3d959c69..648b077d9 100644 --- a/RiotTests/MatrixKitTests/MatrixKitTests-Bridging-Header.h +++ b/RiotTests/MatrixKitTests/MatrixKitTests-Bridging-Header.h @@ -3,3 +3,4 @@ // #import "MXKRoomDataSource+Tests.h" +#import "MXKEventFormatter+Tests.h" diff --git a/changelog.d/pr-6380.change b/changelog.d/pr-6380.change new file mode 100644 index 000000000..1465b71f0 --- /dev/null +++ b/changelog.d/pr-6380.change @@ -0,0 +1 @@ +Add formatter build reply HTML unit tests From c10143413051d66ce576cab914a00ed626dbac5e Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 6 Jul 2022 16:28:59 +0200 Subject: [PATCH 61/97] Live location sharing: Update stop sharing text. --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 871d73183..645af8d6c 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2182,7 +2182,7 @@ To enable access, tap Settings> Location and select Always"; "location_sharing_live_list_item_last_update" = "Updated %@ ago"; "location_sharing_live_list_item_last_update_invalid" = "Unknown last update"; "location_sharing_live_list_item_current_user_display_name" = "You"; -"location_sharing_live_list_item_stop_sharing_action" = "Stop sharing"; +"location_sharing_live_list_item_stop_sharing_action" = "Stop"; "location_sharing_live_timer_incoming" = "Live until %@"; "location_sharing_live_loading" = "Loading Live location..."; "location_sharing_live_error" = "Live location error"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 20c3bbab8..e7a782ff8 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2843,7 +2843,7 @@ public class VectorL10n: NSObject { public static var locationSharingLiveListItemSharingExpired: String { return VectorL10n.tr("Vector", "location_sharing_live_list_item_sharing_expired") } - /// Stop sharing + /// Stop public static var locationSharingLiveListItemStopSharingAction: String { return VectorL10n.tr("Vector", "location_sharing_live_list_item_stop_sharing_action") } From 7ffe1d62a381bc1af7cc2cea9be84bafb24100c5 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 6 Jul 2022 16:29:47 +0200 Subject: [PATCH 62/97] RoomTimelineLocationView: Update information banner opacity. --- Riot/Modules/Room/Location/RoomTimelineLocationView.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/Location/RoomTimelineLocationView.swift b/Riot/Modules/Room/Location/RoomTimelineLocationView.swift index cd290a412..4e59799e7 100644 --- a/Riot/Modules/Room/Location/RoomTimelineLocationView.swift +++ b/Riot/Modules/Room/Location/RoomTimelineLocationView.swift @@ -49,7 +49,7 @@ struct TimelineLiveLocationViewData { } var showMap: Bool { - guard case .started(_, _) = status else { + guard case .started = status else { return false } return true @@ -200,7 +200,7 @@ class RoomTimelineLocationView: UIView, NibLoadable, Themable, MGLMapViewDelegat } liveLocationContainerView.isHidden = false - liveLocationContainerView.backgroundColor = theme.colors.background.withAlphaComponent(0.75) + liveLocationContainerView.backgroundColor = theme.colors.background.withAlphaComponent(0.90) liveLocationIcon.image = Asset.Images.locationLiveCellIcon.image liveLocationIcon.tintColor = bannerViewData.iconTint From f3131446d743590d366eacf4ff8055da740b5e8f Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 6 Jul 2022 16:30:14 +0200 Subject: [PATCH 63/97] LiveLocationListItem: Update stop action font size. --- .../LiveLocationSharingViewer/View/LiveLocationListItem.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationListItem.swift b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationListItem.swift index 472652420..b6af40483 100644 --- a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationListItem.swift +++ b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationListItem.swift @@ -85,7 +85,7 @@ struct LiveLocationListItem: View { Button(VectorL10n.locationSharingLiveListItemStopSharingAction) { onStopSharingAction?() } - .font(theme.fonts.caption1) + .font(theme.fonts.body) .foregroundColor(theme.colors.alert) } } From c22afff461ead984f55cd5ce818b62618369e758 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 6 Jul 2022 16:37:41 +0200 Subject: [PATCH 64/97] LocationSharingView: Change close action title. --- .../Modules/Room/LocationSharing/View/LocationSharingView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift index 70d31f6da..f67e50821 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift @@ -43,7 +43,7 @@ struct LocationSharingView: View { .background(theme.colors.background.ignoresSafeArea()) .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(VectorL10n.cancel, action: { + Button(VectorL10n.close, action: { context.send(viewAction: .cancel) }) } From a115d9455a6dd41cee3821cf0fc1e1e11a9f1e2b Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 6 Jul 2022 17:05:15 +0200 Subject: [PATCH 65/97] LocationSharingView: Dismiss always authorization requirement alert on cancel. --- .../Modules/Room/LocationSharing/LocationSharingViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift index 91dc1153b..6261f9e6b 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift @@ -158,7 +158,7 @@ class LocationSharingViewModel: LocationSharingViewModelType, LocationSharingVie self.state.bindings.alertInfo = AlertInfo(id: .userLocatingError, title: VectorL10n.locationSharingAllowBackgroundLocationTitle, message: VectorL10n.locationSharingAllowBackgroundLocationMessage, - primaryButton: (VectorL10n.locationSharingAllowBackgroundLocationCancelAction, { [weak self] in self?.state.bindings.showingTimerSelector = true }), + primaryButton: (VectorL10n.locationSharingAllowBackgroundLocationCancelAction, {}), secondaryButton: (VectorL10n.locationSharingAllowBackgroundLocationValidateAction, { UIApplication.shared.vc_openSettings() })) case .authorizedAlways: self.state.bindings.showingTimerSelector = true From 3260b3a2548ca018a396a463d09a780df0b0da4e Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 6 Jul 2022 17:18:55 +0200 Subject: [PATCH 66/97] LiveLocationSharingViewer: Fix light theme issue. --- .../View/LiveLocationSharingViewer.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift index fbce0630a..b1913766e 100644 --- a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift +++ b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift @@ -85,6 +85,7 @@ struct LiveLocationSharingViewer: View { } .padding() } + .background(theme.colors.background.ignoresSafeArea()) } } From a10f0729b2a4fad7e68f1142c584fa2254e4efac Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 6 Jul 2022 17:22:47 +0200 Subject: [PATCH 67/97] Update changes --- changelog.d/6382.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6382.change diff --git a/changelog.d/6382.change b/changelog.d/6382.change new file mode 100644 index 000000000..6ed30b502 --- /dev/null +++ b/changelog.d/6382.change @@ -0,0 +1 @@ +Location sharing: Live location sharing UI polishing. From a0d3201b6edfb81e0e0a2011adbfc4a18533ae3d Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 7 Jul 2022 11:04:08 +0200 Subject: [PATCH 68/97] LiveLocationSharingViewer: Change close action title. --- .../View/LiveLocationSharingViewer.swift | 2 +- .../Modules/Room/LocationSharing/View/LocationSharingView.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift index b1913766e..25edbdd82 100644 --- a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift +++ b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift @@ -57,7 +57,7 @@ struct LiveLocationSharingViewer: View { .navigationTitle(VectorL10n.locationSharingLiveViewerTitle) .toolbar { ToolbarItem(placement: .cancellationAction) { - Button(VectorL10n.cancel) { + Button(VectorL10n.close) { viewModel.send(viewAction: .done) } } diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift index f67e50821..70d31f6da 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift @@ -43,7 +43,7 @@ struct LocationSharingView: View { .background(theme.colors.background.ignoresSafeArea()) .toolbar { ToolbarItem(placement: .navigationBarLeading) { - Button(VectorL10n.close, action: { + Button(VectorL10n.cancel, action: { context.send(viewAction: .cancel) }) } From cce5421bc18eef66f014ee6b226cbccdab0d7fdc Mon Sep 17 00:00:00 2001 From: Doug Date: Thu, 7 Jul 2022 10:05:43 +0100 Subject: [PATCH 69/97] Fix a few failing UI tests. --- .../MockAuthenticationServerSelectionScreenState.swift | 4 ++-- .../Test/UI/AuthenticationServerSelectionUITests.swift | 2 +- .../ChangePassword/Test/UI/ChangePasswordUITests.swift | 8 ++++---- changelog.d/6386.bugfix | 1 + 4 files changed, 8 insertions(+), 7 deletions(-) create mode 100644 changelog.d/6386.bugfix diff --git a/RiotSwiftUI/Modules/Authentication/ServerSelection/MockAuthenticationServerSelectionScreenState.swift b/RiotSwiftUI/Modules/Authentication/ServerSelection/MockAuthenticationServerSelectionScreenState.swift index 02cfc1ba5..ac95c3d69 100644 --- a/RiotSwiftUI/Modules/Authentication/ServerSelection/MockAuthenticationServerSelectionScreenState.swift +++ b/RiotSwiftUI/Modules/Authentication/ServerSelection/MockAuthenticationServerSelectionScreenState.swift @@ -38,7 +38,7 @@ enum MockAuthenticationServerSelectionScreenState: MockScreenState, CaseIterable let viewModel: AuthenticationServerSelectionViewModel switch self { case .matrix: - viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "https://matrix.org", + viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "matrix.org", hasModalPresentation: true) case .emptyAddress: viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "", @@ -48,7 +48,7 @@ enum MockAuthenticationServerSelectionScreenState: MockScreenState, CaseIterable hasModalPresentation: true) Task { await viewModel.displayError(.footerMessage(VectorL10n.errorCommonMessage)) } case .nonModal: - viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "https://matrix.org", + viewModel = AuthenticationServerSelectionViewModel(homeserverAddress: "matrix.org", hasModalPresentation: false) } diff --git a/RiotSwiftUI/Modules/Authentication/ServerSelection/Test/UI/AuthenticationServerSelectionUITests.swift b/RiotSwiftUI/Modules/Authentication/ServerSelection/Test/UI/AuthenticationServerSelectionUITests.swift index 84a39a4f4..4908493f2 100644 --- a/RiotSwiftUI/Modules/Authentication/ServerSelection/Test/UI/AuthenticationServerSelectionUITests.swift +++ b/RiotSwiftUI/Modules/Authentication/ServerSelection/Test/UI/AuthenticationServerSelectionUITests.swift @@ -43,7 +43,7 @@ class AuthenticationServerSelectionUITests: MockScreenTest { func verifyNormalState() { let serverTextField = app.textFields.element - XCTAssertEqual(serverTextField.value as? String, "matrix.org", "The server shown should be matrix.org with the https scheme hidden.") + XCTAssertEqual(serverTextField.value as? String, "matrix.org", "The server shown should be matrix.org as passed to the view model init.") let confirmButton = app.buttons["confirmButton"] XCTAssertEqual(confirmButton.label, VectorL10n.confirm, "The confirm button should say Confirm when in modal presentation.") diff --git a/RiotSwiftUI/Modules/Settings/ChangePassword/Test/UI/ChangePasswordUITests.swift b/RiotSwiftUI/Modules/Settings/ChangePassword/Test/UI/ChangePasswordUITests.swift index 5bdc3fddc..d534eac16 100644 --- a/RiotSwiftUI/Modules/Settings/ChangePassword/Test/UI/ChangePasswordUITests.swift +++ b/RiotSwiftUI/Modules/Settings/ChangePassword/Test/UI/ChangePasswordUITests.swift @@ -47,15 +47,15 @@ class ChangePasswordUITests: MockScreenTest { let oldPasswordTextField = app.secureTextFields["oldPasswordTextField"] XCTAssertTrue(oldPasswordTextField.exists, "The text field should be shown.") - XCTAssertEqual(oldPasswordTextField.label, "old password", "The text field should be showing the placeholder before text is input.") + XCTAssertEqual(oldPasswordTextField.label, "Old password", "The text field should be showing the placeholder before text is input.") let newPasswordTextField1 = app.secureTextFields["newPasswordTextField1"] XCTAssertTrue(newPasswordTextField1.exists, "The text field should be shown.") - XCTAssertEqual(newPasswordTextField1.label, "new password", "The text field should be showing the placeholder before text is input.") + XCTAssertEqual(newPasswordTextField1.label, "New password", "The text field should be showing the placeholder before text is input.") let newPasswordTextField2 = app.secureTextFields["newPasswordTextField2"] XCTAssertTrue(newPasswordTextField2.exists, "The text field should be shown.") - XCTAssertEqual(newPasswordTextField2.label, "confirm password", "The text field should be showing the placeholder before text is input.") + XCTAssertEqual(newPasswordTextField2.label, "Confirm password", "The text field should be showing the placeholder before text is input.") let submitButton = app.buttons["submitButton"] XCTAssertTrue(submitButton.exists, "The submit button should be shown.") @@ -80,7 +80,7 @@ class ChangePasswordUITests: MockScreenTest { let newPasswordTextField2 = app.secureTextFields["newPasswordTextField2"] XCTAssertTrue(newPasswordTextField2.exists, "The text field should be shown.") - XCTAssertEqual(newPasswordTextField2.label, "confirm password", "The text field should be showing the placeholder before text is input.") + XCTAssertEqual(newPasswordTextField2.label, "Confirm password", "The text field should be showing the placeholder before text is input.") let submitButton = app.buttons["submitButton"] XCTAssertTrue(submitButton.exists, "The submit button should be shown.") diff --git a/changelog.d/6386.bugfix b/changelog.d/6386.bugfix new file mode 100644 index 000000000..3135984e4 --- /dev/null +++ b/changelog.d/6386.bugfix @@ -0,0 +1 @@ +Fix a few failing UI tests. From 768a9e1cfda070c6ae21d24a8e2664bfabc8c4b0 Mon Sep 17 00:00:00 2001 From: Doug <6060466+pixlwave@users.noreply.github.com> Date: Thu, 7 Jul 2022 15:31:37 +0100 Subject: [PATCH 70/97] Add support for in-app notifications. (#6341) --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 2 +- .../PushNotificationService.m | 4 +- Riot/Managers/Settings/RiotSettings.swift | 4 + Riot/Modules/Application/LegacyAppDelegate.m | 132 ------------------ .../Modules/Settings/SettingsViewController.m | 19 +++ changelog.d/1108.feature | 1 + 7 files changed, 28 insertions(+), 136 deletions(-) create mode 100644 changelog.d/1108.feature diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 871d73183..34604b8fa 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2346,7 +2346,7 @@ To enable access, tap Settings> Location and select Always"; // Settings "settings" = "Settings"; -"settings_enable_inapp_notifications" = "Enable In-App notifications"; +"settings_enable_inapp_notifications" = "Enable in-app notifications"; "settings_enable_push_notifications" = "Enable push notifications"; "settings_enter_validation_token_for" = "Enter validation token for %@:"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 20c3bbab8..04a364597 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -6743,7 +6743,7 @@ public class VectorL10n: NSObject { public static var settingsEnableCallkit: String { return VectorL10n.tr("Vector", "settings_enable_callkit") } - /// Enable In-App notifications + /// Enable in-app notifications public static var settingsEnableInappNotifications: String { return VectorL10n.tr("Vector", "settings_enable_inapp_notifications") } diff --git a/Riot/Managers/PushNotification/PushNotificationService.m b/Riot/Managers/PushNotification/PushNotificationService.m index 27b07292a..58b9aaaf9 100644 --- a/Riot/Managers/PushNotification/PushNotificationService.m +++ b/Riot/Managers/PushNotification/PushNotificationService.m @@ -335,7 +335,7 @@ Matrix session observer used to detect new opened sessions. - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { NSDictionary *userInfo = notification.request.content.userInfo; - if (userInfo[Constants.userInfoKeyPresentNotificationOnForeground]) + if (RiotSettings.shared.showInAppNotifications || userInfo[Constants.userInfoKeyPresentNotificationOnForeground]) { if (!userInfo[Constants.userInfoKeyPresentNotificationInRoom] && [[AppDelegate theDelegate].visibleRoomId isEqualToString:userInfo[@"room_id"]]) @@ -347,7 +347,7 @@ Matrix session observer used to detect new opened sessions. { completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound - | UNNotificationPresentationOptionAlert); + | UNNotificationPresentationOptionBanner); } } else diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index f2dfc96cc..548fad9c7 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -80,6 +80,10 @@ final class RiotSettings: NSObject { return RiotSettings.defaults.object(forKey: UserDefaultsKeys.notificationsShowDecryptedContent) != nil } + /// Indicate if notifications should be shown whilst the app is in the foreground. + @UserDefault(key: "showInAppNotifications", defaultValue: true, storage: defaults) + var showInAppNotifications + /// Indicate if encrypted messages content should be displayed in notifications. @UserDefault(key: UserDefaultsKeys.notificationsShowDecryptedContent, defaultValue: false, storage: defaults) var showDecryptedContentInNotifications diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index 316f80c18..06dfac203 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -195,8 +195,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni UIView *launchAnimationContainerView; } -@property (strong, nonatomic) UIAlertController *mxInAppNotification; - @property (strong, nonatomic) UIAlertController *logoutConfirmation; @property (weak, nonatomic) UIAlertController *gdprConsentNotGivenAlertController; @@ -587,13 +585,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Remove expired URL previews from the cache [URLPreviewService.shared removeExpiredCacheData]; - // Hide potential notification - if (self.mxInAppNotification) - { - [self.mxInAppNotification dismissViewControllerAnimated:NO completion:nil]; - self.mxInAppNotification = nil; - } - // Discard any process on pending universal link [self resetPendingUniversalLink]; @@ -1820,18 +1811,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // start the call service [self.callPresenter start]; - // Look for the account related to this session. - NSArray *mxAccounts = [MXKAccountManager sharedManager].activeAccounts; - for (MXKAccount *account in mxAccounts) - { - if (account.mxSession == mxSession) - { - // Enable inApp notifications (if they are allowed for this account). - [self enableInAppNotificationsForAccount:account]; - break; - } - } - [self.configuration setupSettingsWhenLoadedFor:mxSession]; // Register to user new device sign in notification @@ -1888,9 +1867,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Set up push notifications [self.pushNotificationService registerUserNotificationSettings]; } - - // Observe inApp notifications toggle change - [account addObserver:self forKeyPath:@"enableInAppNotifications" options:0 context:nil]; } [self.delegate legacyAppDelegate:self didAddAccount:account]; @@ -1901,10 +1877,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Remove inApp notifications toggle change MXKAccount *account = notif.object; - if (!account.isSoftLogout) - { - [account removeObserver:self forKeyPath:@"enableInAppNotifications"]; - } // Clear Modular data [[WidgetManager sharedManager] deleteDataForUser:account.mxCredentials.userId]; @@ -1984,12 +1956,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Set up push notifications [self.pushNotificationService registerUserNotificationSettings]; - - // Observe inApp notifications toggle change for each account - for (MXKAccount *account in mxAccounts) - { - [account addObserver:self forKeyPath:@"enableInAppNotifications" options:0 context:nil]; - } } } @@ -2256,10 +2222,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Flush and restore Matrix data [self reloadMatrixSessions:NO]; } - else if ([@"enableInAppNotifications" isEqualToString:keyPath] && [object isKindOfClass:[MXKAccount class]]) - { - [self enableInAppNotificationsForAccount:(MXKAccount*)object]; - } else if (object == [MXKAppSettings standardAppSettings] && [keyPath isEqualToString:@"enableCallKit"]) { BOOL isCallKitEnabled = [MXKAppSettings standardAppSettings].isCallKitEnabled; @@ -2656,100 +2618,6 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni #pragma mark - Matrix Accounts handling -- (void)enableInAppNotificationsForAccount:(MXKAccount*)account -{ - if (account.mxSession) - { - if (account.enableInAppNotifications) - { - // Build MXEvent -> NSString formatter - EventFormatter *eventFormatter = [[EventFormatter alloc] initWithMatrixSession:account.mxSession]; - eventFormatter.isForSubtitle = YES; - - [account listenToNotifications:^(MXEvent *event, MXRoomState *roomState, MXPushRule *rule) { - - // Check conditions to display this notification - if (![self.visibleRoomId isEqualToString:event.roomId] - && !self.window.rootViewController.presentedViewController) - { - MXKEventFormatterError error; - NSString* messageText = [eventFormatter stringFromEvent:event - withRoomState:roomState - andLatestRoomState:nil - error:&error]; - if (messageText.length && (error == MXKEventFormatterErrorNone)) - { - // Removing existing notification (if any) - if (self.mxInAppNotification) - { - [self.mxInAppNotification dismissViewControllerAnimated:NO completion:nil]; - } - - // Check whether tweak is required - for (MXPushRuleAction *ruleAction in rule.actions) - { - if (ruleAction.actionType == MXPushRuleActionTypeSetTweak) - { - if ([[ruleAction.parameters valueForKey:@"set_tweak"] isEqualToString:@"sound"]) - { - // Play message sound - AudioServicesPlaySystemSound(self->_messageSound); - } - } - } - - MXRoomSummary *roomSummary = [account.mxSession roomSummaryWithRoomId:event.roomId]; - - __weak typeof(self) weakSelf = self; - self.mxInAppNotification = [UIAlertController alertControllerWithTitle:roomSummary.displayname - message:messageText - preferredStyle:UIAlertControllerStyleAlert]; - - [self.mxInAppNotification addAction:[UIAlertAction actionWithTitle:[VectorL10n cancel] - style:UIAlertActionStyleCancel - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self.mxInAppNotification = nil; - [account updateNotificationListenerForRoomId:event.roomId ignore:YES]; - } - - }]]; - - [self.mxInAppNotification addAction:[UIAlertAction actionWithTitle:[VectorL10n view] - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self.mxInAppNotification = nil; - // Show the room - [self showRoom:event.roomId andEventId:nil withMatrixSession:account.mxSession]; - } - - }]]; - - [self.window.rootViewController presentViewController:self.mxInAppNotification animated:YES completion:nil]; - } - } - }]; - } - else - { - [account removeNotificationListener]; - } - } - - if (self.mxInAppNotification) - { - [self.mxInAppNotification dismissViewControllerAnimated:NO completion:nil]; - self.mxInAppNotification = nil; - } -} - - (void)selectMatrixAccount:(void (^)(MXKAccount *selectedAccount))onSelection { NSArray *mxAccounts = [MXKAccountManager sharedManager].activeAccounts; diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index f8e383773..b0d5ffc15 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -103,6 +103,7 @@ typedef NS_ENUM(NSUInteger, NOTIFICATION_SETTINGS) { NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX = 0, NOTIFICATION_SETTINGS_SYSTEM_SETTINGS, + NOTIFICATION_SETTINGS_SHOW_IN_APP_INDEX, NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT, NOTIFICATION_SETTINGS_PIN_MISSED_NOTIFICATIONS_INDEX, NOTIFICATION_SETTINGS_PIN_UNREAD_INDEX, @@ -410,6 +411,7 @@ ChangePasswordCoordinatorBridgePresenterDelegate> Section *sectionNotificationSettings = [Section sectionWithTag:SECTION_TAG_NOTIFICATIONS]; [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_ENABLE_PUSH_INDEX]; [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SYSTEM_SETTINGS]; + [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_IN_APP_INDEX]; if (RiotSettings.shared.settingsScreenShowNotificationDecodedContentOption) { [sectionNotificationSettings addRowWithTag:NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT]; @@ -2076,6 +2078,18 @@ ChangePasswordCoordinatorBridgePresenterDelegate> [cell vc_setAccessoryDisclosureIndicatorWithCurrentTheme]; cell.selectionStyle = UITableViewCellSelectionStyleDefault; } + else if (row == NOTIFICATION_SETTINGS_SHOW_IN_APP_INDEX) + { + MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; + + labelAndSwitchCell.mxkLabel.text = VectorL10n.settingsEnableInappNotifications; + labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.showInAppNotifications; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; + labelAndSwitchCell.mxkSwitch.enabled = account.pushNotificationServiceIsActive; + [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleShowInAppNotifications:) forControlEvents:UIControlEventTouchUpInside]; + + cell = labelAndSwitchCell; + } else if (row == NOTIFICATION_SETTINGS_SHOW_DECODED_CONTENT) { MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; @@ -3165,6 +3179,11 @@ ChangePasswordCoordinatorBridgePresenterDelegate> } } +- (void)toggleShowInAppNotifications:(UISwitch *)sender +{ + RiotSettings.shared.showInAppNotifications = sender.isOn; +} + - (void)openSystemSettingsApp { NSURL *settingsAppURL = [NSURL URLWithString:UIApplicationOpenSettingsURLString]; diff --git a/changelog.d/1108.feature b/changelog.d/1108.feature new file mode 100644 index 000000000..e33cae1a0 --- /dev/null +++ b/changelog.d/1108.feature @@ -0,0 +1 @@ +Notifications: Add a setting for in-app notifications and use the value with existing functionality in PushNotificationService. From 4858d72afb2ab782b4a947787493711a5b2beff1 Mon Sep 17 00:00:00 2001 From: Doug Date: Fri, 20 May 2022 18:43:35 +0100 Subject: [PATCH 71/97] Replace DesignKit with package from ElementX. --- DesignKit/Common.xcconfig | 28 ---- DesignKit/Debug.xcconfig | 20 --- DesignKit/DesignKit.h | 27 ---- DesignKit/Extensions/UIFont.swift | 55 ------- DesignKit/Info.plist | 22 --- DesignKit/Release.xcconfig | 20 --- DesignKit/Source/ColorValues.swift | 52 ------ DesignKit/Source/Colors.swift | 73 --------- DesignKit/Source/ColorsSwiftUI.swift | 69 -------- DesignKit/Source/ColorsUIkit.swift | 67 -------- DesignKit/Source/Fonts.swift | 85 ---------- DesignKit/Source/FontsSwiftUI.swift | 91 ----------- DesignKit/Source/FontsUIkit.swift | 87 ---------- .../Variants/Colors/Dark/DarkColors.swift | 51 ------ .../Variants/Colors/Light/LightColors.swift | 57 ------- DesignKit/Variants/Fonts/ElementFonts.swift | 150 ------------------ DesignKit/target.yml | 33 ---- .../Managers/Theme}/ThemeV2.swift | 35 ++-- Riot/Managers/Theme/Themes/DarkTheme.swift | 6 +- Riot/Managers/Theme/Themes/DefaultTheme.swift | 7 +- .../ManageSessionViewController.m | 2 +- .../Security/SecurityViewController.m | 2 +- .../Modules/Settings/SettingsViewController.m | 2 +- Riot/target.yml | 2 +- RiotShareExtension/target.yml | 2 + .../Common/Avatar/Model}/AvatarSize.swift | 2 + .../Modules/Common/Theme/ThemeSwiftUI.swift | 35 +++- .../Theme/Themes/DarkThemeSwiftUI.swift | 6 +- .../Theme/Themes/DefaultThemeSwiftUI.swift | 6 +- .../Common/Util/MultilineTextField.swift | 2 +- RiotSwiftUI/target.yml | 2 +- project.yml | 4 +- 32 files changed, 81 insertions(+), 1021 deletions(-) delete mode 100644 DesignKit/Common.xcconfig delete mode 100644 DesignKit/Debug.xcconfig delete mode 100644 DesignKit/DesignKit.h delete mode 100644 DesignKit/Extensions/UIFont.swift delete mode 100644 DesignKit/Info.plist delete mode 100644 DesignKit/Release.xcconfig delete mode 100644 DesignKit/Source/ColorValues.swift delete mode 100644 DesignKit/Source/Colors.swift delete mode 100644 DesignKit/Source/ColorsSwiftUI.swift delete mode 100644 DesignKit/Source/ColorsUIkit.swift delete mode 100644 DesignKit/Source/Fonts.swift delete mode 100644 DesignKit/Source/FontsSwiftUI.swift delete mode 100644 DesignKit/Source/FontsUIkit.swift delete mode 100644 DesignKit/Variants/Colors/Dark/DarkColors.swift delete mode 100644 DesignKit/Variants/Colors/Light/LightColors.swift delete mode 100644 DesignKit/Variants/Fonts/ElementFonts.swift delete mode 100644 DesignKit/target.yml rename {DesignKit/Source => Riot/Managers/Theme}/ThemeV2.swift (58%) rename {DesignKit/Source => RiotSwiftUI/Modules/Common/Avatar/Model}/AvatarSize.swift (95%) diff --git a/DesignKit/Common.xcconfig b/DesignKit/Common.xcconfig deleted file mode 100644 index eb4b88c16..000000000 --- a/DesignKit/Common.xcconfig +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright 2021 Vector Creations Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// Configuration settings file format documentation can be found at: -// https://help.apple.com/xcode/#/dev745c5c974 - -#include "Config/AppIdentifiers.xcconfig" -#include "Config/AppVersion.xcconfig" - -PRODUCT_NAME = DesignKit -PRODUCT_BUNDLE_IDENTIFIER = $(BASE_BUNDLE_IDENTIFIER).designkit - -INFOPLIST_FILE = DesignKit/Info.plist - -SKIP_INSTALL = YES diff --git a/DesignKit/Debug.xcconfig b/DesignKit/Debug.xcconfig deleted file mode 100644 index 11a7288a4..000000000 --- a/DesignKit/Debug.xcconfig +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright 2021 Vector Creations Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// Configuration settings file format documentation can be found at: -// https://help.apple.com/xcode/#/dev745c5c974 - -#include "Common.xcconfig" diff --git a/DesignKit/DesignKit.h b/DesignKit/DesignKit.h deleted file mode 100644 index 4ff68e722..000000000 --- a/DesignKit/DesignKit.h +++ /dev/null @@ -1,27 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -#import - -//! Project version number for DesignKit. -FOUNDATION_EXPORT double DesignKitVersionNumber; - -//! Project version string for DesignKit. -FOUNDATION_EXPORT const unsigned char DesignKitVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/DesignKit/Extensions/UIFont.swift b/DesignKit/Extensions/UIFont.swift deleted file mode 100644 index 7804c8066..000000000 --- a/DesignKit/Extensions/UIFont.swift +++ /dev/null @@ -1,55 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import UIKit - -public extension UIFont { - - // MARK: - Convenient methods - - /// Update current font with a SymbolicTraits - func vc_withTraits(_ traits: UIFontDescriptor.SymbolicTraits) -> UIFont { - guard let descriptor = fontDescriptor.withSymbolicTraits(traits) else { - return self - } - return UIFont(descriptor: descriptor, size: 0) // Size 0 means keep the size as it is - } - - /// Update current font with a given Weight - func vc_withWeight(weight: Weight) -> UIFont { - // Add the font weight to the descriptor - let weightedFontDescriptor = fontDescriptor.addingAttributes([ - UIFontDescriptor.AttributeName.traits: [ - UIFontDescriptor.TraitKey.weight: weight - ] - ]) - return UIFont(descriptor: weightedFontDescriptor, size: 0) - } - - // MARK: - Shortcuts - - var vc_bold: UIFont { - return self.vc_withTraits(.traitBold) - } - - var vc_semiBold: UIFont { - return self.vc_withWeight(weight: .semibold) - } - - var vc_italic: UIFont { - return self.vc_withTraits(.traitItalic) - } -} diff --git a/DesignKit/Info.plist b/DesignKit/Info.plist deleted file mode 100644 index c0701c6d7..000000000 --- a/DesignKit/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - $(MARKETING_VERSION) - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - - diff --git a/DesignKit/Release.xcconfig b/DesignKit/Release.xcconfig deleted file mode 100644 index 11a7288a4..000000000 --- a/DesignKit/Release.xcconfig +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright 2021 Vector Creations Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -// Configuration settings file format documentation can be found at: -// https://help.apple.com/xcode/#/dev745c5c974 - -#include "Common.xcconfig" diff --git a/DesignKit/Source/ColorValues.swift b/DesignKit/Source/ColorValues.swift deleted file mode 100644 index 338d1cfe8..000000000 --- a/DesignKit/Source/ColorValues.swift +++ /dev/null @@ -1,52 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import UIKit - -/** - Struct for holding colour values for a particular theme. - */ -public struct ColorValues: Colors { - - public let accent: UIColor - - public let alert: UIColor - - public let primaryContent: UIColor - - public let secondaryContent: UIColor - - public let tertiaryContent: UIColor - - public let quarterlyContent: UIColor - - public let quinaryContent: UIColor - - public let separator: UIColor - - public let system: UIColor - - public let tile: UIColor - - public let navigation: UIColor - - public let background: UIColor - - public let ems: UIColor - - public let namesAndAvatars: [UIColor] -} diff --git a/DesignKit/Source/Colors.swift b/DesignKit/Source/Colors.swift deleted file mode 100644 index bf3e9abd3..000000000 --- a/DesignKit/Source/Colors.swift +++ /dev/null @@ -1,73 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -/// Colors at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1255%3A1104 -public protocol Colors { - - associatedtype ColorType - - /// - Focused/Active states - /// - CTAs - var accent: ColorType { get } - - /// - Error messages - /// - Content requiring user attention - /// - Notification, alerts - var alert: ColorType { get } - - /// - Text - /// - Icons - var primaryContent: ColorType { get } - - /// - Text - /// - Icons - var secondaryContent: ColorType { get } - - /// - Text - /// - Icons - var tertiaryContent: ColorType { get } - - /// - Text - /// - Icons - var quarterlyContent: ColorType { get } - - /// - separating lines and other UI components - var quinaryContent: ColorType { get } - - /// - System-based areas and backgrounds - var system: ColorType { get } - - /// Separating line - var separator: ColorType { get } - - /// Cards, tiles - var tile: ColorType { get } - - /// Top navigation background on iOS - var navigation: ColorType { get } - - /// Background UI color - var background: ColorType { get } - - /// Global color: The EMS brand's purple colour. - var ems: ColorType { get } - - /// - Names in chat timeline - /// - Avatars default states that include first name letter - var namesAndAvatars: [ColorType] { get } -} diff --git a/DesignKit/Source/ColorsSwiftUI.swift b/DesignKit/Source/ColorsSwiftUI.swift deleted file mode 100644 index ea3ca6779..000000000 --- a/DesignKit/Source/ColorsSwiftUI.swift +++ /dev/null @@ -1,69 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import SwiftUI - -/** - Struct for holding colors for use in SwiftUI. - */ -public struct ColorSwiftUI: Colors { - - public let accent: Color - - public let alert: Color - - public let primaryContent: Color - - public let secondaryContent: Color - - public let tertiaryContent: Color - - public let quarterlyContent: Color - - public let quinaryContent: Color - - public let separator: Color - - public var system: Color - - public let tile: Color - - public let navigation: Color - - public let background: Color - - public var ems: Color - - public let namesAndAvatars: [Color] - - init(values: ColorValues) { - accent = Color(values.accent) - alert = Color(values.alert) - primaryContent = Color(values.primaryContent) - secondaryContent = Color(values.secondaryContent) - tertiaryContent = Color(values.tertiaryContent) - quarterlyContent = Color(values.quarterlyContent) - quinaryContent = Color(values.quinaryContent) - separator = Color(values.separator) - system = Color(values.system) - tile = Color(values.tile) - navigation = Color(values.navigation) - background = Color(values.background) - ems = Color(values.ems) - namesAndAvatars = values.namesAndAvatars.map({ Color($0) }) - } -} diff --git a/DesignKit/Source/ColorsUIkit.swift b/DesignKit/Source/ColorsUIkit.swift deleted file mode 100644 index 3add385c3..000000000 --- a/DesignKit/Source/ColorsUIkit.swift +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import UIKit - -/** - ObjC class for holding colors for use in UIKit. - */ -@objcMembers public class ColorsUIKit: NSObject { - - public let accent: UIColor - - public let alert: UIColor - - public let primaryContent: UIColor - - public let secondaryContent: UIColor - - public let tertiaryContent: UIColor - - public let quarterlyContent: UIColor - - public let quinaryContent: UIColor - - public let separator: UIColor - - public let system: UIColor - - public let tile: UIColor - - public let navigation: UIColor - - public let background: UIColor - - public let namesAndAvatars: [UIColor] - - init(values: ColorValues) { - accent = values.accent - alert = values.alert - primaryContent = values.primaryContent - secondaryContent = values.secondaryContent - tertiaryContent = values.tertiaryContent - quarterlyContent = values.quarterlyContent - quinaryContent = values.quinaryContent - separator = values.separator - system = values.system - tile = values.tile - navigation = values.navigation - background = values.background - namesAndAvatars = values.namesAndAvatars - } -} - diff --git a/DesignKit/Source/Fonts.swift b/DesignKit/Source/Fonts.swift deleted file mode 100644 index 1203a2888..000000000 --- a/DesignKit/Source/Fonts.swift +++ /dev/null @@ -1,85 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import UIKit - -/// Describe fonts used in the application. -/// Font names are based on Element typograhy https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0 which is based on Apple font text styles (UIFont.TextStyle): https://developer.apple.com/documentation/uikit/uifonttextstyle -/// Create a custom TextStyle enum (like DesignKit.Fonts.TextStyle) is also a possiblity -public protocol Fonts { - - associatedtype FontType - - /// The font for large titles. - var largeTitle: FontType { get } - - /// `largeTitle` with a Bold weight. - var largeTitleB: FontType { get } - - /// The font for first-level hierarchical headings. - var title1: FontType { get } - - /// `title1` with a Bold weight. - var title1B: FontType { get } - - /// The font for second-level hierarchical headings. - var title2: FontType { get } - - /// `title2` with a Bold weight. - var title2B: FontType { get } - - /// The font for third-level hierarchical headings. - var title3: FontType { get } - - /// `title3` with a Semi Bold weight. - var title3SB: FontType { get } - - /// The font for headings. - var headline: FontType { get } - - /// The font for subheadings. - var subheadline: FontType { get } - - /// The font for body text. - var body: FontType { get } - - /// `body` with a Semi Bold weight. - var bodySB: FontType { get } - - /// The font for callouts. - var callout: FontType { get } - - /// `callout` with a Semi Bold weight. - var calloutSB: FontType { get } - - /// The font for footnotes. - var footnote: FontType { get } - - /// `footnote` with a Semi Bold weight. - var footnoteSB: FontType { get } - - /// The font for standard captions. - var caption1: FontType { get } - - /// `caption1` with a Semi Bold weight. - var caption1SB: FontType { get } - - /// The font for alternate captions. - var caption2: FontType { get } - - /// `caption2` with a Semi Bold weight. - var caption2SB: FontType { get } -} diff --git a/DesignKit/Source/FontsSwiftUI.swift b/DesignKit/Source/FontsSwiftUI.swift deleted file mode 100644 index 83b4e820b..000000000 --- a/DesignKit/Source/FontsSwiftUI.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import SwiftUI - -/** - Struct for holding fonts for use in SwiftUI. - */ -public struct FontSwiftUI: Fonts { - - public let uiFonts: FontsUIKit - - public var largeTitle: Font - - public var largeTitleB: Font - - public var title1: Font - - public var title1B: Font - - public var title2: Font - - public var title2B: Font - - public var title3: Font - - public var title3SB: Font - - public var headline: Font - - public var subheadline: Font - - public var body: Font - - public var bodySB: Font - - public var callout: Font - - public var calloutSB: Font - - public var footnote: Font - - public var footnoteSB: Font - - public var caption1: Font - - public var caption1SB: Font - - public var caption2: Font - - public var caption2SB: Font - - public init(values: ElementFonts) { - self.uiFonts = FontsUIKit(values: values) - - self.largeTitle = values.largeTitle.font - self.largeTitleB = values.largeTitleB.font - self.title1 = values.title1.font - self.title1B = values.title1B.font - self.title2 = values.title2.font - self.title2B = values.title2B.font - self.title3 = values.title3.font - self.title3SB = values.title3SB.font - self.headline = values.headline.font - self.subheadline = values.subheadline.font - self.body = values.body.font - self.bodySB = values.bodySB.font - self.callout = values.callout.font - self.calloutSB = values.calloutSB.font - self.footnote = values.footnote.font - self.footnoteSB = values.footnoteSB.font - self.caption1 = values.caption1.font - self.caption1SB = values.caption1SB.font - self.caption2 = values.caption2.font - self.caption2SB = values.caption2SB.font - } -} diff --git a/DesignKit/Source/FontsUIkit.swift b/DesignKit/Source/FontsUIkit.swift deleted file mode 100644 index ec65cdaa6..000000000 --- a/DesignKit/Source/FontsUIkit.swift +++ /dev/null @@ -1,87 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import UIKit - -/** - ObjC class for holding fonts for use in UIKit. - */ -@objcMembers public class FontsUIKit: NSObject, Fonts { - - public var largeTitle: UIFont - - public var largeTitleB: UIFont - - public var title1: UIFont - - public var title1B: UIFont - - public var title2: UIFont - - public var title2B: UIFont - - public var title3: UIFont - - public var title3SB: UIFont - - public var headline: UIFont - - public var subheadline: UIFont - - public var body: UIFont - - public var bodySB: UIFont - - public var callout: UIFont - - public var calloutSB: UIFont - - public var footnote: UIFont - - public var footnoteSB: UIFont - - public var caption1: UIFont - - public var caption1SB: UIFont - - public var caption2: UIFont - - public var caption2SB: UIFont - - public init(values: ElementFonts) { - self.largeTitle = values.largeTitle.uiFont - self.largeTitleB = values.largeTitleB.uiFont - self.title1 = values.title1.uiFont - self.title1B = values.title1B.uiFont - self.title2 = values.title2.uiFont - self.title2B = values.title2B.uiFont - self.title3 = values.title3.uiFont - self.title3SB = values.title3SB.uiFont - self.headline = values.headline.uiFont - self.subheadline = values.subheadline.uiFont - self.body = values.body.uiFont - self.bodySB = values.bodySB.uiFont - self.callout = values.callout.uiFont - self.calloutSB = values.calloutSB.uiFont - self.footnote = values.footnote.uiFont - self.footnoteSB = values.footnoteSB.uiFont - self.caption1 = values.caption1.uiFont - self.caption1SB = values.caption1SB.uiFont - self.caption2 = values.caption2.uiFont - self.caption2SB = values.caption2SB.uiFont - } -} diff --git a/DesignKit/Variants/Colors/Dark/DarkColors.swift b/DesignKit/Variants/Colors/Dark/DarkColors.swift deleted file mode 100644 index 88bd12ff3..000000000 --- a/DesignKit/Variants/Colors/Dark/DarkColors.swift +++ /dev/null @@ -1,51 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import UIKit -import SwiftUI - -/// Dark theme colors. -public class DarkColors { - private static let values = ColorValues( - accent: UIColor(rgb:0x0DBD8B), - alert: UIColor(rgb:0xFF4B55), - primaryContent: UIColor(rgb:0xFFFFFF), - secondaryContent: UIColor(rgb:0xA9B2BC), - tertiaryContent: UIColor(rgb:0x8E99A4), - quarterlyContent: UIColor(rgb:0x6F7882), - quinaryContent: UIColor(rgb:0x394049), - separator: UIColor(rgb:0x21262C), - system: UIColor(rgb:0x21262C), - tile: UIColor(rgb:0x394049), - navigation: UIColor(rgb:0x21262C), - background: UIColor(rgb:0x15191E), - ems: UIColor(rgb: 0x7E69FF), - namesAndAvatars: [ - UIColor(rgb:0x368BD6), - UIColor(rgb:0xAC3BA8), - UIColor(rgb:0x03B381), - UIColor(rgb:0xE64F7A), - UIColor(rgb:0xFF812D), - UIColor(rgb:0x2DC2C5), - UIColor(rgb:0x5C56F5), - UIColor(rgb:0x74D12C) - ] - ) - - public static var uiKit = ColorsUIKit(values: values) - public static var swiftUI = ColorSwiftUI(values: values) -} diff --git a/DesignKit/Variants/Colors/Light/LightColors.swift b/DesignKit/Variants/Colors/Light/LightColors.swift deleted file mode 100644 index 93cb3eadb..000000000 --- a/DesignKit/Variants/Colors/Light/LightColors.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import UIKit -import SwiftUI - - -/// Light theme colors. -public class LightColors { - private static let values = ColorValues( - accent: UIColor(rgb:0x0DBD8B), - alert: UIColor(rgb:0xFF4B55), - primaryContent: UIColor(rgb:0x17191C), - secondaryContent: UIColor(rgb:0x737D8C), - tertiaryContent: UIColor(rgb:0x8D97A5), - quarterlyContent: UIColor(rgb:0xC1C6CD), - quinaryContent: UIColor(rgb:0xE3E8F0), - separator: UIColor(rgb:0xE3E8F0), - system: UIColor(rgb:0xF4F6FA), - tile: UIColor(rgb:0xF3F8FD), - navigation: UIColor(rgb:0xF4F6FA), - background: UIColor(rgb:0xFFFFFF), - ems: UIColor(rgb: 0x7E69FF), - namesAndAvatars: [ - UIColor(rgb:0x368BD6), - UIColor(rgb:0xAC3BA8), - UIColor(rgb:0x03B381), - UIColor(rgb:0xE64F7A), - UIColor(rgb:0xFF812D), - UIColor(rgb:0x2DC2C5), - UIColor(rgb:0x5C56F5), - UIColor(rgb:0x74D12C) - ] - ) - - public static var uiKit = ColorsUIKit(values: values) - public static var swiftUI = ColorSwiftUI(values: values) -} - - - - - diff --git a/DesignKit/Variants/Fonts/ElementFonts.swift b/DesignKit/Variants/Fonts/ElementFonts.swift deleted file mode 100644 index e0a612f85..000000000 --- a/DesignKit/Variants/Fonts/ElementFonts.swift +++ /dev/null @@ -1,150 +0,0 @@ -// -// Copyright 2021 New Vector Ltd -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import SwiftUI - -/// Fonts at https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1362%3A0 -@objcMembers -public class ElementFonts { - - // MARK: - Types - - /// A wrapper to provide both a `UIFont` and a SwiftUI `Font` in the same type. - /// The need for this comes from `Font` not adapting for dynamic type until the app - /// is restarted (or working at all in Xcode Previews) when initialised from a `UIFont` - /// (even if that font was created with the appropriate metrics). - public struct SharedFont { - public let uiFont: UIFont - public let font: Font - } - - // MARK: - Setup - - public init() { - } - - // MARK: - Private - - /// Returns an instance of the font associated with the text style and scaled appropriately for the content size category defined in the trait collection. - /// Keep this method private method at the moment and create a DesignKit.Fonts.TextStyle if needed. - fileprivate func font(forTextStyle textStyle: UIFont.TextStyle, compatibleWith traitCollection: UITraitCollection? = nil) -> UIFont { - return UIFont.preferredFont(forTextStyle: textStyle, compatibleWith: traitCollection) - } -} - -// MARK: - Fonts protocol -extension ElementFonts: Fonts { - - public var largeTitle: SharedFont { - let uiFont = self.font(forTextStyle: .largeTitle) - return SharedFont(uiFont: uiFont, font: .largeTitle) - } - - public var largeTitleB: SharedFont { - let uiFont = self.largeTitle.uiFont.vc_bold - return SharedFont(uiFont: uiFont, font: .largeTitle.bold()) - } - - public var title1: SharedFont { - let uiFont = self.font(forTextStyle: .title1) - return SharedFont(uiFont: uiFont, font: .title) - } - - public var title1B: SharedFont { - let uiFont = self.title1.uiFont.vc_bold - return SharedFont(uiFont: uiFont, font: .title.bold()) - } - - public var title2: SharedFont { - let uiFont = self.font(forTextStyle: .title2) - return SharedFont(uiFont: uiFont, font: .title2) - } - - public var title2B: SharedFont { - let uiFont = self.title2.uiFont.vc_bold - return SharedFont(uiFont: uiFont, font: .title2.bold()) - } - - public var title3: SharedFont { - let uiFont = self.font(forTextStyle: .title3) - return SharedFont(uiFont: uiFont, font: .title3) - } - - public var title3SB: SharedFont { - let uiFont = self.title3.uiFont.vc_semiBold - return SharedFont(uiFont: uiFont, font: .title3.weight(.semibold)) - } - - public var headline: SharedFont { - let uiFont = self.font(forTextStyle: .headline) - return SharedFont(uiFont: uiFont, font: .headline) - } - - public var subheadline: SharedFont { - let uiFont = self.font(forTextStyle: .subheadline) - return SharedFont(uiFont: uiFont, font: .subheadline) - } - - public var body: SharedFont { - let uiFont = self.font(forTextStyle: .body) - return SharedFont(uiFont: uiFont, font: .body) - } - - public var bodySB: SharedFont { - let uiFont = self.body.uiFont.vc_semiBold - return SharedFont(uiFont: uiFont, font: .body.weight(.semibold)) - } - - public var callout: SharedFont { - let uiFont = self.font(forTextStyle: .callout) - return SharedFont(uiFont: uiFont, font: .callout) - } - - public var calloutSB: SharedFont { - let uiFont = self.callout.uiFont.vc_semiBold - return SharedFont(uiFont: uiFont, font: .callout.weight(.semibold)) - } - - public var footnote: SharedFont { - let uiFont = self.font(forTextStyle: .footnote) - return SharedFont(uiFont: uiFont, font: .footnote) - } - - public var footnoteSB: SharedFont { - let uiFont = self.footnote.uiFont.vc_semiBold - return SharedFont(uiFont: uiFont, font: .footnote.weight(.semibold)) - } - - public var caption1: SharedFont { - let uiFont = self.font(forTextStyle: .caption1) - return SharedFont(uiFont: uiFont, font: .caption) - } - - public var caption1SB: SharedFont { - let uiFont = self.caption1.uiFont.vc_semiBold - return SharedFont(uiFont: uiFont, font: .caption.weight(.semibold)) - } - - public var caption2: SharedFont { - let uiFont = self.font(forTextStyle: .caption2) - return SharedFont(uiFont: uiFont, font: .caption2) - } - - public var caption2SB: SharedFont { - let uiFont = self.caption2.uiFont.vc_semiBold - return SharedFont(uiFont: uiFont, font: .caption2.weight(.semibold)) - } -} diff --git a/DesignKit/target.yml b/DesignKit/target.yml deleted file mode 100644 index e10f76f12..000000000 --- a/DesignKit/target.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: DesignKit - -schemes: - DesignKit: - analyze: - config: Debug - archive: - config: Release - build: - targets: - DesignKit: - - running - - profiling - - analyzing - - archiving - profile: - config: Release - run: - config: Debug - disableMainThreadChecker: true - -targets: - DesignKit: - type: framework - platform: iOS - - configFiles: - Debug: Debug.xcconfig - Release: Release.xcconfig - - sources: - - path: . - - path: ../Riot/Categories/UIColor.swift diff --git a/DesignKit/Source/ThemeV2.swift b/Riot/Managers/Theme/ThemeV2.swift similarity index 58% rename from DesignKit/Source/ThemeV2.swift rename to Riot/Managers/Theme/ThemeV2.swift index dedc4d6df..1dff3cf19 100644 --- a/DesignKit/Source/ThemeV2.swift +++ b/Riot/Managers/Theme/ThemeV2.swift @@ -14,29 +14,38 @@ // limitations under the License. // -import Foundation import UIKit +import DesignKit +import DesignTokens /// Theme v2. May be named again as `Theme` when the migration completed. @objc public protocol ThemeV2 { /// Colors object - var colors: ColorsUIKit { get } + var colors: ElementUIColors { get } /// Fonts object - var fonts: FontsUIKit { get } + var fonts: ElementUIFonts { get } /// may contain more design components in future, like icons, audio files etc. } -/// Theme v2 for SwiftUI. -public protocol ThemeSwiftUIType { - - /// Colors object - var colors: ColorSwiftUI { get } - - /// Fonts object - var fonts: FontSwiftUI { get } - - /// may contain more design components in future, like icons, audio files etc. +#warning("Temporary missing colors") +public extension ElementUIColors { + var quarterlyContent: UIColor { quaternaryContent } + var navigation: UIColor { system } + var tile: UIColor { system } + var separator: UIColor { system } + var namesAndAvatars: [UIColor] { + [ + globalAzure, + globalGrape, + globalVerde, + globalPolly, + globalMelon, + globalAqua, + globalPrune, + globalKiwi + ] + } } diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift index 84b0239ce..4facd084c 100644 --- a/Riot/Managers/Theme/Themes/DarkTheme.swift +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -181,9 +181,9 @@ class DarkTheme: NSObject, Theme { button.setTitleColor(self.tintColor, for: .normal) } - /// MARK: - Theme v2 - var colors: ColorsUIKit = DarkColors.uiKit + // MARK: - Theme v2 + var colors = UIColor.element - var fonts: FontsUIKit = FontsUIKit(values: ElementFonts()) + var fonts = UIFont.element } diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift index a07de1e8f..6145b76a3 100644 --- a/Riot/Managers/Theme/Themes/DefaultTheme.swift +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -14,7 +14,6 @@ limitations under the License. */ -import Foundation import UIKit import DesignKit @@ -186,8 +185,8 @@ class DefaultTheme: NSObject, Theme { button.setTitleColor(self.tintColor, for: .normal) } - /// MARK: - Theme v2 - var colors: ColorsUIKit = LightColors.uiKit + // MARK: - Theme v2 + var colors = UIColor.element - var fonts: FontsUIKit = FontsUIKit(values: ElementFonts()) + var fonts = UIFont.element } diff --git a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m index d06b4a577..dce0e8a62 100644 --- a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m +++ b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m @@ -493,7 +493,7 @@ enum { // Customize label style UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view; tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.colors.secondaryContent; - tableViewHeaderFooterView.textLabel.font = ThemeService.shared.theme.fonts.footnote; + tableViewHeaderFooterView.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; } } diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 6d5c7c10b..9f25ba711 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -1297,7 +1297,7 @@ TableViewSectionsDelegate> // Customize label style UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view; tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.colors.secondaryContent; - tableViewHeaderFooterView.textLabel.font = ThemeService.shared.theme.fonts.footnote; + tableViewHeaderFooterView.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; } } diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index b0d5ffc15..e296c9263 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -2634,7 +2634,7 @@ ChangePasswordCoordinatorBridgePresenterDelegate> // Customize label style UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view; tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.colors.secondaryContent; - tableViewHeaderFooterView.textLabel.font = ThemeService.shared.theme.fonts.footnote; + tableViewHeaderFooterView.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; } } diff --git a/Riot/target.yml b/Riot/target.yml index ec016f09a..5a0505ad7 100644 --- a/Riot/target.yml +++ b/Riot/target.yml @@ -34,8 +34,8 @@ targets: - target: RiotShareExtension - target: SiriIntents - target: RiotNSE - - target: DesignKit - target: CommonKit + - package: DesignKit - package: Mapbox - package: OrderedCollections diff --git a/RiotShareExtension/target.yml b/RiotShareExtension/target.yml index bf2a032f5..c50705aa0 100644 --- a/RiotShareExtension/target.yml +++ b/RiotShareExtension/target.yml @@ -30,6 +30,8 @@ targets: RiotShareExtension: platform: iOS type: app-extension + dependencies: + - package: DesignKit configFiles: Debug: Debug.xcconfig diff --git a/DesignKit/Source/AvatarSize.swift b/RiotSwiftUI/Modules/Common/Avatar/Model/AvatarSize.swift similarity index 95% rename from DesignKit/Source/AvatarSize.swift rename to RiotSwiftUI/Modules/Common/Avatar/Model/AvatarSize.swift index bac46e6f3..09daff1d2 100644 --- a/DesignKit/Source/AvatarSize.swift +++ b/RiotSwiftUI/Modules/Common/Avatar/Model/AvatarSize.swift @@ -17,6 +17,8 @@ import Foundation import UIKit +// TODO: Move into element-design-tokens repo. + // Figma Avatar Sizes: https://www.figma.com/file/X4XTH9iS2KGJ2wFKDqkyed/Compound?node-id=1258%3A19678 public enum AvatarSize: Int { case xxSmall = 16 diff --git a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift index f5a15424f..e0ff5bdc1 100644 --- a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift +++ b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift @@ -14,10 +14,43 @@ // limitations under the License. // -import Foundation +import SwiftUI import DesignKit +import DesignTokens protocol ThemeSwiftUI: ThemeSwiftUIType { var identifier: ThemeIdentifier { get } var isDark: Bool { get } } + +/// Theme v2 for SwiftUI. +@available(iOS 14.0, *) +public protocol ThemeSwiftUIType { + + /// Colors object + var colors: ElementColors { get } + + /// Fonts object + var fonts: ElementFonts { get } + + /// may contain more design components in future, like icons, audio files etc. +} + +#warning("Temporary missing colors") +public extension ElementColors { + var quarterlyContent: Color { quaternaryContent } + var navigation: Color { system } + var tile: Color { system } + var namesAndAvatars: [Color] { + [ + globalAzure, + globalGrape, + globalVerde, + globalPolly, + globalMelon, + globalAqua, + globalPrune, + globalKiwi + ] + } +} diff --git a/RiotSwiftUI/Modules/Common/Theme/Themes/DarkThemeSwiftUI.swift b/RiotSwiftUI/Modules/Common/Theme/Themes/DarkThemeSwiftUI.swift index 0e9250070..a572a4694 100644 --- a/RiotSwiftUI/Modules/Common/Theme/Themes/DarkThemeSwiftUI.swift +++ b/RiotSwiftUI/Modules/Common/Theme/Themes/DarkThemeSwiftUI.swift @@ -14,12 +14,12 @@ // limitations under the License. // -import Foundation +import SwiftUI import DesignKit struct DarkThemeSwiftUI: ThemeSwiftUI { var identifier: ThemeIdentifier = .dark let isDark: Bool = true - var colors: ColorSwiftUI = DarkColors.swiftUI - var fonts: FontSwiftUI = FontSwiftUI(values: ElementFonts()) + var colors = Color.element + var fonts = Font.element } diff --git a/RiotSwiftUI/Modules/Common/Theme/Themes/DefaultThemeSwiftUI.swift b/RiotSwiftUI/Modules/Common/Theme/Themes/DefaultThemeSwiftUI.swift index 85ba4d810..bfc2e87c0 100644 --- a/RiotSwiftUI/Modules/Common/Theme/Themes/DefaultThemeSwiftUI.swift +++ b/RiotSwiftUI/Modules/Common/Theme/Themes/DefaultThemeSwiftUI.swift @@ -14,12 +14,12 @@ // limitations under the License. // -import Foundation +import SwiftUI import DesignKit struct DefaultThemeSwiftUI: ThemeSwiftUI { var identifier: ThemeIdentifier = .light let isDark: Bool = false - var colors: ColorSwiftUI = LightColors.swiftUI - var fonts: FontSwiftUI = FontSwiftUI(values: ElementFonts()) + var colors = Color.element + var fonts = Font.element } diff --git a/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift b/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift index 5e20f11b0..c447d7c7b 100644 --- a/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift +++ b/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift @@ -75,7 +75,7 @@ struct MultilineTextField: View { .overlay(rect.stroke(borderColor, lineWidth: borderWidth)) .introspectTextView { textView in textView.textColor = UIColor(textColor) - textView.font = theme.fonts.uiFonts.callout + textView.font = .element.callout } } diff --git a/RiotSwiftUI/target.yml b/RiotSwiftUI/target.yml index 2fc955cb9..3ceb278ab 100644 --- a/RiotSwiftUI/target.yml +++ b/RiotSwiftUI/target.yml @@ -30,7 +30,7 @@ targets: type: application platform: iOS dependencies: - - target: DesignKit + - package: DesignKit - package: Mapbox sources: - path: . diff --git a/project.yml b/project.yml index 84a0cb73f..e06807c8d 100644 --- a/project.yml +++ b/project.yml @@ -32,7 +32,6 @@ include: - path: RiotShareExtension/target.yml - path: SiriIntents/target.yml - path: RiotNSE/target.yml - - path: DesignKit/target.yml - path: RiotSwiftUI/target.yml - path: RiotSwiftUI/targetUnitTests.yml - path: RiotSwiftUI/targetUITests.yml @@ -40,6 +39,9 @@ include: - path: CommonKit/targetUnitTests.yml packages: + DesignKit: + url: https://github.com/vector-im/element-x-ios + branch: doug/designkit Mapbox: url: https://github.com/maplibre/maplibre-gl-native-distribution minVersion: 5.12.2 From 6827d2a85474e2a59acc6f9ed3bd8aff5430e134 Mon Sep 17 00:00:00 2001 From: Doug Date: Fri, 27 May 2022 20:42:44 +0100 Subject: [PATCH 72/97] Fixup missing colours and use resolved colours in UIKit. Fix the confetti colour when using DesignKit. Pin swift packages. Fix UI tests target. --- .gitignore | 8 +- .../xcshareddata/xcschemes/Riot.xcscheme | 2 +- .../xcshareddata/swiftpm/Package.resolved | 50 ++++++++++ .../Theme/ElementUIColorsResolved.swift | 91 +++++++++++++++++++ Riot/Managers/Theme/ThemeV2.swift | 22 +---- Riot/Managers/Theme/Themes/DarkTheme.swift | 2 +- Riot/Managers/Theme/Themes/DefaultTheme.swift | 2 +- .../SwiftUI/VectorHostingController.swift | 3 + .../Common/EffectsScene/EffectsScene.swift | 5 +- .../Common/EffectsScene/EffectsView.swift | 6 ++ .../Modules/Common/Theme/ThemeSwiftUI.swift | 11 ++- .../Common/Util/BorderedInputFieldStyle.swift | 2 +- .../Common/Util/ClearViewModifier.swift | 2 +- .../Common/Util/MultilineTextField.swift | 2 +- .../Modules/Common/Util/OptionButton.swift | 2 +- .../Modules/Common/Util/SearchBar.swift | 6 +- .../Util/SecondaryActionButtonStyle.swift | 2 +- .../Modules/Common/Util/WaitOverlay.swift | 2 +- .../OnboardingSplashScreenPageIndicator.swift | 2 +- .../View/RoomAccessTypeChooserRow.swift | 2 +- ...RestrictedAccessSpaceChooserSelector.swift | 4 +- .../View/TimelinePollAnswerOptionButton.swift | 4 +- .../View/FormInputFieldStyle.swift | 2 +- .../View/MatrixItemChooser.swift | 2 +- .../View/MatrixItemChooserSectionHeader.swift | 2 +- .../SpaceSettings/View/SpaceSettings.swift | 2 +- .../View/SpaceSettingsOptionListItem.swift | 2 +- RiotSwiftUI/targetUITests.yml | 1 + changelog.d/6276.change | 1 + project.yml | 2 +- 30 files changed, 195 insertions(+), 51 deletions(-) create mode 100644 Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 Riot/Managers/Theme/ElementUIColorsResolved.swift create mode 100644 changelog.d/6276.change diff --git a/.gitignore b/.gitignore index 695d4cd61..2530cc0a7 100644 --- a/.gitignore +++ b/.gitignore @@ -30,8 +30,14 @@ vendor/ Pods/ ## Ignore project files as we generate them with xcodegen (https://github.com/yonaskolb/XcodeGen) +# Plus ridiculous workaround to unignore the Package.resolved file for SwiftPM. *.xcodeproj -*.xcworkspace +*.xcworkspace/* +!Riot.xcworkspace/xcshareddata +Riot.xcworkspace/xcshareddata/* +!Riot.xcworkspace/xcshareddata/swiftpm/ +Riot.xcworkspace/xcshareddata/swiftpm/* +!Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved # Fastlane fastlane/report.xml diff --git a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme index a9bea1d96..f973b344c 100644 --- a/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme +++ b/Riot.xcodeproj/xcshareddata/xcschemes/Riot.xcscheme @@ -1,7 +1,7 @@ + version = "1.7"> { } private func update(theme: Theme) { + // Ensure dynamic colors are shown correctly when the theme is the opposite appearance to the system. + overrideUserInterfaceStyle = theme.userInterfaceStyle + if let navigationBar = self.navigationController?.navigationBar { theme.applyStyle(onNavigationBar: navigationBar, withModernScrollEdgeAppearance: enableNavigationBarScrollEdgeAppearance) } diff --git a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift index 67ef9bc7c..38eb5db11 100644 --- a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift +++ b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift @@ -65,9 +65,12 @@ fileprivate extension Color { /// /// SceneKit works in a colorspace with a linear gamma, which is why this conversion is necessary. var floatComponents: [Float]? { + // Get the CGColor from a UIColor as it is nil on Color when loaded from an asset catalog. + let cgColor = UIColor(self).cgColor + guard let colorSpace = CGColorSpace(name: CGColorSpace.extendedLinearSRGB), - let linearColor = cgColor?.converted(to: colorSpace, intent: .defaultIntent, options: nil), + let linearColor = cgColor.converted(to: colorSpace, intent: .defaultIntent, options: nil), let components = linearColor.components else { return nil } diff --git a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsView.swift b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsView.swift index 2422a2ef5..4ab2c5746 100644 --- a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsView.swift +++ b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsView.swift @@ -59,3 +59,9 @@ struct EffectsView: UIViewRepresentable { } } } + +struct EffectsView_Previews: PreviewProvider { + static var previews: some View { + EffectsView(effect: .confetti) + } +} diff --git a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift index e0ff5bdc1..90d9a4852 100644 --- a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift +++ b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift @@ -36,11 +36,14 @@ public protocol ThemeSwiftUIType { /// may contain more design components in future, like icons, audio files etc. } -#warning("Temporary missing colors") public extension ElementColors { - var quarterlyContent: Color { quaternaryContent } - var navigation: Color { system } - var tile: Color { system } + // MARK: - Legacy + var legacyTile: Color { + let dynamicColor = UIColor { $0.userInterfaceStyle == .light ? .elementLight.tile : .elementDark.tile } + return Color(dynamicColor) + } + + // TODO: Generate in DesignTokens repo. var namesAndAvatars: [Color] { [ globalAzure, diff --git a/RiotSwiftUI/Modules/Common/Util/BorderedInputFieldStyle.swift b/RiotSwiftUI/Modules/Common/Util/BorderedInputFieldStyle.swift index fe75aa300..2615efa47 100644 --- a/RiotSwiftUI/Modules/Common/Util/BorderedInputFieldStyle.swift +++ b/RiotSwiftUI/Modules/Common/Util/BorderedInputFieldStyle.swift @@ -50,7 +50,7 @@ struct BorderedInputFieldStyle: TextFieldStyle { if (theme.identifier == ThemeIdentifier.dark) { return (isEnabled ? theme.colors.primaryContent : theme.colors.tertiaryContent) } else { - return (isEnabled ? theme.colors.primaryContent : theme.colors.quarterlyContent) + return (isEnabled ? theme.colors.primaryContent : theme.colors.quaternaryContent) } } diff --git a/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift b/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift index 7eb67d39c..6ab0832ac 100644 --- a/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift +++ b/RiotSwiftUI/Modules/Common/Util/ClearViewModifier.swift @@ -47,7 +47,7 @@ struct ClearViewModifier: ViewModifier { }) { Image(systemName: "xmark.circle.fill") .renderingMode(.template) - .foregroundColor(theme.colors.quarterlyContent) + .foregroundColor(theme.colors.quaternaryContent) } .padding(.top, alignment == .top ? 8 : 0) .padding(.bottom, alignment == .bottom ? 8 : 0) diff --git a/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift b/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift index c447d7c7b..a03c4b21b 100644 --- a/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift +++ b/RiotSwiftUI/Modules/Common/Util/MultilineTextField.swift @@ -56,7 +56,7 @@ struct MultilineTextField: View { return theme.colors.accent } - return theme.colors.quarterlyContent + return theme.colors.quaternaryContent } private var borderWidth: CGFloat { diff --git a/RiotSwiftUI/Modules/Common/Util/OptionButton.swift b/RiotSwiftUI/Modules/Common/Util/OptionButton.swift index 17e54bbda..428ecf09c 100644 --- a/RiotSwiftUI/Modules/Common/Util/OptionButton.swift +++ b/RiotSwiftUI/Modules/Common/Util/OptionButton.swift @@ -55,7 +55,7 @@ struct OptionButton: View { } } Spacer() - Image(systemName: "chevron.right").font(.system(size: 16, weight: .regular)).foregroundColor(theme.colors.quarterlyContent) + Image(systemName: "chevron.right").font(.system(size: 16, weight: .regular)).foregroundColor(theme.colors.quaternaryContent) } .padding(EdgeInsets(top: 15, leading: 16, bottom: 15, trailing: 16)) .background(theme.colors.quinaryContent) diff --git a/RiotSwiftUI/Modules/Common/Util/SearchBar.swift b/RiotSwiftUI/Modules/Common/Util/SearchBar.swift index 4edaa2e5c..3901ea65a 100644 --- a/RiotSwiftUI/Modules/Common/Util/SearchBar.swift +++ b/RiotSwiftUI/Modules/Common/Util/SearchBar.swift @@ -38,7 +38,7 @@ struct SearchBar: View { } .padding(8) .padding(.horizontal, 25) - .background(theme.colors.navigation) + .background(theme.colors.system) .cornerRadius(8) .padding(.leading) .padding(.trailing, isEditing ? 8 : 16) @@ -46,7 +46,7 @@ struct SearchBar: View { HStack { Image(systemName: "magnifyingglass") .renderingMode(.template) - .foregroundColor(theme.colors.quarterlyContent) + .foregroundColor(theme.colors.quaternaryContent) .frame(minWidth: 0, maxWidth: .infinity, alignment: .leading) if isEditing && !text.isEmpty { @@ -55,7 +55,7 @@ struct SearchBar: View { }) { Image(systemName: "multiply.circle.fill") .renderingMode(.template) - .foregroundColor(theme.colors.quarterlyContent) + .foregroundColor(theme.colors.quaternaryContent) } } } diff --git a/RiotSwiftUI/Modules/Common/Util/SecondaryActionButtonStyle.swift b/RiotSwiftUI/Modules/Common/Util/SecondaryActionButtonStyle.swift index 8f0eb6aac..a20eba58e 100644 --- a/RiotSwiftUI/Modules/Common/Util/SecondaryActionButtonStyle.swift +++ b/RiotSwiftUI/Modules/Common/Util/SecondaryActionButtonStyle.swift @@ -68,7 +68,7 @@ struct SecondaryActionButtonStyle_Previews: PreviewProvider { Text("Custom") .foregroundColor(theme.colors.secondaryContent) } - .buttonStyle(SecondaryActionButtonStyle(customColor: theme.colors.quarterlyContent)) + .buttonStyle(SecondaryActionButtonStyle(customColor: theme.colors.quaternaryContent)) } .padding() } diff --git a/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift b/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift index 60f7315bb..4abada5e5 100644 --- a/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift +++ b/RiotSwiftUI/Modules/Common/Util/WaitOverlay.swift @@ -89,7 +89,7 @@ struct WaitOverlay: ViewModifier { } .padding(12) .background(RoundedRectangle(cornerRadius: 8, style: .continuous) - .fill(theme.colors.navigation.opacity(0.9))) + .fill(theme.colors.system.opacity(0.9))) } .edgesIgnoringSafeArea(.all) .transition(.opacity) diff --git a/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPageIndicator.swift b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPageIndicator.swift index 63e8507a0..f2306eaf4 100644 --- a/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPageIndicator.swift +++ b/RiotSwiftUI/Modules/Onboarding/SplashScreen/View/OnboardingSplashScreenPageIndicator.swift @@ -47,7 +47,7 @@ struct OnboardingSplashScreenPageIndicator: View { ForEach(0.. Date: Fri, 1 Jul 2022 14:36:04 +0100 Subject: [PATCH 73/97] Pin DesignKit version and use contantAndAvatars array. --- .../xcshareddata/swiftpm/Package.resolved | 8 ++++---- .../Theme/ElementUIColorsResolved.swift | 11 +---------- .../Common/Avatar/View/AvatarImage.swift | 2 +- .../Avatar/View/PlaceholderAvatarImage.swift | 2 +- .../Common/Avatar/View/SpaceAvatarImage.swift | 6 +++--- .../Common/EffectsScene/EffectsScene.swift | 2 +- .../Modules/Common/Theme/ThemeSwiftUI.swift | 17 ++--------------- .../Theme/ThemeUsersColorsExtension.swift | 4 ++-- .../OnboardingAvatarCoordinator.swift | 2 +- .../MockOnboardingAvatarScreenState.swift | 2 +- changelog.d/pr-6275.api | 1 + project.yml | 2 +- 12 files changed, 19 insertions(+), 40 deletions(-) create mode 100644 changelog.d/pr-6275.api diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index d586862d6..7cce2fd2b 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -5,8 +5,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vector-im/element-design-tokens.git", "state" : { - "revision" : "4aafdc25ca0e322c0de930d4ec86121f5503023e", - "version" : "0.0.1" + "revision" : "02ba42d9ec02f90370a6cfc35a68d7312696636c", + "version" : "0.0.2" } }, { @@ -14,8 +14,8 @@ "kind" : "remoteSourceControl", "location" : "https://github.com/vector-im/element-x-ios", "state" : { - "branch" : "develop", - "revision" : "272fc5000bfe6e15a0f0ea669ef3088c7d163ce7" + "revision" : "0a199ee61126feb8c8a462200cb4749d6eb3ba77", + "version" : "1.0.1-202207011447" } }, { diff --git a/Riot/Managers/Theme/ElementUIColorsResolved.swift b/Riot/Managers/Theme/ElementUIColorsResolved.swift index 65c6531a8..20118bfd3 100644 --- a/Riot/Managers/Theme/ElementUIColorsResolved.swift +++ b/Riot/Managers/Theme/ElementUIColorsResolved.swift @@ -63,16 +63,7 @@ extension UIColor { self.system = dynamicColors.system.resolvedColor(with: traitCollection) self.background = dynamicColors.background.resolvedColor(with: traitCollection) - self.namesAndAvatars = [ - dynamicColors.globalAzure.resolvedColor(with: traitCollection), - dynamicColors.globalGrape.resolvedColor(with: traitCollection), - dynamicColors.globalVerde.resolvedColor(with: traitCollection), - dynamicColors.globalPolly.resolvedColor(with: traitCollection), - dynamicColors.globalMelon.resolvedColor(with: traitCollection), - dynamicColors.globalAqua.resolvedColor(with: traitCollection), - dynamicColors.globalPrune.resolvedColor(with: traitCollection), - dynamicColors.globalKiwi.resolvedColor(with: traitCollection) - ] + self.namesAndAvatars = dynamicColors.contentAndAvatars // Legacy colours self.quarterlyContent = dynamicColors.quaternaryContent.resolvedColor(with: traitCollection) diff --git a/RiotSwiftUI/Modules/Common/Avatar/View/AvatarImage.swift b/RiotSwiftUI/Modules/Common/Avatar/View/AvatarImage.swift index 2b7fa9e60..55904b5d1 100644 --- a/RiotSwiftUI/Modules/Common/Avatar/View/AvatarImage.swift +++ b/RiotSwiftUI/Modules/Common/Avatar/View/AvatarImage.swift @@ -49,7 +49,7 @@ struct AvatarImage: View { mxContentUri: mxContentUri, matrixItemId: matrixItemId, displayName: displayName, - colorCount: theme.colors.namesAndAvatars.count, + colorCount: theme.colors.contentAndAvatars.count, avatarSize: size ) } diff --git a/RiotSwiftUI/Modules/Common/Avatar/View/PlaceholderAvatarImage.swift b/RiotSwiftUI/Modules/Common/Avatar/View/PlaceholderAvatarImage.swift index f119a7e14..7dbc2ba4f 100644 --- a/RiotSwiftUI/Modules/Common/Avatar/View/PlaceholderAvatarImage.swift +++ b/RiotSwiftUI/Modules/Common/Avatar/View/PlaceholderAvatarImage.swift @@ -36,7 +36,7 @@ struct PlaceholderAvatarImage: View { var body: some View { ZStack { - theme.colors.namesAndAvatars[colorIndex] + theme.colors.contentAndAvatars[colorIndex] Text(String(firstCharacter)) .padding(4) diff --git a/RiotSwiftUI/Modules/Common/Avatar/View/SpaceAvatarImage.swift b/RiotSwiftUI/Modules/Common/Avatar/View/SpaceAvatarImage.swift index d82e2107f..31e734c58 100644 --- a/RiotSwiftUI/Modules/Common/Avatar/View/SpaceAvatarImage.swift +++ b/RiotSwiftUI/Modules/Common/Avatar/View/SpaceAvatarImage.swift @@ -38,7 +38,7 @@ struct SpaceAvatarImage: View { .padding(10) .frame(width: CGFloat(size.rawValue), height: CGFloat(size.rawValue)) .foregroundColor(.white) - .background(theme.colors.namesAndAvatars[colorIndex]) + .background(theme.colors.contentAndAvatars[colorIndex]) .clipShape(RoundedRectangle(cornerRadius: 8)) // Make the text resizable (i.e. Make it large and then allow it to scale down) .font(.system(size: 200)) @@ -55,7 +55,7 @@ struct SpaceAvatarImage: View { mxContentUri: mxContentUri, matrixItemId: matrixItemId, displayName: value, - colorCount: theme.colors.namesAndAvatars.count, + colorCount: theme.colors.contentAndAvatars.count, avatarSize: size ) }) @@ -65,7 +65,7 @@ struct SpaceAvatarImage: View { mxContentUri: mxContentUri, matrixItemId: matrixItemId, displayName: displayName, - colorCount: theme.colors.namesAndAvatars.count, + colorCount: theme.colors.contentAndAvatars.count, avatarSize: size ) } diff --git a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift index 38eb5db11..daf298461 100644 --- a/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift +++ b/RiotSwiftUI/Modules/Common/EffectsScene/EffectsScene.swift @@ -31,7 +31,7 @@ class EffectsScene: SCNScene { static func confetti(with theme: ThemeSwiftUI) -> EffectsScene? { guard let scene = EffectsScene(named: Constants.confettiSceneName) else { return nil } - let colors: [[Float]] = theme.colors.namesAndAvatars.compactMap { $0.floatComponents } + let colors: [[Float]] = theme.colors.contentAndAvatars.compactMap { $0.floatComponents } if let particles = scene.rootNode.childNode(withName: Constants.particlesNodeName, recursively: false)?.particleSystems?.first { // The particles need a non-zero color variation for the handler to affect the color diff --git a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift index 90d9a4852..7e8fe5308 100644 --- a/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift +++ b/RiotSwiftUI/Modules/Common/Theme/ThemeSwiftUI.swift @@ -36,24 +36,11 @@ public protocol ThemeSwiftUIType { /// may contain more design components in future, like icons, audio files etc. } +// MARK: - Legacy Colors + public extension ElementColors { - // MARK: - Legacy var legacyTile: Color { let dynamicColor = UIColor { $0.userInterfaceStyle == .light ? .elementLight.tile : .elementDark.tile } return Color(dynamicColor) } - - // TODO: Generate in DesignTokens repo. - var namesAndAvatars: [Color] { - [ - globalAzure, - globalGrape, - globalVerde, - globalPolly, - globalMelon, - globalAqua, - globalPrune, - globalKiwi - ] - } } diff --git a/RiotSwiftUI/Modules/Common/Theme/ThemeUsersColorsExtension.swift b/RiotSwiftUI/Modules/Common/Theme/ThemeUsersColorsExtension.swift index ad1eeb222..6c3e3c2e2 100644 --- a/RiotSwiftUI/Modules/Common/Theme/ThemeUsersColorsExtension.swift +++ b/RiotSwiftUI/Modules/Common/Theme/ThemeUsersColorsExtension.swift @@ -23,7 +23,7 @@ extension ThemeSwiftUI { /// - Parameter userId: The user id used to hash. /// - Returns: The SwiftUI color for the associated userId. func userColor(for userId: String) -> Color { - let senderNameColorIndex = Int(userId.vc_hashCode % Int32(colors.namesAndAvatars.count)) - return colors.namesAndAvatars[senderNameColorIndex] + let senderNameColorIndex = Int(userId.vc_hashCode % Int32(colors.contentAndAvatars.count)) + return colors.contentAndAvatars[senderNameColorIndex] } } diff --git a/RiotSwiftUI/Modules/Onboarding/Avatar/Coordinator/OnboardingAvatarCoordinator.swift b/RiotSwiftUI/Modules/Onboarding/Avatar/Coordinator/OnboardingAvatarCoordinator.swift index 216a65ea4..5884e363e 100644 --- a/RiotSwiftUI/Modules/Onboarding/Avatar/Coordinator/OnboardingAvatarCoordinator.swift +++ b/RiotSwiftUI/Modules/Onboarding/Avatar/Coordinator/OnboardingAvatarCoordinator.swift @@ -72,7 +72,7 @@ final class OnboardingAvatarCoordinator: Coordinator, Presentable { self.parameters = parameters let viewModel = OnboardingAvatarViewModel(userId: parameters.userSession.userId, displayName: parameters.userSession.account.userDisplayName, - avatarColorCount: DefaultThemeSwiftUI().colors.namesAndAvatars.count) + avatarColorCount: DefaultThemeSwiftUI().colors.contentAndAvatars.count) viewModel.updateAvatarImage(with: parameters.avatar) let view = OnboardingAvatarScreen(viewModel: viewModel.context) diff --git a/RiotSwiftUI/Modules/Onboarding/Avatar/MockOnboardingAvatarScreenState.swift b/RiotSwiftUI/Modules/Onboarding/Avatar/MockOnboardingAvatarScreenState.swift index 7cf96984a..8982961f6 100644 --- a/RiotSwiftUI/Modules/Onboarding/Avatar/MockOnboardingAvatarScreenState.swift +++ b/RiotSwiftUI/Modules/Onboarding/Avatar/MockOnboardingAvatarScreenState.swift @@ -44,7 +44,7 @@ enum MockOnboardingAvatarScreenState: MockScreenState, CaseIterable { /// Generate the view struct for the screen state. var screenView: ([Any], AnyView) { - let avatarColorCount = DefaultThemeSwiftUI().colors.namesAndAvatars.count + let avatarColorCount = DefaultThemeSwiftUI().colors.contentAndAvatars.count let viewModel: OnboardingAvatarViewModel switch self { case .placeholderAvatar(let userId, let displayName): diff --git a/changelog.d/pr-6275.api b/changelog.d/pr-6275.api new file mode 100644 index 000000000..a4e2fb491 --- /dev/null +++ b/changelog.d/pr-6275.api @@ -0,0 +1 @@ +Replace DesignKit framework with [DesignKit package](https://github.com/vector-im/element-x-ios/tree/develop/DesignKit/Sources). Colours are now generated in the [DesignTokens repo](https://github.com/vector-im/element-design-tokens) to be shared across all of our apps. diff --git a/project.yml b/project.yml index bc215b2c9..fb4a0d0ba 100644 --- a/project.yml +++ b/project.yml @@ -41,7 +41,7 @@ include: packages: DesignKit: url: https://github.com/vector-im/element-x-ios - branch: develop + exactVersion: 1.0.1-202207011447 Mapbox: url: https://github.com/maplibre/maplibre-gl-native-distribution minVersion: 5.12.2 From 0a9bd1fbe60f8a54d36ca95e0272331a252594f6 Mon Sep 17 00:00:00 2001 From: Doug Date: Fri, 1 Jul 2022 15:14:15 +0100 Subject: [PATCH 74/97] Fix SwiftUI Unit tests. --- .../Avatar/Test/Unit/OnboardingAvatarViewModelTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Onboarding/Avatar/Test/Unit/OnboardingAvatarViewModelTests.swift b/RiotSwiftUI/Modules/Onboarding/Avatar/Test/Unit/OnboardingAvatarViewModelTests.swift index fd0f284d3..0fe73b772 100644 --- a/RiotSwiftUI/Modules/Onboarding/Avatar/Test/Unit/OnboardingAvatarViewModelTests.swift +++ b/RiotSwiftUI/Modules/Onboarding/Avatar/Test/Unit/OnboardingAvatarViewModelTests.swift @@ -23,7 +23,7 @@ class OnboardingAvatarViewModelTests: XCTestCase { private enum Constants { static let userId = "@user:matrix.org" static let displayName = "Alice" - static let avatarColorCount = DefaultThemeSwiftUI().colors.namesAndAvatars.count + static let avatarColorCount = DefaultThemeSwiftUI().colors.contentAndAvatars.count static let avatarImage = Asset.Images.appSymbol.image } From 3e94baa757ddb4d3579f52e89065f9a786639164 Mon Sep 17 00:00:00 2001 From: Nikita Epifanov Date: Wed, 6 Jul 2022 20:28:51 +0000 Subject: [PATCH 75/97] Translated using Weblate (Russian) 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/ru/ --- Riot/Assets/ru.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/ru.lproj/InfoPlist.strings b/Riot/Assets/ru.lproj/InfoPlist.strings index 19e148ef6..c3bb844cb 100644 --- a/Riot/Assets/ru.lproj/InfoPlist.strings +++ b/Riot/Assets/ru.lproj/InfoPlist.strings @@ -6,3 +6,4 @@ "NSCalendarsUsageDescription" = "Просматривайте запланированные встречи в приложении."; "NSFaceIDUsageDescription" = "Face ID используется для доступа к вашему приложению."; "NSLocationWhenInUseUsageDescription" = "Когда вы делитесь с людьми своим местоположением, Element необходим доступ, чтобы показать им карту."; +"NSLocationAlwaysAndWhenInUseUsageDescription" = "Когда вы сообщаете людям свое местоположение, Element будет необходим доступ, чтобы показать им карту."; From 59e0d4a0a91912770176ffb35cb5107c48cac499 Mon Sep 17 00:00:00 2001 From: Doug Date: Fri, 8 Jul 2022 12:37:49 +0100 Subject: [PATCH 76/97] Revert some fonts that were changed in #6275 --- .../Security/ManageSession/ManageSessionViewController.m | 3 ++- Riot/Modules/Settings/Security/SecurityViewController.m | 4 +++- Riot/Modules/Settings/SettingsViewController.m | 4 +++- changelog.d/6392.misc | 1 + 4 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 changelog.d/6392.misc diff --git a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m index dce0e8a62..991f4544a 100644 --- a/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m +++ b/Riot/Modules/Settings/Security/ManageSession/ManageSessionViewController.m @@ -24,6 +24,7 @@ #import "GeneratedInterface-Swift.h" +@import DesignKit; enum { @@ -493,7 +494,7 @@ enum { // Customize label style UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view; tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.colors.secondaryContent; - tableViewHeaderFooterView.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; + tableViewHeaderFooterView.textLabel.font = ThemeService.shared.theme.fonts.footnote; } } diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 9f25ba711..3656218a2 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -26,6 +26,8 @@ #import "GeneratedInterface-Swift.h" +@import DesignKit; + // Dev flag to have more options //#define CROSS_SIGNING_AND_BACKUP_DEV @@ -1297,7 +1299,7 @@ TableViewSectionsDelegate> // Customize label style UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view; tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.colors.secondaryContent; - tableViewHeaderFooterView.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; + tableViewHeaderFooterView.textLabel.font = ThemeService.shared.theme.fonts.footnote; } } diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index e296c9263..b26daf997 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -45,6 +45,8 @@ #import "GeneratedInterface-Swift.h" +@import DesignKit; + NSString* const kSettingsViewControllerPhoneBookCountryCellId = @"kSettingsViewControllerPhoneBookCountryCellId"; typedef NS_ENUM(NSUInteger, SECTION_TAG) @@ -2634,7 +2636,7 @@ ChangePasswordCoordinatorBridgePresenterDelegate> // Customize label style UITableViewHeaderFooterView *tableViewHeaderFooterView = (UITableViewHeaderFooterView*)view; tableViewHeaderFooterView.textLabel.textColor = ThemeService.shared.theme.colors.secondaryContent; - tableViewHeaderFooterView.textLabel.font = [UIFont preferredFontForTextStyle:UIFontTextStyleFootnote]; + tableViewHeaderFooterView.textLabel.font = ThemeService.shared.theme.fonts.footnote; } } diff --git a/changelog.d/6392.misc b/changelog.d/6392.misc new file mode 100644 index 000000000..4237bba17 --- /dev/null +++ b/changelog.d/6392.misc @@ -0,0 +1 @@ +Revert some font changes made when merging #6392. From cd4fde605742220998a03700494cc61649d5745c Mon Sep 17 00:00:00 2001 From: Johan Smits Date: Sat, 9 Jul 2022 08:22:08 +0000 Subject: [PATCH 77/97] Translated using Weblate (Dutch) Currently translated at 100.0% (2059 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/nl/ --- Riot/Assets/nl.lproj/Vector.strings | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index 19c900719..7fc62a33f 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -2491,3 +2491,21 @@ /* The %@ placeholder will be replaced with the integration manager's URL. */ "settings_integrations_allow_description" = "Gebruik een integratiebeheerder (%@) om bots, bruggen, widgets en stickerpakketten te beheren.\n\nIntegratiebeheerders ontvangen configuratiedata en kunnen widgets aanpassen, kameruitnodigingen versturen en bestuursniveaus instellen namens u."; "settings_ui_show_redactions_in_room_history" = "Toon een aanduiding voor verwijderde berichten"; + +// MARK: Reactions + +"room_event_action_reaction_more" = "%@ meer"; +"leave_space_selection_no_rooms" = "Selecteer geen kamers"; +"leave_space_selection_all_rooms" = "Selecteer alle kamers"; +"leave_space_selection_title" = "KAMERS KIEZEN"; +"leave_space_and_more_rooms" = "Verlaat space en %@ kamers"; +"leave_space_and_one_room" = "Verlaat space en 1 kamer"; + +// Mark: Leave space + +"leave_space_action" = "Verlaat space"; +"spaces_feature_not_available" = "Deze functie is hier niet beschikbaar. Voor nu kunt u dit doen met %@ op uw computer."; +"home_context_menu_mark_as_read" = "Markeer als gelezen"; +"settings_timeline" = "TIJDLIJN"; +"room_accessibility_record_voice_message_hint" = "Dubbeltik en houd vast om op te nemen."; +"room_accessibility_record_voice_message" = "Spraakbericht opnemen"; From 440e9b973161f2af8e4a8f2d2b537d3837ff2fd5 Mon Sep 17 00:00:00 2001 From: LinAGKar Date: Sat, 9 Jul 2022 19:32:14 +0000 Subject: [PATCH 78/97] Translated using Weblate (Swedish) Currently translated at 100.0% (2059 of 2059 strings) Translation: Element iOS/Element iOS Translate-URL: https://translate.element.io/projects/riot-ios/riot-ios/sv/ --- Riot/Assets/sv.lproj/Vector.strings | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/sv.lproj/Vector.strings b/Riot/Assets/sv.lproj/Vector.strings index 2fa6e75c8..1847abd92 100644 --- a/Riot/Assets/sv.lproj/Vector.strings +++ b/Riot/Assets/sv.lproj/Vector.strings @@ -294,7 +294,7 @@ "settings_add_email_address" = "Lägg till e-postadress"; "settings_phone_number" = "Telefon"; "settings_add_phone_number" = "Lägg till telefonnummer"; -"settings_change_password" = "Byt Matrixkontolösenord"; +"settings_change_password" = "Byt lösenord"; "settings_night_mode" = "Nattläge"; "settings_fail_to_update_profile" = "Misslyckades att uppdatera profil"; "settings_three_pids_management_information_part1" = "Hantera vilka e-postadresser eller telefonnummer som du kan använda för att logga in eller återfå ditt konto här. Kontrollera vilka som kan hitta dig i "; @@ -325,9 +325,9 @@ "settings_privacy_policy" = "Integritetspolicy"; "settings_send_crash_report" = "Skicka anonyma krasch- och användningsdata"; "settings_enable_rageshake" = "Raseriskaka för att rapportera bugg"; -"settings_old_password" = "gammalt lösenord"; -"settings_new_password" = "nytt lösenord"; -"settings_confirm_password" = "bekräfta lösenord"; +"settings_old_password" = "Gammalt lösenord"; +"settings_new_password" = "Nytt lösenord"; +"settings_confirm_password" = "Bekräfta lösenord"; "settings_fail_to_update_password" = "Misslyckades att uppdatera Matrixkontolösenord"; "settings_password_updated" = "Ditt Matrixkontolösenord har uppdaterats"; "settings_add_3pid_password_title_email" = "Lägg till e-postadress"; @@ -2267,3 +2267,22 @@ "location_sharing_allow_background_location_message" = "Om du vill dela din realtidsplats så behöver Element platsåtkomst när appen är i bakgrunden. För att aktivera åtkomst, gå till Inställningar > Plats och välj Alltid"; "location_sharing_allow_background_location_title" = "Tillåt åtkomst"; "settings_labs_enable_live_location_sharing" = "Platsdelning i realtid - dela nuvarande plats (aktiv utveckling, och för tillfället ligger platser kvar i rumshistoriken)"; + +// MARK: Reactions + +"room_event_action_reaction_more" = "%@ till"; +"leave_space_selection_no_rooms" = "Välj inga rum"; +"leave_space_selection_all_rooms" = "Välj alla rum"; +"leave_space_selection_title" = "VÄLJ RUM"; +"leave_space_and_more_rooms" = "Lämna utrymme och %@ rum"; +"leave_space_and_one_room" = "Lämna utrymme och 1 rum"; + +// Mark: Leave space + +"leave_space_action" = "Lämna utrymme"; +"spaces_feature_not_available" = "Den här funktionen är inte tillgänglig här. För tillfället kan du göra det med %@ på din dator."; +"home_context_menu_mark_as_read" = "Markera som läst"; +"settings_ui_show_redactions_in_room_history" = "Visa en platshållare för borttagna meddelanden"; +"settings_timeline" = "TIDSLINJE"; +"room_accessibility_record_voice_message_hint" = "Dubbeltryck och håll för att spela in."; +"room_accessibility_record_voice_message" = "Spela in röstmeddelande"; From 421bcbeb89454f2e29e33caf98f922bdd74e3062 Mon Sep 17 00:00:00 2001 From: Andy Uhnak Date: Mon, 11 Jul 2022 10:37:43 +0100 Subject: [PATCH 79/97] Log sentry events as debug instead of error --- Riot/Modules/Analytics/SentryMonitoringClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Analytics/SentryMonitoringClient.swift b/Riot/Modules/Analytics/SentryMonitoringClient.swift index 286c0fb43..32b2169f2 100644 --- a/Riot/Modules/Analytics/SentryMonitoringClient.swift +++ b/Riot/Modules/Analytics/SentryMonitoringClient.swift @@ -35,7 +35,7 @@ struct SentryMonitoringClient { options.tracesSampleRate = 1.0 options.beforeSend = { event in - MXLog.error("[SentryMonitoringClient] Issue detected: \(event)") + MXLog.debug("[SentryMonitoringClient] Issue detected: \(event)") return event } From fa338ad3b3ef033ad68b0c3cd087f8bfc6254541 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 11 Jul 2022 14:39:43 +0200 Subject: [PATCH 80/97] Map credits: Add credits title string. --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index d739a4cd8..d3216796b 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -2166,6 +2166,7 @@ Tap the + to start adding people."; To enable access, tap Settings> Location and select Always"; "location_sharing_allow_background_location_validate_action" = "Settings"; "location_sharing_allow_background_location_cancel_action" = "Not now"; +"location_sharing_map_credits_title" = "© Copyright"; // MARK: Live location sharing diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 17933d4ab..48ef09ad7 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2907,6 +2907,10 @@ public class VectorL10n: NSObject { public static func locationSharingLocatingUserErrorTitle(_ p1: String) -> String { return VectorL10n.tr("Vector", "location_sharing_locating_user_error_title", p1) } + /// © Copyright + public static var locationSharingMapCreditsTitle: String { + return VectorL10n.tr("Vector", "location_sharing_map_credits_title") + } /// Open in Apple Maps public static var locationSharingOpenAppleMaps: String { return VectorL10n.tr("Vector", "location_sharing_open_apple_maps") From 994ec1afcbbe155797dba2beacc03c00cadc5c99 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 11 Jul 2022 14:41:58 +0200 Subject: [PATCH 81/97] Add MapCreditsActionSheet. --- .../MapCreditsActionSheet.swift | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 RiotSwiftUI/Modules/Room/LocationSharing/MapCreditsActionSheet.swift diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/MapCreditsActionSheet.swift b/RiotSwiftUI/Modules/Room/LocationSharing/MapCreditsActionSheet.swift new file mode 100644 index 000000000..3f08da5ac --- /dev/null +++ b/RiotSwiftUI/Modules/Room/LocationSharing/MapCreditsActionSheet.swift @@ -0,0 +1,37 @@ +// +// Copyright 2022 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import SwiftUI + +struct MapCreditsActionSheet { + + // Open URL action + let openURL: (URL) -> Void + + // Map credits action sheet + var sheet: ActionSheet { + ActionSheet(title: Text(VectorL10n.locationSharingMapCreditsTitle), + buttons: [ + .default(Text("© MapTiler")) { + openURL(URL(string: "https://www.maptiler.com/copyright/")!) + }, + .default(Text("© OpenStreetMap")) { + openURL(URL(string: "https://www.openstreetmap.org/copyright")!) + }, + .cancel() + ]) + } +} From edef19f99fd6656f2f75e2c3faaad13cf25e806c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 11 Jul 2022 14:42:55 +0200 Subject: [PATCH 82/97] MapCreditsView: Update view and add tap action closure. --- .../Room/LocationSharing/View/MapCreditsView.swift | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/View/MapCreditsView.swift b/RiotSwiftUI/Modules/Room/LocationSharing/View/MapCreditsView.swift index da8509918..3edb2fbfc 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/View/MapCreditsView.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/View/MapCreditsView.swift @@ -26,12 +26,20 @@ struct MapCreditsView: View { // MARK: Public + var action: (() -> Void)? + var body: some View { HStack { - Link("© MapTiler", destination: URL(string: "https://www.maptiler.com/copyright/")!) - Link("© OpenStreetMap contributors", destination: URL(string: "https://www.openstreetmap.org/copyright")!) + Spacer() + Button { + action?() + } label: { + Text(VectorL10n.locationSharingMapCreditsTitle) + .font(theme.fonts.footnote) + .foregroundColor(theme.colors.accent) + } + .padding(.horizontal) } - .font(theme.fonts.caption1) } } From 4303b6b58b137161d9f2923e5f6d9ece74ae259f Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 11 Jul 2022 14:43:40 +0200 Subject: [PATCH 83/97] LocationSharingView: Handle map credits action sheet. --- .../Room/LocationSharing/LocationSharingModels.swift | 2 ++ .../LocationSharing/LocationSharingViewModel.swift | 2 ++ .../LocationSharing/View/LocationSharingView.swift | 12 +++++++++++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingModels.swift b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingModels.swift index 82445ac5e..46d694fcf 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingModels.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingModels.swift @@ -40,6 +40,7 @@ enum LocationSharingViewAction { case startLiveSharing case shareLiveLocation(timeout: LiveLocationSharingTimeout) case userDidPan + case mapCreditsDidTap } enum LocationSharingViewModelResult { @@ -95,6 +96,7 @@ struct LocationSharingViewStateBindings { var userLocation: CLLocationCoordinate2D? var pinLocation: CLLocationCoordinate2D? var showingTimerSelector = false + var showMapCreditsSheet = false } enum LocationSharingAlertType { diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift index 6261f9e6b..1fc7242c3 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/LocationSharingViewModel.swift @@ -86,6 +86,8 @@ class LocationSharingViewModel: LocationSharingViewModelType, LocationSharingVie case .userDidPan: state.showsUserLocation = false state.isPinDropSharing = true + case .mapCreditsDidTap: + state.bindings.showMapCreditsSheet.toggle() } } diff --git a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift index 70d31f6da..c5e3c4b29 100644 --- a/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift +++ b/RiotSwiftUI/Modules/Room/LocationSharing/View/LocationSharingView.swift @@ -25,6 +25,8 @@ struct LocationSharingView: View { @Environment(\.theme) private var theme: ThemeSwiftUI + @Environment(\.openURL) var openURL + // MARK: Public @ObservedObject var context: LocationSharingViewModel.Context @@ -34,7 +36,15 @@ struct LocationSharingView: View { ZStack(alignment: .bottom) { mapView VStack(spacing: 0) { - MapCreditsView() + MapCreditsView(action: { + context.send(viewAction: .mapCreditsDidTap) + }) + .padding(.bottom, 10.0) + .actionSheet(isPresented: $context.showMapCreditsSheet) { + return MapCreditsActionSheet(openURL: { url in + openURL(url) + }).sheet + } buttonsView .background(theme.colors.background) .clipShape(RoundedCornerShape(radius: 8, corners: [.topLeft, .topRight])) From 3b748d7630ae3ef061b773f8c4462fb263f3448d Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 11 Jul 2022 14:44:30 +0200 Subject: [PATCH 84/97] LiveLocationSharingViewer: Handle map credits action sheet. --- .../LiveLocationSharingViewerModels.swift | 2 ++ .../LiveLocationSharingViewerViewModel.swift | 2 ++ .../View/LiveLocationSharingViewer.swift | 18 +++++++++++++++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerModels.swift b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerModels.swift index 7d5a38ab4..9cb66ad7d 100644 --- a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerModels.swift +++ b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerModels.swift @@ -56,6 +56,7 @@ struct LiveLocationSharingViewerViewState: BindableState { struct LiveLocationSharingViewerViewStateBindings { var alertInfo: AlertInfo? + var showMapCreditsSheet = false } enum LiveLocationSharingViewerViewAction { @@ -63,4 +64,5 @@ enum LiveLocationSharingViewerViewAction { case stopSharing case tapListItem(_ userId: String) case share(_ annotation: UserLocationAnnotation) + case mapCreditsDidTap } diff --git a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerViewModel.swift b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerViewModel.swift index 3a365b627..8da097b41 100644 --- a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerViewModel.swift +++ b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/LiveLocationSharingViewerViewModel.swift @@ -69,6 +69,8 @@ class LiveLocationSharingViewerViewModel: LiveLocationSharingViewerViewModelType self.highlighAnnotation(with: userId) case .share(let userLocationAnnotation): completion?(.share(userLocationAnnotation.coordinate)) + case .mapCreditsDidTap: + state.bindings.showMapCreditsSheet.toggle() } } diff --git a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift index 25edbdd82..d468edf2f 100644 --- a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift +++ b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift @@ -25,9 +25,13 @@ struct LiveLocationSharingViewer: View { @Environment(\.theme) private var theme: ThemeSwiftUI + @Environment(\.openURL) var openURL + var isBottomSheetVisible = true @State private var isBottomSheetExpanded = false + var bottomSheetCollapsedHeight: CGFloat = 150.0 + // MARK: Public @ObservedObject var viewModel: LiveLocationSharingViewerViewModel.Context @@ -50,9 +54,12 @@ struct LiveLocationSharingViewer: View { errorSubject: viewModel.viewState.errorSubject) VStack(alignment: .center) { Spacer() - MapCreditsView() - .offset(y: -130) + MapCreditsView(action: { + viewModel.send(viewAction: .mapCreditsDidTap) + }) + .offset(y: -(bottomSheetCollapsedHeight + 10)) } + .ignoresSafeArea() } .navigationTitle(VectorL10n.locationSharingLiveViewerTitle) .toolbar { @@ -64,6 +71,11 @@ struct LiveLocationSharingViewer: View { } .accentColor(theme.colors.accent) .bottomSheet(sheet, if: isBottomSheetVisible) + .actionSheet(isPresented: $viewModel.showMapCreditsSheet) { + return MapCreditsActionSheet(openURL: { url in + openURL(url) + }).sheet + } .alert(item: $viewModel.alertInfo) { info in info.alert } @@ -108,7 +120,7 @@ extension LiveLocationSharingViewer { var sheet: some BottomSheetView { BottomSheet( isExpanded: $isBottomSheetExpanded, - minHeight: .points(150), + minHeight: .points(bottomSheetCollapsedHeight), maxHeight: .available, style: sheetStyle) { userLocationList From ba384d85f8792fa738d5573fb8ae8bbf1eb4c3f3 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 11 Jul 2022 14:48:56 +0200 Subject: [PATCH 85/97] Update changes --- changelog.d/6108.change | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/6108.change diff --git a/changelog.d/6108.change b/changelog.d/6108.change new file mode 100644 index 000000000..ba3e87f10 --- /dev/null +++ b/changelog.d/6108.change @@ -0,0 +1 @@ +Location sharing: Update map credits display and behavior. From ddbfd39bd627d1db99983eaf2ad78e99b3b9e2d7 Mon Sep 17 00:00:00 2001 From: aringenbach Date: Mon, 11 Jul 2022 16:40:43 +0200 Subject: [PATCH 86/97] Fix inoperant room links with alias/identifiers --- Riot/Utils/UniversalLink.m | 8 ++++++++ changelog.d/6395.bugfix | 1 + 2 files changed, 9 insertions(+) create mode 100644 changelog.d/6395.bugfix diff --git a/Riot/Utils/UniversalLink.m b/Riot/Utils/UniversalLink.m index edd73c7b8..2f1a97936 100644 --- a/Riot/Utils/UniversalLink.m +++ b/Riot/Utils/UniversalLink.m @@ -16,6 +16,7 @@ #import "UniversalLink.h" #import "NSArray+Element.h" +#import "MXTools.h" @implementation UniversalLink @@ -51,6 +52,13 @@ // Remove the first empty path param string pathParams = [pathParams filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"length > 0"]]; + // Handle room links with aliases/identifiers + if ([MXTools isMatrixRoomAlias:[_url.absoluteString stringByRemovingPercentEncoding]] + || [MXTools isMatrixRoomIdentifier:[_url.absoluteString stringByRemovingPercentEncoding]]) + { + pathParams = @[_url.absoluteString]; + } + // URL decode each path param pathParams = [pathParams vc_map:^id _Nonnull(NSString * _Nonnull item) { return [item stringByRemovingPercentEncoding]; diff --git a/changelog.d/6395.bugfix b/changelog.d/6395.bugfix new file mode 100644 index 000000000..0bafa6d70 --- /dev/null +++ b/changelog.d/6395.bugfix @@ -0,0 +1 @@ +Fix inoperant room links with alias/identifiers From bd9952c3505bb36733fde9214c63971530b5524f Mon Sep 17 00:00:00 2001 From: wtimme Date: Mon, 11 Jul 2022 17:49:06 +0200 Subject: [PATCH 87/97] Run `bundle exec pod install --repo-update` (#6387) This change was introduced automatically by CocoaPods, likely because someone ran `pod install` using their locally installed version of CocoaPods. --- Podfile.lock | 2 +- changelog.d/pr-6387.build | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/pr-6387.build diff --git a/Podfile.lock b/Podfile.lock index 33e81e347..33e1eaaf5 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -243,4 +243,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: b3c7c064fc2b74dc937762364faab403fc3fd041 -COCOAPODS: 1.11.2 +COCOAPODS: 1.11.3 diff --git a/changelog.d/pr-6387.build b/changelog.d/pr-6387.build new file mode 100644 index 000000000..18755d1f0 --- /dev/null +++ b/changelog.d/pr-6387.build @@ -0,0 +1 @@ +Update Podfile.lock From 64d4b2c0eb6a8c78bd84a6c12a46bb4761fdb80f Mon Sep 17 00:00:00 2001 From: aringenbach Date: Mon, 11 Jul 2022 18:17:12 +0200 Subject: [PATCH 88/97] Fix slash commands from room composer --- Riot/Modules/Pills/PillsFormatter.swift | 23 ++++++++++++++++--- .../Room/DataSources/RoomDataSource.swift | 2 +- Riot/Modules/Room/RoomViewController.m | 20 ++++++++++++++-- changelog.d/6398.bugfix | 1 + 4 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 changelog.d/6398.bugfix diff --git a/Riot/Modules/Pills/PillsFormatter.swift b/Riot/Modules/Pills/PillsFormatter.swift index cdf13ebd8..ccee48317 100644 --- a/Riot/Modules/Pills/PillsFormatter.swift +++ b/Riot/Modules/Pills/PillsFormatter.swift @@ -25,6 +25,14 @@ class PillsFormatter: NSObject { /// UTType identifier for pills. Should be declared as Document type & Exported type identifier inside Info.plist static let pillUTType: String = "im.vector.app.pills" + // MARK: - Internal Enums + /// Defines a replacement mode for converting Pills to plain text. + @objc enum PillsReplacementTextMode: Int { + case displayname + case identifier + case markdown + } + // MARK: - Internal Methods /// Insert text attachments for pills inside given message attributed string. /// @@ -66,15 +74,24 @@ class PillsFormatter: NSObject { /// /// - Parameters: /// - attributedString: attributed string with pills - /// - asMarkdown: wether pill should be replaced by markdown links or raw text + /// - mode: replacement mode for pills (default: displayname) /// - Returns: string with display names - static func stringByReplacingPills(in attributedString: NSAttributedString, asMarkdown: Bool = false) -> String { + static func stringByReplacingPills(in attributedString: NSAttributedString, + mode: PillsReplacementTextMode = .displayname) -> String { let newAttr = NSMutableAttributedString(attributedString: attributedString) newAttr.vc_enumerateAttribute(.attachment) { (attachment: PillTextAttachment, range: NSRange, _) in if let displayText = attachment.data?.displayText, let userId = attachment.data?.matrixItemId, let permalink = MXTools.permalinkToUser(withUserId: userId) { - let pillString = asMarkdown ? "[\(displayText)](\(permalink))" : "\(displayText)" + let pillString: String + switch mode { + case .displayname: + pillString = displayText + case .identifier: + pillString = userId + case .markdown: + pillString = "[\(displayText)](\(permalink))" + } newAttr.replaceCharacters(in: range, with: pillString) } } diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.swift b/Riot/Modules/Room/DataSources/RoomDataSource.swift index 1fa660bc9..f57896b62 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.swift +++ b/Riot/Modules/Room/DataSources/RoomDataSource.swift @@ -219,7 +219,7 @@ private extension RoomDataSource { func htmlMessageFromSanitizedAttributedText(_ sanitizedText: NSAttributedString) -> String? { let rawText: String if #available(iOS 15.0, *) { - rawText = PillsFormatter.stringByReplacingPills(in: sanitizedText, asMarkdown: true) + rawText = PillsFormatter.stringByReplacingPills(in: sanitizedText, mode: .markdown) } else { rawText = sanitizedText.string } diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index d922f182b..00c120f22 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -4801,7 +4801,22 @@ static CGSize kThreadListBarButtonItemImageSize; - (void)roomInputToolbarView:(RoomInputToolbarView *)toolbarView sendAttributedTextMessage:(NSAttributedString *)attributedTextMessage { - [self sendAttributedTextMessage:attributedTextMessage]; + // "/me" command is supported with Pills in RoomDataSource. Other commands + // currently work with identifiers (e.g. ban, invite, op, etc). + NSString *message; + if (@available(iOS 15.0, *)) + { + message = [PillsFormatter stringByReplacingPillsIn:attributedTextMessage mode:PillsReplacementTextModeIdentifier]; + } + else + { + message = attributedTextMessage.string; + } + + if ([message hasPrefix:kMXKSlashCmdEmote] || [self isIRCStyleCommand:message] == NO) + { + [self sendAttributedTextMessage:attributedTextMessage]; + } } #pragma mark - MXKRoomMemberDetailsViewControllerDelegate @@ -6819,7 +6834,8 @@ static CGSize kThreadListBarButtonItemImageSize; { if (@available(iOS 15.0, *)) { - MXKPasteboardManager.shared.pasteboard.string = [PillsFormatter stringByReplacingPillsIn:attributedTextMessage asMarkdown:YES]; + MXKPasteboardManager.shared.pasteboard.string = [PillsFormatter stringByReplacingPillsIn:attributedTextMessage + mode:PillsReplacementTextModeMarkdown]; } else { diff --git a/changelog.d/6398.bugfix b/changelog.d/6398.bugfix new file mode 100644 index 000000000..037415029 --- /dev/null +++ b/changelog.d/6398.bugfix @@ -0,0 +1 @@ +Fix slash commands from room composer From 8be2d84c78f1fef6d69ba7a096a694ab8646735e Mon Sep 17 00:00:00 2001 From: aringenbach Date: Mon, 11 Jul 2022 18:36:56 +0200 Subject: [PATCH 89/97] Move fix to AppDelegate links handling --- Riot/Modules/Application/LegacyAppDelegate.m | 13 +++++++++++-- Riot/Utils/UniversalLink.m | 8 -------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Riot/Modules/Application/LegacyAppDelegate.m b/Riot/Modules/Application/LegacyAppDelegate.m index 06dfac203..a5fe2bd43 100644 --- a/Riot/Modules/Application/LegacyAppDelegate.m +++ b/Riot/Modules/Application/LegacyAppDelegate.m @@ -1312,8 +1312,17 @@ NSString *const AppDelegateUniversalLinkDidChangeNotification = @"AppDelegateUni // Sanity check if (!pathParams.count) { - MXLogDebug(@"[AppDelegate] Universal link: Error: No path parameters"); - return NO; + // Handle simple room links with aliases/identifiers as UniversalLink will not parse these. + NSString* absoluteUrl = [universalLink.url.absoluteString stringByRemovingPercentEncoding]; + if ([MXTools isMatrixRoomAlias:absoluteUrl] + || [MXTools isMatrixRoomIdentifier:absoluteUrl]) + { + pathParams = @[absoluteUrl]; + } + else { + MXLogDebug(@"[AppDelegate] Universal link: Error: No path parameters"); + return NO; + } } NSString *roomIdOrAlias; diff --git a/Riot/Utils/UniversalLink.m b/Riot/Utils/UniversalLink.m index 2f1a97936..edd73c7b8 100644 --- a/Riot/Utils/UniversalLink.m +++ b/Riot/Utils/UniversalLink.m @@ -16,7 +16,6 @@ #import "UniversalLink.h" #import "NSArray+Element.h" -#import "MXTools.h" @implementation UniversalLink @@ -52,13 +51,6 @@ // Remove the first empty path param string pathParams = [pathParams filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"length > 0"]]; - // Handle room links with aliases/identifiers - if ([MXTools isMatrixRoomAlias:[_url.absoluteString stringByRemovingPercentEncoding]] - || [MXTools isMatrixRoomIdentifier:[_url.absoluteString stringByRemovingPercentEncoding]]) - { - pathParams = @[_url.absoluteString]; - } - // URL decode each path param pathParams = [pathParams vc_map:^id _Nonnull(NSString * _Nonnull item) { return [item stringByRemovingPercentEncoding]; From c600a0d7a0e7022a9df69b52359d0d5126537edc Mon Sep 17 00:00:00 2001 From: aringenbach Date: Mon, 11 Jul 2022 17:09:25 +0200 Subject: [PATCH 90/97] Move room info back button title for translation --- Riot/Assets/en.lproj/Untranslated.strings | 3 --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 ++++ Riot/Generated/UntranslatedStrings.swift | 4 ---- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Riot/Assets/en.lproj/Untranslated.strings b/Riot/Assets/en.lproj/Untranslated.strings index 92e34f942..b87ff55be 100644 --- a/Riot/Assets/en.lproj/Untranslated.strings +++ b/Riot/Assets/en.lproj/Untranslated.strings @@ -88,6 +88,3 @@ "password_validation_error_contain_uppercase_letter" = "Contain an upper-case letter."; "password_validation_error_contain_number" = "Contain a number."; "password_validation_error_contain_symbol" = "Contain a symbol."; - -// MARK: Room Info -"room_info_back_button_title" = "Room Info"; diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index d739a4cd8..ad15ce9e2 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -1824,6 +1824,7 @@ Tap the + to start adding people."; "room_info_list_one_member" = "1 member"; "room_info_list_several_members" = "%@ members"; "room_info_list_section_other" = "Other"; +"room_info_back_button_title" = "Room Info"; // MARK: - Dial Pad "dialpad_title" = "Dial pad"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 17933d4ab..e468d6a82 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -5251,6 +5251,10 @@ public class VectorL10n: NSObject { public static var roomEventFailedToSend: String { return VectorL10n.tr("Vector", "room_event_failed_to_send") } + /// Room Info + public static var roomInfoBackButtonTitle: String { + return VectorL10n.tr("Vector", "room_info_back_button_title") + } /// 1 member public static var roomInfoListOneMember: String { return VectorL10n.tr("Vector", "room_info_list_one_member") diff --git a/Riot/Generated/UntranslatedStrings.swift b/Riot/Generated/UntranslatedStrings.swift index 6e22234a3..f69a82fbc 100644 --- a/Riot/Generated/UntranslatedStrings.swift +++ b/Riot/Generated/UntranslatedStrings.swift @@ -234,10 +234,6 @@ public extension VectorL10n { static var passwordValidationInfoHeader: String { return VectorL10n.tr("Untranslated", "password_validation_info_header") } - /// Room Info - static var roomInfoBackButtonTitle: String { - return VectorL10n.tr("Untranslated", "room_info_back_button_title") - } } // swiftlint:enable function_parameter_count identifier_name line_length type_body_length From bef199d9c2b3ccda1279fc3614378e8be81d63f1 Mon Sep 17 00:00:00 2001 From: aringenbach Date: Tue, 12 Jul 2022 09:47:12 +0200 Subject: [PATCH 91/97] Fix PillsFormatter unit tests --- RiotTests/PillsFormatterTests.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/RiotTests/PillsFormatterTests.swift b/RiotTests/PillsFormatterTests.swift index 93cfcb76e..3b5d2a83c 100644 --- a/RiotTests/PillsFormatterTests.swift +++ b/RiotTests/PillsFormatterTests.swift @@ -21,10 +21,11 @@ import XCTest private enum Inputs { static let messageStart = "Hello " static let aliceDisplayname = "Alice" + static let aliceUserId = "@alice:matrix.org" static let aliceAvatarUrl = "mxc://matrix.org/VyNYAgahaiAzUoOeZETtQ" static let aliceAwayDisplayname = "Alice_away" static let aliceNewAvatarUrl = "mxc://matrix.org/VyNYAgaFdlLojoOeZETtQ" - static let aliceMember = FakeMXRoomMember(displayname: aliceDisplayname, avatarUrl: aliceAvatarUrl, userId: "@alice:matrix.org") + static let aliceMember = FakeMXRoomMember(displayname: aliceDisplayname, avatarUrl: aliceAvatarUrl, userId: aliceUserId) static let aliceMemberAway = FakeMXRoomMember(displayname: aliceAwayDisplayname, avatarUrl: aliceNewAvatarUrl, userId: "@alice:matrix.org") static let bobMember = FakeMXRoomMember(displayname: "Bob", avatarUrl: "", userId: "@bob:matrix.org") static let alicePermalink = "https://matrix.to/#/@alice:matrix.org" @@ -77,14 +78,16 @@ class PillsFormatterTests: XCTestCase { func testPillsToMarkdown() { let messageWithPills = createMessageWithMentionFromBobToAlice() - let markdownMessage = PillsFormatter.stringByReplacingPills(in: messageWithPills, asMarkdown: true) + let markdownMessage = PillsFormatter.stringByReplacingPills(in: messageWithPills, mode: .markdown) XCTAssertEqual(markdownMessage, Inputs.messageStart + Inputs.markdownLinkToAlice) } func testPillsToRawBody() { let messageWithPills = createMessageWithMentionFromBobToAlice() - let rawMessage = PillsFormatter.stringByReplacingPills(in: messageWithPills, asMarkdown: false) - XCTAssertEqual(rawMessage, Inputs.messageStart + Inputs.aliceDisplayname) + let messageWithDisplayname = PillsFormatter.stringByReplacingPills(in: messageWithPills, mode: .displayname) + let messageWithUserId = PillsFormatter.stringByReplacingPills(in: messageWithPills, mode: .identifier) + XCTAssertEqual(messageWithDisplayname, Inputs.messageStart + Inputs.aliceDisplayname) + XCTAssertEqual(messageWithUserId, Inputs.messageStart + Inputs.aliceUserId) } } From 72890ddb7faccac70cd8a00d9214fef3323a72ff Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 11 Jul 2022 17:22:32 +0100 Subject: [PATCH 92/97] Rename riot-keys.txt to element-keys.txt. --- .../KeyBackup/ManualExport/EncryptionKeysExportPresenter.swift | 2 +- Riot/Modules/Settings/Security/SecurityViewController.m | 2 +- changelog.d/6391.bugfix | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 changelog.d/6391.bugfix diff --git a/Riot/Modules/KeyBackup/ManualExport/EncryptionKeysExportPresenter.swift b/Riot/Modules/KeyBackup/ManualExport/EncryptionKeysExportPresenter.swift index d18c9a991..e5111eacc 100644 --- a/Riot/Modules/KeyBackup/ManualExport/EncryptionKeysExportPresenter.swift +++ b/Riot/Modules/KeyBackup/ManualExport/EncryptionKeysExportPresenter.swift @@ -21,7 +21,7 @@ final class EncryptionKeysExportPresenter: NSObject { // MARK: - Constants private enum Constants { - static let keyExportFileName = "riot-keys.txt" + static let keyExportFileName = "element-keys.txt" } // MARK: - Properties diff --git a/Riot/Modules/Settings/Security/SecurityViewController.m b/Riot/Modules/Settings/Security/SecurityViewController.m index 3656218a2..297e0b6eb 100644 --- a/Riot/Modules/Settings/Security/SecurityViewController.m +++ b/Riot/Modules/Settings/Security/SecurityViewController.m @@ -1445,7 +1445,7 @@ TableViewSectionsDelegate> currentAlert = exportView.alertController; // Use a temporary file for the export - keyExportsFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"riot-keys.txt"]]; + keyExportsFile = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:@"element-keys.txt"]]; // Make sure the file is empty [self deleteKeyExportFile]; diff --git a/changelog.d/6391.bugfix b/changelog.d/6391.bugfix new file mode 100644 index 000000000..fe821f8a2 --- /dev/null +++ b/changelog.d/6391.bugfix @@ -0,0 +1 @@ +Rename riot-keys.txt to element-keys.txt. From d5bc0415a059240558c86c08697f445f8b325bb6 Mon Sep 17 00:00:00 2001 From: aringenbach Date: Tue, 12 Jul 2022 10:35:05 +0200 Subject: [PATCH 93/97] Rework slash command condition to make it a bit clearer --- Riot/Modules/Room/RoomViewController.m | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 00c120f22..7b1de8bf2 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -4801,19 +4801,25 @@ static CGSize kThreadListBarButtonItemImageSize; - (void)roomInputToolbarView:(RoomInputToolbarView *)toolbarView sendAttributedTextMessage:(NSAttributedString *)attributedTextMessage { - // "/me" command is supported with Pills in RoomDataSource. Other commands - // currently work with identifiers (e.g. ban, invite, op, etc). - NSString *message; - if (@available(iOS 15.0, *)) + BOOL isMessageAHandledCommand = NO; + // "/me" command is supported with Pills in RoomDataSource. + if (![attributedTextMessage.string hasPrefix:kMXKSlashCmdEmote]) { - message = [PillsFormatter stringByReplacingPillsIn:attributedTextMessage mode:PillsReplacementTextModeIdentifier]; - } - else - { - message = attributedTextMessage.string; + // Other commands currently work with identifiers (e.g. ban, invite, op, etc). + NSString *message; + if (@available(iOS 15.0, *)) + { + message = [PillsFormatter stringByReplacingPillsIn:attributedTextMessage mode:PillsReplacementTextModeIdentifier]; + } + else + { + message = attributedTextMessage.string; + } + // Try to send the slash command + isMessageAHandledCommand = [self isIRCStyleCommand:message]; } - if ([message hasPrefix:kMXKSlashCmdEmote] || [self isIRCStyleCommand:message] == NO) + if (!isMessageAHandledCommand) { [self sendAttributedTextMessage:attributedTextMessage]; } From dfd0ff41edb6a058436437654144b56e4d181f3f Mon Sep 17 00:00:00 2001 From: aringenbach Date: Tue, 12 Jul 2022 10:37:29 +0200 Subject: [PATCH 94/97] Rename `isIRCStyleCommand` to `sendAsIRCStyleCommandIfPossible` --- Riot/Modules/Room/MXKRoomViewController.h | 2 +- Riot/Modules/Room/MXKRoomViewController.m | 4 ++-- Riot/Modules/Room/RoomViewController.m | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Riot/Modules/Room/MXKRoomViewController.h b/Riot/Modules/Room/MXKRoomViewController.h index 17aa34a37..0ff875fc3 100644 --- a/Riot/Modules/Room/MXKRoomViewController.h +++ b/Riot/Modules/Room/MXKRoomViewController.h @@ -394,7 +394,7 @@ typedef NS_ENUM(NSUInteger, MXKRoomViewControllerJoinRoomResult) { @param string to analyse @return YES if IRC style command has been detected and interpreted. */ -- (BOOL)isIRCStyleCommand:(NSString*)string; +- (BOOL)sendAsIRCStyleCommandIfPossible:(NSString*)string; /** Mention the member display name in the current text of the message composer. diff --git a/Riot/Modules/Room/MXKRoomViewController.m b/Riot/Modules/Room/MXKRoomViewController.m index 17c4c2b5e..814977644 100644 --- a/Riot/Modules/Room/MXKRoomViewController.m +++ b/Riot/Modules/Room/MXKRoomViewController.m @@ -1250,7 +1250,7 @@ customEventDetailsViewClass = eventDetailsViewClass; } -- (BOOL)isIRCStyleCommand:(NSString*)string +- (BOOL)sendAsIRCStyleCommandIfPossible:(NSString*)string { // Check whether the provided text may be an IRC-style command if ([string hasPrefix:@"/"] == NO || [string hasPrefix:@"//"] == YES) @@ -3375,7 +3375,7 @@ - (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView sendTextMessage:(NSString*)textMessage { // Handle potential IRC commands in typed string - if ([self isIRCStyleCommand:textMessage] == NO) + if ([self sendAsIRCStyleCommandIfPossible:textMessage] == NO) { // Send text message in the current room [self sendTextMessage:textMessage]; diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 7b1de8bf2..26dd78547 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -1228,7 +1228,7 @@ static CGSize kThreadListBarButtonItemImageSize; } } -- (BOOL)isIRCStyleCommand:(NSString*)string +- (BOOL)sendAsIRCStyleCommandIfPossible:(NSString*)string { // Override the default behavior for `/join` command in order to open automatically the joined room @@ -1271,7 +1271,7 @@ static CGSize kThreadListBarButtonItemImageSize; } return YES; } - return [super isIRCStyleCommand:string]; + return [super sendAsIRCStyleCommandIfPossible:string]; } - (void)setKeyboardHeight:(CGFloat)keyboardHeight @@ -4816,7 +4816,7 @@ static CGSize kThreadListBarButtonItemImageSize; message = attributedTextMessage.string; } // Try to send the slash command - isMessageAHandledCommand = [self isIRCStyleCommand:message]; + isMessageAHandledCommand = [self sendAsIRCStyleCommandIfPossible:message]; } if (!isMessageAHandledCommand) From 7b863c7763fbcdd41454d0e1d11084cb7c1ac6b5 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 12 Jul 2022 15:51:33 +0200 Subject: [PATCH 95/97] LiveLocationSharingViewer: Update copyright bottom padding. --- .../View/LiveLocationSharingViewer.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift index d468edf2f..fed3e4c41 100644 --- a/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift +++ b/RiotSwiftUI/Modules/Room/LiveLocationSharingViewer/View/LiveLocationSharingViewer.swift @@ -57,7 +57,8 @@ struct LiveLocationSharingViewer: View { MapCreditsView(action: { viewModel.send(viewAction: .mapCreditsDidTap) }) - .offset(y: -(bottomSheetCollapsedHeight + 10)) + .offset(y: -(bottomSheetCollapsedHeight)) // Put the copyright action above the collapsed bottom sheet + .padding(.bottom, 10) } .ignoresSafeArea() } From 61c053bb973881f1667d04f3e24f467cf3cee875 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 12 Jul 2022 16:03:39 +0100 Subject: [PATCH 96/97] changelog.d: Upgrade MatrixSDK version ([v0.23.11](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.23.11)). --- Podfile | 2 +- changelog.d/x-nolink-0.change | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 changelog.d/x-nolink-0.change diff --git a/Podfile b/Podfile index 17a98de3b..1ad092f27 100644 --- a/Podfile +++ b/Podfile @@ -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.23.10' +$matrixSDKVersion = '= 0.23.11' # $matrixSDKVersion = :local # $matrixSDKVersion = { :branch => 'develop'} # $matrixSDKVersion = { :specHash => { git: 'https://git.io/fork123', branch: 'fix' } } diff --git a/changelog.d/x-nolink-0.change b/changelog.d/x-nolink-0.change new file mode 100644 index 000000000..189377e2c --- /dev/null +++ b/changelog.d/x-nolink-0.change @@ -0,0 +1 @@ +Upgrade MatrixSDK version ([v0.23.11](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.23.11)). \ No newline at end of file From 2a907e6888197200fc17d557e52b9d53192b6faf Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 12 Jul 2022 16:03:39 +0100 Subject: [PATCH 97/97] version++ --- CHANGES.md | 47 +++++++++++++++++++++++++++++++++++ changelog.d/1108.feature | 1 - changelog.d/5326.bugfix | 1 - changelog.d/5341.bugfix | 1 - changelog.d/5372.change | 1 - changelog.d/5607.feature | 1 - changelog.d/6108.change | 1 - changelog.d/6203.build | 1 - changelog.d/6238.change | 1 - changelog.d/6273.change | 1 - changelog.d/6276.change | 1 - changelog.d/6358.bugfix | 1 - changelog.d/6361.bugfix | 1 - changelog.d/6371.bugfix | 1 - changelog.d/6376.change | 1 - changelog.d/6382.change | 1 - changelog.d/6386.bugfix | 1 - changelog.d/6391.bugfix | 1 - changelog.d/6392.misc | 1 - changelog.d/6395.bugfix | 1 - changelog.d/6398.bugfix | 1 - changelog.d/pr-6275.api | 1 - changelog.d/pr-6308.feature | 1 - changelog.d/pr-6380.change | 1 - changelog.d/pr-6381.bugfix | 1 - changelog.d/pr-6387.build | 1 - changelog.d/x-nolink-0.change | 1 - 27 files changed, 47 insertions(+), 26 deletions(-) delete mode 100644 changelog.d/1108.feature delete mode 100644 changelog.d/5326.bugfix delete mode 100644 changelog.d/5341.bugfix delete mode 100644 changelog.d/5372.change delete mode 100644 changelog.d/5607.feature delete mode 100644 changelog.d/6108.change delete mode 100644 changelog.d/6203.build delete mode 100644 changelog.d/6238.change delete mode 100644 changelog.d/6273.change delete mode 100644 changelog.d/6276.change delete mode 100644 changelog.d/6358.bugfix delete mode 100644 changelog.d/6361.bugfix delete mode 100644 changelog.d/6371.bugfix delete mode 100644 changelog.d/6376.change delete mode 100644 changelog.d/6382.change delete mode 100644 changelog.d/6386.bugfix delete mode 100644 changelog.d/6391.bugfix delete mode 100644 changelog.d/6392.misc delete mode 100644 changelog.d/6395.bugfix delete mode 100644 changelog.d/6398.bugfix delete mode 100644 changelog.d/pr-6275.api delete mode 100644 changelog.d/pr-6308.feature delete mode 100644 changelog.d/pr-6380.change delete mode 100644 changelog.d/pr-6381.bugfix delete mode 100644 changelog.d/pr-6387.build delete mode 100644 changelog.d/x-nolink-0.change diff --git a/CHANGES.md b/CHANGES.md index 4457e8f2f..5065fd74e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,50 @@ +## Changes in 1.8.21 (2022-07-12) + +✨ Features + +- Analytics: Track non-fatal issues if consent provided ([#6308](https://github.com/vector-im/element-ios/pull/6308)) +- Notifications: Add a setting for in-app notifications and use the value with existing functionality in PushNotificationService. ([#1108](https://github.com/vector-im/element-ios/issues/1108)) +- Server Offline Activity Indicator ([#5607](https://github.com/vector-im/element-ios/issues/5607)) + +🙌 Improvements + +- Add formatter build reply HTML unit tests ([#6380](https://github.com/vector-im/element-ios/pull/6380)) +- Upgrade MatrixSDK version ([v0.23.11](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.23.11)). +- Update Files component ([#5372](https://github.com/vector-im/element-ios/issues/5372)) +- Location sharing: Update map credits display and behavior. ([#6108](https://github.com/vector-im/element-ios/issues/6108)) +- Location sharing: Add view to promote live location sharing labs flag on the sharing screen. ([#6238](https://github.com/vector-im/element-ios/issues/6238)) +- Remove legacy Riot-Defaults property list ([#6273](https://github.com/vector-im/element-ios/issues/6273)) +- DesignKit: Replace the local DesignKit target with the shared Swift package from ElementX. ([#6276](https://github.com/vector-im/element-ios/issues/6276)) +- Enhance the VectorHostingController to be presented as a bottom sheet ([#6376](https://github.com/vector-im/element-ios/issues/6376)) +- Location sharing: Live location sharing UI polishing. ([#6382](https://github.com/vector-im/element-ios/issues/6382)) + +🐛 Bugfixes + +- VectorHostingController: Fix infinite loop due to the safe area insets fix. ([#6381](https://github.com/vector-im/element-ios/pull/6381)) +- Fix layout issues in timeline poll cells (PSB-125) ([#5326](https://github.com/vector-im/element-ios/issues/5326)) +- Fixed Invite user UI is always hidden by the keyboard ([#5341](https://github.com/vector-im/element-ios/issues/5341)) +- Cross-Signing: Use ZXing library to generate QR codes ([#6358](https://github.com/vector-im/element-ios/issues/6358)) +- Location sharing: Fix live location sharing lab flag activation, no more app relaunch needed. ([#6361](https://github.com/vector-im/element-ios/issues/6361)) +- Display fallback when replied event content is partially missing ([#6371](https://github.com/vector-im/element-ios/issues/6371)) +- Fix a few failing UI tests. ([#6386](https://github.com/vector-im/element-ios/issues/6386)) +- Rename riot-keys.txt to element-keys.txt. ([#6391](https://github.com/vector-im/element-ios/issues/6391)) +- Fix inoperant room links with alias/identifiers ([#6395](https://github.com/vector-im/element-ios/issues/6395)) +- Fix slash commands from room composer ([#6398](https://github.com/vector-im/element-ios/issues/6398)) + +⚠️ API Changes + +- Replace DesignKit framework with [DesignKit package](https://github.com/vector-im/element-x-ios/tree/develop/DesignKit/Sources). Colours are now generated in the [DesignTokens repo](https://github.com/vector-im/element-design-tokens) to be shared across all of our apps. ([#6275](https://github.com/vector-im/element-ios/pull/6275)) + +🧱 Build + +- Update Podfile.lock ([#6387](https://github.com/vector-im/element-ios/pull/6387)) +- Split `IntentHandler` into smaller, dedicated entities ([#6203](https://github.com/vector-im/element-ios/issues/6203)) + +Others + +- Revert some font changes made when merging #6392. ([#6392](https://github.com/vector-im/element-ios/issues/6392)) + + ## Changes in 1.8.20 (2022-06-28) ✨ Features diff --git a/changelog.d/1108.feature b/changelog.d/1108.feature deleted file mode 100644 index e33cae1a0..000000000 --- a/changelog.d/1108.feature +++ /dev/null @@ -1 +0,0 @@ -Notifications: Add a setting for in-app notifications and use the value with existing functionality in PushNotificationService. diff --git a/changelog.d/5326.bugfix b/changelog.d/5326.bugfix deleted file mode 100644 index 044887f79..000000000 --- a/changelog.d/5326.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix layout issues in timeline poll cells (PSB-125) diff --git a/changelog.d/5341.bugfix b/changelog.d/5341.bugfix deleted file mode 100644 index bb2b1d244..000000000 --- a/changelog.d/5341.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fixed Invite user UI is always hidden by the keyboard diff --git a/changelog.d/5372.change b/changelog.d/5372.change deleted file mode 100644 index cdf6e1433..000000000 --- a/changelog.d/5372.change +++ /dev/null @@ -1 +0,0 @@ -Update Files component diff --git a/changelog.d/5607.feature b/changelog.d/5607.feature deleted file mode 100644 index f17d5c084..000000000 --- a/changelog.d/5607.feature +++ /dev/null @@ -1 +0,0 @@ -Server Offline Activity Indicator diff --git a/changelog.d/6108.change b/changelog.d/6108.change deleted file mode 100644 index ba3e87f10..000000000 --- a/changelog.d/6108.change +++ /dev/null @@ -1 +0,0 @@ -Location sharing: Update map credits display and behavior. diff --git a/changelog.d/6203.build b/changelog.d/6203.build deleted file mode 100644 index fb5d20f04..000000000 --- a/changelog.d/6203.build +++ /dev/null @@ -1 +0,0 @@ -Split `IntentHandler` into smaller, dedicated entities diff --git a/changelog.d/6238.change b/changelog.d/6238.change deleted file mode 100644 index 798c5f25a..000000000 --- a/changelog.d/6238.change +++ /dev/null @@ -1 +0,0 @@ -Location sharing: Add view to promote live location sharing labs flag on the sharing screen. diff --git a/changelog.d/6273.change b/changelog.d/6273.change deleted file mode 100644 index 28212eb87..000000000 --- a/changelog.d/6273.change +++ /dev/null @@ -1 +0,0 @@ -Remove legacy Riot-Defaults property list diff --git a/changelog.d/6276.change b/changelog.d/6276.change deleted file mode 100644 index ebab74708..000000000 --- a/changelog.d/6276.change +++ /dev/null @@ -1 +0,0 @@ -DesignKit: Replace the local DesignKit target with the shared Swift package from ElementX. diff --git a/changelog.d/6358.bugfix b/changelog.d/6358.bugfix deleted file mode 100644 index a0e99dd20..000000000 --- a/changelog.d/6358.bugfix +++ /dev/null @@ -1 +0,0 @@ -Cross-Signing: Use ZXing library to generate QR codes diff --git a/changelog.d/6361.bugfix b/changelog.d/6361.bugfix deleted file mode 100644 index e514e9699..000000000 --- a/changelog.d/6361.bugfix +++ /dev/null @@ -1 +0,0 @@ -Location sharing: Fix live location sharing lab flag activation, no more app relaunch needed. \ No newline at end of file diff --git a/changelog.d/6371.bugfix b/changelog.d/6371.bugfix deleted file mode 100644 index 7829efb59..000000000 --- a/changelog.d/6371.bugfix +++ /dev/null @@ -1 +0,0 @@ -Display fallback when replied event content is partially missing diff --git a/changelog.d/6376.change b/changelog.d/6376.change deleted file mode 100644 index ce6c3b487..000000000 --- a/changelog.d/6376.change +++ /dev/null @@ -1 +0,0 @@ -Enhance the VectorHostingController to be presented as a bottom sheet diff --git a/changelog.d/6382.change b/changelog.d/6382.change deleted file mode 100644 index 6ed30b502..000000000 --- a/changelog.d/6382.change +++ /dev/null @@ -1 +0,0 @@ -Location sharing: Live location sharing UI polishing. diff --git a/changelog.d/6386.bugfix b/changelog.d/6386.bugfix deleted file mode 100644 index 3135984e4..000000000 --- a/changelog.d/6386.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix a few failing UI tests. diff --git a/changelog.d/6391.bugfix b/changelog.d/6391.bugfix deleted file mode 100644 index fe821f8a2..000000000 --- a/changelog.d/6391.bugfix +++ /dev/null @@ -1 +0,0 @@ -Rename riot-keys.txt to element-keys.txt. diff --git a/changelog.d/6392.misc b/changelog.d/6392.misc deleted file mode 100644 index 4237bba17..000000000 --- a/changelog.d/6392.misc +++ /dev/null @@ -1 +0,0 @@ -Revert some font changes made when merging #6392. diff --git a/changelog.d/6395.bugfix b/changelog.d/6395.bugfix deleted file mode 100644 index 0bafa6d70..000000000 --- a/changelog.d/6395.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix inoperant room links with alias/identifiers diff --git a/changelog.d/6398.bugfix b/changelog.d/6398.bugfix deleted file mode 100644 index 037415029..000000000 --- a/changelog.d/6398.bugfix +++ /dev/null @@ -1 +0,0 @@ -Fix slash commands from room composer diff --git a/changelog.d/pr-6275.api b/changelog.d/pr-6275.api deleted file mode 100644 index a4e2fb491..000000000 --- a/changelog.d/pr-6275.api +++ /dev/null @@ -1 +0,0 @@ -Replace DesignKit framework with [DesignKit package](https://github.com/vector-im/element-x-ios/tree/develop/DesignKit/Sources). Colours are now generated in the [DesignTokens repo](https://github.com/vector-im/element-design-tokens) to be shared across all of our apps. diff --git a/changelog.d/pr-6308.feature b/changelog.d/pr-6308.feature deleted file mode 100644 index d288d13bf..000000000 --- a/changelog.d/pr-6308.feature +++ /dev/null @@ -1 +0,0 @@ -Analytics: Track non-fatal issues if consent provided diff --git a/changelog.d/pr-6380.change b/changelog.d/pr-6380.change deleted file mode 100644 index 1465b71f0..000000000 --- a/changelog.d/pr-6380.change +++ /dev/null @@ -1 +0,0 @@ -Add formatter build reply HTML unit tests diff --git a/changelog.d/pr-6381.bugfix b/changelog.d/pr-6381.bugfix deleted file mode 100644 index f34e363c1..000000000 --- a/changelog.d/pr-6381.bugfix +++ /dev/null @@ -1 +0,0 @@ -VectorHostingController: Fix infinite loop due to the safe area insets fix. diff --git a/changelog.d/pr-6387.build b/changelog.d/pr-6387.build deleted file mode 100644 index 18755d1f0..000000000 --- a/changelog.d/pr-6387.build +++ /dev/null @@ -1 +0,0 @@ -Update Podfile.lock diff --git a/changelog.d/x-nolink-0.change b/changelog.d/x-nolink-0.change deleted file mode 100644 index 189377e2c..000000000 --- a/changelog.d/x-nolink-0.change +++ /dev/null @@ -1 +0,0 @@ -Upgrade MatrixSDK version ([v0.23.11](https://github.com/matrix-org/matrix-ios-sdk/releases/tag/v0.23.11)). \ No newline at end of file