From 16dc04e18dd433d93a32c0c707d22d5a10eb9be6 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 30 Apr 2019 17:07:43 +0200 Subject: [PATCH 001/266] RoomInputToolbarView: Turn the boolean replyToEnabled into an enum: sendMode because message editing (#2404) is coming --- Riot/Modules/Room/RoomViewController.m | 33 +++++++++------ .../Views/InputToolbar/RoomInputToolbarView.h | 14 ++++++- .../Views/InputToolbar/RoomInputToolbarView.m | 40 ++++++++++++++++--- 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 49440b8da7..97ec2831ee 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -206,9 +206,6 @@ @interface RoomViewController () /** @@ -70,9 +80,9 @@ @property (nonatomic) BOOL isEncryptionEnabled; /** - Tell whether the input text will be a reply to a message. + Destination of the message in the composer. */ -@property (nonatomic, getter=isReplyToEnabled) BOOL replyToEnabled; +@property (nonatomic) RoomInputToolbarViewSendMode sendMode; /** Tell whether a call is active. diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 0b483bd5c4..5ddca56b22 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -69,6 +69,7 @@ - (void)awakeFromNib [super awakeFromNib]; _supportCallOption = YES; + _sendMode = RoomInputToolbarViewSendModeSend; self.rightInputToolbarButton.hidden = YES; @@ -154,10 +155,10 @@ - (void)setIsEncryptionEnabled:(BOOL)isEncryptionEnabled self.placeholder = placeholder; } -- (void)setReplyToEnabled:(BOOL)isReplyToEnabled +- (void)setSendMode:(RoomInputToolbarViewSendMode)sendMode { - _replyToEnabled = isReplyToEnabled; - + _sendMode = sendMode; + [self updatePlaceholder]; } @@ -172,17 +173,44 @@ - (void)updatePlaceholder if (!shouldDisplayLargePlaceholder) { - placeholder = _replyToEnabled ? NSLocalizedStringFromTable(@"room_message_reply_to_short_placeholder", @"Vector", nil) : NSLocalizedStringFromTable(@"room_message_short_placeholder", @"Vector", nil); + switch (_sendMode) + { + case RoomInputToolbarViewSendModeReply: + placeholder = NSLocalizedStringFromTable(@"room_message_reply_to_short_placeholder", @"Vector", nil); + break; + + default: + placeholder = NSLocalizedStringFromTable(@"room_message_short_placeholder", @"Vector", nil); + break; + } } else { if (_isEncryptionEnabled) { - placeholder = _replyToEnabled ? NSLocalizedStringFromTable(@"encrypted_room_message_reply_to_placeholder", @"Vector", nil) : NSLocalizedStringFromTable(@"encrypted_room_message_placeholder", @"Vector", nil); + switch (_sendMode) + { + case RoomInputToolbarViewSendModeReply: + placeholder = NSLocalizedStringFromTable(@"encrypted_room_message_reply_to_placeholder", @"Vector", nil); + break; + + default: + placeholder = NSLocalizedStringFromTable(@"encrypted_room_message_placeholder", @"Vector", nil); + break; + } } else { - placeholder = _replyToEnabled ? NSLocalizedStringFromTable(@"room_message_reply_to_placeholder", @"Vector", nil) : NSLocalizedStringFromTable(@"room_message_placeholder", @"Vector", nil); + switch (_sendMode) + { + case RoomInputToolbarViewSendModeReply: + placeholder = NSLocalizedStringFromTable(@"room_message_reply_to_placeholder", @"Vector", nil); + break; + + default: + placeholder = NSLocalizedStringFromTable(@"room_message_placeholder", @"Vector", nil); + break; + } } } From ef4fed1bb2f14cb6ae0b55361bbbb34fc9b946c0 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 30 Apr 2019 17:27:57 +0200 Subject: [PATCH 002/266] RoomVC: When replying, use a "Reply" button instead of "Send" --- CHANGES.rst | 6 ++++++ Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 ++++ .../Views/InputToolbar/RoomInputToolbarView.m | 20 +++++++++++++++++++ 4 files changed, 31 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index d8128c0450..4e072709e9 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +Changes in 0.8.6 (2019-xx-xx) +=============================================== + +Improvements: + * RoomVC: When replying, use a "Reply" button instead of "Send". + Changes in 0.8.5 (2019-xx-xx) =============================================== diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index adc5906501..9e580c24f6 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -282,6 +282,7 @@ "room_event_failed_to_send" = "Failed to send"; "room_action_send_photo_or_video" = "Send photo or video"; "room_action_send_sticker" = "Send sticker"; +"room_action_reply" = "Reply"; "room_replacement_information" = "This room has been replaced and is no longer active."; "room_replacement_link" = "The conversation continues here."; "room_predecessor_information" = "This room is a continuation of another conversation."; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 1239b656be..5932c9364a 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1338,6 +1338,10 @@ internal enum VectorL10n { internal static var retry: String { return VectorL10n.tr("Vector", "retry") } + /// Reply + internal static var roomActionReply: String { + return VectorL10n.tr("Vector", "room_action_reply") + } /// Send photo or video internal static var roomActionSendPhotoOrVideo: String { return VectorL10n.tr("Vector", "room_action_send_photo_or_video") diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 5ddca56b22..66c182cc57 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -160,6 +160,26 @@ - (void)setSendMode:(RoomInputToolbarViewSendMode)sendMode _sendMode = sendMode; [self updatePlaceholder]; + [self updateToolbarButtonLabel]; +} + +- (void)updateToolbarButtonLabel +{ + NSString *title; + + switch (_sendMode) + { + case RoomInputToolbarViewSendModeReply: + title = NSLocalizedStringFromTable(@"room_action_reply", @"Vector", nil); + break; + + default: + title = [NSBundle mxk_localizedStringForKey:@"send"]; + break; + } + + [self.rightInputToolbarButton setTitle:title forState:UIControlStateNormal]; + [self.rightInputToolbarButton setTitle:title forState:UIControlStateHighlighted]; } - (void)updatePlaceholder From b4d7b3f78e64f2b2d2868f637942ad4626fc1f1c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 7 May 2019 16:49:12 +0200 Subject: [PATCH 003/266] MXKRoomBubbleTableViewCell+Riot: Add possibility to hide edit button on selection. --- Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h | 8 ++++++++ Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m | 12 ++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h index 5162457f28..9063887dce 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h @@ -51,6 +51,14 @@ extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer; */ - (void)selectComponent:(NSUInteger)componentIndex; +/** + Highlight a component in receiver and show or not edit button. + + @param componentIndex index of the component in bubble message data + @param showEditButton true to show edit button + */ +- (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton; + /** Mark a component in receiver. diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 2e025b1ac4..2560f91e89 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -137,6 +137,11 @@ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex } - (void)selectComponent:(NSUInteger)componentIndex +{ + [self selectComponent:componentIndex showEditButton:YES]; +} + +- (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton { if (componentIndex < bubbleData.bubbleComponents.count) { @@ -164,8 +169,11 @@ - (void)selectComponent:(NSUInteger)componentIndex } } - // Add the edit button - [self addEditButtonForComponent:componentIndex completion:nil]; + if (showEditButton) + { + // Add the edit button + [self addEditButtonForComponent:componentIndex completion:nil]; + } } } From ca2454a81116d8b34d0fe1d610cec91ecb93c566 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 7 May 2019 18:24:05 +0200 Subject: [PATCH 004/266] MXKRoomBubbleTableViewCell: Enable long press on event. --- Riot/AppDelegate.m | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 65b7ffdd3f..bc8c22bdd2 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -2555,8 +2555,8 @@ - (void)initMatrixSessions // Get modular widget events in rooms histories [[MXKAppSettings standardAppSettings] addSupportedEventTypes:@[kWidgetMatrixEventTypeString, kWidgetModularEventTypeString]]; - // Disable long press on event in bubble cells - [MXKRoomBubbleTableViewCell disableLongPressGestureOnEvent:YES]; + // Enable long press on event in bubble cells + [MXKRoomBubbleTableViewCell disableLongPressGestureOnEvent:NO]; // Set first RoomDataSource class used in Vector [MXKRoomDataSourceManager registerRoomDataSourceClass:RoomDataSource.class]; From fe1e9cedf8816dfc5158ff2334f5c10bcdf7ff64 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Mon, 18 Mar 2019 21:18:27 +0000 Subject: [PATCH 005/266] Translated using Weblate (Albanian) Currently translated at 100.0% (4 of 4 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-dialogs/sq/ --- Riot/Assets/sq.lproj/InfoPlist.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/sq.lproj/InfoPlist.strings b/Riot/Assets/sq.lproj/InfoPlist.strings index ede69c7652..680c8399d1 100644 --- a/Riot/Assets/sq.lproj/InfoPlist.strings +++ b/Riot/Assets/sq.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "NSCameraUsageDescription" = "Kamera përdoret për të bërë foto dhe regjistruar video, dhe për të bërë thirrje video."; "NSPhotoLibraryUsageDescription" = "Fototeka përdoret për të dërguar foto dhe video."; "NSMicrophoneUsageDescription" = "Mikrofoni përdoret për të regjistruar video, dhe për të bërë thirrje."; -"NSContactsUsageDescription" = "Që të mund t’ju shfaqim se cilët prej kontakteve tuaj përdorin tashmë Riot ose Matrix, mund të dërgojmë adresat email dhe numrat e telefonave nga libri juaj i adresave te Shërbyesi Matrix i Identiteteve. Vektori i ri nuk i depoziton këto të dhëna apo t’i përdorë për ndonjë qëllim tjetër. Për më tepër të dhëna, shihni faqen e rregullave të privatësisë, te rregullimet e aplikacionit."; +"NSContactsUsageDescription" = "Që të mund t’ju shfaqim se cilët prej kontakteve tuaj përdorin tashmë Riot ose Matrix, mund të dërgojmë adresat email dhe numrat e telefonave nga libri juaj i adresave te Shërbyesi Matrix i Identiteteve. Vektori i ri nuk i depoziton këto të dhëna, as i përdor për ndonjë qëllim tjetër. Për më tepër të dhëna, shihni faqen e rregullave të privatësisë, te rregullimet e aplikacionit."; From e7c9cb2f3aa73403b20cdf3ad9694012c04aea9f Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Thu, 28 Mar 2019 08:04:54 +0000 Subject: [PATCH 006/266] Translated using Weblate (Dutch) Currently translated at 100.0% (4 of 4 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-dialogs/nl/ --- Riot/Assets/nl.lproj/InfoPlist.strings | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/nl.lproj/InfoPlist.strings b/Riot/Assets/nl.lproj/InfoPlist.strings index d81fc329df..2f756c8142 100644 --- a/Riot/Assets/nl.lproj/InfoPlist.strings +++ b/Riot/Assets/nl.lproj/InfoPlist.strings @@ -15,7 +15,7 @@ */ // Permissions usage explanations -"NSCameraUsageDescription" = "De camera wordt gebruikt om foto's te schieten, video's te maken en voor videogesprekken."; -"NSPhotoLibraryUsageDescription" = "De fotogalerij wordt gebruikt om foto's en video's te versturen."; -"NSMicrophoneUsageDescription" = "De microffon wordt gebruikt om video's te maken en voor spraakoproepen."; -"NSContactsUsageDescription" = "De contactenlijst wordt gebruikt om naar gebruikers te zoeken door middel van een e-mailadres of telefoonnummer op Riot."; +"NSCameraUsageDescription" = "De camera wordt gebruikt om foto’s en video’s te maken, en voor videogesprekken."; +"NSPhotoLibraryUsageDescription" = "De fotogalerij wordt gebruikt om foto’s en video’s te versturen."; +"NSMicrophoneUsageDescription" = "De microfoon wordt gebruikt om video’s te maken, en voor spraakoproepen."; +"NSContactsUsageDescription" = "Om u te kunnen tonen welke van uw contacten reeds Riot of Matrix gebruiken, kunnen we de e-mailadressen en telefoonnummers in uw adresboek naar uw Matrix-identiteitsserver sturen. New Vector bewaart deze gegevens niet en gebruikt ze niet voor andere doeleinden. Bekijk voor meer informatie de privacybeleidspagina in de instellingen van de app."; From 45faad3dee450dfaa04e184f577c1d518afef578 Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Thu, 28 Mar 2019 08:02:54 +0000 Subject: [PATCH 007/266] Translated using Weblate (Dutch) Currently translated at 100.0% (26 of 26 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/nl/ --- Riot/Assets/nl.lproj/Localizable.strings | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Riot/Assets/nl.lproj/Localizable.strings b/Riot/Assets/nl.lproj/Localizable.strings index 10883cd3cb..ecca3ab6c3 100644 --- a/Riot/Assets/nl.lproj/Localizable.strings +++ b/Riot/Assets/nl.lproj/Localizable.strings @@ -33,9 +33,9 @@ /** Image Messages **/ /* New action message from a specific person, not referencing a room. */ -"IMAGE_FROM_USER" = "%@ heeft je een afbeelding %@ gestuurd"; +"IMAGE_FROM_USER" = "%@ heeft u een afbeelding %@ gestuurd"; /* New action message from a specific person in a named room. */ -"IMAGE_FROM_USER_IN_ROOM" = "%@ plaatste een afbeelding %@ in %@"; +"IMAGE_FROM_USER_IN_ROOM" = "%@ heeft een afbeelding %@ in %@ geplaatst"; /** Coalesced messages **/ /* Multiple unread messages in a room */ @@ -55,11 +55,11 @@ /** Invites **/ /* A user has invited you to a chat */ -"USER_INVITE_TO_CHAT" = "%@ heeft je voor een gesprek uitgenodigd"; +"USER_INVITE_TO_CHAT" = "%@ heeft u voor een gesprek uitgenodigd"; /* A user has invited you to an (unamed) group chat */ -"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ heeft je voor een groepsgesprek uitgenodigd"; +"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ heeft u in een groepsgesprek uitgenodigd"; /* A user has invited you to a named room */ -"USER_INVITE_TO_NAMED_ROOM" = "%@ heeft je in %@ uitgenodigd"; +"USER_INVITE_TO_NAMED_ROOM" = "%@ heeft u in %@ uitgenodigd"; /** Calls **/ /* Incoming one-to-one voice call */ @@ -69,12 +69,12 @@ /* Incoming unnamed voice conference invite from a specific person */ "VOICE_CONF_FROM_USER" = "Groepsoproep van %@"; /* Incoming unnamed video conference invite from a specific person */ -"VIDEO_CONF_FROM_USER" = "Video groepsoproep van %@"; +"VIDEO_CONF_FROM_USER" = "Video-groepsoproep van %@"; /* Incoming named voice conference invite from a specific person */ -"VOICE_CONF_NAMED_FROM_USER" = "Groepsoproep van %@: '%@'"; +"VOICE_CONF_NAMED_FROM_USER" = "Groepsoproep van %@: ‘%@’"; /* Incoming named video conference invite from a specific person */ -"VIDEO_CONF_NAMED_FROM_USER" = "Video groepsoproep van %@: '%@'"; +"VIDEO_CONF_NAMED_FROM_USER" = "Video-groepsoproep van %@: ‘%@’"; /* A single unread message in a room */ -"SINGLE_UNREAD_IN_ROOM" = "Je hebt een bericht ontvangen in %@"; +"SINGLE_UNREAD_IN_ROOM" = "U heeft een bericht ontvangen in %@"; /* A single unread message */ -"SINGLE_UNREAD" = "Je hebt een bericht ontvangen"; +"SINGLE_UNREAD" = "U heeft een bericht ontvangen"; From 6e420200c99c00b7044bab33b07cc24986818af2 Mon Sep 17 00:00:00 2001 From: tea Date: Sun, 24 Mar 2019 14:53:31 +0000 Subject: [PATCH 008/266] Translated using Weblate (Italian) Currently translated at 100.0% (4 of 4 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-dialogs/it/ --- Riot/Assets/it.lproj/InfoPlist.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/it.lproj/InfoPlist.strings b/Riot/Assets/it.lproj/InfoPlist.strings index 62fa600597..046971b314 100644 --- a/Riot/Assets/it.lproj/InfoPlist.strings +++ b/Riot/Assets/it.lproj/InfoPlist.strings @@ -2,4 +2,4 @@ "NSCameraUsageDescription" = "La fotocamera viene utilizzata per scattare fotografie, registrare video ed eseguire videochiamate."; "NSPhotoLibraryUsageDescription" = "La libreria fotografica viene utilizzata per inviare foto e video."; "NSMicrophoneUsageDescription" = "Il microfono viene utilizzato per registrare video ed effettuare chiamate."; -"NSContactsUsageDescription" = "La rubrica viene utilizzata per ricercare utenti tramite email o numero di telefono su Riot."; +"NSContactsUsageDescription" = "Per mostrare i tuoi contatti che stanno già usando Riot o Matrix, possiamo inviare gli indirizzi email e i numeri di telefono della tua rubrica al tuo server identità. New Vector non memorizza e non usa per altri scopi questi dati. Per maggiori informazioni guarda l'informativa sulla privacy presente nelle impostazioni di questa applicazione."; From 586e60c436f6243d6b0203ef06aadb7ad9ff4498 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Mon, 18 Mar 2019 14:57:01 +0000 Subject: [PATCH 009/266] Translated using Weblate (Albanian) Currently translated at 99.7% (616 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 36 +++++++++++++++-------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 04ea808f32..52921d82de 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -47,8 +47,8 @@ "auth_phone_placeholder" = "Numër telefoni"; "auth_repeat_password_placeholder" = "Rijepeni fjalëkalimin"; "auth_repeat_new_password_placeholder" = "Ripohoni fjalëkalimin tuaj të ri"; -"auth_home_server_placeholder" = "URL (p.sh. https://matrix.org)"; -"auth_identity_server_placeholder" = "URL (p.sh. https://matrix.org)"; +"auth_home_server_placeholder" = "URL (p.sh., https://matrix.org)"; +"auth_identity_server_placeholder" = "URL (p.sh., https://matrix.org)"; "auth_invalid_login_param" = "Emër përdoruesi dhe/ose fjalëkalim i pasaktë"; "auth_invalid_password" = "Fjalëkalim shumë i shkurtër (min 6)"; "auth_invalid_email" = "Kjo s’duket si adresë email e vlefshme"; @@ -83,7 +83,7 @@ "room_creation_keep_private" = "Mbaje private"; "room_creation_make_private" = "Bëje private"; // Room recents -"room_recents_directory_section" = "DREJTORI DHOME"; +"room_recents_directory_section" = "DREJTORI DHOMASH"; "room_recents_directory_section_network" = "Rrjet"; "room_recents_people_section" = "PERSONA"; "room_recents_conversations_section" = "DHOMA"; @@ -116,16 +116,16 @@ "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" = "u gjetën >%tu përfundime për %@"; +"directory_search_results" = "U gjetën %tu përfundime për %@"; +"directory_search_results_more_than" = ">%tu përfundime të gjetura për %@"; "directory_searching_title" = "Po kërkohet në drejtori…"; "directory_search_fail" = "S’u arrit të sillen të dhëna"; // Contacts "contacts_address_book_section" = "KONTAKTE VENDORE"; "contacts_address_book_matrix_users_toggle" = "Vetëm përdorues të Matrix-it"; "contacts_address_book_no_contact" = "S’ka kontakte vendore"; -"contacts_user_directory_section" = "DREJTORI PËRDORUESI"; -"contacts_user_directory_offline_section" = "DREJTORI PËRDORUESI (jashtë linje)"; +"contacts_user_directory_section" = "DREJTORI PËRDORUESISH"; +"contacts_user_directory_offline_section" = "DREJTORI PËRDORUESISH (jashtë linje)"; // Chat participants "room_participants_title" = "Pjesëmarrës"; "room_participants_add_participant" = "Shtoni pjesmarrës"; @@ -138,7 +138,7 @@ "room_participants_invite_prompt_title" = "Ripohim"; "room_participants_invite_prompt_msg" = "Jeni i sigurt se doni të ftohet %@ te kjo fjalosje?"; "room_participants_filter_room_members" = "Filtroni anëtarë dhome"; -"room_participants_invite_malformed_id_title" = "Gabim Ftim"; +"room_participants_invite_malformed_id_title" = "Gabim Ftimi"; "room_participants_invited_section" = "TË FTUAR"; "room_participants_online" = "Në linjë"; "room_participants_offline" = "Jo në linjë"; @@ -319,7 +319,7 @@ // Group Home "group_home_one_member_format" = "1 anëtar"; "group_home_one_room_format" = "1 dhomë"; -"group_invitation_format" = "%s ju ftoi të bëheni pjesë e kësaj bashkësie"; +"group_invitation_format" = "%@ ju ftoi të bëheni pjesë e kësaj bashkësie"; // Group participants "group_participants_add_participant" = "Shtoni pjesmarrës"; "group_participants_leave_prompt_title" = "Braktiseni grupin"; @@ -351,7 +351,7 @@ "you" = "Ju"; "today" = "Sot"; "yesterday" = "Dje"; -"network_offline_prompt" = "Lidhja Internet duket se s’funksionin."; +"network_offline_prompt" = "Lidhja Internet duket se s’funksionon."; "public_room_section_title" = "Dhoma Publike (at %@):"; "do_not_ask_again" = "Mos pyet sërish"; // Call @@ -460,7 +460,7 @@ "call_jitsi_error" = "S'u arrit të hyhej në thirrjen për konferencë."; "widget_creation_failure" = "Krijimi i widget-it dështoi"; "widget_sticker_picker_no_stickerpacks_alert_add_now" = "Të shtohen ca tani?"; -"widget_integration_room_not_visible" = "Dhoma %s s’është e dukshme."; +"widget_integration_room_not_visible" = "Dhoma %@ s’është e dukshme."; "gdpr_consent_not_given_alert_review_now_action" = "Shqyrtojini tani"; "deactivate_account_title" = "Çaktivizoni Llogarinë"; "deactivate_account_informations_part2_emphasize" = "Ky veprim është i paprapakthyeshëm."; @@ -473,7 +473,7 @@ "rerequest_keys_alert_title" = "Kërkesa u Dërgua"; "auth_reset_password_success_message" = "Fjalëkalimi juaj u ri caktua.\n\nËshtë bërë dalja juaj nga llogaria në krejt pajisjet dhe s’do të merrni më njoftime push. Për riaktivizim të njoftimeve, ribëni hyrjen në çdo pajisje."; "room_creation_make_public_prompt_msg" = "Jeni i sigurt se doni ta bëni publike këtë fjalosje? Në një të tillë, mesazhet tuaj mund t’i lexojë cilido dhe mund të hyjë në bisedë."; -"room_creation_wait_for_creation" = "Po krijohet tashmë një dhomë, Ju lutemi, prisni."; +"room_creation_wait_for_creation" = "Po krijohet tashmë një dhomë. Ju lutemi, prisni."; "contacts_address_book_permission_required" = "Lypset leje për hyrje në kontaktet vendore"; "contacts_address_book_permission_denied" = "S’e lejuat Riot-i të hyjë në kontaktet tuaja vendore"; "room_participants_remove_third_party_invite_msg" = "Heqja e ftesave nga palë të treta nuk mbulohet ende, derisa të kihet API"; @@ -492,7 +492,7 @@ "room_resource_usage_limit_reached_message_1_monthly_active_user" = "Ky shërbyes home ka tejkaluar kufirin Përdorues Aktivë Mujorë, ndaj "; "unknown_devices_alert" = "Kjo dhomë përmban pajisje të panjohura që s’janë verifikuar.\nKjo do të thotë se nuk ka garanci se pajisjet u përkasin përdoruesve që pretendojnë se u përkasin.\nPërpara se të vazhdoni, këshillojmë që të kaloni në proces verifikimi çdo pajisje, por mund të ridërgoni mesazhin pa verifikuar gjë, nëse parapëlqeni kështu."; "room_preview_subtitle" = "Kjo është një paraparje e kësaj dhome. Ndërveprimet në dhomë janë çaktivizuar."; -"room_preview_unlinked_email_warning" = "Kjo ftesë i qe dërguar %@, që s’është i përshoqëruar me këtë llogari. Mund të doni të hyni me një llogari tjetër, ose ta shtoni këtë email te kjo llogari."; +"room_preview_unlinked_email_warning" = "Kjo ftesë i qe dërguar %@, që s’është i përshoqëruar me këtë llogari. Mund të doni të hyni me një llogari tjetër, ose ta shtoni këtë email te llogaria juaj."; "room_preview_try_join_an_unknown_room" = "Po përpiqeni të hyni në %@. Do të donit të bëheni pjesë, që të mundni të merrni pjesë te diskutimi?"; "settings_sign_out_e2e_warn" = "Do të humbni kyçet tuaj të fshehtëzimit skaj-më-skaj. Kjo do të thotë se s’do të jeni më në gjendje të lexoni mesazhe të vjetër te dhoma të fshehtëzuara në këtë pajisje."; "settings_surname" = "Mbiemër"; @@ -500,7 +500,7 @@ "settings_on_denied_notification" = "Njoftimet për %@ s’pranohen, ju lutemi, lejojini që nga rregullimet e pajisjes tuaj"; "settings_enable_callkit" = "Thirrje të integruara"; "settings_callkit_info" = "Merrini thirrjet ardhëse edhe me ekran të kyçur. Shihni thirrjet tuaja nën Riot te historiku i thirrjeve të sistemit. Nëse iCloud është i aktivizuar, ky historik thirrjesh do t’i jepet kompanisë Apple."; -"settings_ui_theme_picker_message" = "\"Auto\" përdor rregullimet Përmbysi Ngjyrat\" të pajisjes tuaj"; +"settings_ui_theme_picker_message" = "\"Auto\" përdor rregullimet \"Përmbysi Ngjyrat\" të pajisjes tuaj"; "settings_contacts_discover_matrix_users" = "Përdorni email-e dhe numra telefoni për të gjetur përdorues"; "settings_labs_e2e_encryption_prompt_message" = "Që të përfundohet rregullimi i fshehtëzimit duhet të ribëni hyrjen në llogari."; "settings_labs_room_members_lazy_loading" = "Lazy-load anëtarët e dhomave"; @@ -541,14 +541,14 @@ "e2e_room_key_request_message" = "Pajisja juaj e paverifikuar '%@' po kërkon kyçe fshehtëzimi."; // GDPR "gdpr_consent_not_given_alert_message" = "Që të vazhdohet të përdoret shërbyesi home %@, duhet të shqyrtoni dhe pajtoheni me termat dhe kushtet."; -"deactivate_account_informations_part1" = "Kjo do ta bëjë përgjithnjë të papërdorshëm llogarinë tuaj. Nuk do të jeni në gjendje të bëni hyrjen në të, dhe askush s’do të jetë në gjendje të rrregjistrohet me të njëjtën ID përdoruesi. Kjo do të sjellë daljen e llogarisë tuaj nga krejt dhomat në të cilat po merr pjesë, dhe do të heqë nga shërbyesi i identitetit tuaj hollësitë e llogarisë tuaj. "; +"deactivate_account_informations_part1" = "Kjo do ta bëjë përgjithnjë të papërdorshëm llogarinë tuaj. Nuk do të jeni në gjendje të bëni hyrjen në të, dhe askush s’do të jetë në gjendje të regjistrohet me të njëjtën ID përdoruesi. Kjo do të sjellë daljen e llogarisë tuaj nga krejt dhomat në të cilat po merr pjesë, dhe do të heqë nga shërbyesi i identitetit tuaj hollësitë e llogarisë tuaj. "; "deactivate_account_informations_part4_emphasize" = "si parazgjedhje, nuk na bën të harrojmë mesazhet që keni dërguar. "; "deactivate_account_informations_part5" = "Nëse do të donit që të harrohen mesazhet tuaj, ju lutemi, i vini shenjë kutizës më poshtë\n\nDukshmëria e mesazheve në Matrix është e ngjashme me atë në email. Harrimi i mesazheve nga ana jonë do të thotë që mesazhet që keni dërguar nuk do të ndahen me çfarëdo përdoruesi të ri apo të paregjistruar, por përdoruesit e regjistruar, që kanë tashmë hyrje në këto mesazhe, do të kenë prapëseprapë hyrje te kopja e tyre."; "deactivate_account_forget_messages_information_part1" = "Të lutem, harro krejt mesazhet që kamë dërguar, kur të çaktivizohet llogaria ime ("; "deactivate_account_forget_messages_information_part3" = ": kjo do të bëjë që përdorues të ardhshëm të shohin një pamje jo të plotë të bisedave)"; "rerequest_keys_alert_message" = "Ju lutemi, niseni Riot-in në një tjetër pajisje që mundet të shfshehtëzojë mesazhin, që kështu të mund të dërgojë kyçet te kjo pajisje."; "room_event_action_redact" = "Hiqe"; -"e2e_need_log_in_again" = "Që të prodhohen kyçe fshehtëzimi skaj-më-skaj për këtë pajisje, lypset të ribëni hyrjen dhe të parashtroni kyçin publik te shërbyesi juaj homë.\nKjo duhet vetëm një herë; na ndjeni për belanë."; +"e2e_need_log_in_again" = "Që të prodhohen kyçe fshehtëzimi skaj-më-skaj për këtë pajisje, lypset të ribëni hyrjen dhe të parashtroni kyçin publik te shërbyesi juaj Home.\nKjo duhet vetëm një herë; na ndjeni për belanë."; "room_action_send_sticker" = "Dërgoni ngjitës"; "settings_flair" = "Shfaq simbole, kur lejohet"; "room_details_flair_section" = "Shfaq simbole për bashkësi"; @@ -667,6 +667,8 @@ "key_backup_setup_intro_manual_export_action" = "Eksportoni kyçe dorazi"; // String for App Store "store_short_description" = "Fjalosje /VoIP e siguruar, e decentralizuar"; -"store_full_description" = "Komunikoni sipas rrugës tuaj.\n\nNjë aplikacion fjalosjesh, nën kontrollin tuaj dhe krejtësisht i zhdërvjellët. Riot-i ju lejon të komunikoni si doni. Krijuar për [matrix] - standardi për për komunikim të hapur, të decentralizuar.\n\nMerrni një llogari matrix.org falas, merrni shërbyesin tuaj te https://modular.im, ose përdorni një tjetër shërbyes Matrix.\n\nPse të zgjidhet Riot.im?\n\nKOMUNIKIM I PLOTË: Krijoni dhoma rreth ekipeve tuaja, shokëve tuaj, bashkësisë tuaj - rreth kujtdo që doni! Bisedoni, shkëmbeni kartela, shtoni widget-e dhe bëni thirrje audio dhe video - gjithçka falas.\n\nINTEGRIME TË FUQISHME: Përdoreni Riot.im me mjetet që njihni dhe doni. Me Riot.im mundeni madje të bisedoni me përdorues dhe grupe nën aplikacione të tjera fjalosjesh.\n\nPRIVAT DHE I SIGURUAR: Mbajini bisedat tuaja të fshehta. Fshehtëzimi skaj-më-skaj i fjalës së fundit garanton që komunikimi privat mbetet privat.\n\nI HAPUR, JO I MBYLLUR: Me burim të hapur dhe i ngritur mbi Matrix. Jini zot i të dhënave tuaja, duke strehuar shërbyesin tuaj, ose duke përzgjedhur një të cilit i besoni.\n\nKUDO QOFSHI: Mbani lidhjet, kudo qofshi, me historik plotësisht të njëkohësuar mesazhesh në krejt pajisjet tuaja dhe në internet, te https://riot.im."; +"store_full_description" = "Komunikoni sipas rrugës tuaj.\n\nNjë aplikacion fjalosjesh, nën kontrollin tuaj dhe krejtësisht i zhdërvjellët. Riot-i ju lejon të komunikoni si doni. Krijuar për [matrix] - standardi për komunikim të hapur, të decentralizuar.\n\nMerrni një llogari matrix.org falas, merrni shërbyesin tuaj te https://modular.im, ose përdorni një tjetër shërbyes Matrix.\n\nPse të zgjidhet Riot.im?\n\nKOMUNIKIM I PLOTË: Krijoni dhoma rreth ekipeve tuaja, shokëve tuaj, bashkësisë tuaj - rreth kujtdo që doni! Bisedoni, shkëmbeni kartela, shtoni widget-e dhe bëni thirrje audio dhe video - gjithçka falas.\n\nINTEGRIME TË FUQISHME: Përdoreni Riot.im me mjetet që njihni dhe doni. Me Riot.im mundeni madje të bisedoni me përdorues dhe grupe nën aplikacione të tjera fjalosjesh.\n\nPRIVAT DHE I SIGURUAR: Mbajini bisedat tuaja të fshehta. Fshehtëzimi skaj-më-skaj i fjalës së fundit garanton që komunikimi privat mbetet privat.\n\nI HAPUR, JO I MBYLLUR: Me burim të hapur dhe i ngritur mbi Matrix. Jini zot i të dhënave tuaja, duke strehuar shërbyesin tuaj, ose duke përzgjedhur një të cilit i besoni.\n\nKUDO QOFSHI: Mbani lidhjet, kudo qofshi, me historik plotësisht të njëkohësuar mesazhesh në krejt pajisjet tuaja dhe në internet, te https://riot.im."; "auth_login_single_sign_on" = "Hyni përmes një hyrjeje njëshe"; "room_message_unable_open_link_error_message" = "S’arrihet të hapet lidhja."; +"auth_autodiscover_invalid_response" = "Përgjigje e pavlefshme pikasjeje shërbyesi Home"; +"room_details_fail_to_update_room_direct" = "S’arrihet të përditësohet shenja si e drejtpërdrejtë e kësaj dhome"; From 027e9d89d47c69dc49f6f479e30a162688ce89f9 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Tue, 19 Mar 2019 17:31:08 +0000 Subject: [PATCH 010/266] Translated using Weblate (Basque) Currently translated at 100.0% (618 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eu/ --- Riot/Assets/eu.lproj/Vector.strings | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index c8a433c966..9239d91ec7 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -304,7 +304,7 @@ "room_title_members" = "%@ kide"; // Room Preview "room_preview_invitation_format" = "%@ erabiltzaileak gela honetara elkartzera gonbidatu zaitu"; -"room_preview_unlinked_email_warning" = "Zure gonbidapena bidali da %@ helbidera, hau ez dago kontu honetara lotuta.\nBeste kontu batekin hasi dezakezu saioa, edo e-mail hau kontu honetara gehitu."; +"room_preview_unlinked_email_warning" = "Zure gonbidapena %@ helbidera bidali da, hau ez dago kontu honetara lotuta.\nBeste kontu batekin hasi dezakezu saioa, edo e-mail hau zure kontura gehitu."; "room_preview_try_join_an_unknown_room" = "%@ gelan sartzen saiatzen ari zara. Elkartu nahi duzu elkarrizketan parte hartzeko?"; // Settings "settings_title" = "Ezarpenak"; @@ -477,7 +477,7 @@ // Groups tab "group_invite_section" = "GONBIDAPENAK"; "group_section" = "KOMUNITATEAK"; -"settings_flair" = "Erakutsi ikurra non baimenduta"; +"settings_flair" = "Erakutsi ikurra baimenduta dagoen tokietan"; "room_details_flair_section" = "Erakutsi ikurra komunitateentzat"; "room_details_new_flair_placeholder" = "Gehitu komunitate ID berria (adib. +foo%@)"; "room_details_flair_invalid_id_prompt_title" = "Baliogabeko formatua"; @@ -657,3 +657,9 @@ "key_backup_setup_intro_setup_action_with_existing_backup" = "Erabili gakoen-babes-kopia"; "key_backup_setup_intro_manual_export_info" = "(Aurreratua)"; "key_backup_setup_intro_manual_export_action" = "Esportatu gakoak eskuz"; +// String for App Store +"store_short_description" = "Deszentralizatutako txat/VoIP segurua"; +"store_full_description" = "Komunikatu, zure erara.\n\nTxat aplikazio bat, zure kontrolpean eta erabat malgua. Riot-ek zuk nahi eran komunikatzea ahalbidetzen dizu. Komunikaziorako estandar ireki eta deszentralizatua den [matrix] protokolorako egina.\n\nEskuratu matrix.org kontu bat, ezarri zure zerbitzaria https://modular.im erabiliz, edo erabili beste Matrix zerbitzari bat.\n\nZertgatik aukeratu Riot.im?\n\nERABATEKO KOMUNIKAZIOA: Sortu zure taldeentzako gelak, zure lagunentzko, zure komunitatearentzat, zuk nahi eran! Txateatu, partekatu fitxategiak, gehitu trepetak eta egin ahots eta bideo-deiak, dena doan.\n\nINTEGRAZIO AHALTSUAK: Erabili Riot.im jada ezagutu eta maite dituzun tresnekin. Riot.im erabilita beste txat aplikazioak erabiltzen dituzten erabiltzaile eta taldeekin ere aritu zaitezke.\n\nPRIBATUA ETA SEGURUA: Mantendu zure elkarrizketak sekretupean. Gaur egungo muturretik muturrerako zifratzeak pribatua dena pribatu mantentzen laguntzen du.\n\nIREKIA, EZ ITXIA: Kode irekikoa, eta Matrix protokoloan eraikia. Izan zure datuen jabe zure zerbitzaria osatatauz, edo fidagarritzat duzun bat aukeratuz.\n\nZUREKIN NONAHI: Mantendu kontaktua zauden tokian zaudela gailuetan zehar erabat sinkronizatutako mezuen historialarekin, eta web bidez https://riot.im helbidean."; +"auth_login_single_sign_on" = "Hasi saioa urrats batean"; +"auth_autodiscover_invalid_response" = "Erantzun baliogabea hasiera-zerbitzarien bilaketari"; +"room_message_unable_open_link_error_message" = "Ezin izan da esteka ireki."; From fcb9032107e1765eb70dc374e9d81d21d8272771 Mon Sep 17 00:00:00 2001 From: tea Date: Sun, 24 Mar 2019 14:43:46 +0000 Subject: [PATCH 011/266] Translated using Weblate (Italian) Currently translated at 100.0% (26 of 26 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/it/ --- Riot/Assets/it.lproj/Localizable.strings | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/Riot/Assets/it.lproj/Localizable.strings b/Riot/Assets/it.lproj/Localizable.strings index 3ca46524de..daa2aefdd6 100644 --- a/Riot/Assets/it.lproj/Localizable.strings +++ b/Riot/Assets/it.lproj/Localizable.strings @@ -28,3 +28,25 @@ "MSGS_FROM_THREE_USERS" = "%@ nuovi messaggi da %@, %@ e %@"; /* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */ "MSGS_FROM_TWO_PLUS_USERS" = "%@ nuovi messaggi da %@, %@ e altri"; +/* Multiple messages in two rooms */ +"MSGS_IN_TWO_ROOMS" = "%@ nuovi messaggi in %@ e %@"; +/* Look, stuff's happened, alright? Just open the app. */ +"MSGS_IN_TWO_PLUS_ROOMS" = "%@ nuovi messaggi in %@, %@ e altre"; +/* A user has invited you to a chat */ +"USER_INVITE_TO_CHAT" = "%@ ti ha invitato a chattare"; +/* A user has invited you to an (unamed) group chat */ +"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ ti ha invitato in una chat di gruppo"; +/* A user has invited you to a named room */ +"USER_INVITE_TO_NAMED_ROOM" = "%@ ti ha invitato in %@"; +/* Incoming one-to-one voice call */ +"VOICE_CALL_FROM_USER" = "Chiamata da %@"; +/* Incoming one-to-one video call */ +"VIDEO_CALL_FROM_USER" = "Video chiamata da %@"; +/* Incoming unnamed voice conference invite from a specific person */ +"VOICE_CONF_FROM_USER" = "Chiamata di gruppo da %@"; +/* Incoming unnamed video conference invite from a specific person */ +"VIDEO_CONF_FROM_USER" = "Video chiamata di gruppo da %@"; +/* Incoming named voice conference invite from a specific person */ +"VOICE_CONF_NAMED_FROM_USER" = "Chiamata di gruppo da %@: '%@'"; +/* Incoming named video conference invite from a specific person */ +"VIDEO_CONF_NAMED_FROM_USER" = "Video chiamata di gruppo da %@: '%@'"; From aacb3b527b22b54f21695b21fdf2a0e04ce68dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0?= Date: Sun, 17 Mar 2019 15:31:23 +0000 Subject: [PATCH 012/266] Translated using Weblate (Catalan) Currently translated at 83.8% (518 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/ca/ --- Riot/Assets/ca.lproj/Vector.strings | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/ca.lproj/Vector.strings b/Riot/Assets/ca.lproj/Vector.strings index 9bb795aac6..5d99aa97cd 100644 --- a/Riot/Assets/ca.lproj/Vector.strings +++ b/Riot/Assets/ca.lproj/Vector.strings @@ -216,7 +216,7 @@ "room_delete_unsent_messages" = "Esborrar missatges no enviats"; "room_event_action_copy" = "Copiar"; "room_event_action_quote" = "Cita"; -"room_event_action_redact" = "Redactar"; +"room_event_action_redact" = "Eliminar"; "room_event_action_more" = "Més"; "room_event_action_share" = "Comparteix"; "room_event_action_permalink" = "Permalink"; @@ -250,7 +250,7 @@ // Room Preview "room_preview_invitation_format" = "L'usuari %@ t'ha convidat a unir-te a aquesta sala"; "room_preview_subtitle" = "Aquesta es una previsualització d'aquesta sala. Les interaccions amb la sala estan deshabilitades."; -"room_preview_unlinked_email_warning" = "L'invitació s'ha enviat a %@, però no està associat amb aquest compte. Potser vols iniciar la sessió amb un compte diferent o afegir aquesta adreça de correu al teu compte actual."; +"room_preview_unlinked_email_warning" = "La invitació s'ha enviat a %@, però no està associat amb aquest compte. Potser voleu iniciar la sessió amb un compte diferent o afegir aquesta adreça de correu al vostre compte actual."; "room_preview_try_join_an_unknown_room" = "Estàs provant de accedir a %@. T'agradaria unir-te per a poder participar en el debat?"; "room_preview_try_join_an_unknown_room_default" = "una sala"; // Settings @@ -520,7 +520,7 @@ "room_replacement_information" = "Aquesta sala s'ha substituït i ja no està activa."; "room_replacement_link" = "La conversa continua aquí."; "room_predecessor_information" = "Aquesta sala és una continuació d'una altra conversa."; -"room_predecessor_link" = "Fes clic aquí per veure missatges antics."; +"room_predecessor_link" = "Premeu aquí per veure missatges més antics."; "room_resource_limit_exceeded_message_contact_1" = " Si us plau "; "room_resource_limit_exceeded_message_contact_2_link" = "Posa't en contacte amb l'administrador del servei"; "room_resource_limit_exceeded_message_contact_3" = " per a continuar usant aquest servei."; From 35a5d2a04da17f3c26275cb9a8c8357147370a7e Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Thu, 28 Mar 2019 12:48:50 +0000 Subject: [PATCH 013/266] Translated using Weblate (Dutch) Currently translated at 100.0% (618 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/nl/ --- Riot/Assets/nl.lproj/Vector.strings | 664 ++++++++++++++++------------ 1 file changed, 390 insertions(+), 274 deletions(-) diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index aed22a6bf2..c4c986498d 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -15,17 +15,17 @@ */ // Titles -"title_home" = "Home"; +"title_home" = "Thuis"; "title_favourites" = "Favorieten"; "title_people" = "Personen"; -"title_rooms" = "Ruimtes"; +"title_rooms" = "Kamers"; "warning" = "Waarschuwing"; // Actions "view" = "Weergeven"; "next" = "Volgende"; "back" = "Terug"; "continue" = "Verder gaan"; -"create" = "Maak"; +"create" = "Aanmaken"; "start" = "Starten"; "leave" = "Verlaten"; "remove" = "Verwijderen"; @@ -37,23 +37,23 @@ "save" = "Opslaan"; "join" = "Toetreden"; "decline" = "Afwijzen"; -"accept" = "Accepteren"; +"accept" = "Aanvaarden"; "preview" = "Voorvertonen"; "camera" = "Camera"; "voice" = "Spraak"; "video" = "Video"; -"active_call" = "Actief Gesprek"; -"active_call_details" = "Actief gesprek (%@)"; +"active_call" = "Actieve oproep"; +"active_call_details" = "Actieve oproep (%@)"; "later" = "Later"; -"rename" = "Herbenoemen"; +"rename" = "Hernoemen"; // Authentication -"auth_login" = "Inloggen"; +"auth_login" = "Aanmelden"; "auth_register" = "Registreren"; "auth_submit" = "Versturen"; "auth_skip" = "Overslaan"; -"auth_send_reset_email" = "Stuur Herstel E-mail"; -"auth_return_to_login" = "Ga terug naar het herstel scherm"; -"auth_user_id_placeholder" = "E-mail of gebruikersnaam"; +"auth_send_reset_email" = "Herstel-e-mail versturen"; +"auth_return_to_login" = "Terug naar het aanmeldingsscherm"; +"auth_user_id_placeholder" = "E-mailadres of gebruikersnaam"; "auth_password_placeholder" = "Wachtwoord"; "auth_new_password_placeholder" = "Nieuw wachtwoord"; "auth_user_name_placeholder" = "Gebruikersnaam"; @@ -62,112 +62,112 @@ "auth_optional_phone_placeholder" = "Telefoonnummer (optioneel)"; "auth_phone_placeholder" = "Telefoonnummer"; "auth_repeat_password_placeholder" = "Wachtwoord herhalen"; -"auth_repeat_new_password_placeholder" = "Bevestig je nieuwe wachtwoord"; -"auth_invalid_login_param" = "Incorrecte gebruikersnaam en/of wachtwoord"; -"auth_invalid_user_name" = "Gebruikersnamen mogen alleen letters, cijfers, punten, afbreekstreepjes en lage streepjes bevatten"; +"auth_repeat_new_password_placeholder" = "Bevestig uw nieuwe wachtwoord"; +"auth_invalid_login_param" = "Onjuiste gebruikersnaam en/of wachtwoord"; +"auth_invalid_user_name" = "Gebruikersnamen mogen alleen letters, cijfers, punten, afbreek- en lage streepjes bevatten"; "auth_invalid_password" = "Het wachtwoord is te kort (min 6)"; "auth_invalid_email" = "Dit ziet er niet uit als een geldig e-mailadres"; "auth_invalid_phone" = "Dit ziet er niet uit als een geldig telefoonnummer"; -"auth_missing_password" = "Missend wachtwoord"; -"auth_add_email_message" = "Voeg een e-mailadres aan je account toe zodat gebruikers je kunnen vinden en je het wachtwoord kunt herstellen."; -"auth_add_phone_message" = "Voeg een telefoonnummer aan je account toe zodat gebruikers je kunnen vinden."; -"auth_add_email_phone_message" = "Voeg een e-mailadres en/of een telefoonnummer aan je account toe zodat gebruikers je kunnen vinden. Je e-mailadres maakt het ook mogelijk om je wachtwoord te herstellen."; -"auth_add_email_and_phone_message" = "Voeg een e-mailadres en een telefoonnummer aan je account toe zodat gebruikers je kunnen vinden. Je e-mailadres maakt het ook mogelijk om je wachtwoord te herstellen."; -"auth_missing_email" = "Het e-mailadres mist"; -"auth_missing_phone" = "Het telefoonnummer mist"; -"auth_missing_email_or_phone" = "Het e-mailadres of telefoonnummer mist"; +"auth_missing_password" = "Wachtwoord ontbreekt"; +"auth_add_email_message" = "Voeg een e-mailadres aan uw account toe zodat gebruikers u kunnen vinden en u uw wachtwoord kunt herstellen."; +"auth_add_phone_message" = "Voeg een telefoonnummer aan uw account toe zodat gebruikers uw kunnen vinden."; +"auth_add_email_phone_message" = "Voeg een e-mailadres en/of telefoonnummer aan uw account toe zodat gebruikers u kunnen vinden. Uw e-mailadres maakt het ook mogelijk om uw wachtwoord te herstellen."; +"auth_add_email_and_phone_message" = "Voeg een e-mailadres en een telefoonnummer aan uw account toe zodat gebruikers u kunnen vinden. Uw e-mailadres maakt het ook mogelijk om uw wachtwoord te herstellen."; +"auth_missing_email" = "E-mailadres ontbreekt"; +"auth_missing_phone" = "Telefoonnummer ontbreekt"; +"auth_missing_email_or_phone" = "E-mailadres of telefoonnummer ontbreekt"; "auth_password_dont_match" = "De wachtwoorden komen niet overeen"; "auth_username_in_use" = "De gebruikersnaam is al in gebruik"; "auth_forgot_password" = "Wachtwoord vergeten?"; -"auth_use_server_options" = "Gebruik alternatieve serverinstellingen (geavanceerd)"; -"auth_email_validation_message" = "Bekijk je e-mail om verder te gaan met de registratie"; -"auth_msisdn_validation_title" = "Verificatie Aanhangig"; -"auth_msisdn_validation_message" = "We hebben een SMS met een activatie code gestuurd. Vul deze code hieronder in."; -"auth_msisdn_validation_error" = "Niet mogelijk om het telefoonnummer te verifiëren."; -"auth_recaptcha_message" = "Deze Thuisserver wil er graag zeker van zijn dat je geen robot bent"; -"auth_reset_password_message" = "Om je wachtwoord te herstellen, voer het e-mailadres dat met je account verbonden is in:"; -"auth_reset_password_missing_email" = "Het e-mailadres dat met je account verbonden is moet ingevoerd worden."; -"auth_reset_password_missing_password" = "Een nieuw wachtwoord moet ingevoerd worden."; -"auth_reset_password_email_validation_message" = "Een e-mail is naar %@ gestuurd. Zodra je de link die het bevat hebt gevolgd, klik hieronder."; +"auth_use_server_options" = "Aangepaste serverinstellingen gebruiken (geavanceerd)"; +"auth_email_validation_message" = "Bekijk uw e-mail om verder te gaan met de registratie"; +"auth_msisdn_validation_title" = "Verificatie in afwachting"; +"auth_msisdn_validation_message" = "We hebben een sms met een activatiecode gestuurd. Voer deze code hieronder in."; +"auth_msisdn_validation_error" = "Kan het telefoonnummer niet verifiëren."; +"auth_recaptcha_message" = "Deze thuisserver wil er graag zeker van zijn dat u geen robot bent"; +"auth_reset_password_message" = "Voer het e-mailadres dat met uw account verbonden is in om uw wachtwoord opnieuw in te stellen:"; +"auth_reset_password_missing_email" = "Het e-mailadres dat met uw account verbonden is moet ingevoerd worden."; +"auth_reset_password_missing_password" = "Er moet een nieuw wachtwoord ingevoerd worden."; +"auth_reset_password_email_validation_message" = "Er is een e-mail naar %@ gestuurd. Klik hieronder zodra u de koppeling erin hebt gevolgd."; "auth_reset_password_next_step_button" = "Ik heb mijn e-mailadres geverifieerd"; -"auth_reset_password_error_unauthorized" = "Het is niet gelukt om het e-mailadres te verifiëren: wees er zeker van dat je op de link in de e-mail hebt geklikt"; -"auth_reset_password_error_not_found" = "Het ziet er niet naar uit dat het e-mailadres met het Matrix ID op deze homeserver is verbonden."; -"auth_reset_password_success_message" = "Je wachtwoord is opnieuw ingesteld.\n\nJe bent op alle apparaten uitgelogd en je zal geen notificaties meer ontvangen. Om notificaties weer in te schakelen, log op elk apparaat opnieuw in."; -"auth_add_email_and_phone_warning" = "Registratie met e-mail en telefoonnummer tegelijkertijd wordt nog niet ondersteund totdat de api bestaat. Alleen het telefoonnummer zal worden gebruikt. Je kan je e-mailadres later aan je profiel in de instellingen toevoegen."; +"auth_reset_password_error_unauthorized" = "Verifiëren van e-mailadres is mislukt: wees er zeker van dat u op de koppeling in de e-mail hebt getikt"; +"auth_reset_password_error_not_found" = "Het ziet er niet naar uit dat uw e-mailadres met een Matrix-ID op deze thuisserver is verbonden."; +"auth_reset_password_success_message" = "Uw wachtwoord is opnieuw ingesteld.\n\nU bent op alle apparaten afgemeld en u zult geen pushmeldingen meer ontvangen. Om meldingen weer in te schakelen, meldt u zich op elk apparaat opnieuw aan."; +"auth_add_email_and_phone_warning" = "Registratie met e-mailadres en telefoonnummer tegelijkertijd wordt, totdat de API bestaat, nog niet ondersteund. Alleen het telefoonnummer zal worden gebruikt. U kunt uw e-mailadres later aan uw profiel in de instellingen toevoegen."; // Chat creation -"room_creation_title" = "Niew gesprek"; +"room_creation_title" = "Nieuw gesprek"; "room_creation_account" = "Account"; "room_creation_appearance" = "Uiterlijk"; "room_creation_appearance_name" = "Naam"; "room_creation_appearance_picture" = "Gespreksafbeelding (optioneel)"; "room_creation_privacy" = "Privacy"; "room_creation_private_room" = "Dit gesprek is privé"; -"room_creation_public_room" = "Dit gesprek ik publiek"; -"room_creation_make_public" = "Maak publiek"; +"room_creation_public_room" = "Dit gesprek is publiek"; +"room_creation_make_public" = "Publiek maken"; "room_creation_make_public_prompt_title" = "Dit gesprek publiek maken?"; -"room_creation_make_public_prompt_msg" = "Weet je zeker dat je dit gesprek publiek wilt maken? Iedereen kan je berichten lezen en aan het gesprek deelnemen."; +"room_creation_make_public_prompt_msg" = "Weet u zeker dat u dit gesprek publiek wilt maken? Iedereen kan uw berichten lezen en aan het gesprek deelnemen."; "room_creation_keep_private" = "Privé houden"; "room_creation_make_private" = "Privé maken"; -"room_creation_wait_for_creation" = "Er wordt al een ruimte aangemaakt. Even geduld alstublieft."; -"room_creation_invite_another_user" = "Zoek / nodig uit bij Gebruikers ID, Naam of e-mail"; +"room_creation_wait_for_creation" = "Er wordt al een gesprek aangemaakt. Even geduld."; +"room_creation_invite_another_user" = "Zoeken/uitnodigen met gebruikers-ID, naam of e-mailadres"; // Room recents -"room_recents_directory_section" = "RUIMTE ADRESBOEK"; +"room_recents_directory_section" = "GESPREKSCATALOGUS"; "room_recents_directory_section_network" = "Netwerk"; "room_recents_favourites_section" = "FAVORIETEN"; "room_recents_people_section" = "PERSONEN"; -"room_recents_conversations_section" = "RUIMTES"; -"room_recents_no_conversation" = "Geen ruimtes"; +"room_recents_conversations_section" = "KAMERS"; +"room_recents_no_conversation" = "Geen kamers"; "room_recents_low_priority_section" = "LAGE PRIORITEIT"; "room_recents_invites_section" = "UITNODIGINGEN"; -"room_recents_start_chat_with" = "Start gesprek"; -"room_recents_create_empty_room" = "Creeër ruimte"; -"room_recents_join_room" = "Aan de ruimte deelnemen"; -"room_recents_join_room_title" = "Neem aan een ruimte deel"; -"room_recents_join_room_prompt" = "Typ een ruimte id of naam in"; +"room_recents_start_chat_with" = "Gesprek beginnen"; +"room_recents_create_empty_room" = "Gesprek aanmaken"; +"room_recents_join_room" = "Gesprek toetreden"; +"room_recents_join_room_title" = "Neem aan een gesprek deel"; +"room_recents_join_room_prompt" = "Voer een gespreks-ID of -alias in"; // People tab "people_invites_section" = "UITNODIGINGEN"; "people_conversation_section" = "GESPREKKEN"; "people_no_conversation" = "Geen gesprekken"; // Rooms tab -"room_directory_no_public_room" = "Geen publieke ruimtes beschikbaar"; +"room_directory_no_public_room" = "Geen publieke kamers beschikbaar"; // Search -"search_rooms" = "Ruimtes"; +"search_rooms" = "Kamers"; "search_messages" = "Berichten"; "search_people" = "Personen"; "search_files" = "Bestanden"; "search_default_placeholder" = "Zoeken"; -"search_people_placeholder" = "Zoek bij Gebruikers ID, Naam of e-mail"; +"search_people_placeholder" = "Zoeken op gebruikers-ID, naam of e-mailadres"; "search_no_result" = "Geen resultaten"; // Directory -"directory_cell_title" = "Blader door de catalogus"; -"directory_cell_description" = "%tu ruimtes"; -"directory_search_results_title" = "Blader door catalogus resultaten"; +"directory_cell_title" = "Bladeren door de catalogus"; +"directory_cell_description" = "%tu kamers"; +"directory_search_results_title" = "Bladeren door catalogusresultaten"; "directory_search_results" = "%tu resultaten gevonden voor %@"; "directory_search_results_more_than" = ">%tu resultaten gevonden voor %@"; -"directory_searching_title" = "Catalogus afzoeken…"; -"directory_search_fail" = "Niet gelukt om data op te halen"; +"directory_searching_title" = "Catalogus wordt doorzocht…"; +"directory_search_fail" = "Ophalen van gegevens is mislukt"; // Contacts "contacts_address_book_section" = "LOKALE CONTACTEN"; -"contacts_address_book_matrix_users_toggle" = "Alleen Matrix gebruikers"; +"contacts_address_book_matrix_users_toggle" = "Alleen Matrix-gebruikers"; "contacts_address_book_no_contact" = "Geen lokale contacten"; -"contacts_address_book_permission_required" = "Permissie vereist voor toegang tot de lokale contacten"; -"contacts_address_book_permission_denied" = "Je hebt Riot geen toegang tot je lokale contacten toegestaan"; +"contacts_address_book_permission_required" = "Toestemming vereist voor toegang tot de lokale contacten"; +"contacts_address_book_permission_denied" = "U heeft Riot geen toegang tot uw lokale contacten verleend"; // Chat participants "room_participants_title" = "Deelnemers"; "room_participants_add_participant" = "Deelnemer toevoegen"; "room_participants_one_participant" = "1 deelnemer"; "room_participants_multi_participants" = "%d deelnemers"; -"room_participants_leave_prompt_title" = "Ruimte verlaten"; -"room_participants_leave_prompt_msg" = "Weet je zeker dat je de ruimte wilt verlaten?"; +"room_participants_leave_prompt_title" = "Gesprek verlaten"; +"room_participants_leave_prompt_msg" = "Weet u zeker dat u het gesprek wilt verlaten?"; "room_participants_remove_prompt_title" = "Bevestiging"; -"room_participants_remove_prompt_msg" = "Weet je zeker dat je %@ van dit gesprek wilt verwijderen?"; -"room_participants_remove_third_party_invite_msg" = "Verwijderen derde-partij uitnodiging is nog niet ondersteund tot de api bestaat"; +"room_participants_remove_prompt_msg" = "Weet u zeker dat u %@ uit dit gesprek wilt verwijderen?"; +"room_participants_remove_third_party_invite_msg" = "Verwijderen van uitnodigingen door derde partijen wordt, totdat de API bestaat, nog niet ondersteund"; "room_participants_invite_prompt_title" = "Bevestiging"; -"room_participants_invite_prompt_msg" = "Weet je zeker dat je %@ voor dit gesprek wilt uitnodigen?"; -"room_participants_filter_room_members" = "Filter ruimteleden"; -"room_participants_invite_another_user" = "Zoek / nodig uit bij Gebruikers ID, Naam of e-mail"; +"room_participants_invite_prompt_msg" = "Weet u zeker dat u %@ in dit gesprek wilt uitnodigen?"; +"room_participants_filter_room_members" = "Filter gespreksleden"; +"room_participants_invite_another_user" = "Zoeken/uitnodigen met gebruikers-ID, naam of e-mailadres"; "room_participants_invite_malformed_id_title" = "Uitnodigingsfout"; -"room_participants_invite_malformed_id" = "Ongeldig ID. Het hoort een e-mailadres of een Matrix ID zoals '@localpart:domain' te zijn"; +"room_participants_invite_malformed_id" = "Ongeldige ID. Dit zou een e-mailadres of een Matrix-ID zoals ‘@gebruikersnaam:domein’ moeten zijn"; "room_participants_invited_section" = "UITGENODIGD"; "room_participants_online" = "Online"; "room_participants_offline" = "Offline"; @@ -175,122 +175,122 @@ "room_participants_idle" = "Afwezig"; "room_participants_now" = "nu"; "room_participants_ago" = "geleden"; -"room_participants_action_section_admin_tools" = "Beheerder gereedschappen"; -"room_participants_action_section_direct_chats" = "Directe gesprekken"; +"room_participants_action_section_admin_tools" = "Beheerdersgereedschap"; +"room_participants_action_section_direct_chats" = "Tweegesprekken"; "room_participants_action_section_devices" = "Apparaten"; -"room_participants_action_section_other" = "Anders"; -"room_participants_action_invite" = "Nodig uit"; -"room_participants_action_leave" = "Deze ruimte verlaten"; -"room_participants_action_remove" = "Verwijder van deze ruimte"; -"room_participants_action_ban" = "Verban uit deze ruimte"; -"room_participants_action_unban" = "Ontban"; -"room_participants_action_ignore" = "Verberg alle berichten van deze gebruiker"; -"room_participants_action_unignore" = "Toon alle berichten van deze gebruiker"; +"room_participants_action_section_other" = "Overige"; +"room_participants_action_invite" = "Uitnodigen"; +"room_participants_action_leave" = "Dit gesprek verlaten"; +"room_participants_action_remove" = "Verwijderen uit dit gesprek"; +"room_participants_action_ban" = "Verbannen uit dit gesprek"; +"room_participants_action_unban" = "Ontbannen"; +"room_participants_action_ignore" = "Alle berichten van deze gebruiker verbergen"; +"room_participants_action_unignore" = "Alle berichten van deze gebruiker tonen"; "room_participants_action_set_default_power_level" = "Terugzetten naar normale gebruiker"; -"room_participants_action_set_moderator" = "Maak moderator"; -"room_participants_action_set_admin" = "Maak admin"; -"room_participants_action_start_new_chat" = "Start nieuw gesprek"; -"room_participants_action_start_voice_call" = "Start spraakoproep"; -"room_participants_action_start_video_call" = "Start video-oproep"; +"room_participants_action_set_moderator" = "Benoemen tot moderator"; +"room_participants_action_set_admin" = "Benoemen tot beheerder"; +"room_participants_action_start_new_chat" = "Nieuw gesprek beginnen"; +"room_participants_action_start_voice_call" = "Spraakoproep beginnen"; +"room_participants_action_start_video_call" = "Video-oproep beginnen"; "room_participants_action_mention" = "Vermelden"; // Chat "room_jump_to_first_unread" = "Spring naar het eerste ongelezen bericht"; "room_new_message_notification" = "%d nieuw bericht"; "room_new_messages_notification" = "%d nieuwe berichten"; "room_one_user_is_typing" = "%@ is aan het typen…"; -"room_two_users_are_typing" = "%@ & %@ zijn aan het typen…"; -"room_many_users_are_typing" = "%@, %@ & anderen zijn aan het typen…"; +"room_two_users_are_typing" = "%@ en %@ zijn aan het typen…"; +"room_many_users_are_typing" = "%@, %@ en anderen zijn aan het typen…"; "room_message_placeholder" = "Stuur een bericht (onversleuteld)…"; "encrypted_room_message_placeholder" = "Stuur een versleuteld bericht…"; "room_message_short_placeholder" = "Stuur een bericht…"; -"room_offline_notification" = "De verbinding met de server is verloren."; -"room_unsent_messages_notification" = "Berichten niet verstuurd. %@ of %@ nu?"; -"room_unsent_messages_unknown_devices_notification" = "Bericht is niet verstuurd doordat onbekende apparaten aanwezig zijn. %@ of %@ nu?"; -"room_ongoing_conference_call" = "Er is een vergadergesprek gaande. Deelnemen als %@ of %@."; -"room_prompt_resend" = "Alles opnieuw versturen"; -"room_prompt_cancel" = "Alles annuleren"; -"room_resend_unsent_messages" = "Niet verstuurde berichten opnieuw versturen"; -"room_delete_unsent_messages" = "Niet verstuurde berichten verwijderen"; +"room_offline_notification" = "De verbinding met de server is verbroken."; +"room_unsent_messages_notification" = "Berichten niet verstuurd. Nu %@ of %@?"; +"room_unsent_messages_unknown_devices_notification" = "Bericht is niet verstuurd doordat er onbekende apparaten aanwezig zijn. Nu %@ of %@?"; +"room_ongoing_conference_call" = "Er is een vergadergesprek gaande. Neem deel met %@ of %@."; +"room_prompt_resend" = "alles opnieuw versturen"; +"room_prompt_cancel" = "alles annuleren"; +"room_resend_unsent_messages" = "Onverstuurde berichten opnieuw versturen"; +"room_delete_unsent_messages" = "Onverstuurde berichten verwijderen"; "room_event_action_copy" = "Kopiëren"; -"room_event_action_quote" = "Citeer"; +"room_event_action_quote" = "Citeren"; "room_event_action_redact" = "Verwijderen"; "room_event_action_more" = "Meer"; "room_event_action_share" = "Delen"; "room_event_action_permalink" = "Permalink"; -"room_event_action_view_source" = "Bekijk bron"; +"room_event_action_view_source" = "Bron weergeven"; "room_event_action_report" = "Inhoud melden"; -"room_event_action_report_prompt_reason" = "Reden voor het aangeven van deze content"; -"room_event_action_report_prompt_ignore_user" = "Wil je alle berichten van deze gebruiker verbergen?"; +"room_event_action_report_prompt_reason" = "Reden voor het melden van deze inhoud"; +"room_event_action_report_prompt_ignore_user" = "Wilt u alle berichten van deze gebruiker verbergen?"; "room_event_action_save" = "Opslaan"; "room_event_action_resend" = "Opnieuw versturen"; "room_event_action_delete" = "Verwijderen"; "room_event_action_cancel_upload" = "Uploaden annuleren"; "room_event_action_cancel_download" = "Downloaden annuleren"; "room_event_action_view_encryption" = "Versleutelingsinformatie"; -"room_warning_about_encryption" = "End-to-endbeveiliging is in bèta en kan onbetrouwbaar zijn.\n\nHet is beter om het nog niet met gevoelige gegevens te vertrouwen.\n\nApparaten kunnen de geschiedenis van voordat ze de ruimte betraden nog niet ontsleutelen.\n\nVersleutelde berichten zullen nog niet zichtbaar zijn op programma's die geen versleuteling ondersteunen."; +"room_warning_about_encryption" = "Eind-tot-eindversleuteling is in bèta en kan onbetrouwbaar zijn.\n\nHet is beter om het nog niet met gevoelige gegevens te vertrouwen.\n\nApparaten kunnen de geschiedenis van vóór ze het gesprek betraden nog niet ontsleutelen.\n\nVersleutelde berichten zullen niet zichtbaar zijn op cliënten die nog geen versleuteling ondersteunen."; // Unknown devices -"unknown_devices_alert_title" = "Ruimte bevat onbekende apparaten"; -"unknown_devices_alert" = "Deze ruimte bevat onbekende apparaten die niet geen geverifieerd.\nDit betekent dat er geen garantie is dat de apparaten bij de gebruikers horen waar het beweert dat het bij hoort.\nWe raden je aan om bij elk apparaat door het verificatieprocces heen te gaan voordat je doorgaat, maar je kan het bericht opnieuw versturen zonder te verifiëren als je dat prefereert."; +"unknown_devices_alert_title" = "Gesprek bevat onbekende apparaten"; +"unknown_devices_alert" = "Dit gesprek bevat onbekende apparaten die niet geverifieerd zijn.\nDit betekent dat er geen garantie is dat de apparaten bij de gebruikers horen waarbij ze beweren te horen.\nWe raden u aan om bij elk apparaat door het verificatieproces heen te gaan voordat u verdergaat, maar u kunt het bericht ook zonder verificatie opnieuw versturen."; "unknown_devices_send_anyway" = "Alsnog versturen"; "unknown_devices_call_anyway" = "Alsnog bellen"; "unknown_devices_answer_anyway" = "Alsnog beantwoorden"; "unknown_devices_verify" = "Verifiëren…"; "unknown_devices_title" = "Onbekende apparaten"; // Room Title -"room_title_new_room" = "Nieuwe ruimte"; +"room_title_new_room" = "Nieuw gesprek"; "room_title_multiple_active_members" = "%@/%@ actieve leden"; "room_title_one_active_member" = "%@/%@ actief lid"; "room_title_invite_members" = "Leden uitnodigen"; "room_title_members" = "%@ leden"; "room_title_one_member" = "1 lid"; // Room Preview -"room_preview_invitation_format" = "Je bent uitgenodigd om aan deze ruimte deel te nemen bij %@"; -"room_preview_subtitle" = "Dit is een voorvertoning van deze ruimte. Ruimte interacties zijn uitgeschakeld."; -"room_preview_unlinked_email_warning" = "Deze uitnodiging is naar %@ verstuurd. Maar diegene is niet geassocieerd met dit account.\nMisschien wil je met een ander account inloggen of deze e-mail aan dit account toevoegen."; -"room_preview_try_join_an_unknown_room" = "Je probeert aan %@ deel te nemen. Zou je willen toetreden om aan de discussie deel te nemen?"; -"room_preview_try_join_an_unknown_room_default" = "een ruimte"; +"room_preview_invitation_format" = "U bent door %@ uitgenodigd om dit gesprek toe te treden"; +"room_preview_subtitle" = "Dit is een voorvertoning van dit gesprek. Gespreksinteracties zijn uitgeschakeld."; +"room_preview_unlinked_email_warning" = "Deze uitnodiging is naar %@ verstuurd, maar dat is niet geassocieerd met deze account. U kunt zich met een andere account aanmelden, of dit e-mailadres aan deze account toevoegen."; +"room_preview_try_join_an_unknown_room" = "U probeert toegang te verkrijgen tot %@. Zou u willen toetreden om aan het gesprek deel te nemen?"; +"room_preview_try_join_an_unknown_room_default" = "een gesprek"; // Settings "settings_title" = "Instellingen"; -"account_logout_all" = "Op alle accounts uitloggen"; -"settings_config_no_build_info" = "Geen bouw informatie"; +"account_logout_all" = "Alle accounts afmelden"; +"settings_config_no_build_info" = "Geen versie-informatie"; "settings_mark_all_as_read" = "Alle berichten als gelezen markeren"; "settings_report_bug" = "Fout rapporteren"; "settings_clear_cache" = "Cache verwijderen"; "settings_config_home_server" = "Thuisserver is %@"; "settings_config_identity_server" = "Identiteitsserver is %@"; -"settings_config_user_id" = "Ingelogd als %@"; -"settings_user_settings" = "GEBRUIKER INSTELLINGEN"; -"settings_notifications_settings" = "NOTIFICATIE INSTELLINGEN"; +"settings_config_user_id" = "Aangemeld als %@"; +"settings_user_settings" = "GEBRUIKERSINSTELLINGEN"; +"settings_notifications_settings" = "MELDINGSINSTELLINGEN"; "settings_ignored_users" = "GENEGEERDE GEBRUIKERS"; "settings_contacts" = "LOKALE CONTACTEN"; "settings_advanced" = "GEAVANCEERD"; -"settings_other" = "ANDERS"; -"settings_labs" = "LABS"; +"settings_other" = "OVERIGE"; +"settings_labs" = "EXPERIMENTEEL"; "settings_devices" = "APPARATEN"; "settings_cryptography" = "CRYPTOGRAFIE"; -"settings_sign_out" = "Uitloggen"; -"settings_sign_out_confirmation" = "Weet je het zeker?"; -"settings_sign_out_e2e_warn" = "Je zal je eind-tot-eind encryptie sleutels kwijtraken. Dat betekent dat je op dit apparaat geen oude berichten meer kan lezen in een versleutelde ruimtes."; +"settings_sign_out" = "Afmelden"; +"settings_sign_out_confirmation" = "Weet u het zeker?"; +"settings_sign_out_e2e_warn" = "U zult uw sleutels voor eind-tot-eind-versleuteling kwijtraken. Dat betekent dat u op dit apparaat geen oude berichten in versleutelde kamers meer zult kunnen lezen."; "settings_profile_picture" = "Profielfoto"; -"settings_display_name" = "Naam"; +"settings_display_name" = "Weergavenaam"; "settings_first_name" = "Voornaam"; "settings_surname" = "Achternaam"; "settings_remove_prompt_title" = "Bevestiging"; -"settings_remove_email_prompt_msg" = "Weet je zeker dat je het e-mailadres %@ wilt verwijderen?"; -"settings_remove_phone_prompt_msg" = "Weet je zeker dat je het telefoonnummer %@ wilt verwijderen?"; -"settings_email_address" = "E-mail"; -"settings_email_address_placeholder" = "Voer je e-mailadres in"; +"settings_remove_email_prompt_msg" = "Weet u zeker dat u het e-mailadres %@ wilt verwijderen?"; +"settings_remove_phone_prompt_msg" = "Weet u zeker dat u het telefoonnummer %@ wilt verwijderen?"; +"settings_email_address" = "E-mailadres"; +"settings_email_address_placeholder" = "Voer uw e-mailadres in"; "settings_add_email_address" = "E-mailadres toevoegen"; "settings_phone_number" = "Telefoon"; "settings_add_phone_number" = "Telefoonnummer toevoegen"; "settings_change_password" = "Wachtwoord veranderen"; "settings_night_mode" = "Nachtmodus"; -"settings_fail_to_update_profile" = "Het vernieuwen van het profiel is mislukt"; -"settings_enable_push_notif" = "Notificaties op dit apparaat"; -"settings_global_settings_info" = "Globale notificatie instellingen zijn beschikbaar op je %@ web client"; -"settings_pin_rooms_with_missed_notif" = "Pin ruimtes met gemiste notificaties"; -"settings_pin_rooms_with_unread" = "Pin ruimtes met ongelezen berichten"; -"settings_on_denied_notification" = "Notificaties worden ontkent voor %@, sta ze toe in je apparaat instellingen"; +"settings_fail_to_update_profile" = "Bijwerken van profiel is mislukt"; +"settings_enable_push_notif" = "Meldingen op dit apparaat"; +"settings_global_settings_info" = "Globale meldingsinstellingen zijn beschikbaar op uw %@-webcliënt"; +"settings_pin_rooms_with_missed_notif" = "Kamers met gemiste meldingen vastprikken"; +"settings_pin_rooms_with_unread" = "Kamers met ongelezen berichten vastprikken"; +"settings_on_denied_notification" = "Meldingen worden geweigerd voor %@, sta ze toe in uw apparaatinstellingen"; //"settings_enable_all_notif" = "Alle notificaties aanzetten"; //"settings_messages_my_display_name" = "Bericht dat mijn naam bevat"; //"settings_messages_my_user_name" = "Bericht dat mijn gebruikersnaam bevat"; @@ -301,269 +301,385 @@ "settings_unignore_user" = "Alle berichten van %@ laten zien?"; "settings_contacts_discover_matrix_users" = "Gebruik e-mailadressen en telefoonnummers om gebruikers te vinden"; -"settings_contacts_phonebook_country" = "Telefoonboek land"; -"settings_labs_e2e_encryption" = "End-to-endbeveiliging"; -"settings_labs_e2e_encryption_prompt_message" = "Om het opzetten van de versleuteling af te ronden moet je opnieuw inloggen."; +"settings_contacts_phonebook_country" = "Land voor telefoonboek"; +"settings_labs_e2e_encryption" = "Eind-tot-eind-versleuteling"; +"settings_labs_e2e_encryption_prompt_message" = "Om het opzetten van de versleuteling af te ronden moet u zich opnieuw aanmelden."; "settings_version" = "Versie %@"; -"settings_olm_version" = "Olm Versie %@"; +"settings_olm_version" = "Olm-versie %@"; "settings_copyright" = "Copyright"; "settings_copyright_url" = "https://riot.im/copyright"; "settings_term_conditions" = "Algemene voorwaarden"; "settings_term_conditions_url" = "https://riot.im/tac_apple"; "settings_privacy_policy" = "Privacybeleid"; "settings_privacy_policy_url" = "https://riot.im/privacy"; -"settings_third_party_notices" = "Derde partij meldingen"; -"settings_send_crash_report" = "Stuur anon crash & gebruiksdata"; +"settings_third_party_notices" = "Derdepartijmeldingen"; +"settings_send_crash_report" = "Anonieme crash- en gebruiksgegevens versturen"; "settings_clear_cache" = "Cache verwijderen"; "settings_change_password" = "Wachtwoord veranderen"; "settings_old_password" = "oud wachtwoord"; "settings_new_password" = "nieuw wachtwoord"; "settings_confirm_password" = "wachtwoord bevestigen"; -"settings_fail_to_update_password" = "Wachtwoord updaten mislukt"; -"settings_password_updated" = "Je wachtwoord is geüpdatet"; +"settings_fail_to_update_password" = "Bijwerken van wachtwoord is mislukt"; +"settings_password_updated" = "Uw wachtwoord is bijgewerkt"; "settings_crypto_device_name" = "Apparaatnaam: "; -"settings_crypto_device_id" = "\nApparaat-ID: "; -"settings_crypto_device_key" = "\nApparaatsleutel: "; +"settings_crypto_device_id" = "\nApparaats-ID: "; +"settings_crypto_device_key" = "\nApparaatssleutel: "; "settings_crypto_export" = "Sleutels exporteren"; "settings_crypto_blacklist_unverified_devices" = "Alleen naar geverifieerde apparaten versleutelen"; // Room Details -"room_details_title" = "Ruimte Details"; +"room_details_title" = "Gespreksdetails"; "room_details_people" = "Leden"; "room_details_files" = "Bestanden"; "room_details_settings" = "Instellingen"; -"room_details_photo" = "Ruimte foto"; -"room_details_room_name" = "Ruimtenaam"; +"room_details_photo" = "Gespreksfoto"; +"room_details_room_name" = "Gespreksnaam"; "room_details_topic" = "Onderwerp"; "room_details_favourite_tag" = "Favoriet"; "room_details_low_priority_tag" = "Lage prioriteit"; -"room_details_mute_notifs" = "Notificaties dempen"; -"room_details_access_section" = "Wie kan tot deze ruimte toetreden?"; +"room_details_mute_notifs" = "Meldingen dempen"; +"room_details_access_section" = "Wie kan er tot dit gesprek toetreden?"; "room_details_access_section_invited_only" = "Alleen personen die zijn uitgenodigd"; -"room_details_access_section_anyone_apart_from_guest" = "Iedereen die de ruimte's link kent, behalve gasten"; -"room_details_access_section_anyone" = "Iedereen die de ruimte's link kent, inclusief gasten"; -"room_details_access_section_no_address_warning" = "De ruimte moet een adres hebben om er naar te linken"; -"room_details_access_section_directory_toggle" = "Zet deze ruimte in de ruimte catalogus"; -"room_details_history_section" = "Wie kan de geschiedenis lezen?"; +"room_details_access_section_anyone_apart_from_guest" = "Iedereen die de koppeling van het gesprek kent, behalve gasten"; +"room_details_access_section_anyone" = "Iedereen die de koppeling van het gesprek kent, inclusief gasten"; +"room_details_access_section_no_address_warning" = "Het gesprek moet een adres hebben om ernaar te verwijzen"; +"room_details_access_section_directory_toggle" = "Gesprek tonen in gesprekscatalogus"; +"room_details_history_section" = "Wie kan er de geschiedenis lezen?"; "room_details_history_section_anyone" = "Iedereen"; -"room_details_history_section_members_only" = "Alleen leden (vanaf de tijd dat deze optie geselecteerd wordt)"; +"room_details_history_section_members_only" = "Alleen leden (vanaf het moment dat deze optie geselecteerd wordt)"; "room_details_history_section_members_only_since_invited" = "Alleen leden (vanaf het moment dat ze uitgenodigd zijn)"; "room_details_history_section_members_only_since_joined" = "Alleen leden (vanaf het moment dat ze toetreden)"; -"room_details_history_section_prompt_title" = "Privacy waarschuwing"; -"room_details_history_section_prompt_msg" = "Veranderingen van wie de geschiedenis kan lezen zal alleen gelden voor toekomstige berichten in deze ruimte. De zichtbaarheid van bestaande geschiedenis zal ondervanderd blijven."; +"room_details_history_section_prompt_title" = "Privacywaarschuwing"; +"room_details_history_section_prompt_msg" = "Veranderingen aan wie de geschiedenis kan lezen zullen alleen gelden voor toekomstige berichten in dit gesprek. De zichtbaarheid van de bestaande geschiedenis zal onveranderd blijven."; "room_details_addresses_section" = "Adressen"; -"room_details_no_local_addresses" = "Deze ruimte heeft geen lokale adressen"; +"room_details_no_local_addresses" = "Dit gesprek heeft geen lokale adressen"; "room_details_new_address" = "Nieuw adres toevoegen"; -"room_details_new_address_placeholder" = "Nieuw adres toevoegen (bijv. #foo%@)"; -"room_details_addresses_invalid_address_prompt_title" = "Ongeldige naam formaat"; -"room_details_addresses_invalid_address_prompt_msg" = "%@ is geen geldig formaat voor een naam"; -"room_details_addresses_disable_main_address_prompt_title" = "Hoofdadres waarschuwing"; -"room_details_addresses_disable_main_address_prompt_msg" = "Je hebt geen hoofdadres gespecificeerd. Het standaard hoofdadres voor deze ruimte zal willekeurig gekozen worden"; +"room_details_new_address_placeholder" = "Nieuw adres toevoegen (bv. #foo%@)"; +"room_details_addresses_invalid_address_prompt_title" = "Ongeldig aliasformaat"; +"room_details_addresses_invalid_address_prompt_msg" = "%@ is geen geldig formaat voor een alias"; +"room_details_addresses_disable_main_address_prompt_title" = "Hoofdadreswaarschuwing"; +"room_details_addresses_disable_main_address_prompt_msg" = "U heeft geen hoofdadres opgegeven. Het standaardhoofdadres voor dit gesprek zal willekeurig gekozen worden"; "room_details_banned_users_section" = "Verbannen gebruikers"; "room_details_advanced_section" = "Geavanceerd"; -"room_details_advanced_room_id" = "Ruimte ID:"; -"room_details_advanced_enable_e2e_encryption" = "Versleuteling aanzetten (waarschuwing: dit kan niet meer worden uitgezet!)"; -"room_details_advanced_e2e_encryption_enabled" = "Versleuteling staat aan in deze ruimte"; -"room_details_advanced_e2e_encryption_disabled" = "Versleuteling staat niet aan in deze ruimte."; +"room_details_advanced_room_id" = "Gespreks-ID:"; +"room_details_advanced_enable_e2e_encryption" = "Versleuteling inschakelen (let op: dit kan niet meer worden uitgeschakeld!)"; +"room_details_advanced_e2e_encryption_enabled" = "Versleuteling is ingeschakeld in dit gesprek"; +"room_details_advanced_e2e_encryption_disabled" = "Versleuteling is niet ingeschakeld in dit gesprek."; "room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "Alleen naar geverifieerde apparaten versleutelen"; "room_details_advanced_e2e_encryption_prompt_message" = "End-to-endbeveiliging is experimenteel en kan onbetrouwbaar zijn.\n\nHet is beter om het nog niet met gevoelige gegevens te vertrouwen.\n\nApparaten kunnen de geschiedenis van voordat ze de ruimte betraden nog niet ontsleutelen.\n\nZodra de versleuteling aan staat kan het (voorlopig) niet worden uitgezet.\n\nVersleutelde berichten zullen nog niet zichtbaar zijn op programma's die geen versleuteling ondersteunen."; -"room_details_fail_to_update_avatar" = "Ruimte-foto vernieuwen mislukt"; -"room_details_fail_to_update_room_name" = "Ruimtenaam vernieuwen mislukt"; -"room_details_fail_to_update_topic" = "Ruimteonderwerp vernieuwen mislukt"; -"room_details_fail_to_update_room_guest_access" = "Ruimte's gast toegang vernieuwen mislukt"; -"room_details_fail_to_update_room_join_rule" = "Toetredingsregel vernieuwen mislukt"; -"room_details_fail_to_update_room_directory_visibility" = "Ruimte catalogus vernieuwen mislukt"; -"room_details_fail_to_update_history_visibility" = "Geschiedenis zichtbaarheid vernieuwen mislukt"; -"room_details_fail_to_add_room_aliases" = "Nieuwe ruimte adressen toevoegen mislukt"; -"room_details_fail_to_remove_room_aliases" = "Ruimte adressen verwijderen mislukt"; -"room_details_fail_to_update_room_canonical_alias" = "Hoofdadres vernieuwen mislukt"; -"room_details_fail_to_enable_encryption" = "Versleuteling in deze ruimte aanzetten mislukt"; -"room_details_save_changes_prompt" = "Wil je de veranderingen opslaan?"; +"room_details_fail_to_update_avatar" = "Bijwerken van gespreksfoto is mislukt"; +"room_details_fail_to_update_room_name" = "Bijwerken van gespreksnaam is mislukt"; +"room_details_fail_to_update_topic" = "Bijwerken van kameronderwerp is mislukt"; +"room_details_fail_to_update_room_guest_access" = "Bijwerken van gasttoegang tot gesprek is mislukt"; +"room_details_fail_to_update_room_join_rule" = "Bijwerken van toetredingsregel is mislukt"; +"room_details_fail_to_update_room_directory_visibility" = "Bijwerken van zichtbaarheid in de gesprekscatalogus is mislukt"; +"room_details_fail_to_update_history_visibility" = "Bijwerken van zichtbaarheid van geschiedenis is mislukt"; +"room_details_fail_to_add_room_aliases" = "Toevoegen van nieuwe gespreksadressen is mislukt"; +"room_details_fail_to_remove_room_aliases" = "Verwijderen van gespreksadressen is mislukt"; +"room_details_fail_to_update_room_canonical_alias" = "Bijwerken van hoofdadres is mislukt"; +"room_details_fail_to_enable_encryption" = "Inschakelen van versleuteling in dit gesprek is mislukt"; +"room_details_save_changes_prompt" = "Wilt u de wijzigingen opslaan?"; "room_details_set_main_address" = "Instellen als hoofdadres"; -"room_details_unset_main_address" = "Instellen als niet hoofdadres"; -"room_details_copy_room_id" = "Kopieer Ruimte ID"; -"room_details_copy_room_address" = "Kopieer Ruimte Adres"; -"room_details_copy_room_url" = "Kopieer Ruimte URL"; +"room_details_unset_main_address" = "Niet instellen als hoofdadres"; +"room_details_copy_room_id" = "Gespreks-ID kopiëren"; +"room_details_copy_room_address" = "Gespreksadres kopiëren"; +"room_details_copy_room_url" = "Gespreks-URL kopiëren"; // Media picker "media_picker_library" = "Bibliotheek"; "media_picker_select" = "Selecteren"; // Directory "directory_title" = "Catalogus"; "directory_server_picker_title" = "Selecteer een catalogus"; -"directory_server_all_rooms" = "Alle ruimtes op %@ server"; -"directory_server_all_native_rooms" = "Alle lokale Matrix ruimtes"; -"directory_server_type_homeserver" = "Voer een thuisserver in om de publieke ruimtes ervan te weergeven"; +"directory_server_all_rooms" = "Alle kamers op server %@"; +"directory_server_all_native_rooms" = "Alle lokale Matrix-kamers"; +"directory_server_type_homeserver" = "Voer een thuisserver in om de publieke kamers ervan weer te geven"; "directory_server_placeholder" = "matrix.org"; // Others "or" = "of"; -"you" = "Jij"; +"you" = "U"; "today" = "Vandaag"; "yesterday" = "Gisteren"; -"network_offline_prompt" = "Het ziet er naar uit dat de internet verbinding offline is."; -"public_room_section_title" = "Publieke ruimtes (op %@):"; -"bug_report_prompt" = "De applicatie is de vorige keer gecrasht. Wil je een crash rapport indienen?"; -"rage_shake_prompt" = "Het ziet er naar uit dat je de telefoon in frustratie schudt. Wil je een foutmelding indienen?"; -"camera_access_not_granted" = "%@ heeft geen permission om de Camera te gebruiken, verander de privacy instellingen"; +"network_offline_prompt" = "Het ziet er naar uit dat de internetverbinding offline is."; +"public_room_section_title" = "Publieke kamers (op %@):"; +"bug_report_prompt" = "De app is de vorige keer gecrasht. Wilt u een crashrapport indienen?"; +"rage_shake_prompt" = "Het ziet er naar uit dat u de telefoon in frustratie schudt. Wilt u een foutmelding indienen?"; +"camera_access_not_granted" = "%@ heeft geen toestemming om de camera te gebruiken, pas de privacy-instellingen aan"; "large_badge_value_k_format" = "%.1fK"; // Call "call_incoming_voice_prompt" = "Inkomende spraakoproep van %@"; -"call_incoming_video_prompt" = "Inkomende video oproep van %@"; +"call_incoming_video_prompt" = "Inkomende video-oproep van %@"; // No VoIP support "no_voip_title" = "Inkomende oproep"; -"no_voip" = "%@ belt jou %@ maar ondersteunt nog geen oproepen.\nJe kan deze notificatie negeren en vanaf een ander apparaat opnemen of de oproep afwijzen."; +"no_voip" = "%@ belt u %@ maar ondersteunt nog geen oproepen.\nU kunt deze melding negeren en vanaf een ander apparaat opnemen, of de oproep afwijzen."; // Crash report -"google_analytics_use_prompt" = "Wil je helpen met het verbeteren van %@ bij het automatisch rapporteren van crash rapporten en data van gebruik?"; +"google_analytics_use_prompt" = "Wilt u helpen met het verbeteren van %@ door anonieme crashrapporten en gebruiksstatistieken te versturen?"; // Crypto -"e2e_enabling_on_app_update" = "Riot ondersteunt nu eind-tot-eind sleuteling maar je moet opnieuw inloggen om het aan te zetten.\n\nJe kan het nu of later doen vanaf de applicatie instellingen."; -"e2e_need_log_in_again" = "Je moet opnieuw inloggen om end-to-endbeveligingssleutels te genereren voor dit apparaat en om de publieke sleutel naar de thuisserver te sturen.\nDit is eenmalig; excuses voor het ongemak."; +"e2e_enabling_on_app_update" = "Riot ondersteunt nu eind-tot-eind-versleuteling, maar u moet zich opnieuw aanmelden om het in te schakelen.\n\nU kunt dit nu of later doen vanuit de app-instellingen."; +"e2e_need_log_in_again" = "U moet zich opnieuw aanmelden om sleutels voor eind-tot-eind-versleuteling te genereren voor dit apparaat, en om de publieke sleutel naar uw thuisserver te sturen.\nDit is eenmalig; excuses voor het ongemak."; // Bug report "bug_report_title" = "Foutmelding"; -"bug_report_description" = "Beschrijf de foutmelding. Wat heb je gedaan? Wat verwachte je dat er zou gebeuren? Wat gebeurde er werkelijk?"; -"bug_crash_report_title" = "Crash Rapport"; -"bug_crash_report_description" = "Beschrijf wat je deed voor de crash:"; -"bug_report_logs_description" = "Om problemen te diagnoseren worden rapporten van deze applicatie met de foutmelding meegestuurd. Als je liever alleen bovenstaande tekst stuurt, haal dan het vinkje weg:"; -"bug_report_send_logs" = "Rapporten versturen"; +"bug_report_description" = "Beschrijf de foutmelding. Wat heeft u gedaan? Wat verwachtte u dat er zou gebeuren? Wat is er echt gebeurd?"; +"bug_crash_report_title" = "Crashrapport"; +"bug_crash_report_description" = "Beschrijf wat u deed vóór de crash:"; +"bug_report_logs_description" = "Om problemen te onderzoeken worden logboeken van deze app met de foutmelding meegestuurd. Als u liever alleen bovenstaande tekst stuurt, haal dan het vinkje weg:"; +"bug_report_send_logs" = "Logboeken versturen"; "bug_report_send_screenshot" = "Schermafbeelding versturen"; -"bug_report_progress_zipping" = "Rapporten verzamelen"; -"bug_report_progress_uploading" = "Rapport uploaden"; -"collapse" = "inkorten"; +"bug_report_progress_zipping" = "Logboeken worden verzameld"; +"bug_report_progress_uploading" = "Rapport wordt geüpload"; +"collapse" = "invouwen"; "auth_email_in_use" = "Dit e-mailadres is al in gebruik"; "auth_phone_in_use" = "Dit telefoonnummer is al in gebruik"; "auth_untrusted_id_server" = "De identiteitsserver is niet vertrouwd"; "auth_email_not_found" = "E-mail versturen mislukt: dit e-mailadres werd niet gevonden"; -"contacts_user_directory_section" = "GEBRUIKERSADRESBOEK"; -"contacts_user_directory_offline_section" = "GEBRUIKERSADRESBOEK (offline)"; +"contacts_user_directory_section" = "GEBRUIKERSCATALOGUS"; +"contacts_user_directory_offline_section" = "GEBRUIKERSCATALOGUS (offline)"; "settings_user_interface" = "GEBRUIKERSINTERFACE"; "settings_ui_language" = "Taal"; // Read Receipts -"read_receipts_list" = "Leesbewijzen Lijst"; -"receipt_status_read" = "Lees: "; +"read_receipts_list" = "Leesbevestigingslijst"; +"receipt_status_read" = "Gelezen: "; // Events formatter -"event_formatter_member_updates" = "%tu lidmaatschap aanpassingen"; -"bug_report_send" = "Stuur"; +"event_formatter_member_updates" = "%tu lidmaatschapsaanpassingen"; +"bug_report_send" = "Versturen"; "auth_home_server_placeholder" = "URL (bv. https://matrix.org)"; "auth_identity_server_placeholder" = "URL (bv. https://matrix.org)"; -"room_ongoing_conference_call_with_close" = "Lopend vergadergesprek. Neem deel als %@ of %@. %@ het."; +"room_ongoing_conference_call_with_close" = "Er is een vergadergesprek gaande. Neem deel met %@ of %@. %@ het."; "room_ongoing_conference_call_close" = "Sluiten"; -"room_conference_call_no_power" = "Je hebt permissie nodig om het vergadergesprek in deze ruimte te beheren"; +"room_conference_call_no_power" = "U heeft toestemming nodig om vergadergesprekken in dit groepsgesprek te beheren"; "settings_labs_create_conference_with_jitsi" = "Maak vergadergesprekken met jitsi"; -"call_already_displayed" = "Er is al een gesprek aan de gang."; -"call_jitsi_error" = "Het is niet gelukt om aan het vergadergesprek deel te nemen."; +"call_already_displayed" = "Er is al een oproep aan de gang."; +"call_jitsi_error" = "Deelnemen aan het vergadergesprek is mislukt."; // Widget -"widget_no_power_to_manage" = "Je hebt permissie nodig om widgets in deze ruimte te beheren"; -"widget_creation_failure" = "Het creëren van de widget is fout gegaan"; +"widget_no_power_to_manage" = "U heeft toestemming nodig om widgets in dit gesprek te beheren"; +"widget_creation_failure" = "Aanmaken van widget is mislukt"; "send_to" = "Stuur naar %@"; -"sending" = "Aan het sturen"; +"sending" = "Wordt verstuurd"; "search_in_progress" = "Aan het zoeken…"; -"room_event_action_cancel_send" = "Annuleer verzending"; -"room_event_failed_to_send" = "Verzenden is gefaald"; +"room_event_action_cancel_send" = "Verzending annuleren"; +"room_event_failed_to_send" = "Verzenden is mislukt"; "settings_calls_settings" = "OPROEPEN"; -"settings_show_decrypted_content" = "Geef ontsleutelde content weer"; +"settings_show_decrypted_content" = "Ontsleutelde inhoud tonen"; "settings_enable_callkit" = "Geïntegreerde oproepen"; -"settings_callkit_info" = "Ontvang inkomende oproepen op je toegangsscherm. Zie je Riot oproepen in de gesprekkengeschiedenis van het systeem. Als iCloud ingeschakeld is zal deze geschiedenis met Apple gedeeld worden."; +"settings_callkit_info" = "Ontvang inkomende oproepen op uw toegangsscherm. Geef uw Riot-oproepen weer in de gespreksgeschiedenis van het systeem. Als iCloud ingeschakeld is zal deze geschiedenis met Apple gedeeld worden."; "settings_ui_theme" = "Thema"; "settings_ui_theme_auto" = "Automatisch"; "settings_ui_theme_light" = "Licht"; "settings_ui_theme_dark" = "Donker"; "settings_ui_theme_black" = "Zwart"; "settings_ui_theme_picker_title" = "Selecteer een thema"; -"settings_ui_theme_picker_message" = "\"Automatisch\" gebruikt de \"Kleurweergave omkeren\" instelling van je apparaat"; +"settings_ui_theme_picker_message" = "‘Automatisch’ gebruikt de instelling ‘Kleurweergave omkeren’ van uw apparaat"; "settings_enable_rageshake" = "Schud de telefoon om een fout te rapporteren"; -"room_details_direct_chat" = "Privégesprek"; -"room_details_fail_to_update_room_direct" = "Het is niet gelukt om de direct (privé) vlag in deze ruimte te updaten"; +"room_details_direct_chat" = "Tweegesprek"; +"room_details_fail_to_update_room_direct" = "Bijwerken van de tweegespreksvlag in dit gesprek is mislukt"; "event_formatter_widget_added" = "%@ widget toegevoegd door %@"; "event_formatter_widget_removed" = "%@ widget verwijderd door %@"; -"event_formatter_jitsi_widget_added" = "VoIP vergadergesprek toegevoegd door %@"; -"event_formatter_jitsi_widget_removed" = "VoIP vergadergesprek verwijderd door %@"; +"event_formatter_jitsi_widget_added" = "VoIP-vergadergesprek toegevoegd door %@"; +"event_formatter_jitsi_widget_removed" = "VoIP-vergadergesprek verwijderd door %@"; "do_not_ask_again" = "Niet opnieuw vragen"; -"call_incoming_voice" = "Inkomende oproep..."; -"call_incoming_video" = "Inkomend videogesprek..."; +"call_incoming_voice" = "Inkomende oproep…"; +"call_incoming_video" = "Inkomende video-oproep…"; // Widget Integration Manager -"widget_integration_need_to_be_able_to_invite" = "Je moet het machtsniveau waarmee je gebruikers kan uitnodigen hebben om dat te kunnen doen."; -"widget_integration_unable_to_create" = "Niet in staat om een widget aan te maken."; -"widget_integration_failed_to_send_request" = "Het is niet gelukt om een verzoek te versturen."; -"widget_integration_room_not_recognised" = "Deze ruimte wordt niet herkend."; +"widget_integration_need_to_be_able_to_invite" = "U moet gebruikers kunnen uitnodigen om dat te kunnen doen."; +"widget_integration_unable_to_create" = "Kan widget niet aanmaken."; +"widget_integration_failed_to_send_request" = "Versturen van verzoek is mislukt."; +"widget_integration_room_not_recognised" = "Dit gesprek wordt niet herkend."; "widget_integration_positive_power_level" = "Het machtsniveau moet een positief geheel getal zijn."; -"widget_integration_must_be_in_room" = "Je zit niet in deze ruimte."; -"widget_integration_no_permission_in_room" = "Je hebt niet de permissie om dat in deze ruimte te doen."; -"widget_integration_missing_room_id" = "Het room_id mist in het verzoek."; -"widget_integration_missing_user_id" = "Het user_id mist in het verzoek."; -"widget_integration_room_not_visible" = "Room %@ is niet zichtbaar."; +"widget_integration_must_be_in_room" = "U zit niet in dit gesprek."; +"widget_integration_no_permission_in_room" = "U heeft geen toestemming om dat in dit gesprek te doen."; +"widget_integration_missing_room_id" = "room_id ontbreekt in het verzoek."; +"widget_integration_missing_user_id" = "user_id ontbreekt in het verzoek."; +"widget_integration_room_not_visible" = "Gesprek %@ is niet zichtbaar."; // Share extension -"share_extension_auth_prompt" = "Log in op de hoofdapplicatie om de content te delen"; -"share_extension_failed_to_encrypt" = "Niet gelukt om te versturen. Controleer in de hoofdapplicatie de versleutelingsinstellingen van deze ruimte"; +"share_extension_auth_prompt" = "Meld u aan in de hoofdapp om inhoud te delen"; +"share_extension_failed_to_encrypt" = "Versturen is mislukt. Controleer in de hoofdapp de versleutelingsinstellingen van dit gesprek"; // Room key request dialog -"e2e_room_key_request_title" = "Versleutelingssleutel verzoek"; -"e2e_room_key_request_message" = "Je niet geverifieerde apparaat '%@' vraagt naar versleutelingssleutels."; -"e2e_room_key_request_start_verification" = "Start verificatie..."; +"e2e_room_key_request_title" = "Versleutelingssleutelverzoek"; +"e2e_room_key_request_message" = "Uw ongeverifieerde apparaat ‘%@’ vraagt naar versleutelingssleutels."; +"e2e_room_key_request_start_verification" = "Verificatie beginnen…"; "e2e_room_key_request_share_without_verifying" = "Delen zonder te verifiëren"; -"e2e_room_key_request_ignore_request" = "Negeer verzoek"; +"e2e_room_key_request_ignore_request" = "Verzoek negeren"; "title_groups" = "Gemeenschappen"; // Groups tab "group_invite_section" = "UITNODIGINGEN"; "group_section" = "GEMEENSCHAPPEN"; -"room_do_not_have_permission_to_post" = "Je hebt geen toestemming om in deze ruimte te posten"; +"room_do_not_have_permission_to_post" = "U heeft geen toestemming om in dit gesprek te posten"; "settings_flair" = "Badge weergeven waar toegestaan"; "room_details_flair_section" = "Badge voor gemeenschappen weergeven"; -"room_details_new_flair_placeholder" = "Voeg nieuw gemeenschaps-ID toe (bv. +foo%@)"; +"room_details_new_flair_placeholder" = "Nieuwe gemeenschaps-ID toevoegen (bv. +foo%@)"; "room_details_flair_invalid_id_prompt_title" = "Ongeldig formaat"; -"room_details_flair_invalid_id_prompt_msg" = "%@ is niet een geldige identificatie voor een gemeenschap"; -"room_details_fail_to_update_room_communities" = "Niet gelukt om de gerelateerde gemeenschappen te updaten"; +"room_details_flair_invalid_id_prompt_msg" = "%@ is geen geldige identificatie voor een gemeenschap"; +"room_details_fail_to_update_room_communities" = "Bijwerken van gerelateerde gemeenschappen is mislukt"; // Group Details "group_details_title" = "Gemeenschapsdetails"; -"group_details_home" = "Home"; +"group_details_home" = "Thuis"; "group_details_people" = "Personen"; -"group_details_rooms" = "Ruimtes"; +"group_details_rooms" = "Kamers"; // Group Home "group_home_one_member_format" = "1 lid"; "group_home_multi_members_format" = "%tu leden"; -"group_home_one_room_format" = "1 ruimte"; -"group_home_multi_rooms_format" = "%tu ruimtes"; -"group_invitation_format" = "%@ heeft je uitgenodigd om tot deze gemeenschap toe te treden"; +"group_home_one_room_format" = "1 gesprek"; +"group_home_multi_rooms_format" = "%tu kamers"; +"group_invitation_format" = "%@ heeft u uitgenodigd om tot deze gemeenschap toe te treden"; // Group participants "group_participants_add_participant" = "Deelnemer toevoegen"; "group_participants_leave_prompt_title" = "Groep verlaten"; -"group_participants_leave_prompt_msg" = "Weet je zeker dat je de groep wilt verlaten?"; -"group_participants_remove_prompt_title" = "Bevestigen"; -"group_participants_remove_prompt_msg" = "Weet je zeker dat je %@ van deze groep wilt verwijderen?"; +"group_participants_leave_prompt_msg" = "Weet u zeker dat u de groep wilt verlaten?"; +"group_participants_remove_prompt_title" = "Bevestiging"; +"group_participants_remove_prompt_msg" = "Weet u zeker dat u %@ uit deze groep wilt verwijderen?"; "group_participants_invite_prompt_title" = "Bevestiging"; -"group_participants_invite_prompt_msg" = "Weet je zeker dat je %@ in deze groep wilt uitnodigen?"; +"group_participants_invite_prompt_msg" = "Weet u zeker dat u %@ in deze groep wilt uitnodigen?"; "group_participants_filter_members" = "Gemeenschapsleden filteren"; -"group_participants_invite_another_user" = "Zoeken / uitnodigen bij Gebruikers-ID of Naam"; +"group_participants_invite_another_user" = "Zoeken/uitnodigen met gebruikers-ID of naam"; "group_participants_invite_malformed_id_title" = "Uitnodigingsfout"; -"group_participants_invite_malformed_id" = "Misvormd ID. Het moet een Matrix ID zijn, zoals '@localpart:domain'"; +"group_participants_invite_malformed_id" = "Misvormde ID. Dit moet een Matrix-ID zijn, zoals ‘@gebruikersnaam:domein’"; "group_participants_invited_section" = "UITGENODIGD"; // Group rooms -"group_rooms_filter_rooms" = "Gemeenschapsruimtes filteren"; -"e2e_room_key_request_message_new_device" = "Je hebt een nieuwe apparaat '%@' toegevoegd, die een verzoek doet naar versleutelingssleutels."; +"group_rooms_filter_rooms" = "Gemeenschapskamers filteren"; +"e2e_room_key_request_message_new_device" = "U heeft een nieuw apparaat ‘%@’ toegevoegd, dat vraagt naar versleutelingssleutels."; "room_event_action_kick_prompt_reason" = "Reden voor het verwijderen van deze gebruiker"; "room_event_action_ban_prompt_reason" = "Reden voor het verbannen van deze gebruiker"; // GDPR -"gdpr_consent_not_given_alert_message" = "Om de %@ thuisserver te blijven gebruiken moet je de algemeen voorwaarden lezen en daarmee akkoord gaan."; -"gdpr_consent_not_given_alert_review_now_action" = "Nu doorlezen"; +"gdpr_consent_not_given_alert_message" = "Om de %@-thuisserver te blijven gebruiken moet u de algemene voorwaarden lezen en er mee akkoord gaan."; +"gdpr_consent_not_given_alert_review_now_action" = "Nu lezen"; "room_action_send_photo_or_video" = "Foto of video versturen"; "room_action_send_sticker" = "Sticker versturen"; -"settings_deactivate_account" = "DEACTIVEER ACCOUNT"; +"settings_deactivate_account" = "ACCOUNT DEACTIVEREN"; "deactivate_account_forget_messages_information_part2_emphasize" = "Waarschuwing"; // Re-request confirmation dialog "rerequest_keys_alert_title" = "Aanvraag verstuurd"; -"rerequest_keys_alert_message" = "Start Riot alstublieft op een ander apparaat dat het bericht kan ontsleutelen zodat deze de sleutels kan sturen naar dit apparaat."; -"settings_deactivate_my_account" = "Account deactiveren"; -"event_formatter_rerequest_keys_part1_link" = "Vraag beveiligingssleutels opnieuw aan"; -"event_formatter_rerequest_keys_part2" = " van je andere apparaten."; -"widget_sticker_picker_no_stickerpacks_alert" = "Je hebt momenteel geen stickerpakketten aan staan."; -"widget_sticker_picker_no_stickerpacks_alert_add_now" = "Wil je er nu een paar toevoegen?"; +"rerequest_keys_alert_message" = "Start Riot op een ander apparaat dat het bericht kan ontsleutelen, zodat het de sleutels kan sturen naar dit apparaat."; +"settings_deactivate_my_account" = "Mijn account deactiveren"; +"event_formatter_rerequest_keys_part1_link" = "Versleutelingssleutels opnieuw aanvragen"; +"event_formatter_rerequest_keys_part2" = " van uw andere apparaten."; +"widget_sticker_picker_no_stickerpacks_alert" = "U heeft momenteel geen stickerpakketten ingeschakeld."; +"widget_sticker_picker_no_stickerpacks_alert_add_now" = "Wilt u er nu een paar toevoegen?"; "deactivate_account_title" = "Account deactiveren"; -"deactivate_account_informations_part1" = "Dit zal je account voorgoed onbruikbaar maken. Je zal niet meer in kunnen loggen en niemand anders zal met dezelfde gebruikers ID kunnen registreren. Dit zal er voor zorgen dat je account alle ruimtes verlaat waar het momenteel onderdeel van is en het verwijderd de accountgegevens van de identiteitsserver. "; +"deactivate_account_informations_part1" = "Dit zal uw account voorgoed onbruikbaar maken. U zult zich niet meer kunnen aanmelden, en niemand anders zal zich met dezelfde gebruikers-ID kunnen registreren. Dit zal er voor zorgen dat uw account alle kamers verlaat waar deze momenteel lid van is, en het verwijdert uw accountgegevens van de identiteitsserver. "; "deactivate_account_informations_part2_emphasize" = "Deze actie is onomkeerbaar."; -"deactivate_account_informations_part3" = "\n\nHet deactiveren van je account "; -"deactivate_account_informations_part4_emphasize" = "zal er niet standaard voor zorgen dat de berichten die je verzonden hebt vergeten worden. "; -"deactivate_account_informations_part5" = "Als je wilt dat wij de berichten vergeten, klikt alsjeblieft op het vakje hieronder.\n\nDe zichtbaarheid van berichten in Matrix is hetzelfde als in e-mail. Het vergeten van je berichten betekent dat berichten die je hebt verstuurd niet meer gedeeld worden met nieuwe of ongeregistreerde gebruikers, maar geregistreerde gebruikers die al toegang hebben tot deze berichten zullen alsnog toegang hebben tot hun eigen kopie van het bericht."; +"deactivate_account_informations_part3" = "\n\nHet deactiveren van uw account "; +"deactivate_account_informations_part4_emphasize" = "zal er niet standaard voor zorgen dat de berichten die u heeft verzonden worden vergeten. "; +"deactivate_account_informations_part5" = "Als u wilt dat wij de berichten vergeten, vinkt u het vakje hieronder aan.\n\nDe zichtbaarheid van berichten in Matrix is gelijkaardig aan e-mails. Het vergeten van uw berichten betekent dat berichten die u heeft verstuurd niet meer gedeeld worden met nieuwe of ongeregistreerde gebruikers, maar geregistreerde gebruikers die al toegang hebben tot deze berichten zullen alsnog toegang hebben tot hun eigen kopie ervan."; "deactivate_account_forget_messages_information_part1" = "Vergeet alle berichten die ik heb verstuurd wanneer mijn account gedeactiveerd is ("; -"deactivate_account_forget_messages_information_part3" = ": dit zal er voor zorgen dat toekomstige gebruikers een incompleet beeld krijgen van gesprekken)"; +"deactivate_account_forget_messages_information_part3" = ": dit zal er voor zorgen dat toekomstige gebruikers een onvolledig beeld krijgen van gesprekken)"; "deactivate_account_validate_action" = "Account deactiveren"; "deactivate_account_password_alert_title" = "Account deactiveren"; -"deactivate_account_password_alert_message" = "Voer je wachtwoord in om verder te gaan"; -"room_event_action_view_decrypted_source" = "Bekijk ontsleutelde bron"; -"room_message_reply_to_placeholder" = "Verstuur een antwoord (onversleuteld)…"; -"encrypted_room_message_reply_to_placeholder" = "Verstuur een versleuteld antwoord…"; -"room_message_reply_to_short_placeholder" = "Verstuur een antwoord…"; +"deactivate_account_password_alert_message" = "Voer uw wachtwoord in om verder te gaan"; +"room_event_action_view_decrypted_source" = "Ontsleutelde bron weergeven"; +"room_message_reply_to_placeholder" = "Stuur een antwoord (onversleuteld)…"; +"encrypted_room_message_reply_to_placeholder" = "Stuur een versleuteld antwoord…"; +"room_message_reply_to_short_placeholder" = "Stuur een antwoord…"; +// String for App Store +"store_short_description" = "Veilig en gedecentraliseerd chatten en bellen"; +"store_full_description" = "Communiceer op uw manier.\n\nEen chat-app, onder uw controle en heel flexibel. Riot laat u communiceren zoals u dat wilt. Gemaakt voor [matrix] - de standaard voor open, gedecentraliseerde communicatie.\n\nMaak een gratis account aan op matrix.org, verkrijg uw eigen server op https://modular.im, of gebruik een andere Matrix-server.\n\nWaarom zou ik voor Riot.im kiezen?\n\n• VOLLEDIGE COMMUNICATIE: maak kamers aan rond uw teams, uw vrienden, uw gemeenschap - hoe u maar wilt! Chat, deel bestanden, voeg widgets toe en maak stem- en video-oproepen - allemaal volledig gratis.\n\n• KRACHTIGE INTEGRATIE: gebruik Riot.im met de hulpmiddelen waarmee u vertrouwd bent. Met Riot.im kunt u zelfs chatten met gebruikers en groepen op andere chat-apps.\n\n• PRIVÉ EN VEILIG: houd uw gesprekken geheim. Eind-tot-eind-versleuteling van de bovenste plank zorgt ervoor dat uw privécommunicatie ook privé blijft.\n\n• OPEN, NIET GESLOTEN: vrije software, gebouwd op Matrix. Wees baas over uw eigen gegevens door uw eigen server te gebruiken, of te kiezen voor een andere server die u vertrouwt.\n\n• WAAR U OOK BENT: houd contact waar u ook bent met volledig gesynchroniseerde berichtgeschiedenis op al uw apparaten, en online op https://riot.im."; +"auth_login_single_sign_on" = "Aanmelden met enkele aanmelding"; +"auth_accept_policies" = "Gelieve het beleid van deze thuisserver te lezen en aanvaarden:"; +"auth_autodiscover_invalid_response" = "Ongeldig thuisserverontdekkingsantwoord"; +"room_recents_server_notice_section" = "SYSTEEMMELDINGEN"; +"room_message_unable_open_link_error_message" = "Kan de koppeling niet openen."; +"room_replacement_information" = "Dit gesprek is vervangen en is niet langer actief."; +"room_replacement_link" = "Het gesprek gaat hier verder."; +"room_predecessor_information" = "Dit gesprek is een voortzetting van een ander gesprek."; +"room_predecessor_link" = "Tik hier om oudere berichten te zien."; +"room_resource_limit_exceeded_message_contact_1" = " Gelieve "; +"room_resource_limit_exceeded_message_contact_2_link" = "contact op te nemen met uw dienstbeheerder"; +"room_resource_limit_exceeded_message_contact_3" = " om deze dienst te blijven gebruiken."; +"room_resource_usage_limit_reached_message_1_default" = "Deze thuisserver heeft één van zijn bronlimieten overschreden, dus "; +"room_resource_usage_limit_reached_message_1_monthly_active_user" = "Deze thuisserver heeft zijn limiet voor maandelijks actieve gebruikers overschreden, dus "; +"room_resource_usage_limit_reached_message_2" = "sommige gebruikers zullen zich niet kunnen aanmelden."; +"room_resource_usage_limit_reached_message_contact_3" = " om deze limiet te verhogen."; +"settings_key_backup" = "SLEUTELBACK-UP"; +"settings_labs_room_members_lazy_loading" = "Kamerleden lui laden"; +"settings_labs_room_members_lazy_loading_error_message" = "Uw thuisserver ondersteunt het lui laden van gespreksleden nog niet. Probeer het later opnieuw."; +"settings_key_backup_info" = "Versleutelde berichten worden beveiligd met eind-tot-eind-versleuteling. Enkel de ontvanger(s) en u hebben de sleutels om deze berichten te lezen."; +"settings_key_backup_info_checking" = "Bezig met controleren…"; +"settings_key_backup_info_none" = "Uw sleutels worden niet geback-upt vanaf dit apparaat."; +"settings_key_backup_info_signout_warning" = "Maak een back-up van uw sleutels vooraleer u zich afmeldt om ze niet te verliezen."; +"settings_key_backup_info_version" = "Sleutelback-upversie: %@"; +"settings_key_backup_info_algorithm" = "Algoritme: %@"; +"settings_key_backup_info_valid" = "Dit apparaat maakt een back-up van uw sleutels."; +"settings_key_backup_info_not_valid" = "Dit apparaat maakt geen back-up van uw sleutels."; +"settings_key_backup_info_progress" = "Back-up van %@ sleutels wordt gemaakt…"; +"settings_key_backup_info_progress_done" = "Alle sleutels zijn geback-upt"; +"settings_key_backup_info_trust_signature_unknown" = "De back-up heeft een ondertekening van apparaat met ID: %@"; +"settings_key_backup_info_trust_signature_valid" = "De back-up heeft een geldige ondertekening van dit apparaat"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "De back-up heeft een geldige ondertekening van %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "De back-up heeft een ondertekening van %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "De back-up heeft een ongeldige ondertekening van %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "De back-up heeft een ongeldige ondertekening van %@"; +"settings_key_backup_button_create" = "Begin sleutelback-up te gebruiken"; +"settings_key_backup_button_restore" = "Herstellen uit back-up"; +"settings_key_backup_button_delete" = "Back-up verwijderen"; +"settings_key_backup_delete_confirmation_prompt_title" = "Back-up verwijderen"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Weet u het zeker? Als uw sleutels niet correct geback-upt zijn, zult u uw versleutelde berichten verliezen."; +"homeserver_connection_lost" = "Kon geen verbinding maken met de thuisserver."; +"room_does_not_exist" = "%@ bestaat niet"; +// Key backup wrong version +"e2e_key_backup_wrong_version_title" = "Nieuwe sleutelback-up"; +"e2e_key_backup_wrong_version" = "Er is een nieuwe sleutelback-up voor versleutelde berichten gedetecteerd.\n\nIndien deze niet van u komt, stel dan een nieuw wachtwoord in in de instellingen."; +"e2e_key_backup_wrong_version_button_settings" = "Instellingen"; +"e2e_key_backup_wrong_version_button_wasme" = "Ik was het"; +"key_backup_setup_title" = "Sleutelback-up"; +"key_backup_setup_skip_alert_title" = "Weet u het zeker?"; +"key_backup_setup_skip_alert_message" = "U verliest mogelijk uw versleutelde berichten als u zich afmeldt of uw apparaat verliest."; +"key_backup_setup_skip_alert_skip_action" = "Overslaan"; +"key_backup_setup_intro_title" = "Verlies nooit uw versleutelde berichten"; +"key_backup_setup_intro_info" = "Berichten in versleutelde kamers worden versleutelde met eind-tot-eind-beveiliging. Enkel de ontvanger(s) en u hebben de sleutels om deze berichten te lezen.\n\nMaak een veilige back-up van uw sleutels om deze niet te verliezen."; +"key_backup_setup_intro_setup_action_without_existing_backup" = "Begin sleutelback-up te gebruiken"; +"key_backup_setup_intro_manual_export_info" = "(Geavanceerd)"; +"key_backup_setup_intro_manual_export_action" = "Sleutels handmatig exporteren"; +"key_backup_setup_passphrase_title" = "Beveilig uw back-up met een wachtwoord"; +"key_backup_setup_passphrase_info" = "We bewaren een versleutelde kopie van uw sleutels op onze server. Bescherm uw back-up met een wachtwoord om deze veilig te houden.\n\nVoor maximale beveiliging zou dit moeten verschillen van uw accountwachtwoord."; +"key_backup_setup_passphrase_passphrase_title" = "Invoeren"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Voer wachtwoord in"; +"key_backup_setup_passphrase_passphrase_valid" = "Top!"; +"key_backup_setup_passphrase_passphrase_invalid" = "Probeer nog een woord toe te voegen"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Bevestigen"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Wachtwoord bevestigen"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Top!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Wachtwoorden komen niet overeen"; +"key_backup_setup_passphrase_set_passphrase_action" = "Wachtwoord instellen"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Of beveilig uw back-up met een herstelsleutel, en bewaar deze op een veilige plaats."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Geavanceerd) Instellen met herstelsleutel"; +"key_backup_setup_success_title" = "Klaar!"; +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "Er wordt een back-up van uw sleutels gemaakt.\n\nUw herstelsleutel is een veiligheidsnet - u kunt deze gebruiken om de toegang tot uw versleutelde berichten te herstellen als u uw wachtwoord zou vergeten.\n\nBewaar uw herstelsleutel op een heel veilig plaats, zoals een wachtwoordbeheerder (of een kluis)."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Herstelsleutel opslaan"; +"key_backup_setup_success_from_passphrase_done_action" = "Klaar"; +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "Er wordt een back-up van uw sleutels gemaakt.\n\nMaak een kopie van deze herstelsleutel en bewaar deze op een veilige plaats."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Herstelsleutel"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "Maak een kopie"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "Ik heb een kopie gemaakt"; +"key_backup_recover_title" = "Versleutelde berichten"; +"key_backup_recover_invalid_passphrase_title" = "Onjuist herstelwachtwoord"; +"key_backup_recover_invalid_passphrase" = "De back-up kon niet ontsleuteld worden met dit wachtwoord: controleer of u het herstelwachtwoord juist hebt ingevoerd."; +"key_backup_recover_invalid_recovery_key_title" = "Herstelsleutel komt niet overeen"; +"key_backup_recover_invalid_recovery_key" = "De back-up kon niet ontsleuteld worden met deze sleutel: controleer of u de juiste herstelsleutel hebt ingevoerd."; +"key_backup_recover_from_passphrase_info" = "Gebruik uw herstelwachtwoord om uw versleutelde berichtgeschiedenis te ontgrendelen"; +"key_backup_recover_from_passphrase_passphrase_title" = "Invoeren"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Voer wachtwoord in"; +"key_backup_recover_from_passphrase_recover_action" = "Geschiedenis ontgrendelen"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Herstelwachtwoord vergeten? Dan kunt u "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "uw herstelsleutel gebruiken"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_recovery_key_info" = "Gebruik uw herstelsleutel om uw versleutelde berichtgeschiedenis te ontgrendelen"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Invoeren"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Voer herstelsleutel in"; +"key_backup_recover_from_recovery_key_recover_action" = "Geschiedenis ontgrendelen"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Herstelsleutel verloren? U kunt er een nieuwe aanmaken in de instellingen."; +"key_backup_recover_success_info" = "Back-up hersteld!"; +"key_backup_recover_done_action" = "Klaar"; +"key_backup_setup_banner_title" = "Verlies nooit uw versleutelde berichten"; +"key_backup_setup_banner_subtitle" = "Begin sleutelback-up te gebruiken"; +"key_backup_recover_banner_title" = "Verlies nooit uw versleutelde berichten"; +"sign_out_existing_key_backup_alert_title" = "Weet u zeker dat u zich wilt afmelden?"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Afmelden"; +"sign_out_non_existing_key_backup_alert_title" = "Als u zich nu afmeldt, zult u de toegang tot uw versleutelde berichten verliezen"; +"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Begin sleutelback-up te gebruiken"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Ik wil mijn versleutelde berichten niet"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "U zult uw versleutelde berichten verliezen"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "U zult de toegang tot uw versleutelde berichten verliezen, tenzij u een back-up van uw sleutels maakt vooraleer u zich afmeldt."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Afmelden"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Back-up"; +"sign_out_key_backup_in_progress_alert_title" = "Sleutelback-up is bezig. Als u zich nu afmeldt zult u de toegang tot uw versleutelde berichten verliezen."; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Ik wil mijn versleutelde berichten niet"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "Ik wacht wel"; From 193f5371b256c512042bb40fbf7828e9b11a924e Mon Sep 17 00:00:00 2001 From: Victor Grousset Date: Wed, 13 Mar 2019 22:30:21 +0000 Subject: [PATCH 014/266] Translated using Weblate (Esperanto) Currently translated at 2.6% (16 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eo/ --- Riot/Assets/eo.lproj/Vector.strings | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index 72b237636f..1c6cbc40ee 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -2,3 +2,16 @@ "encrypted_room_message_reply_to_placeholder" = "Sendi ĉifritan respondon…"; "room_message_short_placeholder" = "Sendi mesaĝon…"; "room_message_reply_to_short_placeholder" = "Sendi respondon…"; +"title_people" = "Homoj"; +"title_rooms" = "Babilejoj"; +"title_groups" = "Komunumoj"; +"room_recents_people_section" = "HOMOJ"; +"room_recents_conversations_section" = "BABILEJOJ"; +"room_recents_no_conversation" = "Neniu babilejo"; +"search_people" = "Homoj"; +"search_files" = "Dosieroj"; +"search_default_placeholder" = "Serĉi"; +// Chat +"room_jump_to_first_unread" = "Salti al unua nelegita mesaĝo"; +"group_details_people" = "Homoj"; +"group_details_rooms" = "Babilejoj"; From 8c5077f2e81f4390a4e0f7972003f54b722ca361 Mon Sep 17 00:00:00 2001 From: Samu Voutilainen Date: Fri, 29 Mar 2019 06:22:21 +0000 Subject: [PATCH 015/266] Translated using Weblate (Finnish) Currently translated at 6.8% (42 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fi/ --- Riot/Assets/fi.lproj/Vector.strings | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Riot/Assets/fi.lproj/Vector.strings b/Riot/Assets/fi.lproj/Vector.strings index 6ae6798fdd..d3b8ffd71d 100644 --- a/Riot/Assets/fi.lproj/Vector.strings +++ b/Riot/Assets/fi.lproj/Vector.strings @@ -29,3 +29,20 @@ "video" = "Video"; // Room Details "room_details_title" = "Huoneen tiedot"; +"auth_msisdn_validation_error" = "Puhelinnumeron vahvistus epäonnistui."; +"auth_reset_password_error_unauthorized" = "Sähköpostin vahvistus epäonnistui: varmistathan, että seurasit lähettämässämme viestissä olevaa linkkiä"; +// Room recents +"room_recents_directory_section" = "HUONELUETTELO"; +// Directory +"directory_cell_title" = "Selaa luetteloa"; +"directory_search_results_title" = "Selaa luettelon tuloksia"; +"directory_searching_title" = "Etsitään luettelosta…"; +"contacts_user_directory_section" = "KÄYTTÄJÄLUETTELO"; +"contacts_user_directory_offline_section" = "KÄYTTÄJÄLUETTELO (ei yhteydessä)"; +"unknown_devices_verify" = "Vahvista…"; +"room_details_access_section_directory_toggle" = "Listaa tämä huone huoneluettelossa"; +"room_details_fail_to_update_room_directory_visibility" = "Huoneen huoneluettelossa näkymisen muuttaminen epäonnistui"; +"room_details_fail_to_update_history_visibility" = "Huoneen keskusteluhistorian näkyvyyden muuttaminen epäonnistui"; +// Directory +"directory_title" = "Luettelo"; +"directory_server_picker_title" = "Valitse luettelo"; From 9f40c49d4d261d5975b7afb58bbf7698eeb4ac95 Mon Sep 17 00:00:00 2001 From: Krombel Date: Tue, 26 Mar 2019 15:37:54 +0000 Subject: [PATCH 016/266] Translated using Weblate (German) Currently translated at 99.5% (615 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 249422b2de..0db8211c49 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -654,3 +654,9 @@ "e2e_key_backup_wrong_version_button_wasme" = "Das war ich"; "key_backup_setup_intro_manual_export_info" = "(Erweitert)"; "key_backup_setup_intro_manual_export_action" = "Manueller Schlüssel Export"; +// String for App Store +"store_short_description" = "Sicherer, dezentralisierter Chat/VoIP"; +"store_full_description" = "Kommuniziere - auf deine Art.\n\nEine Chat-App - unter deiner Kontrolle und komplett flexibel. Riot lässt dich so kommunizieren wie du willst. Gebaut für [matrix] - dem Standard für offene, dezentrale Kommunikation.\n\nHol dir ein kostenloses Konto auf matrix.org, hol deinen eigenen Server auf https://modular.im oder nutze einen anderen Matrix-Server.\n\nWarum Riot.im wählen?\n\nKOMPLETTE KOMMUNIKATION: Baue Räume um deine Teams, deine Freunde, deine Community - wie auch immer du es willst! Chatte, teile Dateien, füge Widgets hinzu, führe Sprach- und Videogespräche - alles kostenlos.\n\nMÄCHTIGE INTEGRATIONEN: Nutze Riot.im mit den Werkzeugen die du kennst und liebst. Mit Riot.im kannst du auch mit Nutzern und Gruppen anderer Chat-Apps in Kontakt bleiben.\n\nPRIVAT UND SICHER: Halte deine Gespräche geheim. Modernste Verschlüsselung sogt dafür, dass deine private Kommunikation auch privat bleibt.\n\nOFFEN, NICHT VERSCHLOSSEN: Open Source - basierend auf [matrix]. Behalte deine Daten indem du deinen eigenen Server betreibst - oder einen wählst, dem du vertraust.\n\nWOIMMER DU AUCH BIST: Halte den Kontakt wo auch immer du bist mit einem Nachrichtenverlauf der auf allen deinen Geräten voll synchronisiert wird - in einer App oder im Browser z.B. auf https://riot.im"; +"auth_login_single_sign_on" = "Anmelden mit Single Sign-On"; +"auth_autodiscover_invalid_response" = "Ungültige Antwort beim Entdecken des Heimservers"; +"room_message_unable_open_link_error_message" = "Konnte Link nicht öffnen."; From 37a2e20ae7f6d06bc1dc5ac03a3c7b9df8db7a57 Mon Sep 17 00:00:00 2001 From: tea Date: Wed, 20 Mar 2019 19:59:50 +0000 Subject: [PATCH 017/266] Translated using Weblate (Italian) Currently translated at 100.0% (618 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/it/ --- Riot/Assets/it.lproj/Vector.strings | 601 +++++++++++++++++++++++++++- 1 file changed, 599 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/it.lproj/Vector.strings b/Riot/Assets/it.lproj/Vector.strings index e23b35b4b1..c5c5d48e9a 100644 --- a/Riot/Assets/it.lproj/Vector.strings +++ b/Riot/Assets/it.lproj/Vector.strings @@ -3,10 +3,10 @@ "title_favourites" = "Preferiti"; "title_people" = "Persone"; "title_rooms" = "Stanze"; -"store_full_description" = "Riot.im - Comunica, a modo tuo.\n\nUn'app di chat, sotto il tuo controllo e interamente flessibile. Riot ti permette di comunicare a modo tuo. Creata per [matrix] - lo standard per le comunicazioni aperte, decentralizzate.\n\nOttieni un account matrix.org gratuito, ottieni il tuo server su https://modular.im, o usa un altro server Matrix.\n\nPerchè scegliere Riot.im?\n\n• COMUNICAZIONE COMPLETA: crea stanze per i tuoi team, i tuoi amici, la tua comunità - come preferisci! Chatta, condividi file, aggiungi widget e fai videochiamate vocali - tutto gratuito.\n\n• GRANDI INTEGRAZIONI: usa Riot.im con gli strumenti che conosci ed ami. Con Riot.im puoi addirittura chattare con utenti e gruppi su altre app di chat.\n\n• PRIVATO E SICURO: tieni segrete le tue conversazioni. Una crittografia end-to-end allo stato dell'arte assicura che le comunicazioni private restino tali.\n\n• OPEN, NON CHIUSO: open source e costruito su Matrix. Possiedi i tuoi dati ospitando il tuo server personale, o scegliendone uno di cui ti fidi.\n\n• OVUNQUE TU SIA: resta in contatto ovunque tu sia con la cronologia dei messaggi totalmente sincronizzata tra i tuoi dispositivi ed online su https://riot.im."; +"store_full_description" = "Comunica, a modo tuo.\n\nUn'app di messaggistica, sotto il tuo controllo e interamente flessibile. Riot ti permette di comunicare a modo tuo. Creata per [matrix] - lo standard per le comunicazioni aperte, decentralizzate.\n\nOttieni un account matrix.org gratuito, ottieni il tuo server su https://modular.im, o usa un altro server Matrix.\n\nPerché scegliere Riot.im?\n\n• COMUNICAZIONE COMPLETA: crea stanze per i tuoi team, i tuoi amici, la tua comunità - come preferisci! Chatta, condividi file, aggiungi widget e fai videochiamate vocali - tutto gratuito.\n\n• GRANDI INTEGRAZIONI: usa Riot.im con gli strumenti che conosci ed ami. Con Riot.im puoi addirittura chattare con utenti e gruppi su altre applicazioni di messaggistica.\n\n• PRIVATO E SICURO: tieni segrete le tue conversazioni. Una crittografia end-to-end allo stato dell'arte assicura che le comunicazioni private restino tali.\n\n• APERTO, NON CHIUSO: open source e costruito su Matrix. Possiedi i tuoi dati ospitando il tuo server personale, o scegliendone uno di cui ti fidi.\n\n• OVUNQUE TU SIA: resta in contatto ovunque tu sia con la cronologia dei messaggi totalmente sincronizzata tra i tuoi dispositivi ed online su https://riot.im."; "title_groups" = "Comunità"; "warning" = "Attenzione"; -"next" = ""; +"next" = "Prossimo"; "leave" = "Lascia"; "remove" = "Rimuovi"; "invite" = "Invita"; @@ -58,3 +58,600 @@ "auth_msisdn_validation_message" = "Un SMS è stato spedito con il codice di attivazione. Per favore inserisci il codice qui sotto."; "auth_msisdn_validation_error" = "Impossibile verificare il numero di telefono."; "auth_reset_password_message" = "Per ripristinare la password, inserisci l'indirizzo email associato al tuo account:"; +// String for App Store +"store_short_description" = "Conversazioni sicure e decentralizzate"; +// Actions +"view" = "Vedi"; +"back" = "Indietro"; +"continue" = "Continua"; +"create" = "Crea"; +"start" = "Inizia"; +"retry" = "Riprova"; +"on" = "Attivo"; +"off" = "Disattivo"; +"camera" = "Fotocamera"; +"voice" = "Voce"; +"video" = "Video"; +"active_call" = "Chiamata attiva"; +"active_call_details" = "Chiamata attiva (%@)"; +"later" = "Più avanti"; +"sending" = "Inviando"; +"auth_submit" = "Invia"; +"auth_login_single_sign_on" = "Accedi con single sign-on"; +"auth_untrusted_id_server" = "Il server identità non è affidabile"; +"auth_password_dont_match" = "Le password non corrispondono"; +"auth_username_in_use" = "Nome utente in uso"; +"auth_email_not_found" = "Invio dell'email fallito: Questo indirizzo email non è stato trovato"; +"auth_use_server_options" = "Usa opzioni server personalizzate (avanzate)"; +"auth_recaptcha_message" = "Questo server home vorrebbe assicurarsi che tu non sia un robot"; +"auth_reset_password_missing_email" = "L'indirizzo email associato al tuo account deve essere inserito."; +"auth_reset_password_missing_password" = "Una nuova password deve essere inserita."; +"auth_reset_password_email_validation_message" = "Un'email è stata inviata a %s. Appena avrai seguito il link lì contenuto, clicca qui sotto."; +"auth_reset_password_next_step_button" = "Ho verificato il mio indirizzo email"; +"auth_reset_password_error_unauthorized" = "Verifica indirizzo email fallita: assicurati di aver cliccato sul link contenuto nell'email"; +"auth_reset_password_error_not_found" = "Il tuo indirizzo email non sembra associato a nessun ID Matrix su questo server home."; +"auth_reset_password_success_message" = "La tua password è stata ripristinata.\n\n Sei stato disconnesso da tutti i dispositivi e non riceverai più alcuna notifica. Per riabilitare le notifiche, riconnettiti su ciascun dispositivo."; +"auth_add_email_and_phone_warning" = "La registrazione con email e numero di telefono in una volta sola non è ancora supportata (relative API in sviluppo). Sarà utilizzato solo il numero di telefono. Puoi aggiungere la tua email al tuo profilo dalle impostazioni."; +"auth_accept_policies" = "Per favore, rivedere e accettare le politiche di questo server home:"; +"auth_autodiscover_invalid_response" = "Risposta alla scoperta di un homeserver non valida"; +// Chat creation +"room_creation_title" = "Nuova chat"; +"room_creation_account" = "Account"; +"room_creation_appearance" = "Aspetto"; +"room_creation_appearance_name" = "Nome"; +"room_creation_appearance_picture" = "Immagine chat (opzionale)"; +"room_creation_privacy" = "Privacy"; +"room_creation_private_room" = "Questa chat è privata"; +"room_creation_public_room" = "Questa chat è pubblica"; +"room_creation_make_public" = "Rendi pubblica"; +"room_creation_make_public_prompt_title" = "Rendere pubblica questa chat?"; +"room_creation_make_public_prompt_msg" = "Sei sicuro di voler rendere pubblica questa chat? Chiunque potrà leggere i tuoi messaggi e unirsi alla chat."; +"room_creation_keep_private" = "Mantieni privata"; +"room_creation_make_private" = "Rendi privata"; +"room_creation_wait_for_creation" = "Una stanza sta già venendo creata. Per favore attendi."; +"room_creation_invite_another_user" = "Cerca / invita per ID utente, nome o email"; +// Room recents +"room_recents_directory_section" = "ELENCO STANZE"; +"room_recents_directory_section_network" = "Rete"; +"room_recents_favourites_section" = "PREFERITI"; +"room_recents_people_section" = "PERSONE"; +"room_recents_conversations_section" = "STANZE"; +"room_recents_no_conversation" = "Nessuna stanza"; +"room_recents_low_priority_section" = "BASSA PRIORITÀ"; +"room_recents_server_notice_section" = "AVVISI DI SISTEMA"; +"room_recents_invites_section" = "INVITI"; +"room_recents_start_chat_with" = "Avvia chat"; +"room_recents_create_empty_room" = "Crea stanza"; +"room_recents_join_room" = "Entra nella stanza"; +"room_recents_join_room_title" = "Entra in una stanza"; +"room_recents_join_room_prompt" = "Digita ID o soprannome (alias)"; +// People tab +"people_invites_section" = "INVITI"; +"people_conversation_section" = "CONVERSAZIONI"; +"people_no_conversation" = "Nessuna conversazione"; +// Rooms tab +"room_directory_no_public_room" = "Nessuna stanza pubblica disponibile"; +// Groups tab +"group_invite_section" = "INVITI"; +"group_section" = "COMUNITÀ"; +// Search +"search_rooms" = "Stanze"; +"search_messages" = "Messaggi"; +"search_people" = "Persone"; +"search_files" = "File"; +"search_default_placeholder" = "Cerca"; +"search_people_placeholder" = "Cerca per ID utente, nome o email"; +"search_no_result" = "Nessun risultato"; +"search_in_progress" = "Ricerca…"; +// Directory +"directory_cell_title" = "Esplora elenco"; +"directory_cell_description" = "%tu stanze"; +"directory_search_results_title" = "Esplora risultati elenco"; +"directory_search_results" = "%tu risultati trovati per %@"; +"directory_search_results_more_than" = ">%tu risultati trovati per %@"; +"directory_searching_title" = "Ricerca negli elenchi…"; +"directory_search_fail" = "Fallita la ricerca di informazioni"; +// Contacts +"contacts_address_book_section" = "CONTATTI LOCALI"; +"contacts_address_book_matrix_users_toggle" = "Solo utenti Matrix"; +"contacts_address_book_no_contact" = "Nessun contatto locale"; +"contacts_address_book_permission_required" = "Permesso richiesto per accedere ai contatti"; +"contacts_address_book_permission_denied" = "Non hai permesso a Riot di accedere ai tuoi contatti"; +"contacts_user_directory_section" = "ELENCO UTENTI"; +"contacts_user_directory_offline_section" = "ELENCO UTENTI (offline)"; +// Chat participants +"room_participants_title" = "Membri"; +"room_participants_add_participant" = "Aggiungi membro"; +"room_participants_one_participant" = "1 membro"; +"room_participants_multi_participants" = "%d membri"; +"room_participants_leave_prompt_title" = "Lascia stanza"; +"room_participants_leave_prompt_msg" = "Sei sicuro di voler uscire dalla stanza?"; +"room_participants_remove_prompt_title" = "Conferma"; +"room_participants_remove_prompt_msg" = "Sei sicuro di voler rimuovere %@ da questa chat?"; +"room_participants_remove_third_party_invite_msg" = "Finché non esistono le API, la rimozione di inviti di terze parti non è possibile"; +"room_participants_invite_prompt_title" = "Conferma"; +"room_participants_invite_prompt_msg" = "Sei sicuro di voler invitare %@ in questa chat?"; +"room_participants_filter_room_members" = "Filtra i membri della stanza"; +"room_participants_invite_another_user" = "Cerca / invita per ID utente, nome o email"; +"room_participants_invite_malformed_id_title" = "Errore durante l'invito"; +"room_participants_invite_malformed_id" = "ID malformato. Dovrebbe essere un indirizzo email o un ID Matrix come '@localpart:domain'"; +"room_participants_invited_section" = "INVITATI"; +"room_participants_online" = "Online"; +"room_participants_offline" = "Offline"; +"room_participants_unknown" = "Sconosciuto"; +"room_participants_idle" = "Inattivo"; +"room_participants_now" = "adesso"; +"room_participants_ago" = "fa"; +"room_participants_action_section_admin_tools" = "Strumenti admin"; +"room_participants_action_section_direct_chats" = "Chat dirette"; +"room_participants_action_section_devices" = "Dispositivi"; +"room_participants_action_section_other" = "Altro"; +"room_participants_action_invite" = "Invita"; +"room_participants_action_leave" = "Lascia questa stanza"; +"room_participants_action_remove" = "Rimuovi da questa stanza"; +"room_participants_action_ban" = "Bandisci da questa stanza"; +"room_participants_action_unban" = "Togli il bando"; +"room_participants_action_ignore" = "Nascondi tutti i messaggi di questo utente"; +"room_participants_action_unignore" = "Mostra tutti i messaggi di questo utente"; +"room_participants_action_set_default_power_level" = "Ripristina ad utente normale"; +"room_participants_action_set_moderator" = "Rendi moderatore"; +"room_participants_action_set_admin" = "Rendi amministratore"; +"room_participants_action_start_new_chat" = "Inizia nuova chat"; +"room_participants_action_start_voice_call" = "Avvia chiamata vocale"; +"room_participants_action_start_video_call" = "Avvia videochiamata"; +"room_participants_action_mention" = "Citazione"; +// Chat +"room_jump_to_first_unread" = "Primo messaggio non letto"; +"room_new_message_notification" = "%d nuovo messaggio"; +"room_new_messages_notification" = "%d nuovi messaggi"; +"room_one_user_is_typing" = "%@ sta scrivendo…"; +"room_two_users_are_typing" = "%@ e %@ stanno scrivendo…"; +"room_many_users_are_typing" = "%@, %@ e altri stanno scrivendo…"; +"room_message_placeholder" = "Invia un messaggio (non criptato)…"; +"room_message_reply_to_placeholder" = "Invia una risposta (non criptata)…"; +"room_message_unable_open_link_error_message" = "Impossibile aprire il link."; +"room_do_not_have_permission_to_post" = "Non hai il permesso di pubblicare in questa stanza"; +"encrypted_room_message_placeholder" = "Invia un messaggio criptato…"; +"encrypted_room_message_reply_to_placeholder" = "Invia una risposta criptata…"; +"room_message_short_placeholder" = "Invia un messaggio…"; +"room_message_reply_to_short_placeholder" = "Invia una risposta…"; +"room_offline_notification" = "La connessione al server è stata persa."; +"room_unsent_messages_notification" = "Messaggi non inviati. %1$s o %2$s ora?"; +"room_unsent_messages_unknown_devices_notification" = "Messaggi non inviati a causa di dispositivi sconosciuti qui presenti. %1$s o %2$s ora?"; +"room_ongoing_conference_call" = "Chiamata di conferenza in corso. Unisciti come %@ o %@."; +"room_ongoing_conference_call_with_close" = "Chiamata di conferenza in corso. Unisciti come %@ o %@. %@."; +"room_ongoing_conference_call_close" = "Chiudi"; +"room_conference_call_no_power" = "Hai bisogno i permessi per gestire le chiamate di gruppo in questa stanza"; +"room_prompt_resend" = "Rinvia tutto"; +"room_prompt_cancel" = "annulla tutto"; +"room_resend_unsent_messages" = "Rinvia i messaggi non spediti"; +"room_delete_unsent_messages" = "Elimina messaggi non spediti"; +"room_event_action_copy" = "Copia"; +"room_event_action_quote" = "Cita"; +"room_event_action_redact" = "Rimuovi"; +"room_event_action_more" = "Altro"; +"room_event_action_share" = "Condividi"; +"room_event_action_permalink" = "Collegamento permanente"; +"room_event_action_view_source" = "Vedi sorgente"; +"room_event_action_view_decrypted_source" = "Vedi sorgente decriptato"; +"room_event_action_report" = "Segnala contenuto"; +"room_event_action_report_prompt_reason" = "Motivo per segnalare questo contenuto"; +"room_event_action_kick_prompt_reason" = "Motivo per buttare fuori questo utente"; +"room_event_action_ban_prompt_reason" = "Motivo per bandire questo utente"; +"room_event_action_report_prompt_ignore_user" = "Vuoi nascondere tutti i messaggi da questo utente?"; +"room_event_action_save" = "Salva"; +"room_event_action_resend" = "Rinvia"; +"room_event_action_delete" = "Elimina"; +"room_event_action_cancel_send" = "Annulla invio"; +"room_event_action_cancel_download" = "Annulla download"; +"room_event_action_view_encryption" = "Informazioni crittografia"; +"room_warning_about_encryption" = "La crittografia da-utente-a-utente è in fase sperimentale e potrebbe non essere affidabile.\n\nNon dovesti ancora fidarti di questo per proteggere i tuoi dati.\n\nI dispositivi non potranno decifrare la cronologia dei messaggi precedenti all'entrata nella stanza.\n\nI messaggi cifrati non saranno visibili dalle applicazioni che non hanno ancora implementato la crittografia."; +"room_event_failed_to_send" = "Invio fallito"; +"room_action_send_photo_or_video" = "Invia foto o video"; +"room_action_send_sticker" = "Invia sticker"; +"room_replacement_information" = "Questa stanza è stata sostituita e non è più attiva."; +"room_replacement_link" = "La conversazione continua qui."; +"room_predecessor_information" = "Questa stanza è la continuazione di un'altra conversazione."; +"room_predecessor_link" = "Clicca per vedere messaggi più vecchi."; +"room_resource_limit_exceeded_message_contact_1" = " Per favore "; +"room_resource_limit_exceeded_message_contact_2_link" = "contatta l'amministratore del servizio"; +"room_resource_limit_exceeded_message_contact_3" = " per continuare ad usare questo servizio."; +"room_resource_usage_limit_reached_message_1_default" = "Questo server home ha superato uno dei limiti delle risorse, pertanto "; +"room_resource_usage_limit_reached_message_1_monthly_active_user" = "Questo server home ha raggiunto il limite mensile di utenti attivi, pertanto "; +"room_resource_usage_limit_reached_message_2" = "alcuni utenti non potranno accedere."; +"room_resource_usage_limit_reached_message_contact_3" = " per aumentare questo limite."; +// Unknown devices +"unknown_devices_alert_title" = "La stanza contiene dispositivi sconosciuti"; +"unknown_devices_alert" = "Questa stanza contiene dispositivi sconosciuti che non sono stati verificati.\nCiò significa che non esiste alcuna garanzia che i dispositivi appartengano agli utenti che ne rivendicano il possesso.\nSi consiglia di passare attraverso il processo di verifica per ogni dispositivo prima di continuare, ma è possibile inviare ugualmente il messaggio senza fare la verifica se preferisci."; +"unknown_devices_send_anyway" = "Invia comunque"; +"unknown_devices_call_anyway" = "Chiama comunque"; +"unknown_devices_answer_anyway" = "Rispondi comunque"; +"unknown_devices_verify" = "Verifica…"; +"unknown_devices_title" = "Dispositivi sconosciuti"; +// Room Title +"room_title_new_room" = "Nuova stanza"; +"room_title_multiple_active_members" = "%@/%@ membri attivi"; +"room_title_one_active_member" = "%@/%@ membro attivo"; +"room_title_invite_members" = "Invita membri"; +"room_title_members" = "%@ membri"; +"room_title_one_member" = "1 membro"; +// Room Preview +"room_preview_invitation_format" = "Sei stato invitato ad unirti in questa stanza da %@"; +"room_preview_subtitle" = "Questa è l'anteprima della stanza. Le interazioni con la stanza sono state disabilitate."; +"room_preview_unlinked_email_warning" = "Questo invito è stato spedito da %@, che non è associato a questo account. È possibile che tu voglia connetterti con un altro account, o aggiungere l'email al tuo account."; +"room_preview_try_join_an_unknown_room" = "Stai provando ad accedere a %@. Desideri entrare per partecipare alla discussione?"; +"room_preview_try_join_an_unknown_room_default" = "una stanza"; +// Settings +"settings_title" = "Impostazioni"; +"account_logout_all" = "Sconnetti tutti gli account"; +"settings_config_no_build_info" = "Nessuna informazione di compilazione"; +"settings_mark_all_as_read" = "Segna tutti i messaggi come letti"; +"settings_report_bug" = "Segnala errore"; +"settings_clear_cache" = "Elimina cache"; +"settings_config_home_server" = "Il server home è %@"; +"settings_config_identity_server" = "Il server identità è %@"; +"settings_config_user_id" = "Connesso come %@"; +"settings_user_settings" = "IMPOSTAZIONI UTENTE"; +"settings_notifications_settings" = "IMPOSTAZIONI NOTIFICHE"; +"settings_calls_settings" = "CHIAMATE"; +"settings_user_interface" = "INTERFACCIA UTENTE"; +"settings_ignored_users" = "UTENTI IGNORATI"; +"settings_contacts" = "CONTATTI LOCALI"; +"settings_advanced" = "AVANZATE"; +"settings_other" = "ALTRO"; +"settings_labs" = "LABORATORIO"; +"settings_flair" = "Mostra predisposizione se permesso"; +"settings_devices" = "DISPOSITIVI"; +"settings_cryptography" = "CRITTOGRAFIA"; +"settings_key_backup" = "BACKUP CHIAVE"; +"settings_deactivate_account" = "DISATTIVA ACCOUNT"; +"settings_sign_out" = "Disconnetti"; +"settings_sign_out_confirmation" = "Sei sicuro?"; +"settings_sign_out_e2e_warn" = "Perderai le tue chiavi di crittografia da-utente-a-utente. Questo significa che non potrai più leggere i vecchi messaggi nelle stanze cifrate su questo dispositivo."; +"settings_profile_picture" = "Immagine profilo"; +"settings_display_name" = "Nome visualizzato"; +"settings_first_name" = "Nome"; +"settings_surname" = "Cognome"; +"settings_remove_prompt_title" = "Conferma"; +"settings_remove_email_prompt_msg" = "Sei sicuro di voler rimuovere l'indirizzo email %@?"; +"settings_remove_phone_prompt_msg" = "Sei sicuro di voler rimuovere il numero di telefono %@?"; +"settings_email_address" = "Email"; +"settings_email_address_placeholder" = "Inserisci il tuo indirizzo email"; +"settings_add_email_address" = "Aggiungi indirizzo email"; +"settings_phone_number" = "Telefono"; +"settings_add_phone_number" = "Aggiungi numero di telefono"; +"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"; +"settings_show_decrypted_content" = "Mostra contenuto decriptato"; +"settings_global_settings_info" = "Impostazioni globali di notifica sono disponibili nel tuo %@ web client"; +"settings_pin_rooms_with_missed_notif" = "Segna le stanze con notifiche perse"; +"settings_pin_rooms_with_unread" = "Segna le stanze con messaggi non letti"; +"settings_on_denied_notification" = "Le notifiche non sono permesse per %@, abilitale nelle impostazioni del tuo dispositivo"; +"settings_enable_callkit" = "Chiamate integrate"; +"settings_callkit_info" = "Ricevi le chiamate in arrivo nel blocca schermo. Mostra le chiamate Riot nella cronologia di chiamate del dispositivo. Se iCloud è attivo, questa cronologia sarà condivisa con Apple."; +"settings_ui_language" = "Lingua"; +"settings_ui_theme" = "Tema"; +"settings_ui_theme_auto" = "Automatico"; +"settings_ui_theme_light" = "Chiaro"; +"settings_ui_theme_dark" = "Scuro"; +"settings_ui_theme_black" = "Nero"; +"settings_ui_theme_picker_title" = "Seleziona un tema"; +"settings_ui_theme_picker_message" = "\"Automatico\" usa l'impostazione \"Inverti Colori\" del tuo dispositivo"; +"settings_unignore_user" = "Mostrare tutti i messaggi da %@?"; +"settings_contacts_discover_matrix_users" = "Usa email e numeri di telefono per trovare utenti"; +"settings_contacts_phonebook_country" = "Nazione rubrica telefonica"; +"settings_labs_e2e_encryption" = "Crittografia da-utente-a-utente"; +"settings_labs_e2e_encryption_prompt_message" = "Per finire la configurazione della crittografia devi rieseguire l'accesso."; +"settings_labs_room_members_lazy_loading" = "Caricamento posticipato dei membri della stanza"; +"settings_labs_room_members_lazy_loading_error_message" = "Il tuo server home non supporta ancora il caricamento posticipato dei membri delle stanze. Prova in seguito."; +"settings_labs_create_conference_with_jitsi" = "Crea una videoconferenza con jitsi"; +"settings_version" = "Versione %@"; +"settings_olm_version" = "Versione olm %@"; +"settings_copyright" = "Copyright"; +"settings_copyright_url" = "https://riot.im/copyright"; +"settings_term_conditions" = "Termini e condizioni"; +"settings_term_conditions_url" = "https://riot.im/tac_apple"; +"settings_privacy_policy" = "Politica sulla privacy"; +"settings_privacy_policy_url" = "https://riot.im/privacy"; +"settings_third_party_notices" = "Avvisi di terze parti"; +"settings_send_crash_report" = "Invia dati di utilizzo anonimi"; +"settings_enable_rageshake" = "Agita con rabbia per segnalare un errore"; +"settings_old_password" = "vecchia password"; +"settings_new_password" = "nuova password"; +"settings_confirm_password" = "conferma password"; +"settings_fail_to_update_password" = "Fallito l'aggiornamento password"; +"settings_password_updated" = "La tua password è stata aggiornata"; +"settings_crypto_device_name" = "Nome dispositivo: "; +"settings_crypto_device_id" = "\nID Dispositivo: "; +"settings_crypto_device_key" = "\nChiave dispositivo: "; +"settings_crypto_export" = "Esporta chiavi"; +"settings_crypto_blacklist_unverified_devices" = "Cripta solo per i dispositivi verificati"; +"settings_deactivate_my_account" = "Disattiva il mio account"; +"settings_key_backup_info" = "I messaggi criptati sono protetti con la cifratura end-to-end. Solo tu e il/i destinatario/i avete le chiavi per leggere questi messaggi."; +"settings_key_backup_info_checking" = "Verifica..."; +"settings_key_backup_info_none" = "Nessun backup programmato per le chiavi da questo dispositivo."; +"settings_key_backup_info_signout_warning" = "Fai un backup sicuro delle tue chiavi prima di uscire per evitare di perderle."; +"settings_key_backup_info_version" = "Versione backup chiave: %@"; +"settings_key_backup_info_algorithm" = "Algoritmo: %@"; +"settings_key_backup_info_valid" = "Questo dispositivo sta eseguendo il backup delle tue chiavi."; +"settings_key_backup_info_not_valid" = "Questo dispositivo non sta eseguendo il backup delle tue chiavi."; +"settings_key_backup_info_progress" = "Backup di %@ chiavi…"; +"settings_key_backup_info_progress_done" = "Backup di tutte le chiavi completato"; +"settings_key_backup_info_trust_signature_unknown" = "Il backup ha una firma dal dispositivo con ID: %@"; +"settings_key_backup_info_trust_signature_valid" = "Il backup ha una firma valida da questo dispositivo"; +"settings_key_backup_info_trust_signature_valid_device_verified" = "Il backup ha una firma valida da %@"; +"settings_key_backup_info_trust_signature_valid_device_unverified" = "Il backup ha una firma da %@"; +"settings_key_backup_info_trust_signature_invalid_device_verified" = "Il backup ha una firma non valida da %@"; +"settings_key_backup_info_trust_signature_invalid_device_unverified" = "Il backup ha una firma non valida da %@"; +"settings_key_backup_button_create" = "Inizia ad usare il backup chiavi"; +"settings_key_backup_button_restore" = "Ripristina da backup"; +"settings_key_backup_button_delete" = "Elimina backup"; +"settings_key_backup_delete_confirmation_prompt_title" = "Elimina backup"; +"settings_key_backup_delete_confirmation_prompt_msg" = "Sei sicuro? Perderai i tuoi messaggi cifrati se le tue chiavi non sono salvate correttamente."; +// Room Details +"room_details_title" = "Dettagli stanza"; +"room_details_people" = "Membri"; +"room_details_files" = "File"; +"room_details_settings" = "Impostazioni"; +"room_details_photo" = "Immagine stanza"; +"room_details_room_name" = "Nome stanza"; +"room_details_topic" = "Argomento"; +"room_details_favourite_tag" = "Preferito"; +"room_details_low_priority_tag" = "Bassa priorità"; +"room_details_mute_notifs" = "Silenzia notifiche"; +"room_details_direct_chat" = "Chat diretta"; +"room_details_access_section" = "Chi può entrare in questa stanza?"; +"room_details_access_section_invited_only" = "Solo le persone che sono state invitate"; +"room_details_access_section_anyone_apart_from_guest" = "Chiunque conosca il link della stanza, eccetto gli ospiti"; +"room_details_access_section_anyone" = "Chiunque conosca il link della stanza, compresi gli ospiti"; +"room_details_access_section_no_address_warning" = "Per indirizzare ad una stanza tramite link, essa deve avere un indirizzo"; +"room_details_access_section_directory_toggle" = "Mostra questa stanza nell'elenco delle stanze"; +"room_details_history_section" = "Chi può leggere la cronologia?"; +"room_details_history_section_anyone" = "Chiunque"; +"room_details_history_section_members_only" = "Solo i membri (dal momento in cui questa opzione è stata selezionata)"; +"room_details_history_section_members_only_since_invited" = "Solo i membri (dal momento in cui vengono invitati)"; +"room_details_history_section_members_only_since_joined" = "Solo i membri (dal momento in cui entrano nella stanza)"; +"room_details_history_section_prompt_title" = "Avvertimento sulla privacy"; +"room_details_history_section_prompt_msg" = "La leggibilità della cronologia sarà modificata solo per i futuri messaggi di questa stanza. La visibilità dei messaggi esistenti rimarrà invariata."; +"room_details_addresses_section" = "Indirizzi"; +"room_details_no_local_addresses" = "Questa stanza non ha indirizzi locali"; +"room_details_new_address" = "Aggiungi nuovo indirizzo"; +"room_details_new_address_placeholder" = "Aggiungi nuovo indirizzo (es. #foo%@)"; +"room_details_addresses_invalid_address_prompt_title" = "Formato soprannome non valido"; +"room_details_addresses_invalid_address_prompt_msg" = "%@ non è un formato valido per un soprannome"; +"room_details_addresses_disable_main_address_prompt_title" = "Avviso per l'indirizzo principale"; +"room_details_addresses_disable_main_address_prompt_msg" = "Non avrai nessun indirizzo principale specificato. L'indirizzo principale per questa stanza sarà selezionato casualmente"; +"room_details_flair_section" = "Mostra insegna per le comunità"; +"room_details_new_flair_placeholder" = "Aggiungi nuovo ID della comunità (es. +foo%@)"; +"room_details_flair_invalid_id_prompt_title" = "Formato non valido"; +"room_details_flair_invalid_id_prompt_msg" = "%@ non è un identificatore valido per una comunità"; +"room_details_banned_users_section" = "Utenti banditi"; +"room_details_advanced_section" = "Avanzate"; +"room_details_advanced_room_id" = "ID stanza:"; +"room_details_advanced_enable_e2e_encryption" = "Abilita crittografia (attenzione: non può più essere disabilitata!)"; +"room_details_advanced_e2e_encryption_enabled" = "Crittografia abilitata in questa stanza"; +"room_details_advanced_e2e_encryption_disabled" = "Crittografia non abilitata in questa stanza."; +"room_details_advanced_e2e_encryption_blacklist_unverified_devices" = "Cripta solo per i dispositivi verificati"; +"room_details_fail_to_update_avatar" = "Fallita modifica dell'immagine stanza"; +"room_details_fail_to_update_room_name" = "Fallita modifica del nome stanza"; +"room_details_fail_to_update_topic" = "Fallita modifica dell'argomento"; +"room_details_fail_to_update_room_guest_access" = "Fallita modifica dell'accesso agli ospiti alla stanza"; +"room_details_fail_to_update_room_join_rule" = "Fallita modifica delle regole di accesso"; +"room_details_fail_to_update_room_directory_visibility" = "Fallita modifica della visibilità nell'elenco stanze"; +"room_details_fail_to_update_history_visibility" = "Fallita modifica della visibilità della cronologia"; +"room_details_fail_to_add_room_aliases" = "Fallita aggiunta dei nuovi indirizzi stanza"; +"room_details_fail_to_remove_room_aliases" = "Fallita rimozione degli indirizzi stanza"; +"room_details_fail_to_update_room_canonical_alias" = "Fallita modifica dell'indirizzo principale"; +"room_details_fail_to_update_room_communities" = "Fallita modifica delle comunità collegate"; +"room_details_fail_to_update_room_direct" = "Fallita modifica nel rendere diretta questa stanza"; +"room_details_fail_to_enable_encryption" = "Fallita attivazione della cifratura in questa stanza"; +"room_details_save_changes_prompt" = "Vuoi salvare le modifiche?"; +"room_details_set_main_address" = "Imposta come indirizzo principale"; +"room_details_unset_main_address" = "Non più come indirizzo principale"; +"room_details_copy_room_id" = "Copia ID stanza"; +"room_details_copy_room_address" = "Copia indirizzo stanza"; +"room_details_copy_room_url" = "Copia URL stanza"; +// Group Details +"group_details_title" = "Dettagli comunità"; +"group_details_home" = "Home"; +"group_details_people" = "Persone"; +"group_details_rooms" = "Stanze"; +// Group Home +"group_home_one_member_format" = "1 membro"; +"group_home_multi_members_format" = "%tu membri"; +"group_home_one_room_format" = "1 stanza"; +"group_home_multi_rooms_format" = "%tu stanze"; +"group_invitation_format" = "%@ ti ha invitato ad unirti a questa comunità"; +// Group participants +"group_participants_add_participant" = "Aggiungi membri"; +"group_participants_leave_prompt_title" = "Lascia stanza"; +"group_participants_leave_prompt_msg" = "Sei sicuro di voler uscire dalla stanza?"; +"group_participants_remove_prompt_title" = "Conferma"; +"group_participants_remove_prompt_msg" = "Sei sicuro di voler rimuovere %@ da questa chat?"; +"group_participants_invite_prompt_title" = "Conferma"; +"group_participants_invite_prompt_msg" = "Sei sicuro di voler invitare %@ in questa stanza?"; +"group_participants_filter_members" = "Filtra membri comunità"; +"group_participants_invite_another_user" = "Cerca / invita per ID utente o nome"; +"group_participants_invite_malformed_id_title" = "Invito fallito"; +"group_participants_invite_malformed_id" = "ID malformato. Dovrebbe essere ID Matrix come '@localpart:domain'"; +"group_participants_invited_section" = "INVITATI"; +// Group rooms +"group_rooms_filter_rooms" = "Filtra stanze comunità"; +// Read Receipts +"read_receipts_list" = "Elenco ricevute di lettura"; +"receipt_status_read" = "Letto: "; +// Media picker +"media_picker_library" = "Galleria"; +"media_picker_select" = "Seleziona"; +// Directory +"directory_title" = "Elenco"; +"directory_server_picker_title" = "Seleziona un elenco"; +"directory_server_all_rooms" = "Tutte le stanze sul server %@"; +"directory_server_all_native_rooms" = "Tutte le stanze Matrix native"; +"directory_server_type_homeserver" = "Digita un server home per elencare le sue stanze pubbliche"; +"directory_server_placeholder" = "matrix.org"; +// Events formatter +"event_formatter_member_updates" = "%tu cambi d'appartenenza"; +"event_formatter_widget_added" = "%@ widget aggiunto da %@"; +"event_formatter_widget_removed" = "%@ widget rimosso da %@"; +"event_formatter_jitsi_widget_added" = "Conferenza VoIP aggiunta da %@"; +"event_formatter_jitsi_widget_removed" = "Conferenza VoIP rimossa da %@"; +"event_formatter_rerequest_keys_part1_link" = "Richiedi di nuovo le chiavi di cifratura"; +"event_formatter_rerequest_keys_part2" = " dai tuoi altri dispositivi."; +// Others +"or" = "o"; +"you" = "Tu"; +"today" = "Oggi"; +"yesterday" = "Ieri"; +"network_offline_prompt" = "La connessione a internet sembra essere disconnessa."; +"homeserver_connection_lost" = "Non riesco a connettermi al server home."; +"public_room_section_title" = "Stanze pubbliche (in %@):"; +"bug_report_prompt" = "L'applicazione è andata in crash l'ultima volta. Desideri aprire la schermata di segnalazione del crash?"; +"rage_shake_prompt" = "Sembra che tu stia scuotendo il dispositivo con frustrazione. Desideri aprire la schermata di segnalazione errore?"; +"do_not_ask_again" = "Non chiedere più"; +"camera_access_not_granted" = "%@ non ha il permesso per usare la fotocamera, modifica le impostazioni privacy"; +"large_badge_value_k_format" = "%.1fK"; +"room_does_not_exist" = "%@ non esiste"; +// Call +"call_incoming_voice_prompt" = "Chiamata vocale in arrivo da %@"; +"call_incoming_video_prompt" = "Videochiamata in arrivo da %@"; +"call_incoming_voice" = "Chiamata in arrivo..."; +"call_incoming_video" = "Videochiamata in arrivo..."; +"call_already_displayed" = "Già presente una chiamata in corso."; +"call_jitsi_error" = "Fallito ingresso alla chiamata di conferenza."; +// No VoIP support +"no_voip_title" = "Chiamata in arrivo"; +"no_voip" = "%@ ti sta chiamando, ma %@ non supporta ancora le chiamate.\nPuoi ignorare questa notifica e rispondere alla chiamata da un altro dispositivo o puoi rifiutare la chiamata."; +// Crash report +"google_analytics_use_prompt" = "Vuoi aiutare a migliorare %@ inviando automaticamente in modo anonimo i dati di utilizzo e le segnalazioni di crash?"; +// Crypto +"e2e_enabling_on_app_update" = "Riot ora supporta la crittografia da-utente-a-utente ma devi rieseguire l'accesso per abilitarla.\n\nPuoi farlo adesso oppure dopo dalle impostazioni dell'applicazione."; +"e2e_need_log_in_again" = "È necessario eseguire nuovamente l'accesso per generare le chiavi di crittografia da-utente-a-utente per questo dispositivo ed inviare la chiave pubblica al server home.\nCiò può capitare solo una volta; ci scusiamo per l'inconveniente."; +// Key backup wrong version +"e2e_key_backup_wrong_version_title" = "Nuovo backup chiave"; +"e2e_key_backup_wrong_version" = "È stato rilevato un nuovo backup chiavi per messaggi sicuri. \n\nSe non sei stato tu, imposta una nuova passphrase dalle impostazioni."; +"e2e_key_backup_wrong_version_button_settings" = "Impostazioni"; +"e2e_key_backup_wrong_version_button_wasme" = "Sono stato io"; +// Bug report +"bug_report_title" = "Rapporto errori"; +"bug_report_description" = "Per favore descrivi l'errore. Cosa hai fatto? Cosa ti aspettavi dovesse accadere? Cosa è effettivamente successo?"; +"bug_crash_report_title" = "Segnalazione del crash"; +"bug_crash_report_description" = "Per favore descrivi cosa hai fatto prima del crash:"; +"bug_report_logs_description" = "Al fine di diagnosticare i problemi, i registri Riot di questo dispositivo saranno inviati con il rapporto dell'errore. Se preferisci inviare solo il testo soprastante, deseleziona:"; +"bug_report_send_logs" = "Invia registri"; +"bug_report_send_screenshot" = "Invia screenshot"; +"bug_report_progress_zipping" = "Ottenimento registri"; +"bug_report_progress_uploading" = "Invio rapporto"; +"bug_report_send" = "Invia"; +// Widget +"widget_no_power_to_manage" = "Hai bisogno dei permessi per gestire i widget in questa stanza"; +"widget_creation_failure" = "Creazione del widget fallita"; +"widget_sticker_picker_no_stickerpacks_alert" = "Non hai ancora alcun pacco di adesivi attivo."; +"widget_sticker_picker_no_stickerpacks_alert_add_now" = "Aggiungerne qualcuno ora?"; +// Widget Integration Manager +"widget_integration_need_to_be_able_to_invite" = "Devi poter inviare utenti per compiere questa azione."; +"widget_integration_unable_to_create" = "Impossibile creare il widget."; +"widget_integration_failed_to_send_request" = "Invio della richiesta fallito."; +"widget_integration_room_not_recognised" = "Questa stanza non è riconosciuta."; +"widget_integration_positive_power_level" = "Il livello di potere deve essere un intero positivo."; +"widget_integration_must_be_in_room" = "Non sei in questa stanza."; +"widget_integration_no_permission_in_room" = "Non hai i permessi per eseguire l'azione in questa stanza."; +"widget_integration_missing_room_id" = "room_id mancante nella richiesta."; +"widget_integration_missing_user_id" = "user_id mancante nella richiesta."; +"widget_integration_room_not_visible" = "La stanza %@ non è visibile."; +// Share extension +"share_extension_auth_prompt" = "Esegui l'accesso nell'app principale per condividere il contenuto"; +"share_extension_failed_to_encrypt" = "Invio fallito. Controlla le impostazioni di crittografia per questa stanza"; +// Room key request dialog +"e2e_room_key_request_title" = "Richiesta chiave di crittografia"; +"e2e_room_key_request_message_new_device" = "Hai aggiunto un nuovo dispositivo '%@', che sta richiedendo le chiavi crittografiche."; +"e2e_room_key_request_message" = "Il tuo dispositivo non verificato '%@' sta richiedendo le chiavi crittografiche."; +"e2e_room_key_request_start_verification" = "Inizia la verifica..."; +"e2e_room_key_request_share_without_verifying" = "Condividi senza verificare"; +"e2e_room_key_request_ignore_request" = "Ignora richiesta"; +// GDPR +"gdpr_consent_not_given_alert_message" = "Per continuare ad usare l'homeserver %@ devi leggere e accettare i termini e le condizioni di utilizzo."; +"gdpr_consent_not_given_alert_review_now_action" = "Leggi adesso"; +"deactivate_account_title" = "Disattiva account"; +"deactivate_account_informations_part1" = "Questo renderà il tuo account permanentemente inutilizzabile. Non ci potrai più accedere e nessuno potrà ri-registrare lo stesso ID utente. Il tuo account abbandonerà tutte le stanze a cui partecipa e i dettagli del tuo account saranno rimossi dal server identità. "; +"deactivate_account_informations_part2_emphasize" = "Questa azione è irreversibile."; +"deactivate_account_informations_part3" = "\n\nDisattivare il tuo account "; +"deactivate_account_informations_part4_emphasize" = "non eliminerà in modo automatico i messaggi che hai inviato. "; +"deactivate_account_informations_part5" = "Se vuoi che siano dimenticati i tuoi messaggi, seleziona la casella qua sotto\n\nLa visibilità dei messaggi in Matrix è simile alle email. \"Dimenticare i tuoi messaggi\" significa che quelli che hai inviato non verranno condivisi con alcun utente nuovo o non registrato, ma gli utenti registrati che hanno avuto accesso ai tuoi messaggi avranno ancora accesso alla loro copia."; +"deactivate_account_forget_messages_information_part1" = "Per favore dimenticate tutti i messaggi che ho inviato al momento della disattivazione del mio account ("; +"deactivate_account_forget_messages_information_part2_emphasize" = "Attenzione"; +"deactivate_account_forget_messages_information_part3" = ": gli utenti futuri vedranno un elenco incompleto di conversazioni)"; +"deactivate_account_validate_action" = "Disattiva account"; +"deactivate_account_password_alert_title" = "Disattiva account"; +"deactivate_account_password_alert_message" = "Per proseguire, inserisci la tua password"; +// Re-request confirmation dialog +"rerequest_keys_alert_title" = "Richiesta inviata"; +"rerequest_keys_alert_message" = "Avvia Riot su un altro dispositivo che possa decifrare il messaggio, in modo da inviare le chiavi a questo dispositivo."; +"key_backup_setup_title" = "Backup chiave"; +"key_backup_setup_skip_alert_title" = "Sei sicuro?"; +"key_backup_setup_skip_alert_message" = "Potresti perdere i tuoi messaggi cifrati se ti disconnetti o perdi il dispositivo."; +"key_backup_setup_skip_alert_skip_action" = "Salta"; +"key_backup_setup_intro_title" = "Non perdere mai i messaggi cifrati"; +"key_backup_setup_intro_info" = "I messaggi nelle stanze cifrate sono protetti con la cifratura end-to-end. Solo tu e il/i destinatario/i avete le chiavi per leggere questi messaggi. \n \nFai un backup sicuro delle tue chiavi per evitare di perderle."; +"key_backup_setup_intro_setup_action_without_existing_backup" = "Inizia ad usare il backup chiavi"; +"key_backup_setup_intro_manual_export_info" = "(Avanzato)"; +"key_backup_setup_intro_manual_export_action" = "Esporta manualmente le chiavi"; +"key_backup_setup_passphrase_title" = "Proteggi il tuo backup con una frase d'accesso"; +"key_backup_setup_passphrase_info" = "Salveremo una copia cifrata delle tue chiavi nel tuo homeserver. Proteggi il tuo backup con una frase d'accesso (passphrase) per tenerlo sicuro. \n \nPer una massima sicurezza, dovrebbe essere diversa dalla password del tuo account."; +"key_backup_setup_passphrase_passphrase_title" = "Inserisci"; +"key_backup_setup_passphrase_passphrase_placeholder" = "Inserisci passphrase"; +"key_backup_setup_passphrase_passphrase_valid" = "Bene!"; +"key_backup_setup_passphrase_passphrase_invalid" = "Prova ad aggiungere una parola"; +"key_backup_setup_passphrase_confirm_passphrase_title" = "Conferma"; +"key_backup_setup_passphrase_confirm_passphrase_placeholder" = "Conferma passphrase"; +"key_backup_setup_passphrase_confirm_passphrase_valid" = "Bene!"; +"key_backup_setup_passphrase_confirm_passphrase_invalid" = "Le passphrase devono corrispondere"; +"key_backup_setup_passphrase_set_passphrase_action" = "Imposta passphrase"; +"key_backup_setup_passphrase_setup_recovery_key_info" = "Oppure, proteggi il tuo backup con una chiave di ripristino, salvandola in un luogo sicuro."; +"key_backup_setup_passphrase_setup_recovery_key_action" = "(Avanzato) Imposta con chiave di ripristino"; +"key_backup_setup_success_title" = "Completato!"; +// Success from passphrase +"key_backup_setup_success_from_passphrase_info" = "Backup delle tue chiavi in corso.\n\nLa tua chiave di ripristino è una rete di sicurezza - puoi usarla per recuperare l'accesso ai tuoi messaggi cifrati se dimentichi la tua passphrase. \n\nTieni la tua chiave di ripristino in un luogo sicuro, come un password manager (o una cassaforte)."; +"key_backup_setup_success_from_passphrase_save_recovery_key_action" = "Salva chiave di ripristino"; +"key_backup_setup_success_from_passphrase_done_action" = "Fatto"; +// Success from recovery key +"key_backup_setup_success_from_recovery_key_info" = "Backup delle tue chiavi in corso.\n\nFai una copia di questa chiave di ripristino e tienila al sicuro."; +"key_backup_setup_success_from_recovery_key_recovery_key_title" = "Chiave di ripristino"; +"key_backup_setup_success_from_recovery_key_make_copy_action" = "Fai una copia"; +"key_backup_setup_success_from_recovery_key_made_copy_action" = "Ho fatto una copia"; +"key_backup_recover_title" = "Messaggi sicuri"; +"key_backup_recover_invalid_passphrase_title" = "Passphrase di ripristino non corretta"; +"key_backup_recover_invalid_passphrase" = "Impossibile decifrare il backup con questa passphrase: verifica di avere inserito la passphrase di ripristino corretta."; +"key_backup_recover_invalid_recovery_key_title" = "La chiave di ripristino non corrisponde"; +"key_backup_recover_invalid_recovery_key" = "Impossibile decifrare il backup con questa chiave di ripristino: verifica di avere inserito la chiave di ripristino corretta."; +"key_backup_recover_from_passphrase_info" = "Usa la tua chiave di ripristino per sbloccare la cronologia dei messaggi cifrati"; +"key_backup_recover_from_passphrase_passphrase_title" = "Inserisci"; +"key_backup_recover_from_passphrase_passphrase_placeholder" = "Inserisci passphrase"; +"key_backup_recover_from_passphrase_recover_action" = "Sblocca cronologia"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part1" = "Non conosci la tua passphrase di ripristino? Puoi "; +"key_backup_recover_from_passphrase_lost_passphrase_action_part2" = "usa la tua chiave di ripristino"; +"key_backup_recover_from_passphrase_lost_passphrase_action_part3" = "."; +"key_backup_recover_from_recovery_key_info" = "Usa la tua chiave di ripristino per sbloccare la cronologia dei tuoi messaggi sicuri"; +"key_backup_recover_from_recovery_key_recovery_key_title" = "Inserisci"; +"key_backup_recover_from_recovery_key_recovery_key_placeholder" = "Inserisci chiave di ripristino"; +"key_backup_recover_from_recovery_key_recover_action" = "Sblocca cronologia"; +"key_backup_recover_from_recovery_key_lost_recovery_key_action" = "Hai perso la chiave di ripristino? Puoi crearne una nuova nelle impostazioni."; +"key_backup_recover_success_info" = "Backup ripristinato!"; +"key_backup_recover_done_action" = "Fatto"; +"key_backup_setup_banner_title" = "Non perdere mai i messaggi cifrati"; +"key_backup_setup_banner_subtitle" = "Inizia ad usare il backup chiavi"; +"key_backup_recover_banner_title" = "Non perdere mai i messaggi cifrati"; +"sign_out_existing_key_backup_alert_title" = "Sei sicuro di volerti disconnettere?"; +"sign_out_existing_key_backup_alert_sign_out_action" = "Disconnetti"; +"sign_out_non_existing_key_backup_alert_title" = "Se ti disconnetti ora, perderai l'accesso ai tuoi messaggi cifrati"; +"sign_out_non_existing_key_backup_alert_setup_key_backup_action" = "Inizia ad usare il backup chiavi"; +"sign_out_non_existing_key_backup_alert_discard_key_backup_action" = "Non voglio i miei messaggi cifrati"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_title" = "Perderai i tuoi messaggi cifrati"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_message" = "Perderai l'accesso ai tuoi messaggi cifrati a meno che non fai il backup delle tue chiavi prima di disconnetterti."; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_sign_out_action" = "Disconnetti"; +"sign_out_non_existing_key_backup_sign_out_confirmation_alert_backup_action" = "Backup"; +"sign_out_key_backup_in_progress_alert_title" = "Backup chiave in corso. Se ti disconnetti ora, perderai l'accesso ai tuoi messaggi cifrati."; +"sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Non voglio i miei messaggi cifrati"; +"sign_out_key_backup_in_progress_alert_cancel_action" = "Aspetto"; From e419eecfeb6bbcf2d230b28579bc4c419314720b Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 15 May 2019 15:58:38 +0200 Subject: [PATCH 018/266] Reactions: Add primary reactions as message actions --- Riot.xcodeproj/project.pbxproj | 40 ++++++++ .../ReactionsMenu/ReactionsMenuAction.swift | 23 +++++ .../ReactionsMenuReactions.swift | 24 +++++ .../ReactionsMenu/ReactionsMenuView.swift | 64 ++++++++++++ .../ReactionsMenu/ReactionsMenuView.xib | 94 ++++++++++++++++++ .../ReactionsMenuViewModel.swift | 99 +++++++++++++++++++ .../ReactionsMenuViewModelType.swift | 38 +++++++ 7 files changed, 382 insertions(+) create mode 100644 Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuAction.swift create mode 100644 Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuReactions.swift create mode 100644 Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift create mode 100644 Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib create mode 100644 Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift create mode 100644 Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 08f2bccf2e..50fe5f59ab 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -65,6 +65,12 @@ 324A2054225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324A204C225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift */; }; 324A2055225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324A204D225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift */; }; 324A2056225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324A204E225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift */; }; + 325380D1228C1BE500ADDEFA /* ReactionsMenuAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D0228C1BE500ADDEFA /* ReactionsMenuAction.swift */; }; + 325380D3228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D2228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift */; }; + 325380D5228C245D00ADDEFA /* ReactionsMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D4228C245D00ADDEFA /* ReactionsMenuViewModel.swift */; }; + 325380D7228C2E5800ADDEFA /* ReactionsMenuReactions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D6228C2E5800ADDEFA /* ReactionsMenuReactions.swift */; }; + 325380DB228C34EF00ADDEFA /* ReactionsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */; }; + 325380DD228C34FC00ADDEFA /* ReactionsMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 325380DC228C34FC00ADDEFA /* ReactionsMenuView.xib */; }; 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; }; 3281BCF72201FA4200F4A383 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3281BCF62201FA4200F4A383 /* UIControl.swift */; }; 3284A35120A07C210044F922 /* postMessageAPI.js in Resources */ = {isa = PBXBuildFile; fileRef = 3284A35020A07C210044F922 /* postMessageAPI.js */; }; @@ -562,6 +568,12 @@ 324A204C225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingCoordinatorType.swift; sourceTree = ""; }; 324A204D225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingViewModelType.swift; sourceTree = ""; }; 324A204E225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingCoordinator.swift; sourceTree = ""; }; + 325380D0228C1BE500ADDEFA /* ReactionsMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuAction.swift; sourceTree = ""; }; + 325380D2228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModelType.swift; sourceTree = ""; }; + 325380D4228C245D00ADDEFA /* ReactionsMenuViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModel.swift; sourceTree = ""; }; + 325380D6228C2E5800ADDEFA /* ReactionsMenuReactions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuReactions.swift; sourceTree = ""; }; + 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuView.swift; sourceTree = ""; }; + 325380DC228C34FC00ADDEFA /* ReactionsMenuView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReactionsMenuView.xib; sourceTree = ""; }; 3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = ""; }; 3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 3267EFB520E379FD00FF1CAA /* AUTHORS.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS.rst; sourceTree = ""; }; @@ -1406,6 +1418,27 @@ path = Incoming; sourceTree = ""; }; + 325380CE228C180000ADDEFA /* ContextualMenu */ = { + isa = PBXGroup; + children = ( + 325380CF228C181400ADDEFA /* ReactionsMenu */, + ); + path = ContextualMenu; + sourceTree = ""; + }; + 325380CF228C181400ADDEFA /* ReactionsMenu */ = { + isa = PBXGroup; + children = ( + 325380D0228C1BE500ADDEFA /* ReactionsMenuAction.swift */, + 325380D2228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift */, + 325380D4228C245D00ADDEFA /* ReactionsMenuViewModel.swift */, + 325380D6228C2E5800ADDEFA /* ReactionsMenuReactions.swift */, + 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */, + 325380DC228C34FC00ADDEFA /* ReactionsMenuView.xib */, + ); + path = ReactionsMenu; + sourceTree = ""; + }; 32891D682264C6A000C82226 /* SimpleScreenTemplate */ = { isa = PBXGroup; children = ( @@ -1985,6 +2018,7 @@ B1B5568E20EE6C4C00210D55 /* Room */ = { isa = PBXGroup; children = ( + 325380CE228C180000ADDEFA /* ContextualMenu */, B1B5568F20EE6C4C00210D55 /* RoomViewController.h */, B1B556A020EE6C4C00210D55 /* RoomViewController.m */, B1B5569620EE6C4C00210D55 /* RoomViewController.xib */, @@ -3461,6 +3495,7 @@ 3232AB2122564D9100AD6A5C /* README.md in Resources */, B1B5593920EF7BAC00210D55 /* TableViewCellWithCheckBoxes.xib in Resources */, B1B557C120EF5B4500210D55 /* DisabledRoomInputToolbarView.xib in Resources */, + 325380DD228C34FC00ADDEFA /* ReactionsMenuView.xib in Resources */, 32891D6C2264CBA300C82226 /* SimpleScreenTemplateViewController.storyboard in Resources */, B1664DA320F4F96200808783 /* Vector.strings in Resources */, B1B557C720EF5CD400210D55 /* DirectoryServerDetailTableViewCell.xib in Resources */, @@ -3767,6 +3802,7 @@ B16932FA20F3C51A00746532 /* RecentCellData.m in Sources */, B16932F220F3C49E00746532 /* GroupsDataSource.m in Sources */, B1B5581C20EF625800210D55 /* RoomAvatarTitleView.m in Sources */, + 325380D3228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift in Sources */, B169330820F3CA0E00746532 /* ContactsDataSource.m in Sources */, B1B5574B20EE6C4D00210D55 /* MediaAlbumContentViewController.m in Sources */, B1B5598820EFC3E000210D55 /* WidgetManager.m in Sources */, @@ -3831,6 +3867,7 @@ 32F6B9692270623100BBA352 /* DeviceVerificationDataLoadingCoordinator.swift in Sources */, B1B5594720EF7BD000210D55 /* RoomCollectionViewCell.m in Sources */, B10CFBC32268D99D00A5842E /* JitsiService.swift in Sources */, + 325380D7228C2E5800ADDEFA /* ReactionsMenuReactions.swift in Sources */, B1B558C120EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.m in Sources */, B1B5573E20EE6C4D00210D55 /* RiotNavigationController.m in Sources */, B1B5593B20EF7BAC00210D55 /* TableViewCellWithCheckBoxAndLabel.m in Sources */, @@ -3883,6 +3920,7 @@ B1B5590620EF768F00210D55 /* RoomMembershipCollapsedWithPaginationTitleBubbleCell.m in Sources */, B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */, F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */, + 325380DB228C34EF00ADDEFA /* ReactionsMenuView.swift in Sources */, 324A2056225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift in Sources */, B16932F720F3C50E00746532 /* RecentsDataSource.m in Sources */, 3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */, @@ -3893,6 +3931,7 @@ B1B558E020EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.m in Sources */, B1B5593C20EF7BAC00210D55 /* TableViewCellWithCheckBoxes.m in Sources */, 32891D6B2264CBA300C82226 /* SimpleScreenTemplateViewController.swift in Sources */, + 325380D1228C1BE500ADDEFA /* ReactionsMenuAction.swift in Sources */, B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */, F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */, B1B5596F20EFA85D00210D55 /* EncryptionInfoView.m in Sources */, @@ -3908,6 +3947,7 @@ B1B5579A20EF575B00210D55 /* ForgotPasswordInputsView.m in Sources */, B1B558CC20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B1B5571D20EE6C4D00210D55 /* HomeViewController.m in Sources */, + 325380D5228C245D00ADDEFA /* ReactionsMenuViewModel.swift in Sources */, 3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */, B16932EA20F3C39000746532 /* UnifiedSearchRecentsDataSource.m in Sources */, B1B557DE20EF5FBB00210D55 /* FilesSearchTableViewCell.m in Sources */, diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuAction.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuAction.swift new file mode 100644 index 0000000000..39bfd21472 --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuAction.swift @@ -0,0 +1,23 @@ +/* + Copyright 2019 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 + +/// TemplateScreenViewController view actions exposed to view model +enum ReactionsMenuAction { + case react(String) + case unreact(String) +} diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuReactions.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuReactions.swift new file mode 100644 index 0000000000..12a54ae3ba --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuReactions.swift @@ -0,0 +1,24 @@ +/* + Copyright 2019 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 + +enum ReactionsMenuReactions: String { + case agree = "👍" + case disagree = "👎" + case like = "🙂" + case dislike = "😔" +} diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift new file mode 100644 index 0000000000..391756a72a --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift @@ -0,0 +1,64 @@ +/* + Copyright 2019 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 +import Reusable + +final class ReactionsMenuView: UIView, NibOwnerLoadable { + + // MARK: - Properties + + // MARK: Outlets + + // MARK: Private + + //private var strengthViews: [UIView] = [] + + // MARK: Public + + var viewModel: ReactionsMenuViewModelType? { + didSet { + self.updateView() + self.viewModel?.viewDelegate = self + } + } + + // MARK: - Setup + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.loadNibContent() + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.loadNibContent() + } + + // MARK: - Private + + private func updateView() { + guard let viewModel = self.viewModel else { + return + } + } +} + +extension ReactionsMenuView: ReactionsMenuViewModelDelegate { + func reactionsMenuViewModelDidUpdate(_ viewModel: ReactionsMenuViewModelType) { + self.updateView() + } +} diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib new file mode 100644 index 0000000000..a35cf55e32 --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift new file mode 100644 index 0000000000..819ce6ec0a --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -0,0 +1,99 @@ +/* + Copyright 2019 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 + +final class ReactionsMenuViewModel: ReactionsMenuViewModelType { + + // MARK: - Properties + + // MARK: Private + private let aggregations: MXAggregations + private let roomId: String + private let eventId: String + + // MARK: Public + + var isAgreeButtonSelected: Bool = false + var isDisagreeButtonSelected: Bool = false + var isLikeButtonSelected: Bool = false + var isDislikeButtonSelected: Bool = false + + weak var viewDelegate: ReactionsMenuViewModelDelegate? + weak var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? + + // MARK: - Setup + + init(aggregations: MXAggregations, roomId: String, eventId: String) { + + self.aggregations = aggregations + self.roomId = roomId + self.eventId = eventId + + self.loadData() + self.listenToDataUpdate() + } + + // MARK: - Private + + + private func resetData() { + self.isAgreeButtonSelected = false + self.isDisagreeButtonSelected = false + self.isLikeButtonSelected = false + self.isDislikeButtonSelected = false + } + + private func loadData() { + guard let reactionCounts = self.aggregations.reactions(onEvent: self.eventId, inRoom: self.roomId) else { + return + } + + self.resetData() + reactionCounts.forEach { (reaction) in + if let reaction = ReactionsMenuReactions(rawValue: reaction.reaction) { + switch reaction { + case .agree: + self.isAgreeButtonSelected = true + case .disagree: + self.isDisagreeButtonSelected = true + case .like: + self.isLikeButtonSelected = true + case .dislike: + self.isDislikeButtonSelected = true + } + } + } + + if let viewDelegate = self.viewDelegate { + viewDelegate.reactionsMenuViewModelDidUpdate(self) + } + } + + private func listenToDataUpdate() { + self.aggregations.listenToReactionCountUpdate(inRoom: self.roomId) { [weak self] (changes) in + + guard let sself = self else { + return + } + + if changes[sself.eventId] != nil { + sself.loadData() + } + } + } + +} diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift new file mode 100644 index 0000000000..bbdfbf5bed --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift @@ -0,0 +1,38 @@ +/* + Copyright 2019 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 ReactionsMenuViewModelDelegate: class { + func reactionsMenuViewModelDidUpdate(_ viewModel: ReactionsMenuViewModelType) +} + +protocol ReactionsMenuViewModelCoordinatorDelegate: class { + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, doAction action: ReactionsMenuAction) +} + + +protocol ReactionsMenuViewModelType { + + var isAgreeButtonSelected: Bool { get } + var isDisagreeButtonSelected: Bool { get } + var isLikeButtonSelected: Bool { get } + var isDislikeButtonSelected: Bool { get } + + + var viewDelegate: ReactionsMenuViewModelDelegate? { get set } + var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? { get set } +} From 52a480db927e64bc8c45439a206cec104c6ee367 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 15 May 2019 16:38:28 +0200 Subject: [PATCH 019/266] Reactions: Translate primary reactions --- Riot/Assets/en.lproj/Vector.strings | 6 ++++ Riot/Generated/Strings.swift | 16 ++++++++++ .../ReactionsMenu/ReactionsMenuView.swift | 29 +++++++++++++++++++ .../ReactionsMenu/ReactionsMenuView.xib | 9 +++++- 4 files changed, 59 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 9e580c24f6..369c820aec 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -278,6 +278,10 @@ "room_event_action_cancel_send" = "Cancel Send"; "room_event_action_cancel_download" = "Cancel Download"; "room_event_action_view_encryption" = "Encryption Information"; +"room_event_action_reaction_agree" = "Agree %@"; +"room_event_action_reaction_disagree" = "Disagree %@"; +"room_event_action_reaction_like" = "Like %@"; +"room_event_action_reaction_dislike" = "Dislike %@"; "room_warning_about_encryption" = "End-to-end encryption is in beta and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nEncrypted messages will not be visible on clients that do not yet implement encryption."; "room_event_failed_to_send" = "Failed to send"; "room_action_send_photo_or_video" = "Send photo or video"; @@ -889,3 +893,5 @@ "device_verification_emoji_headphones" = "Headphones"; "device_verification_emoji_folder" = "Folder"; "device_verification_emoji_pin" = "Pin"; + + diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 5932c9364a..de76c9b76f 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1714,6 +1714,22 @@ internal enum VectorL10n { internal static var roomEventActionQuote: String { return VectorL10n.tr("Vector", "room_event_action_quote") } + /// Agree %@ + internal static func roomEventActionReactionAgree(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_event_action_reaction_agree", p1) + } + /// Disagree %@ + internal static func roomEventActionReactionDisagree(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_event_action_reaction_disagree", p1) + } + /// Dislike %@ + internal static func roomEventActionReactionDislike(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_event_action_reaction_dislike", p1) + } + /// Like %@ + internal static func roomEventActionReactionLike(_ p1: String) -> String { + return VectorL10n.tr("Vector", "room_event_action_reaction_like", p1) + } /// Remove internal static var roomEventActionRedact: String { return VectorL10n.tr("Vector", "room_event_action_redact") diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift index 391756a72a..879847c82e 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift @@ -22,6 +22,10 @@ final class ReactionsMenuView: UIView, NibOwnerLoadable { // MARK: - Properties // MARK: Outlets + @IBOutlet weak var agreeButton: UIButton! + @IBOutlet weak var disagreeButton: UIButton! + @IBOutlet weak var likeButton: UIButton! + @IBOutlet weak var dislikeButton: UIButton! // MARK: Private @@ -41,19 +45,44 @@ final class ReactionsMenuView: UIView, NibOwnerLoadable { required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.loadNibContent() + self.commonInit() } override init(frame: CGRect) { super.init(frame: frame) self.loadNibContent() + self.commonInit() } // MARK: - Private + private func commonInit() { + + agreeButton.setTitle(VectorL10n.roomEventActionReactionAgree(ReactionsMenuReactions.agree.rawValue), for: .normal) + agreeButton.setTitle(VectorL10n.roomEventActionReactionAgree(ReactionsMenuReactions.agree.rawValue), for: .highlighted) + disagreeButton.setTitle(VectorL10n.roomEventActionReactionDisagree(ReactionsMenuReactions.disagree.rawValue), for: .normal) + disagreeButton.setTitle(VectorL10n.roomEventActionReactionDisagree(ReactionsMenuReactions.disagree.rawValue), for: .highlighted) + likeButton.setTitle(VectorL10n.roomEventActionReactionLike(ReactionsMenuReactions.like.rawValue), for: .normal) + likeButton.setTitle(VectorL10n.roomEventActionReactionLike(ReactionsMenuReactions.like.rawValue), for: .highlighted) + dislikeButton.setTitle(VectorL10n.roomEventActionReactionDislike(ReactionsMenuReactions.dislike.rawValue), for: .normal) + dislikeButton.setTitle(VectorL10n.roomEventActionReactionDislike(ReactionsMenuReactions.dislike.rawValue), for: .highlighted) + + customizeViewRendering() + } + + func customizeViewRendering() { + self.backgroundColor = UIColor.clear + } + private func updateView() { guard let viewModel = self.viewModel else { return } + + agreeButton.isSelected = viewModel.isAgreeButtonSelected + disagreeButton.isSelected = viewModel.isDisagreeButtonSelected + likeButton.isSelected = viewModel.isLikeButtonSelected + dislikeButton.isSelected = viewModel.isDislikeButtonSelected } } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib index a35cf55e32..08c0301592 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib @@ -10,7 +10,14 @@ - + + + + + + + + From dc3b10e9f1c345348f6c0e451e28aa5f409d5991 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 21:11:36 +0200 Subject: [PATCH 020/266] Add room contextual menu images --- .../Room/ContextMenu/Contents.json | 6 +++++ .../Contents.json | 23 ++++++++++++++++++ .../room_context_menu_copy.png | Bin 0 -> 506 bytes .../room_context_menu_copy@2x.png | Bin 0 -> 968 bytes .../room_context_menu_copy@3x.png | Bin 0 -> 1434 bytes .../Contents.json | 23 ++++++++++++++++++ .../room_context_menu_edit.png | Bin 0 -> 871 bytes .../room_context_menu_edit@2x.png | Bin 0 -> 1852 bytes .../room_context_menu_edit@3x.png | Bin 0 -> 2704 bytes .../Contents.json | 23 ++++++++++++++++++ .../room_context_menu_more.png | Bin 0 -> 366 bytes .../room_context_menu_more@2x.png | Bin 0 -> 658 bytes .../room_context_menu_more@3x.png | Bin 0 -> 567 bytes .../Contents.json | 23 ++++++++++++++++++ .../room_context_menu_reply.png | Bin 0 -> 476 bytes .../room_context_menu_reply@2x.png | Bin 0 -> 920 bytes .../room_context_menu_reply@3x.png | Bin 0 -> 1389 bytes Riot/Generated/Images.swift | 4 +++ 18 files changed, 102 insertions(+) create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/room_context_menu_copy.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/room_context_menu_copy@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/room_context_menu_copy@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/room_context_menu_edit.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/room_context_menu_edit@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/room_context_menu_edit@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/room_context_menu_more.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/room_context_menu_more@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/room_context_menu_more@3x.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/room_context_menu_reply.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/room_context_menu_reply@2x.png create mode 100644 Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/room_context_menu_reply@3x.png diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/Contents.json b/Riot/Assets/Images.xcassets/Room/ContextMenu/Contents.json new file mode 100644 index 0000000000..da4a164c91 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/ContextMenu/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/Contents.json new file mode 100644 index 0000000000..a5230d6fe0 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "room_context_menu_copy.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "room_context_menu_copy@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "room_context_menu_copy@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/room_context_menu_copy.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/room_context_menu_copy.png new file mode 100644 index 0000000000000000000000000000000000000000..7bf0c6e82c0ba0f80d1f7cee62128556de72b200 GIT binary patch literal 506 zcmVOEYA-{iGT9i-O zVBy)&X4{43j#GRT8(!-;f z`<1O18*yl#IX@h0zP~n%A5ml$DLA1lxVZ~C@W27@QcjZroPN8Q_{7C0ae$9P@z^DR zDUk~>DaI)S|7<@e?e?aYWllRy%WqjsA|$u$dF5brrv_AP28KLnYo~BNk{oO%IOj$2 z@PjEf1KgaLpQa{9GR@rfGn}GUx^^)B@c7m+enhp{473&JS*Y$pj=;bHZ_26vV*^Cp zHu9|VRwe+n1>=tijdV(IXaYdEpe-1Gzf!6WRrb~saP@RC0U%gf>t)r9i=L=HjNL8@ z3dO4O;P){cDl>h($07*qoM6N<$f@rDWsQ>@~ literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/room_context_menu_copy@2x.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/room_context_menu_copy@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..853e0332667097a68bba1673d11dfd881492fe9c GIT binary patch literal 968 zcmV;(12_DMP)g7a=$Q2TdcT zF?#T?=s}_Npof&&e?Ta)DW#W6!Hb}0p@_+5cYJTMGns4?&1PoPR1!#bb~5w5?|bw1 z$9w>j&-rG6^$P!pZ@C|KPi{lYpXZ!k?KLKDA7ku0q}ebwLNW*(Pj?L9ujP;}xK?q0A%bbO?M3jBSaf*CP$t7NyAfGESet@3imp4UBnL zmXomns(Dd1ZGxw%lrtY)<{`POX;Zr|U;9n3h8;7s65=`oIHUpQC4?8OiiDZBO`fou zi_eZ}RXcGyF^vU?B@U5om5m~*HFRt}rm+CyTct*F;HGwaHWx5()MN(!KLUhr6DA33 zj5gpG;?r3HG@UfYE3^UE&d0W?o&)5Vp{;TtY!$RVJm%Hi>bkO3X!k@LknnweYy>F* z*rkyA#F8JSMwLhxTa)C$OfBm!N! zT-4lEC1hb6C}Y_;o)JKxq1{K0S(j^OV-IVCw2skLLi!&Ur9BaF-YYd(fyoLOX286J z(E2ds5P!NB(^x>iol<;Of1jc6!t|KN0?2eyq0v2v+8*jZgNDFtI}>dbEvA`shH#;+ z@=5cAlC)KDZ_oBtmrBZ3F&026k;WD_r8h*~Nql~xZIjiV5Aza2ngmF|l$|746DDCt q$%}rQ0jZouZSLCSKfxL%1AhP^J!6bWAFxOO0000P!&-RLcP?3*itNrLYh)N6?>_u zJ*Xgf@Ln>e+@VUP_ovYpP4FTOY{o2TH`M*ypAs$moZEM#_r{koQKa$W>Hz%9k{AKi+ z#ZKpIWqe&g!_z13%=6O61m^ZuCiK~tKwV6d!b3}=hkx?vN1xjY2yn8z^sP{8qu#kM z%2LjyN#Wj5kP;p=wiQ-RU&4V{defi}I=OZq^#Y%?eCF_NMW-e8n@DByd7i63`50gI zB#}2}Du00HKS*Gzs~y`OI>~<0m+$3yYXIRuUT!S^3pk0|myNzOnHdeiDreH^@ef5V zKQSO()9D?MvBmIFZuzvD0GwbrERHJvC-f%hgkIg|qig`ebq;reZN+W9sM~bd^mbdD z0mya{81l?%6r4?mO>d()+G#TYwKKtL9}@vN-r<1(lnU_&kHu!=eLAfV44}omFJ2T< zzEKUB%4J*C;HNM!fDnE*cI4@TNbbNN%6^cTv({WFn$7)J3$beSs0V5V>B2*mBOnLq z2q*$_5O6fd732Wa(@(JtU!1t%>Z01JWVTUcvUH{|MR3u32r zdlz)ogA@c1wpB?K@gQJxkf>XALb^(7=_BamLu(AYLpSQz&K&E5$Fqr$(@MzSyWyvq zIiyfGyaU2@j_wd;Y%6Z-Mct-@>-7$( zd=bc^4|(P=*GAQ*gX{GUD99DE5q5_ac17kTA)EujJ=&^O~s}fRIg!gh>|tLCVkB%WP|=TJv3uZtI<*FK(|^2_UnJ zl9$q{qcs_3c=Q|kIaO6xpn|c2;1j8~$R;)X1CmmYW_fm6iF{`HuO#xH3Eb;O-&Izr z0fancq}tMR@VTa9RqCmc$zy$!D%_Oj7CO}c!dRr(BGtBLCABc|YUyePv<&H%WYe}p z%4aPH_e%VMvMg2{hAq_`(z^Rf+CMT$PuhN~Q9h){C@p0bzZwG4~&?@Bz9UzBB5*6jyp6A)GP2cY5L_Gm|hvx|4{&qS!zOPpqW|TZ-z(IV-dgXEY z&rK$e$o)fhD?#en|Np@Q=XC<{!_M`)w)2!=?^_H);Itry@F3uCF03!=q^lr@ALekN zEeIeux;pm!uR~(!o`(0g(J}A~D+juu&*nPF7SkQCWZJ7k{9Mxe`7Yyw181Wv(>^#L oJ@7&Jt%j~QmLq{kpz8$w2aeeCq18D(+5i9m07*qoM6N<$f-n%q;{X5v literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/Contents.json new file mode 100644 index 0000000000..1290bc2c47 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "room_context_menu_edit.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "room_context_menu_edit@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "room_context_menu_edit@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/room_context_menu_edit.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/room_context_menu_edit.png new file mode 100644 index 0000000000000000000000000000000000000000..208378c76e31f3af85746a6d6336306f566718e8 GIT binary patch literal 871 zcmV-t1DO1YP)keINM zCa>WSL=}4m>$Fe}M|O&ITkLw9JnfW~nt1u`v5ZdX4qfFOTYN%a%&Gj-pJIKZm zc2)+IQY`_#27G~{djLEGSv*h9D-_5vKgQWUTQ^jDz4Tzc{=+V_oP#)_w>~kNK7u% z{SCtlKpv(tita4S=$6fZTFwiIUs61=n@V*}*LjQ~wjhq3huv4b) zD4{y<;|2~R@&|O#Ov;Mv)yvfpi;_dgS)wkeHswgDgZxvn1ThD5#%6pszUAQkwv;n~ zur~ebnc10cX(6m~tY7A_fdOSGK6HRH x{L>%J=9(=^3XYXHOGly}&+LyTd-Wea{Q|RGGF=4&-oCg zuit39Y_=Oj_a)6a_uO;u`JHoL=iU-B_Kij0{~ZCi6W!0Ks}mY~8d8WvQV0b~IB0qz z@7|_8P$b=o_l|Qwtg)t0iEN{Urf9(ekmDRLJl|(7Jv~6KE!=KiS5rAplBLfHB|=av z0KGhr8GV-WGpwFt%Lg5Qkw-e3a2sc1vY><-h47S^W6Tyl}OTX!Rdm%TC!DALdlc;})d?fe&*nN5|- zKyIjoTNu${dtH|0O-zj7Ps9kZ+3luf*O|;uslm^a9>xcZfbqb)3GKbTA2S8)^bBrg zUjk4ilEeCN2R!G=D?1RZk>Ic`m#Msg)uElAV-0b+ZKkDdOvb2KYn>r`=sZ@&p*q3% z00%nU?wJ_*af$+V`rBkKamM+)CB3XcB&cAwl~5{IvA)nwV`8G-+>*AmgUJ~6f&T%^ zIz@)M`v>2JBziwTje9b2oCEzRF+q4=`h2pVwz-i&#TA3VUm>31bP84Nt{wB=I*-{B_W0}q!3QTIr%IXHO5kr5k# zwUly#(}t%jU8qJ3V0W5fR&jl5;K|I}E9MYNzrvsB)dzA>6d|)Gr{sd#J_s`+4m?Gx zd7HzkoW8{xo4e*(rY_h6rht@K=d>0{lwux4`|$w$p%Xb%ch2fdn)X52h&!MpTKk+0 zyU^Oxszre9WtOrR*8+Hx5><|j&R#iRlI5Mq@`@bS$61|DpULtdMK;(MQ3oD8{)u?x zO2cl96!LU&Fby1xKns1102Sj2LI?%v*I$t2Y!rA`akG$VMkD%piiV^5mDl z$;w(PIIYWMLO)c&134BO8=uvl8!Q9WwhYckY7IQ9>3#=-FV&P)7Qw$wWx9?D@Sb}# zo->-So0haQ8KZh<)x93-uEjG!7D1QOlV9>DtJBhmi~t%j`8(yl_Va!YQ;G6hz#ldm zCgfbt3k0vUl8C1W`<;U9J?KcGzo&s**bXE()){eo>Ca$+c#K9}!$1>wS~@B!af($i zIWF!WCnhJGubE!$WU>G%6TK?q`crAOtYN~QLY=fP1N-v^0yoOz+O#<7`}Kxax2u9u zvYgq(^|2q5xH-X+IHofk8ny#?@|bX_y&nZQ$vY}62r`|>G`+>`O2sNG>zd%=>pkZp%p5taj-9&;NvQEpT0;vpgCCVT`qjMM2*ED;)R z7L<5jCF+L=Xt-)&IiS2Ch&7d2RFc#20rqrag04}!h==`*FHMkI078*gq`p$Mq|_z?fIg`8{mLa>3!yq@r~wA_&8E1S=Q_g1to9_p(Z|G zSMvbHm}v(2qajpRk(NU%&%yk`z$+GmjsSxacd%-_tJj@@d47s_Xj#PwQoLri;UB{% qW1{#&9kW-pj-aWruE!#vMBpFIw`?zltGX=!0000~5y~~9=FpxrGr5ZZgg3PpnXj_WVgkWMW!P1s6 zQp-r4k}0Sy3<#9`kUI!aMznM&1!byq#1XKiE*IwRpMBpS@4MgcXW#ex_Pu>aNb;9NAc;T{fg}P+1O_SsFi;)u zt>3Qd%HdrkG?Gw}ItHcGU&~&6DH0C_Bj+as=ngE%HcfwkW)Owxp@&NA)Os%eKAO|9-Rsmmfx+` zi@FBcsy*YVOgt25K0YByZRE>qLkCF0gM2Vn_U-;vk6?;y*ZT#R1wqUWx>tN?WJC0= z+=_jEp_Vy>)LYx#PdA2nU!WIe7+od5XOSY4qc~L->YXs^(N`W#rLyv5Z+hFRJac44 z`Llsv7es+%n}NgbAp-2@?-ih(x2buSBf(bX{gt99&#F7cxZM)1eVfnwxI&e!sIPs{ z01YC6sR4J{sYtf4n7ep6012phTEl7yw(UOe7=_TdfrB;RW4n;z<=ikN5u9J5Z!e%w z8v~GfiNcgXVPtTaQLLelXpMvHwA(*Z^CFHQnb2}HBdR-K_5V~LLInuIQGqJ$8rcmH zBC&U|+z&-zqkB|S<$0Yd`eq}xoX(=Ws^ixB8={HLp%&@4UU{;yb+vnPB?6-Q`_>ET;OFqE%{ci zMH@?bKARZOMs7>?Lc`h993b9OKhX%zvzQW#_}e2mAEr8;)1ukeL2x$U=wGO_LEwW_ zhcxY4?vl&AEKDaXK=`yl@_RSpRlhz0a?DBW4_?h)+#C+X=-O?==lMN?vw)XlS{-$Q zE$?dX@`yGP5}+P%Jex7YOZ0(}cL%4WA=D1YS$#!1C}IHd zZ3KPw*p@-{4d?eFI8$|w0eN+Xd-TtCL9wipsTj;gV?$FF+Q?C{KX1a0AGt@**#+4V zZRq=HIsF3A@Y+qT3yqC_T*yq;x`UJe+sg{d<~{?U=wvEJkBz2k1m^)2yQc~8QklE# zaVs0Teg=%V12n99NKNb#1$a+MWnU8xdy?xU*{c=%^27MtH5sYtr^-Xx6~jw_vvg9vMJ;)f6~f^K|fPg z+=?`NyKQT0Wg|xi4hZCTa$&`MtH1{DuS{>J!MBXS!Nu!ya#6(-RyItLFcHKJph~~D z7{~l~c|UOZus-Cj_%+YxX;;nq5gnbOS_EetFGFzFg$gTlD}G+DFLc~eOa}50zrBQk zt;WDgtsHmyf1BAE?06fe@-e(F+VG+c+&ho_V@18?`jwv&g($;s9q~%EZ`5%cLFibv zIs{CG!_k0~TD9u7F4}Rx5F7!@q5+4!spG_O2tQx_5%Jl2p`r0Dmhx(U%-T^YnRSkq zpR#Ywowz>wnns_}{TkYUNPTnd?Ez2_9P$lbjBm(Gu^_MF`9}0d4Hw6ujeEi}mg<%M zO3f$cu#CUPCB>9bFo(Y}e%7qj@l*^)jdxU_D^QE;vpZDm?xp~+Fj!cw`vSr1g=zd; zK7>ziMUZa^%OhRmGJ^1pPm^A_eC)^~Y44gjmQv-tD;QJ`>cGKw8w6)TZ@@9TN3{I7 z0EACZ0}vM0!)C|c>@}66LqHE;pOKm%y1^my5S%|~_%SA8o?`au%DZJbn8C%NNw3uE zbt7fkHCd))iW~_280%F!r?24G+@A;Cr7v^*n3FLFh{_>d{Skniv-v%bDF_IHF*zW2 ze%(A$knxnpoPfJ@hq+GdB(VnwNDf~hWre;HRY(9q{604uPigFqN{em3g9F;a{v zKm;V*tcti>8URAx$4%pd2+F~s&aWH}e~t;oDB#|jjVVA~`O8m>J`bJ)r1N?l!!wA4 z9KlVogShlKkeTj$PrZ?TvA5pGK|iJdG5_Vl@)A6$_B>W2$G1jgoH#zABp8mXKHNy) zYs>*+v4@Yt9Zz5&%v_7<{=R3#>d6?%71X>1J=eHvTbnzL*VhRxZ z-voY|;SP?|+3c?XY#YPKSfcTmpZHg;?UMAvXu)BzMg)i{ zbQG2T*C`FY59?9C?F%zMZs^x@R_HYs*zdodzoMZxwj!n&Z^*W9^>`}Fvv9NPJ1EP< z)U)$Ci9%ywY%7!fIrj(%;%kxqyP7CIC;dnwkVGJfKoWsoBJh7(Sf4YKN>1$n0000< KMNUMnLSTZ6$0JPu literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/Contents.json new file mode 100644 index 0000000000..aba738f89d --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "room_context_menu_more.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "room_context_menu_more@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "room_context_menu_more@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/room_context_menu_more.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/room_context_menu_more.png new file mode 100644 index 0000000000000000000000000000000000000000..bb4a70d8a3fdd5143d949806d5483ed57041bddf GIT binary patch literal 366 zcmV-!0g?WRP)Jmc2ysKm0|e1t zQScjN3n6}hmiF4%*li^)$+~yEXP3c6tOeoTX6BqTk2{CRKktBd;Qu?oIH5H?SPx# literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/room_context_menu_more@2x.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/room_context_menu_more@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..bedd0c99c582b968d4354cb1f33a205721a9289a GIT binary patch literal 658 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTCmUKs7M+SzC{oH>NSs56ZJUv|; zLo!(3h8p@@O%T|Zo!GGBXU4?`$8ww!31aKs}(ofEVjfpOOp$1Exq&2qpM5TYR4HKtKPG>?7qZihx8b?=61HTwG3w~tk{!!=SjMMY{HXbbO-|y!CTjcyJHqmdBW|!ogRd^P0IBI&^ z`DZer=gbbQ=M_wi2`#-Ix9(*s$HOOIzPz(MDKzJejO+H(0qgn}KW24OQ`J&F7ifA= zEN0K=Aic$ovn=|SG4$w{e{s{C_2%}g)C_Z#%X%?M$KF4CwP+io=C5+4+YV;A=T=lt zc(%uWlfSXH@M- zS+RW99iyL3mQq{GHQ!vZ?AvVUWw|P*_TFLn{}UN&mYv-AJ=)%=A@Y{B;S2Y$T(|45 k8mt!Ztzbr_>Mu9OH@RKqy)WG?4@_hXp00i_>zopr00ZkQ@c;k- literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/room_context_menu_more@3x.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/room_context_menu_more@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..b7eb52e563d48c57d9c976e71fab84fd22f553d6 GIT binary patch literal 567 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY1|&n@ZgvM!Ea{HEjtmSN`?>!lvNA9*zVmc( z45?szd)?5B*-_-!$CDLUCraPaCo{K#6NOYO9 z=gQ0dn{#5L`}^*lx$E~_!!E?{%f_a!$KIYk`}z8dUn><}XT6^I=hG(#y_-+Y_bfd( z>1wS`>9m}6?o1}X^OtIKU7u#<9kzY@mB>vyv2*Ap2|{!jo4 zYHXjj=gQNf&!4w`^=L@hHtD0{xtPa4+oshWpYuf5I5HP#+QA7M9gnr&nXB^rzfszi uYs&A6Kf1dyE&F-YOvaUnP(PsXf!8)WzTnnH1~FhtVDNPHb6Mw<&;$V78TvW^ literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/Contents.json b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/Contents.json new file mode 100644 index 0000000000..caedf0179f --- /dev/null +++ b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "room_context_menu_reply.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "room_context_menu_reply@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "room_context_menu_reply@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/room_context_menu_reply.png b/Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/room_context_menu_reply.png new file mode 100644 index 0000000000000000000000000000000000000000..da8c8d608972b0d19a60395d9ca367656d5ecfa5 GIT binary patch literal 476 zcmV<20VDp2P)uNot-zmD zz(~gDl&j|0Z*)jNGs?du=t^&S^i+J=pUvt&ZQ#oUoyo*NkqUVtTD>e@^|VGnEBNv}io6WE8EF%CFm~QdVTDza z7GB?WfzZ+j4^#}*!;x9{af_eGPp9-nhU`wo8#Q9WKmo%fU|ueFgdr|iq1cYfFD=n7 z+DSy{uN2%D#2c+$#t8r^t2BH-#)8&#^tgOcAz%z<3GfhzY|z*W2=Vj@Xyh%6S_a4K zLU`@59$%731sz8lUe4_^Kq3v;xEYGx)`MF)SASD;DsrPabiW*vIEkDiweD-BV0})N zKwX%D$em6&28b3XJL@{#?5$5|_FO}tl8fQ!y#dCkT>4(0<$Q0pU>pAr75D&-x`o~C S(yYAz0000mk}`#Rz*$Na*Wr)j%nBEo*mf=PsRbv~|@7cU+na zJLk-qJ9B>LfA5_;%b3|r1xy7@1xy7@1xy7@1^!6qZ1bzSTO04{u4 z95SH^_&AF{5Ul76(<=^=IAlUoS-hAy^?t?_K=2FgiVN62++{z)2eKAE$f)xb6=3lz z4dJOs_(|aX$NXnq1x7*Z^DespOF(H-8S7!3ugBlc88y03|DaX(0W4Y$fM4VCAEvyE z79jh)7r3fkrjDq5Xk+f?s;TNT-zk<3yY20*92RHYx?KJG(N9`{>{r@2Q#GZPyi+7YHQcW5^)Mhevcc!?-V*;I~IOOAf5Q_YQ2|?Nt;5bc44v(hQ$D)4B)oa zYTdmUykOI&Q0eGE_SmPf5nQ}`2^xp=9d24fbiUdaVP=ph%q2crWZx0|;;8?*R8Fk` z(eXeqD2JkaBZT#=y{1hefhOLsHO>|s)#vTU(vZyHsuKDp<>SVW~}x~A_g#Pk=bYc^^EbYK5uc} uVCR;M0X9KR1xy7@1xy7@1xy82QGq|)VEyBL_@5X60000@I2oipP!y@swd7Fx?62G004lxx;T36(B=Q^lHO^e zu+Di0kU+Et8UXk^Ymel2DF8r9*VPg29S0OXe0NpdN#%{8&?m>DKZ|~!?xjG(e9%NO z%RqM82v{jY0|M{0_mpMnps1X*_l@_gWfd_lF!)sWEkZUa<)dySW~8FymZpM|>Xe&W z!LYaM_?S2(E{7W(I5WnG@)Kt0h0G@e2FCK=T;=yMfE&)rgAfa#2kIB_(|{~te`u-- zFdk8J4;x4h4-DE~jnZ9kq;sJOi|@YkxgM9Dh&! z>Y3()VERW_;J`+}9uxe9)0^N2?Lp0v@t<{_C-%aVC#%P-0uWWSLQ6P$>+w5_3l9t5 zMo=LU6uAy{%aH^P;?#|E4igGYZlAMhIv+!ivevchU+3u|E9iLbE^T(w%4fDe%Czz& zx4a196d}(SevVBw7ok7Ug3K9VOU-f?Kfi^*fL99OW0ah6NV3+KYx=z@M`mO7vEFtm zjWQB+%=vz(X&ohOlBzZW-ZDKrE{K{sB}OU*IKm^!QVAT*vxVyihsP9DKkY4Po*v~& z1ub7nBy(d12pGz+Ak|R0$k#w^_4Fo_r{k2ZCF~U)OM?a+UWb0%ZjF2^?`11W!3!O&=8r_--aIf|Mt>G|=gC(#0+_uN#(<%z(T#q2~zb?Y|B!^%B@J z(`Z?tR@#a|_F=y;Lr@OMnC3sLCM7!zAp(B;R5#b5_=N>m@h{a13m+oifuAcklzHK+ z>z`j5>`X9fztVI*R5GlhY*p)hwW!vEcKYz4mLSWP4QjltLS1@{=;tV23DoSidg8(2 zgGUHJ;8K70#>~)GHSt&@k?LrG<*Wuw@sGe(@wU$g+*kg3y;cHye%V9MEnK{ay(qw& zJa>D6!L8jaoX6{Zb z2w%2&rNK<0NzVbTviB<*L|(@Zh4h+OcS$sDr#n?Z>4VX&i=&kjJ$BbJ8ovnBdMU`hATb}+|Mq0zcLy;np+-qYjnZn zy8CcQhoLd;E5&wNbSgt#hg3b^koUxzy!NH)C{HzSL+uC`hK^5ksN{n+QFLDjd7W4M zL`wR`q%fnHu)#qXgLK=VncwW^cK#}CZaT?7$dk*w9m^7{y4HAI^x4_{6X2JRMLrE* zrxceNb-uLG4;T)?mezgAp#tVv- literal 0 HcmV?d00001 diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index 486035dc0c..5007a2609f 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -75,6 +75,10 @@ internal enum Asset { internal static let scrolldown = ImageAsset(name: "scrolldown") internal static let scrollup = ImageAsset(name: "scrollup") internal static let typing = ImageAsset(name: "typing") + internal static let roomContextMenuCopy = ImageAsset(name: "room_context_menu_copy") + internal static let roomContextMenuEdit = ImageAsset(name: "room_context_menu_edit") + internal static let roomContextMenuMore = ImageAsset(name: "room_context_menu_more") + internal static let roomContextMenuReply = ImageAsset(name: "room_context_menu_reply") internal static let uploadIcon = ImageAsset(name: "upload_icon") internal static let voiceCallIcon = ImageAsset(name: "voice_call_icon") internal static let addParticipant = ImageAsset(name: "add_participant") From acc13b089a4cf4f1144ecfa1b523fb5e88e81336 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 21:11:55 +0200 Subject: [PATCH 021/266] Add contextual menu strings --- Riot/Assets/en.lproj/Vector.strings | 2 ++ Riot/Generated/Strings.swift | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 9e580c24f6..75c74b9df1 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -278,6 +278,8 @@ "room_event_action_cancel_send" = "Cancel Send"; "room_event_action_cancel_download" = "Cancel Download"; "room_event_action_view_encryption" = "Encryption Information"; +"room_event_action_reply" = "Reply"; +"room_event_action_edit" = "Edit"; "room_warning_about_encryption" = "End-to-end encryption is in beta and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nEncrypted messages will not be visible on clients that do not yet implement encryption."; "room_event_failed_to_send" = "Failed to send"; "room_action_send_photo_or_video" = "Send photo or video"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 5932c9364a..59c64bda3d 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1698,6 +1698,10 @@ internal enum VectorL10n { internal static var roomEventActionDelete: String { return VectorL10n.tr("Vector", "room_event_action_delete") } + /// Edit + internal static var roomEventActionEdit: String { + return VectorL10n.tr("Vector", "room_event_action_edit") + } /// Reason for kicking this user internal static var roomEventActionKickPromptReason: String { return VectorL10n.tr("Vector", "room_event_action_kick_prompt_reason") @@ -1718,6 +1722,10 @@ internal enum VectorL10n { internal static var roomEventActionRedact: String { return VectorL10n.tr("Vector", "room_event_action_redact") } + /// Reply + internal static var roomEventActionReply: String { + return VectorL10n.tr("Vector", "room_event_action_reply") + } /// Report content internal static var roomEventActionReport: String { return VectorL10n.tr("Vector", "room_event_action_report") From d6509d01f8131b638400597913792fd3e11e2f98 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 21:15:50 +0200 Subject: [PATCH 022/266] MXKRoomBubbleTableViewCell+Riot: Add possibility to hide timestamp when selecting a component. --- Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h | 3 ++- Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h index 9063887dce..dade6d2ee9 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h @@ -56,8 +56,9 @@ extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer; @param componentIndex index of the component in bubble message data @param showEditButton true to show edit button + @param showTimestamp true to show timestamp label */ -- (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton; +- (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton showTimestamp:(BOOL)showTimestamp; /** Mark a component in receiver. diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 2560f91e89..019756683c 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -138,15 +138,18 @@ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex - (void)selectComponent:(NSUInteger)componentIndex { - [self selectComponent:componentIndex showEditButton:YES]; + [self selectComponent:componentIndex showEditButton:YES showTimestamp:YES]; } -- (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton +- (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton showTimestamp:(BOOL)showTimestamp { if (componentIndex < bubbleData.bubbleComponents.count) { - // Add time label - [self addTimestampLabelForComponent:componentIndex]; + if (showTimestamp) + { + // Add time label + [self addTimestampLabelForComponent:componentIndex]; + } // Blur timestamp labels which are not related to the selected component (if any) for (UIView* view in self.bubbleInfoContainer.subviews) From 7de616ffc0a11984bd89f1cafd74f2ea7bcffda0 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 21:18:02 +0200 Subject: [PATCH 023/266] Make Themable protocol available for Obj-C --- Riot/Managers/Theme/Themable.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Managers/Theme/Themable.swift b/Riot/Managers/Theme/Themable.swift index 3764b24b7f..f4f59f1e9c 100644 --- a/Riot/Managers/Theme/Themable.swift +++ b/Riot/Managers/Theme/Themable.swift @@ -16,6 +16,6 @@ import Foundation -protocol Themable: class { +@objc protocol Themable: class { func update(theme: Theme) } From 0c8a98327d71583252fe7ac572c616f6cd225566 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 21:29:47 +0200 Subject: [PATCH 024/266] Add convenient methods on UIGestureRecognizer, UIImage and UIStackView. --- Riot/Categories/UIGestureRecognizer.swift | 28 +++++++++++++ Riot/Categories/UIImage.swift | 51 +++++++++++++++++++++++ Riot/Categories/UIStackView.swift | 28 +++++++++++++ 3 files changed, 107 insertions(+) create mode 100644 Riot/Categories/UIGestureRecognizer.swift create mode 100644 Riot/Categories/UIImage.swift create mode 100644 Riot/Categories/UIStackView.swift diff --git a/Riot/Categories/UIGestureRecognizer.swift b/Riot/Categories/UIGestureRecognizer.swift new file mode 100644 index 0000000000..26dd05082f --- /dev/null +++ b/Riot/Categories/UIGestureRecognizer.swift @@ -0,0 +1,28 @@ +/* + Copyright 2019 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 + +extension UIGestureRecognizer { + + func vc_isTouchingInside(view: UIView? = nil) -> Bool { + guard let view = view ?? self.view else { + return false + } + let touchedLocation = self.location(in: view) + return view.bounds.contains(touchedLocation) + } +} diff --git a/Riot/Categories/UIImage.swift b/Riot/Categories/UIImage.swift new file mode 100644 index 0000000000..9c6847645d --- /dev/null +++ b/Riot/Categories/UIImage.swift @@ -0,0 +1,51 @@ +/* + Copyright 2019 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 + +extension UIImage { + + class func vc_image(from color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage? { + let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) + UIGraphicsBeginImageContext(rect.size) + let context = UIGraphicsGetCurrentContext() + + context?.setFillColor(color.cgColor) + context?.fill(rect) + + var image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + UIGraphicsBeginImageContext(size) + image?.draw(in: rect) + image = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + + return image + } + + func vc_tintedImage(usingColor tintColor: UIColor) -> UIImage? { + UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale) + let drawRect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height) + + self.draw(in: drawRect) + tintColor.set() + UIRectFillUsingBlendMode(drawRect, .sourceAtop) + let tintedImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext() + UIGraphicsEndImageContext() + return tintedImage + } +} diff --git a/Riot/Categories/UIStackView.swift b/Riot/Categories/UIStackView.swift new file mode 100644 index 0000000000..e1ebb800b0 --- /dev/null +++ b/Riot/Categories/UIStackView.swift @@ -0,0 +1,28 @@ +/* + Copyright 2019 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 + +extension UIStackView { + + func vc_removeAllSubviews() { + let subviews = self.arrangedSubviews + for subview in subviews { + self.removeArrangedSubview(subview) + subview.removeFromSuperview() + } + } +} From c808ae50577d61e88f881a46a30404d0273deb19 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 21:34:32 +0200 Subject: [PATCH 025/266] RoomBubbleCellData: Add a property to show or hide timestamp for selected component. --- Riot/Modules/Room/CellData/RoomBubbleCellData.h | 4 ++++ Riot/Modules/Room/CellData/RoomBubbleCellData.m | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.h b/Riot/Modules/Room/CellData/RoomBubbleCellData.h index dc59bf3a67..922fea0394 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.h +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.h @@ -35,6 +35,10 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag) */ @property(nonatomic) BOOL containsLastMessage; +/** + Indicate true to display the timestamp of the selected component. + */ +@property(nonatomic) BOOL showTimestampForSelectedComponent; /** The event id of the current selected event inside the bubble. Default is nil. diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 81cbef55f7..2b953e464f 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -199,7 +199,7 @@ - (NSAttributedString*)refreshAttributedTextMessage } // Check whether the timestamp is displayed for this component, and check whether a vertical whitespace is required - if ((selectedComponentIndex == index || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName)) + if (((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName)) { currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; [currentAttributedTextMsg appendAttributedString:componentString]; @@ -238,7 +238,7 @@ - (NSAttributedString*)refreshAttributedTextMessage } // Check whether the timestamp is displayed - if (selectedComponentIndex == index || lastMessageIndex == index) + if ((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) { [currentAttributedTextMsg appendAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; } @@ -294,7 +294,7 @@ - (void)refreshBubbleComponentsPosition NSInteger lastMessageIndex = self.containsLastMessage ? self.mostRecentComponentIndex : NSNotFound; // Check whether the timestamp is displayed for this first component, and check whether a vertical whitespace is required - if ((selectedComponentIndex == index || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName)) + if (((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName)) { attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; [attributedString appendAttributedString:component.attributedTextMessage]; @@ -322,7 +322,7 @@ - (void)refreshBubbleComponentsPosition { // Prepare its attributed string by considering potential vertical margin required to display timestamp. NSAttributedString *componentString; - if (selectedComponentIndex == index || lastMessageIndex == index) + if ((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) { NSMutableAttributedString *componentAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; [componentAttributedString appendAttributedString:component.attributedTextMessage]; From 3be7a5274a02bdc9e74d3cef8f786a9a0a4ab464 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 21:44:07 +0200 Subject: [PATCH 026/266] RoomDataSource: Add a property to show or hide timestamp when selecting an event. --- Riot/Modules/Room/DataSources/RoomDataSource.h | 5 +++++ Riot/Modules/Room/DataSources/RoomDataSource.m | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.h b/Riot/Modules/Room/DataSources/RoomDataSource.h index dc695ba7c8..cfc62b45d8 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.h +++ b/Riot/Modules/Room/DataSources/RoomDataSource.h @@ -34,6 +34,11 @@ */ @property(nonatomic) BOOL markTimelineInitialEvent; +/** + Tell whether timestamp should be displayed on event selection. Default is YES. + */ +@property(nonatomic) BOOL showBubbleDateTimeOnSelection; + /** Check if there is an active jitsi widget in the room and return it. diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 49d189283b..c7f2e74aae 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -58,6 +58,8 @@ - (instancetype)initWithRoomId:(NSString *)roomId andMatrixSession:(MXSession *) self.markTimelineInitialEvent = NO; + self.showBubbleDateTimeOnSelection = YES; + // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { @@ -445,7 +447,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N NSInteger selectedComponentIndex = cellData.selectedComponentIndex; if (selectedComponentIndex != NSNotFound) { - [bubbleCell selectComponent:cellData.selectedComponentIndex]; + [bubbleCell selectComponent:cellData.selectedComponentIndex showEditButton:NO showTimestamp:cellData.showTimestampForSelectedComponent]; } else { @@ -492,11 +494,14 @@ - (void)setSelectedEventId:(NSString *)selectedEventId { RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:_selectedEventId]; cellData.selectedEventId = nil; + cellData.showTimestampForSelectedComponent = NO; } if (selectedEventId.length) { RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:selectedEventId]; + + cellData.showTimestampForSelectedComponent = self.showBubbleDateTimeOnSelection; if (cellData.collapsed && cellData.nextCollapsableCellData) { From 444a2ee452f8b546ae6d854fbe4b21ffeacc389e Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 23:02:25 +0200 Subject: [PATCH 027/266] Create room contextual menu action enum --- .../RoomContextualMenuAction.swift | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 Riot/Modules/Room/ContextualMenu/RoomContextualMenuAction.swift diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuAction.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuAction.swift new file mode 100644 index 0000000000..f4898fca04 --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuAction.swift @@ -0,0 +1,62 @@ +/* + Copyright 2019 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 + +@objc enum RoomContextualMenuAction: Int { + case copy + case reply + case edit + case more + + // MARK: - Properties + + var title: String { + let title: String + + switch self { + case .copy: + title = VectorL10n.roomEventActionCopy + case .reply: + title = VectorL10n.roomEventActionReply + case .edit: + title = VectorL10n.roomEventActionEdit + case .more: + title = VectorL10n.roomEventActionMore + } + + return title + } + + var image: UIImage? { + let image: UIImage? + + switch self { + case .copy: + image = Asset.Images.roomContextMenuCopy.image + case .reply: + image = Asset.Images.roomContextMenuReply.image + case .edit: + image = Asset.Images.roomContextMenuEdit.image + case .more: + image = Asset.Images.roomContextMenuMore.image + default: + image = nil + } + + return image + } +} From cd238cc2f0b1c1a321daae9ce1e106aa44e6b28a Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 23:07:50 +0200 Subject: [PATCH 028/266] Create RoomContextualMenuToolbarView --- .../ContextualMenuItemView.swift | 172 ++++++++++++++++++ .../ContextualMenu/ContextualMenuItemView.xib | 55 ++++++ .../RoomContextualMenuItem.swift | 37 ++++ .../RoomContextualMenuToolbarView.swift | 141 ++++++++++++++ .../RoomContextualMenuToolbarView.xib | 71 ++++++++ 5 files changed, 476 insertions(+) create mode 100644 Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift create mode 100644 Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib create mode 100644 Riot/Modules/Room/ContextualMenu/RoomContextualMenuItem.swift create mode 100644 Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift create mode 100644 Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.xib diff --git a/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift new file mode 100644 index 0000000000..a76537140a --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift @@ -0,0 +1,172 @@ +/* + Copyright 2019 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 +import Reusable + +final class ContextualMenuItemView: UIView, NibOwnerLoadable { + + // MARK: - Constants + + private enum ColorAlpha { + static let normal: CGFloat = 1.0 + static let highlighted: CGFloat = 0.3 + } + + private enum ViewAlpha { + static let normal: CGFloat = 1.0 + static let disabled: CGFloat = 0.5 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var imageView: UIImageView! + @IBOutlet private weak var titleLabel: UILabel! + + // MARK: Private + + private var originalImage: UIImage? + + private var isHighlighted: Bool = false { + didSet { + self.updateView() + } + } + + // MARK: Public + + var titleColor: UIColor = .black { + didSet { + self.updateView() + } + } + + var imageColor: UIColor = .black { + didSet { + self.updateView() + } + } + + var isEnabled: Bool = true { + didSet { + self.updateView() + } + } + + var action: (() -> Void)? + + // MARK: Setup + + private func commonInit() { + self.setupGestureRecognizer() + } + + convenience init() { + self.init(frame: CGRect.zero) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.loadNibContent() + self.commonInit() + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.loadNibContent() + self.commonInit() + } + + // MARK: - Public + + func fill(title: String, image: UIImage?) { + self.originalImage = image?.withRenderingMode(.alwaysTemplate) + self.titleLabel.text = title + self.updateView() + } + + func fill(menuItem: RoomContextualMenuItem) { + self.fill(title: menuItem.title, image: menuItem.image) + self.action = menuItem.action + self.isEnabled = menuItem.isEnabled + } + + // MARK: - Private + + private func setupGestureRecognizer() { + let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(buttonAction(_:))) + gestureRecognizer.minimumPressDuration = 0 + self.addGestureRecognizer(gestureRecognizer) + } + + private func updateView() { + + let viewAlpha = self.isEnabled ? ViewAlpha.normal : ViewAlpha.disabled + let colorAlpha = self.isHighlighted ? ColorAlpha.highlighted : ColorAlpha.normal + + self.updateTitleAndImageAlpha(viewAlpha) + self.imageView.tintColor = self.imageColor + self.updateTitleAndImageColorAlpha(colorAlpha) + } + + private func updateTitleAndImageAlpha(_ alpha: CGFloat) { + self.imageView.alpha = alpha + self.titleLabel.alpha = alpha + } + + private func updateTitleAndImageColorAlpha(_ alpha: CGFloat) { + let titleColor: UIColor + let image: UIImage? + + if alpha < 1.0 { + titleColor = self.titleColor.withAlphaComponent(alpha) + image = self.originalImage?.vc_tintedImage(usingColor: self.imageColor.withAlphaComponent(alpha)) + } else { + titleColor = self.titleColor + image = self.originalImage + } + + self.titleLabel.textColor = titleColor + self.imageView.image = image + } + + // MARK: - Actions + + @objc private func buttonAction(_ sender: UILongPressGestureRecognizer) { + guard self.isEnabled else { + return + } + + let isBackgroundViewTouched = sender.vc_isTouchingInside() + + switch sender.state { + case .began, .changed: + self.isHighlighted = isBackgroundViewTouched + case .ended: + self.isHighlighted = false + + if isBackgroundViewTouched { + self.action?() + } + case .cancelled: + self.isHighlighted = false + default: + break + } + } +} diff --git a/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib new file mode 100644 index 0000000000..9c2ce9ac52 --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuItem.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuItem.swift new file mode 100644 index 0000000000..ba32691f3e --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuItem.swift @@ -0,0 +1,37 @@ +/* + Copyright 2019 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 + +@objcMembers +final class RoomContextualMenuItem: NSObject { + + // MARK: - Properties + + let title: String + let image: UIImage? + + var isEnabled: Bool = true + var action: (() -> Void)? + + // MARK: - Setup + + init(menuAction: RoomContextualMenuAction) { + self.title = menuAction.title + self.image = menuAction.image + super.init() + } +} diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift new file mode 100644 index 0000000000..c4c3893725 --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift @@ -0,0 +1,141 @@ +/* + Copyright 2019 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 +import Reusable + +final class RoomContextualMenuToolbarView: MXKRoomInputToolbarView, NibOwnerLoadable, Themable { + + // MARK: - Constants + + private enum Constants { + static let menuItemMinWidth: CGFloat = 50.0 + static let menuItemMaxWidth: CGFloat = 80.0 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var menuItemsStackView: UIStackView! + @IBOutlet private weak var separatorView: UIView! + + // MARK: Private + + private var theme: Theme? + private var menuItemViews: [ContextualMenuItemView] = [] + + // MARK: - Public + + @objc func update(theme: Theme) { + self.theme = theme + self.backgroundColor = theme.backgroundColor + self.tintColor = theme.tintColor + self.separatorView.backgroundColor = theme.lineBreakColor + + for menuItemView in self.menuItemViews { + menuItemView.titleColor = theme.textPrimaryColor + menuItemView.imageColor = theme.tintColor + } + } + + @objc func fill(contextualMenuItems: [RoomContextualMenuItem]) { + self.menuItemsStackView.vc_removeAllSubviews() + self.menuItemViews.removeAll() + + for menuItem in contextualMenuItems { + let menuItemView = ContextualMenuItemView() + menuItemView.fill(menuItem: menuItem) + + if let theme = theme { + menuItemView.titleColor = theme.textPrimaryColor + menuItemView.imageColor = theme.tintColor + } + + self.add(menuItemView: menuItemView) + } + + self.layoutIfNeeded() + } + + // MARK: - Setup + + private func commonInit() { + } + + convenience init() { + self.init(frame: CGRect.zero) + self.loadNibContent() + commonInit() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.loadNibContent() + commonInit() + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.loadNibContent() + commonInit() + } + + // MARK: - Life cycle + + override func awakeFromNib() { + super.awakeFromNib() + } + + // MARK: - Private + + private func add(menuItemView: ContextualMenuItemView) { + let menuItemContentView = UIView() + menuItemContentView.backgroundColor = .clear + + self.add(menuItemView: menuItemView, on: menuItemContentView) + + self.menuItemsStackView.addArrangedSubview(menuItemContentView) + + let widthConstraint = menuItemContentView.widthAnchor.constraint(equalTo: self.menuItemsStackView.widthAnchor) + widthConstraint.priority = .defaultLow + widthConstraint.isActive = true + + self.menuItemViews.append(menuItemView) + } + + private func add(menuItemView: ContextualMenuItemView, on contentView: UIView) { + contentView.translatesAutoresizingMaskIntoConstraints = false + menuItemView.translatesAutoresizingMaskIntoConstraints = false + + contentView.addSubview(menuItemView) + + menuItemView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true + menuItemView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true + + let widthConstraint = menuItemView.widthAnchor.constraint(equalToConstant: 0.0) + widthConstraint.priority = .defaultLow + widthConstraint.isActive = true + + let minWidthConstraint = menuItemView.widthAnchor.constraint(greaterThanOrEqualToConstant: Constants.menuItemMinWidth) + minWidthConstraint.priority = .required + minWidthConstraint.isActive = true + + let maxWidthConstraint = menuItemView.widthAnchor.constraint(lessThanOrEqualToConstant: Constants.menuItemMaxWidth) + maxWidthConstraint.priority = .required + maxWidthConstraint.isActive = true + } +} diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.xib b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.xib new file mode 100644 index 0000000000..42b2b9ab3f --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.xib @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From d682b97441f67460a51117ad45d1d28330fad4fd Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 23:09:03 +0200 Subject: [PATCH 029/266] Create RoomContextualMenuViewController --- Riot/Generated/Storyboards.swift | 5 + ...oomContextualMenuViewController.storyboard | 57 +++++++++ .../RoomContextualMenuViewController.swift | 113 ++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard create mode 100644 Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index 5db46ea188..b2aa4fda87 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -72,6 +72,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: KeyBackupSetupSuccessFromRecoveryKeyViewController.self) } + internal enum RoomContextualMenuViewController: StoryboardType { + internal static let storyboardName = "RoomContextualMenuViewController" + + internal static let initialScene = InitialSceneType(storyboard: RoomContextualMenuViewController.self) + } internal enum SimpleScreenTemplateViewController: StoryboardType { internal static let storyboardName = "SimpleScreenTemplateViewController" diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard new file mode 100644 index 0000000000..a06eecc818 --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift new file mode 100644 index 0000000000..7492a1e5ee --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift @@ -0,0 +1,113 @@ +/* + Copyright 2019 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 + +@objc protocol RoomContextualMenuViewControllerDelegate: class { + func roomContextualMenuViewControllerDidTapBackgroundOverlay(_ viewController: RoomContextualMenuViewController) +} + +@objcMembers +final class RoomContextualMenuViewController: UIViewController, Themable { + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var backgroundOverlayView: UIView! + @IBOutlet private weak var menuToolbarView: RoomContextualMenuToolbarView! + + @IBOutlet private weak var menuToolbarViewHeightConstraint: NSLayoutConstraint! + @IBOutlet private weak var menuToolbarViewBottomConstraint: NSLayoutConstraint! + + // MARK: Private + + private var theme: Theme! + private var contextualMenuItems: [RoomContextualMenuItem] = [] + + private var hiddenToolbarViewBottomConstant: CGFloat { + let bottomSafeAreaHeight: CGFloat + + if #available(iOS 11.0, *) { + bottomSafeAreaHeight = self.view.safeAreaInsets.bottom + } else { + bottomSafeAreaHeight = self.bottomLayoutGuide.length + } + + return -(self.menuToolbarViewHeightConstraint.constant + bottomSafeAreaHeight) + } + + // MARK: Public + + weak var delegate: RoomContextualMenuViewControllerDelegate? + + // MARK: - Setup + + class func instantiate(with contextualMenuItems: [RoomContextualMenuItem]) -> RoomContextualMenuViewController { + let viewController = StoryboardScene.RoomContextualMenuViewController.initialScene.instantiate() + viewController.theme = ThemeService.shared().theme + viewController.contextualMenuItems = contextualMenuItems + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.backgroundOverlayView.isUserInteractionEnabled = true + self.menuToolbarView.fill(contextualMenuItems: self.contextualMenuItems) + self.setupBackgroundOverlayTapGestureRecognizer() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + } + + // MARK: - Public + + func showMenuToolbar() { + self.menuToolbarViewBottomConstraint.constant = 0 + } + + func hideMenuToolbar() { + self.menuToolbarViewBottomConstraint.constant = self.hiddenToolbarViewBottomConstant + } + + func update(theme: Theme) { + self.menuToolbarView.update(theme: theme) + } + + // MARK: - Private + + private func setupBackgroundOverlayTapGestureRecognizer() { + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(gestureRecognizer:))) + self.backgroundOverlayView.addGestureRecognizer(tapGestureRecognizer) + } + + @objc private func handleTap(gestureRecognizer: UIGestureRecognizer) { + self.delegate?.roomContextualMenuViewControllerDidTapBackgroundOverlay(self) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } +} From 1f43a8ee5ea40377e241f157d81ed27bbba9c18b Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 23:10:07 +0200 Subject: [PATCH 030/266] Create RoomContextualMenuPresenter --- .../RoomContextualMenuPresenter.swift | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift new file mode 100644 index 0000000000..b066de8ebb --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift @@ -0,0 +1,105 @@ +/* + Copyright 2019 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 + +@objcMembers +final class RoomContextualMenuPresenter: NSObject { + + // MARK: - Constants + + private enum Constants { + static let animationDuration: TimeInterval = 0.3 + } + + // MARK: - Properties + + // MARK: Private + + private weak var roomContextualMenuViewController: RoomContextualMenuViewController? + + // MARK: Public + + var isPresenting: Bool { + return self.roomContextualMenuViewController != nil + } + + // MARK: - Public + + func present(roomContextualMenuViewController: RoomContextualMenuViewController, + from viewController: UIViewController, + on view: UIView, + animated: Bool, + completion: (() -> Void)?) { + guard self.roomContextualMenuViewController == nil else { + return + } + + roomContextualMenuViewController.view.alpha = 0 + + viewController.vc_addChildViewController(viewController: roomContextualMenuViewController, onView: view) + + self.roomContextualMenuViewController = roomContextualMenuViewController + + roomContextualMenuViewController.hideMenuToolbar() + roomContextualMenuViewController.view.layoutIfNeeded() + + let animationInstructions: (() -> Void) = { + roomContextualMenuViewController.showMenuToolbar() + roomContextualMenuViewController.view.alpha = 1 + roomContextualMenuViewController.view.layoutIfNeeded() + } + + if animated { + UIView.animate(withDuration: Constants.animationDuration, animations: { + animationInstructions() + }, completion: { completed in + completion?() + }) + } else { + animationInstructions() + completion?() + } + } + + func hideContextualMenu(animated: Bool, completion: (() -> Void)?) { + guard let roomContextualMenuViewController = self.roomContextualMenuViewController else { + return + } + + let animationInstructions: (() -> Void) = { + roomContextualMenuViewController.hideMenuToolbar() + roomContextualMenuViewController.view.alpha = 0 + roomContextualMenuViewController.view.layoutIfNeeded() + } + + let animationCompletionInstructions: (() -> Void) = { + roomContextualMenuViewController.vc_removeFromParent() + completion?() + } + + if animated { + UIView.animate(withDuration: Constants.animationDuration, animations: { + animationInstructions() + }, completion: { completed in + animationCompletionInstructions() + }) + } else { + animationInstructions() + animationCompletionInstructions() + } + } +} From c0e313f156e86fb723eb5a223b8cc6c6b4e50744 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 23:24:34 +0200 Subject: [PATCH 031/266] RoomViewController: Show contextual menu toolbar on long press. --- Riot/Modules/Room/RoomViewController.m | 292 +++++++++++++++++------ Riot/Modules/Room/RoomViewController.xib | 19 +- 2 files changed, 235 insertions(+), 76 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 97ec2831ee..7f4ad5a592 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -123,7 +123,7 @@ #import "Riot-Swift.h" -@interface RoomViewController () +@interface RoomViewController () { // The expanded header ExpandedRoomTitleView *expandedHeader; @@ -213,6 +213,10 @@ @interface RoomViewController () )cell withTappedEvent:(MXEvent*)event +{ + if (event && !customizedRoomDataSource.selectedEventId) + { + [self showContextualMenuForEvent:event cell:cell animated:YES]; + } +} + #pragma mark - Hide/Show expanded header - (void)showExpandedHeader:(BOOL)isVisible @@ -1552,6 +1579,9 @@ - (void)showExpandedHeader:(BOOL)isVisible mainNavigationController.navigationBar.translucent = isVisible; self.navigationController.navigationBar.translucent = isVisible; + // Hide contextual menu if needed + [self hideContextualMenuAnimated:YES]; + [UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ @@ -2030,9 +2060,6 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac [self selectEventWithId:tappedEvent.eventId]; } } - - // Force table refresh - [self dataSource:self.roomDataSource didCellChange:nil]; } else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnOverlayContainer]) { @@ -2073,9 +2100,6 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac // Highlight this event in displayed message [self selectEventWithId:((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.eventId]; } - - // Force table refresh - [self dataSource:self.roomDataSource didCellChange:nil]; } else { @@ -2105,6 +2129,11 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac [self.roomDataSource collapseRoomBubble:((MXKRoomBubbleTableViewCell*)cell).bubbleData collapsed:YES]; } + else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnEvent]) + { + MXEvent *tappedEvent = userInfo[kMXKRoomBubbleCellEventKey]; + [self handleLongPressFromCell:cell withTappedEvent:tappedEvent]; + } else { // Keep default implementation for other actions @@ -2213,24 +2242,6 @@ - (void)showEditButtonAlertMenuForEvent:(MXEvent*)selectedEvent inCell:(id*)contextualMenuItemsForEvent:(MXEvent*)event andCell:(id)cell +{ + NSString *eventId = event.eventId; + MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell *)cell; + MXKAttachment *attachment = roomBubbleTableViewCell.bubbleData.attachment; + + MXWeakify(self); + + // Copy action + + RoomContextualMenuItem *copyMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionCopy]; + copyMenuItem.isEnabled = !attachment || attachment.type != MXKAttachmentTypeSticker; + copyMenuItem.action = ^{ + MXStrongifyAndReturnIfNil(self); + + if (!attachment) + { + NSArray *components = roomBubbleTableViewCell.bubbleData.bubbleComponents; + MXKRoomBubbleComponent *selectedComponent; + for (selectedComponent in components) + { + if ([selectedComponent.event.eventId isEqualToString:event.eventId]) + { + break; + } + selectedComponent = nil; + } + NSString *textMessage = selectedComponent.textMessage; + + [UIPasteboard generalPasteboard].string = textMessage; + + [self hideContextualMenuAnimated:YES]; + } + else if (attachment.type != MXKAttachmentTypeSticker) + { + [self hideContextualMenuAnimated:YES completion:^{ + [self startActivityIndicator]; + + [attachment copy:^{ + + [self stopActivityIndicator]; + + } failure:^(NSError *error) { + + [self stopActivityIndicator]; + + //Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + }]; + + // Start animation in case of download during attachment preparing + [roomBubbleTableViewCell startProgressUI]; + }]; + } + }; + + // Reply action + + RoomContextualMenuItem *replyMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionReply]; + replyMenuItem.isEnabled = [self.roomDataSource canReplyToEventWithId:eventId]; + replyMenuItem.action = ^{ + MXStrongifyAndReturnIfNil(self); + + [self hideContextualMenuAnimated:YES cancelEventSelection:NO completion:nil]; + [self selectEventWithId:eventId enableReplyMode:YES showTimestamp:NO]; + }; + + // Edit action + + RoomContextualMenuItem *editMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionEdit]; + // TODO: Handle edit action + editMenuItem.isEnabled = NO; + + // More action + + RoomContextualMenuItem *moreMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionMore]; + moreMenuItem.action = ^{ + MXStrongifyAndReturnIfNil(self); + [self showEditButtonAlertMenuForEvent:event inCell:cell level:0]; + }; + + // Actions list + + NSArray *actionItems = @[ + copyMenuItem, + replyMenuItem, + editMenuItem, + moreMenuItem + ]; + + return actionItems; +} + +- (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)cell animated:(BOOL)animated +{ + if (self.roomContextualMenuPresenter.isPresenting) + { + return; + } + + [self selectEventWithId:event.eventId enableReplyMode:NO showTimestamp:NO]; + + NSArray* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell]; + + RoomContextualMenuViewController *roomContextualMenuViewController = [RoomContextualMenuViewController instantiateWith:contextualMenuItems]; + roomContextualMenuViewController.delegate = self; + + [self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:roomContextualMenuViewController + from:self + on:self.overlayContainerView + animated:YES + completion:^{ + [self contextualMenuAnimationCompletionAfterBeingShown:YES]; + }]; +} + +- (void)hideContextualMenuAnimated:(BOOL)animated +{ + [self hideContextualMenuAnimated:animated completion:nil]; +} + +- (void)hideContextualMenuAnimated:(BOOL)animated completion:(void(^)(void))completion +{ + [self hideContextualMenuAnimated:animated cancelEventSelection:YES completion:completion]; +} + +- (void)hideContextualMenuAnimated:(BOOL)animated cancelEventSelection:(BOOL)cancelEventSelection completion:(void(^)(void))completion +{ + if (!self.roomContextualMenuPresenter.isPresenting) + { + return; + } + + if (cancelEventSelection) + { + [self cancelEventSelection]; + } + + [self.roomContextualMenuPresenter hideContextualMenuWithAnimated:animated completion:^{ + [self contextualMenuAnimationCompletionAfterBeingShown:NO]; + + if (completion) + { + completion(); + } + }]; +} + +- (void)contextualMenuAnimationCompletionAfterBeingShown:(BOOL)isShown +{ + self.inputToolbarView.editable = !isShown; + self.bubblesTableView.scrollsToTop = !isShown; + self.overlayContainerView.userInteractionEnabled = isShown; +} + +#pragma mark - RoomContextualMenuViewControllerDelegate + +- (void)roomContextualMenuViewControllerDidTapBackgroundOverlay:(RoomContextualMenuViewController *)viewController +{ + [self hideContextualMenuAnimated:YES]; +} + @end diff --git a/Riot/Modules/Room/RoomViewController.xib b/Riot/Modules/Room/RoomViewController.xib index 0a8a14425f..6ea9ec67fd 100644 --- a/Riot/Modules/Room/RoomViewController.xib +++ b/Riot/Modules/Room/RoomViewController.xib @@ -1,11 +1,11 @@ - + - + @@ -21,6 +21,7 @@ + @@ -130,7 +131,7 @@ - + @@ -146,10 +147,15 @@ + + + + + @@ -160,12 +166,15 @@ + + + @@ -174,7 +183,7 @@ - - + + From fd4c46c55b73431377ab354c241ab18e363a3ab4 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 23:24:54 +0200 Subject: [PATCH 032/266] Update pbxproj --- Riot.xcodeproj/project.pbxproj | 56 ++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 08f2bccf2e..0a23f01f57 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -189,6 +189,7 @@ B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1798301211B13B3001FD722 /* OnBoardingManager.swift */; }; B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */; }; B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */; }; + B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A5B33D227ADF2A004CBA85 /* UIImage.swift */; }; B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */; }; B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */; }; B1B5571A20EE6C4D00210D55 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567E20EE6C4C00210D55 /* SettingsViewController.m */; }; @@ -426,6 +427,17 @@ B1B5599320EFC5E400210D55 /* DecryptionFailure.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5598D20EFC5E400210D55 /* DecryptionFailure.m */; }; B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5599120EFC5E400210D55 /* DecryptionFailureTracker.m */; }; B1B9194C2118984300FE25B5 /* RoomPredecessorBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1B9194A2118984300FE25B5 /* RoomPredecessorBubbleCell.xib */; }; + B1C562CA2289C2690037F12A /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */; }; + B1C562CC228AB3510037F12A /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562CB228AB3510037F12A /* UIStackView.swift */; }; + B1C562D9228C0B760037F12A /* RoomContextualMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */; }; + B1C562DB228C0BB00037F12A /* RoomContextualMenuAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562DA228C0BB00037F12A /* RoomContextualMenuAction.swift */; }; + B1C562E1228C7C8C0037F12A /* RoomContextualMenuToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562DC228C7C890037F12A /* RoomContextualMenuToolbarView.swift */; }; + B1C562E2228C7C8D0037F12A /* RoomContextualMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562DD228C7C8A0037F12A /* RoomContextualMenuViewController.swift */; }; + B1C562E3228C7C8D0037F12A /* RoomContextualMenuPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562DE228C7C8B0037F12A /* RoomContextualMenuPresenter.swift */; }; + B1C562E4228C7C8D0037F12A /* RoomContextualMenuToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1C562DF228C7C8C0037F12A /* RoomContextualMenuToolbarView.xib */; }; + B1C562E5228C7C8D0037F12A /* RoomContextualMenuViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1C562E0228C7C8C0037F12A /* RoomContextualMenuViewController.storyboard */; }; + B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562E6228C7CF10037F12A /* ContextualMenuItemView.swift */; }; + B1C562E9228C7CF20037F12A /* ContextualMenuItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1C562E7228C7CF20037F12A /* ContextualMenuItemView.xib */; }; B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2621EF6913000D1D89 /* UIViewController.swift */; }; B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2821EF692B000D1D89 /* UIView.swift */; }; B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */; }; @@ -760,6 +772,7 @@ B1798301211B13B3001FD722 /* OnBoardingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnBoardingManager.swift; sourceTree = ""; }; B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorType.swift; sourceTree = ""; }; B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinator.swift; sourceTree = ""; }; + B1A5B33D227ADF2A004CBA85 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; }; B1B5567920EE6C4C00210D55 /* CountryPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountryPickerViewController.h; sourceTree = ""; }; B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountryPickerViewController.m; sourceTree = ""; }; B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LanguagePickerViewController.m; sourceTree = ""; }; @@ -1133,6 +1146,17 @@ B1B5599020EFC5E400210D55 /* Analytics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Analytics.h; sourceTree = ""; }; B1B5599120EFC5E400210D55 /* DecryptionFailureTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DecryptionFailureTracker.m; sourceTree = ""; }; B1B9194A2118984300FE25B5 /* RoomPredecessorBubbleCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RoomPredecessorBubbleCell.xib; sourceTree = ""; }; + B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = ""; }; + B1C562CB228AB3510037F12A /* UIStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackView.swift; sourceTree = ""; }; + B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuItem.swift; sourceTree = ""; }; + B1C562DA228C0BB00037F12A /* RoomContextualMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuAction.swift; sourceTree = ""; }; + B1C562DC228C7C890037F12A /* RoomContextualMenuToolbarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuToolbarView.swift; sourceTree = ""; }; + B1C562DD228C7C8A0037F12A /* RoomContextualMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuViewController.swift; sourceTree = ""; }; + B1C562DE228C7C8B0037F12A /* RoomContextualMenuPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuPresenter.swift; sourceTree = ""; }; + B1C562DF228C7C8C0037F12A /* RoomContextualMenuToolbarView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RoomContextualMenuToolbarView.xib; sourceTree = ""; }; + B1C562E0228C7C8C0037F12A /* RoomContextualMenuViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = RoomContextualMenuViewController.storyboard; sourceTree = ""; }; + B1C562E6228C7CF10037F12A /* ContextualMenuItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextualMenuItemView.swift; sourceTree = ""; }; + B1C562E7228C7CF20037F12A /* ContextualMenuItemView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContextualMenuItemView.xib; sourceTree = ""; }; B1CA3A2621EF6913000D1D89 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = ""; }; B1CA3A2821EF692B000D1D89 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignOutAlertPresenter.swift; sourceTree = ""; }; @@ -1997,6 +2021,7 @@ B1B556A120EE6C4C00210D55 /* Files */, B1B556A420EE6C4C00210D55 /* Members */, B1B5569020EE6C4C00210D55 /* Settings */, + B1C562D7228C0B4C0037F12A /* ContextualMenu */, ); path = Room; sourceTree = ""; @@ -2977,6 +3002,22 @@ path = Analytics; sourceTree = ""; }; + B1C562D7228C0B4C0037F12A /* ContextualMenu */ = { + isa = PBXGroup; + children = ( + B1C562DA228C0BB00037F12A /* RoomContextualMenuAction.swift */, + B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */, + B1C562DE228C7C8B0037F12A /* RoomContextualMenuPresenter.swift */, + B1C562DD228C7C8A0037F12A /* RoomContextualMenuViewController.swift */, + B1C562E0228C7C8C0037F12A /* RoomContextualMenuViewController.storyboard */, + B1C562E6228C7CF10037F12A /* ContextualMenuItemView.swift */, + B1C562E7228C7CF20037F12A /* ContextualMenuItemView.xib */, + B1C562DC228C7C890037F12A /* RoomContextualMenuToolbarView.swift */, + B1C562DF228C7C8C0037F12A /* RoomContextualMenuToolbarView.xib */, + ); + path = ContextualMenu; + sourceTree = ""; + }; B1CE9EFB22148681000FAE6A /* SignOut */ = { isa = PBXGroup; children = ( @@ -3131,6 +3172,9 @@ B109D6F0222D8C400061B6D9 /* UIApplication.swift */, B1DB4F05223015080065DBFA /* Character.swift */, B1DB4F0A223131600065DBFA /* String.swift */, + B1A5B33D227ADF2A004CBA85 /* UIImage.swift */, + B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */, + B1C562CB228AB3510037F12A /* UIStackView.swift */, ); path = Categories; sourceTree = ""; @@ -3426,6 +3470,7 @@ B1B558EA20EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib in Resources */, B1B558CD20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.xib in Resources */, B1B9194C2118984300FE25B5 /* RoomPredecessorBubbleCell.xib in Resources */, + B1C562E9228C7CF20037F12A /* ContextualMenuItemView.xib in Resources */, B1B5572120EE6C4D00210D55 /* ContactsTableViewController.xib in Resources */, B1B5593A20EF7BAC00210D55 /* TableViewCellWithLabelAndLargeTextView.xib in Resources */, B1B558D820EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.xib in Resources */, @@ -3496,6 +3541,7 @@ B1B557D720EF5EA900210D55 /* RoomActivitiesView.xib in Resources */, B1098BF821ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard in Resources */, F083BDF31E7009ED00A9B29C /* Images.xcassets in Resources */, + B1C562E4228C7C8D0037F12A /* RoomContextualMenuToolbarView.xib in Resources */, B1B5590720EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib in Resources */, B169329920F39E6300746532 /* LaunchScreen.storyboard in Resources */, B1B5595320EF9A8700210D55 /* RecentTableViewCell.xib in Resources */, @@ -3520,6 +3566,7 @@ B1B558C020EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib in Resources */, B1B5572420EE6C4D00210D55 /* RoomViewController.xib in Resources */, B169331520F3CAFC00746532 /* PublicRoomTableViewCell.xib in Resources */, + B1C562E5228C7C8D0037F12A /* RoomContextualMenuViewController.storyboard in Resources */, 3232ABA2225730E100AD6A5C /* DeviceVerificationStartViewController.storyboard in Resources */, 3284A35120A07C210044F922 /* postMessageAPI.js in Resources */, B1B557A220EF58AD00210D55 /* ContactTableViewCell.xib in Resources */, @@ -3808,6 +3855,7 @@ B1B5572F20EE6C4D00210D55 /* ReadReceiptsViewController.m in Sources */, B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B169330B20F3CA3A00746532 /* Contact.m in Sources */, + B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */, B1D4752A21EE52B10067973F /* KeyBackupSetupIntroViewController.swift in Sources */, B1B5599220EFC5E400210D55 /* Analytics.m in Sources */, B14F143422144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift in Sources */, @@ -3852,6 +3900,7 @@ 32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */, F083BDEF1E7009ED00A9B29C /* UINavigationController+Riot.m in Sources */, B1B5581F20EF625800210D55 /* SimpleRoomTitleView.m in Sources */, + B1C562E2228C7C8D0037F12A /* RoomContextualMenuViewController.swift in Sources */, B169330020F3C97D00746532 /* RoomDataSource.m in Sources */, B1B558ED20EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderNameBubbleCell.m in Sources */, B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */, @@ -3891,6 +3940,7 @@ 3232ABBA2257BE6500AD6A5C /* DeviceVerificationVerifyViewModel.swift in Sources */, B1098C1021ED07E4000DDA48 /* Presentable.swift in Sources */, B1B558E020EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.m in Sources */, + B1C562E3228C7C8D0037F12A /* RoomContextualMenuPresenter.swift in Sources */, B1B5593C20EF7BAC00210D55 /* TableViewCellWithCheckBoxes.m in Sources */, 32891D6B2264CBA300C82226 /* SimpleScreenTemplateViewController.swift in Sources */, B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */, @@ -3936,6 +3986,8 @@ B1B5572020EE6C4D00210D55 /* ContactsTableViewController.m in Sources */, B1B5581920EF625800210D55 /* RoomTitleView.m in Sources */, B1098BE321ECE09F000DDA48 /* RiotDefaults.swift in Sources */, + B1C562CA2289C2690037F12A /* UIGestureRecognizer.swift in Sources */, + B1C562CC228AB3510037F12A /* UIStackView.swift in Sources */, B1B557BE20EF5B4500210D55 /* RoomInputToolbarView.m in Sources */, B1B5573B20EE6C4D00210D55 /* FavouritesViewController.m in Sources */, B1B5579920EF575B00210D55 /* AuthInputsView.m in Sources */, @@ -3977,11 +4029,14 @@ 324A2054225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift in Sources */, 3232ABB92257BE6500AD6A5C /* DeviceVerificationVerifyViewController.swift in Sources */, B139C21F21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift in Sources */, + B1C562DB228C0BB00037F12A /* RoomContextualMenuAction.swift in Sources */, B1B5574720EE6C4D00210D55 /* UsersDevicesViewController.m in Sources */, B1098BFF21ECFE65000DDA48 /* PasswordStrengthView.swift in Sources */, B1B558D220EF768F00210D55 /* RoomEncryptedDataBubbleCell.m in Sources */, B1B558FA20EF768F00210D55 /* RoomMembershipBubbleCell.m in Sources */, 3232ABA1225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift in Sources */, + B1C562D9228C0B760037F12A /* RoomContextualMenuItem.swift in Sources */, + B1C562E1228C7C8C0037F12A /* RoomContextualMenuToolbarView.swift in Sources */, B1B557BF20EF5B4500210D55 /* DisabledRoomInputToolbarView.m in Sources */, B1B5578620EF564900210D55 /* GroupTableViewCellWithSwitch.m in Sources */, B1098BE821ECFE52000DDA48 /* Coordinator.swift in Sources */, @@ -4036,6 +4091,7 @@ B1098C0021ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.swift in Sources */, B1B5591020EF782800210D55 /* TableViewCellWithPhoneNumberTextField.m in Sources */, B1DB4F06223015080065DBFA /* Character.swift in Sources */, + B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */, B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */, B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */, ); From f5887c477493b1ad5710609a61492224e1180429 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 23:30:33 +0200 Subject: [PATCH 033/266] RoomViewController: Disable reply on single tap selection. --- Riot/Modules/Room/RoomViewController.m | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 7f4ad5a592..c81d636d69 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2854,9 +2854,7 @@ - (BOOL)dataSource:(MXKDataSource *)dataSource shouldDoAction:(NSString *)action - (void)selectEventWithId:(NSString*)eventId { - BOOL shouldEnableReplyMode = [self.roomDataSource canReplyToEventWithId:eventId]; - - [self selectEventWithId:eventId enableReplyMode:shouldEnableReplyMode showTimestamp:YES]; + [self selectEventWithId:eventId enableReplyMode:NO showTimestamp:YES]; } - (void)selectEventWithId:(NSString*)eventId enableReplyMode:(BOOL)enableReplyMode showTimestamp:(BOOL)showTimestamp From b49b24713844c5ee3f85c4b4657079a102cf0f8c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 15 May 2019 23:34:21 +0200 Subject: [PATCH 034/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 4e072709e9..ae4a5aa138 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -3,6 +3,7 @@ Changes in 0.8.6 (2019-xx-xx) Improvements: * RoomVC: When replying, use a "Reply" button instead of "Send". + * RoomVC: New message actions (#2394). Changes in 0.8.5 (2019-xx-xx) =============================================== From 7cd5e79bc6199c05b3545c84fbb488ccced89ba4 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 15 May 2019 23:35:09 +0200 Subject: [PATCH 035/266] Reactions: Customise primary reactions buttons --- Riot.xcodeproj/project.pbxproj | 4 + Riot/Managers/Theme/Theme.swift | 1 + Riot/Managers/Theme/Themes/DarkTheme.swift | 1 + Riot/Managers/Theme/Themes/DefaultTheme.swift | 1 + .../ReactionsMenu/ReactionsMenuButton.swift | 73 +++++++++++++++++++ .../ReactionsMenu/ReactionsMenuView.xib | 8 +- 6 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 50fe5f59ab..7c626871df 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -71,6 +71,7 @@ 325380D7228C2E5800ADDEFA /* ReactionsMenuReactions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D6228C2E5800ADDEFA /* ReactionsMenuReactions.swift */; }; 325380DB228C34EF00ADDEFA /* ReactionsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */; }; 325380DD228C34FC00ADDEFA /* ReactionsMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 325380DC228C34FC00ADDEFA /* ReactionsMenuView.xib */; }; + 325380DF228C5C2800ADDEFA /* ReactionsMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380DE228C5C2800ADDEFA /* ReactionsMenuButton.swift */; }; 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; }; 3281BCF72201FA4200F4A383 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3281BCF62201FA4200F4A383 /* UIControl.swift */; }; 3284A35120A07C210044F922 /* postMessageAPI.js in Resources */ = {isa = PBXBuildFile; fileRef = 3284A35020A07C210044F922 /* postMessageAPI.js */; }; @@ -574,6 +575,7 @@ 325380D6228C2E5800ADDEFA /* ReactionsMenuReactions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuReactions.swift; sourceTree = ""; }; 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuView.swift; sourceTree = ""; }; 325380DC228C34FC00ADDEFA /* ReactionsMenuView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReactionsMenuView.xib; sourceTree = ""; }; + 325380DE228C5C2800ADDEFA /* ReactionsMenuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuButton.swift; sourceTree = ""; }; 3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = ""; }; 3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 3267EFB520E379FD00FF1CAA /* AUTHORS.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS.rst; sourceTree = ""; }; @@ -1435,6 +1437,7 @@ 325380D6228C2E5800ADDEFA /* ReactionsMenuReactions.swift */, 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */, 325380DC228C34FC00ADDEFA /* ReactionsMenuView.xib */, + 325380DE228C5C2800ADDEFA /* ReactionsMenuButton.swift */, ); path = ReactionsMenu; sourceTree = ""; @@ -4026,6 +4029,7 @@ B1B5578620EF564900210D55 /* GroupTableViewCellWithSwitch.m in Sources */, B1098BE821ECFE52000DDA48 /* Coordinator.swift in Sources */, B1B557E920EF60F500210D55 /* MessagesSearchResultTextMsgBubbleCell.m in Sources */, + 325380DF228C5C2800ADDEFA /* ReactionsMenuButton.swift in Sources */, 324A2050225FC571004FE8B0 /* DeviceVerificationIncomingViewController.swift in Sources */, B1098C0D21ED07E4000DDA48 /* NavigationRouter.swift in Sources */, B110872321F098F0003554A5 /* ActivityIndicatorPresenterType.swift in Sources */, diff --git a/Riot/Managers/Theme/Theme.swift b/Riot/Managers/Theme/Theme.swift index 375dd3913d..c8a10674aa 100644 --- a/Riot/Managers/Theme/Theme.swift +++ b/Riot/Managers/Theme/Theme.swift @@ -38,6 +38,7 @@ import UIKit var textSecondaryColor: UIColor { get } var tintColor: UIColor { get } + var tintBackgroundColor: UIColor { get } var unreadRoomIndentColor: UIColor { get } diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift index 1b3651a6e7..3c939e2c10 100644 --- a/Riot/Managers/Theme/Themes/DarkTheme.swift +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -39,6 +39,7 @@ class DarkTheme: NSObject, Theme { var textSecondaryColor: UIColor = UIColor(rgb: 0xA1B2D1) var tintColor: UIColor = UIColor(rgb: 0x03B381) + var tintBackgroundColor: UIColor = UIColor(rgb: 0xe9fff9) var unreadRoomIndentColor: UIColor = UIColor(rgb: 0x2E3648) var lineBreakColor: UIColor = UIColor(rgb: 0x61708B) diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift index d1bcaf9f80..19b1ba1094 100644 --- a/Riot/Managers/Theme/Themes/DefaultTheme.swift +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -39,6 +39,7 @@ class DefaultTheme: NSObject, Theme { var textSecondaryColor: UIColor = UIColor(rgb: 0x9E9E9E) var tintColor: UIColor = UIColor(rgb: 0x03B381) + var tintBackgroundColor: UIColor = UIColor(rgb: 0xe9fff9) var unreadRoomIndentColor: UIColor = UIColor(rgb: 0x2E3648) var lineBreakColor: UIColor = UIColor(rgb: 0xEEEFEF) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift new file mode 100644 index 0000000000..b68f07e040 --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift @@ -0,0 +1,73 @@ +/* + Copyright 2019 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 + +class ReactionsMenuButton: UIButton { + + // MARK: Private + + private var theme: Theme! + + // MARK: - Setup + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.commonInit() + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.commonInit() + } + + // MARK: - Life cycle + + override func layoutSubviews() { + super.layoutSubviews() + self.layer.cornerRadius = self.frame.size.height / 2 + self.layer.borderWidth = self.isSelected ? 1 : 0 + } + + // MARK: - Private + + private func commonInit() { + self.theme = ThemeService.shared().theme + + customizeViewRendering() + updateView() + } + + func customizeViewRendering() { + self.tintColor = UIColor.clear + + // TODO: Color for black theme + self.setTitleColor(self.theme.textPrimaryColor, for: .normal) + self.setTitleColor(self.theme.textPrimaryColor, for: .selected) + + self.layer.borderColor = self.theme.tintColor.cgColor + } + + func updateView() { + backgroundColor = isSelected ? self.theme.tintBackgroundColor : self.theme.headerBackgroundColor + } + + override open var isSelected: Bool { + didSet { + self.updateView() + } + } +} diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib index 08c0301592..9b81ffaeb5 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib @@ -23,7 +23,7 @@ - - - - diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewAction.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewAction.swift new file mode 100644 index 0000000000..b98ac829ae --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewAction.swift @@ -0,0 +1,22 @@ +/* + Copyright 2019 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 + +/// Action chosen by the user +enum ReactionsMenuViewAction { + case toggleReaction(ReactionsMenuReaction) +} diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 819ce6ec0a..43b94d0c96 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -38,7 +38,6 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { // MARK: - Setup init(aggregations: MXAggregations, roomId: String, eventId: String) { - self.aggregations = aggregations self.roomId = roomId self.eventId = eventId @@ -47,8 +46,36 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { self.listenToDataUpdate() } - // MARK: - Private + // MARK: - Public + + func process(viewAction: ReactionsMenuViewAction) { + var reaction: ReactionsMenuReaction? + var newState: Bool? + + switch viewAction { + case .toggleReaction(let menuReaction): + reaction = menuReaction + + switch menuReaction { + case .agree: + newState = !self.isAgreeButtonSelected + case .disagree: + newState = !self.isDisagreeButtonSelected + case .like: + newState = !self.isLikeButtonSelected + case .dislike: + newState = !self.isDislikeButtonSelected + } + } + + guard let theReaction = reaction, let theNewState = newState else { + return + } + self.react(withReaction: theReaction, selected: theNewState) + } + + // MARK: - Private private func resetData() { self.isAgreeButtonSelected = false @@ -64,7 +91,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { self.resetData() reactionCounts.forEach { (reaction) in - if let reaction = ReactionsMenuReactions(rawValue: reaction.reaction) { + if let reaction = ReactionsMenuReaction(rawValue: reaction.reaction) { switch reaction { case .agree: self.isAgreeButtonSelected = true @@ -78,9 +105,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { } } - if let viewDelegate = self.viewDelegate { - viewDelegate.reactionsMenuViewModelDidUpdate(self) - } + self.viewDelegate?.reactionsMenuViewModelDidUpdate(self) } private func listenToDataUpdate() { @@ -96,4 +121,34 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { } } + private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool) { + if selected { + self.aggregations.sendReaction(reaction.rawValue, toEvent: self.eventId, inRoom: self.roomId, success: {_ in + + }, failure: {(error) in + print("[ReactionsMenuViewModel] react: Error: \(error)") + }) + } else { + // TODO + } + + self.fakeToggleReaction(reaction: reaction) + } + + // TODO: to remove + private func fakeToggleReaction(reaction: ReactionsMenuReaction) { + switch reaction { + case .agree: + isAgreeButtonSelected = !isDislikeButtonSelected + case .disagree: + isDisagreeButtonSelected = !isDisagreeButtonSelected + case .like: + isLikeButtonSelected = !isLikeButtonSelected + case .dislike: + isDislikeButtonSelected = !isDislikeButtonSelected + } + + self.viewDelegate?.reactionsMenuViewModelDidUpdate(self) + } + } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift index bbdfbf5bed..81b52fd0d1 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift @@ -32,7 +32,8 @@ protocol ReactionsMenuViewModelType { var isLikeButtonSelected: Bool { get } var isDislikeButtonSelected: Bool { get } - var viewDelegate: ReactionsMenuViewModelDelegate? { get set } var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? { get set } + + func process(viewAction: ReactionsMenuViewAction) } From 13beb50abb406250a6b6eac694e3a1269d3246e6 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 16 May 2019 08:07:52 +0200 Subject: [PATCH 037/266] Reactions: notify coordinator for every reaction request steps so that it can leave the view when it wants --- Riot.xcodeproj/project.pbxproj | 4 ---- .../ReactionsMenu/ReactionsMenuAction.swift | 23 ------------------- .../ReactionsMenuViewModel.swift | 23 ++++++++++++++++--- .../ReactionsMenuViewModelType.swift | 4 +++- 4 files changed, 23 insertions(+), 31 deletions(-) delete mode 100644 Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuAction.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 8bd2e1c78c..74c5f7fb5f 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -65,7 +65,6 @@ 324A2054225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324A204C225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift */; }; 324A2055225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324A204D225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift */; }; 324A2056225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324A204E225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift */; }; - 325380D1228C1BE500ADDEFA /* ReactionsMenuAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D0228C1BE500ADDEFA /* ReactionsMenuAction.swift */; }; 325380D3228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D2228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift */; }; 325380D5228C245D00ADDEFA /* ReactionsMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D4228C245D00ADDEFA /* ReactionsMenuViewModel.swift */; }; 325380DB228C34EF00ADDEFA /* ReactionsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */; }; @@ -570,7 +569,6 @@ 324A204C225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingCoordinatorType.swift; sourceTree = ""; }; 324A204D225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingViewModelType.swift; sourceTree = ""; }; 324A204E225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingCoordinator.swift; sourceTree = ""; }; - 325380D0228C1BE500ADDEFA /* ReactionsMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuAction.swift; sourceTree = ""; }; 325380D2228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModelType.swift; sourceTree = ""; }; 325380D4228C245D00ADDEFA /* ReactionsMenuViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModel.swift; sourceTree = ""; }; 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuView.swift; sourceTree = ""; }; @@ -1434,7 +1432,6 @@ isa = PBXGroup; children = ( 325380E2228D2EE500ADDEFA /* ReactionsMenuReaction.swift */, - 325380D0228C1BE500ADDEFA /* ReactionsMenuAction.swift */, 325380D2228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift */, 325380D4228C245D00ADDEFA /* ReactionsMenuViewModel.swift */, 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */, @@ -3937,7 +3934,6 @@ B1B558E020EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.m in Sources */, B1B5593C20EF7BAC00210D55 /* TableViewCellWithCheckBoxes.m in Sources */, 32891D6B2264CBA300C82226 /* SimpleScreenTemplateViewController.swift in Sources */, - 325380D1228C1BE500ADDEFA /* ReactionsMenuAction.swift in Sources */, B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */, F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */, B1B5596F20EFA85D00210D55 /* EncryptionInfoView.m in Sources */, diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuAction.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuAction.swift deleted file mode 100644 index 39bfd21472..0000000000 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuAction.swift +++ /dev/null @@ -1,23 +0,0 @@ -/* - Copyright 2019 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 - -/// TemplateScreenViewController view actions exposed to view model -enum ReactionsMenuAction { - case react(String) - case unreact(String) -} diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 43b94d0c96..bb8759ca75 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -123,15 +123,32 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool) { if selected { - self.aggregations.sendReaction(reaction.rawValue, toEvent: self.eventId, inRoom: self.roomId, success: {_ in + self.aggregations.sendReaction(reaction.rawValue, toEvent: self.eventId, inRoom: self.roomId, success: {[weak self] _ in - }, failure: {(error) in + guard let sself = self else { + return + } + + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: true) + + }, failure: {[weak self] (error) in print("[ReactionsMenuViewModel] react: Error: \(error)") + + guard let sself = self else { + return + } + + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) }) + + self.coordinatorDelegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: true) } else { + // TODO + self.coordinatorDelegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: false) } + // TODO: to remove self.fakeToggleReaction(reaction: reaction) } @@ -139,7 +156,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { private func fakeToggleReaction(reaction: ReactionsMenuReaction) { switch reaction { case .agree: - isAgreeButtonSelected = !isDislikeButtonSelected + isAgreeButtonSelected = !isAgreeButtonSelected case .disagree: isDisagreeButtonSelected = !isDisagreeButtonSelected case .like: diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift index 81b52fd0d1..777376a4a1 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift @@ -21,7 +21,9 @@ protocol ReactionsMenuViewModelDelegate: class { } protocol ReactionsMenuViewModelCoordinatorDelegate: class { - func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, doAction action: ReactionsMenuAction) + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didSendReaction reaction: String, isAddReaction: Bool) + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didReactionComplete reaction: String, isAddReaction: Bool) + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didReactionFailedWithError error: Error, reaction: String, isAddReaction: Bool) } From 028763ba243c08ed8b5d6c576e3d160622664246 Mon Sep 17 00:00:00 2001 From: Giom Foret Date: Thu, 16 May 2019 10:19:23 +0200 Subject: [PATCH 038/266] Update third-party licences --- Riot/Assets/third_party_licenses.html | 1910 +++++++++++++------------ 1 file changed, 965 insertions(+), 945 deletions(-) diff --git a/Riot/Assets/third_party_licenses.html b/Riot/Assets/third_party_licenses.html index b1f0e155bd..5f067b63d2 100644 --- a/Riot/Assets/third_party_licenses.html +++ b/Riot/Assets/third_party_licenses.html @@ -21,6 +21,9 @@ href="https://github.com/matrix-org/matrix-ios-kit.git">https://github.com/matrix-org/matrix-ios-kit.git)

The Matrix reusable UI library for iOS based on MatrixSDK.

Copyright (c) 2014-2016 OpenMarket Ltd. +
Copyright (c) 2016-2017 Vector Creations Ltd +
Copyright (c) 2018-2019 New Vector Ltd +
Copyright (c) 2019 The Matrix.org Foundation C.I.C

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License at:

https://www.apache.org/licenses/LICENSE-2.0 @@ -33,6 +36,9 @@

The iOS SDK to build apps compatible with Matrix (https://www.matrix.org).

Copyright (c) 2014-2016 OpenMarket Ltd. +
Copyright (c) 2016-2017 Vector Creations Ltd +
Copyright (c) 2018-2019 New Vector Ltd +
Copyright (c) 2019 The Matrix.org Foundation C.I.C

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this work except in compliance with the License. You may obtain a copy of the License at:

https://www.apache.org/licenses/LICENSE-2.0 @@ -131,951 +137,965 @@

  • - WebRTC-iOS (WebRTC iOS framework) -

    webrtc

    -
    -            Copyright (c) 2011, The WebRTC project authors. All rights reserved.
    -
    -            Redistribution and use in source and binary forms, with or without
    -            modification, are permitted provided that the following conditions are
    -            met:
    -
    -            * Redistributions of source code must retain the above copyright
    -            notice, this list of conditions and the following disclaimer.
    -
    -            * Redistributions in binary form must reproduce the above copyright
    -            notice, this list of conditions and the following disclaimer in
    -            the documentation and/or other materials provided with the
    -            distribution.
    -
    -            * Neither the name of Google nor the names of its contributors may
    -            be used to endorse or promote products derived from this software
    -            without specific prior written permission.
    -
    -            THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    -            "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    -            LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    -            A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    -            HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    -            SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    -            LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    -            DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    -            THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    -            (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    -            OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    -
    -            This source tree contains third party source code which is governed by third
    -            party licenses. Paths to the files and associated licenses are collected here.
    -
    -            Files governed by third party licenses:
    -            common_audio/fft4g.c
    -            common_audio/signal_processing/spl_sqrt_floor.c
    -            common_audio/signal_processing/spl_sqrt_floor_arm.S
    -            modules/audio_coding/codecs/g711/main/source/g711.c
    -            modules/audio_coding/codecs/g711/main/source/g711.h
    -            modules/audio_coding/codecs/g722/main/source/g722_decode.c
    -            modules/audio_coding/codecs/g722/main/source/g722_enc_dec.h
    -            modules/audio_coding/codecs/g722/main/source/g722_encode.c
    -            modules/audio_coding/codecs/isac/main/source/fft.c
    -            modules/audio_device/mac/portaudio/pa_memorybarrier.h
    -            modules/audio_device/mac/portaudio/pa_ringbuffer.c
    -            modules/audio_device/mac/portaudio/pa_ringbuffer.h
    -            modules/audio_processing/aec/aec_rdft.c
    -            system_wrappers/source/condition_variable_event_win.cc
    -            system_wrappers/source/set_thread_name_win.h
    -            system_wrappers/source/spreadsortlib/constants.hpp
    -            system_wrappers/source/spreadsortlib/spreadsort.hpp
    -
    -            Individual licenses for each file:
    -            -------------------------------------------------------------------------------
    -            Files:
    -            common_audio/signal_processing/spl_sqrt_floor.c
    -            common_audio/signal_processing/spl_sqrt_floor_arm.S
    -
    -            License:
    -            /*
    -            * Written by Wilco Dijkstra, 1996. The following email exchange establishes the
    -            * license.
    -            *
    -            * From: Wilco Dijkstra <Wilco.Dijkstra@ntlworld.com>
    -            * Date: Fri, Jun 24, 2011 at 3:20 AM
    -            * Subject: Re: sqrt routine
    -            * To: Kevin Ma <kma@google.com>
    -            * Hi Kevin,
    -            * Thanks for asking. Those routines are public domain (originally posted to
    -            * comp.sys.arm a long time ago), so you can use them freely for any purpose.
    -            * Cheers,
    -            * Wilco
    -            *
    -            * ----- Original Message -----
    -            * From: "Kevin Ma" <kma@google.com>
    -            * To: <Wilco.Dijkstra@ntlworld.com>
    -            * Sent: Thursday, June 23, 2011 11:44 PM
    -            * Subject: Fwd: sqrt routine
    -            * Hi Wilco,
    -            * I saw your sqrt routine from several web sites, including
    -            * http://www.finesse.demon.co.uk/steven/sqrt.html.
    -            * Just wonder if there's any copyright information with your Successive
    -            * approximation routines, or if I can freely use it for any purpose.
    -            * Thanks.
    -            * Kevin
    -            */
    -            -------------------------------------------------------------------------------
    -            Files:
    -            modules/audio_coding/codecs/g711/main/source/g711.c
    -            modules/audio_coding/codecs/g711/main/source/g711.h
    -
    -            License:
    -            /*
    -            * SpanDSP - a series of DSP components for telephony
    -            *
    -            * g711.h - In line A-law and u-law conversion routines
    -            *
    -            * Written by Steve Underwood <steveu@coppice.org>
    -            *
    -            * Copyright (C) 2001 Steve Underwood
    -            *
    -            *  Despite my general liking of the GPL, I place this code in the
    -            *  public domain for the benefit of all mankind - even the slimy
    -            *  ones who might try to proprietize my work and use it to my
    -            *  detriment.
    -            */
    -            -------------------------------------------------------------------------------
    -            Files:
    -            modules/audio_coding/codecs/g722/main/source/g722_decode.c
    -            modules/audio_coding/codecs/g722/main/source/g722_enc_dec.h
    -            modules/audio_coding/codecs/g722/main/source/g722_encode.c
    -
    -            License:
    -            /*
    -            * SpanDSP - a series of DSP components for telephony
    -            *
    -            * g722_decode.c - The ITU G.722 codec, decode part.
    -            *
    -            * Written by Steve Underwood <steveu@coppice.org>
    -            *
    -            * Copyright (C) 2005 Steve Underwood
    -            *
    -            *  Despite my general liking of the GPL, I place my own contributions
    -            *  to this code in the public domain for the benefit of all mankind -
    -            *  even the slimy ones who might try to proprietize my work and use it
    -            *  to my detriment.
    -            *
    -            * Based in part on a single channel G.722 codec which is:
    -            *
    -            * Copyright (c) CMU 1993
    -            * Computer Science, Speech Group
    -            * Chengxiang Lu and Alex Hauptmann
    -            */
    -            -------------------------------------------------------------------------------
    -            Files:
    -            modules/audio_coding/codecs/isac/main/source/fft.c
    -
    -            License:
    -            /*
    -            * Copyright(c)1995,97 Mark Olesen <olesen@me.QueensU.CA>
    -            *    Queen's Univ at Kingston (Canada)
    -            *
    -            * Permission to use, copy, modify, and distribute this software for
    -            * any purpose without fee is hereby granted, provided that this
    -            * entire notice is included in all copies of any software which is
    -            * or includes a copy or modification of this software and in all
    -            * copies of the supporting documentation for such software.
    -            *
    -            * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
    -            * IMPLIED WARRANTY.  IN PARTICULAR, NEITHER THE AUTHOR NOR QUEEN'S
    -            * UNIVERSITY AT KINGSTON MAKES ANY REPRESENTATION OR WARRANTY OF ANY
    -            * KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
    -            * FITNESS FOR ANY PARTICULAR PURPOSE.
    -            *
    -            * All of which is to say that you can do what you like with this
    -            * source code provided you don't try to sell it as your own and you
    -            * include an unaltered copy of this message (including the
    -            * copyright).
    -            *
    -            * It is also implicitly understood that bug fixes and improvements
    -            * should make their way back to the general Internet community so
    -            * that everyone benefits.
    -            */
    -            -------------------------------------------------------------------------------
    -            Files:
    -            modules/audio_device/mac/portaudio/pa_memorybarrier.h
    -            modules/audio_device/mac/portaudio/pa_ringbuffer.c
    -            modules/audio_device/mac/portaudio/pa_ringbuffer.h
    -
    -            License:
    -            /*
    -            * $Id: pa_memorybarrier.h 1240 2007-07-17 13:05:07Z bjornroche $
    -            * Portable Audio I/O Library
    -            * Memory barrier utilities
    -            *
    -            * Author: Bjorn Roche, XO Audio, LLC
    -            *
    -            * This program uses the PortAudio Portable Audio Library.
    -            * For more information see: http://www.portaudio.com
    -            * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
    -            *
    -            * Permission is hereby granted, free of charge, to any person obtaining
    -            * a copy of this software and associated documentation files
    -            * (the "Software"), to deal in the Software without restriction,
    -            * including without limitation the rights to use, copy, modify, merge,
    -            * publish, distribute, sublicense, and/or sell copies of the Software,
    -            * and to permit persons to whom the Software is furnished to do so,
    -            * subject to the following conditions:
    -            *
    -            * The above copyright notice and this permission notice shall be
    -            * included in all copies or substantial portions of the Software.
    -            *
    -            * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    -            * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    -            * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    -            * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
    -            * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
    -            * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    -            * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    -            */
    -
    -            /*
    -            * The text above constitutes the entire PortAudio license; however,
    -            * the PortAudio community also makes the following non-binding requests:
    -            *
    -            * Any person wishing to distribute modifications to the Software is
    -            * requested to send the modifications to the original developer so that
    -            * they can be incorporated into the canonical version. It is also
    -            * requested that these non-binding requests be included along with the
    -            * license above.
    -            */
    -
    -            /*
    -            * $Id: pa_ringbuffer.c 1421 2009-11-18 16:09:05Z bjornroche $
    -            * Portable Audio I/O Library
    -            * Ring Buffer utility.
    -            *
    -            * Author: Phil Burk, http://www.softsynth.com
    -            * modified for SMP safety on Mac OS X by Bjorn Roche
    -            * modified for SMP safety on Linux by Leland Lucius
    -            * also, allowed for const where possible
    -            * modified for multiple-byte-sized data elements by Sven Fischer
    -            *
    -            * Note that this is safe only for a single-thread reader and a
    -            * single-thread writer.
    -            *
    -            * This program uses the PortAudio Portable Audio Library.
    -            * For more information see: http://www.portaudio.com
    -            * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
    -            *
    -            * Permission is hereby granted, free of charge, to any person obtaining
    -            * a copy of this software and associated documentation files
    -            * (the "Software"), to deal in the Software without restriction,
    -            * including without limitation the rights to use, copy, modify, merge,
    -            * publish, distribute, sublicense, and/or sell copies of the Software,
    -            * and to permit persons to whom the Software is furnished to do so,
    -            * subject to the following conditions:
    -            *
    -            * The above copyright notice and this permission notice shall be
    -            * included in all copies or substantial portions of the Software.
    -            *
    -            * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    -            * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    -            * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    -            * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
    -            * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
    -            * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
    -            * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    -            */
    -
    -            /*
    -            * The text above constitutes the entire PortAudio license; however,
    -            * the PortAudio community also makes the following non-binding requests:
    -            *
    -            * Any person wishing to distribute modifications to the Software is
    -            * requested to send the modifications to the original developer so that
    -            * they can be incorporated into the canonical version. It is also
    -            * requested that these non-binding requests be included along with the
    -            * license above.
    -            */
    -            -------------------------------------------------------------------------------
    -            Files:
    -            common_audio/fft4g.c
    -            modules/audio_processing/aec/aec_rdft.c
    -
    -            License:
    -            /*
    -            * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html
    -            * Copyright Takuya OOURA, 1996-2001
    -            *
    -            * You may use, copy, modify and distribute this code for any purpose (include
    -            * commercial use) and without fee. Please refer to this package when you modify
    -            * this code.
    -            */
    -            -------------------------------------------------------------------------------
    -            Files:
    -            system_wrappers/source/condition_variable_event_win.cc
    -
    -            Source:
    -            http://www1.cse.wustl.edu/~schmidt/ACE-copying.html
    -
    -            License:
    -            Copyright and Licensing Information for ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM),
    -            and CoSMIC(TM)
    -
    -            ACE(TM), TAO(TM), CIAO(TM), DAnCE>(TM), and CoSMIC(TM) (henceforth referred to
    -            as "DOC software") are copyrighted by Douglas C. Schmidt and his research
    -            group at Washington University, University of California, Irvine, and
    -            Vanderbilt University, Copyright (c) 1993-2009, all rights reserved. Since DOC
    -            software is open-source, freely available software, you are free to use,
    -            modify, copy, and distribute--perpetually and irrevocably--the DOC software
    -            source code and object code produced from the source, as well as copy and
    -            distribute modified versions of this software. You must, however, include this
    -            copyright statement along with any code built using DOC software that you
    -            release. No copyright statement needs to be provided if you just ship binary
    -            executables of your software products.
    -            You can use DOC software in commercial and/or binary software releases and are
    -            under no obligation to redistribute any of your source code that is built
    -            using DOC software. Note, however, that you may not misappropriate the DOC
    -            software code, such as copyrighting it yourself or claiming authorship of the
    -            DOC software code, in a way that will prevent DOC software from being
    -            distributed freely using an open-source development model. You needn't inform
    -            anyone that you're using DOC software in your software, though we encourage
    -            you to let us know so we can promote your project in the DOC software success
    -            stories.
    -
    -            The ACE, TAO, CIAO, DAnCE, and CoSMIC web sites are maintained by the DOC
    -            Group at the Institute for Software Integrated Systems (ISIS) and the Center
    -            for Distributed Object Computing of Washington University, St. Louis for the
    -            development of open-source software as part of the open-source software
    -            community. Submissions are provided by the submitter ``as is'' with no
    -            warranties whatsoever, including any warranty of merchantability,
    -            noninfringement of third party intellectual property, or fitness for any
    -            particular purpose. In no event shall the submitter be liable for any direct,
    -            indirect, special, exemplary, punitive, or consequential damages, including
    -            without limitation, lost profits, even if advised of the possibility of such
    -            damages. Likewise, DOC software is provided as is with no warranties of any
    -            kind, including the warranties of design, merchantability, and fitness for a
    -            particular purpose, noninfringement, or arising from a course of dealing,
    -            usage or trade practice. Washington University, UC Irvine, Vanderbilt
    -            University, their employees, and students shall have no liability with respect
    -            to the infringement of copyrights, trade secrets or any patents by DOC
    -            software or any part thereof. Moreover, in no event will Washington
    -            University, UC Irvine, or Vanderbilt University, their employees, or students
    -            be liable for any lost revenue or profits or other special, indirect and
    -            consequential damages.
    -
    -            DOC software is provided with no support and without any obligation on the
    -            part of Washington University, UC Irvine, Vanderbilt University, their
    -            employees, or students to assist in its use, correction, modification, or
    -            enhancement. A number of companies around the world provide commercial support
    -            for DOC software, however. DOC software is Y2K-compliant, as long as the
    -            underlying OS platform is Y2K-compliant. Likewise, DOC software is compliant
    -            with the new US daylight savings rule passed by Congress as "The Energy Policy
    -            Act of 2005," which established new daylight savings times (DST) rules for the
    -            United States that expand DST as of March 2007. Since DOC software obtains
    -            time/date and calendaring information from operating systems users will not be
    -            affected by the new DST rules as long as they upgrade their operating systems
    -            accordingly.
    -
    -            The names ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), CoSMIC(TM), Washington
    -            University, UC Irvine, and Vanderbilt University, may not be used to endorse
    -            or promote products or services derived from this source without express
    -            written permission from Washington University, UC Irvine, or Vanderbilt
    -            University. This license grants no permission to call products or services
    -            derived from this source ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), or CoSMIC(TM),
    -            nor does it grant permission for the name Washington University, UC Irvine, or
    -            Vanderbilt University to appear in their names.
    -            -------------------------------------------------------------------------------
    -            Files:
    -            system_wrappers/source/set_thread_name_win.h
    -
    -            Source:
    -            http://msdn.microsoft.com/en-us/cc300389.aspx#P
    -
    -            License:
    -            This license governs use of code marked as “sample” or “example” available on
    -            this web site without a license agreement, as provided under the section above
    -            titled “NOTICE SPECIFIC TO SOFTWARE AVAILABLE ON THIS WEB SITE.” If you use
    -            such code (the “software”), you accept this license. If you do not accept the
    -            license, do not use the software.
    -
    -            1. Definitions
    -
    -            The terms “reproduce,” “reproduction,” “derivative works,” and “distribution”
    -            have the same meaning here as under U.S. copyright law.
    -
    -            A “contribution” is the original software, or any additions or changes to the
    -            software.
    -
    -            A “contributor” is any person that distributes its contribution under this
    -            license.
    -
    -            “Licensed patents” are a contributor’s patent claims that read directly on its
    -            contribution.
    -
    -            2. Grant of Rights
    -
    -            (A) Copyright Grant - Subject to the terms of this license, including the
    -            license conditions and limitations in section 3, each contributor grants you a
    -            non-exclusive, worldwide, royalty-free copyright license to reproduce its
    -            contribution, prepare derivative works of its contribution, and distribute its
    -            contribution or any derivative works that you create.
    -
    -            (B) Patent Grant - Subject to the terms of this license, including the license
    -            conditions and limitations in section 3, each contributor grants you a
    -            non-exclusive, worldwide, royalty-free license under its licensed patents to
    -            make, have made, use, sell, offer for sale, import, and/or otherwise dispose
    -            of its contribution in the software or derivative works of the contribution in
    -            the software.
    -
    -            3. Conditions and Limitations
    -
    -            (A) No Trademark License- This license does not grant you rights to use any
    -            contributors’ name, logo, or trademarks.
    -
    -            (B) If you bring a patent claim against any contributor over patents that you
    -            claim are infringed by the software, your patent license from such contributor
    -            to the software ends automatically.
    -
    -            (C) If you distribute any portion of the software, you must retain all
    -            copyright, patent, trademark, and attribution notices that are present in the
    -            software.
    -
    -            (D) If you distribute any portion of the software in source code form, you may
    -            do so only under this license by including a complete copy of this license
    -            with your distribution. If you distribute any portion of the software in
    -            compiled or object code form, you may only do so under a license that complies
    -            with this license.
    -
    -            (E) The software is licensed “as-is.” You bear the risk of using it. The
    -            contributors give no express warranties, guarantees or conditions. You may
    -            have additional consumer rights under your local laws which this license
    -            cannot change. To the extent permitted under your local laws, the contributors
    -            exclude the implied warranties of merchantability, fitness for a particular
    -            purpose and non-infringement.
    -
    -            (F) Platform Limitation - The licenses granted in sections 2(A) and 2(B)
    -            extend only to the software or derivative works that you create that run on a
    -            Microsoft Windows operating system product.
    -            -------------------------------------------------------------------------------
    -            Files:
    -            system_wrappers/source/spreadsortlib/constants.hpp
    -            system_wrappers/source/spreadsortlib/spreadsort.hpp
    -
    -            License:
    -            /*Boost Software License - Version 1.0 - August 17th, 2003
    -
    -            Permission is hereby granted, free of charge, to any person or organization
    -            obtaining a copy of the software and accompanying documentation covered by
    -            this license (the "Software") to use, reproduce, display, distribute,
    -            execute, and transmit the Software, and to prepare derivative works of the
    -            Software, and to permit third-parties to whom the Software is furnished to
    -            do so, all subject to the following:
    -
    -            The copyright notices in the Software and this entire statement, including
    -            the above license grant, this restriction and the following disclaimer,
    -            must be included in all copies of the Software, in whole or in part, and
    -            all derivative works of the Software, unless such copies or derivative
    -            works are solely in the form of machine-executable object code generated by
    -            a source language processor.
    -
    -            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    -            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    -            FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
    -            SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
    -            FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
    -            ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
    -            DEALINGS IN THE SOFTWARE.*/
    -
    -        
    -

    boringssl

    -
    -            BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
    -            licensing. Files that are completely new have a Google copyright and an ISC
    -            license. This license is reproduced at the bottom of this file.
    -
    -            Contributors to BoringSSL are required to follow the CLA rules for Chromium:
    -            https://cla.developers.google.com/clas
    -
    -            Some files from Intel are under yet another license, which is also included
    -            underneath.
    -
    -            The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the
    -            OpenSSL License and the original SSLeay license apply to the toolkit. See below
    -            for the actual license texts. Actually both licenses are BSD-style Open Source
    -            licenses. In case of any license issues related to OpenSSL please contact
    -            openssl-core@openssl.org.
    -
    -            The following are Google-internal bug numbers where explicit permission from
    -            some authors is recorded for use of their work. (This is purely for our own
    -            record keeping.)
    -            27287199
    -            27287880
    -            27287883
    -
    -            OpenSSL License
    -            ---------------
    -
    -            /* ====================================================================
    -            * Copyright (c) 1998-2011 The OpenSSL Project.  All rights reserved.
    -            *
    -            * Redistribution and use in source and binary forms, with or without
    -            * modification, are permitted provided that the following conditions
    -            * are met:
    -            *
    -            * 1. Redistributions of source code must retain the above copyright
    -            *    notice, this list of conditions and the following disclaimer.
    -            *
    -            * 2. Redistributions in binary form must reproduce the above copyright
    -            *    notice, this list of conditions and the following disclaimer in
    -            *    the documentation and/or other materials provided with the
    -            *    distribution.
    -            *
    -            * 3. All advertising materials mentioning features or use of this
    -            *    software must display the following acknowledgment:
    -            *    "This product includes software developed by the OpenSSL Project
    -            *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
    -            *
    -            * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
    -            *    endorse or promote products derived from this software without
    -            *    prior written permission. For written permission, please contact
    -            *    openssl-core@openssl.org.
    -            *
    -            * 5. Products derived from this software may not be called "OpenSSL"
    -            *    nor may "OpenSSL" appear in their names without prior written
    -            *    permission of the OpenSSL Project.
    -            *
    -            * 6. Redistributions of any form whatsoever must retain the following
    -            *    acknowledgment:
    -            *    "This product includes software developed by the OpenSSL Project
    -            *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
    -            *
    -            * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
    -            * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -            * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    -            * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
    -            * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    -            * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    -            * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    -            * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    -            * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    -            * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -            * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    -            * OF THE POSSIBILITY OF SUCH DAMAGE.
    -            * ====================================================================
    -            *
    -            * This product includes cryptographic software written by Eric Young
    -            * (eay@cryptsoft.com).  This product includes software written by Tim
    -            * Hudson (tjh@cryptsoft.com).
    -            *
    -            */
    -
    -            Original SSLeay License
    -            -----------------------
    -
    -            /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
    -            * All rights reserved.
    -            *
    -            * This package is an SSL implementation written
    -            * by Eric Young (eay@cryptsoft.com).
    -            * The implementation was written so as to conform with Netscapes SSL.
    -            *
    -            * This library is free for commercial and non-commercial use as long as
    -            * the following conditions are aheared to.  The following conditions
    -            * apply to all code found in this distribution, be it the RC4, RSA,
    -            * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
    -            * included with this distribution is covered by the same copyright terms
    -            * except that the holder is Tim Hudson (tjh@cryptsoft.com).
    -            *
    -            * Copyright remains Eric Young's, and as such any Copyright notices in
    -            * the code are not to be removed.
    -            * If this package is used in a product, Eric Young should be given attribution
    -            * as the author of the parts of the library used.
    -            * This can be in the form of a textual message at program startup or
    -            * in documentation (online or textual) provided with the package.
    -            *
    -            * Redistribution and use in source and binary forms, with or without
    -            * modification, are permitted provided that the following conditions
    -            * are met:
    -            * 1. Redistributions of source code must retain the copyright
    -            *    notice, this list of conditions and the following disclaimer.
    -            * 2. Redistributions in binary form must reproduce the above copyright
    -            *    notice, this list of conditions and the following disclaimer in the
    -            *    documentation and/or other materials provided with the distribution.
    -            * 3. All advertising materials mentioning features or use of this software
    -            *    must display the following acknowledgement:
    -            *    "This product includes cryptographic software written by
    -            *     Eric Young (eay@cryptsoft.com)"
    -            *    The word 'cryptographic' can be left out if the rouines from the library
    -            *    being used are not cryptographic related :-).
    -            * 4. If you include any Windows specific code (or a derivative thereof) from
    -            *    the apps directory (application code) you must include an acknowledgement:
    -            *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
    -            *
    -            * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
    -            * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -            * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -            * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
    -            * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    -            * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    -            * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    -            * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    -            * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    -            * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    -            * SUCH DAMAGE.
    -            *
    -            * The licence and distribution terms for any publically available version or
    -            * derivative of this code cannot be changed.  i.e. this code cannot simply be
    -            * copied and put under another distribution licence
    -            * [including the GNU Public Licence.]
    -            */
    -
    -
    -            ISC license used for completely new code in BoringSSL:
    -
    -            /* Copyright (c) 2015, Google Inc.
    -            *
    -            * Permission to use, copy, modify, and/or distribute this software for any
    -            * purpose with or without fee is hereby granted, provided that the above
    -            * copyright notice and this permission notice appear in all copies.
    -            *
    -            * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    -            * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    -            * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
    -            * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    -            * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
    -            * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
    -            * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
    -
    -
    -            Some files from Intel carry the following license:
    -
    -            # Copyright (c) 2012, Intel Corporation
    -            #
    -            # All rights reserved.
    -            #
    -            # Redistribution and use in source and binary forms, with or without
    -            # modification, are permitted provided that the following conditions are
    -            # met:
    -            #
    -            # *  Redistributions of source code must retain the above copyright
    -            #    notice, this list of conditions and the following disclaimer.
    -            #
    -            # *  Redistributions in binary form must reproduce the above copyright
    -            #    notice, this list of conditions and the following disclaimer in the
    -            #    documentation and/or other materials provided with the
    -            #    distribution.
    -            #
    -            # *  Neither the name of the Intel Corporation nor the names of its
    -            #    contributors may be used to endorse or promote products derived from
    -            #    this software without specific prior written permission.
    -            #
    -            #
    -            # THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY
    -            # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -            # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    -            # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR
    -            # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    -            # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    -            # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    -            # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    -            # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    -            # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    -            # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    -
    -        
    -

    jsoncpp

    -
    -            The JsonCpp library's source code, including accompanying documentation,
    -            tests and demonstration applications, are licensed under the following
    -            conditions...
    -
    -            The author (Baptiste Lepilleur) explicitly disclaims copyright in all
    -            jurisdictions which recognize such a disclaimer. In such jurisdictions,
    -            this software is released into the Public Domain.
    -
    -            In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
    -            2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
    -            released under the terms of the MIT License (see below).
    -
    -            In jurisdictions which recognize Public Domain property, the user of this
    -            software may choose to accept it either as 1) Public Domain, 2) under the
    -            conditions of the MIT License (see below), or 3) under the terms of dual
    -            Public Domain/MIT License conditions described here, as they choose.
    -
    -            The MIT License is about as close to Public Domain as a license can get, and is
    -            described in clear, concise terms at:
    -
    -            http://en.wikipedia.org/wiki/MIT_License
    -
    -            The full text of the MIT License follows:
    -
    -            ========================================================================
    -            Copyright (c) 2007-2010 Baptiste Lepilleur
    -
    -            Permission is hereby granted, free of charge, to any person
    -            obtaining a copy of this software and associated documentation
    -            files (the "Software"), to deal in the Software without
    -            restriction, including without limitation the rights to use, copy,
    -            modify, merge, publish, distribute, sublicense, and/or sell copies
    -            of the Software, and to permit persons to whom the Software is
    -            furnished to do so, subject to the following conditions:
    -
    -            The above copyright notice and this permission notice shall be
    -            included in all copies or substantial portions of the Software.
    -
    -            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    -            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    -            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
    -            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
    -            BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
    -            ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
    -            CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    -            SOFTWARE.
    -            ========================================================================
    -            (END LICENSE TEXT)
    -
    -            The MIT license is compatible with both the GPL and commercial
    -            software, affording one all of the rights of Public Domain with the
    -            minor nuisance of being required to keep the above copyright notice
    -            and license text in the source code. Note also that by accepting the
    -            Public Domain "license" you can re-license your copy using whatever
    -            license you like.
    -
    -        
    -

    opus

    -
    -            Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic,
    -            Jean-Marc Valin, Timothy B. Terriberry,
    -            CSIRO, Gregory Maxwell, Mark Borgerding,
    -            Erik de Castro Lopo
    -
    -            Redistribution and use in source and binary forms, with or without
    -            modification, are permitted provided that the following conditions
    -            are met:
    -
    -            - Redistributions of source code must retain the above copyright
    -            notice, this list of conditions and the following disclaimer.
    -
    -            - Redistributions in binary form must reproduce the above copyright
    -            notice, this list of conditions and the following disclaimer in the
    -            documentation and/or other materials provided with the distribution.
    -
    -            - Neither the name of Internet Society, IETF or IETF Trust, nor the
    -            names of specific contributors, may be used to endorse or promote
    -            products derived from this software without specific prior written
    -            permission.
    -
    -            THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    -            ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    -            LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    -            A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
    -            OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
    -            EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    -            PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
    -            PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
    -            LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
    -            NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
    -            SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    -
    -            Opus is subject to the royalty-free patent licenses which are
    -            specified at:
    -
    -            Xiph.Org Foundation:
    -            https://datatracker.ietf.org/ipr/1524/
    -
    -            Microsoft Corporation:
    -            https://datatracker.ietf.org/ipr/1914/
    -
    -            Broadcom Corporation:
    -            https://datatracker.ietf.org/ipr/1526/
    -
    -        
    -

    protobuf_lite

    -
    -            This license applies to all parts of Protocol Buffers except the following:
    -
    -            - Atomicops support for generic gcc, located in
    -            src/google/protobuf/stubs/atomicops_internals_generic_gcc.h.
    -            This file is copyrighted by Red Hat Inc.
    -
    -            - Atomicops support for AIX/POWER, located in
    -            src/google/protobuf/stubs/atomicops_internals_power.h.
    -            This file is copyrighted by Bloomberg Finance LP.
    -
    -            Copyright 2014, Google Inc.  All rights reserved.
    -
    -            Redistribution and use in source and binary forms, with or without
    -            modification, are permitted provided that the following conditions are
    -            met:
    -
    -            * Redistributions of source code must retain the above copyright
    -            notice, this list of conditions and the following disclaimer.
    -            * Redistributions in binary form must reproduce the above
    -            copyright notice, this list of conditions and the following disclaimer
    -            in the documentation and/or other materials provided with the
    -            distribution.
    -            * Neither the name of Google Inc. nor the names of its
    -            contributors may be used to endorse or promote products derived from
    -            this software without specific prior written permission.
    -
    -            THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    -            "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    -            LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    -            A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    -            OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    -            SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    -            LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    -            DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    -            THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    -            (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    -            OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    -
    -            Code generated by the Protocol Buffer compiler is owned by the owner
    -            of the input file used when generating it.  This code is not
    -            standalone and requires a support library to be linked with it.  This
    -            support library is itself covered by the above license.
    -
    -        
    -

    srtp

    -
    -            /*
    -            *
    -            * Copyright (c) 2001-2006 Cisco Systems, Inc.
    -            * All rights reserved.
    -            *
    -            * Redistribution and use in source and binary forms, with or without
    -            * modification, are permitted provided that the following conditions
    -            * are met:
    -            *
    -            *   Redistributions of source code must retain the above copyright
    -            *   notice, this list of conditions and the following disclaimer.
    -            *
    -            *   Redistributions in binary form must reproduce the above
    -            *   copyright notice, this list of conditions and the following
    -            *   disclaimer in the documentation and/or other materials provided
    -            *   with the distribution.
    -            *
    -            *   Neither the name of the Cisco Systems, Inc. nor the names of its
    -            *   contributors may be used to endorse or promote products derived
    -            *   from this software without specific prior written permission.
    -            *
    -            * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    -            * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    -            * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
    -            * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
    -            * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
    -            * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    -            * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
    -            * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    -            * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    -            * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    -            * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
    -            * OF THE POSSIBILITY OF SUCH DAMAGE.
    -            *
    -            */
    -
    -        
    -

    usrsctplib

    -
    -            (Copied from the COPYRIGHT file of
    -            https://code.google.com/p/sctp-refimpl/source/browse/trunk/COPYRIGHT)
    -            --------------------------------------------------------------------------------
    -
    -            Copyright (c) 2001, 2002 Cisco Systems, Inc.
    -            Copyright (c) 2002-12 Randall R. Stewart
    -            Copyright (c) 2002-12 Michael Tuexen
    -            All rights reserved.
    -
    -            Redistribution and use in source and binary forms, with or without
    -            modification, are permitted provided that the following conditions
    -            are met:
    -
    -            1. Redistributions of source code must retain the above copyright
    -            notice, this list of conditions and the following disclaimer.
    -            2. Redistributions in binary form must reproduce the above copyright
    -            notice, this list of conditions and the following disclaimer in the
    -            documentation and/or other materials provided with the distribution.
    -
    -            THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
    -            ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    -            IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    -            ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
    -            FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
    -            DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
    -            OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    -            HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
    -            LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
    -            OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    -            SUCH DAMAGE.
    -
    -        
    -

    vpx

    -
    -            Copyright (c) 2010, The WebM Project authors. All rights reserved.
    -
    -            Redistribution and use in source and binary forms, with or without
    -            modification, are permitted provided that the following conditions are
    -            met:
    -            
    -            * Redistributions of source code must retain the above copyright
    -            notice, this list of conditions and the following disclaimer.
    -            
    -            * Redistributions in binary form must reproduce the above copyright
    -            notice, this list of conditions and the following disclaimer in
    -            the documentation and/or other materials provided with the
    -            distribution.
    -            
    -            * Neither the name of Google, nor the WebM Project, nor the names
    -            of its contributors may be used to endorse or promote products
    -            derived from this software without specific prior written
    -            permission.
    -            
    -            THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    -            "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    -            LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    -            A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    -            HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    -            SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    -            LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    -            DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    -            THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    -            (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    -            OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    -            
    -            
    -        
    -

    yuv

    -
    -            Copyright 2011 The LibYuv Project Authors. All rights reserved.
    -            
    -            Redistribution and use in source and binary forms, with or without
    -            modification, are permitted provided that the following conditions are
    -            met:
    -            
    -            * Redistributions of source code must retain the above copyright
    -            notice, this list of conditions and the following disclaimer.
    -            
    -            * Redistributions in binary form must reproduce the above copyright
    -            notice, this list of conditions and the following disclaimer in
    -            the documentation and/or other materials provided with the
    -            distribution.
    -            
    -            * Neither the name of Google nor the names of its contributors may
    -            be used to endorse or promote products derived from this software
    -            without specific prior written permission.
    -            
    -            THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    -            "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    -            LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    -            A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    -            HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    -            SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    -            LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    -            DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    -            THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    -            (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    -            OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    -        
    -

    + Jitsi Meet iOS SDK (Jitsi Meet iOS SDK binaries) +

    It is composed of 2 frameworks:

    +
      +
    • + JitsiMeet.framework +

      Copyright 2018-present 8x8, Inc. +

      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: +

      https://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. +

      +
    • +
    • + WebRTC.framework +

      webrtc

      +
      +                    Copyright (c) 2011, The WebRTC project authors. All rights reserved.
      +                    
      +                    Redistribution and use in source and binary forms, with or without
      +                    modification, are permitted provided that the following conditions are
      +                    met:
      +                    
      +                    * Redistributions of source code must retain the above copyright
      +                    notice, this list of conditions and the following disclaimer.
      +                    
      +                    * Redistributions in binary form must reproduce the above copyright
      +                    notice, this list of conditions and the following disclaimer in
      +                    the documentation and/or other materials provided with the
      +                    distribution.
      +                    
      +                    * Neither the name of Google nor the names of its contributors may
      +                    be used to endorse or promote products derived from this software
      +                    without specific prior written permission.
      +                    
      +                    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      +                    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      +                    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      +                    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      +                    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      +                    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      +                    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      +                    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      +                    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      +                    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      +                    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      +                    
      +                    This source tree contains third party source code which is governed by third
      +                    party licenses. Paths to the files and associated licenses are collected here.
      +                    
      +                    Files governed by third party licenses:
      +                    common_audio/fft4g.c
      +                    common_audio/signal_processing/spl_sqrt_floor.c
      +                    common_audio/signal_processing/spl_sqrt_floor_arm.S
      +                    modules/audio_coding/codecs/g711/main/source/g711.c
      +                    modules/audio_coding/codecs/g711/main/source/g711.h
      +                    modules/audio_coding/codecs/g722/main/source/g722_decode.c
      +                    modules/audio_coding/codecs/g722/main/source/g722_enc_dec.h
      +                    modules/audio_coding/codecs/g722/main/source/g722_encode.c
      +                    modules/audio_coding/codecs/isac/main/source/fft.c
      +                    modules/audio_device/mac/portaudio/pa_memorybarrier.h
      +                    modules/audio_device/mac/portaudio/pa_ringbuffer.c
      +                    modules/audio_device/mac/portaudio/pa_ringbuffer.h
      +                    modules/audio_processing/aec/aec_rdft.c
      +                    system_wrappers/source/condition_variable_event_win.cc
      +                    system_wrappers/source/set_thread_name_win.h
      +                    system_wrappers/source/spreadsortlib/constants.hpp
      +                    system_wrappers/source/spreadsortlib/spreadsort.hpp
      +                    
      +                    Individual licenses for each file:
      +                    -------------------------------------------------------------------------------
      +                    Files:
      +                    common_audio/signal_processing/spl_sqrt_floor.c
      +                    common_audio/signal_processing/spl_sqrt_floor_arm.S
      +                    
      +                    License:
      +                    /*
      +                    * Written by Wilco Dijkstra, 1996. The following email exchange establishes the
      +                    * license.
      +                    *
      +                    * From: Wilco Dijkstra <Wilco.Dijkstra@ntlworld.com>
      +                    * Date: Fri, Jun 24, 2011 at 3:20 AM
      +                    * Subject: Re: sqrt routine
      +                    * To: Kevin Ma <kma@google.com>
      +                    * Hi Kevin,
      +                    * Thanks for asking. Those routines are public domain (originally posted to
      +                    * comp.sys.arm a long time ago), so you can use them freely for any purpose.
      +                    * Cheers,
      +                    * Wilco
      +                    *
      +                    * ----- Original Message -----
      +                    * From: "Kevin Ma" <kma@google.com>
      +                    * To: <Wilco.Dijkstra@ntlworld.com>
      +                    * Sent: Thursday, June 23, 2011 11:44 PM
      +                    * Subject: Fwd: sqrt routine
      +                    * Hi Wilco,
      +                    * I saw your sqrt routine from several web sites, including
      +                    * http://www.finesse.demon.co.uk/steven/sqrt.html.
      +                    * Just wonder if there's any copyright information with your Successive
      +                    * approximation routines, or if I can freely use it for any purpose.
      +                    * Thanks.
      +                    * Kevin
      +                    */
      +                    -------------------------------------------------------------------------------
      +                    Files:
      +                    modules/audio_coding/codecs/g711/main/source/g711.c
      +                    modules/audio_coding/codecs/g711/main/source/g711.h
      +                    
      +                    License:
      +                    /*
      +                    * SpanDSP - a series of DSP components for telephony
      +                    *
      +                    * g711.h - In line A-law and u-law conversion routines
      +                    *
      +                    * Written by Steve Underwood <steveu@coppice.org>
      +                    *
      +                    * Copyright (C) 2001 Steve Underwood
      +                    *
      +                    *  Despite my general liking of the GPL, I place this code in the
      +                    *  public domain for the benefit of all mankind - even the slimy
      +                    *  ones who might try to proprietize my work and use it to my
      +                    *  detriment.
      +                    */
      +                    -------------------------------------------------------------------------------
      +                    Files:
      +                    modules/audio_coding/codecs/g722/main/source/g722_decode.c
      +                    modules/audio_coding/codecs/g722/main/source/g722_enc_dec.h
      +                    modules/audio_coding/codecs/g722/main/source/g722_encode.c
      +                    
      +                    License:
      +                    /*
      +                    * SpanDSP - a series of DSP components for telephony
      +                    *
      +                    * g722_decode.c - The ITU G.722 codec, decode part.
      +                    *
      +                    * Written by Steve Underwood <steveu@coppice.org>
      +                    *
      +                    * Copyright (C) 2005 Steve Underwood
      +                    *
      +                    *  Despite my general liking of the GPL, I place my own contributions
      +                    *  to this code in the public domain for the benefit of all mankind -
      +                    *  even the slimy ones who might try to proprietize my work and use it
      +                    *  to my detriment.
      +                    *
      +                    * Based in part on a single channel G.722 codec which is:
      +                    *
      +                    * Copyright (c) CMU 1993
      +                    * Computer Science, Speech Group
      +                    * Chengxiang Lu and Alex Hauptmann
      +                    */
      +                    -------------------------------------------------------------------------------
      +                    Files:
      +                    modules/audio_coding/codecs/isac/main/source/fft.c
      +                    
      +                    License:
      +                    /*
      +                    * Copyright(c)1995,97 Mark Olesen <olesen@me.QueensU.CA>
      +                    *    Queen's Univ at Kingston (Canada)
      +                    *
      +                    * Permission to use, copy, modify, and distribute this software for
      +                    * any purpose without fee is hereby granted, provided that this
      +                    * entire notice is included in all copies of any software which is
      +                    * or includes a copy or modification of this software and in all
      +                    * copies of the supporting documentation for such software.
      +                    *
      +                    * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
      +                    * IMPLIED WARRANTY.  IN PARTICULAR, NEITHER THE AUTHOR NOR QUEEN'S
      +                    * UNIVERSITY AT KINGSTON MAKES ANY REPRESENTATION OR WARRANTY OF ANY
      +                    * KIND CONCERNING THE MERCHANTABILITY OF THIS SOFTWARE OR ITS
      +                    * FITNESS FOR ANY PARTICULAR PURPOSE.
      +                    *
      +                    * All of which is to say that you can do what you like with this
      +                    * source code provided you don't try to sell it as your own and you
      +                    * include an unaltered copy of this message (including the
      +                    * copyright).
      +                    *
      +                    * It is also implicitly understood that bug fixes and improvements
      +                    * should make their way back to the general Internet community so
      +                    * that everyone benefits.
      +                    */
      +                    -------------------------------------------------------------------------------
      +                    Files:
      +                    modules/audio_device/mac/portaudio/pa_memorybarrier.h
      +                    modules/audio_device/mac/portaudio/pa_ringbuffer.c
      +                    modules/audio_device/mac/portaudio/pa_ringbuffer.h
      +                    
      +                    License:
      +                    /*
      +                    * $Id: pa_memorybarrier.h 1240 2007-07-17 13:05:07Z bjornroche $
      +                    * Portable Audio I/O Library
      +                    * Memory barrier utilities
      +                    *
      +                    * Author: Bjorn Roche, XO Audio, LLC
      +                    *
      +                    * This program uses the PortAudio Portable Audio Library.
      +                    * For more information see: http://www.portaudio.com
      +                    * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
      +                    *
      +                    * Permission is hereby granted, free of charge, to any person obtaining
      +                    * a copy of this software and associated documentation files
      +                    * (the "Software"), to deal in the Software without restriction,
      +                    * including without limitation the rights to use, copy, modify, merge,
      +                    * publish, distribute, sublicense, and/or sell copies of the Software,
      +                    * and to permit persons to whom the Software is furnished to do so,
      +                    * subject to the following conditions:
      +                    *
      +                    * The above copyright notice and this permission notice shall be
      +                    * included in all copies or substantial portions of the Software.
      +                    *
      +                    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      +                    * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      +                    * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      +                    * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
      +                    * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
      +                    * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
      +                    * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      +                    */
      +                    
      +                    /*
      +                    * The text above constitutes the entire PortAudio license; however,
      +                    * the PortAudio community also makes the following non-binding requests:
      +                    *
      +                    * Any person wishing to distribute modifications to the Software is
      +                    * requested to send the modifications to the original developer so that
      +                    * they can be incorporated into the canonical version. It is also
      +                    * requested that these non-binding requests be included along with the
      +                    * license above.
      +                    */
      +                    
      +                    /*
      +                    * $Id: pa_ringbuffer.c 1421 2009-11-18 16:09:05Z bjornroche $
      +                    * Portable Audio I/O Library
      +                    * Ring Buffer utility.
      +                    *
      +                    * Author: Phil Burk, http://www.softsynth.com
      +                    * modified for SMP safety on Mac OS X by Bjorn Roche
      +                    * modified for SMP safety on Linux by Leland Lucius
      +                    * also, allowed for const where possible
      +                    * modified for multiple-byte-sized data elements by Sven Fischer
      +                    *
      +                    * Note that this is safe only for a single-thread reader and a
      +                    * single-thread writer.
      +                    *
      +                    * This program uses the PortAudio Portable Audio Library.
      +                    * For more information see: http://www.portaudio.com
      +                    * Copyright (c) 1999-2000 Ross Bencina and Phil Burk
      +                    *
      +                    * Permission is hereby granted, free of charge, to any person obtaining
      +                    * a copy of this software and associated documentation files
      +                    * (the "Software"), to deal in the Software without restriction,
      +                    * including without limitation the rights to use, copy, modify, merge,
      +                    * publish, distribute, sublicense, and/or sell copies of the Software,
      +                    * and to permit persons to whom the Software is furnished to do so,
      +                    * subject to the following conditions:
      +                    *
      +                    * The above copyright notice and this permission notice shall be
      +                    * included in all copies or substantial portions of the Software.
      +                    *
      +                    * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      +                    * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      +                    * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
      +                    * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
      +                    * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
      +                    * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
      +                    * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      +                    */
      +                    
      +                    /*
      +                    * The text above constitutes the entire PortAudio license; however,
      +                    * the PortAudio community also makes the following non-binding requests:
      +                    *
      +                    * Any person wishing to distribute modifications to the Software is
      +                    * requested to send the modifications to the original developer so that
      +                    * they can be incorporated into the canonical version. It is also
      +                    * requested that these non-binding requests be included along with the
      +                    * license above.
      +                    */
      +                    -------------------------------------------------------------------------------
      +                    Files:
      +                    common_audio/fft4g.c
      +                    modules/audio_processing/aec/aec_rdft.c
      +                    
      +                    License:
      +                    /*
      +                    * http://www.kurims.kyoto-u.ac.jp/~ooura/fft.html
      +                    * Copyright Takuya OOURA, 1996-2001
      +                    *
      +                    * You may use, copy, modify and distribute this code for any purpose (include
      +                    * commercial use) and without fee. Please refer to this package when you modify
      +                    * this code.
      +                    */
      +                    -------------------------------------------------------------------------------
      +                    Files:
      +                    system_wrappers/source/condition_variable_event_win.cc
      +                    
      +                    Source:
      +                    http://www1.cse.wustl.edu/~schmidt/ACE-copying.html
      +                    
      +                    License:
      +                    Copyright and Licensing Information for ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM),
      +                    and CoSMIC(TM)
      +                    
      +                    ACE(TM), TAO(TM), CIAO(TM), DAnCE>(TM), and CoSMIC(TM) (henceforth referred to
      +                    as "DOC software") are copyrighted by Douglas C. Schmidt and his research
      +                    group at Washington University, University of California, Irvine, and
      +                    Vanderbilt University, Copyright (c) 1993-2009, all rights reserved. Since DOC
      +                    software is open-source, freely available software, you are free to use,
      +                    modify, copy, and distribute--perpetually and irrevocably--the DOC software
      +                    source code and object code produced from the source, as well as copy and
      +                    distribute modified versions of this software. You must, however, include this
      +                    copyright statement along with any code built using DOC software that you
      +                    release. No copyright statement needs to be provided if you just ship binary
      +                    executables of your software products.
      +                    You can use DOC software in commercial and/or binary software releases and are
      +                    under no obligation to redistribute any of your source code that is built
      +                    using DOC software. Note, however, that you may not misappropriate the DOC
      +                    software code, such as copyrighting it yourself or claiming authorship of the
      +                    DOC software code, in a way that will prevent DOC software from being
      +                    distributed freely using an open-source development model. You needn't inform
      +                    anyone that you're using DOC software in your software, though we encourage
      +                    you to let us know so we can promote your project in the DOC software success
      +                    stories.
      +                    
      +                    The ACE, TAO, CIAO, DAnCE, and CoSMIC web sites are maintained by the DOC
      +                    Group at the Institute for Software Integrated Systems (ISIS) and the Center
      +                    for Distributed Object Computing of Washington University, St. Louis for the
      +                    development of open-source software as part of the open-source software
      +                    community. Submissions are provided by the submitter ``as is'' with no
      +                    warranties whatsoever, including any warranty of merchantability,
      +                    noninfringement of third party intellectual property, or fitness for any
      +                    particular purpose. In no event shall the submitter be liable for any direct,
      +                    indirect, special, exemplary, punitive, or consequential damages, including
      +                    without limitation, lost profits, even if advised of the possibility of such
      +                    damages. Likewise, DOC software is provided as is with no warranties of any
      +                    kind, including the warranties of design, merchantability, and fitness for a
      +                    particular purpose, noninfringement, or arising from a course of dealing,
      +                    usage or trade practice. Washington University, UC Irvine, Vanderbilt
      +                    University, their employees, and students shall have no liability with respect
      +                    to the infringement of copyrights, trade secrets or any patents by DOC
      +                    software or any part thereof. Moreover, in no event will Washington
      +                    University, UC Irvine, or Vanderbilt University, their employees, or students
      +                    be liable for any lost revenue or profits or other special, indirect and
      +                    consequential damages.
      +                    
      +                    DOC software is provided with no support and without any obligation on the
      +                    part of Washington University, UC Irvine, Vanderbilt University, their
      +                    employees, or students to assist in its use, correction, modification, or
      +                    enhancement. A number of companies around the world provide commercial support
      +                    for DOC software, however. DOC software is Y2K-compliant, as long as the
      +                    underlying OS platform is Y2K-compliant. Likewise, DOC software is compliant
      +                    with the new US daylight savings rule passed by Congress as "The Energy Policy
      +                    Act of 2005," which established new daylight savings times (DST) rules for the
      +                    United States that expand DST as of March 2007. Since DOC software obtains
      +                    time/date and calendaring information from operating systems users will not be
      +                    affected by the new DST rules as long as they upgrade their operating systems
      +                    accordingly.
      +                    
      +                    The names ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), CoSMIC(TM), Washington
      +                    University, UC Irvine, and Vanderbilt University, may not be used to endorse
      +                    or promote products or services derived from this source without express
      +                    written permission from Washington University, UC Irvine, or Vanderbilt
      +                    University. This license grants no permission to call products or services
      +                    derived from this source ACE(TM), TAO(TM), CIAO(TM), DAnCE(TM), or CoSMIC(TM),
      +                    nor does it grant permission for the name Washington University, UC Irvine, or
      +                    Vanderbilt University to appear in their names.
      +                    -------------------------------------------------------------------------------
      +                    Files:
      +                    system_wrappers/source/set_thread_name_win.h
      +                    
      +                    Source:
      +                    http://msdn.microsoft.com/en-us/cc300389.aspx#P
      +                    
      +                    License:
      +                    This license governs use of code marked as “sample” or “example” available on
      +                    this web site without a license agreement, as provided under the section above
      +                    titled “NOTICE SPECIFIC TO SOFTWARE AVAILABLE ON THIS WEB SITE.” If you use
      +                    such code (the “software”), you accept this license. If you do not accept the
      +                    license, do not use the software.
      +                    
      +                    1. Definitions
      +                    
      +                    The terms “reproduce,” “reproduction,” “derivative works,” and “distribution”
      +                    have the same meaning here as under U.S. copyright law.
      +                    
      +                    A “contribution” is the original software, or any additions or changes to the
      +                    software.
      +                    
      +                    A “contributor” is any person that distributes its contribution under this
      +                    license.
      +                    
      +                    “Licensed patents” are a contributor’s patent claims that read directly on its
      +                    contribution.
      +                    
      +                    2. Grant of Rights
      +                    
      +                    (A) Copyright Grant - Subject to the terms of this license, including the
      +                    license conditions and limitations in section 3, each contributor grants you a
      +                    non-exclusive, worldwide, royalty-free copyright license to reproduce its
      +                    contribution, prepare derivative works of its contribution, and distribute its
      +                    contribution or any derivative works that you create.
      +                    
      +                    (B) Patent Grant - Subject to the terms of this license, including the license
      +                    conditions and limitations in section 3, each contributor grants you a
      +                    non-exclusive, worldwide, royalty-free license under its licensed patents to
      +                    make, have made, use, sell, offer for sale, import, and/or otherwise dispose
      +                    of its contribution in the software or derivative works of the contribution in
      +                    the software.
      +                    
      +                    3. Conditions and Limitations
      +                    
      +                    (A) No Trademark License- This license does not grant you rights to use any
      +                    contributors’ name, logo, or trademarks.
      +                    
      +                    (B) If you bring a patent claim against any contributor over patents that you
      +                    claim are infringed by the software, your patent license from such contributor
      +                    to the software ends automatically.
      +                    
      +                    (C) If you distribute any portion of the software, you must retain all
      +                    copyright, patent, trademark, and attribution notices that are present in the
      +                    software.
      +                    
      +                    (D) If you distribute any portion of the software in source code form, you may
      +                    do so only under this license by including a complete copy of this license
      +                    with your distribution. If you distribute any portion of the software in
      +                    compiled or object code form, you may only do so under a license that complies
      +                    with this license.
      +                    
      +                    (E) The software is licensed “as-is.” You bear the risk of using it. The
      +                    contributors give no express warranties, guarantees or conditions. You may
      +                    have additional consumer rights under your local laws which this license
      +                    cannot change. To the extent permitted under your local laws, the contributors
      +                    exclude the implied warranties of merchantability, fitness for a particular
      +                    purpose and non-infringement.
      +                    
      +                    (F) Platform Limitation - The licenses granted in sections 2(A) and 2(B)
      +                    extend only to the software or derivative works that you create that run on a
      +                    Microsoft Windows operating system product.
      +                    -------------------------------------------------------------------------------
      +                    Files:
      +                    system_wrappers/source/spreadsortlib/constants.hpp
      +                    system_wrappers/source/spreadsortlib/spreadsort.hpp
      +                    
      +                    License:
      +                    /*Boost Software License - Version 1.0 - August 17th, 2003
      +                    
      +                    Permission is hereby granted, free of charge, to any person or organization
      +                    obtaining a copy of the software and accompanying documentation covered by
      +                    this license (the "Software") to use, reproduce, display, distribute,
      +                    execute, and transmit the Software, and to prepare derivative works of the
      +                    Software, and to permit third-parties to whom the Software is furnished to
      +                    do so, all subject to the following:
      +                    
      +                    The copyright notices in the Software and this entire statement, including
      +                    the above license grant, this restriction and the following disclaimer,
      +                    must be included in all copies of the Software, in whole or in part, and
      +                    all derivative works of the Software, unless such copies or derivative
      +                    works are solely in the form of machine-executable object code generated by
      +                    a source language processor.
      +                    
      +                    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      +                    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      +                    FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
      +                    SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
      +                    FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
      +                    ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      +                    DEALINGS IN THE SOFTWARE.*/
      +                    
      +                
      +

      boringssl

      +
      +                    BoringSSL is a fork of OpenSSL. As such, large parts of it fall under OpenSSL
      +                    licensing. Files that are completely new have a Google copyright and an ISC
      +                    license. This license is reproduced at the bottom of this file.
      +                    
      +                    Contributors to BoringSSL are required to follow the CLA rules for Chromium:
      +                    https://cla.developers.google.com/clas
      +                    
      +                    Some files from Intel are under yet another license, which is also included
      +                    underneath.
      +                    
      +                    The OpenSSL toolkit stays under a dual license, i.e. both the conditions of the
      +                    OpenSSL License and the original SSLeay license apply to the toolkit. See below
      +                    for the actual license texts. Actually both licenses are BSD-style Open Source
      +                    licenses. In case of any license issues related to OpenSSL please contact
      +                    openssl-core@openssl.org.
      +                    
      +                    The following are Google-internal bug numbers where explicit permission from
      +                    some authors is recorded for use of their work. (This is purely for our own
      +                    record keeping.)
      +                    27287199
      +                    27287880
      +                    27287883
      +                    
      +                    OpenSSL License
      +                    ---------------
      +                    
      +                    /* ====================================================================
      +                    * Copyright (c) 1998-2011 The OpenSSL Project.  All rights reserved.
      +                    *
      +                    * Redistribution and use in source and binary forms, with or without
      +                    * modification, are permitted provided that the following conditions
      +                    * are met:
      +                    *
      +                    * 1. Redistributions of source code must retain the above copyright
      +                    *    notice, this list of conditions and the following disclaimer.
      +                    *
      +                    * 2. Redistributions in binary form must reproduce the above copyright
      +                    *    notice, this list of conditions and the following disclaimer in
      +                    *    the documentation and/or other materials provided with the
      +                    *    distribution.
      +                    *
      +                    * 3. All advertising materials mentioning features or use of this
      +                    *    software must display the following acknowledgment:
      +                    *    "This product includes software developed by the OpenSSL Project
      +                    *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
      +                    *
      +                    * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
      +                    *    endorse or promote products derived from this software without
      +                    *    prior written permission. For written permission, please contact
      +                    *    openssl-core@openssl.org.
      +                    *
      +                    * 5. Products derived from this software may not be called "OpenSSL"
      +                    *    nor may "OpenSSL" appear in their names without prior written
      +                    *    permission of the OpenSSL Project.
      +                    *
      +                    * 6. Redistributions of any form whatsoever must retain the following
      +                    *    acknowledgment:
      +                    *    "This product includes software developed by the OpenSSL Project
      +                    *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
      +                    *
      +                    * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
      +                    * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      +                    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      +                    * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
      +                    * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      +                    * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
      +                    * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
      +                    * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      +                    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      +                    * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      +                    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      +                    * OF THE POSSIBILITY OF SUCH DAMAGE.
      +                    * ====================================================================
      +                    *
      +                    * This product includes cryptographic software written by Eric Young
      +                    * (eay@cryptsoft.com).  This product includes software written by Tim
      +                    * Hudson (tjh@cryptsoft.com).
      +                    *
      +                    */
      +                    
      +                    Original SSLeay License
      +                    -----------------------
      +                    
      +                    /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
      +                    * All rights reserved.
      +                    *
      +                    * This package is an SSL implementation written
      +                    * by Eric Young (eay@cryptsoft.com).
      +                    * The implementation was written so as to conform with Netscapes SSL.
      +                    *
      +                    * This library is free for commercial and non-commercial use as long as
      +                    * the following conditions are aheared to.  The following conditions
      +                    * apply to all code found in this distribution, be it the RC4, RSA,
      +                    * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
      +                    * included with this distribution is covered by the same copyright terms
      +                    * except that the holder is Tim Hudson (tjh@cryptsoft.com).
      +                    *
      +                    * Copyright remains Eric Young's, and as such any Copyright notices in
      +                    * the code are not to be removed.
      +                    * If this package is used in a product, Eric Young should be given attribution
      +                    * as the author of the parts of the library used.
      +                    * This can be in the form of a textual message at program startup or
      +                    * in documentation (online or textual) provided with the package.
      +                    *
      +                    * Redistribution and use in source and binary forms, with or without
      +                    * modification, are permitted provided that the following conditions
      +                    * are met:
      +                    * 1. Redistributions of source code must retain the copyright
      +                    *    notice, this list of conditions and the following disclaimer.
      +                    * 2. Redistributions in binary form must reproduce the above copyright
      +                    *    notice, this list of conditions and the following disclaimer in the
      +                    *    documentation and/or other materials provided with the distribution.
      +                    * 3. All advertising materials mentioning features or use of this software
      +                    *    must display the following acknowledgement:
      +                    *    "This product includes cryptographic software written by
      +                    *     Eric Young (eay@cryptsoft.com)"
      +                    *    The word 'cryptographic' can be left out if the rouines from the library
      +                    *    being used are not cryptographic related :-).
      +                    * 4. If you include any Windows specific code (or a derivative thereof) from
      +                    *    the apps directory (application code) you must include an acknowledgement:
      +                    *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
      +                    *
      +                    * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
      +                    * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      +                    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      +                    * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
      +                    * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      +                    * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      +                    * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      +                    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      +                    * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      +                    * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      +                    * SUCH DAMAGE.
      +                    *
      +                    * The licence and distribution terms for any publically available version or
      +                    * derivative of this code cannot be changed.  i.e. this code cannot simply be
      +                    * copied and put under another distribution licence
      +                    * [including the GNU Public Licence.]
      +                    */
      +                    
      +                    
      +                    ISC license used for completely new code in BoringSSL:
      +                    
      +                    /* Copyright (c) 2015, Google Inc.
      +                    *
      +                    * Permission to use, copy, modify, and/or distribute this software for any
      +                    * purpose with or without fee is hereby granted, provided that the above
      +                    * copyright notice and this permission notice appear in all copies.
      +                    *
      +                    * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      +                    * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      +                    * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
      +                    * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      +                    * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
      +                    * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
      +                    * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
      +                    
      +                    
      +                    Some files from Intel carry the following license:
      +                    
      +                    # Copyright (c) 2012, Intel Corporation
      +                    #
      +                    # All rights reserved.
      +                    #
      +                    # Redistribution and use in source and binary forms, with or without
      +                    # modification, are permitted provided that the following conditions are
      +                    # met:
      +                    #
      +                    # *  Redistributions of source code must retain the above copyright
      +                    #    notice, this list of conditions and the following disclaimer.
      +                    #
      +                    # *  Redistributions in binary form must reproduce the above copyright
      +                    #    notice, this list of conditions and the following disclaimer in the
      +                    #    documentation and/or other materials provided with the
      +                    #    distribution.
      +                    #
      +                    # *  Neither the name of the Intel Corporation nor the names of its
      +                    #    contributors may be used to endorse or promote products derived from
      +                    #    this software without specific prior written permission.
      +                    #
      +                    #
      +                    # THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION ""AS IS"" AND ANY
      +                    # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      +                    # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
      +                    # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR
      +                    # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      +                    # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      +                    # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
      +                    # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
      +                    # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      +                    # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      +                    # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      +                    
      +                
      +

      jsoncpp

      +
      +                    The JsonCpp library's source code, including accompanying documentation,
      +                    tests and demonstration applications, are licensed under the following
      +                    conditions...
      +                    
      +                    The author (Baptiste Lepilleur) explicitly disclaims copyright in all
      +                    jurisdictions which recognize such a disclaimer. In such jurisdictions,
      +                    this software is released into the Public Domain.
      +                    
      +                    In jurisdictions which do not recognize Public Domain property (e.g. Germany as of
      +                    2010), this software is Copyright (c) 2007-2010 by Baptiste Lepilleur, and is
      +                    released under the terms of the MIT License (see below).
      +                    
      +                    In jurisdictions which recognize Public Domain property, the user of this
      +                    software may choose to accept it either as 1) Public Domain, 2) under the
      +                    conditions of the MIT License (see below), or 3) under the terms of dual
      +                    Public Domain/MIT License conditions described here, as they choose.
      +                    
      +                    The MIT License is about as close to Public Domain as a license can get, and is
      +                    described in clear, concise terms at:
      +                    
      +                    http://en.wikipedia.org/wiki/MIT_License
      +                    
      +                    The full text of the MIT License follows:
      +                    
      +                    ========================================================================
      +                    Copyright (c) 2007-2010 Baptiste Lepilleur
      +                    
      +                    Permission is hereby granted, free of charge, to any person
      +                    obtaining a copy of this software and associated documentation
      +                    files (the "Software"), to deal in the Software without
      +                    restriction, including without limitation the rights to use, copy,
      +                    modify, merge, publish, distribute, sublicense, and/or sell copies
      +                    of the Software, and to permit persons to whom the Software is
      +                    furnished to do so, subject to the following conditions:
      +                    
      +                    The above copyright notice and this permission notice shall be
      +                    included in all copies or substantial portions of the Software.
      +                    
      +                    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      +                    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      +                    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      +                    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
      +                    BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
      +                    ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
      +                    CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      +                    SOFTWARE.
      +                    ========================================================================
      +                    (END LICENSE TEXT)
      +                    
      +                    The MIT license is compatible with both the GPL and commercial
      +                    software, affording one all of the rights of Public Domain with the
      +                    minor nuisance of being required to keep the above copyright notice
      +                    and license text in the source code. Note also that by accepting the
      +                    Public Domain "license" you can re-license your copy using whatever
      +                    license you like.
      +                    
      +                
      +

      opus

      +
      +                    Copyright 2001-2011 Xiph.Org, Skype Limited, Octasic,
      +                    Jean-Marc Valin, Timothy B. Terriberry,
      +                    CSIRO, Gregory Maxwell, Mark Borgerding,
      +                    Erik de Castro Lopo
      +                    
      +                    Redistribution and use in source and binary forms, with or without
      +                    modification, are permitted provided that the following conditions
      +                    are met:
      +                    
      +                    - Redistributions of source code must retain the above copyright
      +                    notice, this list of conditions and the following disclaimer.
      +                    
      +                    - Redistributions in binary form must reproduce the above copyright
      +                    notice, this list of conditions and the following disclaimer in the
      +                    documentation and/or other materials provided with the distribution.
      +                    
      +                    - Neither the name of Internet Society, IETF or IETF Trust, nor the
      +                    names of specific contributors, may be used to endorse or promote
      +                    products derived from this software without specific prior written
      +                    permission.
      +                    
      +                    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      +                    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      +                    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      +                    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
      +                    OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
      +                    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
      +                    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
      +                    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
      +                    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
      +                    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
      +                    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      +                    
      +                    Opus is subject to the royalty-free patent licenses which are
      +                    specified at:
      +                    
      +                    Xiph.Org Foundation:
      +                    https://datatracker.ietf.org/ipr/1524/
      +                    
      +                    Microsoft Corporation:
      +                    https://datatracker.ietf.org/ipr/1914/
      +                    
      +                    Broadcom Corporation:
      +                    https://datatracker.ietf.org/ipr/1526/
      +                    
      +                
      +

      protobuf_lite

      +
      +                    This license applies to all parts of Protocol Buffers except the following:
      +                    
      +                    - Atomicops support for generic gcc, located in
      +                    src/google/protobuf/stubs/atomicops_internals_generic_gcc.h.
      +                    This file is copyrighted by Red Hat Inc.
      +                    
      +                    - Atomicops support for AIX/POWER, located in
      +                    src/google/protobuf/stubs/atomicops_internals_power.h.
      +                    This file is copyrighted by Bloomberg Finance LP.
      +                    
      +                    Copyright 2014, Google Inc.  All rights reserved.
      +                    
      +                    Redistribution and use in source and binary forms, with or without
      +                    modification, are permitted provided that the following conditions are
      +                    met:
      +                    
      +                    * Redistributions of source code must retain the above copyright
      +                    notice, this list of conditions and the following disclaimer.
      +                    * Redistributions in binary form must reproduce the above
      +                    copyright notice, this list of conditions and the following disclaimer
      +                    in the documentation and/or other materials provided with the
      +                    distribution.
      +                    * Neither the name of Google Inc. nor the names of its
      +                    contributors may be used to endorse or promote products derived from
      +                    this software without specific prior written permission.
      +                    
      +                    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      +                    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      +                    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      +                    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      +                    OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      +                    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      +                    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      +                    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      +                    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      +                    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      +                    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      +                    
      +                    Code generated by the Protocol Buffer compiler is owned by the owner
      +                    of the input file used when generating it.  This code is not
      +                    standalone and requires a support library to be linked with it.  This
      +                    support library is itself covered by the above license.
      +                    
      +                
      +

      srtp

      +
      +                    /*
      +                    *
      +                    * Copyright (c) 2001-2006 Cisco Systems, Inc.
      +                    * All rights reserved.
      +                    *
      +                    * Redistribution and use in source and binary forms, with or without
      +                    * modification, are permitted provided that the following conditions
      +                    * are met:
      +                    *
      +                    *   Redistributions of source code must retain the above copyright
      +                    *   notice, this list of conditions and the following disclaimer.
      +                    *
      +                    *   Redistributions in binary form must reproduce the above
      +                    *   copyright notice, this list of conditions and the following
      +                    *   disclaimer in the documentation and/or other materials provided
      +                    *   with the distribution.
      +                    *
      +                    *   Neither the name of the Cisco Systems, Inc. nor the names of its
      +                    *   contributors may be used to endorse or promote products derived
      +                    *   from this software without specific prior written permission.
      +                    *
      +                    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      +                    * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      +                    * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
      +                    * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
      +                    * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
      +                    * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
      +                    * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
      +                    * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      +                    * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
      +                    * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
      +                    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
      +                    * OF THE POSSIBILITY OF SUCH DAMAGE.
      +                    *
      +                    */
      +                    
      +                
      +

      usrsctplib

      +
      +                    (Copied from the COPYRIGHT file of
      +                    https://code.google.com/p/sctp-refimpl/source/browse/trunk/COPYRIGHT)
      +                    --------------------------------------------------------------------------------
      +                    
      +                    Copyright (c) 2001, 2002 Cisco Systems, Inc.
      +                    Copyright (c) 2002-12 Randall R. Stewart
      +                    Copyright (c) 2002-12 Michael Tuexen
      +                    All rights reserved.
      +                    
      +                    Redistribution and use in source and binary forms, with or without
      +                    modification, are permitted provided that the following conditions
      +                    are met:
      +                    
      +                    1. Redistributions of source code must retain the above copyright
      +                    notice, this list of conditions and the following disclaimer.
      +                    2. Redistributions in binary form must reproduce the above copyright
      +                    notice, this list of conditions and the following disclaimer in the
      +                    documentation and/or other materials provided with the distribution.
      +                    
      +                    THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
      +                    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
      +                    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
      +                    ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
      +                    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
      +                    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
      +                    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
      +                    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
      +                    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
      +                    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
      +                    SUCH DAMAGE.
      +                    
      +                
      +

      vpx

      +
      +                    Copyright (c) 2010, The WebM Project authors. All rights reserved.
      +                    
      +                    Redistribution and use in source and binary forms, with or without
      +                    modification, are permitted provided that the following conditions are
      +                    met:
      +                    
      +                    * Redistributions of source code must retain the above copyright
      +                    notice, this list of conditions and the following disclaimer.
      +                    
      +                    * Redistributions in binary form must reproduce the above copyright
      +                    notice, this list of conditions and the following disclaimer in
      +                    the documentation and/or other materials provided with the
      +                    distribution.
      +                    
      +                    * Neither the name of Google, nor the WebM Project, nor the names
      +                    of its contributors may be used to endorse or promote products
      +                    derived from this software without specific prior written
      +                    permission.
      +                    
      +                    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      +                    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      +                    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      +                    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      +                    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      +                    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      +                    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      +                    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      +                    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      +                    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      +                    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      +                    
      +                    
      +                
      +

      yuv

      +
      +                    Copyright 2011 The LibYuv Project Authors. All rights reserved.
      +                    
      +                    Redistribution and use in source and binary forms, with or without
      +                    modification, are permitted provided that the following conditions are
      +                    met:
      +                    
      +                    * Redistributions of source code must retain the above copyright
      +                    notice, this list of conditions and the following disclaimer.
      +                    
      +                    * Redistributions in binary form must reproduce the above copyright
      +                    notice, this list of conditions and the following disclaimer in
      +                    the documentation and/or other materials provided with the
      +                    distribution.
      +                    
      +                    * Neither the name of Google nor the names of its contributors may
      +                    be used to endorse or promote products derived from this software
      +                    without specific prior written permission.
      +                    
      +                    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
      +                    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
      +                    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
      +                    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
      +                    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
      +                    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
      +                    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      +                    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      +                    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      +                    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
      +                    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      +                
      +
    • +
  • Realm ( Date: Thu, 16 May 2019 14:45:42 +0200 Subject: [PATCH 039/266] Reactions: we cannot like & dislike at the same time --- .../ReactionsMenuViewModel.swift | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index bb8759ca75..6f2d6aff26 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -148,10 +148,42 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { self.coordinatorDelegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: false) } + if selected { + self.ensure3StateButtons(withReaction: reaction) + } + // TODO: to remove self.fakeToggleReaction(reaction: reaction) } + // We can like, dislike, be indifferent but we cannot like & dislike at the same time + private func ensure3StateButtons(withReaction reaction: ReactionsMenuReaction) { + var unreaction: ReactionsMenuReaction? + + switch reaction { + case .agree: + if isDisagreeButtonSelected { + unreaction = .disagree + } + case .disagree: + if isAgreeButtonSelected { + unreaction = .agree + } + case .like: + if isDislikeButtonSelected { + unreaction = .dislike + } + case .dislike: + if isLikeButtonSelected { + unreaction = .like + } + } + + if let unreaction = unreaction { + self.react(withReaction: unreaction, selected: false) + } + } + // TODO: to remove private func fakeToggleReaction(reaction: ReactionsMenuReaction) { switch reaction { @@ -167,5 +199,4 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { self.viewDelegate?.reactionsMenuViewModelDidUpdate(self) } - } From dd59187d4efd65566065c97b3a97db2261dce6b2 Mon Sep 17 00:00:00 2001 From: Maros Dumitru Date: Fri, 5 Apr 2019 11:48:44 +0000 Subject: [PATCH 040/266] Translated using Weblate (Romanian) Currently translated at 1.9% (12 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/ro/ --- Riot/Assets/ro.lproj/Vector.strings | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 Riot/Assets/ro.lproj/Vector.strings diff --git a/Riot/Assets/ro.lproj/Vector.strings b/Riot/Assets/ro.lproj/Vector.strings new file mode 100644 index 0000000000..8d9ff42212 --- /dev/null +++ b/Riot/Assets/ro.lproj/Vector.strings @@ -0,0 +1,11 @@ +// String for App Store +"store_short_description" = "// String pe App Store"; +"title_favourites" = "Favorite"; +"title_people" = "Oamei"; +"title_rooms" = "Camere"; +"title_groups" = "Comunități"; +"warning" = "Greșeală"; +// Actions +"view" = "Văzut"; +"next" = "Următorul"; +"back" = "Înapoi"; From e728719b1f086f32274ffac12b6915a411b75c41 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 16 May 2019 13:07:16 +0000 Subject: [PATCH 041/266] Translated using Weblate (Romanian) Currently translated at 1.9% (12 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/ro/ --- Riot/Assets/ro.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/ro.lproj/Vector.strings b/Riot/Assets/ro.lproj/Vector.strings index 8d9ff42212..95ce74958b 100644 --- a/Riot/Assets/ro.lproj/Vector.strings +++ b/Riot/Assets/ro.lproj/Vector.strings @@ -9,3 +9,7 @@ "view" = "Văzut"; "next" = "Următorul"; "back" = "Înapoi"; +"store_full_description" = "--testing please remove me--"; +// Titles +"title_home" = "--test please remove--"; +"continue" = "test please remove."; From 37518e75297098250f79a68483a6f894d33e20ff Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 16 May 2019 13:45:32 +0000 Subject: [PATCH 042/266] Translated using Weblate (Romanian) Currently translated at 1.5% (9 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/ro/ --- Riot/Assets/ro.lproj/Vector.strings | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/ro.lproj/Vector.strings b/Riot/Assets/ro.lproj/Vector.strings index 95ce74958b..d394ecf327 100644 --- a/Riot/Assets/ro.lproj/Vector.strings +++ b/Riot/Assets/ro.lproj/Vector.strings @@ -9,7 +9,7 @@ "view" = "Văzut"; "next" = "Următorul"; "back" = "Înapoi"; -"store_full_description" = "--testing please remove me--"; +"store_full_description" = ""; // Titles -"title_home" = "--test please remove--"; -"continue" = "test please remove."; +"title_home" = ""; +"continue" = ""; From 81a274be30e30115c1feb0b25500f8841228b2e9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 16 May 2019 13:47:56 +0000 Subject: [PATCH 043/266] Translated using Weblate (Romanian) Currently translated at 1.5% (9 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/ro/ --- Riot/Assets/ro.lproj/Vector.strings | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Riot/Assets/ro.lproj/Vector.strings b/Riot/Assets/ro.lproj/Vector.strings index d394ecf327..8d9ff42212 100644 --- a/Riot/Assets/ro.lproj/Vector.strings +++ b/Riot/Assets/ro.lproj/Vector.strings @@ -9,7 +9,3 @@ "view" = "Văzut"; "next" = "Următorul"; "back" = "Înapoi"; -"store_full_description" = ""; -// Titles -"title_home" = ""; -"continue" = ""; From 0bc5c28390076c720a7a0915ba9e44e4491e3fd7 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 16 May 2019 15:56:34 +0200 Subject: [PATCH 044/266] Reactions: Fix Steve's comments --- .../ReactionsMenu/ReactionsMenuButton.swift | 16 +++++++++++----- .../ReactionsMenu/ReactionsMenuView.swift | 2 +- .../ReactionsMenu/ReactionsMenuViewModel.swift | 8 ++++---- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift index b68f07e040..ae12573ecf 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift @@ -16,7 +16,7 @@ import UIKit -class ReactionsMenuButton: UIButton { +class ReactionsMenuButton: UIButton, Themable { // MARK: Private @@ -45,15 +45,21 @@ class ReactionsMenuButton: UIButton { // MARK: - Private private func commonInit() { - self.theme = ThemeService.shared().theme + self.layer.masksToBounds = true + + self.update(theme: ThemeService.shared().theme) customizeViewRendering() updateView() } - func customizeViewRendering() { + private func customizeViewRendering() { self.tintColor = UIColor.clear - + } + + func update(theme: Theme) { + self.theme = theme + // TODO: Color for black theme self.setTitleColor(self.theme.textPrimaryColor, for: .normal) self.setTitleColor(self.theme.textPrimaryColor, for: .selected) @@ -61,7 +67,7 @@ class ReactionsMenuButton: UIButton { self.layer.borderColor = self.theme.tintColor.cgColor } - func updateView() { + private func updateView() { backgroundColor = isSelected ? self.theme.tintBackgroundColor : self.theme.headerBackgroundColor } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift index e7290d0c27..d4f54bd39e 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift @@ -86,7 +86,7 @@ final class ReactionsMenuView: UIView, NibOwnerLoadable { customizeViewRendering() } - func customizeViewRendering() { + private func customizeViewRendering() { self.backgroundColor = UIColor.clear } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 6f2d6aff26..ad6ba09fe7 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -27,10 +27,10 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { // MARK: Public - var isAgreeButtonSelected: Bool = false - var isDisagreeButtonSelected: Bool = false - var isLikeButtonSelected: Bool = false - var isDislikeButtonSelected: Bool = false + private(set) var isAgreeButtonSelected: Bool = false + private(set) var isDisagreeButtonSelected: Bool = false + private(set) var isLikeButtonSelected: Bool = false + private(set) var isDislikeButtonSelected: Bool = false weak var viewDelegate: ReactionsMenuViewModelDelegate? weak var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? From 0ed0d8cef51b4f3a71982b4cd7e6fb67133f8311 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 16 May 2019 17:44:07 +0200 Subject: [PATCH 045/266] Reactions: Follow small SDK change --- .../ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index ad6ba09fe7..ecfaaaec72 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -85,7 +85,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { } private func loadData() { - guard let reactionCounts = self.aggregations.reactions(onEvent: self.eventId, inRoom: self.roomId) else { + guard let reactionCounts = self.aggregations.aggregatedReactions(onEvent: self.eventId, inRoom: self.roomId)?.reactions else { return } From 3249751dc2ee3065b6ba23a816a9a236a30828cc Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 16 May 2019 18:27:48 +0200 Subject: [PATCH 046/266] RoomBubbleCellData: Do readReceiptVerticalWhitespace thing in one place --- .../Room/CellData/RoomBubbleCellData.m | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 2b953e464f..edc154f3f1 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -1,5 +1,6 @@ /* Copyright 2015 OpenMarket Ltd + Copyright 2019 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. @@ -210,11 +211,7 @@ - (NSAttributedString*)refreshAttributedTextMessage currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:componentString]; } - if (self.readReceipts[component.event.eventId].count) - { - // Add vertical whitespace in case of read receipts - [currentAttributedTextMsg appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]]; - } + [self addverticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId]; // The first non empty component has been handled. break; @@ -246,11 +243,7 @@ - (NSAttributedString*)refreshAttributedTextMessage // Append attributed text [currentAttributedTextMsg appendAttributedString:componentString]; - if (self.readReceipts[component.event.eventId].count) - { - // Add vertical whitespace in case of read receipts - [currentAttributedTextMsg appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]]; - } + [self addverticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId]; } } @@ -344,13 +337,9 @@ - (void)refreshBubbleComponentsPosition positionY = MXKROOMBUBBLECELLDATA_TEXTVIEW_DEFAULT_VERTICAL_INSET + (cumulatedHeight - [self rawTextHeight:componentString]); component.position = CGPointMake(0, positionY); - - // Add vertical whitespace in case of read receipts. - if (self.readReceipts[component.event.eventId].count) - { - [attributedString appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]]; - } - + + [self addverticalWhitespaceToString:attributedString forEvent:component.event.eventId]; + [attributedString appendAttributedString:[MXKRoomBubbleCellDataWithAppendingMode messageSeparator]]; } else @@ -363,6 +352,15 @@ - (void)refreshBubbleComponentsPosition } } +- (void)addverticalWhitespaceToString:(NSMutableAttributedString *)attributedString forEvent:(NSString *)eventId +{ + // Add vertical whitespace in case of read receipts. + if (self.readReceipts[eventId].count) + { + [attributedString appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]]; + } +} + - (void)setContainsLastMessage:(BOOL)containsLastMessage { // Check whether there is something to do From 00bebecc296b93916c229e4607dd40a22e5c1be2 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 17 May 2019 01:28:45 +0200 Subject: [PATCH 047/266] Reactions: Add placeholder for reactions list under message --- .../Room/CellData/RoomBubbleCellData.m | 42 +++++++++++--- .../Modules/Room/DataSources/RoomDataSource.m | 55 ++++++++++++++++++- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index edc154f3f1..df44185b8a 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -211,7 +211,7 @@ - (NSAttributedString*)refreshAttributedTextMessage currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:componentString]; } - [self addverticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId]; + [self addVerticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId]; // The first non empty component has been handled. break; @@ -243,7 +243,7 @@ - (NSAttributedString*)refreshAttributedTextMessage // Append attributed text [currentAttributedTextMsg appendAttributedString:componentString]; - [self addverticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId]; + [self addVerticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId]; } } @@ -259,7 +259,7 @@ - (void)refreshBubbleComponentsPosition // Check whether there is at least one component. if (bubbleComponents.count) { - BOOL hasReadReceipts = NO; + BOOL hasReadReceiptsOrReactions = NO; // Set position of the first component CGFloat positionY = (self.attachment == nil || self.attachment.type == MXKAttachmentTypeFile || self.attachment.type == MXKAttachmentTypeAudio) ? MXKROOMBUBBLECELLDATA_TEXTVIEW_DEFAULT_VERTICAL_INSET : 0; @@ -274,7 +274,8 @@ - (void)refreshBubbleComponentsPosition if (component.attributedTextMessage) { - hasReadReceipts = (self.readReceipts[component.event.eventId].count > 0); + hasReadReceiptsOrReactions = (self.reactions[component.event.eventId].reactions.count > 0); + hasReadReceiptsOrReactions |= (self.readReceipts[component.event.eventId].count > 0); break; } } @@ -299,9 +300,9 @@ - (void)refreshBubbleComponentsPosition } // Vertical whitespace is added in case of read receipts - if (hasReadReceipts) + if (hasReadReceiptsOrReactions) { - [attributedString appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]]; + [self addVerticalWhitespaceToString:attributedString forEvent:component.event.eventId]; } [attributedString appendAttributedString:[MXKRoomBubbleCellDataWithAppendingMode messageSeparator]]; @@ -338,7 +339,7 @@ - (void)refreshBubbleComponentsPosition component.position = CGPointMake(0, positionY); - [self addverticalWhitespaceToString:attributedString forEvent:component.event.eventId]; + [self addVerticalWhitespaceToString:attributedString forEvent:component.event.eventId]; [attributedString appendAttributedString:[MXKRoomBubbleCellDataWithAppendingMode messageSeparator]]; } @@ -352,8 +353,18 @@ - (void)refreshBubbleComponentsPosition } } -- (void)addverticalWhitespaceToString:(NSMutableAttributedString *)attributedString forEvent:(NSString *)eventId +- (void)addVerticalWhitespaceToString:(NSMutableAttributedString *)attributedString forEvent:(NSString *)eventId { + // Add vertical whitespace in case of read receipts. + NSUInteger reactionCount = self.reactions[eventId].reactions.count; + if (reactionCount) + { + // TODO: Set right height: 22 + 8 + // TODO: Set right dynamic line count: reactionCount / 6 + CGFloat height = (22 + 8) * ((reactionCount / 6) + 1); + [attributedString appendAttributedString:[RoomBubbleCellData verticalWhitespaceForHeight: height]]; + } + // Add vertical whitespace in case of read receipts. if (self.readReceipts[eventId].count) { @@ -480,6 +491,21 @@ + (NSAttributedString *)readReceiptVerticalWhitespace return readReceiptVerticalWhitespace; } ++ (NSAttributedString *)verticalWhitespaceForHeight:(CGFloat)height +{ + NSUInteger returns = height / 6; + NSMutableString *returnString = [NSMutableString string]; + + for (NSUInteger i = 0; i < returns; i++) + { + [returnString appendString:@"\n"]; + } + + + return [[NSAttributedString alloc] initWithString:returnString attributes:@{NSForegroundColorAttributeName : [UIColor blackColor], + NSFontAttributeName: [UIFont systemFontOfSize:4]}]; +} + - (BOOL)hasSameSenderAsBubbleCellData:(id)bubbleCellData { if (self.tag == RoomBubbleCellDataTagMembership || bubbleCellData.tag == RoomBubbleCellDataTagMembership) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index c7f2e74aae..1dee9902f1 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -265,7 +265,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N // Handle read receipts and read marker display. // Ignore the read receipts on the bubble without actual display. // Ignore the read receipts on collapsed bubbles - if ((self.showBubbleReceipts && cellData.readReceipts.count && !isCollapsableCellCollapsed) || self.showReadMarker) + if ((((self.showBubbleReceipts && cellData.readReceipts.count) || cellData.reactions.count) && !isCollapsableCellCollapsed) || self.showReadMarker) { // Read receipts container are inserted here on the right side into the content view. // Some vertical whitespaces are added in message text view (see RoomBubbleCellData class) to insert correctly multiple receipts. @@ -277,6 +277,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N if (component.event.sentState != MXEventSentStateFailed) { + MXKReceiptSendersContainer* avatarsContainer; + // Handle read receipts (if any) if (self.showBubbleReceipts && cellData.readReceipts.count && !isCollapsableCellCollapsed) { @@ -307,7 +309,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N if (roomMembers.count) { // Define the read receipts container, positioned on the right border of the bubble cell (Note the right margin 6 pts). - MXKReceiptSendersContainer* avatarsContainer = [[MXKReceiptSendersContainer alloc] initWithFrame:CGRectMake(bubbleCell.frame.size.width - 156, bottomPositionY - 13, 150, 12) andMediaManager:self.mxSession.mediaManager]; + avatarsContainer = [[MXKReceiptSendersContainer alloc] initWithFrame:CGRectMake(bubbleCell.frame.size.width - 156, bottomPositionY - 13, 150, 12) andMediaManager:self.mxSession.mediaManager]; // Custom avatar display avatarsContainer.maxDisplayedAvatars = 5; @@ -376,6 +378,55 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N [NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, topConstraint, trailingConstraint]]; } } + + MXAggregatedReactions* reactions = cellData.reactions[component.event.eventId]; + if (reactions && !isCollapsableCellCollapsed) + { + // TODO: Use final ReactionsView + UITextView* reactionsContainer = [UITextView new]; + reactionsContainer.translatesAutoresizingMaskIntoConstraints = NO; + [bubbleCell.contentView addSubview:reactionsContainer]; + + reactionsContainer.layer.borderColor = UIColor.orangeColor.CGColor; + reactionsContainer.layer.borderWidth = 1; + + if (!bubbleCell.tmpSubviews) + { + bubbleCell.tmpSubviews = [NSMutableArray array]; + } + [bubbleCell.tmpSubviews addObject:reactionsContainer]; + + // At the bottom, we have read receipts or nothing + NSLayoutConstraint *bottomConstraint; + if (avatarsContainer) + { + bottomConstraint = [reactionsContainer.bottomAnchor constraintEqualToAnchor:avatarsContainer.topAnchor]; + } + else + { + bottomConstraint = [reactionsContainer.bottomAnchor constraintEqualToAnchor:reactionsContainer.superview.topAnchor constant:bottomPositionY]; + } + + // TODO: To refine + CGFloat viewHeight = 22; + + // Force receipts container size + [NSLayoutConstraint activateConstraints: + @[ + [reactionsContainer.leadingAnchor constraintEqualToAnchor:reactionsContainer.superview.leadingAnchor constant:50], + [reactionsContainer.trailingAnchor constraintEqualToAnchor:reactionsContainer.superview.trailingAnchor constant:-6], + [reactionsContainer.heightAnchor constraintEqualToConstant:viewHeight], + bottomConstraint + ]]; + + // TODO: To remove + NSMutableString *reactionsString = [NSMutableString string]; + for (MXReactionCount *reactionCount in reactions.reactions) + { + [reactionsString appendFormat:@"%@: %@ ", reactionCount.reaction, @(reactionCount.count)]; + } + reactionsContainer.text = reactionsString; + } // Check whether the read marker must be displayed here. if (self.showReadMarker) From b1ebf79e505664be3c600dc744ac9d9807ec4196 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 17 May 2019 12:10:32 +0200 Subject: [PATCH 048/266] RoomVC: ContextualMenu: Plug reactions menu --- .../RoomContextualMenuPresenter.swift | 6 +++ ...oomContextualMenuViewController.storyboard | 17 +++++++ .../RoomContextualMenuViewController.swift | 48 ++++++++++++++++++- Riot/Modules/Room/RoomViewController.m | 17 +++++++ 4 files changed, 87 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift index b066de8ebb..2a4c39a53e 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift @@ -102,4 +102,10 @@ final class RoomContextualMenuPresenter: NSObject { animationCompletionInstructions() } } + + func showReactionsMenu(forEvent eventId: String, inRoom roomId: String, session: MXSession, + aroundFrame frame: CGRect) { + let reactionsMenuViewModel = ReactionsMenuViewModel(aggregations: session.aggregations, roomId: roomId, eventId: eventId) + self.roomContextualMenuViewController?.showReactionsMenu(withViewModel: reactionsMenuViewModel, aroundFrame: frame) + } } diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard index a06eecc818..711ebfcfb4 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard @@ -20,7 +20,21 @@ + + + + + + + + + + + + + + @@ -47,6 +61,9 @@ + + + diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift index 7492a1e5ee..5a111508db 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift @@ -18,6 +18,7 @@ import UIKit @objc protocol RoomContextualMenuViewControllerDelegate: class { func roomContextualMenuViewControllerDidTapBackgroundOverlay(_ viewController: RoomContextualMenuViewController) + func roomContextualMenuViewControllerDidReaction(_ viewController: RoomContextualMenuViewController) } @objcMembers @@ -33,9 +34,14 @@ final class RoomContextualMenuViewController: UIViewController, Themable { @IBOutlet private weak var menuToolbarViewHeightConstraint: NSLayoutConstraint! @IBOutlet private weak var menuToolbarViewBottomConstraint: NSLayoutConstraint! + @IBOutlet private weak var reactionsMenuView: ReactionsMenuView! + @IBOutlet private weak var reactionsMenuViewHeightConstraint: NSLayoutConstraint! + @IBOutlet private weak var reactionsMenuViewBottomConstraint: NSLayoutConstraint! + // MARK: Private private var theme: Theme! + private var errorPresenter: MXKErrorPresentation! private var contextualMenuItems: [RoomContextualMenuItem] = [] private var hiddenToolbarViewBottomConstant: CGFloat { @@ -69,10 +75,13 @@ final class RoomContextualMenuViewController: UIViewController, Themable { super.viewDidLoad() // Do any additional setup after loading the view. - + self.reactionsMenuView.isHidden = true + self.backgroundOverlayView.isUserInteractionEnabled = true self.menuToolbarView.fill(contextualMenuItems: self.contextualMenuItems) self.setupBackgroundOverlayTapGestureRecognizer() + + self.errorPresenter = MXKErrorAlertPresentation() self.registerThemeServiceDidChangeThemeNotification() self.update(theme: self.theme) @@ -87,6 +96,28 @@ final class RoomContextualMenuViewController: UIViewController, Themable { func hideMenuToolbar() { self.menuToolbarViewBottomConstraint.constant = self.hiddenToolbarViewBottomConstant } + + func showReactionsMenu(withViewModel viewModel: ReactionsMenuViewModel, aroundFrame frame: CGRect) { + self.reactionsMenuView.viewModel = viewModel + self.reactionsMenuView.viewModel?.coordinatorDelegate = self + self.reactionsMenuView.isHidden = false + + let menuHeight = self.reactionsMenuViewHeightConstraint.constant + + // Try to display the menu at the top of the message first + // Then, try at the bottom + // Else, keep the position defined in the storyboard + if frame.origin.y >= self.reactionsMenuViewHeightConstraint.constant { + self.reactionsMenuViewBottomConstraint.constant = frame.origin.y + } else { + let frameBottomY = frame.origin.y + frame.size.height + let visibleViewHeight = self.view.frame.size.height - self.menuToolbarView.frame.size.height + + if frameBottomY + menuHeight < visibleViewHeight { + self.reactionsMenuViewBottomConstraint.constant = frameBottomY + menuHeight + } + } + } func update(theme: Theme) { self.menuToolbarView.update(theme: theme) @@ -111,3 +142,18 @@ final class RoomContextualMenuViewController: UIViewController, Themable { self.update(theme: ThemeService.shared().theme) } } + +extension RoomContextualMenuViewController: ReactionsMenuViewModelCoordinatorDelegate { + + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didSendReaction reaction: String, isAddReaction: Bool) { + self.delegate?.roomContextualMenuViewControllerDidReaction(self) + } + + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didReactionComplete reaction: String, isAddReaction: Bool) { + } + + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didReactionFailedWithError error: Error, reaction: String, isAddReaction: Bool) { + self.errorPresenter?.presentError(from: self, forError: error, animated: true) { + } + } +} diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index c81d636d69..6278e95231 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5076,6 +5076,18 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce completion:^{ [self contextualMenuAnimationCompletionAfterBeingShown:YES]; }]; + + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + { + // Note: For the moment, we use the frame of the cell instead of the component + // From UI perpective, that means the menu will be around a paragraph instead of a message + // This is not bad as the paragraph is kept visible to provide more context to the user + // TO FIX: if paragraph is bigger than the screen, the menu is displayed in the middle + CGRect frame = ((MXKRoomBubbleTableViewCell*)cell).frame; + frame = [self.bubblesTableView convertRect:frame toView:[self.bubblesTableView superview]]; + + [self.roomContextualMenuPresenter showReactionsMenuForEvent:event.eventId inRoom:event.roomId session:self.mainSession aroundFrame:frame]; + } } - (void)hideContextualMenuAnimated:(BOOL)animated @@ -5124,5 +5136,10 @@ - (void)roomContextualMenuViewControllerDidTapBackgroundOverlay:(RoomContextualM [self hideContextualMenuAnimated:YES]; } +- (void)roomContextualMenuViewControllerDidReaction:(RoomContextualMenuViewController *)viewController +{ + [self hideContextualMenuAnimated:YES]; +} + @end From 20946807d573da95b4e652c191a33ecf52a80114 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 17 May 2019 12:21:33 +0200 Subject: [PATCH 049/266] pbxproj: Fix ReactionsMenu group after automatic merge --- Riot.xcodeproj/project.pbxproj | 80 +++++++++++++++------------------- 1 file changed, 36 insertions(+), 44 deletions(-) diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index caab1e0c2e..3bc44f0de2 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -65,13 +65,6 @@ 324A2054225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324A204C225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift */; }; 324A2055225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324A204D225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift */; }; 324A2056225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 324A204E225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift */; }; - 325380D3228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D2228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift */; }; - 325380D5228C245D00ADDEFA /* ReactionsMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380D4228C245D00ADDEFA /* ReactionsMenuViewModel.swift */; }; - 325380DB228C34EF00ADDEFA /* ReactionsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */; }; - 325380DD228C34FC00ADDEFA /* ReactionsMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 325380DC228C34FC00ADDEFA /* ReactionsMenuView.xib */; }; - 325380DF228C5C2800ADDEFA /* ReactionsMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380DE228C5C2800ADDEFA /* ReactionsMenuButton.swift */; }; - 325380E1228D1F7F00ADDEFA /* ReactionsMenuViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380E0228D1F7F00ADDEFA /* ReactionsMenuViewAction.swift */; }; - 325380E3228D2EE500ADDEFA /* ReactionsMenuReaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 325380E2228D2EE500ADDEFA /* ReactionsMenuReaction.swift */; }; 3275FD8C21A5A2C500B9C13D /* TermsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3275FD8B21A5A2C500B9C13D /* TermsView.swift */; }; 3281BCF72201FA4200F4A383 /* UIControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3281BCF62201FA4200F4A383 /* UIControl.swift */; }; 3284A35120A07C210044F922 /* postMessageAPI.js in Resources */ = {isa = PBXBuildFile; fileRef = 3284A35020A07C210044F922 /* postMessageAPI.js */; }; @@ -82,6 +75,13 @@ 32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */; }; 32891D76226728EF00C82226 /* DeviceVerificationDataLoadingViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */; }; 32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32B1FEDA21A46F2C00637127 /* TermsView.xib */; }; + 32B94DF8228EC26400716A26 /* ReactionsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF1228EC26400716A26 /* ReactionsMenuView.swift */; }; + 32B94DF9228EC26400716A26 /* ReactionsMenuViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */; }; + 32B94DFA228EC26400716A26 /* ReactionsMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */; }; + 32B94DFB228EC26400716A26 /* ReactionsMenuViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF4228EC26400716A26 /* ReactionsMenuViewModelType.swift */; }; + 32B94DFC228EC26400716A26 /* ReactionsMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF5228EC26400716A26 /* ReactionsMenuViewModel.swift */; }; + 32B94DFD228EC26400716A26 /* ReactionsMenuReaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF6228EC26400716A26 /* ReactionsMenuReaction.swift */; }; + 32B94DFE228EC26400716A26 /* ReactionsMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32B94DF7228EC26400716A26 /* ReactionsMenuView.xib */; }; 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */; }; 32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */; }; 32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */; }; @@ -581,13 +581,6 @@ 324A204C225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingCoordinatorType.swift; sourceTree = ""; }; 324A204D225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingViewModelType.swift; sourceTree = ""; }; 324A204E225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationIncomingCoordinator.swift; sourceTree = ""; }; - 325380D2228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModelType.swift; sourceTree = ""; }; - 325380D4228C245D00ADDEFA /* ReactionsMenuViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModel.swift; sourceTree = ""; }; - 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuView.swift; sourceTree = ""; }; - 325380DC228C34FC00ADDEFA /* ReactionsMenuView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReactionsMenuView.xib; sourceTree = ""; }; - 325380DE228C5C2800ADDEFA /* ReactionsMenuButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuButton.swift; sourceTree = ""; }; - 325380E0228D1F7F00ADDEFA /* ReactionsMenuViewAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewAction.swift; sourceTree = ""; }; - 325380E2228D2EE500ADDEFA /* ReactionsMenuReaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuReaction.swift; sourceTree = ""; }; 3267EFB320E379FD00FF1CAA /* CHANGES.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = CHANGES.rst; sourceTree = ""; }; 3267EFB420E379FD00FF1CAA /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; fileEncoding = 4; path = Podfile; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 3267EFB520E379FD00FF1CAA /* AUTHORS.rst */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = AUTHORS.rst; sourceTree = ""; }; @@ -602,6 +595,13 @@ 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewController.swift; sourceTree = ""; }; 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DeviceVerificationDataLoadingViewController.storyboard; sourceTree = ""; }; 32B1FEDA21A46F2C00637127 /* TermsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TermsView.xib; sourceTree = ""; }; + 32B94DF1228EC26400716A26 /* ReactionsMenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuView.swift; sourceTree = ""; }; + 32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewAction.swift; sourceTree = ""; }; + 32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuButton.swift; sourceTree = ""; }; + 32B94DF4228EC26400716A26 /* ReactionsMenuViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModelType.swift; sourceTree = ""; }; + 32B94DF5228EC26400716A26 /* ReactionsMenuViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModel.swift; sourceTree = ""; }; + 32B94DF6228EC26400716A26 /* ReactionsMenuReaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuReaction.swift; sourceTree = ""; }; + 32B94DF7228EC26400716A26 /* ReactionsMenuView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReactionsMenuView.xib; sourceTree = ""; }; 32BDC9A1211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/InfoPlist.strings; sourceTree = ""; }; 32BDC9A2211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/Localizable.strings; sourceTree = ""; }; 32BDC9A3211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/Vector.strings; sourceTree = ""; }; @@ -1444,28 +1444,6 @@ path = Incoming; sourceTree = ""; }; - 325380CE228C180000ADDEFA /* ContextualMenu */ = { - isa = PBXGroup; - children = ( - 325380CF228C181400ADDEFA /* ReactionsMenu */, - ); - path = ContextualMenu; - sourceTree = ""; - }; - 325380CF228C181400ADDEFA /* ReactionsMenu */ = { - isa = PBXGroup; - children = ( - 325380E2228D2EE500ADDEFA /* ReactionsMenuReaction.swift */, - 325380D2228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift */, - 325380D4228C245D00ADDEFA /* ReactionsMenuViewModel.swift */, - 325380DA228C34EF00ADDEFA /* ReactionsMenuView.swift */, - 325380DC228C34FC00ADDEFA /* ReactionsMenuView.xib */, - 325380DE228C5C2800ADDEFA /* ReactionsMenuButton.swift */, - 325380E0228D1F7F00ADDEFA /* ReactionsMenuViewAction.swift */, - ); - path = ReactionsMenu; - sourceTree = ""; - }; 32891D682264C6A000C82226 /* SimpleScreenTemplate */ = { isa = PBXGroup; children = ( @@ -1507,6 +1485,20 @@ path = js; sourceTree = ""; }; + 32B94DF0228EC26400716A26 /* ReactionsMenu */ = { + isa = PBXGroup; + children = ( + 32B94DF1228EC26400716A26 /* ReactionsMenuView.swift */, + 32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */, + 32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */, + 32B94DF4228EC26400716A26 /* ReactionsMenuViewModelType.swift */, + 32B94DF5228EC26400716A26 /* ReactionsMenuViewModel.swift */, + 32B94DF6228EC26400716A26 /* ReactionsMenuReaction.swift */, + 32B94DF7228EC26400716A26 /* ReactionsMenuView.xib */, + ); + path = ReactionsMenu; + sourceTree = ""; + }; 32BF994D21FA1C6300698084 /* KeyBackup */ = { isa = PBXGroup; children = ( @@ -2045,7 +2037,6 @@ B1B5568E20EE6C4C00210D55 /* Room */ = { isa = PBXGroup; children = ( - 325380CE228C180000ADDEFA /* ContextualMenu */, B1B5568F20EE6C4C00210D55 /* RoomViewController.h */, B1B556A020EE6C4C00210D55 /* RoomViewController.m */, B1B5569620EE6C4C00210D55 /* RoomViewController.xib */, @@ -3042,6 +3033,7 @@ B1C562D7228C0B4C0037F12A /* ContextualMenu */ = { isa = PBXGroup; children = ( + 32B94DF0228EC26400716A26 /* ReactionsMenu */, B1C562DA228C0BB00037F12A /* RoomContextualMenuAction.swift */, B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */, B1C562DE228C7C8B0037F12A /* RoomContextualMenuPresenter.swift */, @@ -3536,6 +3528,7 @@ B1107ECA2200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard in Resources */, B1B5579C20EF575B00210D55 /* ForgotPasswordInputsView.xib in Resources */, F083BE011E7009ED00A9B29C /* third_party_licenses.html in Resources */, + 32B94DFE228EC26400716A26 /* ReactionsMenuView.xib in Resources */, B1098BFC21ECFE65000DDA48 /* PasswordStrengthView.xib in Resources */, B1B5573720EE6C4D00210D55 /* GroupParticipantsViewController.xib in Resources */, B110872421F098F0003554A5 /* ActivityIndicatorView.xib in Resources */, @@ -3543,7 +3536,6 @@ 3232AB2122564D9100AD6A5C /* README.md in Resources */, B1B5593920EF7BAC00210D55 /* TableViewCellWithCheckBoxes.xib in Resources */, B1B557C120EF5B4500210D55 /* DisabledRoomInputToolbarView.xib in Resources */, - 325380DD228C34FC00ADDEFA /* ReactionsMenuView.xib in Resources */, 32891D6C2264CBA300C82226 /* SimpleScreenTemplateViewController.storyboard in Resources */, B1664DA320F4F96200808783 /* Vector.strings in Resources */, B1B557C720EF5CD400210D55 /* DirectoryServerDetailTableViewCell.xib in Resources */, @@ -3852,7 +3844,6 @@ B16932FA20F3C51A00746532 /* RecentCellData.m in Sources */, B16932F220F3C49E00746532 /* GroupsDataSource.m in Sources */, B1B5581C20EF625800210D55 /* RoomAvatarTitleView.m in Sources */, - 325380D3228C1E3700ADDEFA /* ReactionsMenuViewModelType.swift in Sources */, B169330820F3CA0E00746532 /* ContactsDataSource.m in Sources */, B1B5574B20EE6C4D00210D55 /* MediaAlbumContentViewController.m in Sources */, B1B5598820EFC3E000210D55 /* WidgetManager.m in Sources */, @@ -3864,7 +3855,6 @@ B16932A320F3A21C00746532 /* main.m in Sources */, B1B5574520EE6C4D00210D55 /* StartChatViewController.m in Sources */, 3232AB4C2256558300AD6A5C /* TemplateScreenCoordinator.swift in Sources */, - 325380E1228D1F7F00ADDEFA /* ReactionsMenuViewAction.swift in Sources */, B1B5575920EE6C4D00210D55 /* HomeMessagesSearchViewController.m in Sources */, B1B558DE20EF768F00210D55 /* RoomIncomingAttachmentBubbleCell.m in Sources */, B1B5574820EE6C4D00210D55 /* PeopleViewController.m in Sources */, @@ -3938,6 +3928,7 @@ B1B5594520EF7BD000210D55 /* TableViewCellWithCollectionView.m in Sources */, 32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */, 32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */, + 32B94DFB228EC26400716A26 /* ReactionsMenuViewModelType.swift in Sources */, F083BDEF1E7009ED00A9B29C /* UINavigationController+Riot.m in Sources */, B1B5581F20EF625800210D55 /* SimpleRoomTitleView.m in Sources */, B1C562E2228C7C8D0037F12A /* RoomContextualMenuViewController.swift in Sources */, @@ -3972,7 +3963,6 @@ B1B5590620EF768F00210D55 /* RoomMembershipCollapsedWithPaginationTitleBubbleCell.m in Sources */, B139C21D21FE5BF500BB68EC /* KeyBackupRecoverFromPassphraseViewModelType.swift in Sources */, F083BE031E7009ED00A9B29C /* EventFormatter.m in Sources */, - 325380DB228C34EF00ADDEFA /* ReactionsMenuView.swift in Sources */, 324A2056225FC571004FE8B0 /* DeviceVerificationIncomingCoordinator.swift in Sources */, B16932F720F3C50E00746532 /* RecentsDataSource.m in Sources */, 3232AB4F2256558300AD6A5C /* TemplateScreenViewController.swift in Sources */, @@ -3992,14 +3982,15 @@ 3232ABAB225730E100AD6A5C /* DeviceVerificationCoordinator.swift in Sources */, B1B5583E20EF6E7F00210D55 /* GroupRoomTableViewCell.m in Sources */, B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */, + 32B94DF8228EC26400716A26 /* ReactionsMenuView.swift in Sources */, B1B5574F20EE6C4D00210D55 /* RoomsViewController.m in Sources */, B1B5572520EE6C4D00210D55 /* RoomMessagesSearchViewController.m in Sources */, B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */, B1B5579120EF568D00210D55 /* GroupInviteTableViewCell.m in Sources */, B1B5579A20EF575B00210D55 /* ForgotPasswordInputsView.m in Sources */, + 32B94DFD228EC26400716A26 /* ReactionsMenuReaction.swift in Sources */, B1B558CC20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B1B5571D20EE6C4D00210D55 /* HomeViewController.m in Sources */, - 325380D5228C245D00ADDEFA /* ReactionsMenuViewModel.swift in Sources */, 3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */, B16932EA20F3C39000746532 /* UnifiedSearchRecentsDataSource.m in Sources */, B1B557DE20EF5FBB00210D55 /* FilesSearchTableViewCell.m in Sources */, @@ -4083,7 +4074,6 @@ B1B5578620EF564900210D55 /* GroupTableViewCellWithSwitch.m in Sources */, B1098BE821ECFE52000DDA48 /* Coordinator.swift in Sources */, B1B557E920EF60F500210D55 /* MessagesSearchResultTextMsgBubbleCell.m in Sources */, - 325380DF228C5C2800ADDEFA /* ReactionsMenuButton.swift in Sources */, 324A2050225FC571004FE8B0 /* DeviceVerificationIncomingViewController.swift in Sources */, B1098C0D21ED07E4000DDA48 /* NavigationRouter.swift in Sources */, B110872321F098F0003554A5 /* ActivityIndicatorPresenterType.swift in Sources */, @@ -4106,8 +4096,8 @@ 32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */, 32F6B96A2270623100BBA352 /* DeviceVerificationDataLoadingViewState.swift in Sources */, 32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */, + 32B94DF9228EC26400716A26 /* ReactionsMenuViewAction.swift in Sources */, B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */, - 325380E3228D2EE500ADDEFA /* ReactionsMenuReaction.swift in Sources */, F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */, F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */, 324A2055225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift in Sources */, @@ -4122,6 +4112,7 @@ B1B5575B20EE6C4D00210D55 /* HomeFilesSearchViewController.m in Sources */, B139C22521FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift in Sources */, B1098BFD21ECFE65000DDA48 /* PasswordStrengthManager.swift in Sources */, + 32B94DFC228EC26400716A26 /* ReactionsMenuViewModel.swift in Sources */, B1B558F520EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m in Sources */, 3232AB482256558300AD6A5C /* FlowTemplateCoordinatorType.swift in Sources */, B1B558F820EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, @@ -4135,6 +4126,7 @@ B1098C0021ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.swift in Sources */, B1B5591020EF782800210D55 /* TableViewCellWithPhoneNumberTextField.m in Sources */, B1DB4F06223015080065DBFA /* Character.swift in Sources */, + 32B94DFA228EC26400716A26 /* ReactionsMenuButton.swift in Sources */, B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */, B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */, B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */, From 80d817c98ed55da038250012fde61da522dafd3f Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 17 May 2019 17:39:31 +0200 Subject: [PATCH 050/266] Reactions: Reactions menu: plug unreact --- .../ReactionsMenuViewModel.swift | 42 +++++++++---------- 1 file changed, 19 insertions(+), 23 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index ecfaaaec72..04d8e0407b 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -140,20 +140,32 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) }) - - self.coordinatorDelegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: true) } else { - // TODO - self.coordinatorDelegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: false) + self.aggregations.unReact(onReaction: reaction.rawValue, toEvent: self.eventId, inRoom: self.roomId, success: {[weak self] in + + guard let sself = self else { + return + } + + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: false) + + }, failure: {[weak self] (error) in + print("[ReactionsMenuViewModel] react: Error: \(error)") + + guard let sself = self else { + return + } + + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: false) + }) } + self.coordinatorDelegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: !selected) + if selected { self.ensure3StateButtons(withReaction: reaction) } - - // TODO: to remove - self.fakeToggleReaction(reaction: reaction) } // We can like, dislike, be indifferent but we cannot like & dislike at the same time @@ -183,20 +195,4 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { self.react(withReaction: unreaction, selected: false) } } - - // TODO: to remove - private func fakeToggleReaction(reaction: ReactionsMenuReaction) { - switch reaction { - case .agree: - isAgreeButtonSelected = !isAgreeButtonSelected - case .disagree: - isDisagreeButtonSelected = !isDisagreeButtonSelected - case .like: - isLikeButtonSelected = !isLikeButtonSelected - case .dislike: - isDislikeButtonSelected = !isDislikeButtonSelected - } - - self.viewDelegate?.reactionsMenuViewModelDidUpdate(self) - } } From 44a54b5f42197f93f1412f841e0abb8cd95587d6 Mon Sep 17 00:00:00 2001 From: Karol Kosek Date: Sat, 18 May 2019 07:34:31 +0000 Subject: [PATCH 051/266] Translated using Weblate (Polish) Currently translated at 73.3% (453 of 618 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/pl/ --- Riot/Assets/pl.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/pl.lproj/Vector.strings b/Riot/Assets/pl.lproj/Vector.strings index 86071d940b..0ff5bba82b 100644 --- a/Riot/Assets/pl.lproj/Vector.strings +++ b/Riot/Assets/pl.lproj/Vector.strings @@ -314,7 +314,7 @@ "group_participants_remove_prompt_title" = "Potwierdzenie"; "group_participants_invite_prompt_title" = "Potwierdzenie"; "group_participants_filter_members" = "Filtruj członków społeczności"; -"group_participants_invite_malformed_id" = "Uszkodzony ID. Czy to powinien być Matrix ID podobny do '@localpart:domain'"; +"group_participants_invite_malformed_id" = "Uszkodzony ID. Matrix ID powinien być podobny do '@localpart:domain'"; // Group rooms "group_rooms_filter_rooms" = "Filtruj pokoje społeczności"; // Read Receipts From 0583eb182b6eb632e24a498adef2e7637de833fe Mon Sep 17 00:00:00 2001 From: David Cordero Date: Sun, 19 May 2019 16:41:06 +0200 Subject: [PATCH 052/266] Update build instructions to make use of Bundler --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index d1c56e902e..7e398492bc 100644 --- a/README.rst +++ b/README.rst @@ -21,7 +21,8 @@ Before opening the Riot Xcode workspace, you need to build it with the CocoaPods command:: $ cd Riot - $ pod install + $ bundle install + $ bundle exec pod install This will load all dependencies for the Riot source code, including MatrixKit and MatrixSDK. You will need an recent and updated (``pod setup``) install of From 10c1624f7e75471d8adef84b41e74dcdb22fa787 Mon Sep 17 00:00:00 2001 From: David Cordero Date: Sun, 19 May 2019 17:14:41 +0200 Subject: [PATCH 053/266] Fix text color for user display name and device id labels in dark theme --- .../Incoming/DeviceVerificationIncomingViewController.swift | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/DeviceVerification/Incoming/DeviceVerificationIncomingViewController.swift b/Riot/Modules/DeviceVerification/Incoming/DeviceVerificationIncomingViewController.swift index 7013886dbe..2679fe7df7 100644 --- a/Riot/Modules/DeviceVerification/Incoming/DeviceVerificationIncomingViewController.swift +++ b/Riot/Modules/DeviceVerification/Incoming/DeviceVerificationIncomingViewController.swift @@ -110,7 +110,9 @@ final class DeviceVerificationIncomingViewController: UIViewController { self.titleLabel.textColor = theme.textPrimaryColor self.description1Label.textColor = theme.textPrimaryColor self.description2Label.textColor = theme.textPrimaryColor - + self.userDisplaynameLabel.textColor = theme.textPrimaryColor + self.deviceIdLabel.textColor = theme.textPrimaryColor + self.continueButtonBackgroundView.backgroundColor = theme.backgroundColor theme.applyStyle(onButton: self.continueButton) } From 8dfa3956477344c1ab98bd3d72791026694ae284 Mon Sep 17 00:00:00 2001 From: David Cordero Date: Sun, 19 May 2019 17:19:23 +0200 Subject: [PATCH 054/266] Remove redundant code --- ...eviceVerificationIncomingViewController.swift | 16 ---------------- .../DeviceVerificationStartViewController.swift | 16 ---------------- .../DeviceVerificationVerifyViewController.swift | 12 ------------ .../TemplateScreenViewController.swift | 8 -------- 4 files changed, 52 deletions(-) diff --git a/Riot/Modules/DeviceVerification/Incoming/DeviceVerificationIncomingViewController.swift b/Riot/Modules/DeviceVerification/Incoming/DeviceVerificationIncomingViewController.swift index 2679fe7df7..99874dc1e3 100644 --- a/Riot/Modules/DeviceVerification/Incoming/DeviceVerificationIncomingViewController.swift +++ b/Riot/Modules/DeviceVerification/Incoming/DeviceVerificationIncomingViewController.swift @@ -75,22 +75,6 @@ final class DeviceVerificationIncomingViewController: UIViewController { self.viewModel.viewDelegate = self } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - } - - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - } override var preferredStatusBarStyle: UIStatusBarStyle { return self.theme.statusBarStyle diff --git a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift index 9b6d394510..958965cc6a 100644 --- a/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift +++ b/Riot/Modules/DeviceVerification/Start/DeviceVerificationStartViewController.swift @@ -71,22 +71,6 @@ final class DeviceVerificationStartViewController: UIViewController { self.viewModel.viewDelegate = self } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - } - - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - } override var preferredStatusBarStyle: UIStatusBarStyle { return self.theme.statusBarStyle diff --git a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift index 80e7136bff..61b4e2fd42 100644 --- a/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift +++ b/Riot/Modules/DeviceVerification/Verify/DeviceVerificationVerifyViewController.swift @@ -78,18 +78,6 @@ final class DeviceVerificationVerifyViewController: UIViewController { // Hide back button self.navigationItem.setHidesBackButton(true, animated: animated) } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - } - - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - } - - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - } override var preferredStatusBarStyle: UIStatusBarStyle { return self.theme.statusBarStyle diff --git a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift index 4d1e3b3c67..cc23f78f3b 100644 --- a/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift +++ b/Tools/Templates/buildable/ScreenTemplate/TemplateScreenViewController.swift @@ -77,10 +77,6 @@ final class TemplateScreenViewController: UIViewController { self.keyboardAvoider?.startAvoiding() } - - override func viewDidAppear(_ animated: Bool) { - super.viewDidAppear(animated) - } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) @@ -88,10 +84,6 @@ final class TemplateScreenViewController: UIViewController { self.keyboardAvoider?.stopAvoiding() } - override func viewDidLayoutSubviews() { - super.viewDidLayoutSubviews() - } - override var preferredStatusBarStyle: UIStatusBarStyle { return self.theme.statusBarStyle } From 80eacd3b584807da59f307e70fd3f0e87ffb484f Mon Sep 17 00:00:00 2001 From: David Cordero Date: Sun, 19 May 2019 17:23:59 +0200 Subject: [PATCH 055/266] Update changelog --- CHANGES.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6f580afbc5..168193d985 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,9 @@ +Changes in 0.8.7 (2019-xx-xx) +=============================================== + +Bug fix: + * Device Verification: Fix user display name and device id colors in dark theme + Changes in 0.8.6 (2019-05-06) =============================================== From 0973d3886b6fd6f6660d3971d273f4d24797c789 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 20 May 2019 15:00:47 +0200 Subject: [PATCH 056/266] Reactions: Reactions menu: highlight reactiosn only our user made --- .../ReactionsMenuViewModel.swift | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 04d8e0407b..93ba3cb413 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -90,17 +90,19 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { } self.resetData() - reactionCounts.forEach { (reaction) in - if let reaction = ReactionsMenuReaction(rawValue: reaction.reaction) { - switch reaction { - case .agree: - self.isAgreeButtonSelected = true - case .disagree: - self.isDisagreeButtonSelected = true - case .like: - self.isLikeButtonSelected = true - case .dislike: - self.isDislikeButtonSelected = true + reactionCounts.forEach { (reactionCount) in + if reactionCount.myUserHasReacted { + if let reaction = ReactionsMenuReaction(rawValue: reactionCount.reaction) { + switch reaction { + case .agree: + self.isAgreeButtonSelected = true + case .disagree: + self.isDisagreeButtonSelected = true + case .like: + self.isLikeButtonSelected = true + case .dislike: + self.isDislikeButtonSelected = true + } } } } From 4a7677a667a404ae5f3a3bd3a998b94e5a34fe80 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 20 May 2019 16:04:54 +0200 Subject: [PATCH 057/266] Reactions: Use the hack like on riot-android and riot-web if the server has not yet the aggregations API --- .../ReactionsMenuViewModel.swift | 47 ++++++++++++++++++- .../RoomContextualMenuPresenter.swift | 2 +- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 93ba3cb413..58e05e0111 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -21,6 +21,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { // MARK: - Properties // MARK: Private + private let session: MXSession // TODO: To remove. Only required for reactUsingHack() private let aggregations: MXAggregations private let roomId: String private let eventId: String @@ -37,10 +38,11 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { // MARK: - Setup - init(aggregations: MXAggregations, roomId: String, eventId: String) { + init(aggregations: MXAggregations, roomId: String, eventId: String, session: MXSession) { self.aggregations = aggregations self.roomId = roomId self.eventId = eventId + self.session = session self.loadData() self.listenToDataUpdate() @@ -140,6 +142,16 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } + // The server does not support support reaction yet + // Use a fallback mechanism + // TODO: To remove once the feature has landed on matrix.org homeserver + if let mxError = MXError(nsError: error) { + if mxError.errcode == kMXErrCodeStringUnrecognized { + sself.reactUsingHack(withReaction: reaction) + return + } + } + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) }) } else { @@ -197,4 +209,37 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { self.react(withReaction: unreaction, selected: false) } } + + /// reactUsingHack directly sends a `m.reaction` room message instead of using the `/send_relation` api. + /// + /// TODO: To remove once the feature has landed on matrix.org homeserver + /// + /// - Parameter reaction: the reaction + private func reactUsingHack(withReaction reaction: ReactionsMenuReaction) { + print("[ReactionsMenuViewModel] reactUsingHack") + + let reactionContent = [ + "m.relates_to": [ + "rel_type": "m.annotation", + "event_id": self.eventId, + "key": reaction.rawValue] + ] + + var nilEvent: MXEvent? + let room = self.session.room(withRoomId: self.roomId) + room?.sendEvent(.reaction, content: reactionContent, localEcho: &nilEvent, completion: { [weak self] ( completion) in + guard let sself = self else { + return + } + switch completion { + case .success: + print("[ReactionsMenuViewModel] reactUsingHack: Success") + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: true) + + case.failure(let error): + print("[ReactionsMenuViewModel] reactUsingHack: Error: \(error)") + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) + } + }) + } } diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift index 2a4c39a53e..7bf3be01bb 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift @@ -105,7 +105,7 @@ final class RoomContextualMenuPresenter: NSObject { func showReactionsMenu(forEvent eventId: String, inRoom roomId: String, session: MXSession, aroundFrame frame: CGRect) { - let reactionsMenuViewModel = ReactionsMenuViewModel(aggregations: session.aggregations, roomId: roomId, eventId: eventId) + let reactionsMenuViewModel = ReactionsMenuViewModel(aggregations: session.aggregations, roomId: roomId, eventId: eventId, session: session) self.roomContextualMenuViewController?.showReactionsMenu(withViewModel: reactionsMenuViewModel, aroundFrame: frame) } } From 2b7b2c5a6c463b0ac889815a8f2ea9ff4e9b85ba Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 20 May 2019 17:00:57 +0200 Subject: [PATCH 058/266] Reactions: Use the hack like on riot-android and riot-web if the server has not yet the aggregations API Fix Steve's comment --- .../ReactionsMenu/ReactionsMenuViewModel.swift | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 58e05e0111..593e2f79a8 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -216,6 +216,11 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { /// /// - Parameter reaction: the reaction private func reactUsingHack(withReaction reaction: ReactionsMenuReaction) { + guard let room = self.session.room(withRoomId: self.roomId) else { + print("[ReactionsMenuViewModel] reactUsingHack: Error: Unknown room: \(self.roomId)") + return + } + print("[ReactionsMenuViewModel] reactUsingHack") let reactionContent = [ @@ -226,8 +231,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { ] var nilEvent: MXEvent? - let room = self.session.room(withRoomId: self.roomId) - room?.sendEvent(.reaction, content: reactionContent, localEcho: &nilEvent, completion: { [weak self] ( completion) in + room.sendEvent(.reaction, content: reactionContent, localEcho: &nilEvent, completion: { [weak self] ( completion) in guard let sself = self else { return } From 6866fe8e2c2a41a431e089040994a45bee25f518 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 20 May 2019 17:31:25 +0200 Subject: [PATCH 059/266] Reactions: Add a labs setting #2441 --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 +++ Riot/Managers/Settings/RiotSettings.swift | 9 ++++++ .../Modules/Room/DataSources/RoomDataSource.m | 1 + Riot/Modules/Room/RoomViewController.m | 2 +- .../Modules/Settings/SettingsViewController.m | 32 ++++++++++++++++++- 6 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 13db82f5c9..31ca8d5bf4 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -406,6 +406,7 @@ "settings_labs_room_members_lazy_loading" = "Lazy load rooms members"; "settings_labs_room_members_lazy_loading_error_message" = "Your homeserver does not support lazy loading of room members yet. Try later."; "settings_labs_create_conference_with_jitsi" = "Create conference calls with jitsi"; +"settings_labs_message_reaction" = "React to messages with emoji"; "settings_version" = "Version %@"; "settings_olm_version" = "Olm Version %@"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 44c885e30a..6bfea7cf90 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2474,6 +2474,10 @@ internal enum VectorL10n { internal static var settingsLabsE2eEncryptionPromptMessage: String { return VectorL10n.tr("Vector", "settings_labs_e2e_encryption_prompt_message") } + /// React to messages with emoji + internal static var settingsLabsMessageReaction: String { + return VectorL10n.tr("Vector", "settings_labs_message_reaction") + } /// Lazy load rooms members internal static var settingsLabsRoomMembersLazyLoading: String { return VectorL10n.tr("Vector", "settings_labs_room_members_lazy_loading") diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 6db8ec7b3f..09d5220ff7 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -26,6 +26,7 @@ final class RiotSettings: NSObject { static let enableCrashReport = "enableCrashReport" static let enableRageShake = "enableRageShake" static let createConferenceCallsWithJitsi = "createConferenceCallsWithJitsi" + static let messageReaction = "messageReaction" static let userInterfaceTheme = "userInterfaceTheme" static let notificationsShowDecryptedContent = "showDecryptedContent" static let pinRoomsWithMissedNotifications = "pinRoomsWithMissedNotif" @@ -119,4 +120,12 @@ final class RiotSettings: NSObject { UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.createConferenceCallsWithJitsi) } } + + var messageReaction: Bool { + get { + return UserDefaults.standard.bool(forKey: UserDefaultsKeys.messageReaction) + } set { + UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.messageReaction) + } + } } diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 1dee9902f1..02858a7aaa 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -59,6 +59,7 @@ - (instancetype)initWithRoomId:(NSString *)roomId andMatrixSession:(MXSession *) self.markTimelineInitialEvent = NO; self.showBubbleDateTimeOnSelection = YES; + self.showReactions = RiotSettings.shared.messageReaction; // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 6278e95231..fa8320696c 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5077,7 +5077,7 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce [self contextualMenuAnimationCompletionAfterBeingShown:YES]; }]; - if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if (RiotSettings.shared.messageReaction && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) { // Note: For the moment, we use the frame of the cell instead of the component // From UI perpective, that means the menu will be around a paragraph instead of a message diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index cbda06f84f..b397757e1c 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -119,7 +119,8 @@ enum { LABS_USE_ROOM_MEMBERS_LAZY_LOADING_INDEX = 0, - LABS_USE_JITSI_WIDGET_INDEX = 0, + LABS_USE_JITSI_WIDGET_INDEX, + LABS_USE_MESSAGE_REACTION_INDEX, LABS_CRYPTO_INDEX, LABS_COUNT }; @@ -2136,6 +2137,19 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N cell = labelAndSwitchCell; } + else if (row == LABS_USE_MESSAGE_REACTION_INDEX) + { + MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; + + labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_message_reaction", @"Vector", nil); + labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.messageReaction; + labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; + labelAndSwitchCell.mxkSwitch.enabled = YES; + + [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleMessageReaction:) forControlEvents:UIControlEventTouchUpInside]; + + cell = labelAndSwitchCell; + } else if (row == LABS_CRYPTO_INDEX) { MXSession* session = [AppDelegate theDelegate].mxSessions[0]; @@ -3035,6 +3049,22 @@ - (void)toggleJitsiForConference:(id)sender } } +- (void)toggleMessageReaction:(id)sender +{ + if (sender && [sender isKindOfClass:UISwitch.class]) + { + UISwitch *switchButton = (UISwitch*)sender; + + RiotSettings.shared.messageReaction = switchButton.isOn; + + // Reset cached room data sources + MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mainSession]; + [roomDataSourceManager reset]; + + [self.tableView reloadData]; + } +} + - (void)toggleLabsEndToEndEncryption:(id)sender { if (sender && [sender isKindOfClass:UISwitch.class]) From d72f2eb7626f86dfce1d92d9abf7ba3e630c6357 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 20 May 2019 19:40:52 +0200 Subject: [PATCH 060/266] Reactions: Remove the send reaction hack as it is now done in the SDK --- .../ReactionsMenuViewModel.swift | 51 +------------------ .../RoomContextualMenuPresenter.swift | 2 +- 2 files changed, 2 insertions(+), 51 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 593e2f79a8..93ba3cb413 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -21,7 +21,6 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { // MARK: - Properties // MARK: Private - private let session: MXSession // TODO: To remove. Only required for reactUsingHack() private let aggregations: MXAggregations private let roomId: String private let eventId: String @@ -38,11 +37,10 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { // MARK: - Setup - init(aggregations: MXAggregations, roomId: String, eventId: String, session: MXSession) { + init(aggregations: MXAggregations, roomId: String, eventId: String) { self.aggregations = aggregations self.roomId = roomId self.eventId = eventId - self.session = session self.loadData() self.listenToDataUpdate() @@ -142,16 +140,6 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - // The server does not support support reaction yet - // Use a fallback mechanism - // TODO: To remove once the feature has landed on matrix.org homeserver - if let mxError = MXError(nsError: error) { - if mxError.errcode == kMXErrCodeStringUnrecognized { - sself.reactUsingHack(withReaction: reaction) - return - } - } - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) }) } else { @@ -209,41 +197,4 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { self.react(withReaction: unreaction, selected: false) } } - - /// reactUsingHack directly sends a `m.reaction` room message instead of using the `/send_relation` api. - /// - /// TODO: To remove once the feature has landed on matrix.org homeserver - /// - /// - Parameter reaction: the reaction - private func reactUsingHack(withReaction reaction: ReactionsMenuReaction) { - guard let room = self.session.room(withRoomId: self.roomId) else { - print("[ReactionsMenuViewModel] reactUsingHack: Error: Unknown room: \(self.roomId)") - return - } - - print("[ReactionsMenuViewModel] reactUsingHack") - - let reactionContent = [ - "m.relates_to": [ - "rel_type": "m.annotation", - "event_id": self.eventId, - "key": reaction.rawValue] - ] - - var nilEvent: MXEvent? - room.sendEvent(.reaction, content: reactionContent, localEcho: &nilEvent, completion: { [weak self] ( completion) in - guard let sself = self else { - return - } - switch completion { - case .success: - print("[ReactionsMenuViewModel] reactUsingHack: Success") - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: true) - - case.failure(let error): - print("[ReactionsMenuViewModel] reactUsingHack: Error: \(error)") - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) - } - }) - } } diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift index 7bf3be01bb..2a4c39a53e 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift @@ -105,7 +105,7 @@ final class RoomContextualMenuPresenter: NSObject { func showReactionsMenu(forEvent eventId: String, inRoom roomId: String, session: MXSession, aroundFrame frame: CGRect) { - let reactionsMenuViewModel = ReactionsMenuViewModel(aggregations: session.aggregations, roomId: roomId, eventId: eventId, session: session) + let reactionsMenuViewModel = ReactionsMenuViewModel(aggregations: session.aggregations, roomId: roomId, eventId: eventId) self.roomContextualMenuViewController?.showReactionsMenu(withViewModel: reactionsMenuViewModel, aroundFrame: frame) } } From 86808b16e23793e706ead68034b6d92e76fcd30c Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 20 May 2019 19:48:29 +0200 Subject: [PATCH 061/266] Reactions menu: Do not notify delegate if reaction requires an unreaction This avoids to call the delegate twice. Note: In a short future, we will no more have those 3 state buttons --- .../ReactionsMenuViewModel.swift | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 93ba3cb413..a4158f36fa 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -123,7 +123,13 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { } } - private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool) { + private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool, notifyDelegate: Bool = true) { + + // If required, unreact first + if selected { + self.ensure3StateButtons(withReaction: reaction) + } + if selected { self.aggregations.sendReaction(reaction.rawValue, toEvent: self.eventId, inRoom: self.roomId, success: {[weak self] _ in @@ -131,7 +137,9 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: true) + if notifyDelegate { + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: true) + } }, failure: {[weak self] (error) in print("[ReactionsMenuViewModel] react: Error: \(error)") @@ -140,7 +148,9 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) + if notifyDelegate { + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) + } }) } else { @@ -150,7 +160,9 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: false) + if notifyDelegate { + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: false) + } }, failure: {[weak self] (error) in print("[ReactionsMenuViewModel] react: Error: \(error)") @@ -159,14 +171,14 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: false) + if notifyDelegate { + sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: false) + } }) } - self.coordinatorDelegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: !selected) - - if selected { - self.ensure3StateButtons(withReaction: reaction) + if notifyDelegate { + self.coordinatorDelegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: !selected) } } @@ -194,7 +206,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { } if let unreaction = unreaction { - self.react(withReaction: unreaction, selected: false) + self.react(withReaction: unreaction, selected: false, notifyDelegate: false) } } } From aadac45de68840d09a47cc511d1e13e289839172 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 20 May 2019 20:41:05 +0200 Subject: [PATCH 062/266] Reactions menu: Do not notify delegate if reaction requires an unreaction Fix Steve's remark --- .../ReactionsMenuViewModel.swift | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index a4158f36fa..8fbf061065 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -72,7 +72,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - self.react(withReaction: theReaction, selected: theNewState) + self.react(withReaction: theReaction, selected: theNewState, delegate: self.coordinatorDelegate) } // MARK: - Private @@ -123,7 +123,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { } } - private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool, notifyDelegate: Bool = true) { + private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool, delegate: ReactionsMenuViewModelCoordinatorDelegate? = nil) { // If required, unreact first if selected { @@ -137,9 +137,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - if notifyDelegate { - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: true) - } + delegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: true) }, failure: {[weak self] (error) in print("[ReactionsMenuViewModel] react: Error: \(error)") @@ -148,9 +146,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - if notifyDelegate { - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) - } + delegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) }) } else { @@ -160,9 +156,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - if notifyDelegate { - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: false) - } + delegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: false) }, failure: {[weak self] (error) in print("[ReactionsMenuViewModel] react: Error: \(error)") @@ -171,15 +165,11 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - if notifyDelegate { - sself.coordinatorDelegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: false) - } + delegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: false) }) } - if notifyDelegate { - self.coordinatorDelegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: !selected) - } + delegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: !selected) } // We can like, dislike, be indifferent but we cannot like & dislike at the same time @@ -206,7 +196,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { } if let unreaction = unreaction { - self.react(withReaction: unreaction, selected: false, notifyDelegate: false) + self.react(withReaction: unreaction, selected: false) } } } From 5b9ffd5b0fe8649e1384b83f0b9d9f17eb820298 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 20 May 2019 21:49:27 +0200 Subject: [PATCH 063/266] Theme: Add reaction button colors and update header background color. --- Riot/Managers/Theme/Theme.swift | 3 +++ Riot/Managers/Theme/Themes/DarkTheme.swift | 7 +++++-- Riot/Managers/Theme/Themes/DefaultTheme.swift | 5 ++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Riot/Managers/Theme/Theme.swift b/Riot/Managers/Theme/Theme.swift index c8a10674aa..c913ffd654 100644 --- a/Riot/Managers/Theme/Theme.swift +++ b/Riot/Managers/Theme/Theme.swift @@ -46,6 +46,9 @@ import UIKit var noticeColor: UIColor { get } var noticeSecondaryColor: UIColor { get } + + var reactionButtonSelectedBackgroundColor: UIColor { get } + var reactionButtonSelectedBorderColor: UIColor { get } /// Color for errors or warnings var warningColor: UIColor { get } diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift index 3c939e2c10..1033dae9d5 100644 --- a/Riot/Managers/Theme/Themes/DarkTheme.swift +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -30,8 +30,8 @@ class DarkTheme: NSObject, Theme { var searchBackgroundColor: UIColor = UIColor(rgb: 0x181B21) var searchPlaceholderColor: UIColor = UIColor(rgb: 0x61708B) - var headerBackgroundColor: UIColor = UIColor(rgb: 0x15171B) - var headerBorderColor: UIColor = UIColor(rgb: 0x22262E) + var headerBackgroundColor: UIColor = UIColor(rgb: 0x22262E) + var headerBorderColor: UIColor = UIColor(rgb: 0x181B21) var headerTextPrimaryColor: UIColor = UIColor(rgb: 0xA1B2D1) var headerTextSecondaryColor: UIColor = UIColor(rgb: 0xC8C8CD) @@ -45,6 +45,9 @@ class DarkTheme: NSObject, Theme { var noticeColor: UIColor = UIColor(rgb: 0xFF4B55) var noticeSecondaryColor: UIColor = UIColor(rgb: 0x61708B) + + var reactionButtonSelectedBackgroundColor: UIColor = UIColor(rgb: 0x1F6954) + var reactionButtonSelectedBorderColor: UIColor = UIColor(rgb: 0x03B381) var warningColor: UIColor = UIColor(rgb: 0xFF4B55) diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift index 19b1ba1094..87db69334f 100644 --- a/Riot/Managers/Theme/Themes/DefaultTheme.swift +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -30,7 +30,7 @@ class DefaultTheme: NSObject, Theme { var searchBackgroundColor: UIColor = UIColor(rgb: 0xFFFFFF) var searchPlaceholderColor: UIColor = UIColor(rgb: 0x61708B) - var headerBackgroundColor: UIColor = UIColor(rgb: 0xF2F5F8) + var headerBackgroundColor: UIColor = UIColor(rgb: 0xF3F8FD) var headerBorderColor: UIColor = UIColor(rgb: 0xE9EDF1) var headerTextPrimaryColor: UIColor = UIColor(rgb: 0x61708B) var headerTextSecondaryColor: UIColor = UIColor(rgb: 0xC8C8CD) @@ -45,6 +45,9 @@ class DefaultTheme: NSObject, Theme { var noticeColor: UIColor = UIColor(rgb: 0xFF4B55) var noticeSecondaryColor: UIColor = UIColor(rgb: 0x61708B) + + var reactionButtonSelectedBackgroundColor: UIColor = UIColor(rgb: 0xE9FFF9) + var reactionButtonSelectedBorderColor: UIColor = UIColor(rgb: 0x03B381) var warningColor: UIColor = UIColor(rgb: 0xFF4B55) From 2d118853a7df107f14dec099daf46c8c0439d6cd Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 20 May 2019 21:49:57 +0200 Subject: [PATCH 064/266] Create AutosizedCollectionView a convenient UICollectionView that makes dynamic sizing easier when using Auto Layout --- .../AutosizedCollectionView.swift | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Riot/Modules/Common/CollectionView/AutosizedCollectionView.swift diff --git a/Riot/Modules/Common/CollectionView/AutosizedCollectionView.swift b/Riot/Modules/Common/CollectionView/AutosizedCollectionView.swift new file mode 100644 index 0000000000..10cdbf75c7 --- /dev/null +++ b/Riot/Modules/Common/CollectionView/AutosizedCollectionView.swift @@ -0,0 +1,31 @@ +/* + Copyright 2019 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 + +/// AutosizedCollectionView is a convenient UICollectionView that makes dynamic sizing easier when using Auto Layout +class AutosizedCollectionView: UICollectionView { + + override var contentSize: CGSize { + didSet { + self.invalidateIntrinsicContentSize() + } + } + + override var intrinsicContentSize: CGSize { + return self.contentSize + } +} From e5d8910a92c7be3d49ebf502d9a5ae02f6d48630 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 20 May 2019 21:51:24 +0200 Subject: [PATCH 065/266] Create BubbleReactionViewCell --- .../BubbleReactionViewCell.swift | 107 ++++++++++++++++++ .../BubbleReactionViewCell.xib | 69 +++++++++++ .../BubbleReactionViewData.swift | 23 ++++ 3 files changed, 199 insertions(+) create mode 100644 Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift create mode 100644 Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib create mode 100644 Riot/Modules/Room/BubbleReactions/BubbleReactionViewData.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift new file mode 100644 index 0000000000..34561134d4 --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift @@ -0,0 +1,107 @@ +/* + Copyright 2019 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 +import Reusable + +final class BubbleReactionViewCell: UICollectionViewCell, NibReusable, Themable { + + // MARK: - Constants + + private enum Constants { + static let selectedBorderWidth: CGFloat = 1.0 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var reactionBackgroundView: UIView! + @IBOutlet private weak var emojiLabel: UILabel! + @IBOutlet private weak var countLabel: UILabel! + + // MARK: Private + + private var theme: Theme? + + // MARK: Public + + private var isReactionSelected: Bool = false + + // MARK: - Life cycle + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + + self.reactionBackgroundView.layer.masksToBounds = true + } + + override func layoutSubviews() { + super.layoutSubviews() + + self.reactionBackgroundView.layer.cornerRadius = self.reactionBackgroundView.frame.size.height/2.0 + } + + override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { + if #available(iOS 12.0, *) { + /* + On iOS 12, there are issues with self-sizing cells as described in Apple release notes (https://developer.apple.com/documentation/ios_release_notes/ios_12_release_notes) : + "You might encounter issues with systemLayoutSizeFitting(_:) when using a UICollectionViewCell subclass that requires updateConstraints(). + (42138227) — Workaround: Don't call the cell's setNeedsUpdateConstraints() method unless you need to support live constraint changes. + If you need to support live constraint changes, call updateConstraintsIfNeeded() before calling systemLayoutSizeFitting(_:)." + */ + self.updateConstraintsIfNeeded() + } + return super.preferredLayoutAttributesFitting(layoutAttributes) + } + + // MARK: - Public + + func fill(viewData: BubbleReactionViewData) { + self.emojiLabel.text = viewData.emoji + self.countLabel.text = viewData.countString + self.isReactionSelected = viewData.isCurrentUserReacted + + self.updateViews() + } + + func update(theme: Theme) { + self.theme = theme + self.reactionBackgroundView.layer.borderColor = self.theme?.reactionButtonSelectedBorderColor.cgColor + self.countLabel.textColor = self.theme?.textPrimaryColor + self.updateViews() + } + + // MARK: - Private + + private func updateViews() { + + let reactionBackgroundColor: UIColor? + let reactionBackgroundBorderWidth: CGFloat + + if self.isReactionSelected { + reactionBackgroundColor = self.theme?.reactionButtonSelectedBackgroundColor + reactionBackgroundBorderWidth = Constants.selectedBorderWidth + } else { + reactionBackgroundColor = self.theme?.headerBackgroundColor + reactionBackgroundBorderWidth = 0.0 + } + + self.reactionBackgroundView.layer.borderWidth = reactionBackgroundBorderWidth + self.reactionBackgroundView.backgroundColor = reactionBackgroundColor + } +} diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib new file mode 100644 index 0000000000..35e41eec86 --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewData.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewData.swift new file mode 100644 index 0000000000..316f9c34bd --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewData.swift @@ -0,0 +1,23 @@ +/* + Copyright 2019 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 + +struct BubbleReactionViewData { + let emoji: String + let countString: String + let isCurrentUserReacted: Bool +} From 2caf4e326fdd562a7ee37443244c7da350675f99 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 20 May 2019 21:52:07 +0200 Subject: [PATCH 066/266] Create BubbleReactionsViewModel --- .../BubbleReactionsViewModel.swift | 66 +++++++++++++++++++ .../BubbleReactionsViewModelType.swift | 43 ++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift create mode 100644 Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift new file mode 100644 index 0000000000..339b009872 --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift @@ -0,0 +1,66 @@ +/* + Copyright 2019 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 + +@objc final class BubbleReactionsViewModel: NSObject, BubbleReactionsViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let aggregatedReactions: MXAggregatedReactions + private let reactionsViewData: [BubbleReactionViewData] + private let eventId: String + + // MARK: Public + + @objc weak var viewModelDelegate: BubbleReactionsViewModelDelegate? + weak var viewDelegate: BubbleReactionsViewModelViewDelegate? + + // MARK: - Setup + + @objc init(aggregatedReactions: MXAggregatedReactions, + eventId: String) { + self.aggregatedReactions = aggregatedReactions + self.eventId = eventId + + self.reactionsViewData = aggregatedReactions.reactions.map { (reactionCount) -> BubbleReactionViewData in + return BubbleReactionViewData(emoji: reactionCount.reaction, countString: "\(reactionCount.count)", isCurrentUserReacted: reactionCount.myUserHasReacted) + } + } + + // MARK: - Public + + func process(viewAction: BubbleReactionsViewAction) { + switch viewAction { + case .loadData: + self.viewDelegate?.bubbleReactionsViewModel(self, didUpdateViewState: .loaded(reactionsViewData: self.reactionsViewData)) + case .tapReaction(let index): + guard index < self.aggregatedReactions.reactions.count else { + return + } + let reactionCount = self.aggregatedReactions.reactions[index] + if reactionCount.myUserHasReacted { + self.viewModelDelegate?.bubbleReactionsViewModel(self, didRemoveReaction: reactionCount, forEventId: self.eventId) + } else { + self.viewModelDelegate?.bubbleReactionsViewModel(self, didAddReaction: reactionCount, forEventId: self.eventId) + } + case .addNewReaction: + break + } + } +} diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift new file mode 100644 index 0000000000..04a30726a8 --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift @@ -0,0 +1,43 @@ +/* + Copyright 2019 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 + +enum BubbleReactionsViewAction { + case loadData + case tapReaction(index: Int) + case addNewReaction +} + +enum BubbleReactionsViewState { + case loaded(reactionsViewData: [BubbleReactionViewData]) +} + +@objc protocol BubbleReactionsViewModelDelegate: class { + func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didAddReaction reactionCount: MXReactionCount, forEventId eventId: String) + func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didRemoveReaction reactionCount: MXReactionCount, forEventId eventId: String) +} + +protocol BubbleReactionsViewModelViewDelegate: class { + func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didUpdateViewState viewState: BubbleReactionsViewState) +} + +protocol BubbleReactionsViewModelType { + var viewModelDelegate: BubbleReactionsViewModelDelegate? { get set } + var viewDelegate: BubbleReactionsViewModelViewDelegate? { get set } + + func process(viewAction: BubbleReactionsViewAction) +} From 4ababf957ee9a455c91ee32123c0b5f7b275c21f Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 20 May 2019 21:54:23 +0200 Subject: [PATCH 067/266] Create BubbleReactionsView --- .../BubbleReactions/BubbleReactionsView.swift | 136 ++++++++++++++++++ .../BubbleReactions/BubbleReactionsView.xib | 46 ++++++ 2 files changed, 182 insertions(+) create mode 100644 Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift create mode 100644 Riot/Modules/Room/BubbleReactions/BubbleReactionsView.xib diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift new file mode 100644 index 0000000000..2e61c35e1a --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift @@ -0,0 +1,136 @@ +/* + Copyright 2019 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 MatrixSDK +import Reusable + +@objcMembers +final class BubbleReactionsView: UIView, NibOwnerLoadable { + + // MARK: - Constants + + private enum Constants { + static let minimumInteritemSpacing: CGFloat = 6.0 + static let minimumLineSpacing: CGFloat = 2.0 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var collectionView: UICollectionView! + + // MARK: Private + + private var reactionsViewData: [BubbleReactionViewData] = [] + private var theme: Theme? + + // MARK: Public + + // Do not use `BubbleReactionsViewModelType` here due to Objective-C incompatibily + var viewModel: BubbleReactionsViewModel? { + didSet { + self.viewModel?.viewDelegate = self + self.viewModel?.process(viewAction: .loadData) + } + } + + // MARK: - Setup + + private func commonInit() { + self.collectionView.isScrollEnabled = false + self.collectionView.delegate = self + self.collectionView.dataSource = self + + if let collectionViewFlowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout { + collectionViewFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize + collectionViewFlowLayout.minimumInteritemSpacing = Constants.minimumInteritemSpacing + collectionViewFlowLayout.minimumLineSpacing = Constants.minimumLineSpacing + } + + self.collectionView.register(cellType: BubbleReactionViewCell.self) + self.collectionView.reloadData() + } + + convenience init() { + self.init(frame: CGRect.zero) + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + self.loadNibContent() + self.commonInit() + } + + override init(frame: CGRect) { + super.init(frame: frame) + self.loadNibContent() + self.commonInit() + } + + // MARK: - Public + + func update(theme: Theme) { + self.theme = theme + self.collectionView.reloadData() + } + + func fill(reactionsViewData: [BubbleReactionViewData]) { + self.reactionsViewData = reactionsViewData + self.collectionView.reloadData() + } +} + +// MARK: - UICollectionViewDataSource +extension BubbleReactionsView: UICollectionViewDataSource { + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return self.reactionsViewData.count + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell: BubbleReactionViewCell = collectionView.dequeueReusableCell(for: indexPath) + + if let theme = self.theme { + cell.update(theme: theme) + } + + let viewData = self.reactionsViewData[indexPath.row] + cell.fill(viewData: viewData) + + return cell + } +} + +// MARK: - UICollectionViewDelegate +extension BubbleReactionsView: UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + self.viewModel?.process(viewAction: .tapReaction(index: indexPath.row)) + } +} + +// MARK: - BubbleReactionsViewModelViewDelegate +extension BubbleReactionsView: BubbleReactionsViewModelViewDelegate { + + func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didUpdateViewState viewState: BubbleReactionsViewState) { + switch viewState { + case .loaded(reactionsViewData: let reactionsViewData): + self.fill(reactionsViewData: reactionsViewData) + } + } +} diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.xib b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.xib new file mode 100644 index 0000000000..7c5cedb2ad --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.xib @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 6861e80a98b9c8fac1aa52cc3259d8598554ed01 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 20 May 2019 21:54:32 +0200 Subject: [PATCH 068/266] Update pbxproj --- Riot.xcodeproj/project.pbxproj | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 3bc44f0de2..b33a4fdc69 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -194,6 +194,14 @@ B169331720F3CBE000746532 /* RecentCellData.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932F920F3C51900746532 /* RecentCellData.m */; }; B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */; }; B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1798301211B13B3001FD722 /* OnBoardingManager.swift */; }; + B1963B2B228F1C4900CBA17F /* BubbleReactionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B25228F1C4800CBA17F /* BubbleReactionsView.swift */; }; + B1963B2C228F1C4900CBA17F /* BubbleReactionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1963B26228F1C4800CBA17F /* BubbleReactionViewCell.xib */; }; + B1963B2D228F1C4900CBA17F /* BubbleReactionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B27228F1C4800CBA17F /* BubbleReactionsViewModel.swift */; }; + B1963B2E228F1C4900CBA17F /* BubbleReactionViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B28228F1C4800CBA17F /* BubbleReactionViewData.swift */; }; + B1963B2F228F1C4900CBA17F /* BubbleReactionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B29228F1C4800CBA17F /* BubbleReactionViewCell.swift */; }; + B1963B30228F1C4900CBA17F /* BubbleReactionsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1963B2A228F1C4800CBA17F /* BubbleReactionsView.xib */; }; + B1963B32228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B31228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift */; }; + B1963B3822933BC800CBA17F /* AutosizedCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B3722933BC800CBA17F /* AutosizedCollectionView.swift */; }; B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */; }; B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */; }; B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A5B33D227ADF2A004CBA85 /* UIImage.swift */; }; @@ -784,6 +792,14 @@ B169331320F3CAFC00746532 /* PublicRoomsDirectoryDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicRoomsDirectoryDataSource.h; sourceTree = ""; }; B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDPRConsentViewController.swift; sourceTree = ""; }; B1798301211B13B3001FD722 /* OnBoardingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnBoardingManager.swift; sourceTree = ""; }; + B1963B25228F1C4800CBA17F /* BubbleReactionsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionsView.swift; sourceTree = ""; }; + B1963B26228F1C4800CBA17F /* BubbleReactionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BubbleReactionViewCell.xib; sourceTree = ""; }; + B1963B27228F1C4800CBA17F /* BubbleReactionsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionsViewModel.swift; sourceTree = ""; }; + B1963B28228F1C4800CBA17F /* BubbleReactionViewData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionViewData.swift; sourceTree = ""; }; + B1963B29228F1C4800CBA17F /* BubbleReactionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionViewCell.swift; sourceTree = ""; }; + B1963B2A228F1C4800CBA17F /* BubbleReactionsView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BubbleReactionsView.xib; sourceTree = ""; }; + B1963B31228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BubbleReactionsViewModelType.swift; sourceTree = ""; }; + B1963B3722933BC800CBA17F /* AutosizedCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AutosizedCollectionView.swift; sourceTree = ""; }; B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorType.swift; sourceTree = ""; }; B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinator.swift; sourceTree = ""; }; B1A5B33D227ADF2A004CBA85 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; }; @@ -1936,6 +1952,29 @@ path = OnBoarding; sourceTree = ""; }; + B1963B24228F1C4800CBA17F /* BubbleReactions */ = { + isa = PBXGroup; + children = ( + B1963B31228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift */, + B1963B27228F1C4800CBA17F /* BubbleReactionsViewModel.swift */, + B1963B25228F1C4800CBA17F /* BubbleReactionsView.swift */, + B1963B2A228F1C4800CBA17F /* BubbleReactionsView.xib */, + B1963B28228F1C4800CBA17F /* BubbleReactionViewData.swift */, + B1963B29228F1C4800CBA17F /* BubbleReactionViewCell.swift */, + B1963B26228F1C4800CBA17F /* BubbleReactionViewCell.xib */, + ); + path = BubbleReactions; + sourceTree = ""; + }; + B1963B3622933B9500CBA17F /* CollectionView */ = { + isa = PBXGroup; + children = ( + B1963B3722933BC800CBA17F /* AutosizedCollectionView.swift */, + ); + name = CollectionView; + path = Riot/Modules/Common/CollectionView; + sourceTree = SOURCE_ROOT; + }; B1B5567620EE6C4C00210D55 /* Modules */ = { isa = PBXGroup; children = ( @@ -2050,6 +2089,7 @@ B1B556A420EE6C4C00210D55 /* Members */, B1B5569020EE6C4C00210D55 /* Settings */, B1C562D7228C0B4C0037F12A /* ContextualMenu */, + B1963B24228F1C4800CBA17F /* BubbleReactions */, ); path = Room; sourceTree = ""; @@ -2231,6 +2271,7 @@ B1B556CD20EE6C4C00210D55 /* Common */ = { isa = PBXGroup; children = ( + B1963B3622933B9500CBA17F /* CollectionView */, B1B556CE20EE6C4C00210D55 /* WebViewController */, B1B556D120EE6C4C00210D55 /* NavigationController */, B1B556D420EE6C4C00210D55 /* SegmentedViewController */, @@ -3537,6 +3578,7 @@ B1B5593920EF7BAC00210D55 /* TableViewCellWithCheckBoxes.xib in Resources */, B1B557C120EF5B4500210D55 /* DisabledRoomInputToolbarView.xib in Resources */, 32891D6C2264CBA300C82226 /* SimpleScreenTemplateViewController.storyboard in Resources */, + B1963B2C228F1C4900CBA17F /* BubbleReactionViewCell.xib in Resources */, B1664DA320F4F96200808783 /* Vector.strings in Resources */, B1B557C720EF5CD400210D55 /* DirectoryServerDetailTableViewCell.xib in Resources */, B1B5582620EF638A00210D55 /* RoomMemberTitleView.xib in Resources */, @@ -3588,6 +3630,7 @@ B1B5590F20EF782800210D55 /* TableViewCellWithPhoneNumberTextField.xib in Resources */, B1B5578520EF564900210D55 /* GroupTableViewCellWithSwitch.xib in Resources */, B1B557B320EF5AEF00210D55 /* EventDetailsView.xib in Resources */, + B1963B30228F1C4900CBA17F /* BubbleReactionsView.xib in Resources */, B1B557DD20EF5FBB00210D55 /* FilesSearchTableViewCell.xib in Resources */, B1B5590320EF768F00210D55 /* RoomSelectedStickerBubbleCell.xib in Resources */, 3232ABB62257BE6400AD6A5C /* DeviceVerificationVerifyViewController.storyboard in Resources */, @@ -3882,6 +3925,7 @@ B1098BDF21ECE09F000DDA48 /* Strings.swift in Sources */, B1B558C420EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, 3232ABC022594C0900AD6A5C /* VerifyEmojiCollectionViewCell.swift in Sources */, + B1963B2E228F1C4900CBA17F /* BubbleReactionViewData.swift in Sources */, B1B5572F20EE6C4D00210D55 /* ReadReceiptsViewController.m in Sources */, B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */, B169330B20F3CA3A00746532 /* Contact.m in Sources */, @@ -3914,6 +3958,7 @@ B1B5593B20EF7BAC00210D55 /* TableViewCellWithCheckBoxAndLabel.m in Sources */, B1B5581A20EF625800210D55 /* ExpandedRoomTitleView.m in Sources */, B1107EC82200B0720038014B /* KeyBackupRecoverSuccessViewController.swift in Sources */, + B1963B2F228F1C4900CBA17F /* BubbleReactionViewCell.swift in Sources */, B1B558E920EF768F00210D55 /* RoomSelectedStickerBubbleCell.m in Sources */, B1B558DF20EF768F00210D55 /* RoomOutgoingTextMsgWithoutSenderInfoBubbleCell.m in Sources */, F083BE041E7009ED00A9B29C /* Tools.m in Sources */, @@ -3949,6 +3994,7 @@ 32F6B96E2270623100BBA352 /* DeviceVerificationDataLoadingViewModelType.swift in Sources */, B1B5592C20EF7A5D00210D55 /* TableViewCellWithButton.m in Sources */, 32242F1421E8FBA900725742 /* DefaultTheme.swift in Sources */, + B1963B2D228F1C4900CBA17F /* BubbleReactionsViewModel.swift in Sources */, 32242F1321E8FBA900725742 /* Theme.swift in Sources */, B1B5582520EF638A00210D55 /* RoomMemberTitleView.m in Sources */, B1B5582C20EF666100210D55 /* DirectoryRecentTableViewCell.m in Sources */, @@ -4029,6 +4075,7 @@ B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */, B1B5573520EE6C4D00210D55 /* GroupDetailsViewController.m in Sources */, B10B3B5B2201DD740072C76B /* KeyBackupBannerCell.swift in Sources */, + B1963B32228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift in Sources */, B1098BFA21ECFE65000DDA48 /* KeyBackupSetupPassphraseViewModel.swift in Sources */, B1B5575220EE6C4D00210D55 /* RoomKeyRequestViewController.m in Sources */, F083BD1E1E7009ED00A9B29C /* AppDelegate.m in Sources */, @@ -4082,8 +4129,10 @@ B110872621F098F0003554A5 /* ActivityIndicatorView.swift in Sources */, B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */, B1E5368D21FB7245001F3AFF /* KeyBackupRecoverFromPassphraseViewController.swift in Sources */, + B1963B3822933BC800CBA17F /* AutosizedCollectionView.swift in Sources */, B169330320F3C98900746532 /* RoomBubbleCellData.m in Sources */, B1B557CC20EF5D8000210D55 /* DirectoryServerTableViewCell.m in Sources */, + B1963B2B228F1C4900CBA17F /* BubbleReactionsView.swift in Sources */, B1B5575C20EE6C4D00210D55 /* DirectoryViewController.m in Sources */, B1B558BD20EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderNameBubbleCell.m in Sources */, B1B5577020EE702800210D55 /* WidgetPickerViewController.m in Sources */, From 75d7b26bfc8142160c017904a658fd72d1c99ecd Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 20 May 2019 21:57:29 +0200 Subject: [PATCH 069/266] RoomBubbleCellData: Improve reactions view vertical space calculation --- .../Room/CellData/RoomBubbleCellData.m | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index df44185b8a..bd7aef55a3 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -22,6 +22,8 @@ #import "AvatarGenerator.h" #import "Tools.h" +#import "Riot-Swift.h" + static NSAttributedString *timestampVerticalWhitespace = nil; static NSAttributedString *readReceiptVerticalWhitespace = nil; @@ -357,11 +359,28 @@ - (void)addVerticalWhitespaceToString:(NSMutableAttributedString *)attributedStr { // Add vertical whitespace in case of read receipts. NSUInteger reactionCount = self.reactions[eventId].reactions.count; + + MXAggregatedReactions *aggregatedReactions = self.reactions[eventId]; + if (reactionCount) { - // TODO: Set right height: 22 + 8 - // TODO: Set right dynamic line count: reactionCount / 6 - CGFloat height = (22 + 8) * ((reactionCount / 6) + 1); + CGSize fittingSize = UILayoutFittingCompressedSize; + fittingSize.width = self.maxTextViewWidth; + + static BubbleReactionsView *bubbleReactionsView; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bubbleReactionsView = [BubbleReactionsView new]; + }); + + bubbleReactionsView.frame = CGRectMake(0, 0, self.maxTextViewWidth, 1.0); + BubbleReactionsViewModel *viemModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:eventId]; + bubbleReactionsView.viewModel = viemModel; + [bubbleReactionsView layoutIfNeeded]; + + CGFloat height = [bubbleReactionsView systemLayoutSizeFittingSize:fittingSize].height; + [attributedString appendAttributedString:[RoomBubbleCellData verticalWhitespaceForHeight: height]]; } @@ -503,7 +522,7 @@ + (NSAttributedString *)verticalWhitespaceForHeight:(CGFloat)height return [[NSAttributedString alloc] initWithString:returnString attributes:@{NSForegroundColorAttributeName : [UIColor blackColor], - NSFontAttributeName: [UIFont systemFontOfSize:4]}]; + NSFontAttributeName: [UIFont systemFontOfSize:6]}]; } - (BOOL)hasSameSenderAsBubbleCellData:(id)bubbleCellData From d2cfe4891a45d34a019bb1e53ff2dad3a056f577 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 20 May 2019 21:59:18 +0200 Subject: [PATCH 070/266] RoomDataSource: Handle reactions display on bubble cell. --- .../Modules/Room/DataSources/RoomDataSource.m | 85 +++++++++++-------- 1 file changed, 51 insertions(+), 34 deletions(-) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 02858a7aaa..29cbe5cd80 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -27,7 +27,11 @@ #import "MXRoom+Riot.h" -@interface RoomDataSource() + +static CGFloat kBubbleReactionsViewLeftMargin = 55.0; +static CGFloat kBubbleReactionsViewRightMargin = 15.0; + +@interface RoomDataSource() { // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. id kThemeServiceDidChangeThemeNotificationObserver; @@ -275,6 +279,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N while (index--) { MXKRoomBubbleComponent *component = bubbleComponents[index]; + NSString *componentEventId = component.event.eventId; if (component.event.sentState != MXEventSentStateFailed) { @@ -379,54 +384,46 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N [NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, topConstraint, trailingConstraint]]; } } - - MXAggregatedReactions* reactions = cellData.reactions[component.event.eventId]; + + MXAggregatedReactions* reactions = cellData.reactions[componentEventId]; + if (reactions && !isCollapsableCellCollapsed) { - // TODO: Use final ReactionsView - UITextView* reactionsContainer = [UITextView new]; - reactionsContainer.translatesAutoresizingMaskIntoConstraints = NO; - [bubbleCell.contentView addSubview:reactionsContainer]; - - reactionsContainer.layer.borderColor = UIColor.orangeColor.CGColor; - reactionsContainer.layer.borderWidth = 1; - + BubbleReactionsViewModel *bubbleReactionsViewModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:reactions eventId:componentEventId]; + + BubbleReactionsView *reactionsView = [BubbleReactionsView new]; + reactionsView.viewModel = bubbleReactionsViewModel; + [reactionsView updateWithTheme:ThemeService.shared.theme]; + + bubbleReactionsViewModel.viewModelDelegate = self; + + reactionsView.translatesAutoresizingMaskIntoConstraints = NO; + [bubbleCell.contentView addSubview:reactionsView]; + if (!bubbleCell.tmpSubviews) { bubbleCell.tmpSubviews = [NSMutableArray array]; } - [bubbleCell.tmpSubviews addObject:reactionsContainer]; - + [bubbleCell.tmpSubviews addObject:reactionsView]; + // At the bottom, we have read receipts or nothing NSLayoutConstraint *bottomConstraint; if (avatarsContainer) { - bottomConstraint = [reactionsContainer.bottomAnchor constraintEqualToAnchor:avatarsContainer.topAnchor]; + bottomConstraint = [reactionsView.bottomAnchor constraintEqualToAnchor:avatarsContainer.topAnchor]; } else { - bottomConstraint = [reactionsContainer.bottomAnchor constraintEqualToAnchor:reactionsContainer.superview.topAnchor constant:bottomPositionY]; + bottomConstraint = [reactionsView.bottomAnchor constraintEqualToAnchor:reactionsView.superview.topAnchor constant:bottomPositionY]; } - - // TODO: To refine - CGFloat viewHeight = 22; - + // Force receipts container size [NSLayoutConstraint activateConstraints: - @[ - [reactionsContainer.leadingAnchor constraintEqualToAnchor:reactionsContainer.superview.leadingAnchor constant:50], - [reactionsContainer.trailingAnchor constraintEqualToAnchor:reactionsContainer.superview.trailingAnchor constant:-6], - [reactionsContainer.heightAnchor constraintEqualToConstant:viewHeight], - bottomConstraint - ]]; - - // TODO: To remove - NSMutableString *reactionsString = [NSMutableString string]; - for (MXReactionCount *reactionCount in reactions.reactions) - { - [reactionsString appendFormat:@"%@: %@ ", reactionCount.reaction, @(reactionCount.count)]; - } - reactionsContainer.text = reactionsString; + @[ + [reactionsView.leadingAnchor constraintEqualToAnchor:reactionsView.superview.leadingAnchor constant:kBubbleReactionsViewLeftMargin], + [reactionsView.trailingAnchor constraintEqualToAnchor:reactionsView.superview.trailingAnchor constant:-kBubbleReactionsViewRightMargin], + bottomConstraint + ]]; } // Check whether the read marker must be displayed here. @@ -439,7 +436,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N bubbleCell.bubbleOverlayContainer.userInteractionEnabled = NO; bubbleCell.bubbleOverlayContainer.hidden = NO; - if ([component.event.eventId isEqualToString:self.room.accountData.readMarkerEventId]) + if ([componentEventId isEqualToString:self.room.accountData.readMarkerEventId]) { bubbleCell.readMarkerView = [[UIView alloc] initWithFrame:CGRectMake(0, bottomPositionY - 2, bubbleCell.bubbleOverlayContainer.frame.size.width, 2)]; bubbleCell.readMarkerView.backgroundColor = ThemeService.shared.theme.tintColor; @@ -580,4 +577,24 @@ - (Widget *)jitsiWidget return jitsiWidget; } +#pragma mark - BubbleReactionsViewModelDelegate + +- (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didAddReaction:(MXReactionCount *)reactionCount forEventId:(NSString *)eventId +{ + [self.mxSession.aggregations sendReaction:reactionCount.reaction toEvent:eventId inRoom:self.roomId success:^(NSString * _Nonnull eventId) { + + } failure:^(NSError * _Nonnull error) { + NSLog(@"[MXKRoomDataSource] Fail to send reaction on eventId: %@", eventId); + }]; +} + +- (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didRemoveReaction:(MXReactionCount * _Nonnull)reactionCount forEventId:(NSString * _Nonnull)eventId +{ + [self.mxSession.aggregations unReactOnReaction:reactionCount.reaction toEvent:eventId inRoom:self.roomId success:^{ + + } failure:^(NSError * _Nonnull error) { + NSLog(@"[MXKRoomDataSource] Fail to unreact on eventId: %@", eventId); + }]; +} + @end From 3704c3325dd84a5594a3c3a8e83a8bab5044a6b8 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 20 May 2019 22:11:15 +0200 Subject: [PATCH 071/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 565ba5ebb9..6a65585fad 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,7 @@ Changes in 0.8.7 (2019-xx-xx) Improvements: * RoomVC: When replying, use a "Reply" button instead of "Send". * RoomVC: New message actions (#2394). + * Reactions: Display existing reactions below the message (#2396). Changes in 0.8.6 (2019-05-06) =============================================== From 3a52e17439b6677624d946e23b61a42fa1650061 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 21 May 2019 00:42:20 +0200 Subject: [PATCH 072/266] Add a UICollectionViewFlowLayout with left alignement pod --- Podfile | 1 + Riot.xcodeproj/project.pbxproj | 2 ++ 2 files changed, 3 insertions(+) diff --git a/Podfile b/Podfile index 280ff6c64c..59ed9a2fe1 100644 --- a/Podfile +++ b/Podfile @@ -79,6 +79,7 @@ abstract_target 'RiotPods' do target "Riot" do import_MatrixKit + pod 'DGCollectionViewLeftAlignFlowLayout', '~> 1.0.4' end target "RiotShareExtension" do diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index b33a4fdc69..542e7eb2d7 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -3677,6 +3677,7 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot-frameworks.sh", "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework", + "${BUILT_PRODUCTS_DIR}/DGCollectionViewLeftAlignFlowLayout/DGCollectionViewLeftAlignFlowLayout.framework", "${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework", "${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework", "${BUILT_PRODUCTS_DIR}/GBDeviceInfo/GBDeviceInfo.framework", @@ -3702,6 +3703,7 @@ ); outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DGCollectionViewLeftAlignFlowLayout.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTCoreText.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTFoundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GBDeviceInfo.framework", From 321811283a6683334303835c00bd0f358ffa1fd6 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 21 May 2019 00:43:10 +0200 Subject: [PATCH 073/266] BubbleReactionsView: Use a UICollectionViewFlowLayout subclass with left alignement. --- Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift index 2e61c35e1a..33eaf486c3 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift @@ -17,6 +17,7 @@ import Foundation import MatrixSDK import Reusable +import DGCollectionViewLeftAlignFlowLayout @objcMembers final class BubbleReactionsView: UIView, NibOwnerLoadable { @@ -55,6 +56,7 @@ final class BubbleReactionsView: UIView, NibOwnerLoadable { self.collectionView.isScrollEnabled = false self.collectionView.delegate = self self.collectionView.dataSource = self + self.collectionView.collectionViewLayout = DGCollectionViewLeftAlignFlowLayout() if let collectionViewFlowLayout = self.collectionView.collectionViewLayout as? UICollectionViewFlowLayout { collectionViewFlowLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize From 3117c4322c555caecfac4fa444216a3fa2771629 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 21 May 2019 01:09:42 +0200 Subject: [PATCH 074/266] Theme: Remove non needed reaction colors --- Riot/Managers/Theme/Theme.swift | 3 --- Riot/Managers/Theme/Themes/DarkTheme.swift | 5 +---- Riot/Managers/Theme/Themes/DefaultTheme.swift | 3 --- .../Room/BubbleReactions/BubbleReactionViewCell.swift | 4 ++-- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/Riot/Managers/Theme/Theme.swift b/Riot/Managers/Theme/Theme.swift index c913ffd654..c8a10674aa 100644 --- a/Riot/Managers/Theme/Theme.swift +++ b/Riot/Managers/Theme/Theme.swift @@ -46,9 +46,6 @@ import UIKit var noticeColor: UIColor { get } var noticeSecondaryColor: UIColor { get } - - var reactionButtonSelectedBackgroundColor: UIColor { get } - var reactionButtonSelectedBorderColor: UIColor { get } /// Color for errors or warnings var warningColor: UIColor { get } diff --git a/Riot/Managers/Theme/Themes/DarkTheme.swift b/Riot/Managers/Theme/Themes/DarkTheme.swift index 1033dae9d5..c98b27a245 100644 --- a/Riot/Managers/Theme/Themes/DarkTheme.swift +++ b/Riot/Managers/Theme/Themes/DarkTheme.swift @@ -39,15 +39,12 @@ class DarkTheme: NSObject, Theme { var textSecondaryColor: UIColor = UIColor(rgb: 0xA1B2D1) var tintColor: UIColor = UIColor(rgb: 0x03B381) - var tintBackgroundColor: UIColor = UIColor(rgb: 0xe9fff9) + var tintBackgroundColor: UIColor = UIColor(rgb: 0x1F6954) var unreadRoomIndentColor: UIColor = UIColor(rgb: 0x2E3648) var lineBreakColor: UIColor = UIColor(rgb: 0x61708B) var noticeColor: UIColor = UIColor(rgb: 0xFF4B55) var noticeSecondaryColor: UIColor = UIColor(rgb: 0x61708B) - - var reactionButtonSelectedBackgroundColor: UIColor = UIColor(rgb: 0x1F6954) - var reactionButtonSelectedBorderColor: UIColor = UIColor(rgb: 0x03B381) var warningColor: UIColor = UIColor(rgb: 0xFF4B55) diff --git a/Riot/Managers/Theme/Themes/DefaultTheme.swift b/Riot/Managers/Theme/Themes/DefaultTheme.swift index 87db69334f..7f154f232e 100644 --- a/Riot/Managers/Theme/Themes/DefaultTheme.swift +++ b/Riot/Managers/Theme/Themes/DefaultTheme.swift @@ -45,9 +45,6 @@ class DefaultTheme: NSObject, Theme { var noticeColor: UIColor = UIColor(rgb: 0xFF4B55) var noticeSecondaryColor: UIColor = UIColor(rgb: 0x61708B) - - var reactionButtonSelectedBackgroundColor: UIColor = UIColor(rgb: 0xE9FFF9) - var reactionButtonSelectedBorderColor: UIColor = UIColor(rgb: 0x03B381) var warningColor: UIColor = UIColor(rgb: 0xFF4B55) diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift index 34561134d4..3b3d46a03b 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift @@ -81,7 +81,7 @@ final class BubbleReactionViewCell: UICollectionViewCell, NibReusable, Themable func update(theme: Theme) { self.theme = theme - self.reactionBackgroundView.layer.borderColor = self.theme?.reactionButtonSelectedBorderColor.cgColor + self.reactionBackgroundView.layer.borderColor = self.theme?.tintColor.cgColor self.countLabel.textColor = self.theme?.textPrimaryColor self.updateViews() } @@ -94,7 +94,7 @@ final class BubbleReactionViewCell: UICollectionViewCell, NibReusable, Themable let reactionBackgroundBorderWidth: CGFloat if self.isReactionSelected { - reactionBackgroundColor = self.theme?.reactionButtonSelectedBackgroundColor + reactionBackgroundColor = self.theme?.tintBackgroundColor reactionBackgroundBorderWidth = Constants.selectedBorderWidth } else { reactionBackgroundColor = self.theme?.headerBackgroundColor From a3239fda92cd088e934124bca0dca5d613a287a6 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 21 May 2019 01:39:36 +0200 Subject: [PATCH 075/266] version++ --- Riot/SupportingFiles/Info.plist | 4 ++-- RiotShareExtension/SupportingFiles/Info.plist | 4 ++-- SiriIntents/Info.plist | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Riot/SupportingFiles/Info.plist b/Riot/SupportingFiles/Info.plist index f2e6a76216..bc44ef80da 100644 --- a/Riot/SupportingFiles/Info.plist +++ b/Riot/SupportingFiles/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.8.6 + 0.8.7 CFBundleSignature ???? CFBundleVersion - 0.8.6 + 0.8.7 ITSAppUsesNonExemptEncryption ITSEncryptionExportComplianceCode diff --git a/RiotShareExtension/SupportingFiles/Info.plist b/RiotShareExtension/SupportingFiles/Info.plist index e71361f1c5..4a353b6866 100644 --- a/RiotShareExtension/SupportingFiles/Info.plist +++ b/RiotShareExtension/SupportingFiles/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.8.6 + 0.8.7 CFBundleVersion - 0.8.6 + 0.8.7 NSExtension NSExtensionAttributes diff --git a/SiriIntents/Info.plist b/SiriIntents/Info.plist index dfd30fd667..9eb31162f8 100644 --- a/SiriIntents/Info.plist +++ b/SiriIntents/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.8.6 + 0.8.7 CFBundleVersion - 0.8.6 + 0.8.7 NSExtension NSExtensionAttributes From 98c95fb8490eb1dd6c07fcfecbe5be676d5f777c Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 21 May 2019 02:12:14 +0200 Subject: [PATCH 076/266] Reactions menu: Patch RoomContextualMenuPresenter while we have a retain cycle --- .../Room/ContextualMenu/RoomContextualMenuPresenter.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift index 2a4c39a53e..fa56d2de56 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift @@ -88,6 +88,10 @@ final class RoomContextualMenuPresenter: NSObject { let animationCompletionInstructions: (() -> Void) = { roomContextualMenuViewController.vc_removeFromParent() + + // TODO: To remove once the retain cycle caused by reactionsMenuViewModel is fixed + self.roomContextualMenuViewController = nil + completion?() } From b234a09bf12fa50b0e1a7a34c2b547968b36a294 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 16:17:55 +0200 Subject: [PATCH 077/266] RoomBubbleCellData: Add a property to display the timestamp of the selected component on the left when there is enough space. --- .../Room/CellData/RoomBubbleCellData.h | 5 +++++ .../Room/CellData/RoomBubbleCellData.m | 21 +++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.h b/Riot/Modules/Room/CellData/RoomBubbleCellData.h index 922fea0394..20e6c8b86f 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.h +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.h @@ -40,6 +40,11 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag) */ @property(nonatomic) BOOL showTimestampForSelectedComponent; +/** + Indicate true to display the timestamp of the selected component on the left when there is enough space (YES by default). + */ +@property(nonatomic) BOOL displayTimestampForSelectedComponentOnLeftWhenPossible; + /** The event id of the current selected event inside the bubble. Default is nil. */ diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index bd7aef55a3..bd6fad25a9 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -27,8 +27,19 @@ static NSAttributedString *timestampVerticalWhitespace = nil; static NSAttributedString *readReceiptVerticalWhitespace = nil; +@interface RoomBubbleCellData() + +@property(nonatomic, readonly) BOOL addVerticalWhitespaceForSelectedComponentTimestamp; + +@end + @implementation RoomBubbleCellData +- (BOOL)addVerticalWhitespaceForSelectedComponentTimestamp +{ + return self.showTimestampForSelectedComponent && !self.displayTimestampForSelectedComponentOnLeftWhenPossible; +} + #pragma mark - Override MXKRoomBubbleCellData - (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomState andRoomDataSource:(MXKRoomDataSource *)roomDataSource2 @@ -68,6 +79,8 @@ - (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomS // Reset attributedTextMessage to force reset MXKRoomCellData parameters self.attributedTextMessage = nil; + + self.displayTimestampForSelectedComponentOnLeftWhenPossible = YES; } return self; @@ -202,7 +215,7 @@ - (NSAttributedString*)refreshAttributedTextMessage } // Check whether the timestamp is displayed for this component, and check whether a vertical whitespace is required - if (((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName)) + if (((selectedComponentIndex == index && self.addVerticalWhitespaceForSelectedComponentTimestamp) || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName)) { currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; [currentAttributedTextMsg appendAttributedString:componentString]; @@ -237,7 +250,7 @@ - (NSAttributedString*)refreshAttributedTextMessage } // Check whether the timestamp is displayed - if ((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) + if ((selectedComponentIndex == index && self.addVerticalWhitespaceForSelectedComponentTimestamp) || lastMessageIndex == index) { [currentAttributedTextMsg appendAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; } @@ -290,7 +303,7 @@ - (void)refreshBubbleComponentsPosition NSInteger lastMessageIndex = self.containsLastMessage ? self.mostRecentComponentIndex : NSNotFound; // Check whether the timestamp is displayed for this first component, and check whether a vertical whitespace is required - if (((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName)) + if (((selectedComponentIndex == index && self.addVerticalWhitespaceForSelectedComponentTimestamp) || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName)) { attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; [attributedString appendAttributedString:component.attributedTextMessage]; @@ -318,7 +331,7 @@ - (void)refreshBubbleComponentsPosition { // Prepare its attributed string by considering potential vertical margin required to display timestamp. NSAttributedString *componentString; - if ((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) + if ((selectedComponentIndex == index && self.addVerticalWhitespaceForSelectedComponentTimestamp) || lastMessageIndex == index) { NSMutableAttributedString *componentAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; [componentAttributedString appendAttributedString:component.attributedTextMessage]; From 6419fd6ecca52df8e4b35ed8cb99d26d520e86ba Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 16:22:26 +0200 Subject: [PATCH 078/266] MXRoomBubbleTableViewCell: Display the timestamp of the selected component on the left when there is enough space. --- .../MXKRoomBubbleTableViewCell+Riot.m | 182 +++++++++++------- 1 file changed, 109 insertions(+), 73 deletions(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 019756683c..444278de6e 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -36,8 +36,6 @@ @implementation MXKRoomBubbleTableViewCell (Riot) - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex { - self.bubbleInfoContainer.hidden = NO; - MXKRoomBubbleComponent *component; NSArray *bubbleComponents = bubbleData.bubbleComponents; @@ -49,87 +47,125 @@ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex if (component && component.date) { - // Check whether this is the first displayed component. BOOL isFirstDisplayedComponent = (componentIndex == 0); - if ([bubbleData isKindOfClass:RoomBubbleCellData.class]) - { - isFirstDisplayedComponent = (componentIndex == ((RoomBubbleCellData*)bubbleData).oldestComponentIndex); - } + BOOL isLastMessageMostRecentComponent = NO; - CGFloat timeLabelPosX = self.bubbleInfoContainer.frame.size.width - VECTOR_ROOMBUBBLETABLEVIEWCELL_TIMELABEL_WIDTH; - CGFloat timeLabelPosY = isFirstDisplayedComponent ? 0 : component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant; - UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(timeLabelPosX, timeLabelPosY, VECTOR_ROOMBUBBLETABLEVIEWCELL_TIMELABEL_WIDTH , 18)]; + RoomBubbleCellData *roomBubbleCellData; - timeLabel.text = [bubbleData.eventFormatter timeStringFromDate:component.date]; - timeLabel.textAlignment = NSTextAlignmentRight; - timeLabel.textColor = ThemeService.shared.theme.textSecondaryColor; - if ([UIFont respondsToSelector:@selector(systemFontOfSize:weight:)]) - { - timeLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightLight]; - } - else + if ([bubbleData isKindOfClass:RoomBubbleCellData.class]) { - timeLabel.font = [UIFont systemFontOfSize:12]; + roomBubbleCellData = (RoomBubbleCellData*)bubbleData; + isFirstDisplayedComponent = (componentIndex == roomBubbleCellData.oldestComponentIndex); + isLastMessageMostRecentComponent = roomBubbleCellData.containsLastMessage && (componentIndex == roomBubbleCellData.mostRecentComponentIndex); } - timeLabel.adjustsFontSizeToFitWidth = YES; - - timeLabel.tag = componentIndex; - [timeLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; - timeLabel.accessibilityIdentifier = @"timestampLabel"; - [self.bubbleInfoContainer addSubview:timeLabel]; + // Display timestamp on the left for selected component when there is enough space + BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible && !isFirstDisplayedComponent && !isLastMessageMostRecentComponent; - // Define timeLabel constraints (to handle auto-layout in case of screen rotation) - NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:timeLabel - attribute:NSLayoutAttributeTrailing - relatedBy:NSLayoutRelationEqual - toItem:self.bubbleInfoContainer - attribute:NSLayoutAttributeTrailing - multiplier:1.0 - constant:0]; - NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:timeLabel - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:self.bubbleInfoContainer - attribute:NSLayoutAttributeTop - multiplier:1.0 - constant:timeLabelPosY]; - NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:timeLabel - attribute:NSLayoutAttributeWidth - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0 - constant:VECTOR_ROOMBUBBLETABLEVIEWCELL_TIMELABEL_WIDTH]; - NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:timeLabel - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0 - constant:18]; - - // Available on iOS 8 and later - [NSLayoutConstraint activateConstraints:@[rightConstraint, topConstraint, widthConstraint, heightConstraint]]; + [self addTimestampLabelForComponent:component + isFirstDisplayedComponent:isFirstDisplayedComponent + viewTag:componentIndex + displayOnLeft:displayLabelOnLeft]; + } +} + +- (void)addTimestampLabelForComponent:(MXKRoomBubbleComponent*)component + isFirstDisplayedComponent:(BOOL)isFirstDisplayedComponent + viewTag:(NSInteger)viewTag + displayOnLeft:(BOOL)displayOnLeft +{ + self.bubbleInfoContainer.hidden = NO; + + CGFloat timeLabelPosX; + CGFloat timeLabelPosY; + CGFloat timeLabelWidth; + NSTextAlignment timeLabelTextAlignment; + + if (displayOnLeft) + { + CGFloat leftMargin = 10.0; + CGFloat rightMargin = (self.contentView.frame.size.width - (self.bubbleInfoContainer.frame.origin.x + self.bubbleInfoContainer.frame.size.width)); - // Check whether a vertical whitespace was applied to display correctly the timestamp. - if (!isFirstDisplayedComponent || bubbleData.shouldHideSenderInformation || bubbleData.shouldHideSenderName) + timeLabelPosX = 0; + timeLabelPosY = component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant; + timeLabelWidth = self.contentView.frame.size.width - leftMargin - rightMargin; + timeLabelTextAlignment = NSTextAlignmentLeft; + } + else + { + timeLabelPosX = self.bubbleInfoContainer.frame.size.width - VECTOR_ROOMBUBBLETABLEVIEWCELL_TIMELABEL_WIDTH; + timeLabelPosY = isFirstDisplayedComponent ? 0 : component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant; + timeLabelWidth = VECTOR_ROOMBUBBLETABLEVIEWCELL_TIMELABEL_WIDTH; + timeLabelTextAlignment = NSTextAlignmentRight; + } + + UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(timeLabelPosX, timeLabelPosY, timeLabelWidth , 18)]; + + timeLabel.text = [bubbleData.eventFormatter timeStringFromDate:component.date]; + timeLabel.textAlignment = timeLabelTextAlignment; + timeLabel.textColor = ThemeService.shared.theme.textSecondaryColor; + timeLabel.font = [UIFont systemFontOfSize:12 weight:UIFontWeightLight]; + timeLabel.adjustsFontSizeToFitWidth = YES; + + timeLabel.tag = viewTag; + + [timeLabel setTranslatesAutoresizingMaskIntoConstraints:NO]; + timeLabel.accessibilityIdentifier = @"timestampLabel"; + [self.bubbleInfoContainer addSubview:timeLabel]; + + // Define timeLabel constraints (to handle auto-layout in case of screen rotation) + NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:timeLabel + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:self.bubbleInfoContainer + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0]; + NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:timeLabel + attribute:NSLayoutAttributeTop + relatedBy:NSLayoutRelationEqual + toItem:self.bubbleInfoContainer + attribute:NSLayoutAttributeTop + multiplier:1.0 + constant:timeLabelPosY]; + + NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:timeLabel + attribute:NSLayoutAttributeWidth + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:timeLabelWidth]; + + + NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:timeLabel + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:18]; + + // Available on iOS 8 and later + [NSLayoutConstraint activateConstraints:@[rightConstraint, topConstraint, widthConstraint, heightConstraint]]; + + // Check whether a vertical whitespace was applied to display correctly the timestamp. + if (!displayOnLeft && (!isFirstDisplayedComponent || bubbleData.shouldHideSenderInformation || bubbleData.shouldHideSenderName)) + { + // Adjust the position of the potential encryption icon in this case. + if (self.encryptionStatusContainerView) { - // Adjust the position of the potential encryption icon in this case. - if (self.encryptionStatusContainerView) + NSArray* subviews = self.encryptionStatusContainerView.subviews; + for (UIView *view in subviews) { - NSArray* subviews = self.encryptionStatusContainerView.subviews; - for (UIView *view in subviews) + // Note: The encryption icon has been tagged with the component index. + if (view.tag == viewTag) { - // Note: The encryption icon has been tagged with the component index. - if (view.tag == componentIndex) - { - CGRect frame = view.frame; - frame.origin.y += 15; - view.frame = frame; - - break; - } + CGRect frame = view.frame; + frame.origin.y += 15; + view.frame = frame; + + break; } } } @@ -138,7 +174,7 @@ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex - (void)selectComponent:(NSUInteger)componentIndex { - [self selectComponent:componentIndex showEditButton:YES showTimestamp:YES]; + [self selectComponent:componentIndex showEditButton:NO showTimestamp:YES]; } - (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton showTimestamp:(BOOL)showTimestamp From 727f44295be3cff910cd8bb6f46385e13d094fc2 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 16:35:26 +0200 Subject: [PATCH 079/266] Add convenient method on UITouch to determine if touch is inside a given view. --- Riot.xcodeproj/project.pbxproj | 4 ++++ Riot/Categories/UITouch.swift | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 Riot/Categories/UITouch.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 542e7eb2d7..df79a42305 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -205,6 +205,7 @@ B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */; }; B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */; }; B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A5B33D227ADF2A004CBA85 /* UIImage.swift */; }; + B1B12B2922942315002CB419 /* UITouch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B12B2822942315002CB419 /* UITouch.swift */; }; B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */; }; B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */; }; B1B5571A20EE6C4D00210D55 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567E20EE6C4C00210D55 /* SettingsViewController.m */; }; @@ -803,6 +804,7 @@ B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorType.swift; sourceTree = ""; }; B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinator.swift; sourceTree = ""; }; B1A5B33D227ADF2A004CBA85 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; }; + B1B12B2822942315002CB419 /* UITouch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITouch.swift; sourceTree = ""; }; B1B5567920EE6C4C00210D55 /* CountryPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountryPickerViewController.h; sourceTree = ""; }; B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountryPickerViewController.m; sourceTree = ""; }; B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LanguagePickerViewController.m; sourceTree = ""; }; @@ -3245,6 +3247,7 @@ B1A5B33D227ADF2A004CBA85 /* UIImage.swift */, B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */, B1C562CB228AB3510037F12A /* UIStackView.swift */, + B1B12B2822942315002CB419 /* UITouch.swift */, ); path = Categories; sourceTree = ""; @@ -4036,6 +4039,7 @@ B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */, B1B5579120EF568D00210D55 /* GroupInviteTableViewCell.m in Sources */, B1B5579A20EF575B00210D55 /* ForgotPasswordInputsView.m in Sources */, + B1B12B2922942315002CB419 /* UITouch.swift in Sources */, 32B94DFD228EC26400716A26 /* ReactionsMenuReaction.swift in Sources */, B1B558CC20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B1B5571D20EE6C4D00210D55 /* HomeViewController.m in Sources */, diff --git a/Riot/Categories/UITouch.swift b/Riot/Categories/UITouch.swift new file mode 100644 index 0000000000..be7e8307e8 --- /dev/null +++ b/Riot/Categories/UITouch.swift @@ -0,0 +1,28 @@ +/* + Copyright 2019 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 + +extension UITouch { + + func vc_isInside(view: UIView? = nil) -> Bool { + guard let view = view ?? self.view else { + return false + } + let touchedLocation = self.location(in: view) + return view.bounds.contains(touchedLocation) + } +} From 4bde8a435bb874099ac4209da1000a4320cc7d05 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 16:35:46 +0200 Subject: [PATCH 080/266] Room contextual menu: Dismiss when swipe up or down. --- .../RoomContextualMenuViewController.swift | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift index 5a111508db..2318a0bb4f 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift @@ -79,7 +79,7 @@ final class RoomContextualMenuViewController: UIViewController, Themable { self.backgroundOverlayView.isUserInteractionEnabled = true self.menuToolbarView.fill(contextualMenuItems: self.contextualMenuItems) - self.setupBackgroundOverlayTapGestureRecognizer() + self.setupBackgroundOverlayGestureRecognizers() self.errorPresenter = MXKErrorAlertPresentation() @@ -125,12 +125,20 @@ final class RoomContextualMenuViewController: UIViewController, Themable { // MARK: - Private - private func setupBackgroundOverlayTapGestureRecognizer() { - let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(gestureRecognizer:))) + private func setupBackgroundOverlayGestureRecognizers() { + + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handle(gestureRecognizer:))) + tapGestureRecognizer.delegate = self + + let swipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(handle(gestureRecognizer:))) + swipeGestureRecognizer.direction = [.down, .up] + swipeGestureRecognizer.delegate = self + self.backgroundOverlayView.addGestureRecognizer(tapGestureRecognizer) + self.backgroundOverlayView.addGestureRecognizer(swipeGestureRecognizer) } - @objc private func handleTap(gestureRecognizer: UIGestureRecognizer) { + @objc private func handle(gestureRecognizer: UIGestureRecognizer) { self.delegate?.roomContextualMenuViewControllerDidTapBackgroundOverlay(self) } @@ -143,6 +151,7 @@ final class RoomContextualMenuViewController: UIViewController, Themable { } } +// MARK: - ReactionsMenuViewModelCoordinatorDelegate extension RoomContextualMenuViewController: ReactionsMenuViewModelCoordinatorDelegate { func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didSendReaction reaction: String, isAddReaction: Bool) { @@ -157,3 +166,12 @@ extension RoomContextualMenuViewController: ReactionsMenuViewModelCoordinatorDel } } } + +// MARK: - UIGestureRecognizerDelegate +extension RoomContextualMenuViewController: UIGestureRecognizerDelegate { + + // Avoid triggering background overlay gesture recognizers when touching reactions menu + func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { + return touch.vc_isInside(view: self.reactionsMenuView) == false + } +} From 6ce4a826be454c515fbc23779088cf95a266ee9f Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 16:37:06 +0200 Subject: [PATCH 081/266] RoomVC: Show contextual menu on single tap. --- Riot/Modules/Room/RoomViewController.m | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index fa8320696c..d61b97b988 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2013,6 +2013,15 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac // Handle here user actions on bubbles for Vector app if (customizedRoomDataSource) { + id bubbleData; + + if ([cell isKindOfClass:[MXKRoomBubbleTableViewCell class]]) + { + MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell*)cell; + bubbleData = roomBubbleTableViewCell.bubbleData; + } + + if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAvatarView]) { selectedRoomMember = [self.roomDataSource.roomState.members memberWithUserId:userInfo[kMXKRoomBubbleCellUserIdKey]]; @@ -2056,8 +2065,15 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac } else { - // Highlight this event in displayed message - [self selectEventWithId:tappedEvent.eventId]; + // Show contextual menu on single tap if bubble is not collapsed + if (bubbleData.collapsed) + { + [self selectEventWithId:tappedEvent.eventId]; + } + else + { + [self showContextualMenuForEvent:tappedEvent cell:cell animated:YES]; + } } } } @@ -2132,7 +2148,11 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnEvent]) { MXEvent *tappedEvent = userInfo[kMXKRoomBubbleCellEventKey]; - [self handleLongPressFromCell:cell withTappedEvent:tappedEvent]; + + if (!bubbleData.collapsed) + { + [self handleLongPressFromCell:cell withTappedEvent:tappedEvent]; + } } else { From f271a08be23f37030b96ca19ae6f0c7df32608e8 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 16:39:11 +0200 Subject: [PATCH 082/266] EventFormatter: Use a 24 hour date format for time. --- Riot/Utils/EventFormatter.m | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index 7926f11cfc..78136db18e 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -30,6 +30,8 @@ NSString *const kEventFormatterOnReRequestKeysLinkAction = @"kEventFormatterOnReRequestKeysLinkAction"; NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/"; +static NSString *const kEventFormatterTimeFormat = @"hh:mm"; + @interface EventFormatter () { /** @@ -41,6 +43,13 @@ @interface EventFormatter () @implementation EventFormatter +- (void)initDateTimeFormatters +{ + [super initDateTimeFormatters]; + + [timeFormatter setDateFormat:kEventFormatterTimeFormat]; +} + - (NSAttributedString *)attributedStringFromEvent:(MXEvent *)event withRoomState:(MXRoomState *)roomState error:(MXKEventFormatterError *)error { // Build strings for widget events From 60a9644a7ec9bab262fec1fca53835d295abda67 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 16:43:28 +0200 Subject: [PATCH 083/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6a65585fad..e1a3322d78 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,7 @@ Improvements: * RoomVC: When replying, use a "Reply" button instead of "Send". * RoomVC: New message actions (#2394). * Reactions: Display existing reactions below the message (#2396). + * Menu actions: Display message time (#2463). Changes in 0.8.6 (2019-05-06) =============================================== From 0fb13febee33a59841659d62417d8e45b4858796 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 16:54:51 +0200 Subject: [PATCH 084/266] RoomVC: Display message time when contextual menu is shown. --- Riot/Modules/Room/RoomViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index d61b97b988..fcf11f81a1 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5082,7 +5082,7 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce return; } - [self selectEventWithId:event.eventId enableReplyMode:NO showTimestamp:NO]; + [self selectEventWithId:event.eventId enableReplyMode:NO showTimestamp:YES]; NSArray* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell]; From a62548c54589e9a0d06a3a06d7298ff3a2416699 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 17:27:29 +0200 Subject: [PATCH 085/266] Update Riot/Modules/Room/CellData/RoomBubbleCellData.h Co-Authored-By: manuroe --- Riot/Modules/Room/CellData/RoomBubbleCellData.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.h b/Riot/Modules/Room/CellData/RoomBubbleCellData.h index 20e6c8b86f..fd62967b49 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.h +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.h @@ -41,7 +41,7 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag) @property(nonatomic) BOOL showTimestampForSelectedComponent; /** - Indicate true to display the timestamp of the selected component on the left when there is enough space (YES by default). + Indicate true to display the timestamp of the selected component on the left if possible (YES by default). */ @property(nonatomic) BOOL displayTimestampForSelectedComponentOnLeftWhenPossible; From 4e1e95fd2c05fa0ffa5fd3f6db376431b6bab001 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 17:28:08 +0200 Subject: [PATCH 086/266] Update Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m Co-Authored-By: manuroe --- Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 444278de6e..dd770de1ae 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -59,7 +59,7 @@ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex isLastMessageMostRecentComponent = roomBubbleCellData.containsLastMessage && (componentIndex == roomBubbleCellData.mostRecentComponentIndex); } - // Display timestamp on the left for selected component when there is enough space + // Display timestamp on the left for selected component when it cannot overlap other UI elements like user's avatar BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible && !isFirstDisplayedComponent && !isLastMessageMostRecentComponent; [self addTimestampLabelForComponent:component From 4cb166072a47d48294af3e646bb4090d26c89886 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 18:46:22 +0200 Subject: [PATCH 087/266] MXKRoomBubbleTableViewCell: Add convenient method to calculate a bubble component frame in table view. --- .../MXKRoomBubbleTableViewCell+Riot.h | 8 ++ .../MXKRoomBubbleTableViewCell+Riot.m | 74 +++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h index dade6d2ee9..c6d77e2ae5 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h @@ -82,6 +82,14 @@ extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer; */ - (void)updateUserNameColor; +/** + Calculate component frame in table view. + + @param componentIndex index of the component in bubble message data + @return component frame if component exist or CGRectNull. + */ +- (CGRect)componentFrameForIndex:(NSInteger)componentIndex; + /** Blur the view by adding a transparent overlay. Default is NO. */ diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index dd770de1ae..1a7332b36e 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -459,6 +459,80 @@ - (void)updateUserNameColor } } +- (CGRect)componentFrameForIndex:(NSInteger)componentIndex +{ + MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = self; + MXKRoomBubbleCellData *bubbleCellData = roomBubbleTableViewCell.bubbleData; + MXKRoomBubbleComponent *selectedComponent; + + if (bubbleCellData.bubbleComponents.count > componentIndex) + { + selectedComponent = bubbleCellData.bubbleComponents[componentIndex]; + } + + if (!selectedComponent) + { + return CGRectNull; + } + + CGFloat selectedComponenContentViewYOffset = 0; + CGFloat selectedComponentPositionY = 0; + CGFloat selectedComponentHeight = 0; + + CGRect componentFrame = CGRectNull; + + if (roomBubbleTableViewCell.attachmentView) + { + CGRect attachamentViewFrame = roomBubbleTableViewCell.attachmentView.frame; + + selectedComponenContentViewYOffset = attachamentViewFrame.origin.y; + selectedComponentHeight = attachamentViewFrame.size.height; + } + else if (roomBubbleTableViewCell.messageTextView) + { + CGFloat textMessageHeight = 0; + + if ([bubbleCellData isKindOfClass:[RoomBubbleCellData class]]) + { + RoomBubbleCellData *roomBubbleCellData = (RoomBubbleCellData*)bubbleCellData; + + if (!roomBubbleCellData.attachment && selectedComponent.attributedTextMessage) + { + textMessageHeight = [roomBubbleCellData rawTextHeight:selectedComponent.attributedTextMessage]; + } + } + + selectedComponentPositionY = selectedComponent.position.y; + + if (textMessageHeight > 0) + { + selectedComponentHeight = textMessageHeight; + } + else + { + selectedComponentHeight = roomBubbleTableViewCell.frame.size.height - selectedComponentPositionY; + } + + selectedComponenContentViewYOffset = roomBubbleTableViewCell.messageTextView.frame.origin.y; + } + + if (roomBubbleTableViewCell.attachmentView || roomBubbleTableViewCell.messageTextView) + { + CGRect roomBubbleTableViewCellFrame = roomBubbleTableViewCell.frame; + CGFloat x = roomBubbleTableViewCellFrame.origin.x; + CGFloat y = roomBubbleTableViewCellFrame.origin.y + selectedComponenContentViewYOffset + selectedComponentPositionY; + CGFloat width = roomBubbleTableViewCellFrame.size.width; + + componentFrame = CGRectMake(x, y, width, selectedComponentHeight); + } + else + { + componentFrame = roomBubbleTableViewCell.frame; + } + + return componentFrame; +} + #pragma mark - User actions - (IBAction)onEditButtonPressed:(id)sender From 366d27ebab31e00ea5a6260ab3cb33e0ceaf1403 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 18:48:50 +0200 Subject: [PATCH 088/266] Contextual menu: Improve reactions menu position. --- Riot/Modules/Room/RoomViewController.m | 38 ++++++++++++++++++++------ 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index fcf11f81a1..f8f7265722 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5096,17 +5096,37 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce completion:^{ [self contextualMenuAnimationCompletionAfterBeingShown:YES]; }]; - + if (RiotSettings.shared.messageReaction && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) { - // Note: For the moment, we use the frame of the cell instead of the component - // From UI perpective, that means the menu will be around a paragraph instead of a message - // This is not bad as the paragraph is kept visible to provide more context to the user - // TO FIX: if paragraph is bigger than the screen, the menu is displayed in the middle - CGRect frame = ((MXKRoomBubbleTableViewCell*)cell).frame; - frame = [self.bubblesTableView convertRect:frame toView:[self.bubblesTableView superview]]; - - [self.roomContextualMenuPresenter showReactionsMenuForEvent:event.eventId inRoom:event.roomId session:self.mainSession aroundFrame:frame]; + MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell*)cell; + MXKRoomBubbleCellData *bubbleCellData = roomBubbleTableViewCell.bubbleData; + NSArray *bubbleComponents = bubbleCellData.bubbleComponents; + + NSInteger foundComponentIndex = [bubbleComponents indexOfObjectPassingTest:^BOOL(MXKRoomBubbleComponent * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + if (obj.event.eventId == event.eventId) + { + *stop = YES; + return YES; + } + return NO; + }]; + + CGRect bubbleComponentFrame; + + if (bubbleComponents.count > 0) + { + NSInteger selectedComponentIndex = foundComponentIndex != NSNotFound ? foundComponentIndex : 0; + bubbleComponentFrame = [roomBubbleTableViewCell componentFrameForIndex:selectedComponentIndex]; + } + else + { + bubbleComponentFrame = roomBubbleTableViewCell.frame; + } + + CGRect bubbleComponentFrameInOverlayView = [self.bubblesTableView convertRect:bubbleComponentFrame toView:self.overlayContainerView]; + + [self.roomContextualMenuPresenter showReactionsMenuForEvent:event.eventId inRoom:event.roomId session:self.mainSession aroundFrame:bubbleComponentFrameInOverlayView]; } } From 4edf817a5d3ad65a721294b8aa148b0809cf75c6 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 22 May 2019 18:49:35 +0200 Subject: [PATCH 089/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index e1a3322d78..179bc2b65a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -6,6 +6,7 @@ Improvements: * RoomVC: New message actions (#2394). * Reactions: Display existing reactions below the message (#2396). * Menu actions: Display message time (#2463). + * Reactions Menu: Fix position (#2447). Changes in 0.8.6 (2019-05-06) =============================================== From f14cbf759e80b7b736595370fcd9bbce394d5d77 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 23 May 2019 16:46:00 +0200 Subject: [PATCH 090/266] Registration: Fix infinite loop when register with email and validate from mobile. --- .../Authentication/Views/AuthInputsView.m | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Authentication/Views/AuthInputsView.m b/Riot/Modules/Authentication/Views/AuthInputsView.m index 79280d25de..5685a9f433 100644 --- a/Riot/Modules/Authentication/Views/AuthInputsView.m +++ b/Riot/Modules/Authentication/Views/AuthInputsView.m @@ -814,8 +814,24 @@ - (void)updateAuthSessionWithCompletedStages:(NSArray *)completedStages didUpdat else if ([self isFlowSupported:kMXLoginFlowTypeTerms] && ![self isFlowCompleted:kMXLoginFlowTypeTerms]) { NSLog(@"[AuthInputsView] Prepare a new terms stage"); - - [self prepareParameters:callback]; + + if (externalRegistrationParameters) + { + [self displayTermsView:^{ + + NSDictionary *parameters = @{ + @"auth": @{ + @"session":self->currentSession.session, + @"type": kMXLoginFlowTypeTerms + } + }; + callback(parameters, nil); + }]; + } + else + { + [self prepareParameters:callback]; + } return; } From cc188209a7100f6973145c72f347dc5656bc8b62 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 23 May 2019 16:49:13 +0200 Subject: [PATCH 091/266] Update changes --- CHANGES.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 179bc2b65a..95f2671aff 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,9 @@ Improvements: * Menu actions: Display message time (#2463). * Reactions Menu: Fix position (#2447). +Bug fix: + * Registration with an email is broken (#2417). + Changes in 0.8.6 (2019-05-06) =============================================== From a2352c7817ef4b2de9f36440a7dcc173fe2fd74b Mon Sep 17 00:00:00 2001 From: Victor Grousset Date: Thu, 23 May 2019 07:53:07 +0000 Subject: [PATCH 092/266] Translated using Weblate (Esperanto) Currently translated at 2.4% (17 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eo/ --- Riot/Assets/eo.lproj/Vector.strings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Assets/eo.lproj/Vector.strings b/Riot/Assets/eo.lproj/Vector.strings index 1c6cbc40ee..238f9724e9 100644 --- a/Riot/Assets/eo.lproj/Vector.strings +++ b/Riot/Assets/eo.lproj/Vector.strings @@ -15,3 +15,5 @@ "room_jump_to_first_unread" = "Salti al unua nelegita mesaĝo"; "group_details_people" = "Homoj"; "group_details_rooms" = "Babilejoj"; +// MARK: - Device Verification +"device_verification_title" = "Kontroli aparaton"; From b3d9785de5732aabd3cca144e3ad244791cf1102 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Thu, 23 May 2019 07:15:55 +0000 Subject: [PATCH 093/266] Translated using Weblate (French) Currently translated at 100.0% (710 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 113 ++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 6 deletions(-) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index d9c0dfb366..b48803bd87 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -54,7 +54,7 @@ "auth_invalid_email" = "L'adresse e-mail ne semble pas valide"; "auth_invalid_phone" = "Le numéro de téléphone ne semble pas valide"; "auth_missing_password" = "Mot de passe manquant"; -"auth_add_email_message" = "Ajouter une adresse e-mail au compte pour que les utilisateurs puissent vous retrouver, et pour pouvoir réinitialiser votre mot de passe."; +"auth_add_email_message" = "Ajouter une adresse e-mail au compte pour que les utilisateurs puissent vous retrouver et pour réinitialiser votre mot de passe."; "auth_add_phone_message" = "Ajouter un numéro de téléphone au compte pour que les utilisateurs puissent vous retrouver."; "auth_add_email_phone_message" = "Ajouter une adresse e-mail et/ou un numéro de téléphone à votre compte pour que les utilisateurs puissent vous retrouver. L'adresse e-mail vous permettra également de réinitialiser votre mot de passe."; "auth_add_email_and_phone_message" = "Ajouter une adresse e-mail et un numéro de téléphone à votre compte pour que les utilisateurs puissent vous retrouver. L'adresse e-mail vous permettra également de réinitialiser votre mot de passe."; @@ -69,14 +69,14 @@ "auth_msisdn_validation_title" = "Vérification en attente"; "auth_msisdn_validation_message" = "Nous vous avons envoyé un SMS avec un code d'activation. Merci de le recopier ci-dessous."; "auth_msisdn_validation_error" = "Impossible de vérifier votre numéro de téléphone."; -"auth_recaptcha_message" = "Ce serveur d'accueil voudrait s'assurer que vous n'êtes pas un robot"; +"auth_recaptcha_message" = "Ce serveur d’accueil voudrait s’assurer que vous n’êtes pas un robot"; "auth_reset_password_message" = "Pour réinitialiser votre mot de passe, saisissez l'adresse e-mail liée à votre compte :"; "auth_reset_password_missing_email" = "Vous devez saisir l'adresse e-mail liée à votre compte."; "auth_reset_password_missing_password" = "Vous devez spécifier un nouveau mot de passe."; "auth_reset_password_email_validation_message" = "Un e-mail a été envoyé à %@. Cliquez d'abord sur le lien dans l'e-mail, puis ci-dessous."; "auth_reset_password_next_step_button" = "J'ai vérifié mon adresse e-mail"; "auth_reset_password_error_unauthorized" = "Impossible de vérifier l'adresse e-mail : assurez-vous de cliquer sur le lien dans l'e-mail"; -"auth_reset_password_error_not_found" = "Votre adresse e-mail ne semble pas associée à un identifiant Matrix sur ce serveur d'accueil."; +"auth_reset_password_error_not_found" = "Votre adresse e-mail ne semble pas associée à un identifiant Matrix sur ce serveur d’accueil."; "auth_reset_password_success_message" = "Votre mot de passe a été réinitialisé.\n\nVous avez été déconnecté de tous vos appareils et ne recevrez plus les notifications. Pour réactiver les notifications, reconnectez-vous sur chaque appareil."; "auth_add_email_and_phone_warning" = "L'inscription avec un e-mail et un numéro de téléphone à la fois n'est pas supporté tant que l'API n'existe pas. Seul votre numéro de téléphone sera pris en compte. Vous pourrez ajouter l'adresse e-mail dans vos options de profil."; // Chat creation @@ -246,7 +246,7 @@ "settings_config_no_build_info" = "Aucune information sur la version"; "settings_mark_all_as_read" = "Marquer tous les messages comme lus"; "settings_report_bug" = "Signaler une erreur"; -"settings_config_home_server" = "Le serveur d'accueil est %@"; +"settings_config_home_server" = "Le serveur d’accueil est %@"; "settings_config_identity_server" = "Le serveur d'identité est %@"; "settings_config_user_id" = "Identifié en tant que %@"; "settings_user_settings" = "PRÉFÉRENCES UTILISATEUR"; @@ -563,7 +563,7 @@ "settings_key_backup_info_version" = "Version de sauvegarde de clé : %@"; "settings_key_backup_info_algorithm" = "Algorithme : %@"; "settings_key_backup_info_valid" = "Cet appareil sauvegarde vos clés."; -"settings_key_backup_info_not_valid" = "Cet appareil ne sauvegarde pas vos clés."; +"settings_key_backup_info_not_valid" = "Cet appareil ne sauvegarde pas vos clés, mais vous avez une sauvegarde que vous pouvez restaurer et joindre."; "settings_key_backup_info_progress" = "Sauvegarde de %@ clés…"; "settings_key_backup_info_progress_done" = "Toutes les clés ont été sauvegardées"; "settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "Pour utiliser la récupération de messages sécurisée sur cet appareil, vérifiez %@ maintenant."; @@ -625,7 +625,7 @@ "key_backup_recover_banner_title_part1" = "Lancer la récupération de messages sécurisée"; "key_backup_recover_banner_title_part2" = " pour lire l'historique des messages chiffrés sur cet appareil"; "settings_key_backup_info" = "Les messages chiffrés sont sécurisés avec un chiffrement de bout en bout. Seuls vous et le(s) destinataire(s) avez les clés pour lire ces messages."; -"settings_key_backup_info_signout_warning" = "Sauvegardez vos clés avant de vous déconnecter pour éviter de les perdre."; +"settings_key_backup_info_signout_warning" = "Connectez cet appareil à la sauvegarde de clés avant de vous déconnecter pour éviter de perdre les clés qui ne seraient que sur cet appareil."; "settings_key_backup_button_use" = "Utiliser la sauvegarde de clés"; "key_backup_setup_intro_setup_action_without_existing_backup" = "Commencer à utiliser la sauvegarde de clés"; "key_backup_setup_intro_setup_action_with_existing_backup" = "Utiliser la sauvegarde de clés"; @@ -675,3 +675,104 @@ "auth_login_single_sign_on" = "Se connecter avec l'authentification unique"; "room_message_unable_open_link_error_message" = "Impossible d’ouvrir le lien."; "auth_autodiscover_invalid_response" = "Réponse de découverte du serveur d’accueil non valide"; +"room_event_action_reply" = "Répondre"; +"room_event_action_edit" = "Éditer"; +"room_event_action_reaction_agree" = "D’accord %@"; +"room_event_action_reaction_disagree" = "Pas d’accord %@"; +"room_event_action_reaction_like" = "J’aime %@"; +"room_event_action_reaction_dislike" = "J’aime pas %@"; +"room_action_reply" = "Répondre"; +"settings_labs_message_reaction" = "Réagir aux messages avec des émojis"; +"settings_key_backup_button_connect" = "Connecter cet appareil à la sauvegarde de clés"; +"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Connecter cet appareil à la sauvegarde de clés"; +"key_backup_recover_connent_banner_subtitle" = "Connecter cet appareil à la sauvegarde de clés"; +// MARK: - Device Verification +"device_verification_title" = "Vérifier l’appareil"; +"device_verification_security_advice" = "Pour une sécurité maximale, nous vous recommandons de faire cela en personne ou d’utiliser un autre moyen sûr de communication"; +"device_verification_cancelled" = "L’autre personne a annulé la vérification."; +"device_verification_cancelled_by_me" = "La vérification a été annulée. Motif : %@"; +"device_verification_error_cannot_load_device" = "Impossible de charger les informations de l’appareil."; +// Mark: Incoming +"device_verification_incoming_title" = "Demande de vérification entrante"; +"device_verification_incoming_description_1" = "Vérifier cet appareil pour que ce soit un appareil de confiance. Faire confiance aux appareils de vos partenaires vous apporte une tranquillité d’esprit quand vous utilisez des messages chiffrés de bout en bout."; +"device_verification_incoming_description_2" = "En vérifiant cet appareil, il sera marqué comme appareil de confiance, et le vôtre sera aussi marqué comme appareil de confiance pour votre partenaire."; +// MARK: Start +"device_verification_start_title" = "Vérifier en comparant une chaîne de caractères courte"; +"device_verification_start_wait_partner" = "Nous attendons que le partenaire accepte…"; +"device_verification_start_use_legacy" = "Rien n'apparaît ? Certains clients ne prennent pas encore en charge la vérification interactive. Utilisez la vérification traditionnelle."; +"device_verification_start_verify_button" = "Commencer la vérification"; +"device_verification_start_use_legacy_action" = "Utiliser la vérification traditionnelle"; +// MARK: Verify +"device_verification_verify_title_emoji" = "Vérifier cet appareil en confirmant que les émojis suivant apparaissent sur l’écran de votre partenaire"; +"device_verification_verify_title_number" = "Vérifier cet utilisateur en confirmant que les chiffres suivant apparaissent sur l’écran de votre partenaire"; +"device_verification_verify_wait_partner" = "Nous attendons la confirmation de votre partenaire…"; +// MARK: Verified +"device_verification_verified_title" = "Vérifié !"; +"device_verification_verified_description_1" = "Vous avez bien vérifié cet appareil."; +"device_verification_verified_description_2" = "Les messages sécurisés avec cet utilisateur sont chiffrés de bout en bout et ne peuvent être lus par d’autres personnes."; +"device_verification_verified_got_it_button" = "Compris"; +// MARK: Emoji +"device_verification_emoji_dog" = "Chien"; +"device_verification_emoji_cat" = "Chat"; +"device_verification_emoji_lion" = "Lion"; +"device_verification_emoji_horse" = "Cheval"; +"device_verification_emoji_unicorn" = "Licorne"; +"device_verification_emoji_pig" = "Cochon"; +"device_verification_emoji_elephant" = "Éléphant"; +"device_verification_emoji_rabbit" = "Lapin"; +"device_verification_emoji_panda" = "Panda"; +"device_verification_emoji_rooster" = "Coq"; +"device_verification_emoji_penguin" = "Manchot"; +"device_verification_emoji_turtle" = "Tortue"; +"device_verification_emoji_fish" = "Poisson"; +"device_verification_emoji_octopus" = "Pieuvre"; +"device_verification_emoji_butterfly" = "Papillon"; +"device_verification_emoji_flower" = "Fleur"; +"device_verification_emoji_tree" = "Arbre"; +"device_verification_emoji_cactus" = "Cactus"; +"device_verification_emoji_mushroom" = "Champignon"; +"device_verification_emoji_globe" = "Terre"; +"device_verification_emoji_moon" = "Lune"; +"device_verification_emoji_cloud" = "Nuage"; +"device_verification_emoji_fire" = "Feu"; +"device_verification_emoji_banana" = "Banane"; +"device_verification_emoji_apple" = "Pomme"; +"device_verification_emoji_strawberry" = "Fraise"; +"device_verification_emoji_corn" = "Maïs"; +"device_verification_emoji_pizza" = "Pizza"; +"device_verification_emoji_cake" = "Gâteau"; +"device_verification_emoji_heart" = "Cœur"; +"device_verification_emoji_smiley" = "Smiley"; +"device_verification_emoji_robot" = "Robot"; +"device_verification_emoji_hat" = "Chapeau"; +"device_verification_emoji_glasses" = "Lunettes"; +"device_verification_emoji_spanner" = "Clé plate"; +"device_verification_emoji_santa" = "Père Noël"; +"device_verification_emoji_thumbs up" = "Pouce levé"; +"device_verification_emoji_umbrella" = "Parapluie"; +"device_verification_emoji_hourglass" = "Sablier"; +"device_verification_emoji_clock" = "Horloge"; +"device_verification_emoji_gift" = "Cadeau"; +"device_verification_emoji_light bulb" = "Ampoule"; +"device_verification_emoji_book" = "Livre"; +"device_verification_emoji_pencil" = "Crayon"; +"device_verification_emoji_paperclip" = "Trombone"; +"device_verification_emoji_scissors" = "Ciseaux"; +"device_verification_emoji_padlock" = "Cadenas"; +"device_verification_emoji_key" = "Clé"; +"device_verification_emoji_hammer" = "Marteau"; +"device_verification_emoji_telephone" = "Téléphone"; +"device_verification_emoji_flag" = "Drapeau"; +"device_verification_emoji_train" = "Train"; +"device_verification_emoji_bicycle" = "Vélo"; +"device_verification_emoji_aeroplane" = "Avion"; +"device_verification_emoji_rocket" = "Fusée"; +"device_verification_emoji_trophy" = "Trophée"; +"device_verification_emoji_ball" = "Balle"; +"device_verification_emoji_guitar" = "Guitare"; +"device_verification_emoji_trumpet" = "Trompette"; +"device_verification_emoji_bell" = "Cloche"; +"device_verification_emoji_anchor" = "Ancre"; +"device_verification_emoji_headphones" = "Écouteurs"; +"device_verification_emoji_folder" = "Dossier"; +"device_verification_emoji_pin" = "Épingle"; From 31feca6f17bf68040597d0c6ff49ae486b0f6a68 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 23 May 2019 17:48:52 +0200 Subject: [PATCH 094/266] Room contextual menu: Update button titles color and disabled opacity. --- Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift | 2 +- Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib | 4 ++-- .../Room/ContextualMenu/RoomContextualMenuToolbarView.swift | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift index a76537140a..8a0147e5f5 100644 --- a/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift +++ b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift @@ -28,7 +28,7 @@ final class ContextualMenuItemView: UIView, NibOwnerLoadable { private enum ViewAlpha { static let normal: CGFloat = 1.0 - static let disabled: CGFloat = 0.5 + static let disabled: CGFloat = 0.3 } // MARK: - Properties diff --git a/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib index 9c2ce9ac52..0032099d7f 100644 --- a/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib +++ b/Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib @@ -20,7 +20,7 @@ - + @@ -50,6 +50,6 @@ - + diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift index c4c3893725..8d695c9454 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.swift @@ -47,7 +47,7 @@ final class RoomContextualMenuToolbarView: MXKRoomInputToolbarView, NibOwnerLoad self.separatorView.backgroundColor = theme.lineBreakColor for menuItemView in self.menuItemViews { - menuItemView.titleColor = theme.textPrimaryColor + menuItemView.titleColor = theme.tintColor menuItemView.imageColor = theme.tintColor } } From 2252561088f262bebbc9d51ee614c0f48d6fa56a Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 23 May 2019 17:50:59 +0200 Subject: [PATCH 095/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 95f2671aff..1b066f1ce3 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -7,6 +7,7 @@ Improvements: * Reactions: Display existing reactions below the message (#2396). * Menu actions: Display message time (#2463). * Reactions Menu: Fix position (#2447). + * Context menu polish (#2466). Bug fix: * Registration with an email is broken (#2417). From c2be2b5af960c0bdfe410f0bc1e77b4ded7d510c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 23 May 2019 18:26:38 +0200 Subject: [PATCH 096/266] EventFormatter: Fix 24 hour time format. --- Riot/Utils/EventFormatter.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index 78136db18e..91819e7811 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -30,7 +30,7 @@ NSString *const kEventFormatterOnReRequestKeysLinkAction = @"kEventFormatterOnReRequestKeysLinkAction"; NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/"; -static NSString *const kEventFormatterTimeFormat = @"hh:mm"; +static NSString *const kEventFormatterTimeFormat = @"HH:mm"; @interface EventFormatter () { @@ -47,6 +47,7 @@ - (void)initDateTimeFormatters { [super initDateTimeFormatters]; + timeFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; [timeFormatter setDateFormat:kEventFormatterTimeFormat]; } From 940ce889462dc929fdb70174e2e2163b2a3e43cb Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Mon, 7 Jan 2019 18:42:08 +0100 Subject: [PATCH 097/266] Rename PiwikTracker to MatomoTracker and update to version 6.0.0 --- Podfile | 12 ++---- Podfile.lock | 23 ++++------- Riot.xcodeproj/project.pbxproj | 4 +- Riot/Managers/Analytics/Analytics.m | 63 ++++++++++++++++++----------- 4 files changed, 52 insertions(+), 50 deletions(-) diff --git a/Podfile b/Podfile index 59ed9a2fe1..467fea952d 100644 --- a/Podfile +++ b/Podfile @@ -64,15 +64,15 @@ abstract_target 'RiotPods' do # Piwik for analytics # While https://github.com/matomo-org/matomo-sdk-ios/pull/223 is not released, use the PR branch - pod 'PiwikTracker', :git => 'https://github.com/manuroe/matomo-sdk-ios.git', :branch => 'feature/CustomVariables' - #pod 'PiwikTracker', '~> 4.4.2' + #pod 'PiwikTracker', :git => 'https://github.com/manuroe/matomo-sdk-ios.git', :branch => 'feature/CustomVariables' + pod 'MatomoTracker', '~> 6.0.0' # Remove warnings from "bad" pods pod 'OLMKit', :inhibit_warnings => true pod 'cmark', :inhibit_warnings => true pod 'DTCoreText', :inhibit_warnings => true pod 'zxcvbn-ios' - + # Tools pod 'SwiftGen', '~> 6.1' pod 'SwiftLint', '~> 0.30.1' @@ -101,13 +101,7 @@ post_install do |installer| # Plus the app does not enable it target.build_configurations.each do |config| config.build_settings['ENABLE_BITCODE'] = 'NO' - - # Required for PiwikTracker as `swift_version` is not defined in podspec. Should be removed - if target.name.include? 'PiwikTracker' - config.build_settings['SWIFT_VERSION'] = '4.0' - end end - end end diff --git a/Podfile.lock b/Podfile.lock index 952f5eb90a..207baf23f5 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -45,6 +45,9 @@ PODS: - JitsiMeetSDK (2.1.0) - libbase58 (0.1.4) - libPhoneNumber-iOS (0.9.13) + - MatomoTracker (6.0.0): + - MatomoTracker/Core (= 6.0.0) + - MatomoTracker/Core (6.0.0) - MatrixKit (0.9.9): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) @@ -83,9 +86,6 @@ PODS: - OLMKit/olmcpp (= 3.1.0) - OLMKit/olmc (3.1.0) - OLMKit/olmcpp (3.1.0) - - PiwikTracker (4.4.2): - - PiwikTracker/Core (= 4.4.2) - - PiwikTracker/Core (4.4.2) - Realm (3.13.1): - Realm/Headers (= 3.13.1) - Realm/Headers (3.13.1) @@ -102,12 +102,12 @@ DEPENDENCIES: - cmark - DTCoreText - GBDeviceInfo (~> 5.2.0) + - MatomoTracker (~> 6.0.0) - MatrixKit (= 0.9.9) - MatrixKit/AppExtension (= 0.9.9) - MatrixSDK/JingleCallStack - MatrixSDK/SwiftSupport - OLMKit - - PiwikTracker (from `https://github.com/manuroe/matomo-sdk-ios.git`, branch `feature/CustomVariables`) - Reusable (~> 4.0) - SwiftGen (~> 6.1) - SwiftLint (~> 0.30.1) @@ -125,6 +125,7 @@ SPEC REPOS: - JitsiMeetSDK - libbase58 - libPhoneNumber-iOS + - MatomoTracker - MatrixKit - MatrixSDK - OLMKit @@ -134,16 +135,6 @@ SPEC REPOS: - SwiftLint - zxcvbn-ios -EXTERNAL SOURCES: - PiwikTracker: - :branch: feature/CustomVariables - :git: https://github.com/manuroe/matomo-sdk-ios.git - -CHECKOUT OPTIONS: - PiwikTracker: - :commit: dfb048f25f4eefbe13ff7515c3c1c2cad5d94491 - :git: https://github.com/manuroe/matomo-sdk-ios.git - SPEC CHECKSUMS: AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057 cmark: ec0275215b504780287b6fca360224e384368af8 @@ -155,16 +146,16 @@ SPEC CHECKSUMS: JitsiMeetSDK: 3e66564af7f38a19142338955dd7f581801852b3 libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa + MatomoTracker: f1d0adbe609f8db3740d24e59e16a1855d263921 MatrixKit: 6f553797e1ad42794b5336afb5cecb975ec69daa MatrixSDK: ed0d0cee4877955052f19730bb3ee727e01ec948 OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639 - PiwikTracker: 42862c7b13028065c3dfd36b4dc38db8a5765acf Realm: 50071da38fe079e0735e47c9f2eae738c68c5996 Reusable: 188be1a54ac0691bc66e5bb24ec6eb91971b315b SwiftGen: f872ca75cbd17bf7103c17f13dcfa0d9a15667b0 SwiftLint: a54bf1fe12b55c68560eb2a7689dfc81458508f7 zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c -PODFILE CHECKSUM: cfb6be050dfbb227d58b14434629e447ea54554b +PODFILE CHECKSUM: c43860700c490d04f7c96ecaa573cd991c27a3c4 COCOAPODS: 1.6.1 diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index df79a42305..39b96c6e55 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -3688,10 +3688,10 @@ "${BUILT_PRODUCTS_DIR}/HPGrowingTextView/HPGrowingTextView.framework", "${PODS_ROOT}/JitsiMeetSDK/Frameworks/JitsiMeet.framework", "${PODS_ROOT}/JitsiMeetSDK/Frameworks/WebRTC.framework", + "${BUILT_PRODUCTS_DIR}/MatomoTracker/MatomoTracker.framework", "${BUILT_PRODUCTS_DIR}/MatrixKit/MatrixKit.framework", "${BUILT_PRODUCTS_DIR}/MatrixSDK.common-JingleCallStack/MatrixSDK.framework", "${BUILT_PRODUCTS_DIR}/OLMKit/OLMKit.framework", - "${BUILT_PRODUCTS_DIR}/PiwikTracker/PiwikTracker.framework", "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework", "${BUILT_PRODUCTS_DIR}/Reusable/Reusable.framework", "${BUILT_PRODUCTS_DIR}/cmark/cmark.framework", @@ -3714,10 +3714,10 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HPGrowingTextView.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JitsiMeet.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MatomoTracker.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MatrixKit.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MatrixSDK.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OLMKit.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/PiwikTracker.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reusable.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cmark.framework", diff --git a/Riot/Managers/Analytics/Analytics.m b/Riot/Managers/Analytics/Analytics.m index 552e9e0aba..09a4108f8d 100644 --- a/Riot/Managers/Analytics/Analytics.m +++ b/Riot/Managers/Analytics/Analytics.m @@ -31,7 +31,29 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; -@import PiwikTracker; +@import MatomoTracker; + +@interface MatomoTracker (MatomoTrackerMigration) ++ (MatomoTracker *)shared; + ++ (void)migrateFromFourPointFourSharedInstance; +@end + +@implementation MatomoTracker (MatomoTrackerMigration) ++ (MatomoTracker *)shared +{ + NSDictionary *piwikConfig = [[NSUserDefaults standardUserDefaults] objectForKey:@"piwik"]; + MatomoTracker *matomoTracker = [[MatomoTracker alloc] initWithSiteId:piwikConfig[@"siteId"] baseURL:[NSURL URLWithString:piwikConfig[@"url"]] userAgent:@"iOSMatomoTracker"]; + return matomoTracker; +} + ++ (void)migrateFromFourPointFourSharedInstance +{ + if ([[NSUserDefaults standardUserDefaults] boolForKey:@"migratedFromFourPointFourSharedInstance"]) return; + + [[NSUserDefaults standardUserDefaults] setBool:true forKey:@"migratedFromFourPointFourSharedInstance"]; +} +@end @implementation Analytics @@ -49,29 +71,24 @@ + (instancetype)sharedInstance - (void)start { - NSDictionary *piwikConfig = [[NSUserDefaults standardUserDefaults] objectForKey:@"piwik"]; - [PiwikTracker configureSharedInstanceWithSiteID:piwikConfig[@"siteId"] - baseURL:[NSURL URLWithString:piwikConfig[@"url"]] - userAgent:@"iOSPiwikTracker"]; - // Check whether the user has enabled the sending of crash reports. if (RiotSettings.shared.enableCrashReport) { - [PiwikTracker shared].isOptedOut = NO; + [MatomoTracker shared].isOptedOut = NO; - [[PiwikTracker shared] setCustomVariableWithIndex:1 name:@"App Platform" value:@"iOS Platform"]; - [[PiwikTracker shared] setCustomVariableWithIndex:2 name:@"App Version" value:[AppDelegate theDelegate].appVersion]; + [[MatomoTracker shared] setCustomVariableWithIndex:1 name:@"App Platform" value:@"iOS Platform"]; + [[MatomoTracker shared] setCustomVariableWithIndex:2 name:@"App Version" value:[AppDelegate theDelegate].appVersion]; // The language is either the one selected by the user within the app // or, else, the one configured by the OS NSString *language = [NSBundle mxk_language] ? [NSBundle mxk_language] : [[NSBundle mainBundle] preferredLocalizations][0]; - [[PiwikTracker shared] setCustomVariableWithIndex:4 name:@"Chosen Language" value:language]; + [[MatomoTracker shared] setCustomVariableWithIndex:4 name:@"Chosen Language" value:language]; MXKAccount* account = [MXKAccountManager sharedManager].activeAccounts.firstObject; if (account) { - [[PiwikTracker shared] setCustomVariableWithIndex:7 name:@"Homeserver URL" value:account.mxCredentials.homeServer]; - [[PiwikTracker shared] setCustomVariableWithIndex:8 name:@"Identity Server URL" value:account.identityServerURL]; + [[MatomoTracker shared] setCustomVariableWithIndex:7 name:@"Homeserver URL" value:account.mxCredentials.homeServer]; + [[MatomoTracker shared] setCustomVariableWithIndex:8 name:@"Identity Server URL" value:account.identityServerURL]; } // TODO: We should also track device and os version @@ -83,20 +100,20 @@ - (void)start #ifdef DEBUG // Disable analytics in debug as it pollutes stats - [PiwikTracker shared].isOptedOut = YES; + [MatomoTracker shared].isOptedOut = YES; #endif } else { NSLog(@"[AppDelegate] The user decided to not send analytics"); - [PiwikTracker shared].isOptedOut = YES; + [MatomoTracker shared].isOptedOut = YES; [MXLogger logCrashes:NO]; } } - (void)stop { - [PiwikTracker shared].isOptedOut = YES; + [MatomoTracker shared].isOptedOut = YES; [MXLogger logCrashes:NO]; } @@ -106,20 +123,20 @@ - (void)trackScreen:(NSString *)screenName NSString *appName = [[NSBundle mainBundle] infoDictionary][@"CFBundleDisplayName"]; NSString *appVersion = [AppDelegate theDelegate].appVersion; - [[PiwikTracker shared] trackWithView:@[@"ios", appName, appVersion, screenName] + [[MatomoTracker shared] trackWithView:@[@"ios", appName, appVersion, screenName] url:nil]; } - (void)dispatch { - [[PiwikTracker shared] dispatch]; + [[MatomoTracker shared] dispatch]; } - (void)trackLaunchScreenDisplayDuration:(NSTimeInterval)seconds { NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory]; - [[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory + [[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory action:action name:kMXAnalyticsStartupLaunchScreen number:@(seconds * 1000) @@ -132,7 +149,7 @@ - (void)trackStartupStorePreloadDuration: (NSTimeInterval)seconds { NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory]; - [[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory + [[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory action:action name:kMXAnalyticsStartupStorePreload number:@(seconds * 1000) @@ -143,7 +160,7 @@ - (void)trackStartupMountDataDuration: (NSTimeInterval)seconds { NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory]; - [[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory + [[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory action:action name:kMXAnalyticsStartupMountData number:@(seconds * 1000) @@ -154,7 +171,7 @@ - (void)trackStartupSyncDuration: (NSTimeInterval)seconds isInitial: (BOOL)isIni { NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStartupCategory]; - [[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory + [[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory action:action name:isInitial ? kMXAnalyticsStartupInititialSync : kMXAnalyticsStartupIncrementalSync number:@(seconds * 1000) @@ -165,7 +182,7 @@ - (void)trackRoomCount: (NSUInteger)roomCount { NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStatsCategory]; - [[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory + [[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory action:action name:kMXAnalyticsStatsRooms number:@(roomCount) @@ -178,7 +195,7 @@ - (void)trackFailures:(NSDictionary *)failuresCounts { for (NSString *reason in failuresCounts) { - [[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsE2eCategory + [[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsE2eCategory action:kAnalyticsE2eDecryptionFailureAction name:reason number:failuresCounts[reason] From ed507ff52412e2b7015ba6258b5cefda14de9173 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Mon, 7 Jan 2019 19:03:16 +0100 Subject: [PATCH 098/266] Document changes done (PiwikTracker ~4.4.2 -> MatomoTracker 6.0.0) --- CHANGES.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d615617052..e204657a91 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,7 @@ Improvements: * Menu actions: Display message time (#2463). * Reactions Menu: Fix position (#2447). * Context menu polish (#2466). + * Upgrade Piwik/MatomoTracker (v6.0.1) (#2159). Bug fix: * Registration with an email is broken (#2417). @@ -52,7 +53,7 @@ Changes in 0.8.4 (2019-03-21) Improvements: * Upgrade MatrixKit version ([v0.9.8](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.9.8)). * Share extension: Remove image large size resizing choice if output dimension is too high to prevent memory limit exception (PR #2342). - + Bug fix: * Unable to open a file attachment of a room message (#2338). @@ -61,7 +62,7 @@ Changes in 0.8.3 (2019-03-13) Improvements: * Upgrade MatrixKit version ([v0.9.7](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.9.7)). - + Bug fix: * Widgets: Attempt to re-register for a scalar token if ours is invalid (#2326). * Widgets: Pass scalar_token only when required. @@ -123,7 +124,7 @@ Improvements: * Key backup: Update key backup setup UI and UX (PR #2243). * Key backup: Logout warning (#2245). * Key backup: new recover method detected (#2230). - + Bug fix: * Use white scroll bar on dark themes (#2158). * Registration: fix tap gesture on checkboxes in the terms screen. From 05e9fb0afcd7c229b4316e7a424a3990f0c087b7 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Mon, 7 Jan 2019 19:24:09 +0100 Subject: [PATCH 099/266] (Almost) fix the Matomo migration code --- Riot/Managers/Analytics/Analytics.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Riot/Managers/Analytics/Analytics.m b/Riot/Managers/Analytics/Analytics.m index 09a4108f8d..84bc65b5d8 100644 --- a/Riot/Managers/Analytics/Analytics.m +++ b/Riot/Managers/Analytics/Analytics.m @@ -36,7 +36,7 @@ @interface MatomoTracker (MatomoTrackerMigration) + (MatomoTracker *)shared; -+ (void)migrateFromFourPointFourSharedInstance; +- (void)migrateFromFourPointFourSharedInstance; @end @implementation MatomoTracker (MatomoTrackerMigration) @@ -44,10 +44,11 @@ + (MatomoTracker *)shared { NSDictionary *piwikConfig = [[NSUserDefaults standardUserDefaults] objectForKey:@"piwik"]; MatomoTracker *matomoTracker = [[MatomoTracker alloc] initWithSiteId:piwikConfig[@"siteId"] baseURL:[NSURL URLWithString:piwikConfig[@"url"]] userAgent:@"iOSMatomoTracker"]; + [matomoTracker migrateFromFourPointFourSharedInstance]; return matomoTracker; } -+ (void)migrateFromFourPointFourSharedInstance +- (void)migrateFromFourPointFourSharedInstance { if ([[NSUserDefaults standardUserDefaults] boolForKey:@"migratedFromFourPointFourSharedInstance"]) return; From 47a3618144d4479e14db1689773b0cd60bf1a87d Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Mon, 7 Jan 2019 21:14:34 +0100 Subject: [PATCH 100/266] Removed a comment about why Piwik was pulled in directly from git before --- Podfile | 2 -- 1 file changed, 2 deletions(-) diff --git a/Podfile b/Podfile index 467fea952d..e0e6f35f57 100644 --- a/Podfile +++ b/Podfile @@ -63,8 +63,6 @@ abstract_target 'RiotPods' do pod 'Reusable', '~> 4.0' # Piwik for analytics - # While https://github.com/matomo-org/matomo-sdk-ios/pull/223 is not released, use the PR branch - #pod 'PiwikTracker', :git => 'https://github.com/manuroe/matomo-sdk-ios.git', :branch => 'feature/CustomVariables' pod 'MatomoTracker', '~> 6.0.0' # Remove warnings from "bad" pods From b466af96314b7ac5f4c9dfe03749e7b4552017df Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Mon, 7 Jan 2019 23:02:03 +0100 Subject: [PATCH 101/266] Complete the migration code --- Riot/Managers/Analytics/Analytics.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Managers/Analytics/Analytics.m b/Riot/Managers/Analytics/Analytics.m index 84bc65b5d8..4491c4158a 100644 --- a/Riot/Managers/Analytics/Analytics.m +++ b/Riot/Managers/Analytics/Analytics.m @@ -51,7 +51,7 @@ + (MatomoTracker *)shared - (void)migrateFromFourPointFourSharedInstance { if ([[NSUserDefaults standardUserDefaults] boolForKey:@"migratedFromFourPointFourSharedInstance"]) return; - + [self copyFromOldSharedInstance]; [[NSUserDefaults standardUserDefaults] setBool:true forKey:@"migratedFromFourPointFourSharedInstance"]; } @end From 853627fba13459e4a7b56a3440836e89fac9a695 Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 23 May 2019 21:57:26 +0200 Subject: [PATCH 102/266] bump MatomoTracker to 6.0.1 --- Podfile | 2 +- Podfile.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Podfile b/Podfile index e0e6f35f57..a3b5996a7a 100644 --- a/Podfile +++ b/Podfile @@ -63,7 +63,7 @@ abstract_target 'RiotPods' do pod 'Reusable', '~> 4.0' # Piwik for analytics - pod 'MatomoTracker', '~> 6.0.0' + pod 'MatomoTracker', '~> 6.0.1' # Remove warnings from "bad" pods pod 'OLMKit', :inhibit_warnings => true diff --git a/Podfile.lock b/Podfile.lock index 207baf23f5..c795bbc573 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -45,9 +45,9 @@ PODS: - JitsiMeetSDK (2.1.0) - libbase58 (0.1.4) - libPhoneNumber-iOS (0.9.13) - - MatomoTracker (6.0.0): - - MatomoTracker/Core (= 6.0.0) - - MatomoTracker/Core (6.0.0) + - MatomoTracker (6.0.1): + - MatomoTracker/Core (= 6.0.1) + - MatomoTracker/Core (6.0.1) - MatrixKit (0.9.9): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) @@ -102,7 +102,7 @@ DEPENDENCIES: - cmark - DTCoreText - GBDeviceInfo (~> 5.2.0) - - MatomoTracker (~> 6.0.0) + - MatomoTracker (~> 6.0.1) - MatrixKit (= 0.9.9) - MatrixKit/AppExtension (= 0.9.9) - MatrixSDK/JingleCallStack @@ -146,7 +146,7 @@ SPEC CHECKSUMS: JitsiMeetSDK: 3e66564af7f38a19142338955dd7f581801852b3 libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa - MatomoTracker: f1d0adbe609f8db3740d24e59e16a1855d263921 + MatomoTracker: 3ae4f65a1f5ace8043bda7244888fee28a734de5 MatrixKit: 6f553797e1ad42794b5336afb5cecb975ec69daa MatrixSDK: ed0d0cee4877955052f19730bb3ee727e01ec948 OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639 From 863b1a719b1e90a53b6c4ce3fd7ad3ad88fb496d Mon Sep 17 00:00:00 2001 From: fridtjof <2780577+fridtjof@users.noreply.github.com> Date: Thu, 23 May 2019 22:11:00 +0200 Subject: [PATCH 103/266] update Podfile.lock --- Podfile.lock | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Podfile.lock b/Podfile.lock index c795bbc573..7f3b5e2b7b 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -15,6 +15,7 @@ PODS: - AFNetworking/UIKit (3.2.1): - AFNetworking/NSURLSession - cmark (0.24.1) + - DGCollectionViewLeftAlignFlowLayout (1.0.4) - DTCoreText (1.6.21): - DTCoreText/Core (= 1.6.21) - DTFoundation/Core (~> 1.7.5) @@ -100,6 +101,7 @@ PODS: DEPENDENCIES: - cmark + - DGCollectionViewLeftAlignFlowLayout (~> 1.0.4) - DTCoreText - GBDeviceInfo (~> 5.2.0) - MatomoTracker (~> 6.0.1) @@ -117,6 +119,7 @@ SPEC REPOS: https://github.com/cocoapods/specs.git: - AFNetworking - cmark + - DGCollectionViewLeftAlignFlowLayout - DTCoreText - DTFoundation - GBDeviceInfo @@ -138,6 +141,7 @@ SPEC REPOS: SPEC CHECKSUMS: AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057 cmark: ec0275215b504780287b6fca360224e384368af8 + DGCollectionViewLeftAlignFlowLayout: a0fa58797373ded039cafba8133e79373d048399 DTCoreText: e5d688cffc9f6a61eddd1a4f94e2046851230de3 DTFoundation: f03be9fd786f11e505bb8fc44e2a3732bf0917df GBDeviceInfo: 2c65ceb9404f9079264d4c238f5b81916fdfc5e2 @@ -156,6 +160,6 @@ SPEC CHECKSUMS: SwiftLint: a54bf1fe12b55c68560eb2a7689dfc81458508f7 zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c -PODFILE CHECKSUM: c43860700c490d04f7c96ecaa573cd991c27a3c4 +PODFILE CHECKSUM: 16b6518b09d4e3af0af46ed9c1338e9df8674aff COCOAPODS: 1.6.1 From 12c7424836e09bc4399f04ebe990c87f773aeb45 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 23 May 2019 23:36:00 +0000 Subject: [PATCH 104/266] Translated using Weblate (Bulgarian) Currently translated at 85.9% (610 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/bg/ --- Riot/Assets/bg.lproj/Vector.strings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Assets/bg.lproj/Vector.strings b/Riot/Assets/bg.lproj/Vector.strings index 562310eb87..c17f172d06 100644 --- a/Riot/Assets/bg.lproj/Vector.strings +++ b/Riot/Assets/bg.lproj/Vector.strings @@ -673,3 +673,5 @@ "store_full_description" = "Комуникирай по свой начин.\n\nЧат приложение, което е изцяло гъвкаво и под Ваш контрол. Riot позволява да комуникирате по начина, по който искате. Направено за [matrix] - стандарт за отворена и децентрализирана комуникация.\n\nИзползвайте безплатен matrix.org акаунт, собствен сървър от https://modular.im или друг Matrix сървър.\n\nЗащо да изберете Riot.im?\n\n• ПЪЛНА КОМУНИКАЦИЯ: Създавайте каквито пожелаете чат стаи - свързани с работни екипи, приятели или друг вид общности! Чатете, споделяйте файлове, добавяйте приспособления и водете аудио и видео разговори - всичко това, изцяло безплатно.\n\n• МОЩНИ ИНТЕГРАЦИИ: Използвайте Riot.im с досега-познатите Ви инструменти. Посредством Riot.im дори можете да чатите с потребители и групи използващи други чат приложения.\n\n• ЛИЧНО И ЗАЩИТЕНО: Пазете комуникацията си в тайна. Съвременна технология за шифроване от край до край гарантира, че Вашата комуникацията наистина остава Ваша.\n\n• ОТВОРЕНО, А НЕ ЗАТВОРЕНО: Приложение с отворен код, изградено върху Matrix. Дръжте данните си под Ваш контрол, като използвате Ваш собствен сървър или като изберете сървър, на който вярвате.\n\n• КЪДЕТО И ДА СТЕ: Поддържайте връзка където и да сте, с напълно синхронизирана чат история на всички Ваши устройства или онлайн на https://riot.im."; "auth_autodiscover_invalid_response" = "Невалиден отговор при опит за откриване на адреса на сървъра"; "room_message_unable_open_link_error_message" = "Неуспешно отваряне на връзката."; +"room_event_action_reply" = "Отговори"; +"room_event_action_edit" = "Редактирай"; From 55f3a68afbb5a3f91ba55cc3e5cbc73710761bdb Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 24 May 2019 11:33:44 +0200 Subject: [PATCH 105/266] RoomVC: Prevent user interaction in timeline while presenting context menu. --- Riot/Modules/Room/RoomViewController.m | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index f8f7265722..8ccb07ecc4 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5089,12 +5089,13 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce RoomContextualMenuViewController *roomContextualMenuViewController = [RoomContextualMenuViewController instantiateWith:contextualMenuItems]; roomContextualMenuViewController.delegate = self; + [self enableOverlayContainerUserInteractions:YES]; + [self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:roomContextualMenuViewController from:self on:self.overlayContainerView animated:YES completion:^{ - [self contextualMenuAnimationCompletionAfterBeingShown:YES]; }]; if (RiotSettings.shared.messageReaction && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) @@ -5153,7 +5154,7 @@ - (void)hideContextualMenuAnimated:(BOOL)animated cancelEventSelection:(BOOL)can } [self.roomContextualMenuPresenter hideContextualMenuWithAnimated:animated completion:^{ - [self contextualMenuAnimationCompletionAfterBeingShown:NO]; + [self enableOverlayContainerUserInteractions:NO]; if (completion) { @@ -5162,11 +5163,11 @@ - (void)hideContextualMenuAnimated:(BOOL)animated cancelEventSelection:(BOOL)can }]; } -- (void)contextualMenuAnimationCompletionAfterBeingShown:(BOOL)isShown +- (void)enableOverlayContainerUserInteractions:(BOOL)enableOverlayContainerUserInteractions { - self.inputToolbarView.editable = !isShown; - self.bubblesTableView.scrollsToTop = !isShown; - self.overlayContainerView.userInteractionEnabled = isShown; + self.inputToolbarView.editable = !enableOverlayContainerUserInteractions; + self.bubblesTableView.scrollsToTop = !enableOverlayContainerUserInteractions; + self.overlayContainerView.userInteractionEnabled = enableOverlayContainerUserInteractions; } #pragma mark - RoomContextualMenuViewControllerDelegate From d70dd52ecc9bfde11a9676ed7909d0de067ba9ee Mon Sep 17 00:00:00 2001 From: David Cordero Date: Fri, 24 May 2019 22:13:43 +0200 Subject: [PATCH 106/266] Adjust size of create direct chat button --- .../create_direct_chat.png | Bin 1649 -> 2806 bytes .../create_direct_chat@2x.png | Bin 3798 -> 5102 bytes .../create_direct_chat@3x.png | Bin 6070 -> 7400 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat.png b/Riot/Assets/Images.xcassets/People/create_direct_chat.imageset/create_direct_chat.png index 388b3a1e804597a45182d0d92ed14127aed9cfd0..de289fc08aaab7c91dc8b54f6f465213bec9d5cd 100644 GIT binary patch literal 2806 zcmZ`*2{@E%8y;DbrEC$6OcRyLY#K3*Ibw*cX=E+NEM}O+KQqiA3Q3%)4m!3_LXDkl zM=EKhC|hEreX*QmNu|h{p=Ewoeb@CZ@BQ5Odq24#yn;K;;R=8N94$ye!z(lMMhUs*6u4KxWn|5l5ej^Wbu-0D(Yg7Lkl{#oBz6i$>;P3WpPnf(T`&xPY#v*(}}_U zR+gwhs5k;>=O5BsP;4B5r{jgUZ1m(DMZ?hJSWsL1o3H2eU|Qwn&D7eUnIH zi}@ZuZ7|!udq{$d1J^F5*aJAdXnENO%sD zg<~>lnE5K`;vvWwVhS-{&`L7CiP2E;K9bm13!8Qx742G#f=K!O3xHABYG5b&c*s%g zusd)R*;l6=X+WgL?bDa5qOsA{D`mZ8&bC`wcj4%`waY>BdTqHt3taRXGnXs4Z=_7Ov_D1Xl-2kq4LHVGG!!uci&q|jV>i)zgD(jQYlb4YLis^lF|qgJu&3Zj^*9i1`p`k(Y}0k6=Z#^@5o zW%Ay>QUr2f!>xn+tNTC-VEI9WYOh6n`SFBKrd`CyhN8Lnp_>994M7X1H1?W%u3wtl z^Yn71mltXxb~lv^qAOpJ?qfB4qL9y{D|(ffkHL4INXzzT)%gjw4g;&lcEH2ybkO1Y z!p3fhOHP&0C)e51{o6h%sod72Af$HT2=V3lu7##-~DqYQ;ar!13Rw4c>0PS0X%(&M-j$_WWdX15d zeT7kh;nL@>x+o^{wCm6_3Q67rpVt@l-*$Fd1Br~ivP%;tOQR6)e)*jJkU)t_kN&GG z-#qC!vZ*QP{IV@X@M>4UurqUq@}tdWN*!g%EBf9(Oh8YNgU2#SWon+ySQCBvL*9WY zmFG8$eTI}?p4~oF-5jVSi{`z(@Ws_II-cLz5Kr22-0ZaQ9i?^R%dGlIvx zE>^?2K)#2cNnN)?TFcHA`Iymh`8`z3mKFE2I^8z48f=g4xUTX@sy9Cd7O)mTX_N|Z zSpD})?baaOAb4r3cH{H)%jZmE3NJMS8V)CRgbnNQJCB}CcTT=4Xl*~IErT_(d+9+~ zHI7d7^K-*3-9bX=1bS-=F7n6_4gR5Y^xBC&Mev(3>zKdO^|dofU9#=;P2M%!QQ~G> zx_f6gV?*8gLsWa(9){>eYJ!b1kSfc&v(r{w6)c+-E{$2M}`MDDdU1!d3L zA09A%-`AN*KJnV`WcMG2T6`ei>8XEYVNTIRPeuAR#ay258hCm4OU+fPFu~~{a7jko zAyuhZ9qz1gb#91-79dX(Iydkr?e!7i3kS2tv$4DXWHe#pMuXfNvI~RwPyJ-|yk)(d z0zD12@HfBO`=(`Q9*(*(6u;AaCQdIGd&MBHw&9QJX@zy~_qL4IKnBc9n@yMyIQqW*Vw=4x5=VC>NQs z#ZRI4nr0jh30wZA=}L}31(Ng{)kxFsrd-t+goGxa+{U*?(Ee9gg|ETw>~FD1jlszT zHew1iqHvILnIqSr)O^DwHN5)w7|yfHjK~MOt%YIgwcbbkbysdLoJC{4Ji+`s;*IjX zT)z}PqL-Rl%q`Md@pV5x;oN2;`N_NfRdV>ZqsL3aG*!E%2Z!a03!(!|R?-ib?%wW7 z*;ju;Q;0@?fJc>o$Q!iW^O>e{mu2PF*xr+_p_q6F(d+R^>WZy-+;qs2hCBlH!L%kl z*RkT5iO`3?OtJQWCoP<4Ej#Nwg-6`dbk)3pJ6-IZyI%+@g5KL;@90rof7hSD!W-=y z_ij_fheh6;o3zjl{`3xX3w^Zm;?j@hB?p%pN;5hSj!o;u1*H_#sL6N*AFDL_|4|a9Bdy8-fs81OjdijW|T8 z9FQtNTtGrpEfi7(A;EgOo=Nnw;55WDKL(K_WRwQtluUTS&;wBx$n} zN6;mb;u7*Nk`TWje+lXMGZ*N{*m)t2nw=6`w7Vhs=FXd`Y>olMUI4fsN^gzUrdTCG z-vHbh>NjS@OXvTHR-gabZ2=@d9NkF2ua1$q_9UP@2oVf_F+%5>fOVRTzK`gZtux}O z*(;%I(wx{*K=QrOO;oyY5WVFEw6|T;X1kJC(5v32{I(Cov$L05)ksSK$*)Iq^zA>- zV~#k49+PjCaG~lER6>WHqWf-mM?5flEmR$jlOh9>$A<4EQ@)R0Q*hK9e^iJf8R8jn zc;UM!!h{ASr}y{M4~uVM4tNf|CUiYfgl~ARfMk;1r8|dS6yvAL4a<8J2&I^Oqi{R@ zN7HD0w@01HOA<6s8)#g-T>QCN;hF{{C-2$=FrQ*bUEgeDv6dI&U-a{R;>h=Bn~|fb z+pm1Bupjfy7hMO3iSPx5>`I3w2n7ZtCks!b-jn$2Z=(LB6=m5^+PVaiEzl6NsWY6w zKm)ZNUm;$qJRx2!o@yjQLqKZI;bGO?J;#ef8IJ*vG@6rq0ay7&pGN=-eq?yHw3?9p17f-(p6JV3rIZ#cFS{)M!7)AtRp}0YJ%qwvz8C$ z9G-VP^EGvKZ!$k|8^(y&vSeKkQVUPDwn_;czO394ptI85b(z-zQmgW$Ej(R9x8;y^ zRyz{)wbuq0lB*NICA5I;}k6! zaYMb!b3oaf47m~{1;6NIK%)PmJCtw7MR)Otas%U@pW)AbZV^>cts970?a$g zkf617_e^cZFi*^|vdDslz#ss@1SJa^0uq3feU2tS?a5$+9t7j1ENBRbEjin=OH&|X zGA|jn4Fs#?raNloobg(!ckIG4(dECr^P;B0>P_IC*HzTho|l2VwtDPvR5AU}|Lhpi z{Iq|W@VRBai)F!w0EQSFOtxv0Z0Rh~UqAUY@5tg|_2?lh;qxh?KYw&&`b2q%wJU&4 z&bB6P&lW4s4PoX7h7<2<-a-ybTMRLn+GasRKwdN~J7i6M+LK|UjUJ?ow9}44qrX_< zyBJ%sXTh5Q1A=*f7BmFR1%rJbFw9mpB~qLWCSYSI#}GktDJJ2qI{ZPH4bMRw&R9R$ z|0)4?mO-!pUDx*s;$}57i-^h@PfV) zuM{8V>T=aO*`;W0Ej~aDqQ_*en!+Dj;Re!2VWg z4`5KS>|hjCK#B{T(J(8OQp-pgP=SFX)B&e;t9xj~g05Jt+FU+2LX$jEP%SM{j_XMQ4z>N{T zXujtev5u^(CRCrKz49|>7QY?@V#*5!Zj6YAa)|YUo}jccM#w%tPal?5Kg4WJY=5c= z9QM*;mUWqECXRx2HQ3BW0da?dQQU1@l-Jp z6b{SoJ4t&fmo~TXrU5auG<{eg&>ZCMNowXF8oCGR#}oa;ZLJ256J%|4w{q)PIS$o> zDkt-A_Hh#@4NW}zJdOs8J30HnlADO;IQvAG28>s-@+7jHW%wg+Bbc=!TJ@`syvU7tBk9xdaUE_{fb>j$3P%!SX+yLzF-AD! zJt2-RcqcR@(9@g31_0Co5tKtuG|>?l=!x_4MFgsYju;5a@gWQa0gos|4|R}@2@0r< zC!m4K5G9Bl2+0Tp0@VmEt_X7-y}#v@kvhnoNc2WPp#cE_kN`ypp5O+Rhr{7eIR&VK z0+_-8`v!Rt9RtB$zNddp^3OavXkTXn#+!)2djSvUb#%h}5!FGULr4Gl{fd)_as96+ zFW(6i^usLmf)fnhKN*9J4)zx_`enTrP9WGdK1vT zz7!c!?r+I)?03G+f88Ke{_$}f{9SN?fT3jSco-K_{%;4zvETW&|K{L0cua6udISm+ zh{oCIU?>GW%0gcLtQz!REx#+Z@i;ue!rRdqeHhu1*&W_6Vi) zEs~L<{AUe7GN!-4 z*3{THyL3OPkxkr4R)P^2-fvDr3tGZPp+J!+%`^FVS}#s8u#4R{3bSNK(MX${bE)d_ z7;s;ry(Dew6tU7Apc;}rGS=~NyQ{W-%x*2)E&ol{$E>WR&m%#fvjSY*?uqwcI3xl# z!6_28tP9w+dqru!x<#VBl}5swU~Wv2d%f{J-?*4J_V%`}aSLr69G{=Nf0vECbw#py z;6Mkrc6u+BSRL|*MQh=#`(mn^nnDqcykzI8?so5;|JnKNa^m+S~KP8i>WBghxv3Ah7xj3-nKc2 z!(W{YT581uls&TYlRf!5lhs6?XcG0Ar zP#paL+g8~ff!a$iUJC#>9$|@(B@yglpN1N*nMqc#igsaVjBYonoH06g+VktP*8v-- z`rhd6@j9+&ugZ`@o&)y*^ZxzssbXN|!5r4jq}-i0nLs7o%1~j?-6d>Cc-YBA+2)gb zxF~+Kr`dvW_X2zI(A4b)j)u*OXR*~hpLC=f=3Z9{%?7eg{XpLr*zJx5^lpQs`lS&$ zVsCgVJrrZ?J(ceKF^grYQJR(4iI+XO{z}_)DG-FEJ)@xIN4EhFdH9HQQ+o57VG-+N z-Isu*syGd>T4BxBfTrnDZOUhZp_ zL4wYkEiaaT8iABmS)ilOdHVBam_F?)c}qikU8ApSR^d<3BV}n>Pxcnu?8#ZN*9btkym!Dh>?I+)?zZmO{GNP&Ou-9efmZmrEh zWw+QRFI{pSLzICK$5>>i6vU`oBbin-jI(v*(aYs?{Qd%3QkHC+kDx{VkDBTSUHrqX zJ8dJkV>~uRbox& zOa2(Pa?|`ySfb$b2R@TE6&zpeRPwF~ap&4^Nif3#pg)kVH|=>vRYESRLy9*17T?1`dQxVu{~p$O=!ra@ZH!+i9x|%%v;L| z?lohpsMyO?q*>UT(^F4+E63Ys*~Xi3eTT|f`(zjUnE^?Q1p|)E_4^v=iDJyXf&xKo z!U}7`icE!zo@Rayoh+;T$*pMWgB{`4bo!RIcD>ZivoDsJZ^8lT-|aGmjF;OrSyuBw z)@+-l`t=90qvRIlX1AM4OjHu7+qrMc>Gb7FpS;efigt2!P;C>Ae2;D~Npw^SU_N=R zXFC5{iZADxGa*c(m+{r3ivT_UTbH1<>#(A9+(j+NQB@wwf+f${lNpZ*5+ zG>kQ(SH5EE@zI(S)GqbI!G;&iXZw|9P5)eZbsV*Ibk(D;`SE+}1?4J_WlB-nA4}Te zs&vbOH1c;d9OPRMB))#w2i;mXDYLNJFb7VB={Y{;oFYEX2OTAsaEDcRPU!DRA{rdGQMJkmFJU1qPmDa`IfymOT?O`8Zz z{9&S6!bNZ``y-xL?IG|s5jf4m#NgW!Mm$z8^545$R^R{z*rzEKkBL-Gl+1rCl}D!8 zr`u=TH{7I^ODDqTo1V+7S4`j{I;u%k6NV6x_BqC^K3Z0TukmbtvyW@kLk-&r%eoq(C={eGDEY)fax@)cwLeuwye}R5( zc(N&FQhG1~-SnHvz7>jIUho}4;8+w@w=^AkF_}Aw) zlAIYoA0V*_sJY@~zDLOf@^*KY;&BHq^wRP4>MT z;>gLR`Li7yMGg`T#}F1$)eChUh({zT)G1{596$;xXurw=gWxWiRjB85i zOi^7G%C>41sr7K-OkWjU=C$E`pDj*LMbKw`_k{Xx!Iu9`e3yegLxe=F?Z9N$#p~@) z+!Fd%rH0|&4W&;QX6{wr9Z%M-sq?GN8e>|kij=&EB1lDRC!y|--10f&Hhd|b)8J{v zI=o5rR+L~|M7|62UwXn7qh1jPPejjmwOyW=)rEBNf=6$KuU0(Zdeh?kpe0l+TwH7< zG)0l{Fo0HTfbw+WNZMiv7UO8j3q1FRDls$ps_SP>^-0p%Fs}7)mrhsloN&oo&}={I zMy`%-^7`GbxAZOF&&GVbAB(>L9}Bkt=N!-qKG=(d@U1N>=`jhVF?`Zi{!2g3u9PkQ zL%mHVj|0TV#`z~pJFm40+_r7>l(8z0)i6UIaM26rnbw}=xQK&hE%;fvlp3>WRT#B|2KNkz5> z8!b6S-#G8Ztk0uhm5kyl18;&#*bm&{+S;`2oW8NqGz2ZLXEo0vojN9=xisg(G1;vp z@aK%z%p6>o^dY3!R+HK0Voq$t((+x{>dQzgI%r>UHloT-&Geb1x>Vj7MSo$Dno}Dc z4P}Bi>p56XL{lrz_?WA=!x?6sw?D4GbPgviRt&3>bxBR3yY2pxE|OU1cAso5=eiDm z=kyM{mf;bTQ(H5+be|391W$~DGdut?%fJ#Z@^EF^w$gPCk05S`F%=15FN|-e<8k$B z1Nn~uNy2_-dFmdE>%;wCY&{ZaqL%K(_*`<1%AO1wO0qcDA68)OcBQHK`TN0(V)ySP zmAFE-oED6)JXsa^sbsMH4lC_+mf`8k+fR$cF3Fm+KlNZf@4;R{`(Cr%g)!XYX%2W< z;kp2CULb>ii`N^$mq|u9_3?>4u(t{gtb@6)7GuHQESp^N40ANf7vHZ87bO`{>*EuA zg{vaZ4xX2^D9=D4OB5cLYn1<#z$N^-Ipp{L1xaL9T%P8EXu;-mFMFP`6-y%W_JQ3O zRhO}TL`-g~gg>dLsWK53CtCD|1svBD=vVMv&?d!y%H@ftMMrND!K>{a)RAGd-<`Z+ z;a(HH!;!{ZTO{_F{GS|(M6FPwOA>uZF`Thku*c3 z(nPPmeew4jy^=8_jV0gvZfs^nS!wUjn9x})-hzb*j^YQgB%}h&{H}y#GaY}FiKNBX zzxZ~v{jOELXg_>F+?xou#J$1pq`E^d2N|;_;w)6tzlHn#Nx%6QYnE*Y!^DS^ax3eE z;MBz{4mSFUZmu6ydX;Ohpjqqw>}*uYLb*D)T|Mv{H<`zMWFhc9zFITf>C^)B0(m6R z3RbCSuGlb!7Ji_q>KL?$GoVMmC(<|)taXn}ng3Xuhj_s;XXt)2tuY64fAGxDD zU}v3k`_m&?GYkB!vk6FCc7KiU(&^T6k`iBOPm;66;LyMWY8%I(pP4zO+Z~!n^#0W~ zM(MBCP0~J!kj)dhUa6cF+0d>jbKm{+)Y+6VtT0?C%4br5*yB*T@d@?TJz3OA+uf&V z6UDZ5&-5bzq>?*W*2vvAt0ya2VYa5z`SWzFxVhr0KhkOmp=PlcG$UpyW{CR$+JY2b63afh( zGqmhv;_f4}ahflny}4I$U1AIF$`!Z@Czlq`+VRA%{t?MCy7d5uHqm)!~~j zL&|OQJSgvYaeM{R`8?Ve41JtZ8{%wFxEZZBx~`vp9u{bQz0jDP8J|8z4YJY{&c5aE zDYMrBlzo^><&fyl9}3e?oMhXztJUYA-YPcZVqX$_N4b|x9ln3!9%QJ;@feATrqRGO iuO!87-~KvvKoug_v^DWKw(9T;oT09%PMOx#i2nmA4%Yzy literal 3798 zcmV;{4k_`8P)Ii9?zV)bMKvd=k6no@9R9*UfyS)H&e!(IdM-rou2HbY12bYJw#?D5&r}*bdoWh z#EdKM8TWD($8?DdU-GU7SKJfck@+4UFTme8<$0XecTModk7jI`jQrtNE~k09cNtAl zKNaXM2>%w8brpWyAWA-xax+gO-U>51j{!&jMt7R_p02R!m5YpWM zyd&YDPl4PYQ;y%FrRHebwNokyWX!G{?K`pwcFE%)%N7u4S;9$EIeW}fwH<-I^#<+O za-4Zrp|Z#1zD*T@%--IcplP1MfAC`<&5Gp08X#xPU!zQKLNmR}gQH&th@mM#Qb8bd zVC^cJncfP+^bxdt+M9w@Y>Dy=4F0#NJ%5n5ogIrU*?Yk7|Adm;#AjA7c_`viJ*UjliqkE`&h zOo#bf*n!W{;OOhjY?@t$rV|L&)oKvuFo<(Uv%yGJ`pl<5q^D7>oo{Bon?@k>$A{YJ zpX0kx_~%={Wfhf((PASyL^t;g@o)b$8&xXR;%y3n%)T`%(4X@Tv`&Au%;`5r1ek}> zfAlc#IQO+fvi+%{mTdAnpP5vAkBew%V~P*&-holvt@*p=z{P`%6B}-+s<5!I-8Lb z$h^4rM!J0E7z!eDan!|!z!)uEvYB5v`&E=lMoJ*FXU(n9I>%5)x7NUCloAOuZW*8@ z-po7CeI91C{OJM>G+|!i16hTz^=GbtpEFZK~VWmXaqTv4=6Xq0n7nyofjO)s5T~(N!0B`soI!sX)#wqeFt76SB>zSzUtby#(8(BTk@=J?T zB=69(D$B`P>9*XX$w!l?=?t{5FhZ?i7?bHg>=zGU%Y z-t@SvfOXSA-g09lPr7O0o_kRgB69~KaHkcT$&a1Zr?#Q4M6b zm7BgI%R#D>EZgvAq4WjHFS&KG?e-QYhwMqH7nOF8t*n7$D_tG3$Q(@;FEEBUEGHKFSU@1ViMQ;-tWFW|BLGfVZusBf zegT2J9pH5>dl^ppC=>DJbbvCqaMNykd_dFpZY;}Y%#XKiA^H{eH21r|y-oDl5&xU) z-Frt%{MBFZc^b$bTq0*rWZf;p@nJt*IsYf`)NLtpWDbdW1R^>0CanjT%oRy*B11s9 zAsq3!`2><9lhi2!)etD-$Q*&}VQt=dTh)T=x2z)oj##b~ahM~JchXxfm zjv`lVUak^Ij)_#K2sj~dl`B^X(rQFUxmLZLKti6Ae@LsS zqsce810&jK6&4M=tsg760~<)HFm;MR00h7tX@x0)0$`^>ECNCxlVU4Q#n@Z~%=a=D zhOSU`ia-zq_=5CAj|T~=VH5%3PLA@d1X3;pLjc^#(fY0lw8W-^#h{@wi-2$j+{y9A zk7fYEL}r1|f(J!lLby{JOx0>ED2&++Hzq~*2xLeORZ*u1xFcZb3-Rm`C`X^Thg6@6 zfMt&5j7Ojj8dgJEY2l237|_BQ%Om{ZXGed)Mb1T4rwF(paHhzax$RvrqG67^gj7F@ zfS)T~6=pHXols~L0XGD$a^)(4x^DOsfv4OeP+u8FV2V7hSP13_#Lv8c8HV)783sm+ z?t%a~V!2ZEJ4Ybnel-aayd z`tQB0V5Q|OLjbb394LG3f2>NNqQ@az)fV!jmW_LnxyZh%t4Sc9F@&#i)h$XaCX;G77=ThC8o&g*jy@u`=VK)^ib$-H)9 zxV9J1hcSj}@0wu@bKKz?M*XD_0p?RQc&?wBoi;vn(m(=(7omAthd5G53g;}#@RfPm zl{^xyGl9f2jH>gv9Flu=Es4OPqKusjR~Lb7tT*=zK^@seWzMOn$K)d*G8Ng@>6~pK zf4pYjniVu_MxgGl^S{^ov?9RYQ5*O1j&pU-1FK&H0VA7GjvhxZwUeQhR#-EUZ;>Hn z8H7B7PapzGKn(AHfQ#Bg0!F&sp`t7Ul_D^KY-;_>2SKHifl}5$2|xg{voZ{xm4*mp zcQ?y-Jc#7&W<$ZVp&^@qz!*Vx#U1JbCm+a&E>F$8xb{Z6GW{t!8CR5l)8_;tAQR-5 zReiAUT&P?Gkku%+@}22~fYENC_m_u}%ETSB~L!RsGr15Dg$OMoX83 z*4kPuAEDboK$S9Q3%aUjBkUq9oz_mrN*KAl6YPK? zuxti-B#%weKz?w{p4F?7jl;Uy!;gx?y{w*)3Gm)Lb%y}acxgI;q*&gPq24Dzq?hn; zeKbXFkyz~lPpr`!9DTi>)e}*bJ`n^e70w)7-9^)U03>=6@0wD*mhzIoYXQSUugQv0 z*F}GElp#?BDizr5yX|%=%h8}A_-Ii!R+trquIiCXruwW z%bG?KjdPT+KToYKR!Vp^jjZ)~=$N>8;R6kHBJDxVLr5V@q7ovXcENv8`OS zoxze))Kn0t1Qt3WZ=!;E3jdjpK`XV^^jR7BlkT9$A~(lu2CDgkHAt{m+< zvWW~oj(1xC{Ib%wR9(GuNsf^00FO<)EA7La^zx*VKz>lo%e~9!G7msAJqjY-jc*

    $!|7bN4|{^(k6Uc2U6wzeqz6u z18U_wT6?5u?t~oH=09&M_WXpLU`r9G92={hrl=o-w|BuX#pE{X!LK_qQh8*VKpNvP z_&-Ox@e?`SIPF@d99REyOB2Xn(9*q~R&si>o2E?7M!3aNCkQf5V`&TFb>PHQZbo6!e@pJP=a9#8ZbMW@}Q{dsb5c>D;uQ>hO zod2VQK>nT9d4k{z4_E>u4*qX6c#zxwpj~+WC(X(6-&y(i`+EM)$;lB6_k?@F5q`*X zp2UBZJFo4(iT^LiAehfzV!w;{S8nnbkwc8&NN-R7ixQY3-24{o%gfd4Sdga0d9{(LS#^t_$ioP*V1esD!eaS1sw zaTzg5X%k5ah`0>oVi^8``6Gf0kE$;m=I8Be;_dCJ_&W`+izb&8=mtpYFYcd+zsd68 ziGS&i82v;N&F^mQ>ByXPe{OE19k2L(^NGvh!OG^y_VDJOcA(VFy^)*W zkHeIXH}_6+0~xf0sv^RDt(dPqo!0y2XOvT@*}ZP0{lTve$N#)MwPBsN6x`F4qshzR zpJudJyEV)&uWR+R_mnmb5{>SsJ*4+cO)o3SG~!ItJ1#foEaLW0f6G=@nBg)$hJOHl zmIncLz;IK*|`S6ir1hcvcya(c^J4#{cRbTH;1q;P!d`c=in{z+Pjx1Yt22HIO1yV z0Zr}$5>Mny3+|UXuGfa#>#w-MP+BM-LN}hF;Z!r>v7%f0b$m)o?u)trGw{>)2klg{ z7mU3)_eVn|?~iyt-TL&4*#A?fp-8|RJE-%Igo50$wc%>0_KTXX(RHB32lY%dWb)HN zrL!&C>6i9Oa#hV;9YKtC$*S<}4W+O!&Z2Z&pSlQyd(GG z7HQ0Geew=)r1{QG9UF!0eR#{cav*ACm|edjjh*(8^oLES+lkyO)P!arC}quxuu~h) zMtWZ(pg$yQCghiO%d_*@&AU&h{cU5J^QsC)Mrd}RO&zwVp&(Zl&L!*Py6$H#7j zM-7zvaQKl&*bMnurs#@rqwiFTP_~pWcAXO9J&wWG)(*XAz8R}Q_hE~rw-x;K1@-HCYm?<@vqKCN8p3qu ze=>!+%ArFR{oO(~Za8~hR=ufS?U|@q?stdTwvh0I%Vu`3-Ktjj&RV+oXfuO#0V^8# z+LHv)wKZ+^VYm2Arb3z9-uqW()Su&-U%e|IJ~*^8P0x<3nzEHi*3bxt3RBMJKb&1) zZy=A#-yV`0O~U!h8sF|eoJa^fnDPsY=GhA@UsFEAXAw%v?*i`$+)A`1lnVN|@A;N0 zS_tFc>)+=e50Um~HxV857I!f)Ot+-%0hH0mYMm~AqmiqjQI^Oot$tyOrt6t8UtB1Cnyip6Wfn z9^_S*K#M*Eq=p0?C%zNXF&0IQ47ZTh-zbcg5_vCMV)i(UMoNdqKM@kJ0XdZ_`t1ykzJU3fLvTJA6oLlm5k(?N@M#oD{JYV?LH}c{cajM*9^*Pb6Htk>#1_2 z@!|XqZPPPL>yM#DIQFB10>Kg(_qcUthTn0p5^>M<*oj%%gx(2{<&jv_=%oh3GLfG4 zod`O1QSnEP&%R#wK=;NX6&X)AbykZCT?$QHgR=R%riHL0TXW={fv*Oo1Ma5BSy|&3 z!>bXRqZAFVltn%#?W$ILVlZkc$#HWvFl;`fQ-iJg)k)68QZEa9j}muWP9A_gE{!Cs z9EMHTo{O9?LflTQQ?|@-qALMNb$N>OQjt^b+{M|SnWsH9$IjaFO*SdZWHdNUf0@{q zNbs++YFs?TG1VXvDfYfZQ@-rYZ~%IBG^TyqNbaQ2z$C!RM(QSsLC$E)OzoH5C&R>J z=67858oHOt9#d9Fuy#YQ!l@}i)(G{g9ck3$7y8bo6gJmpotZCiXG{uA$o?ncF&Yx> zPh&J0vN*MST`Nk+;3ZJLuJ07J^|?s0YtrkI(_Q~%NIUgFD-Gz4`T~#1ojY{iCInM( z^aEC3OEv{#9F)sv01{47>h@4{(y(!a?v(+7?P@HmLpCrb--bAl0D@<-j#=AX)dTb{ zr6b8}XU2o%V9l*fld%Wx&1mNdwa9}fzv`GR2m-6!AuYNSnYwz|^$EPZ-(gNJ%Iu>a zU!9PF5rH$AE^Cvv23u&cyN|!~Cnq$CM=h;QO;SoSqfrWxNd&0?rEx=_xEWm1G+L)( z=YHJRI1l1a*Rrx)Cq%F*l{PwSJ5%PNhNjr{{*?k!*fdYCNmsWaTBsE}8F_k+mwEf+B=Yrkd9b zfx_6j(jSSZMKF{K&$$=eY0Ty^Wu~bGO$6eu*;|v;_JOUdX$PYohgK4Jd&V?oagj^k zCOE0vD9tL&7NP)^8h=r?t#OBjFM*0NvV~cuL4l{L z#t~sQIW3%QBs!nV`Or=?=Eb82?rY6r&z@YN)+gAWf*<5|(LEDF|7w~wvgiop_P5%8 z{8qS|!_+@tDukA=v%o88bvEo{VR7JclhW+V^+uS0^Wfu z)2Q={cNsaHe5j)B(^gyFd~`G!-vIR;BSULr=;nl&nhj8rPiF?hgULVWMNd2Ub}HSA zdr|krLVcfgzR9VJBEz5nc=8}swe$JIe&#AT>E67E{NzHR0}crur~(B)ToT4G9nzd$ zy1(i6Gb-wK-wNM&8Q_jhDN{OZ7fX7Yn0SP)VE=J*YAcoRSVjnw-7L;<(~RY|Vva$C zrIWy(DhJwUh~Z?m^KB~6Q?*DY5$ugXHrcS5w}d(2@-DKvd(vNmXBdgCY9fl=;`HpG zyIrRHRMVdyzTT@7Ay-q5)WYtvpC)~au9|r2fPQHl7lfx92-ji6+NyW+P4CAV`rz{O z?mu|lUvo4vfD)p;N{lIkN#~Rp`e?lX$IE4BxfL6mrqk;ad=%>wQHc6reS;L0k)M#3 zptT?nH1<|jlDxJJ<0)CvVvp(lrMG%D(79_to0hn^A_Dd?Z>cU}=Ss#3y_2|X|F(kbO9a)+Q28VQ6R2Rza{T5lZPyB;Bq_;EKDNC!& zf?#TNi~k%isp_V^b3DNJ0QkWiP%T;S_N(8m025qhF}pftIlDsJ!)Df1t==yfkYR?e zL#WI;UJfHTY9VoE9JU&-fV>xP^YrTr->(PDd72YXFGyC|PewIID3i_!qEy<(XiI$A z^$Dt@EED76L0OUUET*xJQ>BnhzSB-JSp(7EOIp06u+* zp=BqY9wn~#VH^*BHPGN>!eCE)&OyC47^fv(D2;e6o=iPwi(#+1HQ$()F#;#7Ld#6zk|*z{tlTU^4Hk zFQ_DT#!qh5S8h$}{4s(F$wJkCj?r>biJQ#t3I9sbavMdo_19 z{LFLnr^eN29g>XozfV!?n>Y9ML+$q1g5SgqjtEUuFYcSzN@&R4^g6Hjx~Q3$(`>$272(^$ z#Nxgh-K9{0<)B=u;_`>%)NtbZP0E7UgT<1v`>MO$RNS_@KaXxt7sDo#`tqitzk&ha z&Ee!yXPFW7FiaMRhkFdXR;2K8E9XB%^y#&jz7vVuD;|8ePTdB7Hywv%zWq5ne465B z=y{ozpC1UJKT{G|$7U7Mf2zX7e(NJAq!0MPrtWDs|4S&uuAvm(PX(M>g8udsIYZ$fZFZ zVQFCvNndRtm#Cx}aAzb(^Df^GQUqC1(y2wV2m_ph0SaJ41Nch-PP*w>Q*t0;akVfU{eOWf4mx}3=Vc-`YmQvO3#`*Z8`6idvb z!q|geZd-oRs$o%fyCbS~ad?5_*sVnKt4*ee7Fb`BWYr}QnwQ$jy@A5}hlhyR^I8d` zh#4o1JX0d(&nbJS%!=EyPGo3_%E5!dYxM)?cnK`_;cpBB#_8W%#l zFL4|o&u&WQWhFfThX3A50zDb5_bk`WFthyey*58Y1dhc_Bj$*HZ$9^YWbo`GVP9{8 z+lp^7FdP99qV;lYyIuIUf;L*%*{bEtW#ZzZ3*lcX9BAjGsk*|$f*c#eA{=>yR(Ba# zFc{m~zlB?Mz*tex0fh}VFXJ5gf&B9+%o2&iPT|-r&)sgKYU{goE`+VUh#2E1_o0ee z<`FrHzdFyl-t%RL?#7QQ2INHTa%SKM$$hTpsYvG(?h~2iii5g z$gw&j8)MMb0lRIo&bvJx@jG>zludp!nc1~JblIqt&Az6JXrD4M=!qRprifiU48)GeD?x}Bouvx1!-F`j|8g81oZ3kkASM>sE2 zhQjGU6sQ)&6+AgjxmTKo6)WSCi6etOTb_(FN~Qb~%IftZLs3{}27_I5RK70+4MZ?E zJ5N#~8I3A0p;}(IEhu-w9_p5vHr6(r7`P=1#YbIGjjlvjw(>dJ?Ue0EnHFF<0 z8Ne@~aD_go%EY|b)GXqejP3{RZi^)m-K5H{rRnu&S36bBd+WtFOa;In)FR2rbDP_% zT&V6a7XQ+pB0{y)KIZ5SczV@JL-D1b86y(jNhR@+x zGSQj>k4b533&3~H%j7hh_PnxY8hIZubm=)ePeIX?`j4UmHHrF^G_#lTEkrQY;!@8~ zBPmt8w2hg4U1_ZvTF+RI+5*4M(u2Cm2&vlH%1fCl z;+wp83dkDn+3x$=2eWzvQX4wnv5B=7@PP2?w+q*Y7eewRS#@8WlGEQk(AJNB zq0NqX$3@5jph%9(p72EX;wm+1M)GKazF<~A1xwZ~SBweViJJ^ij1Be4uxdmiSxOP- zAW1jL>TzE*g`ff8d#z^%3NY1pXJx26Dx06OK&T@G8*uMfyj5fI>xQOg9Z)x;?Bh|E zO%$Srxm(oT^}JLK!VzW1hDA~i!3w5yXt>UhzwUlBOm%2I7IC-Z+E^-P_gYB4QHUhR z#1HBrSu*nHk!dfXfX|`h-CwP&^il7{P=UP7x7KeveZ0K3f-}*=WVf}Wp<4KI%lCEe z=MR{+jCu1UZ-^Y!rJ_cuc8xN;bQtaHU$}u!!DRK^X4OgOkok9d1UEn4r*=IX+TWAK zS1m57HAOOFAyQHu2Jz)X&Z-V4t(l2+H4k%#j%$(UsyHR{ofH7bl&5kEd+R1u>PBb^cnMo9i*!?eBk zzuGQU=T!mJeL%5AviVXPS#u|!`*1mpAg8{>e0a5#%qG<_rTglU*ecz|tb+=Rg`O(Q zjgUwvl|s?vw!-AXmE?IvBdjmVk7ORjhJKo98<#@_?Xn*EFgysQXB6MySXH%f4Zgaz zrEgD`t7)qC4TG!HsOf5@O7RB^cz$*v5p_b;2~>QbxkTfqp&`)`Cw-QX>Xm^#ur^8{b ztHrJxbHt?xzNKS6;B-|#FXE1rTwl_Or`lB4wMO?@Z^E+i{UHZpP*sW4UZ1i1HVP2B z&dMiZ`?xF@A2?2SpQ$|oyrVJb+$`-YL9M@GhM*@!+39vLC1y~*cnux=2}%JIVdmuY zS!`pKoRbba*uL!w_2N@@JDU2$aF_ypXq}5-V37`;Jjh%73(rym?m!P)PMCR8M~U`?qCE@9wxf zk7&H{HZfK#z8Xn+I2SwWz1V|zawFoTy+-o;?&s+yDlZyT=Q2*GxpcKd!C2EWtY{xt zXZ#Ts4ojN=Ea(xphm|OOya(&xI-Xc*$b3^P680dbxqEo~S!M?fDb1pee*?>K#34H`-|N&o;rqls2EB(^jE9ttwzJB~|u zpV)w%ly#H=fVvc_D?3sEfZ0t`RoOTIxSt6rw!1V%e@Z8rIY#HHkbXT=@?4uJ@8;M zDaVhUIDW(Nve#bOrP)I2h}Gq&U)GB8*`deY-CHuq+JXG}hqEVL2Ts!a8Z#sPn}MJZ z#olvIf8!fTH@gn}i!sTco317A_{xzBeti-f7uuVlEAm`aDO*ZcktK)6Vn<#1fGo1& z@WAWpopB<6FQu9<#db%`vzVg;o!DAE?N@pT#|68?&_vwfViqJd%K8w!$CWl^4w+f z&YRCGOtJgs*^_nI2ObZV+$jemQl6Z$tI0gZiUw|dC)m0RR(sxD3efy1!Qw_%=w!d~ zQ#f$myBQW$9StBgA?d2j6~9k5XiYz9IBEQ1a$(gO1}Z-&G@0kizsRVb2`WlEYhQRn zI4m04k-=WjEXGW*AK9oOsxm@ImDBcL_k8l5vXS69-uJS^S+xWn>{;G=6QgfAG~_QD z%Xd~D#ML4>-f(cB#yLS15obUNP3N^i8 zNFOVoz7G-pca7;*XZ0$A|H!8#5#+F<@&e+H@=3I5e`XAIkguAVal4LxAFsGm>;nC) z;AZ2Go;P!F3#9b*evzf1Cb&~xj|%u37U}o?^yhCeVWucI z4NJ!D{3By&)DBphEMQkJ+>JK}@?nHAjY zu8&f0Nb6%8tSpc+(51V&{;}S>Zfh<-^g+&G>+r$WkHt?7SyO`$YU=}AvTr){%;am1 zRgFy#YS^`f?u{1z)e;=*=C~uS)2;RVF)3gl-?}P-;78Y7k^7S8Bns9~+7GJIIDQ%K zK$FFUkl6)GlBVP!;5w)isd0m==IVuGgYgmxEu|BXc!&4<@G|)O6>Oq$3)(-u@u8mXOp4*7Eiq3kQyKV{N7>9s~7zaaDH~SH@W?6gF3TH0b8H2(2y2S zrY+bR*tj5rb>cvCEuFON8g90A#Y*NvG!U}R>1F$vf}jMZ+t642+%eXI?Ujok*?r8nGjg(fhqt)(IfaHCQX0rV> z$y`}ika$lU^Sx$ZbcNPFcU5a+c9Uny$Z)S!mvia(9b7CgmariA!i|Z|+W?yFm?Hzj zU!-B}LZAosb?+nEmhR5KQubW0%;3dYTYk~su$oPnO*A9j==5%@4mf4%y~@kZ+kd4# z>+OJ4XRCt@CXLo|rk&>t_I?^DQm5Z)CSkQQzu^fhFQ}s!OcM6#Dv-VlD<|&@2!_@c zM(Q(JXa8g^V)Uq^&|V-|Ckwl-D~r0ug7>6NEfEhNr%?^+@UE2pV!6rloI(}gAeLMv zlIAz@t}oqB@r7s)D*KHSG&Uks!0(&T68(D7&}Z;Q$vGYM{swh7zixV*l7DNBJ2ZRD?0}~{%7>c)SNYOSs>PCpnY%;7t5?6XFe`c)q7S=ag(JmF*Y=a^ZD*a8-G#bu=^$pi&UPT2BWe4z`~@|Q0oOB ztIa&5DLrjf+}2#}`g$dQHN^m+FgZ1}ibX%V>nSId4(7+_z4g+zR+FcZBLxPZP=qWy z%PbYIR}u`Op5yMf?EPhAZo}WP&_LiCs1u_-gF95$B$wT$Vjn-M{n6`x-Lyh4;_HMm z!Q^_gcFc+e6U(F9dqRD`+wKN1C35S4^-HX0-N#C@a$++T(w4&st!^#}bm1S2&;)lU zxw7MalhZ`^DzZz0o3yTHajolU5k19Bd{@>HlhuSIOZmvQ8ACapvwu^nVnjM>4)XJ% zL%F+8z>P<}qZ=+r7<(#L&lI(9bRRTK#3x{;eksF63&TR|E4TE{9V?(l#vNA4fHL=Y z=!Co@kzyF&VE^r*GW9$couZ89Dg+~|)@$TyDCWL0w3A&D(_p+{p+lr&3kon{;W|sV z1%gmhMa`UUkpL&~{!lC(i0@LD1n6}Qjqy5M$qzVN#Vi3rx0{kEX6ij8=?UjdxY;^9 zNz-%$aJ9q~id6!QhE9Z!U;X~IZ;?K;1P)4I1-_jE@2Hat}rq$oM2 zkC_>85e=l>bQC;m%mQ;G06Nlc66lgwgl_7^X)f9&MA%d4;j1Lk7_Dqy?~;DPs9eBb zwh3FsZL#S#UcA1#lV{Gq!gMpC&ZQRU&D?XJj-zJtWoT7ErF!pUUs97Lr9L4Jn2<*r z01|61`^DxVhcARfx{<|Yz}w8&8YQ~ey5*rde3|F}px}@Bx5Ac=gQG|C;JrisH;i@f zT9Um^NJ}(G@erhakN9yM)J}AK3a&Kiw#2;DQI*;p*cv)A|D1v5 z>H^~#->{|#1yE`$vP8BHO4I#qR|Ub5@qorv3Gtoi04nW4DUG0YMkrU<1-M;OTnppa z6@D!0MZ$5z2aW;(DTKV{SQ#)Ggm1t4*uQQrF#P%T9E`>Yf&AL`>;0n7*@Tf_Y5BV- zj>sXu5J(HTLSl%B<)bkeB$Z$ex;e*+Ku*X7F?9y($gqby5ohd#pyYH_B!SVyLUFu+ zsv)D*)8vv4`^v_43)!X^5J{~Jg6pebaM8mQgH@uKZ#ir!@;pE&g%*IV+`48hkzg;p z^uF7QUPAGL7YHro0no?@XV&C1nrUuM2d>FLQc=&rj~b0(NR=?jzULp%7?!YcbK!hO zT>n&;JTb~tlsB>e8`$LK%LgJJB6p&>flwB80&bvU!n9v(6TM87ke{iurHwy{cx)f4509Zv2)Ui$Xd8B7wk2lDaV#6&bN# zme3KmfdIx=flQ`5g&w2G{tE#x7fu-b)~mQp{67~Gh^+|a0U^j}KLd!XnFK*unqZjk zoN96stuUf>%8@8(hVi0CffOlFmX#l#K7P#%#m0wLeCXE33Lr^{*puMGATXsFUfFwO zxPV-64h+9JNAv4#4%NcOtG(fLzvFjBi{AuyEinhbCsKBoQ+9`&PZpdfa($UeH)u-Q z-CR2Id3K}XE-$2oKv`t$yKbKBQYk-+ryYVAMl=ho8)Lz9p@2q*Hx;#UGh`#_=4c+^ zB<3HQ(imz%yD36`>e`@l#Hfdo#Ir09vOxuisw!Zvz9Y8sL;J6g3#YWMnkS2KXF2=v zEgexd-+%IQe_HzS_S%8uK>Tn5>KS=S#nWhuM0a>t$`aH0xdN=F_wRhjX8AOuM^7dh z``VlIr0cJQ?=UnRm0}&3Aow_Jj8^{V*ZYmx;Bh^a-VEIk=bL4H3p9uo4se@AtR7M4 z=swrv`VGblf5B`&({4mofURgsDnHa;Tw(qDiA##*-l@yT)jURXJCNo1K*UD6PC2g(j*eV^;$%{2q7~&D>uUZo}&aH#uE3LaQyn1vZq=S z0u4l{2~fEjdMb$-_PaV_r6ENLD9!#HC@=E&()Wq2awiRqw>P0xeF8vipEKp@3m0TA z1I)u-QmHW)Y^a7;zvo11GHP8Wmfo9+GJ#BD%hWN^pn)Bd;>B%led&tg29RYN9S29e zL;Myx6r98R-^qF?4MX0SG*etT*?>8sp~;^fDzANRsI+#S2{#4S|?b`jOYQRFdH)4PkdYmE+KM_@B(4j)e$c-tIU9#jT^-W zr}@G%4|W>5p8j{HNkB_EM@%zC$wMqto^O>C+jT(j6$+G~u6Utm0A;0oa1YA<9|CD0 zrL$J~FU7`d7Qc?qvj&rSo4Fr%_Epp|%rx74Yae;S`zfENqpdr5%DG2w>;jZ46P$2E z{%0qPcbvbzO;S|oqVsc+4zTg^3)WGsd5WZkL38PWn@uGQrA8i7 z4n~cyuKB7QlR>^^htj!~aw?XzTs|9{6`fA8y|@)-MX$eDs1W>Yp06g!1<4+fCYi*& z8Vvo+2rS4sG>u>_tbZG%6nRJ7ls?-WddQ9ULYbTlm0PnB268(#D#*MU7%6vm)IRRQ zQw2ez;hdi4h)(dv$`I#!$S3~=nZK~GD`%p@p!Z9xRY@5uf=IjkkuNNHTQ< zgV#q}2v)BD=uRK*;4(g&*tJN}YG*Kt0x%QPEOb02jU$CLw!lUcA54MYlcNEDHsA_t z3%LFLDd^$#zbsrdfjnV%xI)Da`a^0f!AxdPs&NaPLaY~vu*7TnZSR-zYU;c;cI9xU zV3x%rC6-0ch-n2ISXeVR{sOA*c4aO8gwiS^UsKhAb@+1RY6Ajks-46B z5u`1HZXZioDE1+UZ>_pgD!ZqIbYnbsvW<-UQsud`I18jGH&l4OIk9EM0%}&=wYJ1G z?xYVz-v(v|iiNQnO8;bL2fnK8EeI7wCeT_g+)S@QCF0je;Fv z6YK9%)@s4rT{-bS@YGwNb+LCvhS4hG^2TB26{E*LC}|a5nSYK&bJ2nXDg+#)CMWL( zkykX8|4?jZJa}kZ)iLZ1CFb6#<&_Q>1DsCRBSn>(53b$Aqsgl+p1sb(_s8W+G64O2 z{g1A%*0Uz7V(vV!Zrg;7t+xD9baWmqP7e(}xXbG6AjwSvyM}8r4LZGIBJWI5`+3OX zHUJJ3aA;hQ-BF|OlbiY6jm+4Aw5J67fAjr=2buW*I%Y9c#Gsdw0=oW2zw5;2s6SfpMy#i2y znd8^D?y&sO;ecb+1{{P}OD z&B~`O0fw6-3`E+s?_Ygz@W%-Mm?2PCjmBiC;LpI4-kzx+ZACfs>zEW!?y4_&6rI*u zxld%QlAnVe3Pjg9MTgckFLRT@Y|vTwzUHP50kmZA*32q2%^Y z%W(Z{msoz@V3!Fk*c7N?RQq6|CYJSrbXc*#B(_ly0>Z_)^4`nZyn(O_t&uRgjm)KL z@cXVNCjBds#e7T9^ylOf*tl6!;}eI$uqrJT1_5>yw=_pPk?PLcQ}1p&3BUX?{z$b1bDBx%E7K9Re4t$Wv(>k26(w-}x^R z4)s-F6Vik^SJ_7mH1u;cQ!er)xX_(@rA=9{WCHklOzt>XSW;GMfvhiDRIo~_i@gS1 z_xZ|X=f+sPl-@2q%NA(3D^XHK>L}CZxE4d=uY#q+tFN^*&{OG67|eQD{GXC!*0JtxgH_7^m~4=;GfrXHlNz2 zcIBhdC||liu~lg=gGZcBR6Hvryaa149-H|%iI#LP#JEVkMx|;B=ixUg!@1{0GrxIO zHr4ZB5B-iuJ{ljZ4!xBzav&^3Ri_p2(U$?&HYaI%r;^6Y^tR(WG_JsX5uB8RYy5>O zMSSG6!c3C>u?Ey1#Mb27kJ7iCk#p4QBcBJiEk`{?Rr$Ig`k1aKi5K^8a)@EoPIVTn zB6Eb7NfPz}R%umc3hPP^hMzY44jeh}bAfE|1Q0-+ohk43C%q+#I^mDD-*XR}MR`z% zdx7_5;w^U6Vjop#-*|8RV*i`hreXntbmLma3n?O1$-0*N`GkI6Qu;k%!1lCyn|EvH z35VZ%SG8h=%TuS@blbH!5Y@H}%W(~<`Q=rn;rO|s*s%esM&0{|mukV=;v;qYwn*h> zq6)i86&EgJhrt#`I$c5GJH%0$}hf0I$y+uC4;^7mPZSc)=#TBLg04LSTy$T*uqWR4mJ7L8tmD!_3CV7YxJ7Kbsh+m7n-nAIZ?X0f zooE2>|Lf5kvu&~6y6?6>o_GsvN~4voLN=05t*B4gzg#;!7Om#=*XSAB61@sxZW?E3 l^ZPi~p7gpFpYHdUF8YL-PhE7$mMFvnG}UxftM6jM{|CZRHhcg8 From eede36dabe0058beb005c2d1038338c5984fd0bd Mon Sep 17 00:00:00 2001 From: David Cordero Date: Fri, 24 May 2019 22:23:36 +0200 Subject: [PATCH 107/266] Update changelog --- CHANGES.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d615617052..7115b80d1d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -11,9 +11,8 @@ Improvements: Bug fix: * Registration with an email is broken (#2417). - -Bug fix: * Device Verification: Fix user display name and device id colors in dark theme + * Adjust size of the insert button in the People tab Changes in 0.8.6 (2019-05-06) =============================================== From aac990753a9bbd957b17190fbf29c7aa3a015b5a Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 23 May 2019 23:31:42 +0000 Subject: [PATCH 108/266] Translated using Weblate (Bulgarian) Currently translated at 100.0% (5 of 5 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-dialogs/bg/ --- Riot/Assets/bg.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/bg.lproj/InfoPlist.strings b/Riot/Assets/bg.lproj/InfoPlist.strings index 00e7f94984..675f3d3910 100644 --- a/Riot/Assets/bg.lproj/InfoPlist.strings +++ b/Riot/Assets/bg.lproj/InfoPlist.strings @@ -3,3 +3,4 @@ "NSPhotoLibraryUsageDescription" = "Галерията се използва, за да се изпращат снимки и видеа."; "NSMicrophoneUsageDescription" = "Микрофонът се използва, за да се правят видеа и да се водят разговори."; "NSContactsUsageDescription" = "За да покажем кои от контактите Ви използват Riot или Matrix, можем да изпратим имейл адресите и телефонните номера от телефонния указател към Matrix сървъра за самоличност. Компания New Vector не складира тези данни и не ги използва за никаква друга цел. За повече информация, вижте политиката за поверителност в настройките."; +"NSCalendarsUsageDescription" = "Вижте насрочените срещи в приложението."; From 0c562c26df733f0300a00791eafb6a3c4cbf3378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Thu, 23 May 2019 07:50:59 +0000 Subject: [PATCH 109/266] Translated using Weblate (French) Currently translated at 100.0% (5 of 5 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/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 21e8f42c24..aa5c6d53c4 100644 --- a/Riot/Assets/fr.lproj/InfoPlist.strings +++ b/Riot/Assets/fr.lproj/InfoPlist.strings @@ -3,3 +3,4 @@ "NSPhotoLibraryUsageDescription" = "La photothèque est utilisée pour envoyer des photos et des vidéos."; "NSMicrophoneUsageDescription" = "Le microphone est utilisé pour prendre des vidéos et passer des appels."; "NSContactsUsageDescription" = "Afin d’afficher qui parmis vos contacts utilise déjà Riot ou Matrix, nous pouvons envoyer les adresses e-mails et les numéros de téléphone de votre carnet d'adresse à votre Serveur d'Identité Matrix. New Vector ne stocke pas ces données et ne les utilise pas à d'autres fins. Pour plus d'informations, veuillez consulter la page de politique de confidentialité dans les paramètres de l'application."; +"NSCalendarsUsageDescription" = "Voir vos rendez-vous prévus dans l’application."; From e3241e28780daca07bee828ebfbabf889e3d71af Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 23 May 2019 19:56:40 +0000 Subject: [PATCH 110/266] Translated using Weblate (Hungarian) Currently translated at 100.0% (5 of 5 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-dialogs/hu/ --- Riot/Assets/hu.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/hu.lproj/InfoPlist.strings b/Riot/Assets/hu.lproj/InfoPlist.strings index 9413c4955a..fcca04acb5 100644 --- a/Riot/Assets/hu.lproj/InfoPlist.strings +++ b/Riot/Assets/hu.lproj/InfoPlist.strings @@ -3,3 +3,4 @@ "NSPhotoLibraryUsageDescription" = "A fénykép galéria fényképek és videók küldéséhez lesz használva."; "NSMicrophoneUsageDescription" = "A mikrofon videók készítéséhez és hívásokhoz lesz használva."; "NSContactsUsageDescription" = "Ahhoz, hogy meg tudjuk mutatni melyik ismerősöd használja már a Riot-ot vagy Matrix-ot, el tudjuk küldeni az e-mail címeket és telefonszámokat a címjegyzékedből a Matrix Azonosítási Szerverének. „New Vector” nem tárolja és semmilyen más célra nem használja ezeket az információkat. További információkért olvasd el az adatkezelési oldalt az alkalmazás beállításaiban."; +"NSCalendarsUsageDescription" = "Nézd meg a találkozóidat az alkalmazásban."; From a29ea35af44743b3ae084ec0188f7d43d759acb6 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 23 May 2019 23:30:25 +0000 Subject: [PATCH 111/266] Translated using Weblate (Bulgarian) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/bg/ --- Riot/Assets/bg.lproj/Localizable.strings | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/bg.lproj/Localizable.strings b/Riot/Assets/bg.lproj/Localizable.strings index 49c93d17c8..bd8e2cfd6c 100644 --- a/Riot/Assets/bg.lproj/Localizable.strings +++ b/Riot/Assets/bg.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "Съобщение от %@"; +"MSG_FROM_USER" = "%@ изпрати съобщение"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ публикува в %@"; /* New message from a specific person, not referencing a room. Content included. */ @@ -11,7 +11,7 @@ /* New action message from a specific person in a named room. */ "ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@"; /* New action message from a specific person, not referencing a room. */ -"IMAGE_FROM_USER" = "%@ Ви изпрати снимка %@"; +"IMAGE_FROM_USER" = "%@ изпрати снимка %@"; /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ публикува снимка %@ в %@"; /* A single unread message in a room */ @@ -50,3 +50,7 @@ "VOICE_CONF_NAMED_FROM_USER" = "Групово повикване от %@: '%@'"; /* Incoming named video conference invite from a specific person */ "VIDEO_CONF_NAMED_FROM_USER" = "Групово видео повикване от %@: '%@'"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ в %@"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ изпрати стикер"; From 74498a9ef61e7e94ab3cf8139df4dc4fdd26f683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Thu, 23 May 2019 07:51:24 +0000 Subject: [PATCH 112/266] Translated using Weblate (French) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/fr/ --- Riot/Assets/fr.lproj/Localizable.strings | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/fr.lproj/Localizable.strings b/Riot/Assets/fr.lproj/Localizable.strings index 8b51db94b0..7ad385fcbb 100644 --- a/Riot/Assets/fr.lproj/Localizable.strings +++ b/Riot/Assets/fr.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "Message de %@"; +"MSG_FROM_USER" = "%@ a envoyé un message"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ a posté dans %@"; /* New message from a specific person, not referencing a room. Content included. */ @@ -11,7 +11,7 @@ /* New action message from a specific person in a named room. */ "ACTION_FROM_USER_IN_ROOM" = "%@ : * %@ %@"; /* New action message from a specific person, not referencing a room. */ -"IMAGE_FROM_USER" = "%@ vous a envoyé une image %@"; +"IMAGE_FROM_USER" = "%@ a envoyé une image %@"; /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ a posté une image %@ dans %@"; /* A single unread message in a room */ @@ -50,3 +50,7 @@ "VOICE_CONF_NAMED_FROM_USER" = "Téléconférence vocale de %@ : '%@'"; /* Incoming named video conference invite from a specific person */ "VIDEO_CONF_NAMED_FROM_USER" = "Téléconférence vidéo de %@ : '%@'"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ dans %@"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ a envoyé un sticker"; From 488f4cfa03f901441bbec8ed2c9c7dbff3ad5c86 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Fri, 24 May 2019 05:31:57 +0000 Subject: [PATCH 113/266] Translated using Weblate (Hungarian) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/hu/ --- Riot/Assets/hu.lproj/Localizable.strings | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/hu.lproj/Localizable.strings b/Riot/Assets/hu.lproj/Localizable.strings index 08b69bc1aa..1b800160b4 100644 --- a/Riot/Assets/hu.lproj/Localizable.strings +++ b/Riot/Assets/hu.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "Üzenet tőle: %@"; +"MSG_FROM_USER" = "%@ üzenet elküldve"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ küldte ebbe a szobába: %@"; /* New message from a specific person, not referencing a room. Content included. */ @@ -50,3 +50,7 @@ "VOICE_CONF_NAMED_FROM_USER" = "%@ csoportos hívása: '%@'"; /* Incoming named video conference invite from a specific person */ "VIDEO_CONF_NAMED_FROM_USER" = "%@ csoportos videóhívása: '%@'"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ in %@ -ban/ben"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ matricát küldött"; From bbb3a6e822dcd8abf61ccf0955cf854556a793e5 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Sun, 26 May 2019 11:22:45 +0000 Subject: [PATCH 114/266] Translated using Weblate (Albanian) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/sq/ --- Riot/Assets/sq.lproj/Localizable.strings | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/sq.lproj/Localizable.strings b/Riot/Assets/sq.lproj/Localizable.strings index 6587a8ce36..d060cfcd87 100644 --- a/Riot/Assets/sq.lproj/Localizable.strings +++ b/Riot/Assets/sq.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "Mesazh prej %@"; +"MSG_FROM_USER" = "%@ dërgoi një mesazh"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ postuar te %@"; /* New message from a specific person, not referencing a room. Content included. */ @@ -11,7 +11,7 @@ /* New action message from a specific person in a named room. */ "ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@"; /* New action message from a specific person, not referencing a room. */ -"IMAGE_FROM_USER" = "%@ ju dërgoi një foto %@"; +"IMAGE_FROM_USER" = "%@ dërgoi një foto %@"; /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ postoi një foto %@ në %@"; /* A single unread message in a room */ @@ -50,3 +50,7 @@ "VOICE_CONF_NAMED_FROM_USER" = "Thirrje grupi nga %@: '%@'"; /* Incoming named video conference invite from a specific person */ "VIDEO_CONF_NAMED_FROM_USER" = "Thirrje video në grup nga %@: '%@'"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ në %@"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ dërgoi një ngjitës"; From 652ee9920138132c6373467f0daba454f0da71ff Mon Sep 17 00:00:00 2001 From: Kasqade Date: Sun, 26 May 2019 19:18:41 +0000 Subject: [PATCH 115/266] Translated using Weblate (German) Currently translated at 100.0% (5 of 5 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-dialogs/de/ --- Riot/Assets/de.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/de.lproj/InfoPlist.strings b/Riot/Assets/de.lproj/InfoPlist.strings index 501141effe..73cdf84ef0 100644 --- a/Riot/Assets/de.lproj/InfoPlist.strings +++ b/Riot/Assets/de.lproj/InfoPlist.strings @@ -3,3 +3,4 @@ "NSPhotoLibraryUsageDescription" = "Die Foto-Bibliothek wird verwendet um Fotos und Videos zu senden."; "NSMicrophoneUsageDescription" = "Das Mikrofon wird verwendet um Videos aufzunehmen sowie Gespräche zu führen."; "NSContactsUsageDescription" = "Um dir zu zeigen, welche deiner Kontakte bereits Riot oder Matrix benutzen, können wir die E-Mail-Adressen und Telefonnummern deines Adressbuches an deinen Matrix-Identitätsserver senden. New Vector speichert diese Daten nicht und nutzt sie auch für keine anderen Zwecke. Für mehr Informationen sieh dir bitte die Datenschutz-Seite in den App-Einstellungen an."; +"NSCalendarsUsageDescription" = "Sieh dir deine geplanten Meetings in der App an."; From 8a1d3f265d54d84d24655e024481d4b5e530b3d8 Mon Sep 17 00:00:00 2001 From: Kasqade Date: Sun, 26 May 2019 19:09:28 +0000 Subject: [PATCH 116/266] Translated using Weblate (German) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/de/ --- Riot/Assets/de.lproj/Localizable.strings | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/de.lproj/Localizable.strings b/Riot/Assets/de.lproj/Localizable.strings index 54b32d9a00..bcadaeb5f7 100644 --- a/Riot/Assets/de.lproj/Localizable.strings +++ b/Riot/Assets/de.lproj/Localizable.strings @@ -11,7 +11,7 @@ /* New action message from a specific person in a named room. */ "ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@"; /* New action message from a specific person, not referencing a room. */ -"IMAGE_FROM_USER" = "%@ sendet dir ein Bild %@"; +"IMAGE_FROM_USER" = "%@ hat ein Bild gesendet %@"; /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ sendet ein Bild %@ in %@"; /* Multiple unread messages in a room */ @@ -50,3 +50,7 @@ "SINGLE_UNREAD_IN_ROOM" = "Du hast eine Nachricht in %@ bekommen"; /* A single unread message */ "SINGLE_UNREAD" = "Du hast eine Nachricht bekommen"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ in %@"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ hat einen Sticker gesendet"; From 5221ec5ab135d8711aded48e96903b8a750b3f25 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Sun, 26 May 2019 11:50:22 +0000 Subject: [PATCH 117/266] Translated using Weblate (Albanian) Currently translated at 99.3% (705 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 106 ++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index 52921d82de..f0a36c1778 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -213,7 +213,7 @@ "settings_config_no_build_info" = "S’ka të dhëna montimi"; "settings_mark_all_as_read" = "Vëru shenjë krejt mesazheve si të lexuar"; "settings_report_bug" = "Njoftoni një të metë"; -"settings_config_home_server" = "Shërbyesi home është %@"; +"settings_config_home_server" = "Shërbyesi Home është %@"; "settings_config_identity_server" = "Shërbyes identitetesh është %@"; "settings_config_user_id" = "I futur si %@"; "settings_user_settings" = "RREGULLIME PËRDORUESI"; @@ -390,7 +390,7 @@ "on" = "On"; "auth_add_phone_message" = "Shtoni te llogaria juaj një numër telefoni, për t’u dhënë mundësinë përdoruesve t’ju zbulojnë."; "auth_msisdn_validation_message" = "Kemi dërguar një SMS me një kod aktivizimi. Ju lutemi, jepeni këtë kod më poshtë."; -"auth_recaptcha_message" = "Ky Shërbyes Home do të donte të sigurohej se s’jeni robot"; +"auth_recaptcha_message" = "Ky shërbyes Home do të donte të sigurohej se s’jeni robot"; "auth_reset_password_error_unauthorized" = "Verifikimi i adresës email dështoi: sigurohuni se keni klikuar lidhjen te email-i"; "auth_reset_password_error_not_found" = "Adresa juaj email s’duket të jetë e përshoqëruar me ID Matrix në këtë shërbyes Home."; "title_favourites" = "Të parapëlqyer"; @@ -559,7 +559,7 @@ "settings_key_backup_info_version" = "Version Kopjeruajtjeje Kyçesh: %@"; "settings_key_backup_info_algorithm" = "Algoritëm: %@"; "settings_key_backup_info_valid" = "Kjo pajisje po bën kopjeruajtje të kyçeve tuaja."; -"settings_key_backup_info_not_valid" = "Kjo pajisje nuk po bën kopjeruajtje të kyçeve tuaja."; +"settings_key_backup_info_not_valid" = "Kjo pajisje nuk po bën kopjeruajtje të kyçeve tuaja, por keni një kopjeruajtje ekzistuese që mund ta përdorni për rimarrje dhe ta shtoni më tej."; "settings_key_backup_info_progress" = "Po kopjeruhen kyçet për %@…"; "settings_key_backup_info_progress_done" = "U kopjeruajtën krejt kyçet"; "settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "Për të përdorur Rikthim Mesazhesh të Sigurt në këtë pajisje, verifikoni tani %@."; @@ -621,7 +621,7 @@ "key_backup_recover_banner_title_part1" = "Xhironi Rikthim Mesazhesh të Sigurt"; "key_backup_recover_banner_title_part2" = " që të lexoni historik mesazhesh të fshehtëzuar në këtë pajisje"; "settings_key_backup_info" = "Mesazhet e fshehtëzuar sigurohen me fshehtëzim skaj-më-skaj. Vetëm ju dhe marrësi(t) kanë kyçet për të lexuar këto mesazhe."; -"settings_key_backup_info_signout_warning" = "Kopjeruajini kyçet tuaj, përpara se të dilni, që të shmangni humbjen e tyre."; +"settings_key_backup_info_signout_warning" = "Lidheni këtë pajisje me kopjeruajtje kyçesh, përpara se të dilni, që të shmangni humbje të çfarëdo kyçi që mund të gjendet vetëm në këtë pajisje."; "settings_key_backup_button_use" = "Përdor kopjeruajtje kyçesh"; "key_backup_setup_intro_setup_action_without_existing_backup" = "Fillo të përdorësh Kopjeruajtje Kyçesh"; "key_backup_setup_intro_setup_action_with_existing_backup" = "Përdor Kopjeruajtje Kyçesh"; @@ -672,3 +672,101 @@ "room_message_unable_open_link_error_message" = "S’arrihet të hapet lidhja."; "auth_autodiscover_invalid_response" = "Përgjigje e pavlefshme pikasjeje shërbyesi Home"; "room_details_fail_to_update_room_direct" = "S’arrihet të përditësohet shenja si e drejtpërdrejtë e kësaj dhome"; +"room_event_action_reply" = "Përgjigjuni"; +"room_event_action_edit" = "Përpunoni"; +"room_event_action_reaction_agree" = "Pajtohem me %@"; +"room_event_action_reaction_disagree" = "S’pajtohem me %@"; +"room_event_action_reaction_like" = "Pëlqejeni %@"; +"room_event_action_reaction_dislike" = "Shpëlqejeni %@"; +"room_action_reply" = "Përgjigjuni"; +"settings_labs_message_reaction" = "Reagoni ndaj mesazhesh me emoji"; +"settings_key_backup_button_connect" = "Lidhe këtë pajisje me Kopjeruajtje Kyçesh"; +"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Lidhe këtë pajisje me Kopjeruajtje Kyçesh"; +"key_backup_recover_connent_banner_subtitle" = "Lidhe këtë pajisje me Kopjeruajtje Kyçesh"; +// MARK: - Device Verification +"device_verification_title" = "Verifikoni pajisje"; +"device_verification_security_advice" = "Për siguri maksimale, këshillojmë ta bëni këtë në prani të vetë personit, ose të përdorni një tjetër kanal të besuar komunikimesh"; +"device_verification_cancelled" = "Pala tjetër e anuloi verifikimin."; +"device_verification_cancelled_by_me" = "Verifikimi u anulua. Arsye: %@"; +"device_verification_error_cannot_load_device" = "S’ngarkohen dot të dhëna pajisje."; +// Mark: Incoming +"device_verification_incoming_title" = "Kërkesë Verifikimi e Ardhur"; +"device_verification_incoming_description_1" = "Që t’i vihet shenjë si e besuar, verifikojeni këtë pajisje. Besimi i pajisjeve të partnerëve ju jep ca qetësi më tepër kur përdoren mesazhe të fshehtëzuar skaj-më-skaj."; +"device_verification_incoming_description_2" = "Verifikimi i kësaj pajisje do ta shënojë atë të besuar, dhe tuajën si të besuar te partneri."; +// MARK: Start +"device_verification_start_title" = "Verifikoje duke krahasuar një varg të shkurtër teksti"; +"device_verification_start_wait_partner" = "Po pritet pranimi nga partneri…"; +"device_verification_start_use_legacy" = "S’duket gjë? Jo të tërë klientët mbulojnë verifikim ndërveprues ende. Përdorni verifikimin në stil të vjetër."; +"device_verification_start_verify_button" = "Po Verifikohet"; +"device_verification_start_use_legacy_action" = "Përdor Verifikim të Dikurshëm"; +// MARK: Verify +"device_verification_verify_title_emoji" = "Verifikojeni këtë pajisje duke ripohuar shfaqjen e emoji-t vijues në skenën e partnerit"; +"device_verification_verify_title_number" = "Verifikojeni këtë pajisje duke ripohuar se numri vijues shfaqet në ekranin e partnerit"; +"device_verification_verify_wait_partner" = "Po pritet ripohimi nga partneri…"; +// MARK: Verified +"device_verification_verified_title" = "U verifikua!"; +"device_verification_verified_description_1" = "E verifikuat me sukses këtë pajisje."; +"device_verification_verified_description_2" = "Mesazhet e sigurt me këtë përdorues fshehtëzohen skaj-më-skaj dhe janë të palexueshëm nga palë të treta."; +"device_verification_verified_got_it_button" = "E mora vesh"; +// MARK: Emoji +"device_verification_emoji_dog" = "Qen"; +"device_verification_emoji_cat" = "Mace"; +"device_verification_emoji_lion" = "Luan"; +"device_verification_emoji_horse" = "Kalë"; +"device_verification_emoji_unicorn" = "Njëbrirësh"; +"device_verification_emoji_pig" = "Derr"; +"device_verification_emoji_elephant" = "Elefant"; +"device_verification_emoji_rabbit" = "Lepur"; +"device_verification_emoji_panda" = "Panda"; +"device_verification_emoji_rooster" = "Këndes"; +"device_verification_emoji_penguin" = "Pinguin"; +"device_verification_emoji_turtle" = "Breshkë"; +"device_verification_emoji_fish" = "Peshk"; +"device_verification_emoji_octopus" = "Oktapod"; +"device_verification_emoji_butterfly" = "Flutur"; +"device_verification_emoji_flower" = "Lule"; +"device_verification_emoji_tree" = "Pemë"; +"device_verification_emoji_cactus" = "Kaktus"; +"device_verification_emoji_mushroom" = "Kërpudhë"; +"device_verification_emoji_globe" = "Rruzull"; +"device_verification_emoji_moon" = "Hëna"; +"device_verification_emoji_cloud" = "Re"; +"device_verification_emoji_fire" = "Zjarr"; +"device_verification_emoji_banana" = "Banane"; +"device_verification_emoji_apple" = "Mollë"; +"device_verification_emoji_strawberry" = "Luleshtrydhe"; +"device_verification_emoji_corn" = "Misër"; +"device_verification_emoji_pizza" = "Picë"; +"device_verification_emoji_cake" = "Tortë"; +"device_verification_emoji_heart" = "Zemër"; +"device_verification_emoji_smiley" = "Emotikon"; +"device_verification_emoji_robot" = "Robot"; +"device_verification_emoji_hat" = "Kapë"; +"device_verification_emoji_glasses" = "Syze"; +"device_verification_emoji_santa" = "Babagjyshi i Vitit të Ri"; +"device_verification_emoji_umbrella" = "Ombrellë"; +"device_verification_emoji_hourglass" = "Klepsidër"; +"device_verification_emoji_clock" = "Klasë"; +"device_verification_emoji_gift" = "Dhuratë"; +"device_verification_emoji_light bulb" = "Llambë"; +"device_verification_emoji_book" = "Libër"; +"device_verification_emoji_pencil" = "Laps"; +"device_verification_emoji_paperclip" = "Kapëse"; +"device_verification_emoji_scissors" = "Gërshërë"; +"device_verification_emoji_padlock" = "Dry"; +"device_verification_emoji_key" = "Kyç"; +"device_verification_emoji_hammer" = "Çekiç"; +"device_verification_emoji_telephone" = "Telefon"; +"device_verification_emoji_flag" = "Flamur"; +"device_verification_emoji_train" = "Tren"; +"device_verification_emoji_bicycle" = "Biçikletë"; +"device_verification_emoji_aeroplane" = "Avion"; +"device_verification_emoji_rocket" = "Raketë"; +"device_verification_emoji_trophy" = "Trofe"; +"device_verification_emoji_ball" = "Top"; +"device_verification_emoji_guitar" = "Kitarë"; +"device_verification_emoji_trumpet" = "Trombë"; +"device_verification_emoji_bell" = "Kambanë"; +"device_verification_emoji_anchor" = "Spirancë"; +"device_verification_emoji_headphones" = "Kufje"; +"device_verification_emoji_folder" = "Dosje"; From 807f0bdab55b5e0ac04fc633e61c6ca07a287974 Mon Sep 17 00:00:00 2001 From: Kasqade Date: Sun, 26 May 2019 19:36:23 +0000 Subject: [PATCH 118/266] Translated using Weblate (German) Currently translated at 87.0% (618 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 0db8211c49..4d209376b9 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -276,7 +276,7 @@ "title_home" = "Start"; "next" = "Weiter"; "auth_invalid_login_param" = "Falscher Benutzername oder Passwort"; -"auth_add_email_message" = "Füge eine E-Mail-Adresse hinzu, damit dich andere Benutzer finden können, oder um das Passwort zurücksetzen zu können."; +"auth_add_email_message" = "Füge eine E-Mail-Adresse hinzu, damit dich andere Benutzer finden können und um dein Passwort zurückzusetzen."; "auth_add_phone_message" = "Füge eine Telefonnummer hinzu, damit dich andere Benutzer finden können."; "auth_add_email_phone_message" = "Füge eine E-Mail-Adresse und/oder Telefonnummer hinzu, damit dich andere Benutzer finden können. Über die E-Mail-Adresse kannst du das Passwort zurücksetzen."; "auth_add_email_and_phone_message" = "Füge eine E-Mail-Adresse und eine Telefonnummer hinzu, damit dich andere Benutzer finden können. Über die E-Mail-Adresse kannst du das Passwort zurücksetzen."; @@ -656,7 +656,14 @@ "key_backup_setup_intro_manual_export_action" = "Manueller Schlüssel Export"; // String for App Store "store_short_description" = "Sicherer, dezentralisierter Chat/VoIP"; -"store_full_description" = "Kommuniziere - auf deine Art.\n\nEine Chat-App - unter deiner Kontrolle und komplett flexibel. Riot lässt dich so kommunizieren wie du willst. Gebaut für [matrix] - dem Standard für offene, dezentrale Kommunikation.\n\nHol dir ein kostenloses Konto auf matrix.org, hol deinen eigenen Server auf https://modular.im oder nutze einen anderen Matrix-Server.\n\nWarum Riot.im wählen?\n\nKOMPLETTE KOMMUNIKATION: Baue Räume um deine Teams, deine Freunde, deine Community - wie auch immer du es willst! Chatte, teile Dateien, füge Widgets hinzu, führe Sprach- und Videogespräche - alles kostenlos.\n\nMÄCHTIGE INTEGRATIONEN: Nutze Riot.im mit den Werkzeugen die du kennst und liebst. Mit Riot.im kannst du auch mit Nutzern und Gruppen anderer Chat-Apps in Kontakt bleiben.\n\nPRIVAT UND SICHER: Halte deine Gespräche geheim. Modernste Verschlüsselung sogt dafür, dass deine private Kommunikation auch privat bleibt.\n\nOFFEN, NICHT VERSCHLOSSEN: Open Source - basierend auf [matrix]. Behalte deine Daten indem du deinen eigenen Server betreibst - oder einen wählst, dem du vertraust.\n\nWOIMMER DU AUCH BIST: Halte den Kontakt wo auch immer du bist mit einem Nachrichtenverlauf der auf allen deinen Geräten voll synchronisiert wird - in einer App oder im Browser z.B. auf https://riot.im"; +"store_full_description" = "Kommuniziere - auf deine Art.\n\nEine Chat-App - unter deiner Kontrolle und komplett flexibel. Riot lässt dich so kommunizieren wie du willst. Gebaut für [matrix] - dem Standard für offene, dezentrale Kommunikation.\n\nHol dir ein kostenloses Konto auf matrix.org, deinen eigenen Server auf https://modular.im oder nutze einen anderen Matrix-Server.\n\nWarum Riot.im nutzen?\n\nLÜCKENLOSE KOMMUNIKATION: Baue Räume um deine Teams, deine Freunde, deine Community - ganz nach deinen Vorstellungen! Chatte, teile Dateien, füge Widgets hinzu und führe Sprach- sowie Videogespräche - alles kostenlos.\n\nMÄCHTIGE INTEGRATIONEN: Nutze Riot.im mit den Werkzeugen die du kennst und liebst. Mit Riot.im kannst du sogar mit Nutzern und Gruppen anderer Chat-Apps in Kontakt bleiben.\n\nPRIVAT UND SICHER: Halte deine Gespräche geheim. Modernste Verschlüsselung sogt dafür, dass private Kommunikation auch privat bleibt.\n\nOFFEN, NICHT VERSCHLOSSEN: Open Source - basierend auf [matrix]. Behalte die Hoheit über deine Daten indem du deinen eigenen Server betreibst - oder einen wählst, dem du vertraust.\n\nWO AUCH IMMER DU BIST: Halte den Kontakt - mit einem Nachrichtenverlauf der vollständig synchronisiert wird - auf all deinen Geräten oder Online unter https://riot.im"; "auth_login_single_sign_on" = "Anmelden mit Single Sign-On"; "auth_autodiscover_invalid_response" = "Ungültige Antwort beim Entdecken des Heimservers"; "room_message_unable_open_link_error_message" = "Konnte Link nicht öffnen."; +"room_event_action_reply" = "Antworten"; +"room_event_action_edit" = "Bearbeiten"; +"room_event_action_reaction_agree" = "%@ zustimmen"; +"room_event_action_reaction_disagree" = "%@ nicht zustimmen"; +"room_event_action_reaction_like" = "%@ zustimmen"; +"room_event_action_reaction_dislike" = "%@ nicht zustimmen"; +"room_action_reply" = "Antworten"; From b0fbcde9ddf6508061519fa14fdf2ad4a8a31cb8 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 12:15:31 +0200 Subject: [PATCH 119/266] Create RoomBubbleCellLayout class to handle MXKRoomBubbleTableViewCell layout constants. --- Riot.xcodeproj/project.pbxproj | 4 ++ .../Encryption/RoomBubbleCellLayout.swift | 48 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 Riot/Modules/Room/Views/BubbleCells/Encryption/RoomBubbleCellLayout.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index df79a42305..f12b1f043e 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -205,6 +205,7 @@ B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */; }; B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */; }; B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A5B33D227ADF2A004CBA85 /* UIImage.swift */; }; + B1A68593229E807A00D6C09A /* RoomBubbleCellLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A68592229E807800D6C09A /* RoomBubbleCellLayout.swift */; }; B1B12B2922942315002CB419 /* UITouch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1B12B2822942315002CB419 /* UITouch.swift */; }; B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */; }; B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */; }; @@ -804,6 +805,7 @@ B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorType.swift; sourceTree = ""; }; B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinator.swift; sourceTree = ""; }; B1A5B33D227ADF2A004CBA85 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = ""; }; + B1A68592229E807800D6C09A /* RoomBubbleCellLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomBubbleCellLayout.swift; sourceTree = ""; }; B1B12B2822942315002CB419 /* UITouch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITouch.swift; sourceTree = ""; }; B1B5567920EE6C4C00210D55 /* CountryPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountryPickerViewController.h; sourceTree = ""; }; B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountryPickerViewController.m; sourceTree = ""; }; @@ -2885,6 +2887,7 @@ B1B5584220EF768E00210D55 /* Encryption */ = { isa = PBXGroup; children = ( + B1A68592229E807800D6C09A /* RoomBubbleCellLayout.swift */, B1B5584320EF768E00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.h */, B1B5584420EF768E00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.h */, B1B5584520EF768E00210D55 /* RoomOutgoingEncryptedAttachmentBubbleCell.h */, @@ -3920,6 +3923,7 @@ B1B558E820EF768F00210D55 /* RoomIncomingAttachmentWithPaginationTitleBubbleCell.m in Sources */, B1B558F320EF768F00210D55 /* RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B1B557BD20EF5B4500210D55 /* KeyboardGrowingTextView.m in Sources */, + B1A68593229E807A00D6C09A /* RoomBubbleCellLayout.swift in Sources */, B1B558F420EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, B1B5572320EE6C4D00210D55 /* AttachmentsViewController.m in Sources */, F083BDEE1E7009ED00A9B29C /* MXRoom+Riot.m in Sources */, diff --git a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomBubbleCellLayout.swift b/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomBubbleCellLayout.swift new file mode 100644 index 0000000000..dee83d9855 --- /dev/null +++ b/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomBubbleCellLayout.swift @@ -0,0 +1,48 @@ +/* + Copyright 2019 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 + +/// MXKRoomBubbleTableViewCell layout constants +@objcMembers +final class RoomBubbleCellLayout: NSObject { + + // Reactions + + static let reactionsViewTopMargin: CGFloat = 1.0 + static let reactionsViewLeftMargin: CGFloat = 55.0 + static let reactionsViewRightMargin: CGFloat = 15.0 + + // Read receipts + + static let readReceiptsViewTopMargin: CGFloat = 5.0 + static let readReceiptsViewRightMargin: CGFloat = 6.0 + static let readReceiptsViewHeight: CGFloat = 12.0 + static let readReceiptsViewWidth: CGFloat = 150.0 + + // Read marker + + static let readMarkerViewHeight: CGFloat = 2.0 + + // Timestamp + + static let timestampLabelHeight: CGFloat = 18.0 + static let timestampLabelWidth: CGFloat = 39.0 + + // Others + + static let encryptedContentLeftMargin: CGFloat = 15.0 +} From 550f3aac9ef36ab352bd291441004e06c7b78b96 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 12:18:56 +0200 Subject: [PATCH 120/266] MXKRoomBubbleTableViewCell: Add convenient method to get frame of a MXKRoomBubbleComponent in cell contentView. Update timestamp label frame calculation. --- .../MXKRoomBubbleTableViewCell+Riot.h | 12 ++- .../MXKRoomBubbleTableViewCell+Riot.m | 90 +++++++++---------- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h index c6d77e2ae5..823d31924d 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h @@ -86,9 +86,17 @@ extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer; Calculate component frame in table view. @param componentIndex index of the component in bubble message data - @return component frame if component exist or CGRectNull. + @return component frame in table view if component exist or CGRectNull. */ -- (CGRect)componentFrameForIndex:(NSInteger)componentIndex; +- (CGRect)componentFrameInTableViewForIndex:(NSInteger)componentIndex; + +/** + Calculate component frame in tableview cell contentView. + + @param componentIndex index of the component in bubble message data + @return component frame in contentView if component exist or CGRectNull. + */ +- (CGRect)componentFrameInContentViewForIndex:(NSInteger)componentIndex; /** Blur the view by adding a transparent overlay. Default is NO. diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 1a7332b36e..6dd46f4312 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -24,8 +24,6 @@ #import -#define VECTOR_ROOMBUBBLETABLEVIEWCELL_TIMELABEL_WIDTH 39 - #define VECTOR_ROOMBUBBLETABLEVIEWCELL_MARK_X 48 #define VECTOR_ROOMBUBBLETABLEVIEWCELL_MARK_WIDTH 4 @@ -42,7 +40,7 @@ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex if (componentIndex < bubbleComponents.count) { - component = bubbleComponents[componentIndex]; + component = bubbleComponents[componentIndex]; } if (component && component.date) @@ -62,22 +60,26 @@ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex // Display timestamp on the left for selected component when it cannot overlap other UI elements like user's avatar BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible && !isFirstDisplayedComponent && !isLastMessageMostRecentComponent; - [self addTimestampLabelForComponent:component - isFirstDisplayedComponent:isFirstDisplayedComponent - viewTag:componentIndex - displayOnLeft:displayLabelOnLeft]; + [self addTimestampLabelForComponentIndex:componentIndex + isFirstDisplayedComponent:isFirstDisplayedComponent + viewTag:componentIndex + displayOnLeft:displayLabelOnLeft]; } } -- (void)addTimestampLabelForComponent:(MXKRoomBubbleComponent*)component - isFirstDisplayedComponent:(BOOL)isFirstDisplayedComponent - viewTag:(NSInteger)viewTag - displayOnLeft:(BOOL)displayOnLeft +- (void)addTimestampLabelForComponentIndex:(NSInteger)componentIndex + isFirstDisplayedComponent:(BOOL)isFirstDisplayedComponent + viewTag:(NSInteger)viewTag + displayOnLeft:(BOOL)displayOnLeft { + NSArray *bubbleComponents = bubbleData.bubbleComponents; + MXKRoomBubbleComponent *component = bubbleComponents[componentIndex]; + self.bubbleInfoContainer.hidden = NO; CGFloat timeLabelPosX; CGFloat timeLabelPosY; + CGFloat timeLabelHeight = RoomBubbleCellLayout.timestampLabelHeight; CGFloat timeLabelWidth; NSTextAlignment timeLabelTextAlignment; @@ -93,13 +95,24 @@ - (void)addTimestampLabelForComponent:(MXKRoomBubbleComponent*)component } else { - timeLabelPosX = self.bubbleInfoContainer.frame.size.width - VECTOR_ROOMBUBBLETABLEVIEWCELL_TIMELABEL_WIDTH; - timeLabelPosY = isFirstDisplayedComponent ? 0 : component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant; - timeLabelWidth = VECTOR_ROOMBUBBLETABLEVIEWCELL_TIMELABEL_WIDTH; + timeLabelPosX = self.bubbleInfoContainer.frame.size.width - RoomBubbleCellLayout.timestampLabelWidth; + + CGRect componentFrame = [self componentFrameInContentViewForIndex:componentIndex]; + + if (CGRectEqualToRect(componentFrame, CGRectNull) == false) + { + timeLabelPosY = componentFrame.origin.y - timeLabelHeight - self.bubbleInfoContainerTopConstraint.constant; + } + else + { + timeLabelPosY = isFirstDisplayedComponent ? 0 : component.position.y + self.msgTextViewTopConstraint.constant - timeLabelHeight - self.bubbleInfoContainerTopConstraint.constant; + } + + timeLabelWidth = RoomBubbleCellLayout.timestampLabelWidth; timeLabelTextAlignment = NSTextAlignmentRight; } - UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(timeLabelPosX, timeLabelPosY, timeLabelWidth , 18)]; + UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(timeLabelPosX, timeLabelPosY, timeLabelWidth, timeLabelHeight)]; timeLabel.text = [bubbleData.eventFormatter timeStringFromDate:component.date]; timeLabel.textAlignment = timeLabelTextAlignment; @@ -144,32 +157,10 @@ - (void)addTimestampLabelForComponent:(MXKRoomBubbleComponent*)component toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 - constant:18]; + constant:timeLabelHeight]; // Available on iOS 8 and later [NSLayoutConstraint activateConstraints:@[rightConstraint, topConstraint, widthConstraint, heightConstraint]]; - - // Check whether a vertical whitespace was applied to display correctly the timestamp. - if (!displayOnLeft && (!isFirstDisplayedComponent || bubbleData.shouldHideSenderInformation || bubbleData.shouldHideSenderName)) - { - // Adjust the position of the potential encryption icon in this case. - if (self.encryptionStatusContainerView) - { - NSArray* subviews = self.encryptionStatusContainerView.subviews; - for (UIView *view in subviews) - { - // Note: The encryption icon has been tagged with the component index. - if (view.tag == viewTag) - { - CGRect frame = view.frame; - frame.origin.y += 15; - view.frame = frame; - - break; - } - } - } - } } - (void)selectComponent:(NSUInteger)componentIndex @@ -307,7 +298,7 @@ - (void)addDateLabel NSDate *date = bubbleData.date; if (date) { - UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.bubbleInfoContainer.frame.size.width , 18)]; + UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, self.bubbleInfoContainer.frame.size.width, RoomBubbleCellLayout.timestampLabelHeight)]; timeLabel.text = [bubbleData.eventFormatter dateStringFromDate:date withTime:NO]; timeLabel.textAlignment = NSTextAlignmentRight; @@ -354,7 +345,7 @@ - (void)addDateLabel toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 - constant:18]; + constant:RoomBubbleCellLayout.timestampLabelHeight]; // Available on iOS 8 and later [NSLayoutConstraint activateConstraints:@[rightConstraint, topConstraint, widthConstraint, heightConstraint]]; @@ -459,7 +450,13 @@ - (void)updateUserNameColor } } -- (CGRect)componentFrameForIndex:(NSInteger)componentIndex +- (CGRect)componentFrameInTableViewForIndex:(NSInteger)componentIndex +{ + CGRect componentFrameInContentView = [self componentFrameInContentViewForIndex:componentIndex]; + return [self.contentView convertRect:componentFrameInContentView toView:self.superview]; +} + +- (CGRect)componentFrameInContentViewForIndex:(NSInteger)componentIndex { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = self; MXKRoomBubbleCellData *bubbleCellData = roomBubbleTableViewCell.bubbleData; @@ -518,16 +515,15 @@ - (CGRect)componentFrameForIndex:(NSInteger)componentIndex if (roomBubbleTableViewCell.attachmentView || roomBubbleTableViewCell.messageTextView) { - CGRect roomBubbleTableViewCellFrame = roomBubbleTableViewCell.frame; - CGFloat x = roomBubbleTableViewCellFrame.origin.x; - CGFloat y = roomBubbleTableViewCellFrame.origin.y + selectedComponenContentViewYOffset + selectedComponentPositionY; - CGFloat width = roomBubbleTableViewCellFrame.size.width; + CGFloat x = 0; + CGFloat y = selectedComponenContentViewYOffset + selectedComponentPositionY; + CGFloat width = roomBubbleTableViewCell.contentView.frame.size.width; componentFrame = CGRectMake(x, y, width, selectedComponentHeight); } else { - componentFrame = roomBubbleTableViewCell.frame; + componentFrame = roomBubbleTableViewCell.bounds; } return componentFrame; @@ -581,7 +577,7 @@ - (void)addEditButtonForComponent:(NSUInteger)componentIndex completion:(void (^ // Define 'Edit' button frame UIImage *editIcon = [UIImage imageNamed:@"edit_icon"]; - CGFloat editBtnPosX = self.bubbleInfoContainer.frame.size.width - VECTOR_ROOMBUBBLETABLEVIEWCELL_TIMELABEL_WIDTH - 22 - editIcon.size.width / 2; + CGFloat editBtnPosX = self.bubbleInfoContainer.frame.size.width - RoomBubbleCellLayout.timestampLabelWidth - 22 - editIcon.size.width / 2; CGFloat editBtnPosY = isFirstDisplayedComponent ? -13 : component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant - 13; UIButton *editButton = [[UIButton alloc] initWithFrame:CGRectMake(editBtnPosX, editBtnPosY, 44, 44)]; From a682a18b5530947cb73b8e5fc8448f6c31957e46 Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Tue, 28 May 2019 09:42:12 +0000 Subject: [PATCH 121/266] Translated using Weblate (Dutch) Currently translated at 100.0% (710 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/nl/ --- Riot/Assets/nl.lproj/Vector.strings | 147 +++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 23 deletions(-) diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index c4c986498d..1cf3cde2b6 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -18,7 +18,7 @@ "title_home" = "Thuis"; "title_favourites" = "Favorieten"; "title_people" = "Personen"; -"title_rooms" = "Kamers"; +"title_rooms" = "Gesprekken"; "warning" = "Waarschuwing"; // Actions "view" = "Weergeven"; @@ -115,8 +115,8 @@ "room_recents_directory_section_network" = "Netwerk"; "room_recents_favourites_section" = "FAVORIETEN"; "room_recents_people_section" = "PERSONEN"; -"room_recents_conversations_section" = "KAMERS"; -"room_recents_no_conversation" = "Geen kamers"; +"room_recents_conversations_section" = "GESPREKKEN"; +"room_recents_no_conversation" = "Geen gesprekken"; "room_recents_low_priority_section" = "LAGE PRIORITEIT"; "room_recents_invites_section" = "UITNODIGINGEN"; "room_recents_start_chat_with" = "Gesprek beginnen"; @@ -129,9 +129,9 @@ "people_conversation_section" = "GESPREKKEN"; "people_no_conversation" = "Geen gesprekken"; // Rooms tab -"room_directory_no_public_room" = "Geen publieke kamers beschikbaar"; +"room_directory_no_public_room" = "Geen publieke gesprekken beschikbaar"; // Search -"search_rooms" = "Kamers"; +"search_rooms" = "Gesprekken"; "search_messages" = "Berichten"; "search_people" = "Personen"; "search_files" = "Bestanden"; @@ -140,7 +140,7 @@ "search_no_result" = "Geen resultaten"; // Directory "directory_cell_title" = "Bladeren door de catalogus"; -"directory_cell_description" = "%tu kamers"; +"directory_cell_description" = "%tu gesprekken"; "directory_search_results_title" = "Bladeren door catalogusresultaten"; "directory_search_results" = "%tu resultaten gevonden voor %@"; "directory_search_results_more_than" = ">%tu resultaten gevonden voor %@"; @@ -270,7 +270,7 @@ "settings_cryptography" = "CRYPTOGRAFIE"; "settings_sign_out" = "Afmelden"; "settings_sign_out_confirmation" = "Weet u het zeker?"; -"settings_sign_out_e2e_warn" = "U zult uw sleutels voor eind-tot-eind-versleuteling kwijtraken. Dat betekent dat u op dit apparaat geen oude berichten in versleutelde kamers meer zult kunnen lezen."; +"settings_sign_out_e2e_warn" = "U zult uw sleutels voor eind-tot-eind-versleuteling kwijtraken. Dat betekent dat u op dit apparaat geen oude berichten in versleutelde gesprekken meer zult kunnen lezen."; "settings_profile_picture" = "Profielfoto"; "settings_display_name" = "Weergavenaam"; "settings_first_name" = "Voornaam"; @@ -288,8 +288,8 @@ "settings_fail_to_update_profile" = "Bijwerken van profiel is mislukt"; "settings_enable_push_notif" = "Meldingen op dit apparaat"; "settings_global_settings_info" = "Globale meldingsinstellingen zijn beschikbaar op uw %@-webcliënt"; -"settings_pin_rooms_with_missed_notif" = "Kamers met gemiste meldingen vastprikken"; -"settings_pin_rooms_with_unread" = "Kamers met ongelezen berichten vastprikken"; +"settings_pin_rooms_with_missed_notif" = "Gesprekken met gemiste meldingen vastprikken"; +"settings_pin_rooms_with_unread" = "Gesprekken met ongelezen berichten vastprikken"; "settings_on_denied_notification" = "Meldingen worden geweigerd voor %@, sta ze toe in uw apparaatinstellingen"; //"settings_enable_all_notif" = "Alle notificaties aanzetten"; //"settings_messages_my_display_name" = "Bericht dat mijn naam bevat"; @@ -368,7 +368,7 @@ "room_details_advanced_e2e_encryption_prompt_message" = "End-to-endbeveiliging is experimenteel en kan onbetrouwbaar zijn.\n\nHet is beter om het nog niet met gevoelige gegevens te vertrouwen.\n\nApparaten kunnen de geschiedenis van voordat ze de ruimte betraden nog niet ontsleutelen.\n\nZodra de versleuteling aan staat kan het (voorlopig) niet worden uitgezet.\n\nVersleutelde berichten zullen nog niet zichtbaar zijn op programma's die geen versleuteling ondersteunen."; "room_details_fail_to_update_avatar" = "Bijwerken van gespreksfoto is mislukt"; "room_details_fail_to_update_room_name" = "Bijwerken van gespreksnaam is mislukt"; -"room_details_fail_to_update_topic" = "Bijwerken van kameronderwerp is mislukt"; +"room_details_fail_to_update_topic" = "Bijwerken van gespreksonderwerp is mislukt"; "room_details_fail_to_update_room_guest_access" = "Bijwerken van gasttoegang tot gesprek is mislukt"; "room_details_fail_to_update_room_join_rule" = "Bijwerken van toetredingsregel is mislukt"; "room_details_fail_to_update_room_directory_visibility" = "Bijwerken van zichtbaarheid in de gesprekscatalogus is mislukt"; @@ -389,9 +389,9 @@ // Directory "directory_title" = "Catalogus"; "directory_server_picker_title" = "Selecteer een catalogus"; -"directory_server_all_rooms" = "Alle kamers op server %@"; -"directory_server_all_native_rooms" = "Alle lokale Matrix-kamers"; -"directory_server_type_homeserver" = "Voer een thuisserver in om de publieke kamers ervan weer te geven"; +"directory_server_all_rooms" = "Alle gesprekken op server %@"; +"directory_server_all_native_rooms" = "Alle lokale Matrix-gesprekken"; +"directory_server_type_homeserver" = "Voer een thuisserver in om de publieke gesprekken ervan weer te geven"; "directory_server_placeholder" = "matrix.org"; // Others "or" = "of"; @@ -399,7 +399,7 @@ "today" = "Vandaag"; "yesterday" = "Gisteren"; "network_offline_prompt" = "Het ziet er naar uit dat de internetverbinding offline is."; -"public_room_section_title" = "Publieke kamers (op %@):"; +"public_room_section_title" = "Publieke gesprekken (op %@):"; "bug_report_prompt" = "De app is de vorige keer gecrasht. Wilt u een crashrapport indienen?"; "rage_shake_prompt" = "Het ziet er naar uit dat u de telefoon in frustratie schudt. Wilt u een foutmelding indienen?"; "camera_access_not_granted" = "%@ heeft geen toestemming om de camera te gebruiken, pas de privacy-instellingen aan"; @@ -512,12 +512,12 @@ "group_details_title" = "Gemeenschapsdetails"; "group_details_home" = "Thuis"; "group_details_people" = "Personen"; -"group_details_rooms" = "Kamers"; +"group_details_rooms" = "Gesprekken"; // Group Home "group_home_one_member_format" = "1 lid"; "group_home_multi_members_format" = "%tu leden"; "group_home_one_room_format" = "1 gesprek"; -"group_home_multi_rooms_format" = "%tu kamers"; +"group_home_multi_rooms_format" = "%tu gesprekken"; "group_invitation_format" = "%@ heeft u uitgenodigd om tot deze gemeenschap toe te treden"; // Group participants "group_participants_add_participant" = "Deelnemer toevoegen"; @@ -533,7 +533,7 @@ "group_participants_invite_malformed_id" = "Misvormde ID. Dit moet een Matrix-ID zijn, zoals ‘@gebruikersnaam:domein’"; "group_participants_invited_section" = "UITGENODIGD"; // Group rooms -"group_rooms_filter_rooms" = "Gemeenschapskamers filteren"; +"group_rooms_filter_rooms" = "Gemeenschapsgesprekken filteren"; "e2e_room_key_request_message_new_device" = "U heeft een nieuw apparaat ‘%@’ toegevoegd, dat vraagt naar versleutelingssleutels."; "room_event_action_kick_prompt_reason" = "Reden voor het verwijderen van deze gebruiker"; "room_event_action_ban_prompt_reason" = "Reden voor het verbannen van deze gebruiker"; @@ -553,7 +553,7 @@ "widget_sticker_picker_no_stickerpacks_alert" = "U heeft momenteel geen stickerpakketten ingeschakeld."; "widget_sticker_picker_no_stickerpacks_alert_add_now" = "Wilt u er nu een paar toevoegen?"; "deactivate_account_title" = "Account deactiveren"; -"deactivate_account_informations_part1" = "Dit zal uw account voorgoed onbruikbaar maken. U zult zich niet meer kunnen aanmelden, en niemand anders zal zich met dezelfde gebruikers-ID kunnen registreren. Dit zal er voor zorgen dat uw account alle kamers verlaat waar deze momenteel lid van is, en het verwijdert uw accountgegevens van de identiteitsserver. "; +"deactivate_account_informations_part1" = "Dit zal uw account voorgoed onbruikbaar maken. U zult zich niet meer kunnen aanmelden, en niemand anders zal zich met dezelfde gebruikers-ID kunnen registreren. Dit zal er voor zorgen dat uw account alle gesprekken verlaat waar deze momenteel lid van is, en het verwijdert uw accountgegevens van de identiteitsserver. "; "deactivate_account_informations_part2_emphasize" = "Deze actie is onomkeerbaar."; "deactivate_account_informations_part3" = "\n\nHet deactiveren van uw account "; "deactivate_account_informations_part4_emphasize" = "zal er niet standaard voor zorgen dat de berichten die u heeft verzonden worden vergeten. "; @@ -569,7 +569,7 @@ "room_message_reply_to_short_placeholder" = "Stuur een antwoord…"; // String for App Store "store_short_description" = "Veilig en gedecentraliseerd chatten en bellen"; -"store_full_description" = "Communiceer op uw manier.\n\nEen chat-app, onder uw controle en heel flexibel. Riot laat u communiceren zoals u dat wilt. Gemaakt voor [matrix] - de standaard voor open, gedecentraliseerde communicatie.\n\nMaak een gratis account aan op matrix.org, verkrijg uw eigen server op https://modular.im, of gebruik een andere Matrix-server.\n\nWaarom zou ik voor Riot.im kiezen?\n\n• VOLLEDIGE COMMUNICATIE: maak kamers aan rond uw teams, uw vrienden, uw gemeenschap - hoe u maar wilt! Chat, deel bestanden, voeg widgets toe en maak stem- en video-oproepen - allemaal volledig gratis.\n\n• KRACHTIGE INTEGRATIE: gebruik Riot.im met de hulpmiddelen waarmee u vertrouwd bent. Met Riot.im kunt u zelfs chatten met gebruikers en groepen op andere chat-apps.\n\n• PRIVÉ EN VEILIG: houd uw gesprekken geheim. Eind-tot-eind-versleuteling van de bovenste plank zorgt ervoor dat uw privécommunicatie ook privé blijft.\n\n• OPEN, NIET GESLOTEN: vrije software, gebouwd op Matrix. Wees baas over uw eigen gegevens door uw eigen server te gebruiken, of te kiezen voor een andere server die u vertrouwt.\n\n• WAAR U OOK BENT: houd contact waar u ook bent met volledig gesynchroniseerde berichtgeschiedenis op al uw apparaten, en online op https://riot.im."; +"store_full_description" = "Communiceer op uw manier.\n\nEen chat-app, onder uw controle en heel flexibel. Riot laat u communiceren zoals u dat wilt. Gemaakt voor [matrix] - de standaard voor open, gedecentraliseerde communicatie.\n\nMaak een gratis account aan op matrix.org, verkrijg uw eigen server op https://modular.im, of gebruik een andere Matrix-server.\n\nWaarom zou ik voor Riot.im kiezen?\n\n• VOLLEDIGE COMMUNICATIE: maak gesprekken aan rond uw teams, uw vrienden, uw gemeenschap - hoe u maar wilt! Chat, deel bestanden, voeg widgets toe en maak stem- en video-oproepen - allemaal volledig gratis.\n\n• KRACHTIGE INTEGRATIE: gebruik Riot.im met de hulpmiddelen waarmee u vertrouwd bent. Met Riot.im kunt u zelfs chatten met gebruikers en groepen op andere chat-apps.\n\n• PRIVÉ EN VEILIG: houd uw gesprekken geheim. Eind-tot-eind-versleuteling van de bovenste plank zorgt ervoor dat uw privécommunicatie ook privé blijft.\n\n• OPEN, NIET GESLOTEN: vrije software, gebouwd op Matrix. Wees baas over uw eigen gegevens door uw eigen server te gebruiken, of te kiezen voor een andere server die u vertrouwt.\n\n• WAAR U OOK BENT: houd contact waar u ook bent met volledig gesynchroniseerde berichtgeschiedenis op al uw apparaten, en online op https://riot.im."; "auth_login_single_sign_on" = "Aanmelden met enkele aanmelding"; "auth_accept_policies" = "Gelieve het beleid van deze thuisserver te lezen en aanvaarden:"; "auth_autodiscover_invalid_response" = "Ongeldig thuisserverontdekkingsantwoord"; @@ -587,16 +587,16 @@ "room_resource_usage_limit_reached_message_2" = "sommige gebruikers zullen zich niet kunnen aanmelden."; "room_resource_usage_limit_reached_message_contact_3" = " om deze limiet te verhogen."; "settings_key_backup" = "SLEUTELBACK-UP"; -"settings_labs_room_members_lazy_loading" = "Kamerleden lui laden"; +"settings_labs_room_members_lazy_loading" = "Gespreksleden lui laden"; "settings_labs_room_members_lazy_loading_error_message" = "Uw thuisserver ondersteunt het lui laden van gespreksleden nog niet. Probeer het later opnieuw."; "settings_key_backup_info" = "Versleutelde berichten worden beveiligd met eind-tot-eind-versleuteling. Enkel de ontvanger(s) en u hebben de sleutels om deze berichten te lezen."; "settings_key_backup_info_checking" = "Bezig met controleren…"; "settings_key_backup_info_none" = "Uw sleutels worden niet geback-upt vanaf dit apparaat."; -"settings_key_backup_info_signout_warning" = "Maak een back-up van uw sleutels vooraleer u zich afmeldt om ze niet te verliezen."; +"settings_key_backup_info_signout_warning" = "Verbind dit apparaat met de sleutelback-up vooraleer u zich afmeldt om sleutels die enkel op dit apparaat staat niet te verliezen."; "settings_key_backup_info_version" = "Sleutelback-upversie: %@"; "settings_key_backup_info_algorithm" = "Algoritme: %@"; "settings_key_backup_info_valid" = "Dit apparaat maakt een back-up van uw sleutels."; -"settings_key_backup_info_not_valid" = "Dit apparaat maakt geen back-up van uw sleutels."; +"settings_key_backup_info_not_valid" = "Dit apparaat maakt geen back-up van uw sleutels, maar u heeft wel een bestaande back-up waarvan u kunt herstellen, en u vanaf dan nieuwe sleutels aan kunt toevoegen."; "settings_key_backup_info_progress" = "Back-up van %@ sleutels wordt gemaakt…"; "settings_key_backup_info_progress_done" = "Alle sleutels zijn geback-upt"; "settings_key_backup_info_trust_signature_unknown" = "De back-up heeft een ondertekening van apparaat met ID: %@"; @@ -622,7 +622,7 @@ "key_backup_setup_skip_alert_message" = "U verliest mogelijk uw versleutelde berichten als u zich afmeldt of uw apparaat verliest."; "key_backup_setup_skip_alert_skip_action" = "Overslaan"; "key_backup_setup_intro_title" = "Verlies nooit uw versleutelde berichten"; -"key_backup_setup_intro_info" = "Berichten in versleutelde kamers worden versleutelde met eind-tot-eind-beveiliging. Enkel de ontvanger(s) en u hebben de sleutels om deze berichten te lezen.\n\nMaak een veilige back-up van uw sleutels om deze niet te verliezen."; +"key_backup_setup_intro_info" = "Berichten in versleutelde gesprekken worden versleuteld met eind-tot-eind-beveiliging. Enkel de ontvanger(s) en u hebben de sleutels om deze berichten te lezen.\n\nMaak een veilige back-up van uw sleutels om deze niet te verliezen."; "key_backup_setup_intro_setup_action_without_existing_backup" = "Begin sleutelback-up te gebruiken"; "key_backup_setup_intro_manual_export_info" = "(Geavanceerd)"; "key_backup_setup_intro_manual_export_action" = "Sleutels handmatig exporteren"; @@ -683,3 +683,104 @@ "sign_out_key_backup_in_progress_alert_title" = "Sleutelback-up is bezig. Als u zich nu afmeldt zult u de toegang tot uw versleutelde berichten verliezen."; "sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Ik wil mijn versleutelde berichten niet"; "sign_out_key_backup_in_progress_alert_cancel_action" = "Ik wacht wel"; +"room_event_action_reply" = "Beantwoorden"; +"room_event_action_edit" = "Bewerken"; +"room_event_action_reaction_agree" = "Akkoord met %@"; +"room_event_action_reaction_disagree" = "Niet akkoord met %@"; +"room_event_action_reaction_like" = "Duim omhoog voor %@"; +"room_event_action_reaction_dislike" = "Duim omlaag voor %@"; +"room_action_reply" = "Beantwoorden"; +"settings_labs_message_reaction" = "Beantwoord berichten met emoticons"; +"settings_key_backup_button_connect" = "Dit apparaat verbinden met sleutelback-up"; +"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Dit apparaat verbinden met sleutelback-up"; +"key_backup_recover_connent_banner_subtitle" = "Dit apparaat verbinden met sleutelback-up"; +// MARK: - Device Verification +"device_verification_title" = "Apparaat verifiëren"; +"device_verification_security_advice" = "Voor een maximale beveiliging raden we aan om dit onder vier ogen te doen, of via een ander vertrouwd communicatiekanaal"; +"device_verification_cancelled" = "De andere partij heeft de verificatie geannuleerd."; +"device_verification_cancelled_by_me" = "De verificatie is geannuleerd. Reden: %@"; +"device_verification_error_cannot_load_device" = "Kan apparaatsinformatie niet laden."; +// Mark: Incoming +"device_verification_incoming_title" = "Inkomend verificatieverzoek"; +"device_verification_incoming_description_1" = "Verifieer dit apparaat om het als vertrouwd te markeren. Door de apparaten van uw partners te vertrouwen hoeft u zich nog minder zorgen te maken over het gebruik van eind-tot-eind-versleutelde berichten."; +"device_verification_incoming_description_2" = "Dit apparaat verifiëren zal het als vertrouwd markeren, en het ook aan uw gesprekspartner als vertrouwd markeren."; +// MARK: Start +"device_verification_start_title" = "Verifieer door een korte tekenreeks te vergelijken"; +"device_verification_start_wait_partner" = "Wachten op partner om te aanvaarden…"; +"device_verification_start_use_legacy" = "Verschijnt er niets? Nog niet alle cliënten bieden ondersteuning voor interactieve verificatie. Gebruik de traditionele verificatiemethode."; +"device_verification_start_verify_button" = "Verificatie beginnen"; +"device_verification_start_use_legacy_action" = "Traditionele verificatie gebruiken"; +// MARK: Verify +"device_verification_verify_title_emoji" = "Verifieer dit apparaat door te bevestigen dat de volgende emoticons op het scherm van uw gesprekspartner verschijnen"; +"device_verification_verify_title_number" = "Verifieer dit apparaat door te bevestigen dat de volgende cijfers op het scherm van uw gesprekspartner verschijnen"; +"device_verification_verify_wait_partner" = "Wachten op partner voor bevestiging…"; +// MARK: Verified +"device_verification_verified_title" = "Geverifieerd!"; +"device_verification_verified_description_1" = "U heeft dit apparaat geverifieerd."; +"device_verification_verified_description_2" = "Beveiligde berichten met deze gebruiker zijn eind-tot-eind-versleuteld en kunnen niet door derde partijen gelezen worden."; +"device_verification_verified_got_it_button" = "Ik snap het"; +// MARK: Emoji +"device_verification_emoji_dog" = "Hond"; +"device_verification_emoji_cat" = "Kat"; +"device_verification_emoji_lion" = "Leeuw"; +"device_verification_emoji_horse" = "Paard"; +"device_verification_emoji_unicorn" = "Eenhoorn"; +"device_verification_emoji_pig" = "Varken"; +"device_verification_emoji_elephant" = "Olifant"; +"device_verification_emoji_rabbit" = "Konijn"; +"device_verification_emoji_panda" = "Panda"; +"device_verification_emoji_rooster" = "Haan"; +"device_verification_emoji_penguin" = "Pinguïn"; +"device_verification_emoji_turtle" = "Schildpad"; +"device_verification_emoji_fish" = "Vis"; +"device_verification_emoji_octopus" = "Octopus"; +"device_verification_emoji_butterfly" = "Vlinder"; +"device_verification_emoji_flower" = "Bloem"; +"device_verification_emoji_tree" = "Boom"; +"device_verification_emoji_cactus" = "Cactus"; +"device_verification_emoji_mushroom" = "Paddenstoel"; +"device_verification_emoji_globe" = "Wereldbol"; +"device_verification_emoji_moon" = "Maan"; +"device_verification_emoji_cloud" = "Wolk"; +"device_verification_emoji_fire" = "Vuur"; +"device_verification_emoji_banana" = "Banaan"; +"device_verification_emoji_apple" = "Appel"; +"device_verification_emoji_strawberry" = "Aardbei"; +"device_verification_emoji_corn" = "Maïs"; +"device_verification_emoji_pizza" = "Pizza"; +"device_verification_emoji_cake" = "Taart"; +"device_verification_emoji_heart" = "Hart"; +"device_verification_emoji_smiley" = "Smiley"; +"device_verification_emoji_robot" = "Robot"; +"device_verification_emoji_hat" = "Hoed"; +"device_verification_emoji_glasses" = "Bril"; +"device_verification_emoji_spanner" = "Moersleutel"; +"device_verification_emoji_santa" = "Kerstman"; +"device_verification_emoji_thumbs up" = "Duim omhoog"; +"device_verification_emoji_umbrella" = "Paraplu"; +"device_verification_emoji_hourglass" = "Zandloper"; +"device_verification_emoji_clock" = "Klok"; +"device_verification_emoji_gift" = "Cadeau"; +"device_verification_emoji_light bulb" = "Gloeilamp"; +"device_verification_emoji_book" = "Boek"; +"device_verification_emoji_pencil" = "Potlood"; +"device_verification_emoji_paperclip" = "Paperclip"; +"device_verification_emoji_scissors" = "Schaar"; +"device_verification_emoji_padlock" = "Hangslot"; +"device_verification_emoji_key" = "Sleutel"; +"device_verification_emoji_hammer" = "Hamer"; +"device_verification_emoji_telephone" = "Telefoon"; +"device_verification_emoji_flag" = "Vlag"; +"device_verification_emoji_train" = "Trein"; +"device_verification_emoji_bicycle" = "Fiets"; +"device_verification_emoji_aeroplane" = "Vliegtuig"; +"device_verification_emoji_rocket" = "Raket"; +"device_verification_emoji_trophy" = "Trofee"; +"device_verification_emoji_ball" = "Bal"; +"device_verification_emoji_guitar" = "Gitaar"; +"device_verification_emoji_trumpet" = "Trompet"; +"device_verification_emoji_bell" = "Bel"; +"device_verification_emoji_anchor" = "Anker"; +"device_verification_emoji_headphones" = "Koptelefoon"; +"device_verification_emoji_folder" = "Map"; +"device_verification_emoji_pin" = "Speld"; From de83ad37883e155bfffdcab355ee18881f0b9b61 Mon Sep 17 00:00:00 2001 From: Dawid Potocki Date: Tue, 28 May 2019 04:36:12 +0000 Subject: [PATCH 122/266] Translated using Weblate (Polish) Currently translated at 66.5% (472 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/pl/ --- Riot/Assets/pl.lproj/Vector.strings | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/pl.lproj/Vector.strings b/Riot/Assets/pl.lproj/Vector.strings index 0ff5bba82b..4df9c322fd 100644 --- a/Riot/Assets/pl.lproj/Vector.strings +++ b/Riot/Assets/pl.lproj/Vector.strings @@ -207,7 +207,7 @@ // Room Preview "room_preview_invitation_format" = "Zostałeś(-aś) zaproszony(-a) do tego pokoju przez %@"; "room_preview_subtitle" = "To jest podgląd pokoju. Interakcje zostały zablokowane."; -"room_preview_unlinked_email_warning" = "Zaproszenie zostało wysłane do %@, który nie jest powiązany z zalogowanym kontem. Możesz zalogować się z wykorzystaniem innego konta, albo dodać ten adres e-mail do swoich kontaktów."; +"room_preview_unlinked_email_warning" = "Zaproszenie zostało wysłane do %@, który nie jest powiązany z tym kontem. Możesz zalogować się z wykorzystaniem innego konta, albo dodać ten adres email do swojego konta."; "room_preview_try_join_an_unknown_room_default" = "pokój"; // Settings "settings_title" = "Ustawienia"; @@ -488,3 +488,25 @@ "media_picker_library" = "Biblioteka"; "large_badge_value_k_format" = "%.1fK"; "bug_crash_report_title" = "Raport o awarii"; +// String for App Store +"store_short_description" = "Bezpieczny, zdecentralizowany czat/VoIP"; +"store_full_description" = "Komunikuj się, po swojemu.\n\nAplikacja do czatowania, pod Twoją kontrolą i całkowicie elastyczna. Riot pozwala Ci komunikować się tak, jak chcesz. Stworzona dla [matrixa] - otwartego standardu, zdecentralizowanej komunikacji.\n\nZałóż darmowe konto na matrix.org, załatw swój własny serwer na https://modular.im lub skorzystaj z innego serwera Matrix.\n\nDlaczego warto wybrać Riot.im?\n\nPEŁNA KOMUNIKACJA: Zbuduj pokoje wokół swoich zespołów, przyjaciół, społeczności - jak chcesz! Czat, udostępnianie plików, dodawanie widgetów i wykonywanie połączeń głosowych i wideo - wszystko to za darmo.\n\n\nPOTĘŻNA INTEGRACJA: Użyj Riot.im z narzędziami, które znasz i kochasz. Dzięki Riot.im możesz nawet rozmawiać z użytkownikami i grupami z innymi aplikacjami do czatowania.\n\nPRYWATNY I BEZPIECZNY: Trzymaj swoje rozmowy w tajemnicy. Najnowocześniejsze szyfrowanie typu end-to-end zapewnia prywatną komunikację.\n\nOTWARTY, NIE ZAMKNIĘTY: Open source i zbudowany na Matrixie. Miej swoje dane pod kontrolą poprzez hosting własnego serwera lub wybranie serwera, któremu ufasz.\n\nGDZIEKOLWIEK JESTEŚ: Bądź w kontakcie gdziekolwiek jesteś, dzięki w pełni zsynchronizowanej historii wiadomości na wszystkich Twoich urządzeniach i online na https://riot.im."; +"room_creation_make_public_prompt_msg" = "Jesteś pewien, że chcesz zrobić ten czat publiczny? Każdy może czytać Twoje wiadomości i dołączyć do czatu."; +"directory_search_fail" = "Nie udało się pobrać danych"; +"contacts_address_book_permission_required" = "Uprawnienie jest wymagane żeby uzyskać dostęp do kontaktów lokalnych"; +"room_participants_invite_malformed_id_title" = "Błąd Zaproszenia"; +"room_participants_action_ban" = "Zbanuj z tego pokoju"; +"room_unsent_messages_unknown_devices_notification" = "Wiadomość nie została wysłana z powodu obecności nieznanych urządzeń. %@ lub %@@ teraz?"; +"room_event_action_ban_prompt_reason" = "Powód, dla którego zbanowano tego użytkownika"; +"room_event_action_reply" = "Odpowiedz"; +"room_event_action_edit" = "Edytuj"; +"room_action_reply" = "Odpowiedz"; +"room_preview_try_join_an_unknown_room" = "Próbujesz uzyskać dostęp do %@. Czy chciałbyś się przyłączyć, aby wziąć udział w dyskusji?"; +"account_logout_all" = "Wyloguj wszystkie konta"; +"settings_flair" = "Pokazuj wyznacznik społeczności gdzie jest to zezwolone"; +"settings_key_backup" = "KOPIA ZAPASOWA KLUCZY"; +"settings_enable_push_notif" = "Powiadomienia na tym urządzeniu"; +"settings_global_settings_info" = "Globalne ustawienia powiadomień są dostępne na Twoim kliencie internetowym %@"; +"settings_on_denied_notification" = "Powiadomienia są odrzucane w %@, proszę zezwól na nie w ustawieniach urządzenia"; +"settings_callkit_info" = "Odbieraj połączenia przychodzące na ekranie blokady. Zobacz swoje połęczenia Riot w historii połączeń w systemie. Jeśli usługa iCloud jest włączona, historia połączeń zostanie udostępniona Apple."; +"settings_ui_theme_picker_message" = "\"Auto\" używa ustawienia \"Odwróć kolory\" urządzenia"; From 99b746681bc874537e80e37d7e3c847aa75bfc12 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 12:24:43 +0200 Subject: [PATCH 123/266] RoomBubbleCellData: Fix reactions height calculation. Fix timestamp position. --- .../Room/CellData/RoomBubbleCellData.m | 69 +++++++------------ 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index bd6fad25a9..e550c36169 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -25,7 +25,6 @@ #import "Riot-Swift.h" static NSAttributedString *timestampVerticalWhitespace = nil; -static NSAttributedString *readReceiptVerticalWhitespace = nil; @interface RoomBubbleCellData() @@ -274,12 +273,12 @@ - (void)refreshBubbleComponentsPosition // Check whether there is at least one component. if (bubbleComponents.count) { - BOOL hasReadReceiptsOrReactions = NO; - // Set position of the first component CGFloat positionY = (self.attachment == nil || self.attachment.type == MXKAttachmentTypeFile || self.attachment.type == MXKAttachmentTypeAudio) ? MXKROOMBUBBLECELLDATA_TEXTVIEW_DEFAULT_VERTICAL_INSET : 0; MXKRoomBubbleComponent *component; NSUInteger index = 0; + + // Use same position for first components without render (redacted) for (; index < bubbleComponents.count; index++) { // Compute the vertical position for next component @@ -289,8 +288,6 @@ - (void)refreshBubbleComponentsPosition if (component.attributedTextMessage) { - hasReadReceiptsOrReactions = (self.reactions[component.event.eventId].reactions.count > 0); - hasReadReceiptsOrReactions |= (self.readReceipts[component.event.eventId].count > 0); break; } } @@ -314,11 +311,8 @@ - (void)refreshBubbleComponentsPosition attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:component.attributedTextMessage]; } - // Vertical whitespace is added in case of read receipts - if (hasReadReceiptsOrReactions) - { - [self addVerticalWhitespaceToString:attributedString forEvent:component.event.eventId]; - } + // Vertical whitespace is added in case of read receipts or reactions + [self addVerticalWhitespaceToString:attributedString forEvent:component.event.eventId]; [attributedString appendAttributedString:[MXKRoomBubbleCellDataWithAppendingMode messageSeparator]]; @@ -330,17 +324,12 @@ - (void)refreshBubbleComponentsPosition if (component.attributedTextMessage) { // Prepare its attributed string by considering potential vertical margin required to display timestamp. - NSAttributedString *componentString; + NSAttributedString *componentString = component.attributedTextMessage; + + // Check whether the timestamp is displayed for this component, and check whether a vertical whitespace is required if ((selectedComponentIndex == index && self.addVerticalWhitespaceForSelectedComponentTimestamp) || lastMessageIndex == index) { - NSMutableAttributedString *componentAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; - [componentAttributedString appendAttributedString:component.attributedTextMessage]; - - componentString = componentAttributedString; - } - else - { - componentString = component.attributedTextMessage; + [attributedString appendAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; } // Append this attributed string. @@ -353,9 +342,10 @@ - (void)refreshBubbleComponentsPosition positionY = MXKROOMBUBBLECELLDATA_TEXTVIEW_DEFAULT_VERTICAL_INSET + (cumulatedHeight - [self rawTextHeight:componentString]); component.position = CGPointMake(0, positionY); - + + // Vertical whitespace is added in case of read receipts or reactions [self addVerticalWhitespaceToString:attributedString forEvent:component.event.eventId]; - + [attributedString appendAttributedString:[MXKRoomBubbleCellDataWithAppendingMode messageSeparator]]; } else @@ -377,8 +367,10 @@ - (void)addVerticalWhitespaceToString:(NSMutableAttributedString *)attributedStr if (reactionCount) { + CGFloat bubbleReactionsViewWidth = self.maxTextViewWidth - 4; + CGSize fittingSize = UILayoutFittingCompressedSize; - fittingSize.width = self.maxTextViewWidth; + fittingSize.width = bubbleReactionsViewWidth; static BubbleReactionsView *bubbleReactionsView; @@ -387,12 +379,13 @@ - (void)addVerticalWhitespaceToString:(NSMutableAttributedString *)attributedStr bubbleReactionsView = [BubbleReactionsView new]; }); - bubbleReactionsView.frame = CGRectMake(0, 0, self.maxTextViewWidth, 1.0); + bubbleReactionsView.frame = CGRectMake(0, 0, bubbleReactionsViewWidth, 1.0); BubbleReactionsViewModel *viemModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:eventId]; bubbleReactionsView.viewModel = viemModel; + [bubbleReactionsView setNeedsLayout]; [bubbleReactionsView layoutIfNeeded]; - CGFloat height = [bubbleReactionsView systemLayoutSizeFittingSize:fittingSize].height; + CGFloat height = [bubbleReactionsView systemLayoutSizeFittingSize:fittingSize].height + RoomBubbleCellLayout.reactionsViewTopMargin; [attributedString appendAttributedString:[RoomBubbleCellData verticalWhitespaceForHeight: height]]; } @@ -400,7 +393,7 @@ - (void)addVerticalWhitespaceToString:(NSMutableAttributedString *)attributedStr // Add vertical whitespace in case of read receipts. if (self.readReceipts[eventId].count) { - [attributedString appendAttributedString:[RoomBubbleCellData readReceiptVerticalWhitespace]]; + [attributedString appendAttributedString:[RoomBubbleCellData verticalWhitespaceForHeight:RoomBubbleCellLayout.readReceiptsViewHeight + RoomBubbleCellLayout.readReceiptsViewTopMargin]]; } } @@ -509,33 +502,21 @@ + (NSAttributedString *)timestampVerticalWhitespace return timestampVerticalWhitespace; } - -+ (NSAttributedString *)readReceiptVerticalWhitespace -{ - @synchronized(self) - { - if (readReceiptVerticalWhitespace == nil) - { - readReceiptVerticalWhitespace = [[NSAttributedString alloc] initWithString:@"\n\n" attributes:@{NSForegroundColorAttributeName : [UIColor blackColor], - NSFontAttributeName: [UIFont systemFontOfSize:4]}]; - } - } - return readReceiptVerticalWhitespace; -} - + (NSAttributedString *)verticalWhitespaceForHeight:(CGFloat)height { - NSUInteger returns = height / 6; + UIFont *sizingFont = [UIFont systemFontOfSize:2]; + CGFloat returnHeight = sizingFont.lineHeight; + + NSUInteger returns = (NSUInteger)round(height/returnHeight); NSMutableString *returnString = [NSMutableString string]; - + for (NSUInteger i = 0; i < returns; i++) { [returnString appendString:@"\n"]; } - - + return [[NSAttributedString alloc] initWithString:returnString attributes:@{NSForegroundColorAttributeName : [UIColor blackColor], - NSFontAttributeName: [UIFont systemFontOfSize:6]}]; + NSFontAttributeName: sizingFont}]; } - (BOOL)hasSameSenderAsBubbleCellData:(id)bubbleCellData From ee15eccb40917e08e6a29a42da57beb45e611f6b Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 12:25:57 +0200 Subject: [PATCH 124/266] RoomDataSource: Fix reactions and read receipts positions. --- .../Modules/Room/DataSources/RoomDataSource.m | 185 ++++++++++-------- 1 file changed, 99 insertions(+), 86 deletions(-) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 29cbe5cd80..141802bed9 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -28,8 +28,6 @@ #import "MXRoom+Riot.h" -static CGFloat kBubbleReactionsViewLeftMargin = 55.0; -static CGFloat kBubbleReactionsViewRightMargin = 15.0; @interface RoomDataSource() { @@ -274,17 +272,69 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N { // Read receipts container are inserted here on the right side into the content view. // Some vertical whitespaces are added in message text view (see RoomBubbleCellData class) to insert correctly multiple receipts. - NSInteger index = bubbleComponents.count; - CGFloat bottomPositionY = bubbleCell.frame.size.height; - while (index--) + + NSInteger index = 0; + + for (MXKRoomBubbleComponent *component in bubbleComponents) { - MXKRoomBubbleComponent *component = bubbleComponents[index]; NSString *componentEventId = component.event.eventId; if (component.event.sentState != MXEventSentStateFailed) { + CGFloat bottomPositionY; + + CGRect bubbleComponentFrame = [bubbleCell componentFrameInContentViewForIndex:index]; + + if (CGRectEqualToRect(bubbleComponentFrame, CGRectNull) == NO) + { + bottomPositionY = bubbleComponentFrame.origin.y + bubbleComponentFrame.size.height; + } + else + { + continue; + } + + MXAggregatedReactions* reactions = cellData.reactions[componentEventId]; + + BubbleReactionsView *reactionsView; + + if (reactions && !isCollapsableCellCollapsed) + { + BubbleReactionsViewModel *bubbleReactionsViewModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:reactions eventId:componentEventId]; + + reactionsView = [BubbleReactionsView new]; + reactionsView.viewModel = bubbleReactionsViewModel; + [reactionsView updateWithTheme:ThemeService.shared.theme]; + + bubbleReactionsViewModel.viewModelDelegate = self; + + reactionsView.translatesAutoresizingMaskIntoConstraints = NO; + [bubbleCell.contentView addSubview:reactionsView]; + + if (!bubbleCell.tmpSubviews) + { + bubbleCell.tmpSubviews = [NSMutableArray array]; + } + [bubbleCell.tmpSubviews addObject:reactionsView]; + + CGFloat leftMargin = RoomBubbleCellLayout.reactionsViewLeftMargin; + + if (self.room.summary.isEncrypted) + { + leftMargin+= RoomBubbleCellLayout.encryptedContentLeftMargin; + } + + // Force receipts container size + [NSLayoutConstraint activateConstraints: + @[ + [reactionsView.leadingAnchor constraintEqualToAnchor:reactionsView.superview.leadingAnchor constant:leftMargin], + [reactionsView.trailingAnchor constraintEqualToAnchor:reactionsView.superview.trailingAnchor constant:-RoomBubbleCellLayout.reactionsViewRightMargin], + [reactionsView.topAnchor constraintEqualToAnchor:reactionsView.superview.topAnchor constant:bottomPositionY + RoomBubbleCellLayout.reactionsViewTopMargin] + ]]; + } + MXKReceiptSendersContainer* avatarsContainer; - + // Handle read receipts (if any) if (self.showBubbleReceipts && cellData.readReceipts.count && !isCollapsableCellCollapsed) { @@ -315,7 +365,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N if (roomMembers.count) { // Define the read receipts container, positioned on the right border of the bubble cell (Note the right margin 6 pts). - avatarsContainer = [[MXKReceiptSendersContainer alloc] initWithFrame:CGRectMake(bubbleCell.frame.size.width - 156, bottomPositionY - 13, 150, 12) andMediaManager:self.mxSession.mediaManager]; + avatarsContainer = [[MXKReceiptSendersContainer alloc] initWithFrame:CGRectMake(bubbleCell.frame.size.width - RoomBubbleCellLayout.readReceiptsViewWidth + RoomBubbleCellLayout.readReceiptsViewRightMargin, bottomPositionY + RoomBubbleCellLayout.readReceiptsViewTopMargin, RoomBubbleCellLayout.readReceiptsViewWidth, RoomBubbleCellLayout.readReceiptsViewHeight) andMediaManager:self.mxSession.mediaManager]; // Custom avatar display avatarsContainer.maxDisplayedAvatars = 5; @@ -355,14 +405,14 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 - constant:150]; + constant:RoomBubbleCellLayout.readReceiptsViewWidth]; NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 - constant:12]; + constant:RoomBubbleCellLayout.readReceiptsViewHeight]; // Force receipts container position NSLayoutConstraint *trailingConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer @@ -371,61 +421,25 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N toItem:avatarsContainer.superview attribute:NSLayoutAttributeTrailing multiplier:1.0 - constant:-6]; - NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:avatarsContainer - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:avatarsContainer.superview - attribute:NSLayoutAttributeTop - multiplier:1.0 - constant:bottomPositionY - 13]; + constant:-RoomBubbleCellLayout.readReceiptsViewRightMargin]; + + // At the bottom, we have reactions or nothing + NSLayoutConstraint *topConstraint; + if (reactionsView) + { + topConstraint = [avatarsContainer.topAnchor constraintEqualToAnchor:reactionsView.bottomAnchor constant:RoomBubbleCellLayout.readReceiptsViewTopMargin]; + } + else + { + topConstraint = [avatarsContainer.topAnchor constraintEqualToAnchor:avatarsContainer.superview.topAnchor constant:bottomPositionY + RoomBubbleCellLayout.readReceiptsViewTopMargin]; + } + // Available on iOS 8 and later [NSLayoutConstraint activateConstraints:@[widthConstraint, heightConstraint, topConstraint, trailingConstraint]]; } } - MXAggregatedReactions* reactions = cellData.reactions[componentEventId]; - - if (reactions && !isCollapsableCellCollapsed) - { - BubbleReactionsViewModel *bubbleReactionsViewModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:reactions eventId:componentEventId]; - - BubbleReactionsView *reactionsView = [BubbleReactionsView new]; - reactionsView.viewModel = bubbleReactionsViewModel; - [reactionsView updateWithTheme:ThemeService.shared.theme]; - - bubbleReactionsViewModel.viewModelDelegate = self; - - reactionsView.translatesAutoresizingMaskIntoConstraints = NO; - [bubbleCell.contentView addSubview:reactionsView]; - - if (!bubbleCell.tmpSubviews) - { - bubbleCell.tmpSubviews = [NSMutableArray array]; - } - [bubbleCell.tmpSubviews addObject:reactionsView]; - - // At the bottom, we have read receipts or nothing - NSLayoutConstraint *bottomConstraint; - if (avatarsContainer) - { - bottomConstraint = [reactionsView.bottomAnchor constraintEqualToAnchor:avatarsContainer.topAnchor]; - } - else - { - bottomConstraint = [reactionsView.bottomAnchor constraintEqualToAnchor:reactionsView.superview.topAnchor constant:bottomPositionY]; - } - - // Force receipts container size - [NSLayoutConstraint activateConstraints: - @[ - [reactionsView.leadingAnchor constraintEqualToAnchor:reactionsView.superview.leadingAnchor constant:kBubbleReactionsViewLeftMargin], - [reactionsView.trailingAnchor constraintEqualToAnchor:reactionsView.superview.trailingAnchor constant:-kBubbleReactionsViewRightMargin], - bottomConstraint - ]]; - } - // Check whether the read marker must be displayed here. if (self.showReadMarker) { @@ -438,7 +452,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N if ([componentEventId isEqualToString:self.room.accountData.readMarkerEventId]) { - bubbleCell.readMarkerView = [[UIView alloc] initWithFrame:CGRectMake(0, bottomPositionY - 2, bubbleCell.bubbleOverlayContainer.frame.size.width, 2)]; + bubbleCell.readMarkerView = [[UIView alloc] initWithFrame:CGRectMake(0, bottomPositionY - RoomBubbleCellLayout.readMarkerViewHeight, bubbleCell.bubbleOverlayContainer.frame.size.width, RoomBubbleCellLayout.readMarkerViewHeight)]; bubbleCell.readMarkerView.backgroundColor = ThemeService.shared.theme.tintColor; // Hide by default the marker, it will be shown and animated when the cell will be rendered. bubbleCell.readMarkerView.hidden = YES; @@ -450,42 +464,41 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N // Force read marker constraints bubbleCell.readMarkerViewTopConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.readMarkerView - attribute:NSLayoutAttributeTop - relatedBy:NSLayoutRelationEqual - toItem:bubbleCell.bubbleOverlayContainer - attribute:NSLayoutAttributeTop - multiplier:1.0 - constant:bottomPositionY - 2]; - bubbleCell.readMarkerViewLeadingConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.readMarkerView - attribute:NSLayoutAttributeLeading - relatedBy:NSLayoutRelationEqual - toItem:bubbleCell.bubbleOverlayContainer - attribute:NSLayoutAttributeLeading - multiplier:1.0 - constant:0]; - bubbleCell.readMarkerViewTrailingConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.bubbleOverlayContainer - attribute:NSLayoutAttributeTrailing + attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual - toItem:bubbleCell.readMarkerView - attribute:NSLayoutAttributeTrailing + toItem:bubbleCell.bubbleOverlayContainer + attribute:NSLayoutAttributeTop multiplier:1.0 - constant:0]; + constant:bottomPositionY - RoomBubbleCellLayout.readMarkerViewHeight]; + bubbleCell.readMarkerViewLeadingConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.readMarkerView + attribute:NSLayoutAttributeLeading + relatedBy:NSLayoutRelationEqual + toItem:bubbleCell.bubbleOverlayContainer + attribute:NSLayoutAttributeLeading + multiplier:1.0 + constant:0]; + bubbleCell.readMarkerViewTrailingConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.bubbleOverlayContainer + attribute:NSLayoutAttributeTrailing + relatedBy:NSLayoutRelationEqual + toItem:bubbleCell.readMarkerView + attribute:NSLayoutAttributeTrailing + multiplier:1.0 + constant:0]; bubbleCell.readMarkerViewHeightConstraint = [NSLayoutConstraint constraintWithItem:bubbleCell.readMarkerView - attribute:NSLayoutAttributeHeight - relatedBy:NSLayoutRelationEqual - toItem:nil - attribute:NSLayoutAttributeNotAnAttribute - multiplier:1.0 - constant:2]; + attribute:NSLayoutAttributeHeight + relatedBy:NSLayoutRelationEqual + toItem:nil + attribute:NSLayoutAttributeNotAnAttribute + multiplier:1.0 + constant:RoomBubbleCellLayout.readMarkerViewHeight]; [NSLayoutConstraint activateConstraints:@[bubbleCell.readMarkerViewTopConstraint, bubbleCell.readMarkerViewLeadingConstraint, bubbleCell.readMarkerViewTrailingConstraint, bubbleCell.readMarkerViewHeightConstraint]]; } } } - // Prepare the bottom position for the next read receipt container (if any) - bottomPositionY = bubbleCell.msgTextViewTopConstraint.constant + component.position.y; + index++; } } From 6e90168a1c5d05ce0ec4af33a6bde4a50bb13504 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 12:26:48 +0200 Subject: [PATCH 125/266] RoomViewController: Use new component frame method calculation. --- Riot/Modules/Room/RoomViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index f8f7265722..048000f751 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5117,7 +5117,7 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce if (bubbleComponents.count > 0) { NSInteger selectedComponentIndex = foundComponentIndex != NSNotFound ? foundComponentIndex : 0; - bubbleComponentFrame = [roomBubbleTableViewCell componentFrameForIndex:selectedComponentIndex]; + bubbleComponentFrame = [roomBubbleTableViewCell componentFrameInTableViewForIndex:selectedComponentIndex]; } else { From 599bab3967801529cf7961d9d0a02262d192581c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 12:59:46 +0200 Subject: [PATCH 126/266] MXKRoomBubbleTableViewCell: Display timestamp on left when select memberships. --- Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 6dd46f4312..dc8636d9a7 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -58,7 +58,9 @@ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex } // Display timestamp on the left for selected component when it cannot overlap other UI elements like user's avatar - BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible && !isFirstDisplayedComponent && !isLastMessageMostRecentComponent; + BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible + && !isLastMessageMostRecentComponent + && ((isFirstDisplayedComponent && roomBubbleCellData.shouldHideSenderInformation) || !isFirstDisplayedComponent); [self addTimestampLabelForComponentIndex:componentIndex isFirstDisplayedComponent:isFirstDisplayedComponent From c81c3fb9e8113bc4e81c1ea6c4fedb97b3038e6c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 13:01:25 +0200 Subject: [PATCH 127/266] Update changes --- CHANGES.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d615617052..fb713e5cd6 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,11 +9,10 @@ Improvements: * Reactions Menu: Fix position (#2447). * Context menu polish (#2466). -Bug fix: - * Registration with an email is broken (#2417). - Bug fix: * Device Verification: Fix user display name and device id colors in dark theme + * Registration with an email is broken (#2417). + * Reactions: Bad position (#2462). Changes in 0.8.6 (2019-05-06) =============================================== From bd78e32cc702958bbc27a124c4e916cbd34919c9 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 15:16:38 +0200 Subject: [PATCH 128/266] Update Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h Co-Authored-By: giomfo --- Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h index 823d31924d..9de1b0e88c 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h @@ -94,7 +94,7 @@ extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer; Calculate component frame in tableview cell contentView. @param componentIndex index of the component in bubble message data - @return component frame in contentView if component exist or CGRectNull. + @return component frame in the contentView if the component exists or CGRectNull. */ - (CGRect)componentFrameInContentViewForIndex:(NSInteger)componentIndex; From dc61c7dee1916bcb3dcb486d49614a24d791b9cb Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 15:17:20 +0200 Subject: [PATCH 129/266] Update Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h Co-Authored-By: giomfo --- Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h index 9de1b0e88c..e57ee45c55 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h @@ -91,7 +91,7 @@ extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer; - (CGRect)componentFrameInTableViewForIndex:(NSInteger)componentIndex; /** - Calculate component frame in tableview cell contentView. + Calculate the component frame in the contentView of the tableview cell. @param componentIndex index of the component in bubble message data @return component frame in the contentView if the component exists or CGRectNull. From 4e9cb9d52f75ece3cfe191a02c9e6e6f0d8b12ad Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 29 May 2019 15:22:42 +0200 Subject: [PATCH 130/266] Update Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m Co-Authored-By: giomfo --- Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index dc8636d9a7..684f793a4c 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -60,7 +60,7 @@ - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex // Display timestamp on the left for selected component when it cannot overlap other UI elements like user's avatar BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible && !isLastMessageMostRecentComponent - && ((isFirstDisplayedComponent && roomBubbleCellData.shouldHideSenderInformation) || !isFirstDisplayedComponent); + && ( !isFirstDisplayedComponent || roomBubbleCellData.shouldHideSenderInformation); [self addTimestampLabelForComponentIndex:componentIndex isFirstDisplayedComponent:isFirstDisplayedComponent From 6c93b124a27a8ac1bac8658a5bd0325eadf80cdf Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Thu, 30 May 2019 21:07:00 +0000 Subject: [PATCH 131/266] Translated using Weblate (Dutch) Currently translated at 100.0% (710 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/nl/ --- Riot/Assets/nl.lproj/Vector.strings | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index 1cf3cde2b6..d83b3feb91 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -123,7 +123,7 @@ "room_recents_create_empty_room" = "Gesprek aanmaken"; "room_recents_join_room" = "Gesprek toetreden"; "room_recents_join_room_title" = "Neem aan een gesprek deel"; -"room_recents_join_room_prompt" = "Voer een gespreks-ID of -alias in"; +"room_recents_join_room_prompt" = "Voer een gespreks(bij)naam in"; // People tab "people_invites_section" = "UITNODIGINGEN"; "people_conversation_section" = "GESPREKKEN"; @@ -354,8 +354,8 @@ "room_details_no_local_addresses" = "Dit gesprek heeft geen lokale adressen"; "room_details_new_address" = "Nieuw adres toevoegen"; "room_details_new_address_placeholder" = "Nieuw adres toevoegen (bv. #foo%@)"; -"room_details_addresses_invalid_address_prompt_title" = "Ongeldig aliasformaat"; -"room_details_addresses_invalid_address_prompt_msg" = "%@ is geen geldig formaat voor een alias"; +"room_details_addresses_invalid_address_prompt_title" = "Ongeldig bijnaamformaat"; +"room_details_addresses_invalid_address_prompt_msg" = "%@ is geen geldig formaat voor een bijnaam"; "room_details_addresses_disable_main_address_prompt_title" = "Hoofdadreswaarschuwing"; "room_details_addresses_disable_main_address_prompt_msg" = "U heeft geen hoofdadres opgegeven. Het standaardhoofdadres voor dit gesprek zal willekeurig gekozen worden"; "room_details_banned_users_section" = "Verbannen gebruikers"; From 1728764c480fe9e5498bc61f5621122d11d99b7a Mon Sep 17 00:00:00 2001 From: Szimszon Date: Fri, 31 May 2019 17:57:57 +0000 Subject: [PATCH 132/266] Translated using Weblate (Hungarian) Currently translated at 100.0% (710 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 105 +++++++++++++++++++++++++++- 1 file changed, 103 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index b4ea620de0..2b24813f18 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -568,7 +568,7 @@ "settings_key_backup_info_version" = "Kulcs mentés verzió: %@"; "settings_key_backup_info_algorithm" = "Algoritmus: %@"; "settings_key_backup_info_valid" = "Ez az eszköz elmenti a kulcsaidat."; -"settings_key_backup_info_not_valid" = "Ez az eszköz nem menti el a kulcsaidat."; +"settings_key_backup_info_not_valid" = "Ez az eszköz nem menti el a kulcsaidat, de van létező mentésed ahonnan vissza tudsz állni és továbbléphetsz."; "settings_key_backup_info_progress" = "%@ kulcsok mentése..."; "settings_key_backup_info_progress_done" = "Minden kulcs elmentve"; "settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "A Biztonságos Üzenet Visszaállítás használatához ellenőrizd ezt: %@."; @@ -630,7 +630,7 @@ "key_backup_recover_banner_title_part1" = "Biztonságos Üzenet Visszaállítás futtatása"; "key_backup_recover_banner_title_part2" = " , hogy elolvashasd a titkosított üzeneteidet ezen az eszközön"; "settings_key_backup_info" = "Titkosított üzenetek végponttól-végpontig vannak titkosítva. Csak te és a címzettek rendelkeznek a visszafejtéshez szükséges kulcsokkal."; -"settings_key_backup_info_signout_warning" = "Mentsd el a kulcsaidat a kilépés előtt, hogy ne veszítsd el őket."; +"settings_key_backup_info_signout_warning" = "Állítsd be ezen az eszközön a Kulcs Mentést kijelentkezés előtt, hogy ne veszíts el olyan kulcsot ami csak ezen az eszközön van meg."; "settings_key_backup_button_use" = "Kulcs mentés használata"; "key_backup_setup_intro_setup_action_without_existing_backup" = "Kulcs mentés használata"; "key_backup_setup_intro_setup_action_with_existing_backup" = "Használd a Kulcs mentést"; @@ -680,3 +680,104 @@ "room_message_unable_open_link_error_message" = "A linket nem lehet megnyitni."; "store_full_description" = "Beszélgess, ahogy tetszik.\n\nA csevegő alkalmazás ami személyre szabható és az irányításod alatt marad. Riot megteremti a lehetőséget, hogy úgy beszélgess ahogy szeretnél. A [matrix] hálózathoz tervezve - ami egy nyílt és elosztott hálózat.\n\nKészíts egy ingyenes matrix.org fiókot vagy igényelj egy saját szervert a https://modular.im -től, de üzemeltethetsz is saját Matrix szervert.\n\nMiért válaszd a Riot.im-et?\n\nTELJES KOMMUNIKÁCIÓ: Nyiss szobákat a csoportod, barátaid, közösséged vagy bárkiknek akiknek szeretnél! Beszélgess, ossz meg fájlokat, adj hozzá kisalkalmazásokat és indíts hang és videóhívásokat - teljesen ingyen.\n\nERŐS KAPCSOLATOK: Használd a Riot-ot a kedvenc eszközeiddel. A Riottal még másik rendszerekben lévő emberekkel és csoportokkal is képes lehetsz beszélgetni.\n\nSZEMÉLYES ÉS BIZTONSÁGOS: Tartsd a beszélgetéseidet titokban. A végponttól-végpontig titkosítás biztosítja, hogy a személyes beszélgetések személyesek maradnak.\n\nNYÍLT ÉS NEM ZÁRT: Nyílt forrású és a Matrix-hoz készült. Az adataid maradjanak a birtokodban a saját szerver üzemeltetésével vagy válassz olyan szervert amiben megbízol.\n\nMINDENHOL AMERRE JÁRSZ: Maradj kapcsolatban a többiekkel mindenhol az eszközeid közötti teljesen szinkronizált üzenetváltásokkal. Akár a https://riot.im -en."; "auth_autodiscover_invalid_response" = "Matrix szerver felderítésénél érvénytelen válasz érkezett"; +"room_event_action_reply" = "Válasz"; +"room_event_action_edit" = "Szerkeszt"; +"room_event_action_reaction_agree" = "Egyetért %@"; +"room_event_action_reaction_disagree" = "Ellentmond %@"; +"room_event_action_reaction_like" = "Kedveli %@"; +"room_event_action_reaction_dislike" = "Nem kedveli %@"; +"room_action_reply" = "Válasz"; +"settings_labs_message_reaction" = "Emoji reakció az üzenetre"; +"settings_key_backup_button_connect" = "Eszköz csatlakoztatása a Kulcs Mentéshez"; +"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Eszköz csatlakoztatása a Kulcs Mentéshez"; +"key_backup_recover_connent_banner_subtitle" = "Eszköz csatlakoztatása a Kulcs Mentéshez"; +// MARK: - Device Verification +"device_verification_title" = "Eszköz ellenőrzése"; +"device_verification_security_advice" = "A legnagyobb biztonság érdekében javasoljuk, hogy ezt személyesen vagy egy másik biztonságos kommunikációs csatornán tedd meg"; +"device_verification_cancelled" = "A másik fél megszakította az ellenőrzést."; +"device_verification_cancelled_by_me" = "Az ellenőrzés megszakadt. Ok: %@"; +"device_verification_error_cannot_load_device" = "Az eszköz információk nem tölthetők be."; +// Mark: Incoming +"device_verification_incoming_title" = "Beérkező Ellenőrzési Kérés"; +"device_verification_incoming_description_1" = "Eszköz ellenőrzése és beállítás megbízhatónak. A partnerek eszközeiben való megbízás megnyugtató lehet, ha végponttól végpontig titkosítást használsz."; +"device_verification_incoming_description_2" = "Az eszköz ellenőrzése megbízhatónak fogja jelezni az eszközt és a partnernél a te eszközödet szintén megbízhatónak fogja jelezni."; +// MARK: Start +"device_verification_start_title" = "Rövid szöveggel ellenőriz"; +"device_verification_start_wait_partner" = "Várakozás a partner általi elfogadásra..."; +"device_verification_start_use_legacy" = "Nem jelenik meg semmi? Nem minden kliens támogatja az interaktív ellenőrzést. Használd a hagyományos ellenőrzést."; +"device_verification_start_verify_button" = "Ellenőrzés megkezdése"; +"device_verification_start_use_legacy_action" = "Használd a hagyományos ellenőrzést"; +// MARK: Verify +"device_verification_verify_title_emoji" = "Eszköz ellenőrzése az alábbi emojik a partner képernyőjén való megjelenésének megerősítésével történik"; +"device_verification_verify_title_number" = "Eszköz ellenőrzése az alábbi számok a partner képernyőjén való megjelenésének megerősítésével történik"; +"device_verification_verify_wait_partner" = "Várakozás a partner megerősítésére..."; +// MARK: Verified +"device_verification_verified_title" = "Ellenőrizve!"; +"device_verification_verified_description_1" = "Ezt az eszközt sikeresen ellenőrizted."; +"device_verification_verified_description_2" = "Az biztonságos üzentküldés ezzel a felhasználóval végponttól végpontig titkosított és harmadik fél nem tudja elolvasni."; +"device_verification_verified_got_it_button" = "Értem"; +// MARK: Emoji +"device_verification_emoji_dog" = "Kutya"; +"device_verification_emoji_cat" = "Macska"; +"device_verification_emoji_lion" = "Oroszlán"; +"device_verification_emoji_horse" = "Ló"; +"device_verification_emoji_unicorn" = "Egyszarvú"; +"device_verification_emoji_pig" = "Disznó"; +"device_verification_emoji_elephant" = "Elefánt"; +"device_verification_emoji_rabbit" = "Nyúl"; +"device_verification_emoji_panda" = "Panda"; +"device_verification_emoji_rooster" = "Kakas"; +"device_verification_emoji_penguin" = "Pingvin"; +"device_verification_emoji_turtle" = "Teknős"; +"device_verification_emoji_fish" = "Hal"; +"device_verification_emoji_octopus" = "Polip"; +"device_verification_emoji_butterfly" = "Pillangó"; +"device_verification_emoji_flower" = "Virág"; +"device_verification_emoji_tree" = "Fa"; +"device_verification_emoji_cactus" = "Kaktusz"; +"device_verification_emoji_mushroom" = "Gomba"; +"device_verification_emoji_globe" = "Földgömb"; +"device_verification_emoji_moon" = "Hold"; +"device_verification_emoji_cloud" = "Felhő"; +"device_verification_emoji_fire" = "Tűz"; +"device_verification_emoji_banana" = "Banán"; +"device_verification_emoji_apple" = "Alma"; +"device_verification_emoji_strawberry" = "Eper"; +"device_verification_emoji_corn" = "Kukorica"; +"device_verification_emoji_pizza" = "Pizza"; +"device_verification_emoji_cake" = "Süti"; +"device_verification_emoji_heart" = "Szív"; +"device_verification_emoji_smiley" = "Mosoly"; +"device_verification_emoji_robot" = "Robot"; +"device_verification_emoji_hat" = "Kalap"; +"device_verification_emoji_glasses" = "Szemüveg"; +"device_verification_emoji_spanner" = "Csavarkulcs"; +"device_verification_emoji_santa" = "Télapó"; +"device_verification_emoji_thumbs up" = "Hüvelykujj fel"; +"device_verification_emoji_umbrella" = "Esernyő"; +"device_verification_emoji_hourglass" = "Homokóra"; +"device_verification_emoji_clock" = "Osztály"; +"device_verification_emoji_gift" = "Ajándék"; +"device_verification_emoji_light bulb" = "Égő"; +"device_verification_emoji_book" = "Könyv"; +"device_verification_emoji_pencil" = "Toll"; +"device_verification_emoji_paperclip" = "Gémkapocs"; +"device_verification_emoji_scissors" = "Olló"; +"device_verification_emoji_padlock" = "Lakat"; +"device_verification_emoji_key" = "Kulcs"; +"device_verification_emoji_hammer" = "Kalapács"; +"device_verification_emoji_telephone" = "Telefon"; +"device_verification_emoji_flag" = "Zászló"; +"device_verification_emoji_train" = "Vonat"; +"device_verification_emoji_bicycle" = "Kerékpár"; +"device_verification_emoji_aeroplane" = "Repülő"; +"device_verification_emoji_rocket" = "Rakáta"; +"device_verification_emoji_trophy" = "Kupa"; +"device_verification_emoji_ball" = "Labda"; +"device_verification_emoji_guitar" = "Gitár"; +"device_verification_emoji_trumpet" = "Trombita"; +"device_verification_emoji_bell" = "Harang"; +"device_verification_emoji_anchor" = "Vasmacska"; +"device_verification_emoji_headphones" = "Fejhallgató"; +"device_verification_emoji_folder" = "Mappa"; +"device_verification_emoji_pin" = "Rajszeg"; From 7d193277f7545e30d9399c4a2a92b6ffb424127c Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Mon, 3 Jun 2019 10:23:25 +0000 Subject: [PATCH 133/266] Added translation using Weblate (West Flemish) --- Riot/Assets/vls.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) create mode 100644 Riot/Assets/vls.lproj/Vector.strings diff --git a/Riot/Assets/vls.lproj/Vector.strings b/Riot/Assets/vls.lproj/Vector.strings new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/Riot/Assets/vls.lproj/Vector.strings @@ -0,0 +1 @@ + From e8b79b5207614ec889e469d4ff14ba79788527ac Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Mon, 3 Jun 2019 10:23:38 +0000 Subject: [PATCH 134/266] Added translation using Weblate (West Flemish) --- Riot/Assets/vls.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) create mode 100644 Riot/Assets/vls.lproj/InfoPlist.strings diff --git a/Riot/Assets/vls.lproj/InfoPlist.strings b/Riot/Assets/vls.lproj/InfoPlist.strings new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/Riot/Assets/vls.lproj/InfoPlist.strings @@ -0,0 +1 @@ + From b4b32bda461fa95bbf650c9512ee38132574c4da Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Mon, 3 Jun 2019 10:23:47 +0000 Subject: [PATCH 135/266] Added translation using Weblate (West Flemish) --- Riot/Assets/vls.lproj/Localizable.strings | 1 + 1 file changed, 1 insertion(+) create mode 100644 Riot/Assets/vls.lproj/Localizable.strings diff --git a/Riot/Assets/vls.lproj/Localizable.strings b/Riot/Assets/vls.lproj/Localizable.strings new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/Riot/Assets/vls.lproj/Localizable.strings @@ -0,0 +1 @@ + From b03b97eaa2fb5a3973d8d328eba678ec644e7961 Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Mon, 3 Jun 2019 11:22:54 +0000 Subject: [PATCH 136/266] Translated using Weblate (West Flemish) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/vls/ --- Riot/Assets/vls.lproj/Localizable.strings | 57 ++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/vls.lproj/Localizable.strings b/Riot/Assets/vls.lproj/Localizable.strings index 8b13789179..14a79cad6c 100644 --- a/Riot/Assets/vls.lproj/Localizable.strings +++ b/Riot/Assets/vls.lproj/Localizable.strings @@ -1 +1,56 @@ - +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ in %@"; +/* New message from a specific person, not referencing a room */ +"MSG_FROM_USER" = "%@ èt e bericht gesteurd"; +/* New message from a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM" = "%@ geplatst in %@"; +/* New message from a specific person, not referencing a room. Content included. */ +"MSG_FROM_USER_WITH_CONTENT" = "%@: %@"; +/* New message from a specific person in a named room. Content included. */ +"MSG_FROM_USER_IN_ROOM_WITH_CONTENT" = "%@ in %@: %@"; +/* New action message from a specific person, not referencing a room. */ +"ACTION_FROM_USER" = "* %@ %@"; +/* New action message from a specific person in a named room. */ +"ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@"; +/* New action message from a specific person, not referencing a room. */ +"IMAGE_FROM_USER" = "%@ èt e fotootje %@ gesteurd"; +/* New action message from a specific person in a named room. */ +"IMAGE_FROM_USER_IN_ROOM" = "%@ èt e fotootje %@ in %@ geplatst"; +/* A single unread message in a room */ +"SINGLE_UNREAD_IN_ROOM" = "J' èt e bericht ountvangn in %@"; +/* A single unread message */ +"SINGLE_UNREAD" = "J' èt e bericht ountvangn"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ èt e sticker gesteurd"; +/* Multiple unread messages in a room */ +"UNREAD_IN_ROOM" = "%@ nieuwe berichten in %@"; +/* Multiple unread messages from a specific person, not referencing a room */ +"MSGS_FROM_USER" = "%@ nieuwe berichten in %@"; +/* Multiple unread messages from two people */ +"MSGS_FROM_TWO_USERS" = "%@ nieuwe berichten van %@ en %@"; +/* Multiple unread messages from three people */ +"MSGS_FROM_THREE_USERS" = "%@ nieuwe berichten van %@, %@ en %@"; +/* Multiple unread messages from two plus people (ie. for 4+ people: 'others' replaces the third person) */ +"MSGS_FROM_TWO_PLUS_USERS" = "%@ nieuwe berichten van %@, %@ en anderen"; +/* Multiple messages in two rooms */ +"MSGS_IN_TWO_ROOMS" = "%@ nieuwe berichten in %@ en %@"; +/* Look, stuff's happened, alright? Just open the app. */ +"MSGS_IN_TWO_PLUS_ROOMS" = "%@ nieuwe berichten in %@, %@ en anderen"; +/* A user has invited you to a chat */ +"USER_INVITE_TO_CHAT" = "%@ èt joun voor e gesprek uutgenodigd"; +/* A user has invited you to an (unamed) group chat */ +"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@ èt joun in e groepsgesprek uutgenodigd"; +/* A user has invited you to a named room */ +"USER_INVITE_TO_NAMED_ROOM" = "%@ èt joun in %@ uutgenodigd"; +/* Incoming one-to-one voice call */ +"VOICE_CALL_FROM_USER" = "Iproep van %@"; +/* Incoming one-to-one video call */ +"VIDEO_CALL_FROM_USER" = "Video-iproep van %@"; +/* Incoming unnamed voice conference invite from a specific person */ +"VOICE_CONF_FROM_USER" = "Groepsiproep van %@"; +/* Incoming unnamed video conference invite from a specific person */ +"VIDEO_CONF_FROM_USER" = "Video-groepsiproep van %@"; +/* Incoming named voice conference invite from a specific person */ +"VOICE_CONF_NAMED_FROM_USER" = "Groepsiproep van %@: ‘%@’"; +/* Incoming named video conference invite from a specific person */ +"VIDEO_CONF_NAMED_FROM_USER" = "Video-groepsiproep van %@: ‘%@’"; From 5900d017f01453e874d1ec0cc0b5b38d5c269482 Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Mon, 3 Jun 2019 11:56:51 +0000 Subject: [PATCH 137/266] Translated using Weblate (West Flemish) Currently translated at 100.0% (5 of 5 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-dialogs/vls/ --- Riot/Assets/vls.lproj/InfoPlist.strings | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/vls.lproj/InfoPlist.strings b/Riot/Assets/vls.lproj/InfoPlist.strings index 8b13789179..8771d37a3d 100644 --- a/Riot/Assets/vls.lproj/InfoPlist.strings +++ b/Riot/Assets/vls.lproj/InfoPlist.strings @@ -1 +1,6 @@ - +// Permissions usage explanations +"NSCameraUsageDescription" = "De camera wor gebruukt vo fotootjes te trekkn en filmtjes te moakn, en vo videogesprekkn."; +"NSPhotoLibraryUsageDescription" = "De fotogalerie wor gebruukt vo fotootjes en filmtjes te versteurn."; +"NSMicrophoneUsageDescription" = "De microfoon wor gebruukt vo filmtjes te maken, en vo sproakiproepn."; +"NSContactsUsageDescription" = "Vo je te kunn toogn dewelkse van je contactn dat al Riot of Matrix gebruukn, kunn we d’e-mailadressn en telefongnumero’s in jen adresboek noa je Matrix-identiteitsserver steurn. New Vector bewoart deze gegevens nie en gebruukt ze ook nie voor andere doeleindn. Bekykt vo meer informoatie de privacybeleidspagina in d’instelliengn van den app."; +"NSCalendarsUsageDescription" = "Bekykt je geplande afsproakn in den app."; From 5c1d68ab87fbbbcc79b52dea2a84346a8465ae53 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Tue, 4 Jun 2019 17:19:18 +0000 Subject: [PATCH 138/266] Translated using Weblate (Bulgarian) Currently translated at 100.0% (710 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/bg/ --- Riot/Assets/bg.lproj/Vector.strings | 108 ++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/bg.lproj/Vector.strings b/Riot/Assets/bg.lproj/Vector.strings index c17f172d06..6dca9c4f50 100644 --- a/Riot/Assets/bg.lproj/Vector.strings +++ b/Riot/Assets/bg.lproj/Vector.strings @@ -76,14 +76,14 @@ "auth_msisdn_validation_title" = "Очакване на потвърждение"; "auth_msisdn_validation_message" = "Изпратихме Ви SMS с код за активиране. Моля, въведете този код по-долу."; "auth_msisdn_validation_error" = "Неуспешно потвърждение на телефонен номер."; -"auth_recaptcha_message" = "Този Home сървър би искал да се увери, че не сте робот"; +"auth_recaptcha_message" = "Този сървър би искал да се увери, че не сте робот"; "auth_reset_password_message" = "За да възстановите Вашата парола, въведете имейл адреса, свързан с профила Ви:"; "auth_reset_password_missing_email" = "Имейл адресът, свързан с профила Ви, трябва да бъде въведен."; "auth_reset_password_missing_password" = "Трябва да бъде въведена нова парола."; "auth_reset_password_email_validation_message" = "Имейл беше изпратен на %@. След като проследите връзката, която съдържа, натиснете по-долу."; "auth_reset_password_next_step_button" = "Потвърдих имейл адреса си"; "auth_reset_password_error_unauthorized" = "Неуспешно потвърждаване на имейл адреса: уверете се, че сте кликнали върху връзката в имейла"; -"auth_reset_password_error_not_found" = "Изглежда вашият имейл адрес не може да се асоциира с Matrix ID на този Home сървър."; +"auth_reset_password_error_not_found" = "Изглежда имейл адресът Ви не принадлежи на Matrix потребител от този сървър."; "auth_reset_password_success_message" = "Вашата парола беше възстановена.\n\nВие сте излязли от профила си от всички устройства и вече няма да получавате известия. За да включите известията отново, влезте в профила си от всички устройства."; "auth_add_email_and_phone_warning" = "Регистрация с имейл и телефонен номер наведнъж не се поддържа в момента. Само телефонен номер се взима под внимание. Можете да добавите имейл към профила си в настройките."; // Chat creation @@ -562,7 +562,7 @@ "settings_key_backup_info_version" = "Версия на резервното копие на ключовете: %@"; "settings_key_backup_info_algorithm" = "Алгоритъм: %@"; "settings_key_backup_info_valid" = "Това устройство прави резервно копие на ключовете Ви."; -"settings_key_backup_info_not_valid" = "Това устройство не прави резервно копие на ключовете Ви."; +"settings_key_backup_info_not_valid" = "Това устройство не прави резервно копие на ключовете Ви, но имате съществуващо резервно копие, от което да възстановявате или допълвате в бъдеще."; "settings_key_backup_info_progress" = "Правене на резервно копие на %@ ключа..."; "settings_key_backup_info_progress_done" = "Има резервно копие на всички ключове"; "settings_key_backup_info_not_trusted_from_verifiable_device_fix_action" = "За да използвате Възстановяване на Защитени Съобщения на това устройство, потвърдете %@ сега."; @@ -624,7 +624,7 @@ "key_backup_recover_banner_title_part1" = "Стартирайте Възстановяване на Защитени Съобщения"; "key_backup_recover_banner_title_part2" = " за да четете шифрованата история на съобщенията на това устройство"; "settings_key_backup_info" = "Шифрованите съобщения са защитени с шифроване от край до край. Само Вие и получателят (получателите) имате ключове за прочитането им."; -"settings_key_backup_info_signout_warning" = "Направете копие на ключовете преди да излезете от профила, за да не ги загубите."; +"settings_key_backup_info_signout_warning" = "Свържете това устройство с резервно копие за ключове преди да излезете от профила, за да не изгубите ключовете намиращи се само на устройството."; "settings_key_backup_button_use" = "Използвай резервно копие на ключовете"; // Key backup wrong version "e2e_key_backup_wrong_version_title" = "Ново резервно копие на ключовете"; @@ -675,3 +675,103 @@ "room_message_unable_open_link_error_message" = "Неуспешно отваряне на връзката."; "room_event_action_reply" = "Отговори"; "room_event_action_edit" = "Редактирай"; +"auth_login_single_sign_on" = "Влез със SSO"; +"room_event_action_reaction_agree" = "Съгласи се с %@"; +"room_event_action_reaction_disagree" = "Противоречи на %@"; +"room_event_action_reaction_like" = "Харесай %@"; +"room_event_action_reaction_dislike" = "Не харесай %@"; +"room_action_reply" = "Отговори"; +"settings_labs_message_reaction" = "Реагирай на съобщения с емоджи"; +"settings_key_backup_button_connect" = "Свържи устройството към резервно копие на ключове"; +"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Свържи устройството към резервно копие на ключове"; +"key_backup_recover_connent_banner_subtitle" = "Свържи устройството към резервно копие на ключове"; +// MARK: - Device Verification +"device_verification_title" = "Потвърждение на устройство"; +"device_verification_security_advice" = "За максимална сигурност, препоръчваме да правите това на живо или използвайки друг защитен начин за комуникация"; +"device_verification_cancelled" = "Отсрещната страна отказа потвърждението."; +"device_verification_cancelled_by_me" = "Потвърждението беше отказано. Причина: %@"; +"device_verification_error_cannot_load_device" = "Неуспешно зареждане на информация за устройството."; +// Mark: Incoming +"device_verification_incoming_title" = "Входяща заявка за потвърждение"; +"device_verification_incoming_description_1" = "Потвърдете това устройство за да го маркирате като доверено. Доверяването на устройства на партньори Ви дава допълнително спокойствие когато използвате шифроване от-край-до-край."; +"device_verification_incoming_description_2" = "Потвърждаването на устройството ще го маркира като доверено и ще маркира Вашето като доверено при партньора."; +// MARK: Start +"device_verification_start_title" = "Потвърждение чрез сравняване на кратък текст"; +"device_verification_start_wait_partner" = "Изчакване на партньора да приеме..."; +"device_verification_start_use_legacy" = "Не се показва нищо? Засега не всички клиенти поддържат интерактивно потвърждение. Използвайте стария метод за потвърждение."; +"device_verification_start_verify_button" = "Започни потвърждение"; +"device_verification_start_use_legacy_action" = "Потвърди по стария метод"; +// MARK: Verify +"device_verification_verify_title_emoji" = "Потвърдете това устройство като се уверите че следните емоджита се повяват на екрана на партньора"; +"device_verification_verify_title_number" = "Потвърдете това устройство като се уверите че следните числа се повяват на екрана на партньора"; +"device_verification_verify_wait_partner" = "Изчакване на потвърждение от партньора..."; +// MARK: Verified +"device_verification_verified_title" = "Потвърдено!"; +"device_verification_verified_description_1" = "Успешно потвърдихте това устройство."; +"device_verification_verified_description_2" = "Защитените съобщения с този потребител са шифровани от край до край и не могат да бъдат прочетени от трети лица."; +"device_verification_verified_got_it_button" = "Разбрах"; +// MARK: Emoji +"device_verification_emoji_dog" = "Куче"; +"device_verification_emoji_cat" = "Котка"; +"device_verification_emoji_lion" = "Лъв"; +"device_verification_emoji_horse" = "Кон"; +"device_verification_emoji_unicorn" = "Еднорог"; +"device_verification_emoji_pig" = "Прасее"; +"device_verification_emoji_elephant" = "Слон"; +"device_verification_emoji_rabbit" = "Заек"; +"device_verification_emoji_panda" = "Панда"; +"device_verification_emoji_rooster" = "Петел"; +"device_verification_emoji_penguin" = "Пингвин"; +"device_verification_emoji_turtle" = "Костенурка"; +"device_verification_emoji_fish" = "Риба"; +"device_verification_emoji_octopus" = "Октопод"; +"device_verification_emoji_butterfly" = "Пеперуда"; +"device_verification_emoji_flower" = "Цвете"; +"device_verification_emoji_tree" = "Дърво"; +"device_verification_emoji_cactus" = "Кактус"; +"device_verification_emoji_mushroom" = "Гъба"; +"device_verification_emoji_globe" = "Глобус"; +"device_verification_emoji_moon" = "Луна"; +"device_verification_emoji_cloud" = "Облак"; +"device_verification_emoji_fire" = "Огън"; +"device_verification_emoji_banana" = "Банан"; +"device_verification_emoji_apple" = "Ябълка"; +"device_verification_emoji_strawberry" = "Ягода"; +"device_verification_emoji_corn" = "Царевица"; +"device_verification_emoji_pizza" = "Пица"; +"device_verification_emoji_cake" = "Торта"; +"device_verification_emoji_heart" = "Сърце"; +"device_verification_emoji_smiley" = "Усмивка"; +"device_verification_emoji_robot" = "Робот"; +"device_verification_emoji_hat" = "Шапка"; +"device_verification_emoji_glasses" = "Очила"; +"device_verification_emoji_spanner" = "Гаечен ключ"; +"device_verification_emoji_santa" = "Дядо Коледа"; +"device_verification_emoji_thumbs up" = "Палец нагоре"; +"device_verification_emoji_umbrella" = "Чадър"; +"device_verification_emoji_hourglass" = "Пясъчен часовник"; +"device_verification_emoji_clock" = "Часовник"; +"device_verification_emoji_gift" = "Подарък"; +"device_verification_emoji_light bulb" = "Лампа"; +"device_verification_emoji_book" = "Книга"; +"device_verification_emoji_pencil" = "Молив"; +"device_verification_emoji_paperclip" = "Кламер"; +"device_verification_emoji_scissors" = "Ножици"; +"device_verification_emoji_padlock" = "Катинар"; +"device_verification_emoji_key" = "Ключ"; +"device_verification_emoji_hammer" = "Чук"; +"device_verification_emoji_telephone" = "Телефон"; +"device_verification_emoji_flag" = "Знаме"; +"device_verification_emoji_train" = "Влак"; +"device_verification_emoji_bicycle" = "Колело"; +"device_verification_emoji_aeroplane" = "Самолет"; +"device_verification_emoji_rocket" = "Ракета"; +"device_verification_emoji_trophy" = "Трофей"; +"device_verification_emoji_ball" = "Топка"; +"device_verification_emoji_guitar" = "Китара"; +"device_verification_emoji_trumpet" = "Тромпет"; +"device_verification_emoji_bell" = "Звънец"; +"device_verification_emoji_anchor" = "Котва"; +"device_verification_emoji_headphones" = "Слушалки"; +"device_verification_emoji_folder" = "Папка"; +"device_verification_emoji_pin" = "Карфица"; From 940680ed757ae817d79fee39e0879bc02c8fdd96 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 6 Jun 2019 11:29:28 +0200 Subject: [PATCH 139/266] [ReactionsMenuViewModel] Do not perform directly reaction requests and use delegation. --- .../ReactionsMenuViewModel.swift | 59 +++++-------------- .../ReactionsMenuViewModelType.swift | 7 +-- .../RoomContextualMenuPresenter.swift | 6 +- .../RoomContextualMenuViewController.swift | 17 ------ 4 files changed, 19 insertions(+), 70 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 8fbf061065..d204d51e96 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -16,7 +16,7 @@ import UIKit -final class ReactionsMenuViewModel: ReactionsMenuViewModelType { +@objc final class ReactionsMenuViewModel: NSObject, ReactionsMenuViewModelType { // MARK: - Properties @@ -33,14 +33,16 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { private(set) var isDislikeButtonSelected: Bool = false weak var viewDelegate: ReactionsMenuViewModelDelegate? - weak var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? + @objc weak var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? // MARK: - Setup - init(aggregations: MXAggregations, roomId: String, eventId: String) { + @objc init(aggregations: MXAggregations, roomId: String, eventId: String) { self.aggregations = aggregations self.roomId = roomId self.eventId = eventId + + super.init() self.loadData() self.listenToDataUpdate() @@ -72,7 +74,7 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { return } - self.react(withReaction: theReaction, selected: theNewState, delegate: self.coordinatorDelegate) + self.react(withReaction: theReaction, selected: theNewState) } // MARK: - Private @@ -122,54 +124,21 @@ final class ReactionsMenuViewModel: ReactionsMenuViewModelType { } } } - - private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool, delegate: ReactionsMenuViewModelCoordinatorDelegate? = nil) { - + + private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool) { + // If required, unreact first if selected { self.ensure3StateButtons(withReaction: reaction) } - + + let reactionString = reaction.rawValue + if selected { - self.aggregations.sendReaction(reaction.rawValue, toEvent: self.eventId, inRoom: self.roomId, success: {[weak self] _ in - - guard let sself = self else { - return - } - - delegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: true) - - }, failure: {[weak self] (error) in - print("[ReactionsMenuViewModel] react: Error: \(error)") - - guard let sself = self else { - return - } - - delegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: true) - }) + self.coordinatorDelegate?.reactionsMenuViewModel(self, didAddReaction: reactionString, forEventId: self.eventId) } else { - - self.aggregations.unReact(onReaction: reaction.rawValue, toEvent: self.eventId, inRoom: self.roomId, success: {[weak self] in - - guard let sself = self else { - return - } - - delegate?.reactionsMenuViewModel(sself, didReactionComplete: reaction.rawValue, isAddReaction: false) - - }, failure: {[weak self] (error) in - print("[ReactionsMenuViewModel] react: Error: \(error)") - - guard let sself = self else { - return - } - - delegate?.reactionsMenuViewModel(sself, didReactionFailedWithError: error, reaction: reaction.rawValue, isAddReaction: false) - }) + self.coordinatorDelegate?.reactionsMenuViewModel(self, didRemoveReaction: reactionString, forEventId: self.eventId) } - - delegate?.reactionsMenuViewModel(self, didSendReaction: reaction.rawValue, isAddReaction: !selected) } // We can like, dislike, be indifferent but we cannot like & dislike at the same time diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift index 777376a4a1..111c01d03f 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift @@ -20,10 +20,9 @@ protocol ReactionsMenuViewModelDelegate: class { func reactionsMenuViewModelDidUpdate(_ viewModel: ReactionsMenuViewModelType) } -protocol ReactionsMenuViewModelCoordinatorDelegate: class { - func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didSendReaction reaction: String, isAddReaction: Bool) - func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didReactionComplete reaction: String, isAddReaction: Bool) - func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didReactionFailedWithError error: Error, reaction: String, isAddReaction: Bool) +@objc protocol ReactionsMenuViewModelCoordinatorDelegate: class { + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didAddReaction reaction: String, forEventId eventId: String) + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didRemoveReaction reaction: String, forEventId eventId: String) } diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift index fa56d2de56..5e0fbbc7e2 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift @@ -106,10 +106,8 @@ final class RoomContextualMenuPresenter: NSObject { animationCompletionInstructions() } } - - func showReactionsMenu(forEvent eventId: String, inRoom roomId: String, session: MXSession, - aroundFrame frame: CGRect) { - let reactionsMenuViewModel = ReactionsMenuViewModel(aggregations: session.aggregations, roomId: roomId, eventId: eventId) + + func showReactionsMenu(reactionsMenuViewModel: ReactionsMenuViewModel, aroundFrame frame: CGRect) { self.roomContextualMenuViewController?.showReactionsMenu(withViewModel: reactionsMenuViewModel, aroundFrame: frame) } } diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift index 2318a0bb4f..d4d9284006 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift @@ -99,7 +99,6 @@ final class RoomContextualMenuViewController: UIViewController, Themable { func showReactionsMenu(withViewModel viewModel: ReactionsMenuViewModel, aroundFrame frame: CGRect) { self.reactionsMenuView.viewModel = viewModel - self.reactionsMenuView.viewModel?.coordinatorDelegate = self self.reactionsMenuView.isHidden = false let menuHeight = self.reactionsMenuViewHeightConstraint.constant @@ -151,22 +150,6 @@ final class RoomContextualMenuViewController: UIViewController, Themable { } } -// MARK: - ReactionsMenuViewModelCoordinatorDelegate -extension RoomContextualMenuViewController: ReactionsMenuViewModelCoordinatorDelegate { - - func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didSendReaction reaction: String, isAddReaction: Bool) { - self.delegate?.roomContextualMenuViewControllerDidReaction(self) - } - - func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didReactionComplete reaction: String, isAddReaction: Bool) { - } - - func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModelType, didReactionFailedWithError error: Error, reaction: String, isAddReaction: Bool) { - self.errorPresenter?.presentError(from: self, forError: error, animated: true) { - } - } -} - // MARK: - UIGestureRecognizerDelegate extension RoomContextualMenuViewController: UIGestureRecognizerDelegate { From 6f7400840de301e3add3cd56e7d29e8bfe8260dd Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 6 Jun 2019 11:30:57 +0200 Subject: [PATCH 140/266] RoomViewController: Conforms to ReactionsMenuViewModelCoordinatorDelegate. --- .../Modules/Room/DataSources/RoomDataSource.m | 12 +++---- Riot/Modules/Room/RoomViewController.m | 33 +++++++++++++++++-- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 141802bed9..0696c85438 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -594,19 +594,19 @@ - (Widget *)jitsiWidget - (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didAddReaction:(MXReactionCount *)reactionCount forEventId:(NSString *)eventId { - [self.mxSession.aggregations sendReaction:reactionCount.reaction toEvent:eventId inRoom:self.roomId success:^(NSString * _Nonnull eventId) { + [self addReaction:reactionCount.reaction forEventId:eventId success:^(NSString *eventId) { + + } failure:^(NSError *error) { - } failure:^(NSError * _Nonnull error) { - NSLog(@"[MXKRoomDataSource] Fail to send reaction on eventId: %@", eventId); }]; } - (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didRemoveReaction:(MXReactionCount * _Nonnull)reactionCount forEventId:(NSString * _Nonnull)eventId { - [self.mxSession.aggregations unReactOnReaction:reactionCount.reaction toEvent:eventId inRoom:self.roomId success:^{ + [self removeReaction:reactionCount.reaction forEventId:eventId success:^{ + + } failure:^(NSError *error) { - } failure:^(NSError * _Nonnull error) { - NSLog(@"[MXKRoomDataSource] Fail to unreact on eventId: %@", eventId); }]; } diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 048000f751..a9439cea87 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -123,7 +123,8 @@ #import "Riot-Swift.h" -@interface RoomViewController () +@interface RoomViewController () { // The expanded header ExpandedRoomTitleView *expandedHeader; @@ -5124,9 +5125,15 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce bubbleComponentFrame = roomBubbleTableViewCell.frame; } - CGRect bubbleComponentFrameInOverlayView = [self.bubblesTableView convertRect:bubbleComponentFrame toView:self.overlayContainerView]; + CGRect bubbleComponentFrameInOverlayView = [self.bubblesTableView convertRect:bubbleComponentFrame toView:self.overlayContainerView]; - [self.roomContextualMenuPresenter showReactionsMenuForEvent:event.eventId inRoom:event.roomId session:self.mainSession aroundFrame:bubbleComponentFrameInOverlayView]; + NSString *roomId = self.roomDataSource.roomId; + MXAggregations *aggregations = self.mainSession.aggregations; + + ReactionsMenuViewModel *reactionsMenuViewModel = [[ReactionsMenuViewModel alloc] initWithAggregations:aggregations roomId:roomId eventId:event.eventId]; + reactionsMenuViewModel.coordinatorDelegate = self; + + [self.roomContextualMenuPresenter showReactionsMenuWithReactionsMenuViewModel:reactionsMenuViewModel aroundFrame:bubbleComponentFrameInOverlayView]; } } @@ -5181,5 +5188,25 @@ - (void)roomContextualMenuViewControllerDidReaction:(RoomContextualMenuViewContr [self hideContextualMenuAnimated:YES]; } +#pragma mark - ReactionsMenuViewModelCoordinatorDelegate + +- (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didAddReaction:(NSString *)reaction forEventId:(NSString *)eventId +{ + [self.roomDataSource addReaction:reaction forEventId:eventId success:^(NSString *eventId) { + + } failure:^(NSError *error) { + + }]; +} + +- (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didRemoveReaction:(NSString *)reaction forEventId:(NSString *)eventId +{ + [self.roomDataSource removeReaction:reaction forEventId:eventId success:^{ + + } failure:^(NSError *error) { + + }]; +} + @end From daab6950d4d58c80ad3a7ccc976256893d5a355f Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 6 Jun 2019 13:50:45 +0200 Subject: [PATCH 141/266] [Reactions] Allow reaction only on room messages (Fix #2476). --- Riot/Modules/Room/RoomViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 048000f751..1565a54bf4 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5097,7 +5097,7 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce [self contextualMenuAnimationCompletionAfterBeingShown:YES]; }]; - if (RiotSettings.shared.messageReaction && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class]) + if (RiotSettings.shared.messageReaction && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && [self.roomDataSource canReactToEventWithId:event.eventId]) { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell*)cell; MXKRoomBubbleCellData *bubbleCellData = roomBubbleTableViewCell.bubbleData; From c1b1e5c730b077f5c6fb95b70a378d3f230f6b87 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 6 Jun 2019 13:51:55 +0200 Subject: [PATCH 142/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index fb713e5cd6..79f7de7f2c 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,7 @@ Bug fix: * Device Verification: Fix user display name and device id colors in dark theme * Registration with an email is broken (#2417). * Reactions: Bad position (#2462). + * Reactions: It lets you react to join/leave events (#2476). Changes in 0.8.6 (2019-05-06) =============================================== From 75bc6428c7408844d54f980047b0958b8816f86b Mon Sep 17 00:00:00 2001 From: Walter Date: Fri, 7 Jun 2019 12:32:49 +0000 Subject: [PATCH 143/266] Translated using Weblate (Russian) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/ru/ --- Riot/Assets/ru.lproj/Localizable.strings | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/ru.lproj/Localizable.strings b/Riot/Assets/ru.lproj/Localizable.strings index 7e194a1e89..040de742b5 100644 --- a/Riot/Assets/ru.lproj/Localizable.strings +++ b/Riot/Assets/ru.lproj/Localizable.strings @@ -11,7 +11,7 @@ /* New action message from a specific person in a named room. */ "ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@"; /* New action message from a specific person, not referencing a room. */ -"IMAGE_FROM_USER" = "%@ отправил(а) вам фото %@"; +"IMAGE_FROM_USER" = "%@ отправил(а) фото %@"; /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ отправил(а) фото %@ в %@"; /* Multiple unread messages in a room */ @@ -50,3 +50,7 @@ "SINGLE_UNREAD_IN_ROOM" = "Вы получили сообщение в %@"; /* A single unread message */ "SINGLE_UNREAD" = "Вы получили сообщение"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ в %@"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ отправил(а) стикер"; From c19a0ba3fa79c74e202149a4334641c22632182c Mon Sep 17 00:00:00 2001 From: Walter Date: Fri, 7 Jun 2019 12:35:36 +0000 Subject: [PATCH 144/266] Translated using Weblate (Russian) Currently translated at 100.0% (5 of 5 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/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 6f2335ecf0..7922a04593 100644 --- a/Riot/Assets/ru.lproj/InfoPlist.strings +++ b/Riot/Assets/ru.lproj/InfoPlist.strings @@ -3,3 +3,4 @@ "NSPhotoLibraryUsageDescription" = "Галерея используется для отправки фотографий и видео."; "NSMicrophoneUsageDescription" = "Микрофон используется при съемке видео и выполнении звонков."; "NSContactsUsageDescription" = "Для отображения контактов, использующих Riot или Matrix, мы можем отправить адреса email и номера телефонов из вашей адресной книги на ваш сервер идентификации Matrix. Новый Vector не хранит эти данные и не использует их для каких-либо других целей. Для получения дополнительной информации, пожалуйста, ознакомьтесь с Политикой конфиденциальности в настройках приложения."; +"NSCalendarsUsageDescription" = "Ознакомьтесь со своими запланированными встречами в приложении."; From 33d3ab2d392f0ca16ef2e745d21e3f4f022b5b35 Mon Sep 17 00:00:00 2001 From: Walter Date: Fri, 7 Jun 2019 12:36:30 +0000 Subject: [PATCH 145/266] Translated using Weblate (Russian) Currently translated at 84.5% (600 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/ru/ --- Riot/Assets/ru.lproj/Vector.strings | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index 2bf3f0c7f9..da1195712b 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -675,3 +675,13 @@ "store_full_description" = "Приложение для чата, под вашим контролем и полностью гибкое. Райот позволяет вам общаться так, как вы хотите. Сделано на [matrix] — стандарт для открытого, децентрализованного общения.\n\nПолучите бесплатную учетную запись на matrix.org, собственный сервер по адресу https://modular.im или используйте другой сервер Matrix.\n\nПочему стоит выбрать Riot.im?\n\n• ПОЛНАЯ СВЯЗЬ: создавайте комнаты для команд, друзей, сообщест — как хотите! Общайтесь, обменивайтесь файлами, добавляйте виджеты и совершайте голосовые и видеозвонки — и все это бесплатно.\n\n• МОЩНЫЕ ИНТЕГРАЦИИ: Используйте Riot.im с инструментами, которые знаете и любите. С Riot.im вы можете даже общаться с пользователями и группами других приложений.\n\n• ЧАСТНЫЕ И БЕЗОПАСНЫЕ: держите ваши разговоры в тайне. Современное сквозное шифрование гарантирует, что частное общение остается частным.\n\n• ОТКРЫТО, НЕ ЗАКРЫТО: Исходный код открыт, построено на Matrix. Владейте данными, используя собственный сервер или выбирайте тот, которому доверяете.\n\n• Везде, где вы находитесь: оставайтесь на связи, где бы вы ни находились, с полностью синхронизированной историей сообщений на всех ваших устройствах и в Интернете по адресу https://riot.im."; "auth_login_single_sign_on" = "Вход с SSO"; "room_message_unable_open_link_error_message" = "Невозможно открыть ссылку."; +"auth_autodiscover_invalid_response" = "Неверный ответ обнаружения сервера"; +"room_event_action_reply" = "Ответ"; +"room_event_action_edit" = "Редактировать"; +"room_event_action_reaction_agree" = "%@ согласен"; +"room_event_action_reaction_disagree" = "%@ несогласен"; +"room_event_action_reaction_like" = "%@ согласен"; +"room_event_action_reaction_dislike" = "%@ не согласны"; +"room_action_reply" = "Ответ"; +"settings_labs_message_reaction" = "Реагировать на сообщения с Emoji"; +"settings_key_backup_button_connect" = "Подключите это устройство к ключу резервного копирования"; From 683145fe4d1edccb5dcb77c72372fc9016ffad16 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Mon, 10 Jun 2019 06:19:14 +0000 Subject: [PATCH 146/266] Translated using Weblate (Basque) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/eu/ --- Riot/Assets/eu.lproj/Localizable.strings | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/eu.lproj/Localizable.strings b/Riot/Assets/eu.lproj/Localizable.strings index 6786baf50f..a17db7e88e 100644 --- a/Riot/Assets/eu.lproj/Localizable.strings +++ b/Riot/Assets/eu.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "%@ erabiltzailearen mezua"; +"MSG_FROM_USER" = "%@ erabiltzaileak mezu bat bidali du"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@erabiltzaileak %@ gelan idatzi du"; /* New message from a specific person, not referencing a room. Content included. */ @@ -11,7 +11,7 @@ /* New action message from a specific person in a named room. */ "ACTION_FROM_USER_IN_ROOM" = "%@: * %@ %@"; /* New action message from a specific person, not referencing a room. */ -"IMAGE_FROM_USER" = "%@ erabiltzaileak irudi bat %@ bidali dizu"; +"IMAGE_FROM_USER" = "%@ erabiltzaileak irudi bat bidali du %@"; /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ erabiltzaileak irudi bat %@ bidali du %@ gelara"; /* Multiple unread messages in a room */ @@ -50,3 +50,7 @@ "SINGLE_UNREAD_IN_ROOM" = "Mezu bat jaso duzu %@ gelan"; /* A single unread message */ "SINGLE_UNREAD" = "Mezu bat jaso duzu"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ erabiltzailea %@ gelan"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ erabiltzaileak eranskailu bat bidali du"; From cd445244ca585a1a83f78b19dea3e82359f08eb0 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Mon, 10 Jun 2019 06:27:41 +0000 Subject: [PATCH 147/266] Translated using Weblate (Basque) Currently translated at 100.0% (5 of 5 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-dialogs/eu/ --- Riot/Assets/eu.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/eu.lproj/InfoPlist.strings b/Riot/Assets/eu.lproj/InfoPlist.strings index f6610a7ccc..028fbdef0e 100644 --- a/Riot/Assets/eu.lproj/InfoPlist.strings +++ b/Riot/Assets/eu.lproj/InfoPlist.strings @@ -3,3 +3,4 @@ "NSPhotoLibraryUsageDescription" = "Argazkien liburutegia argazkiak eta bideoak bidaltzeko erabiltzen da."; "NSMicrophoneUsageDescription" = "Mikrofonoa bideoak atera eta deiak egiteko erabiltzen da."; "NSContactsUsageDescription" = "Zure kontaktuetatik Riot edo Matrix jada darabiltenak erakusteko, zure kontaktu liburuko e-mail helbideak eta telefono zenbakiak bidali ditzakegu identitate zerbitzarira. New Vector enpresak ez ditu datu hauek gordetzen edo beste ezertarako erabiltzen. Informazio gehiagorako ikusi pribatutasun politikari buruzko orria aplikazioaren ezarpenetan."; +"NSCalendarsUsageDescription" = "Ikusi zure programatutako batzarrak aplikazioan."; From 2db80485c5cb7e2d5c5b4cb6a774852caa8beee5 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Mon, 10 Jun 2019 06:47:59 +0000 Subject: [PATCH 148/266] Translated using Weblate (Basque) Currently translated at 95.8% (680 of 710 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eu/ --- Riot/Assets/eu.lproj/Vector.strings | 83 ++++++++++++++++++++++++++--- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index 9239d91ec7..2de28aa8e2 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -52,7 +52,7 @@ "auth_password_placeholder" = "Pasahitza"; "auth_new_password_placeholder" = "Pasahitz berria"; "auth_user_name_placeholder" = "Erabiltzaile-izena"; -"auth_add_email_message" = "Gehitu e-mail helbide bat zure kontura erabiltzaileek zu aurkitzea baimentzeko, eta zuk pasahitza berrezarri ahal izateko."; +"auth_add_email_message" = "Gehitu e-mail helbide bat zure kontura erabiltzaileek zu aurkitzea baimentzeko, eta zure pasahitza berrezartzeko."; "auth_add_phone_message" = "Gehitu telefono zenbaki bat zure kontura beste erabiltzaileek zu aurkitzea ahalbidetzeko."; "auth_optional_email_placeholder" = "E-mail helbidea (aukerakoa)"; "auth_optional_phone_placeholder" = "Telefono zenbakia (aukerakoa)"; @@ -71,7 +71,7 @@ "auth_forgot_password" = "Pasahitza ahaztu duzu?"; "auth_use_server_options" = "Erabili zerbitzari pertsonalizatuaren ezarpenak (aurreratua)"; "auth_email_validation_message" = "Egiaztatu zure e-mail helbidea erregistroarekin jarraitzeko"; -"auth_recaptcha_message" = "Hasiera zerbitzari honek robot bat ez zarela egiaztatu nahi du"; +"auth_recaptcha_message" = "Hasiera-zerbitzari honek robot bat ez zarela egiaztatu nahi du"; "auth_username_in_use" = "Erabilitako erabiltzaile-izena"; "auth_reset_password_next_step_button" = "Nire e-mail helbidea baieztatu dut"; "auth_reset_password_message" = "Zure pasahitza berrezartzeko, idatzi zure kontura gehitutako e-mail helbidea:"; @@ -222,7 +222,7 @@ "auth_untrusted_id_server" = "Identitate zerbitzaria ez da fidagarria"; "auth_msisdn_validation_error" = "Ezin izan da telefono zenbakia egiaztatu."; "auth_reset_password_email_validation_message" = "E-mail bat bidali da %@ helbidera. Honek dakarren esteka jarraitu eta gero egin klik azpian."; -"auth_reset_password_error_not_found" = "Zure e-mail helbidea ez dago antza hasiera zerbitzari honetako Matrix ID batekin lotuta."; +"auth_reset_password_error_not_found" = "Zure e-mail helbidea ez dago antza hasiera-zerbitzari honetako Matrix ID batekin lotuta."; "room_creation_account" = "Kontua"; "room_creation_appearance" = "Itxura"; "room_creation_appearance_picture" = "Txateko irudia (aukerakoa)"; @@ -312,7 +312,7 @@ "settings_config_no_build_info" = "Konpilazio daturik ez"; "settings_mark_all_as_read" = "Markatu mezu guztiak irakurrita gisa"; "settings_report_bug" = "Arazte-txostena"; -"settings_config_home_server" = "Hasiera zerbitzaria %@ da"; +"settings_config_home_server" = "Hasiera-zerbitzaria %@ da"; "settings_config_identity_server" = "Identitate zerbitzaria %@ da"; "settings_config_user_id" = "%@ gisa hasi duzu saioa"; "settings_user_settings" = "ERABILTZAILE EZARPENAK"; @@ -571,7 +571,7 @@ "settings_key_backup_info_progress" = "%@ gakoen babes-kopia egiten..."; "settings_key_backup_info_progress_done" = "Gako guztien babes-kopia egin da"; "settings_key_backup_info_valid" = "Gailu honek zure gakoen babes-kopia egiten du."; -"settings_key_backup_info_not_valid" = "Gailu honek ez du zure gakoen babes kopia egiten."; +"settings_key_backup_info_not_valid" = "Gailu honek ez du zure gakoen babes kopia egiten, baina badago berreskuratu dezakezun babes-kopia bat jarraitu ahal izateko."; "settings_key_backup_button_create" = "Hasi 'Gakoen babes-kopia' erabiltzen"; "settings_key_backup_button_use" = "Erabili gakoen babes-kopia"; "key_backup_setup_passphrase_title" = "Babestu zure babeskopia pasaesaldi batekin"; @@ -633,7 +633,7 @@ "sign_out_key_backup_in_progress_alert_discard_key_backup_action" = "Ez ditut nire zifratutako mezuak behar"; "sign_out_key_backup_in_progress_alert_cancel_action" = "Itxaron egingo dut"; "settings_key_backup_info" = "Zifratutako mezuak muturretik muturrerako zifratzearen bidez babestuak daude. Zuk eta hartzaileak edo hartzaileek irakurri ditzakezue mezu horiek, beste inork ez."; -"settings_key_backup_info_signout_warning" = "Egin zure gakoen babes-kopia saioa amaitu aurretik galdu nahi ez badituzu."; +"settings_key_backup_info_signout_warning" = "Konektatu gailu hau gakoen babes-kopiara saioa amaitu aurretik, gailu honetan bakarrik egon daitezkeen gakoak galdu nahi ez badituzu."; "settings_key_backup_info_trust_signature_unknown" = "Babes-kopiak gailu honen sinadura du, ID: %@"; "settings_key_backup_info_trust_signature_valid" = "Babes-kopiak gailu honen baliozko sinadura bat du"; "settings_key_backup_info_trust_signature_valid_device_verified" = "Gailuak %@ gailuaren baliozko sinadura bat du"; @@ -663,3 +663,74 @@ "auth_login_single_sign_on" = "Hasi saioa urrats batean"; "auth_autodiscover_invalid_response" = "Erantzun baliogabea hasiera-zerbitzarien bilaketari"; "room_message_unable_open_link_error_message" = "Ezin izan da esteka ireki."; +"room_event_action_reply" = "Erantzun"; +"room_event_action_edit" = "Editatu"; +"room_event_action_reaction_agree" = "Ados %@"; +"room_event_action_reaction_disagree" = "Ez ados %@"; +"room_event_action_reaction_like" = "Gogokoa %@"; +"room_event_action_reaction_dislike" = "Ez gogokoa %@"; +"room_action_reply" = "Erantzun"; +"settings_key_backup_button_connect" = "Konektatu gailu hau gakoen babes-kopiara"; +"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Konektatu gailu hau gakoen babes-kopiara"; +"key_backup_recover_connent_banner_subtitle" = "Konektatu gailu hau gakoen babes-kopiara"; +// MARK: - Device Verification +"device_verification_title" = "Egiaztatu gailua"; +"device_verification_error_cannot_load_device" = "Ezin izan da gailuaren informazioa kargatu."; +// Mark: Incoming +"device_verification_incoming_title" = "Jasotako egiaztaketa eskaria"; +"device_verification_incoming_description_1" = "Egiaztatu gailu hau fidagarri gisa markatzeko. Gailuak fidagarritzat jotzeak lasaitasuna ematen dizu muturretik muturrera zifratutako mezuak erabiltzean."; +"device_verification_incoming_description_2" = "Gailu hau egiaztatzean fidagarri gisa markatuko da, eta zure gailua fidagarri gisa markatuko zaio ere zure kideari."; +// MARK: Start +"device_verification_start_title" = "Egiaztatu testu kate labur bat alderatuz"; +"device_verification_start_wait_partner" = "Kideak onartu bitartean zain..."; +"device_verification_start_use_legacy" = "Ez da ezer agertzen? Bezero guztiek ez dute onartzen egiaztaketa interaktiboa oraindik. Erabili egiaztaketa metodo zaharra."; +"device_verification_start_verify_button" = "Hasi egiaztaketa"; +"device_verification_start_use_legacy_action" = "Erabili egiaztaketa metodo zaharra"; +// MARK: Verify +"device_verification_verify_title_emoji" = "Egiaztatu gailu hau honako emojia kidearen pantailan agertu dela baieztatuz"; +"device_verification_verify_title_number" = "Egiaztatu gailu hau honako zenbaki hauek kidearen pantailan agertu direla baieztatuz"; +"device_verification_verify_wait_partner" = "Kideak baieztatzearen zain..."; +// MARK: Verified +"device_verification_verified_title" = "Egiaztatuta!"; +"device_verification_verified_description_1" = "Ongi egiaztatu duzu gailu hau."; +"device_verification_verified_description_2" = "Kide honekin partekatutako mezu seguruak muturretik muturrera zifratuta daude eta ezin ditu beste inork irakurri."; +"device_verification_verified_got_it_button" = "Ulertuta"; +// MARK: Emoji +"device_verification_emoji_dog" = "Txakurra"; +"device_verification_emoji_cat" = "Katua"; +"device_verification_emoji_lion" = "Lehoia"; +"device_verification_emoji_horse" = "Zaldia"; +"device_verification_emoji_unicorn" = "Unikornioa"; +"device_verification_emoji_pig" = "Zerria"; +"device_verification_emoji_elephant" = "Elefantea"; +"device_verification_emoji_rabbit" = "Untxia"; +"device_verification_emoji_panda" = "Panda hartza"; +"device_verification_emoji_rooster" = "Oilarra"; +"device_verification_emoji_penguin" = "Pinguinoa"; +"device_verification_emoji_turtle" = "Dortoka"; +"device_verification_emoji_fish" = "Arraina"; +"device_verification_emoji_octopus" = "Olagarroa"; +"device_verification_emoji_butterfly" = "Tximeleta"; +"device_verification_emoji_flower" = "Lorea"; +"device_verification_emoji_tree" = "Zuhaitza"; +"device_verification_emoji_cactus" = "Kaktusa"; +"device_verification_emoji_mushroom" = "Perretxikoa"; +"device_verification_emoji_globe" = "Lurra"; +"device_verification_emoji_moon" = "Ilargia"; +"device_verification_emoji_cloud" = "Hodeia"; +"device_verification_emoji_fire" = "Sua"; +"device_verification_emoji_banana" = "Banana"; +"device_verification_emoji_apple" = "Sagarra"; +"device_verification_emoji_strawberry" = "Marrubia"; +"device_verification_emoji_corn" = "Artoa"; +"device_verification_emoji_pizza" = "Pizza"; +"device_verification_emoji_cake" = "Pastela"; +"device_verification_emoji_heart" = "Bihotza"; +"device_verification_emoji_smiley" = "Irrifartxoa"; +"device_verification_emoji_robot" = "Robota"; +"device_verification_emoji_hat" = "Txanoa"; +"device_verification_emoji_glasses" = "Betaurrekoak"; +"device_verification_emoji_spanner" = "Giltza"; +"device_verification_emoji_santa" = "Santa"; +"device_verification_emoji_thumbs up" = "Ederto"; +"device_verification_emoji_umbrella" = "Aterkia"; From 310c5cc58115045aa85e6dee7ebc3eeddbdb711d Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 11 Jun 2019 17:43:32 +0200 Subject: [PATCH 149/266] RoomViewController: Display an error when react or unreact on event fails. --- Riot/Modules/Room/RoomViewController.m | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index a9439cea87..2de9abd39d 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -217,6 +217,7 @@ @interface RoomViewController () Date: Wed, 12 Jun 2019 15:35:30 +0200 Subject: [PATCH 150/266] Add message edited mention 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 22a7b6bd97..e225169c64 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -579,6 +579,7 @@ "event_formatter_jitsi_widget_removed" = "VoIP conference removed by %@"; "event_formatter_rerequest_keys_part1_link" = "Re-request encryption keys"; "event_formatter_rerequest_keys_part2" = " from your other devices."; +"event_formatter_message_edited_mention" = "(Edited)"; // Others "or" = "or"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 563bfc5bdd..8e7aa57b31 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -886,6 +886,10 @@ internal enum VectorL10n { internal static func eventFormatterMemberUpdates(_ p1: Int) -> String { return VectorL10n.tr("Vector", "event_formatter_member_updates", p1) } + /// (Edited) + internal static var eventFormatterMessageEditedMention: String { + return VectorL10n.tr("Vector", "event_formatter_message_edited_mention") + } /// Re-request encryption keys internal static var eventFormatterRerequestKeysPart1Link: String { return VectorL10n.tr("Vector", "event_formatter_rerequest_keys_part1_link") From b6d7a14e75d359f46563aa97f7476b4c77f08786 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 12 Jun 2019 16:05:34 +0200 Subject: [PATCH 151/266] EventFormatter: Add edit mention suffix for edited messages. --- Riot/Utils/EventFormatter.h | 21 +++++++++++++++++++-- Riot/Utils/EventFormatter.m | 31 +++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Riot/Utils/EventFormatter.h b/Riot/Utils/EventFormatter.h index d55c032570..67abcd419d 100644 --- a/Riot/Utils/EventFormatter.h +++ b/Riot/Utils/EventFormatter.h @@ -19,18 +19,35 @@ /** Link string used in attributed strings to mark a keys re-request action. */ -FOUNDATION_EXPORT NSString *const kEventFormatterOnReRequestKeysLinkAction; +FOUNDATION_EXPORT NSString *const EventFormatterOnReRequestKeysLinkAction; /** Parameters separator in the link string. */ -FOUNDATION_EXPORT NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator; +FOUNDATION_EXPORT NSString *const EventFormatterLinkActionSeparator; + +/** + Link string used in attributed strings to mark an edited event action. + */ +FOUNDATION_EXPORT NSString *const EventFormatterEditedEventLinkAction; /** `EventFormatter` class inherits from `MXKEventFormatter` to define Vector formatting */ @interface EventFormatter : MXKEventFormatter +/** + Text color used to display message edited mention. + Default is `textSecondaryColor`. + */ +@property (nonatomic) UIColor *editionMentionTextColor; + +/** + Text font used to display message edited mention. + Default is system font 12. + */ +@property (nonatomic) UIFont *editionMentionTextFont; + /** String attributes for event timestamp displayed in chat history. */ diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index 91819e7811..8b8eeba271 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -27,8 +27,9 @@ #pragma mark - Constants definitions -NSString *const kEventFormatterOnReRequestKeysLinkAction = @"kEventFormatterOnReRequestKeysLinkAction"; -NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/"; +NSString *const EventFormatterOnReRequestKeysLinkAction = @"EventFormatterOnReRequestKeysLinkAction"; +NSString *const EventFormatterLinkActionSeparator = @"/"; +NSString *const EventFormatterEditedEventLinkAction = @"EventFormatterEditedEventLinkAction"; static NSString *const kEventFormatterTimeFormat = @"HH:mm"; @@ -159,8 +160,8 @@ - (NSAttributedString *)attributedStringFromEvent:(MXEvent *)event withRoomState NSMutableAttributedString *attributedStringWithRerequestMessage = [attributedString mutableCopy]; [attributedStringWithRerequestMessage appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]]; - NSString *linkActionString = [NSString stringWithFormat:@"%@%@%@", kEventFormatterOnReRequestKeysLinkAction, - kEventFormatterOnReRequestKeysLinkActionSeparator, + NSString *linkActionString = [NSString stringWithFormat:@"%@%@%@", EventFormatterOnReRequestKeysLinkAction, + EventFormatterLinkActionSeparator, event.eventId]; [attributedStringWithRerequestMessage appendAttributedString: @@ -181,6 +182,26 @@ - (NSAttributedString *)attributedStringFromEvent:(MXEvent *)event withRoomState attributedString = attributedStringWithRerequestMessage; } } + else if (event.contentHasBeenEdited) + { + NSMutableAttributedString *attributedStringWithEditMention = [attributedString mutableCopy]; + + NSString *linkActionString = [NSString stringWithFormat:@"%@%@%@", EventFormatterEditedEventLinkAction, + EventFormatterLinkActionSeparator, + event.eventId]; + + [attributedStringWithEditMention appendAttributedString: + [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@" %@", NSLocalizedStringFromTable(@"event_formatter_message_edited_mention", @"Vector", nil)] + attributes:@{ + NSLinkAttributeName: linkActionString, + // NOTE: Color is curretly overidden by UIText.tintColor as we use `NSLinkAttributeName`. + // If we use UITextView.linkTextAttributes to set link color we will also have the issue that color will be the same for all kind of links. + NSForegroundColorAttributeName: self.editionMentionTextColor, + NSFontAttributeName: self.editionMentionTextFont + }]]; + + attributedString = attributedStringWithEditMention; + } return attributedString; } @@ -234,6 +255,7 @@ - (instancetype)initWithMatrixSession:(MXSession *)matrixSession self.encryptingTextColor = ThemeService.shared.theme.tintColor; self.sendingTextColor = ThemeService.shared.theme.textSecondaryColor; self.errorTextColor = ThemeService.shared.theme.warningColor; + self.editionMentionTextColor = ThemeService.shared.theme.textSecondaryColor; self.defaultTextFont = [UIFont systemFontOfSize:15]; self.prefixTextFont = [UIFont boldSystemFontOfSize:15]; @@ -242,6 +264,7 @@ - (instancetype)initWithMatrixSession:(MXSession *)matrixSession self.callNoticesTextFont = [UIFont italicSystemFontOfSize:15]; self.encryptedMessagesTextFont = [UIFont italicSystemFontOfSize:15]; self.emojiOnlyTextFont = [UIFont systemFontOfSize:48]; + self.editionMentionTextFont = [UIFont systemFontOfSize:12]; } return self; } From d751af48ff11b4d87bc4c0c67960d181c4b750e3 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 12 Jun 2019 16:06:27 +0200 Subject: [PATCH 152/266] RoomViewController: Prepare message edited mention tap. --- Riot/Modules/Room/RoomViewController.m | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 7e696900aa..5dd3cbf129 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2824,9 +2824,9 @@ - (BOOL)dataSource:(MXKDataSource *)dataSource shouldDoAction:(NSString *)action NSString *fragment = [NSString stringWithFormat:@"/group/%@", [MXTools encodeURIComponent:absoluteURLString]]; [[AppDelegate theDelegate] handleUniversalLinkFragment:fragment]; } - else if ([absoluteURLString hasPrefix:kEventFormatterOnReRequestKeysLinkAction]) + else if ([absoluteURLString hasPrefix:EventFormatterOnReRequestKeysLinkAction]) { - NSArray *arguments = [absoluteURLString componentsSeparatedByString:kEventFormatterOnReRequestKeysLinkActionSeparator]; + NSArray *arguments = [absoluteURLString componentsSeparatedByString:EventFormatterLinkActionSeparator]; if (arguments.count > 1) { NSString *eventId = arguments[1]; @@ -2838,6 +2838,19 @@ - (BOOL)dataSource:(MXKDataSource *)dataSource shouldDoAction:(NSString *)action } } } + else if ([absoluteURLString hasPrefix:EventFormatterEditedEventLinkAction]) + { + NSArray *arguments = [absoluteURLString componentsSeparatedByString:EventFormatterLinkActionSeparator]; + if (arguments.count > 1) + { + // TODO: Handle event edition history. + + NSString *eventId = arguments[1]; + + NSLog(@"[RoomViewController] Did tap edited mention for eventId: %@", eventId); + } + shouldDoAction = NO; + } else if (url && urlItemInteractionValue) { // Fallback case for external links From 61f777fabaa6af985b3a9c83db9b90546626b070 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 12 Jun 2019 16:07:16 +0200 Subject: [PATCH 153/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 79f7de7f2c..d8bdd6649d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,7 @@ Improvements: * Menu actions: Display message time (#2463). * Reactions Menu: Fix position (#2447). * Context menu polish (#2466). + * Message Editing: Annotate edited messages in timeline (#2400). Bug fix: * Device Verification: Fix user display name and device id colors in dark theme From 531cc4b68fd4280ac17b8d4b3bd0158591dfd1e1 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 12 Jun 2019 18:03:21 +0200 Subject: [PATCH 154/266] Reactions local echoes: Do not show reactions with 0 count (case of reaction being removed) --- .../ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index d204d51e96..846ce20491 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -87,7 +87,7 @@ import UIKit } private func loadData() { - guard let reactionCounts = self.aggregations.aggregatedReactions(onEvent: self.eventId, inRoom: self.roomId)?.reactions else { + guard let reactionCounts = self.aggregations.aggregatedReactions(onEvent: self.eventId, inRoom: self.roomId)?.withNonZeroCount()?.reactions else { return } From 84cd3d5e9dbcab8c57a565af6fdc4b8b0196f8b9 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 12 Jun 2019 18:25:47 +0200 Subject: [PATCH 155/266] Reactions: Change reaction and unreaction methods signatures --- Riot/Modules/Room/DataSources/RoomDataSource.m | 2 +- Riot/Modules/Room/RoomViewController.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 0696c85438..f569efc26c 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -594,7 +594,7 @@ - (Widget *)jitsiWidget - (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didAddReaction:(MXReactionCount *)reactionCount forEventId:(NSString *)eventId { - [self addReaction:reactionCount.reaction forEventId:eventId success:^(NSString *eventId) { + [self addReaction:reactionCount.reaction forEventId:eventId success:^{ } failure:^(NSError *error) { diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 5dd3cbf129..52361488b7 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5209,7 +5209,7 @@ - (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didAddReactio { MXWeakify(self); - [self.roomDataSource addReaction:reaction forEventId:eventId success:^(NSString *eventId) { + [self.roomDataSource addReaction:reaction forEventId:eventId success:^{ } failure:^(NSError *error) { MXStrongifyAndReturnIfNil(self); From 3c4961af8f60c063efd381b8996f2a89faae92f0 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 13 Jun 2019 16:37:28 +0200 Subject: [PATCH 156/266] RoomInputToolbarView: Add editing mode. --- Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h | 3 ++- Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h index 429727a964..c548f48cfb 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h @@ -25,6 +25,7 @@ typedef enum : NSUInteger { RoomInputToolbarViewSendModeSend, RoomInputToolbarViewSendModeReply, + RoomInputToolbarViewSendModeEdit } RoomInputToolbarViewSendMode; @@ -48,7 +49,7 @@ typedef enum : NSUInteger /** The delegate notified when inputs are ready. */ -@property (nonatomic) id delegate; +@property (nonatomic, weak) id delegate; @property (weak, nonatomic) IBOutlet UIView *mainToolbarView; diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 66c182cc57..2b9ff11e01 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -172,7 +172,9 @@ - (void)updateToolbarButtonLabel case RoomInputToolbarViewSendModeReply: title = NSLocalizedStringFromTable(@"room_action_reply", @"Vector", nil); break; - + case RoomInputToolbarViewSendModeEdit: + title = NSLocalizedStringFromTable(@"save", @"Vector", nil); + break; default: title = [NSBundle mxk_localizedStringForKey:@"send"]; break; From e610bd96e83c4925ef404ea45327ab535c88a084 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 13 Jun 2019 16:38:20 +0200 Subject: [PATCH 157/266] RoomViewController: Add message editing support. --- Riot/Modules/Room/RoomViewController.m | 68 +++++++++++++++++++++++--- 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 5dd3cbf129..ec3815f271 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -218,6 +218,7 @@ @interface RoomViewController () )ce return; } - [self selectEventWithId:event.eventId enableReplyMode:NO showTimestamp:YES]; + [self selectEventWithId:event.eventId]; NSArray* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell]; From ecce667483e6dd1a2b3cbbd32a80db4b703e07d8 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 13 Jun 2019 16:42:21 +0200 Subject: [PATCH 158/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index d8bdd6649d..a923de8aae 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -9,6 +9,7 @@ Improvements: * Reactions Menu: Fix position (#2447). * Context menu polish (#2466). * Message Editing: Annotate edited messages in timeline (#2400). + * Message Editing: Editing in the timeline (#2404). Bug fix: * Device Verification: Fix user display name and device id colors in dark theme From 0a02694e61ea34ceeffaca4c70de70d1ccbeeed7 Mon Sep 17 00:00:00 2001 From: serjor Date: Wed, 12 Jun 2019 19:40:11 +0000 Subject: [PATCH 159/266] Translated using Weblate (Spanish) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/es/ --- Riot/Assets/es.lproj/Localizable.strings | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/es.lproj/Localizable.strings b/Riot/Assets/es.lproj/Localizable.strings index a931b34de3..2a6c23591e 100644 --- a/Riot/Assets/es.lproj/Localizable.strings +++ b/Riot/Assets/es.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "Mensaje de %@"; +"MSG_FROM_USER" = "%@ envió un mensaje"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ publicó en %@"; /* New message from a specific person, not referencing a room. Content included. */ @@ -50,3 +50,7 @@ "VOICE_CONF_NAMED_FROM_USER" = "Llamada en grupo de %@: '%@'"; /* Incoming named video conference invite from a specific person */ "VIDEO_CONF_NAMED_FROM_USER" = "Llamada de vídeo en grupo de %@: '%@'"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ en %@"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ envió una pegatina"; From 693ae297f8da116875927b32aa45def07c099a70 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Thu, 13 Jun 2019 09:27:52 +0000 Subject: [PATCH 160/266] Translated using Weblate (Basque) Currently translated at 100.0% (711 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eu/ --- Riot/Assets/eu.lproj/Vector.strings | 35 +++++++++++++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index 2de28aa8e2..32f5706dc0 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -110,8 +110,8 @@ "room_participants_action_set_default_power_level" = "Berrezarri erabiltzaile arrunt gisa"; "room_participants_action_set_moderator" = "Bihurtu moderatzaile"; "room_participants_action_set_admin" = "Bihurtu kudeatzaile"; -"room_participants_action_ignore" = "Ezkutatu kide honen mezu guztiak"; -"room_participants_action_unignore" = "Erakutsi kide honen mezu guztiak"; +"room_participants_action_ignore" = "Ezkutatu erabiltzaile honen mezu guztiak"; +"room_participants_action_unignore" = "Erakutsi erabiltzaile honen mezu guztiak"; "room_participants_action_mention" = "Aipamena"; "contacts_address_book_matrix_users_toggle" = "Matrix erabiltzaileak besterik ez"; "encrypted_room_message_placeholder" = "Bidali zifratutako mezua…"; @@ -734,3 +734,34 @@ "device_verification_emoji_santa" = "Santa"; "device_verification_emoji_thumbs up" = "Ederto"; "device_verification_emoji_umbrella" = "Aterkia"; +"settings_labs_message_reaction" = "Erreakzionatu mezuei emojiekin"; +"event_formatter_message_edited_mention" = "(Editatua)"; +"device_verification_security_advice" = "Segurtasun gehiagorako, hau aurrez aurre edo bestelako komunikazio bide fidagarriak erabiliz egitea aholkatzen dizugu"; +"device_verification_cancelled" = "Beste aldeak egiaztaketa ezeztatu du."; +"device_verification_cancelled_by_me" = "Egiaztaketa ezeztatu da. Arrazoia: %@"; +"device_verification_emoji_hourglass" = "Harea-erlojua"; +"device_verification_emoji_clock" = "Klasea"; +"device_verification_emoji_gift" = "Oparia"; +"device_verification_emoji_light bulb" = "Bonbilla"; +"device_verification_emoji_book" = "Liburua"; +"device_verification_emoji_pencil" = "Arkatza"; +"device_verification_emoji_paperclip" = "Klipa"; +"device_verification_emoji_scissors" = "Artaziak"; +"device_verification_emoji_padlock" = "Giltzarrapoa"; +"device_verification_emoji_key" = "Giltza"; +"device_verification_emoji_hammer" = "Mailua"; +"device_verification_emoji_telephone" = "Telefonoa"; +"device_verification_emoji_flag" = "Bandera"; +"device_verification_emoji_train" = "Trena"; +"device_verification_emoji_bicycle" = "Bizikleta"; +"device_verification_emoji_aeroplane" = "Hegazkina"; +"device_verification_emoji_rocket" = "Kohetea"; +"device_verification_emoji_trophy" = "Saria"; +"device_verification_emoji_ball" = "Baloia"; +"device_verification_emoji_guitar" = "Gitarra"; +"device_verification_emoji_trumpet" = "Tronpeta"; +"device_verification_emoji_bell" = "Kanpaia"; +"device_verification_emoji_anchor" = "Aingura"; +"device_verification_emoji_headphones" = "Aurikularrak"; +"device_verification_emoji_folder" = "Karpeta"; +"device_verification_emoji_pin" = "Txintxeta"; From 8f56fd84efbe30c6fc7d4c0ee7b3356a680b59e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Wed, 12 Jun 2019 20:05:50 +0000 Subject: [PATCH 161/266] Translated using Weblate (French) Currently translated at 100.0% (711 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index b48803bd87..2671d6211b 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -776,3 +776,4 @@ "device_verification_emoji_headphones" = "Écouteurs"; "device_verification_emoji_folder" = "Dossier"; "device_verification_emoji_pin" = "Épingle"; +"event_formatter_message_edited_mention" = "(Édité)"; From cf9fd2c9ea945e22a607fe014703b4ed3b86d41d Mon Sep 17 00:00:00 2001 From: Walter Date: Thu, 13 Jun 2019 13:56:49 +0000 Subject: [PATCH 162/266] Translated using Weblate (Russian) Currently translated at 84.7% (602 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/ru/ --- Riot/Assets/ru.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/ru.lproj/Vector.strings b/Riot/Assets/ru.lproj/Vector.strings index da1195712b..2bfa610c92 100644 --- a/Riot/Assets/ru.lproj/Vector.strings +++ b/Riot/Assets/ru.lproj/Vector.strings @@ -53,7 +53,7 @@ "auth_invalid_email" = "Это не похоже на допустимый адрес электронной почты"; "auth_invalid_phone" = "Это не похоже на действительный номер телефона"; "auth_missing_password" = "Пароль отсутствует"; -"auth_add_email_message" = "Добавьте адрес электронной почты в свою учетную запись, чтобы другим пользователям было проще вас найти. Также это поможет вам при необходимости восстановить пароль."; +"auth_add_email_message" = "Добавьте адрес электронной почты в свою учетную запись, чтобы другим пользователям было проще вас найти. Также это поможет вам при восстановлении паролья."; "auth_add_phone_message" = "Добавьте номер телефона в свою учетную запись, чтобы другим пользователям было проще вас найти."; "auth_add_email_phone_message" = "Добавьте адрес электронной почты и/или номер телефона в свою учетную запись, чтобы другим пользователям было проще вас найти. Адрес электронной почты также позволит вам сбросить пароль."; "auth_add_email_and_phone_message" = "Добавьте адрес электронной почты и номер телефона в свою учетную запись, чтобы другим пользователям было проще вас найти. Адрес электронной почты также позволит вам сбросить пароль."; From df96dfa5acc39d3d0b58534af6d8000bb9094de8 Mon Sep 17 00:00:00 2001 From: serjor Date: Wed, 12 Jun 2019 19:42:22 +0000 Subject: [PATCH 163/266] Translated using Weblate (Spanish) Currently translated at 71.3% (507 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/es/ --- Riot/Assets/es.lproj/Vector.strings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Assets/es.lproj/Vector.strings b/Riot/Assets/es.lproj/Vector.strings index c6af540455..33891e55c7 100644 --- a/Riot/Assets/es.lproj/Vector.strings +++ b/Riot/Assets/es.lproj/Vector.strings @@ -551,3 +551,5 @@ "deactivate_account_informations_part5" = "Si quieres que olvidemos tus mensajes, por favor marca la casilla a continuación\n\nLa visibilidad de mensajes en Matrix es similar a la del correo electrónico. Que olvidemos tus mensajes implica que los mensajes que hayas enviado no se compartirán con ningún usuario nuevo o no registrado, pero aquellos usuarios registrados que ya tengan acceso a estos mensajes seguirán teniendo acceso a su copia."; "deactivate_account_forget_messages_information_part1" = "Por favor, olvida todos los mensajes enviados al desactivar mi cuenta ("; "deactivate_account_forget_messages_information_part3" = ": esto provocará que los usuarios futuros vean conversaciones incompletas)"; +// String for App Store +"store_short_description" = "Chat/VoIP descentralizado y seguro"; From 6a3cfc017218697ee52904f8ff80b61ce4ce7c33 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 14 Jun 2019 16:15:27 +0200 Subject: [PATCH 164/266] Reactions menu: leave the menu once the user tapped a reaction --- Riot/Modules/Room/RoomViewController.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 28435b73ae..66a61219b6 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5270,6 +5270,8 @@ - (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didAddReactio [self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil]; }]; + + [self hideContextualMenuAnimated:YES]; } - (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didRemoveReaction:(NSString *)reaction forEventId:(NSString *)eventId @@ -5283,6 +5285,8 @@ - (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didRemoveReac [self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil]; }]; + + [self hideContextualMenuAnimated:YES]; } @end From 82f52378ddd9c7ce90b3d565683000f1c2325546 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 14 Jun 2019 16:28:21 +0200 Subject: [PATCH 165/266] Menu actions: Display the keyboard once the users tap on reply or edit This also fixes the local echo issues we had when this screen stayed displayed --- Riot/Modules/Room/RoomViewController.m | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 66a61219b6..590f8360f5 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5112,6 +5112,9 @@ - (void)removeMXSessionStateChangeNotificationsListener [self hideContextualMenuAnimated:YES cancelEventSelection:NO completion:nil]; [self selectEventWithId:eventId inputToolBarSendMode:RoomInputToolbarViewSendModeReply showTimestamp:NO]; + + // And display the keyboard + [self.inputToolbarView becomeFirstResponder]; }; // Edit action @@ -5121,6 +5124,9 @@ - (void)removeMXSessionStateChangeNotificationsListener MXStrongifyAndReturnIfNil(self); [self hideContextualMenuAnimated:YES cancelEventSelection:NO completion:nil]; [self editEventContentWithId:eventId]; + + // And display the keyboard + [self.inputToolbarView becomeFirstResponder]; }; editMenuItem.isEnabled = [self.roomDataSource canEditEventWithId:eventId]; From a902979376062e6d947d42e4138583e1c1f14dec Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 14 Jun 2019 08:30:48 +0000 Subject: [PATCH 166/266] Translated using Weblate (Albanian) Currently translated at 99.3% (706 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index f0a36c1778..faeb381f18 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -770,3 +770,4 @@ "device_verification_emoji_anchor" = "Spirancë"; "device_verification_emoji_headphones" = "Kufje"; "device_verification_emoji_folder" = "Dosje"; +"event_formatter_message_edited_mention" = "(U përpunua)"; From 9247becabe25a5b931ce401f0127c5ee770e32d9 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Fri, 14 Jun 2019 05:59:29 +0000 Subject: [PATCH 167/266] Translated using Weblate (Bulgarian) Currently translated at 100.0% (711 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/bg/ --- Riot/Assets/bg.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/bg.lproj/Vector.strings b/Riot/Assets/bg.lproj/Vector.strings index 6dca9c4f50..b6247fda09 100644 --- a/Riot/Assets/bg.lproj/Vector.strings +++ b/Riot/Assets/bg.lproj/Vector.strings @@ -775,3 +775,4 @@ "device_verification_emoji_headphones" = "Слушалки"; "device_verification_emoji_folder" = "Папка"; "device_verification_emoji_pin" = "Карфица"; +"event_formatter_message_edited_mention" = "(Редактирано)"; From c662952b328db609500a7deca30b283d1d65db3f Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Fri, 14 Jun 2019 09:30:59 +0000 Subject: [PATCH 168/266] Translated using Weblate (Dutch) Currently translated at 100.0% (711 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/nl/ --- Riot/Assets/nl.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index d83b3feb91..0b9471acea 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -784,3 +784,4 @@ "device_verification_emoji_headphones" = "Koptelefoon"; "device_verification_emoji_folder" = "Map"; "device_verification_emoji_pin" = "Speld"; +"event_formatter_message_edited_mention" = "(Bewerkt)"; From ba6d5939a3381ca38dfe7a31a9ba692c20f52231 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 18 Jun 2019 12:28:59 +0200 Subject: [PATCH 169/266] Read receipts: They are now counted at the MatrixKit level --- CHANGES.rst | 1 + Riot/Modules/Room/CellData/RoomBubbleCellData.m | 7 ------- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index a923de8aae..9761acd5a5 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -10,6 +10,7 @@ Improvements: * Context menu polish (#2466). * Message Editing: Annotate edited messages in timeline (#2400). * Message Editing: Editing in the timeline (#2404). + * Read receipts: They are now counted at the MatrixKit level. Bug fix: * Device Verification: Fix user display name and device id colors in dark theme diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index e550c36169..bf8ef79091 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -71,10 +71,6 @@ - (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomS // Increase maximum number of components self.maxComponentCount = 20; - - // Initialize read receipts - self.readReceipts = [NSMutableDictionary dictionary]; - self.readReceipts[event.eventId] = [roomDataSource.room getEventReceipts:event.eventId sorted:YES]; // Reset attributedTextMessage to force reset MXKRoomCellData parameters self.attributedTextMessage = nil; @@ -550,9 +546,6 @@ - (BOOL)addEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState return NO; } - // Update read receipts for this bubble - self.readReceipts[event.eventId] = [roomDataSource.room getEventReceipts:event.eventId sorted:YES]; - return [super addEvent:event andRoomState:roomState]; } From 872976f7982759cc598d1d55d36a22e6044abc10 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 18 Jun 2019 14:06:23 +0200 Subject: [PATCH 170/266] Read receipts: Manage live update in the kit too --- .../Modules/Room/DataSources/RoomDataSource.m | 83 ------------------- 1 file changed, 83 deletions(-) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index f569efc26c..ef62b1aa5d 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -120,89 +120,6 @@ - (void)destroy [super destroy]; } -- (void)didReceiveReceiptEvent:(MXEvent *)receiptEvent roomState:(MXRoomState *)roomState -{ - // Do the processing on the same processing queue as MXKRoomDataSource - dispatch_async(MXKRoomDataSource.processingQueue, ^{ - - // Remove the previous displayed read receipt for each user who sent a - // new read receipt. - // To implement it, we need to find the sender id of each new read receipt - // among the read receipts array of all events in all bubbles. - NSArray *readReceiptSenders = receiptEvent.readReceiptSenders; - - @synchronized(bubbles) - { - for (RoomBubbleCellData *cellData in bubbles) - { - NSMutableDictionary *> *updatedCellDataReadReceipts = [NSMutableDictionary dictionary]; - - for (NSString *eventId in cellData.readReceipts) - { - for (MXReceiptData *receiptData in cellData.readReceipts[eventId]) - { - for (NSString *senderId in readReceiptSenders) - { - if ([receiptData.userId isEqualToString:senderId]) - { - if (!updatedCellDataReadReceipts[eventId]) - { - updatedCellDataReadReceipts[eventId] = cellData.readReceipts[eventId]; - } - - NSPredicate *predicate = [NSPredicate predicateWithFormat:@"userId!=%@", receiptData.userId]; - updatedCellDataReadReceipts[eventId] = [updatedCellDataReadReceipts[eventId] filteredArrayUsingPredicate:predicate]; - break; - } - } - - } - } - - // Flush found changed to the cell data - for (NSString *eventId in updatedCellDataReadReceipts) - { - if (updatedCellDataReadReceipts[eventId].count) - { - cellData.readReceipts[eventId] = updatedCellDataReadReceipts[eventId]; - } - else - { - cellData.readReceipts[eventId] = nil; - } - } - } - } - - // Update cell data we have received a read receipt for - NSArray *readEventIds = receiptEvent.readReceiptEventIds; - for (NSString* eventId in readEventIds) - { - RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:eventId]; - if (cellData) - { - @synchronized(bubbles) - { - if (!cellData.hasNoDisplay) - { - cellData.readReceipts[eventId] = [self.room getEventReceipts:eventId sorted:YES]; - } - else - { - // Ignore the read receipts on the events without an actual display. - cellData.readReceipts[eventId] = nil; - } - } - } - } - - dispatch_async(dispatch_get_main_queue(), ^{ - // TODO: Be smarter and update only updated cells - [super didReceiveReceiptEvent:receiptEvent roomState:roomState]; - }); - }); -} - #pragma mark - - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section From c8bbf9fd0076c8463f8b30a50695878b3e5a05af Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 18 Jun 2019 17:41:09 +0200 Subject: [PATCH 171/266] MXKRoomBubbleTableViewCell: Improve timestamp label positioning. --- .../MXKRoomBubbleTableViewCell+Riot.m | 27 ++++++++++++++----- 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 684f793a4c..2cb67277a0 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -85,13 +85,24 @@ - (void)addTimestampLabelForComponentIndex:(NSInteger)componentIndex CGFloat timeLabelWidth; NSTextAlignment timeLabelTextAlignment; + CGRect componentFrame = [self componentFrameInContentViewForIndex:componentIndex]; + if (displayOnLeft) { CGFloat leftMargin = 10.0; CGFloat rightMargin = (self.contentView.frame.size.width - (self.bubbleInfoContainer.frame.origin.x + self.bubbleInfoContainer.frame.size.width)); timeLabelPosX = 0; - timeLabelPosY = component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant; + + if (CGRectEqualToRect(componentFrame, CGRectNull) == false) + { + timeLabelPosY = componentFrame.origin.y - self.bubbleInfoContainerTopConstraint.constant; + } + else + { + timeLabelPosY = component.position.y + self.msgTextViewTopConstraint.constant - self.bubbleInfoContainerTopConstraint.constant; + } + timeLabelWidth = self.contentView.frame.size.width - leftMargin - rightMargin; timeLabelTextAlignment = NSTextAlignmentLeft; } @@ -99,21 +110,25 @@ - (void)addTimestampLabelForComponentIndex:(NSInteger)componentIndex { timeLabelPosX = self.bubbleInfoContainer.frame.size.width - RoomBubbleCellLayout.timestampLabelWidth; - CGRect componentFrame = [self componentFrameInContentViewForIndex:componentIndex]; - - if (CGRectEqualToRect(componentFrame, CGRectNull) == false) + if (isFirstDisplayedComponent) { - timeLabelPosY = componentFrame.origin.y - timeLabelHeight - self.bubbleInfoContainerTopConstraint.constant; + timeLabelPosY = 0; + } + else if (CGRectEqualToRect(componentFrame, CGRectNull) == false) + { + timeLabelPosY = componentFrame.origin.y - self.bubbleInfoContainerTopConstraint.constant - timeLabelHeight; } else { - timeLabelPosY = isFirstDisplayedComponent ? 0 : component.position.y + self.msgTextViewTopConstraint.constant - timeLabelHeight - self.bubbleInfoContainerTopConstraint.constant; + timeLabelPosY = component.position.y + self.msgTextViewTopConstraint.constant - timeLabelHeight - self.bubbleInfoContainerTopConstraint.constant; } timeLabelWidth = RoomBubbleCellLayout.timestampLabelWidth; timeLabelTextAlignment = NSTextAlignmentRight; } + timeLabelPosY = MAX(0.0, timeLabelPosY); + UILabel *timeLabel = [[UILabel alloc] initWithFrame:CGRectMake(timeLabelPosX, timeLabelPosY, timeLabelWidth, timeLabelHeight)]; timeLabel.text = [bubbleData.eventFormatter timeStringFromDate:component.date]; From 0809c8af55f65485f8cb722b09cea59a4d8d28ea Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 18 Jun 2019 17:42:00 +0200 Subject: [PATCH 172/266] RoomBubbleCellData: Fix bubble component position when display last message timestamp. --- .../Room/CellData/RoomBubbleCellData.m | 23 +++---------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index e550c36169..6b62b990c8 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -295,28 +295,11 @@ - (void)refreshBubbleComponentsPosition // Check whether the position of other components need to be refreshed if (!self.attachment && index < bubbleComponents.count) { - NSMutableAttributedString *attributedString; + NSMutableAttributedString *attributedString = [NSMutableAttributedString new]; NSInteger selectedComponentIndex = self.selectedComponentIndex; NSInteger lastMessageIndex = self.containsLastMessage ? self.mostRecentComponentIndex : NSNotFound; - - // Check whether the timestamp is displayed for this first component, and check whether a vertical whitespace is required - if (((selectedComponentIndex == index && self.addVerticalWhitespaceForSelectedComponentTimestamp) || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName)) - { - attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; - [attributedString appendAttributedString:component.attributedTextMessage]; - } - else - { - // Init attributed string with the first text component - attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:component.attributedTextMessage]; - } - - // Vertical whitespace is added in case of read receipts or reactions - [self addVerticalWhitespaceToString:attributedString forEvent:component.event.eventId]; - - [attributedString appendAttributedString:[MXKRoomBubbleCellDataWithAppendingMode messageSeparator]]; - - for (index++; index < bubbleComponents.count; index++) + + for (index = 0; index < bubbleComponents.count; index++) { // Compute the vertical position for next component component = bubbleComponents[index]; From f33a0e9b7abe7d35163d4dadb824b1874d1390e7 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 18 Jun 2019 18:29:12 +0200 Subject: [PATCH 173/266] MXKRoomBubbleTableViewCell: Add convenient method to compute bubble cell height for cell with attachment view. --- .../MXKRoomBubbleTableViewCell+Riot.h | 9 ++++ .../MXKRoomBubbleTableViewCell+Riot.m | 42 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h index e57ee45c55..c9f58e2aa1 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h @@ -98,6 +98,15 @@ extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer; */ - (CGRect)componentFrameInContentViewForIndex:(NSInteger)componentIndex; +/** + Give the correct cell height for a bubble cell with an attachment view. Handle reactions and read receipts views. + + @param cellData The data object to render. + @param maxWidth The maximum available width. + @return The cell height. + */ ++ (CGFloat)attachmentBubbleCellHeightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth; + /** Blur the view by adding a transparent overlay. Default is NO. */ diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 2cb67277a0..2efb265c67 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -546,6 +546,48 @@ - (CGRect)componentFrameInContentViewForIndex:(NSInteger)componentIndex return componentFrame; } ++ (CGFloat)attachmentBubbleCellHeightForCellData:(MXKCellData *)cellData withMaximumWidth:(CGFloat)maxWidth +{ + MXKRoomBubbleTableViewCell* cell = [self cellWithOriginalXib]; + CGFloat rowHeight = 0; + + RoomBubbleCellData *bubbleData; + + if ([cellData isKindOfClass:[RoomBubbleCellData class]]) + { + bubbleData = (RoomBubbleCellData*)cellData; + } + + if (bubbleData && cell.attachmentView && bubbleData.isAttachmentWithThumbnail) + { + // retrieve the suggested image view height + rowHeight = bubbleData.contentSize.height; + + // Check here the minimum height defined in cell view for text message + if (cell.attachViewMinHeightConstraint && rowHeight < cell.attachViewMinHeightConstraint.constant) + { + rowHeight = cell.attachViewMinHeightConstraint.constant; + } + + // Finalize the row height by adding the vertical constraints. + + rowHeight += cell.attachViewTopConstraint.constant; + + CGFloat additionalHeight = bubbleData.additionalContentHeight; + + if (additionalHeight) + { + rowHeight += additionalHeight; + } + else + { + rowHeight += cell.attachViewBottomConstraint.constant; + } + } + + return rowHeight; +} + #pragma mark - User actions - (IBAction)onEditButtonPressed:(id)sender From 3295b649c96bcb167e66e1b816079f04faefbe24 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 18 Jun 2019 18:30:39 +0200 Subject: [PATCH 174/266] RoomBubbleCellData: Add additionalContentHeight property to cache additional views height like reactions view and read receipts container. --- .../Room/CellData/RoomBubbleCellData.h | 15 +++ .../Room/CellData/RoomBubbleCellData.m | 97 +++++++++++++++++++ 2 files changed, 112 insertions(+) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.h b/Riot/Modules/Room/CellData/RoomBubbleCellData.h index fd62967b49..a8317df00c 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.h +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.h @@ -65,4 +65,19 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag) */ @property(nonatomic, readonly) NSInteger selectedComponentIndex; +/** + Return additional content height (read receipts, reactions). + */ +@property(nonatomic, readonly) CGFloat additionalContentHeight; + +/** + Indicate to update additional content height. + */ +- (void)setNeedsUpdateAdditionalContentHeight; + +/** + Update additional content height if needed. + */ +- (void)updateAdditionalContentHeightIfNeeded; + @end diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 6b62b990c8..0637c96e11 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -29,6 +29,8 @@ @interface RoomBubbleCellData() @property(nonatomic, readonly) BOOL addVerticalWhitespaceForSelectedComponentTimestamp; +@property(nonatomic, readwrite) CGFloat additionalContentHeight; +@property(nonatomic) BOOL shouldUpdateAdditionalContentHeight; @end @@ -106,6 +108,8 @@ - (void)prepareBubbleComponentsPosition shouldUpdateComponentsPosition = NO; } + + [self updateAdditionalContentHeightIfNeeded]; } - (NSAttributedString*)attributedTextMessage @@ -380,6 +384,99 @@ - (void)addVerticalWhitespaceToString:(NSMutableAttributedString *)attributedStr } } +- (CGFloat)computeAdditionalHeight +{ + CGFloat height = 0; + + for (MXKRoomBubbleComponent *bubbleComponent in self.bubbleComponents) + { + NSString *eventId = bubbleComponent.event.eventId; + + height+= [self reactionHeightForEventId:eventId]; + height+= [self readReceiptHeightForEventId:eventId]; + } + + return height; +} + +- (void)updateAdditionalContentHeightIfNeeded; +{ + if (self.shouldUpdateAdditionalContentHeight) + { + void(^updateAdditionalHeight)(void) = ^() { + self.additionalContentHeight = [self computeAdditionalHeight]; + }; + + // The additional height depends on the room read receipts and reactions view which must be calculated on the main thread. + // Check here the current thread, this is just a sanity check because this method is called during the rendering step + // which takes place on the main thread. + if ([NSThread currentThread] != [NSThread mainThread]) + { + NSLog(@"[RoomBubbleCellData] prepareBubbleComponentsPosition called on wrong thread"); + dispatch_sync(dispatch_get_main_queue(), ^{ + updateAdditionalHeight(); + }); + } + else + { + updateAdditionalHeight(); + } + + self.shouldUpdateAdditionalContentHeight = NO; + } +} + +- (void)setNeedsUpdateAdditionalContentHeight +{ + self.shouldUpdateAdditionalContentHeight = YES; +} + +- (CGFloat)reactionHeightForEventId:(NSString*)eventId +{ + CGFloat height = 0; + + NSUInteger reactionCount = self.reactions[eventId].reactions.count; + + MXAggregatedReactions *aggregatedReactions = self.reactions[eventId]; + + if (reactionCount) + { + CGFloat bubbleReactionsViewWidth = self.maxTextViewWidth - 4; + + CGSize fittingSize = UILayoutFittingCompressedSize; + fittingSize.width = bubbleReactionsViewWidth; + + static BubbleReactionsView *bubbleReactionsView; + + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + bubbleReactionsView = [BubbleReactionsView new]; + }); + + bubbleReactionsView.frame = CGRectMake(0, 0, bubbleReactionsViewWidth, 1.0); + BubbleReactionsViewModel *viemModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:eventId]; + bubbleReactionsView.viewModel = viemModel; + [bubbleReactionsView setNeedsLayout]; + [bubbleReactionsView layoutIfNeeded]; + + height = [bubbleReactionsView systemLayoutSizeFittingSize:fittingSize].height + RoomBubbleCellLayout.reactionsViewTopMargin; + } + + return height; +} + +- (CGFloat)readReceiptHeightForEventId:(NSString*)eventId +{ + CGFloat height = 0; + + if (self.readReceipts[eventId].count) + { + height = RoomBubbleCellLayout.readReceiptsViewHeight + RoomBubbleCellLayout.readReceiptsViewTopMargin; + } + + return height; +} + - (void)setContainsLastMessage:(BOOL)containsLastMessage { // Check whether there is something to do From 8e50ff08c15c375767f94f1b8ed4c67c758b37ce Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 18 Jun 2019 18:36:22 +0200 Subject: [PATCH 175/266] Fix bubble cell height for cells with attachment view and reactions. --- .../Modules/Room/DataSources/RoomDataSource.m | 37 +++++++++++++++++++ .../RoomIncomingAttachmentBubbleCell.m | 12 ++++++ ...gAttachmentWithPaginationTitleBubbleCell.m | 12 ++++++ ...ingAttachmentWithoutSenderInfoBubbleCell.m | 13 +++++++ .../RoomOutgoingAttachmentBubbleCell.m | 12 ++++++ ...ingAttachmentWithoutSenderInfoBubbleCell.m | 14 +++++++ 6 files changed, 100 insertions(+) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index f569efc26c..af4e4a99a9 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -170,6 +170,8 @@ - (void)didReceiveReceiptEvent:(MXEvent *)receiptEvent roomState:(MXRoomState *) { cellData.readReceipts[eventId] = nil; } + + [cellData setNeedsUpdateAdditionalContentHeight]; } } } @@ -192,6 +194,8 @@ - (void)didReceiveReceiptEvent:(MXEvent *)receiptEvent roomState:(MXRoomState *) // Ignore the read receipts on the events without an actual display. cellData.readReceipts[eventId] = nil; } + + [cellData setNeedsUpdateAdditionalContentHeight]; } } } @@ -203,6 +207,19 @@ - (void)didReceiveReceiptEvent:(MXEvent *)receiptEvent roomState:(MXRoomState *) }); } +- (void)updateCellDataReactions:(id)cellData forEventId:(NSString*)eventId +{ + [super updateCellDataReactions:cellData forEventId:eventId]; + + RoomBubbleCellData *roomBubbleCellData; + + if ([cellData isKindOfClass:[RoomBubbleCellData class]]) + { + roomBubbleCellData = (RoomBubbleCellData*)cellData; + [roomBubbleCellData setNeedsUpdateAdditionalContentHeight]; + } +} + #pragma mark - - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section @@ -265,6 +282,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N [bubbleCell addTimestampLabelForComponent:cellData.mostRecentComponentIndex]; } + NSMutableArray *temporaryViews = [NSMutableArray new]; + // Handle read receipts and read marker display. // Ignore the read receipts on the bubble without actual display. // Ignore the read receipts on collapsed bubbles @@ -306,6 +325,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N reactionsView.viewModel = bubbleReactionsViewModel; [reactionsView updateWithTheme:ThemeService.shared.theme]; + [temporaryViews addObject:reactionsView]; + bubbleReactionsViewModel.viewModelDelegate = self; reactionsView.translatesAutoresizingMaskIntoConstraints = NO; @@ -387,6 +408,8 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N avatarsContainer.translatesAutoresizingMaskIntoConstraints = NO; avatarsContainer.accessibilityIdentifier = @"readReceiptsContainer"; + [temporaryViews addObject:avatarsContainer]; + // Add this read receipts container in the content view if (!bubbleCell.tmpSubviews) { @@ -502,6 +525,20 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N } } + // Update attachmentView bottom constraint to display reactions and read receipts if needed + + UIView *attachmentView = bubbleCell.attachmentView; + NSLayoutConstraint *attachmentViewBottomConstraint = bubbleCell.attachViewBottomConstraint; + + if (attachmentView && temporaryViews.count) + { + attachmentViewBottomConstraint.constant = roomBubbleCellData.additionalContentHeight; + } + else if (attachmentView) + { + [bubbleCell resetAttachmentViewBottomConstraintConstant]; + } + // Check whether an event is currently selected: the other messages are then blurred if (_selectedEventId) { diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m index 00018fb4de..2f7e4d80aa 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m @@ -39,4 +39,16 @@ - (void)render:(MXKCellData *)cellData [self updateUserNameColor]; } ++ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth +{ + CGFloat rowHeight = [self attachmentBubbleCellHeightForCellData:cellData withMaximumWidth:maxWidth]; + + if (rowHeight <= 0) + { + rowHeight = [super heightForCellData:cellData withMaximumWidth:maxWidth]; + } + + return rowHeight; +} + @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m index 519bff8725..16a614a460 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m @@ -45,4 +45,16 @@ - (void)render:(MXKCellData *)cellData } } ++ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth +{ + CGFloat rowHeight = [self attachmentBubbleCellHeightForCellData:cellData withMaximumWidth:maxWidth]; + + if (rowHeight <= 0) + { + rowHeight = [super heightForCellData:cellData withMaximumWidth:maxWidth]; + } + + return rowHeight; +} + @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m index 7123851524..5d63baf003 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m @@ -16,6 +16,7 @@ */ #import "RoomIncomingAttachmentWithoutSenderInfoBubbleCell.h" +#import "MXKRoomBubbleTableViewCell+Riot.h" #import "ThemeService.h" #import "Riot-Swift.h" @@ -29,4 +30,16 @@ - (void)customizeTableViewCellRendering self.messageTextView.tintColor = ThemeService.shared.theme.tintColor; } ++ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth +{ + CGFloat rowHeight = [self attachmentBubbleCellHeightForCellData:cellData withMaximumWidth:maxWidth]; + + if (rowHeight <= 0) + { + rowHeight = [super heightForCellData:cellData withMaximumWidth:maxWidth]; + } + + return rowHeight; +} + @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m index a50ffe6c99..e6417201bc 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m @@ -62,4 +62,16 @@ + (void)render:(MXKCellData *)cellData inBubbleCell:(MXKRoomOutgoingAttachmentBu } } ++ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth +{ + CGFloat rowHeight = [self attachmentBubbleCellHeightForCellData:cellData withMaximumWidth:maxWidth]; + + if (rowHeight <= 0) + { + rowHeight = [super heightForCellData:cellData withMaximumWidth:maxWidth]; + } + + return rowHeight; +} + @end diff --git a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m index b60dac6771..30a50ae2d2 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentWithoutSenderInfoBubbleCell.m @@ -19,6 +19,8 @@ #import "ThemeService.h" #import "Riot-Swift.h" +#import "RoomBubbleCellData.h" +#import "MXKRoomBubbleTableViewCell+Riot.h" @implementation RoomOutgoingAttachmentWithoutSenderInfoBubbleCell @@ -36,4 +38,16 @@ - (void)render:(MXKCellData *)cellData [RoomOutgoingAttachmentBubbleCell render:cellData inBubbleCell:self]; } ++ (CGFloat)heightForCellData:(MXKCellData*)cellData withMaximumWidth:(CGFloat)maxWidth +{ + CGFloat rowHeight = [self attachmentBubbleCellHeightForCellData:cellData withMaximumWidth:maxWidth]; + + if (rowHeight <= 0) + { + rowHeight = [super heightForCellData:cellData withMaximumWidth:maxWidth]; + } + + return rowHeight; +} + @end From f2149b57c32473746df9f5c0d42096b7a9e31cc9 Mon Sep 17 00:00:00 2001 From: Michael Albert Date: Mon, 17 Jun 2019 13:32:12 +0000 Subject: [PATCH 176/266] Translated using Weblate (German) Currently translated at 96.2% (684 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/de/ --- Riot/Assets/de.lproj/Vector.strings | 69 +++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/Riot/Assets/de.lproj/Vector.strings b/Riot/Assets/de.lproj/Vector.strings index 4d209376b9..c59aa01c55 100644 --- a/Riot/Assets/de.lproj/Vector.strings +++ b/Riot/Assets/de.lproj/Vector.strings @@ -667,3 +667,72 @@ "room_event_action_reaction_like" = "%@ zustimmen"; "room_event_action_reaction_dislike" = "%@ nicht zustimmen"; "room_action_reply" = "Antworten"; +"settings_labs_message_reaction" = "Mit einem Emoji reagieren"; +"settings_key_backup_button_connect" = "Schlüssel dieses Geräts sichern"; +"event_formatter_message_edited_mention" = "(geändert)"; +"key_backup_setup_intro_setup_connect_action_with_existing_backup" = "Schlüssel dieses Geräts sichern"; +"key_backup_recover_connent_banner_subtitle" = "Schlüssel dieses Geräts sichern"; +// MARK: - Device Verification +"device_verification_title" = "Gerät verifizieren"; +"device_verification_cancelled" = "Die Gegenseite hat die Verifikation abgebrochen"; +"device_verification_cancelled_by_me" = "Die Verifikation wurde abgebrochen. Grund: %@"; +"device_verification_error_cannot_load_device" = "Konnte Geräteinformationen nicht laden."; +// Mark: Incoming +"device_verification_incoming_title" = "Eingehende Verifikationsanfrage"; +"device_verification_start_verify_button" = "Beginne Verifikation"; +"device_verification_verified_got_it_button" = "Verstanden"; +// MARK: Emoji +"device_verification_emoji_dog" = "Hund"; +"device_verification_emoji_cat" = "Katze"; +"device_verification_emoji_lion" = "Löwe"; +"device_verification_emoji_horse" = "Pferd"; +"device_verification_emoji_unicorn" = "Einhorn"; +"device_verification_emoji_pig" = "Schwein"; +"device_verification_emoji_elephant" = "Elefant"; +"device_verification_emoji_rabbit" = "Kaninchen"; +"device_verification_emoji_panda" = "Panda"; +"device_verification_emoji_penguin" = "Pinguin"; +"device_verification_emoji_turtle" = "Schildkröte"; +"device_verification_emoji_fish" = "Fisch"; +"device_verification_emoji_octopus" = "Oktopus"; +"device_verification_emoji_butterfly" = "Schmetterling"; +"device_verification_emoji_flower" = "Blume"; +"device_verification_emoji_tree" = "Baum"; +"device_verification_emoji_cactus" = "Kaktus"; +"device_verification_emoji_mushroom" = "Pilz"; +"device_verification_emoji_moon" = "Mond"; +"device_verification_emoji_cloud" = "Wolke"; +"device_verification_emoji_fire" = "Feuer"; +"device_verification_emoji_banana" = "Banane"; +"device_verification_emoji_apple" = "Apfel"; +"device_verification_emoji_strawberry" = "Erdbeere"; +"device_verification_emoji_corn" = "Mais"; +"device_verification_emoji_pizza" = "Pizza"; +"device_verification_emoji_cake" = "Kuchen"; +"device_verification_emoji_heart" = "Herz"; +"device_verification_emoji_robot" = "Roboter"; +"device_verification_emoji_hat" = "Hut"; +"device_verification_emoji_glasses" = "Brille"; +"device_verification_emoji_santa" = "Nikolaus"; +"device_verification_emoji_umbrella" = "Regenschirm"; +"device_verification_emoji_gift" = "Geschenk"; +"device_verification_emoji_light bulb" = "Glühbirne"; +"device_verification_emoji_book" = "Buch"; +"device_verification_emoji_paperclip" = "Büroklammer"; +"device_verification_emoji_scissors" = "Schere"; +"device_verification_emoji_padlock" = "Schloss"; +"device_verification_emoji_key" = "Schlüssel"; +"device_verification_emoji_hammer" = "Hammer"; +"device_verification_emoji_telephone" = "Telefon"; +"device_verification_emoji_flag" = "Flagge"; +"device_verification_emoji_train" = "Zug"; +"device_verification_emoji_bicycle" = "Fahrrad"; +"device_verification_emoji_aeroplane" = "Flugzeug"; +"device_verification_emoji_rocket" = "Rakete"; +"device_verification_emoji_trophy" = "Pokal"; +"device_verification_emoji_ball" = "Ball"; +"device_verification_emoji_guitar" = "Gitarre"; +"device_verification_emoji_trumpet" = "Trompete"; +"device_verification_emoji_bell" = "Glocke"; +"device_verification_emoji_anchor" = "Anker"; +"device_verification_emoji_headphones" = "Kopfhörer"; From 95b55aca3f05957aaea1b7bc3c02e1a5d3d16dfd Mon Sep 17 00:00:00 2001 From: Szimszon Date: Mon, 17 Jun 2019 18:18:58 +0000 Subject: [PATCH 177/266] Translated using Weblate (Hungarian) Currently translated at 100.0% (711 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 2b24813f18..9d20c08494 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -781,3 +781,4 @@ "device_verification_emoji_headphones" = "Fejhallgató"; "device_verification_emoji_folder" = "Mappa"; "device_verification_emoji_pin" = "Rajszeg"; +"event_formatter_message_edited_mention" = "(Szerkesztve)"; From 4e6c1c36af8e5db14083dd7f181cb432b7ccc749 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 19 Jun 2019 14:22:38 +0200 Subject: [PATCH 178/266] RoomDataSource: Fix bubble cell data additional content height update. --- .../Modules/Room/DataSources/RoomDataSource.m | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 3bcdf9b5b1..c7b642fb68 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -120,6 +120,31 @@ - (void)destroy [super destroy]; } +- (void)updateCellDataReactions:(id)cellData forEventId:(NSString*)eventId +{ + [super updateCellDataReactions:cellData forEventId:eventId]; + + [self setNeedsUpdateAdditionalContentHeightForCellData:cellData]; +} + +- (void)updateCellData:(MXKRoomBubbleCellData*)cellData withReadReceipts:(NSArray*)readReceipts forEventId:(NSString*)eventId +{ + [super updateCellData:cellData withReadReceipts:readReceipts forEventId:eventId]; + + [self setNeedsUpdateAdditionalContentHeightForCellData:cellData]; +} + +- (void)setNeedsUpdateAdditionalContentHeightForCellData:(id)cellData +{ + RoomBubbleCellData *roomBubbleCellData; + + if ([cellData isKindOfClass:[RoomBubbleCellData class]]) + { + roomBubbleCellData = (RoomBubbleCellData*)cellData; + [roomBubbleCellData setNeedsUpdateAdditionalContentHeight]; + } +} + #pragma mark - - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section From c71abd5edce1fa98a548863ab1bcf0c55d3c3032 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 19 Jun 2019 15:43:42 +0200 Subject: [PATCH 179/266] Update CHANGES.rst --- CHANGES.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 6106c88a49..02d42666c2 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,6 +8,10 @@ Improvements: * Menu actions: Display message time (#2463). * Reactions Menu: Fix position (#2447). * Context menu polish (#2466). + * Upgrade Piwik/MatomoTracker (v6.0.1) (#2159). + * Message Editing: Annotate edited messages in timeline (#2400). + * Message Editing: Editing in the timeline (#2404). + * Read receipts: They are now counted at the MatrixKit level. Bug fix: * Device Verification: Fix user display name and device id colors in dark theme From 892314498dfd1b76055e34250342d14b78052ee3 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 19 Jun 2019 15:30:21 +0200 Subject: [PATCH 180/266] Migrate project to Swift 5 --- Riot.xcodeproj/project.pbxproj | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 4a3f5a3dd9..2be1cd8180 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -3412,7 +3412,7 @@ 24CBEC4D1F0EAD310093EABB = { CreatedOnToolsVersion = 8.3.2; DevelopmentTeam = 7J4U792NQT; - LastSwiftMigration = 1010; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.ApplicationGroups.iOS = { @@ -3433,7 +3433,7 @@ F094A9A11B78D8F000B1FBBF = { CreatedOnToolsVersion = 6.2; DevelopmentTeam = 7J4U792NQT; - LastSwiftMigration = 1010; + LastSwiftMigration = 1020; ProvisioningStyle = Automatic; SystemCapabilities = { com.apple.ApplicationGroups.iOS = { @@ -4341,7 +4341,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/RiotShareExtension-Bridging-Header.h"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; USER_HEADER_SEARCH_PATHS = "$(inherited)"; }; name = Debug; @@ -4381,7 +4381,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/RiotShareExtension-Bridging-Header.h"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; USER_HEADER_SEARCH_PATHS = "$(inherited)"; }; name = Release; @@ -4584,7 +4584,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/Riot-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = "$(inherited)"; }; @@ -4615,7 +4615,7 @@ PRODUCT_BUNDLE_IDENTIFIER = im.vector.app; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "$(SRCROOT)/$(PRODUCT_NAME)/SupportingFiles/Riot-Bridging-Header.h"; - SWIFT_VERSION = 4.2; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; USER_HEADER_SEARCH_PATHS = "$(inherited)"; }; From fd8aff46e8e7813e2fb3166f3cc54594b9032823 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 19 Jun 2019 15:45:57 +0200 Subject: [PATCH 181/266] Update Reusable pod --- Podfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Podfile b/Podfile index a3b5996a7a..40d75d4090 100644 --- a/Podfile +++ b/Podfile @@ -60,7 +60,7 @@ end abstract_target 'RiotPods' do pod 'GBDeviceInfo', '~> 5.2.0' - pod 'Reusable', '~> 4.0' + pod 'Reusable', '~> 4.1' # Piwik for analytics pod 'MatomoTracker', '~> 6.0.1' From affaa9dfbdc61fb8ec9a60a943fa90d71d7e1b2a Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 19 Jun 2019 15:48:40 +0200 Subject: [PATCH 182/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 02d42666c2..4726fcb91e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -12,6 +12,7 @@ Improvements: * Message Editing: Annotate edited messages in timeline (#2400). * Message Editing: Editing in the timeline (#2404). * Read receipts: They are now counted at the MatrixKit level. + * Migrate to Swift 5.0. Bug fix: * Device Verification: Fix user display name and device id colors in dark theme From 74758f671a5f437138cf3bfe83efda9ce9d162c6 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 19 Jun 2019 16:03:40 +0200 Subject: [PATCH 183/266] Use CocoaPods 1.7.1 and update Podfile.lock --- Podfile.lock | 24 ++++++++--------- Riot.xcodeproj/project.pbxproj | 47 ++-------------------------------- 2 files changed, 14 insertions(+), 57 deletions(-) diff --git a/Podfile.lock b/Podfile.lock index 7f3b5e2b7b..07ae2a6643 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -45,7 +45,7 @@ PODS: - HPGrowingTextView (1.1) - JitsiMeetSDK (2.1.0) - libbase58 (0.1.4) - - libPhoneNumber-iOS (0.9.13) + - libPhoneNumber-iOS (0.9.15) - MatomoTracker (6.0.1): - MatomoTracker/Core (= 6.0.1) - MatomoTracker/Core (6.0.1) @@ -90,11 +90,11 @@ PODS: - Realm (3.13.1): - Realm/Headers (= 3.13.1) - Realm/Headers (3.13.1) - - Reusable (4.0.5): - - Reusable/Storyboard (= 4.0.5) - - Reusable/View (= 4.0.5) - - Reusable/Storyboard (4.0.5) - - Reusable/View (4.0.5) + - Reusable (4.1.0): + - Reusable/Storyboard (= 4.1.0) + - Reusable/View (= 4.1.0) + - Reusable/Storyboard (4.1.0) + - Reusable/View (4.1.0) - SwiftGen (6.1.0) - SwiftLint (0.30.1) - zxcvbn-ios (1.0.4) @@ -110,7 +110,7 @@ DEPENDENCIES: - MatrixSDK/JingleCallStack - MatrixSDK/SwiftSupport - OLMKit - - Reusable (~> 4.0) + - Reusable (~> 4.1) - SwiftGen (~> 6.1) - SwiftLint (~> 0.30.1) - zxcvbn-ios @@ -140,7 +140,7 @@ SPEC REPOS: SPEC CHECKSUMS: AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057 - cmark: ec0275215b504780287b6fca360224e384368af8 + cmark: 1d9ad0375e3b9fa281732e992467903606015520 DGCollectionViewLeftAlignFlowLayout: a0fa58797373ded039cafba8133e79373d048399 DTCoreText: e5d688cffc9f6a61eddd1a4f94e2046851230de3 DTFoundation: f03be9fd786f11e505bb8fc44e2a3732bf0917df @@ -149,17 +149,17 @@ SPEC CHECKSUMS: HPGrowingTextView: 88a716d97fb853bcb08a4a08e4727da17efc9b19 JitsiMeetSDK: 3e66564af7f38a19142338955dd7f581801852b3 libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd - libPhoneNumber-iOS: e444379ac18bbfbdefad571da735b2cd7e096caa + libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75 MatomoTracker: 3ae4f65a1f5ace8043bda7244888fee28a734de5 MatrixKit: 6f553797e1ad42794b5336afb5cecb975ec69daa MatrixSDK: ed0d0cee4877955052f19730bb3ee727e01ec948 OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639 Realm: 50071da38fe079e0735e47c9f2eae738c68c5996 - Reusable: 188be1a54ac0691bc66e5bb24ec6eb91971b315b + Reusable: 82be188f29d96dc5eff0db7b2393bcc08d2cdd5b SwiftGen: f872ca75cbd17bf7103c17f13dcfa0d9a15667b0 SwiftLint: a54bf1fe12b55c68560eb2a7689dfc81458508f7 zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c -PODFILE CHECKSUM: 16b6518b09d4e3af0af46ed9c1338e9df8674aff +PODFILE CHECKSUM: bc84fc2feab8daee9f835d846fa59a12534d846b -COCOAPODS: 1.6.1 +COCOAPODS: 1.7.1 diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 2be1cd8180..9612e42007 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -3679,54 +3679,11 @@ files = ( ); inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot-frameworks.sh", - "${BUILT_PRODUCTS_DIR}/AFNetworking/AFNetworking.framework", - "${BUILT_PRODUCTS_DIR}/DGCollectionViewLeftAlignFlowLayout/DGCollectionViewLeftAlignFlowLayout.framework", - "${BUILT_PRODUCTS_DIR}/DTCoreText/DTCoreText.framework", - "${BUILT_PRODUCTS_DIR}/DTFoundation/DTFoundation.framework", - "${BUILT_PRODUCTS_DIR}/GBDeviceInfo/GBDeviceInfo.framework", - "${BUILT_PRODUCTS_DIR}/GZIP/GZIP.framework", - "${BUILT_PRODUCTS_DIR}/HPGrowingTextView/HPGrowingTextView.framework", - "${PODS_ROOT}/JitsiMeetSDK/Frameworks/JitsiMeet.framework", - "${PODS_ROOT}/JitsiMeetSDK/Frameworks/WebRTC.framework", - "${BUILT_PRODUCTS_DIR}/MatomoTracker/MatomoTracker.framework", - "${BUILT_PRODUCTS_DIR}/MatrixKit/MatrixKit.framework", - "${BUILT_PRODUCTS_DIR}/MatrixSDK.common-JingleCallStack/MatrixSDK.framework", - "${BUILT_PRODUCTS_DIR}/OLMKit/OLMKit.framework", - "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework", - "${BUILT_PRODUCTS_DIR}/Reusable/Reusable.framework", - "${BUILT_PRODUCTS_DIR}/cmark/cmark.framework", - "${BUILT_PRODUCTS_DIR}/libPhoneNumber-iOS/libPhoneNumber_iOS.framework", - "${BUILT_PRODUCTS_DIR}/libbase58/libbase58.framework", - "${BUILT_PRODUCTS_DIR}/zxcvbn-ios/zxcvbn_ios.framework", - "${BUILT_PRODUCTS_DIR}/DTCoreText.default-Extension/DTCoreText.framework", - "${BUILT_PRODUCTS_DIR}/MatrixKit-AppExtension/MatrixKit.framework", + "${PODS_ROOT}/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/AFNetworking.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DGCollectionViewLeftAlignFlowLayout.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTCoreText.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/DTFoundation.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GBDeviceInfo.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GZIP.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/HPGrowingTextView.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/JitsiMeet.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/WebRTC.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MatomoTracker.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MatrixKit.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MatrixSDK.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/OLMKit.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Reusable.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/cmark.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libPhoneNumber_iOS.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libbase58.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/zxcvbn_ios.framework", + "${PODS_ROOT}/Target Support Files/Pods-RiotPods-Riot/Pods-RiotPods-Riot-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; From a27cae781cc346bb86312a2d79d9c482094ea5af Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 19 Jun 2019 16:16:30 +0200 Subject: [PATCH 184/266] Update SwiftLint pod. --- Podfile | 2 +- Podfile.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Podfile b/Podfile index 40d75d4090..fbab2ca18f 100644 --- a/Podfile +++ b/Podfile @@ -73,7 +73,7 @@ abstract_target 'RiotPods' do # Tools pod 'SwiftGen', '~> 6.1' - pod 'SwiftLint', '~> 0.30.1' + pod 'SwiftLint', '~> 0.33.0' target "Riot" do import_MatrixKit diff --git a/Podfile.lock b/Podfile.lock index 07ae2a6643..a55e8c78ee 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -96,7 +96,7 @@ PODS: - Reusable/Storyboard (4.1.0) - Reusable/View (4.1.0) - SwiftGen (6.1.0) - - SwiftLint (0.30.1) + - SwiftLint (0.33.0) - zxcvbn-ios (1.0.4) DEPENDENCIES: @@ -112,7 +112,7 @@ DEPENDENCIES: - OLMKit - Reusable (~> 4.1) - SwiftGen (~> 6.1) - - SwiftLint (~> 0.30.1) + - SwiftLint (~> 0.33.0) - zxcvbn-ios SPEC REPOS: @@ -157,9 +157,9 @@ SPEC CHECKSUMS: Realm: 50071da38fe079e0735e47c9f2eae738c68c5996 Reusable: 82be188f29d96dc5eff0db7b2393bcc08d2cdd5b SwiftGen: f872ca75cbd17bf7103c17f13dcfa0d9a15667b0 - SwiftLint: a54bf1fe12b55c68560eb2a7689dfc81458508f7 + SwiftLint: fed9c66336e41fc74dc48a73678380718f0c8b0e zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c -PODFILE CHECKSUM: bc84fc2feab8daee9f835d846fa59a12534d846b +PODFILE CHECKSUM: 072b76692c9c8cb162ce0267ad1bf0f179dd4d0d COCOAPODS: 1.7.1 From 9b1d2bc77e63a2927eba96fb6116044343b5b3b2 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 19 Jun 2019 16:48:16 +0200 Subject: [PATCH 185/266] Use CocoaPods 1.7.2 --- Gemfile | 2 +- Gemfile.lock | 60 ++++++++++++++++++++++++++-------------------------- Podfile.lock | 2 +- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/Gemfile b/Gemfile index 49ffe69fb4..1498a67105 100644 --- a/Gemfile +++ b/Gemfile @@ -2,7 +2,7 @@ source "https://rubygems.org" gem "xcode-install" gem "fastlane" -gem "cocoapods", '~>1.6.0' +gem "cocoapods", '~>1.7.2' plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile') eval_gemfile(plugins_path) if File.exist?(plugins_path) diff --git a/Gemfile.lock b/Gemfile.lock index 176b06f105..62c17a244a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,7 @@ GEM remote: https://rubygems.org/ specs: CFPropertyList (3.0.0) - activesupport (4.2.11) + activesupport (4.2.11.1) i18n (~> 0.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) @@ -12,11 +12,11 @@ GEM atomos (0.1.3) babosa (1.0.2) claide (1.0.2) - cocoapods (1.6.1) + cocoapods (1.7.2) activesupport (>= 4.0.2, < 5) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.6.1) - cocoapods-deintegrate (>= 1.0.2, < 2.0) + cocoapods-core (= 1.7.2) + cocoapods-deintegrate (>= 1.0.3, < 2.0) cocoapods-downloader (>= 1.2.2, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) @@ -25,17 +25,17 @@ GEM cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) escape (~> 0.0.4) - fourflusher (>= 2.2.0, < 3.0) + fourflusher (>= 2.3.0, < 3.0) gh_inspector (~> 1.0) molinillo (~> 0.6.6) nap (~> 1.0) ruby-macho (~> 1.4) - xcodeproj (>= 1.8.1, < 2.0) - cocoapods-core (1.6.1) + xcodeproj (>= 1.10.0, < 2.0) + cocoapods-core (1.7.2) activesupport (>= 4.0.2, < 6) fuzzy_match (~> 2.0.4) nap (~> 1.0) - cocoapods-deintegrate (1.0.3) + cocoapods-deintegrate (1.0.4) cocoapods-downloader (1.2.2) cocoapods-plugins (1.0.0) nap @@ -49,16 +49,16 @@ GEM colored2 (3.1.2) commander-fastlane (4.4.6) highline (~> 1.7.2) - concurrent-ruby (1.1.4) + concurrent-ruby (1.1.5) declarative (0.0.10) declarative-option (0.1.0) digest-crc (0.4.1) domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.1) + dotenv (2.7.2) emoji_regex (1.0.1) escape (0.0.4) - excon (0.62.0) + excon (0.64.0) faraday (0.15.4) multipart-post (>= 1.2, < 3) faraday-cookie_jar (0.0.6) @@ -67,7 +67,7 @@ GEM faraday_middleware (0.13.1) faraday (>= 0.7.4, < 1.0) fastimage (2.1.5) - fastlane (2.116.1) + fastlane (2.125.2) CFPropertyList (>= 2.3, < 4.0.0) addressable (>= 2.3, < 3.0.0) babosa (>= 1.0.2, < 2.0.0) @@ -86,8 +86,8 @@ GEM google-cloud-storage (>= 1.15.0, < 2.0.0) highline (>= 1.7.2, < 2.0.0) json (< 3.0.0) + jwt (~> 2.1.0) mini_magick (~> 4.5.1) - multi_json multi_xml (~> 0.5) multipart-post (~> 2.0.0) plist (>= 3.1.0, < 4.0.0) @@ -96,16 +96,16 @@ GEM security (= 0.1.3) simctl (~> 1.6.3) slack-notifier (>= 2.0.0, < 3.0.0) - terminal-notifier (>= 1.6.2, < 2.0.0) + terminal-notifier (>= 2.0.0, < 3.0.0) terminal-table (>= 1.4.5, < 2.0.0) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) - xcodeproj (>= 1.6.0, < 2.0.0) + xcodeproj (>= 1.8.1, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) fastlane-plugin-versioning (0.3.4) - fourflusher (2.2.0) + fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) google-api-client (0.23.9) @@ -118,7 +118,7 @@ GEM signet (~> 0.9) google-cloud-core (1.3.0) google-cloud-env (~> 1.0) - google-cloud-env (1.0.5) + google-cloud-env (1.1.0) faraday (~> 0.11) google-cloud-storage (1.16.0) digest-crc (~> 0.4) @@ -143,7 +143,7 @@ GEM memoist (0.16.0) mime-types (3.2.2) mime-types-data (~> 3.2015) - mime-types-data (3.2018.0812) + mime-types-data (3.2019.0331) mini_magick (4.5.1) minitest (5.11.3) molinillo (0.6.6) @@ -154,7 +154,7 @@ GEM nap (1.1.0) naturally (2.2.0) netrc (0.11.0) - os (1.0.0) + os (1.0.1) plist (3.5.0) public_suffix (2.0.5) representable (3.0.4) @@ -164,7 +164,7 @@ GEM retriable (3.1.2) rouge (2.0.7) ruby-macho (1.4.0) - rubyzip (1.2.2) + rubyzip (1.2.3) security (0.1.3) signet (0.11.0) addressable (~> 2.3) @@ -175,26 +175,26 @@ GEM CFPropertyList naturally slack-notifier (2.3.2) - terminal-notifier (1.8.0) + terminal-notifier (2.0.0) terminal-table (1.8.0) unicode-display_width (~> 1.1, >= 1.1.1) thread_safe (0.3.6) - tty-cursor (0.6.0) - tty-screen (0.6.5) - tty-spinner (0.9.0) - tty-cursor (~> 0.6.0) + tty-cursor (0.7.0) + tty-screen (0.7.0) + tty-spinner (0.9.1) + tty-cursor (~> 0.7) tzinfo (1.2.5) thread_safe (~> 0.1) uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.5) - unicode-display_width (1.4.1) + unf_ext (0.0.7.6) + unicode-display_width (1.6.0) word_wrap (1.0.0) xcode-install (2.5.0) claide (>= 0.9.1, < 1.1.0) fastlane (>= 2.1.0, < 3.0.0) - xcodeproj (1.8.1) + xcodeproj (1.10.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) @@ -209,10 +209,10 @@ PLATFORMS ruby DEPENDENCIES - cocoapods (~> 1.6.0) + cocoapods (~> 1.7.2) fastlane fastlane-plugin-versioning xcode-install BUNDLED WITH - 1.17.3 + 2.0.2 diff --git a/Podfile.lock b/Podfile.lock index a55e8c78ee..1b8bd8dfce 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -162,4 +162,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: 072b76692c9c8cb162ce0267ad1bf0f179dd4d0d -COCOAPODS: 1.7.1 +COCOAPODS: 1.7.2 From 98361afa6f2b6aa4c1a8370bb02a827e8ef5b705 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 20 Jun 2019 12:07:04 +0200 Subject: [PATCH 186/266] e2e room message: Fix padlock position on last message with sender name. --- Riot/Modules/Room/CellData/RoomBubbleCellData.m | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 43eb3903d7..9a0d8b8fee 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -270,8 +270,10 @@ - (void)refreshBubbleComponentsPosition @synchronized(bubbleComponents) { + NSInteger bubbleComponentsCount = bubbleComponents.count; + // Check whether there is at least one component. - if (bubbleComponents.count) + if (bubbleComponentsCount) { // Set position of the first component CGFloat positionY = (self.attachment == nil || self.attachment.type == MXKAttachmentTypeFile || self.attachment.type == MXKAttachmentTypeAudio) ? MXKROOMBUBBLECELLDATA_TEXTVIEW_DEFAULT_VERTICAL_INSET : 0; @@ -279,7 +281,7 @@ - (void)refreshBubbleComponentsPosition NSUInteger index = 0; // Use same position for first components without render (redacted) - for (; index < bubbleComponents.count; index++) + for (; index < bubbleComponentsCount; index++) { // Compute the vertical position for next component component = bubbleComponents[index]; @@ -293,13 +295,14 @@ - (void)refreshBubbleComponentsPosition } // Check whether the position of other components need to be refreshed - if (!self.attachment && index < bubbleComponents.count) + if (!self.attachment && index < bubbleComponentsCount) { NSMutableAttributedString *attributedString = [NSMutableAttributedString new]; NSInteger selectedComponentIndex = self.selectedComponentIndex; NSInteger lastMessageIndex = self.containsLastMessage ? self.mostRecentComponentIndex : NSNotFound; + NSInteger visibleMessageIndex = 0; - for (index = 0; index < bubbleComponents.count; index++) + for (; index < bubbleComponentsCount; index++) { // Compute the vertical position for next component component = bubbleComponents[index]; @@ -310,7 +313,9 @@ - (void)refreshBubbleComponentsPosition NSAttributedString *componentString = component.attributedTextMessage; // Check whether the timestamp is displayed for this component, and check whether a vertical whitespace is required - if ((selectedComponentIndex == index && self.addVerticalWhitespaceForSelectedComponentTimestamp) || lastMessageIndex == index) + + if (((selectedComponentIndex == index && self.addVerticalWhitespaceForSelectedComponentTimestamp) || lastMessageIndex == index) + && !(visibleMessageIndex == 0 && !(self.shouldHideSenderInformation || self.shouldHideSenderName))) { [attributedString appendAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]]; } @@ -330,6 +335,8 @@ - (void)refreshBubbleComponentsPosition [self addVerticalWhitespaceToString:attributedString forEvent:component.event.eventId]; [attributedString appendAttributedString:[MXKRoomBubbleCellDataWithAppendingMode messageSeparator]]; + + visibleMessageIndex++; } else { From afd37fa9fba829b86b203338a8aba5fb4c19b092 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 21 Jun 2019 10:23:10 +0200 Subject: [PATCH 187/266] Integrations: Make code support use of an integration manager other than modular/scalar. The default value is still modular/scalar. --- Riot.xcodeproj/project.pbxproj | 4 + Riot/Managers/Widgets/Widget.m | 3 +- Riot/Managers/Widgets/WidgetManager.h | 30 +++- Riot/Managers/Widgets/WidgetManager.m | 131 ++++++++++++++---- .../Widgets/WidgetManagerConfig.swift | 77 ++++++++++ .../IntegrationManagerViewController.m | 6 +- .../Widgets/WidgetViewController.m | 2 +- 7 files changed, 223 insertions(+), 30 deletions(-) create mode 100644 Riot/Managers/Widgets/WidgetManagerConfig.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 9612e42007..6f8412cefb 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 32242F1721E8FBE500725742 /* Theme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0D21E8FBA900725742 /* Theme.swift */; }; 32242F1821E8FBF800725742 /* DefaultTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F0F21E8FBA900725742 /* DefaultTheme.swift */; }; 32242F1921E8FBFB00725742 /* DarkTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32242F1021E8FBA900725742 /* DarkTheme.swift */; }; + 322C110822BBC6F80043FEAC /* WidgetManagerConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322C110722BBC6F80043FEAC /* WidgetManagerConfig.swift */; }; 3232AB1422564D9100AD6A5C /* swiftgen-config.yml in Resources */ = {isa = PBXBuildFile; fileRef = 3232AB0022564D9100AD6A5C /* swiftgen-config.yml */; }; 3232AB1522564D9100AD6A5C /* flat-swift4-vector.stencil in Resources */ = {isa = PBXBuildFile; fileRef = 3232AB0322564D9100AD6A5C /* flat-swift4-vector.stencil */; }; 3232AB2122564D9100AD6A5C /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 3232AB1322564D9100AD6A5C /* README.md */; }; @@ -548,6 +549,7 @@ 32242F0F21E8FBA900725742 /* DefaultTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultTheme.swift; sourceTree = ""; }; 32242F1021E8FBA900725742 /* DarkTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DarkTheme.swift; sourceTree = ""; }; 32242F1121E8FBA900725742 /* ThemeService.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThemeService.h; sourceTree = ""; }; + 322C110722BBC6F80043FEAC /* WidgetManagerConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetManagerConfig.swift; sourceTree = ""; }; 3232AB0022564D9100AD6A5C /* swiftgen-config.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "swiftgen-config.yml"; sourceTree = ""; }; 3232AB0322564D9100AD6A5C /* flat-swift4-vector.stencil */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "flat-swift4-vector.stencil"; sourceTree = ""; }; 3232AB1322564D9100AD6A5C /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; @@ -3042,6 +3044,7 @@ B1B5598420EFC3DF00210D55 /* WidgetManager.m */, B1B5598120EFC3DF00210D55 /* Widget.h */, B1B5598320EFC3DF00210D55 /* Widget.m */, + 322C110722BBC6F80043FEAC /* WidgetManagerConfig.swift */, ); path = Widgets; sourceTree = ""; @@ -3987,6 +3990,7 @@ B1B5593C20EF7BAC00210D55 /* TableViewCellWithCheckBoxes.m in Sources */, 32891D6B2264CBA300C82226 /* SimpleScreenTemplateViewController.swift in Sources */, B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */, + 322C110822BBC6F80043FEAC /* WidgetManagerConfig.swift in Sources */, F0D2ADA11F6AA5FD00A7097D /* MXRoomSummary+Riot.m in Sources */, B1B5596F20EFA85D00210D55 /* EncryptionInfoView.m in Sources */, B1B5573820EE6C4D00210D55 /* GroupParticipantsViewController.m in Sources */, diff --git a/Riot/Managers/Widgets/Widget.m b/Riot/Managers/Widgets/Widget.m index 482f54e9c9..1438701b3b 100644 --- a/Riot/Managers/Widgets/Widget.m +++ b/Riot/Managers/Widgets/Widget.m @@ -1,5 +1,6 @@ /* Copyright 2017 Vector Creations Ltd + Copyright 2019 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. @@ -100,7 +101,7 @@ - (MXHTTPOperation *)widgetUrl:(void (^)(NSString * _Nonnull))success failure:(v _widgetId]]; // Check if their scalar token must added - if ([WidgetManager isScalarUrl:widgetUrl]) + if ([[WidgetManager sharedManager] isScalarUrl:widgetUrl forUser:userId]) { return [[WidgetManager sharedManager] getScalarTokenForMXSession:_mxSession validate:NO success:^(NSString *scalarToken) { // Add the user scalar token diff --git a/Riot/Managers/Widgets/WidgetManager.h b/Riot/Managers/Widgets/WidgetManager.h index 83b41ac2fd..7f94977f72 100644 --- a/Riot/Managers/Widgets/WidgetManager.h +++ b/Riot/Managers/Widgets/WidgetManager.h @@ -1,5 +1,6 @@ /* Copyright 2017 Vector Creations Ltd + Copyright 2019 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. @@ -20,6 +21,8 @@ #import "Widget.h" +@class WidgetManagerConfig; + /** The type of matrix event used for matrix widgets. */ @@ -180,6 +183,30 @@ WidgetManagerErrorCode; #pragma mark - Modular interface +/** + Get the integration manager configuration for a user. + + @param userId the user id. + @return the integration manager configuration. + */ +- (WidgetManagerConfig*)configForUser:(NSString*)userId; + +/** + Store the integration manager configuration for a user. + + @param the integration manager configuration. + @param userId the user id. + */ +- (void)setConfig:(WidgetManagerConfig*)config forUser:(NSString*)userId; + +/** + Check if the user has URLs for an integration manager configured. + + @param userId the user id. + @return YES if they have URLs for an integration manager. + */ +- (BOOL)hasIntegrationManagerForUser:(NSString*)userId; + /** Make sure there is a scalar token for the given Matrix session. @@ -201,8 +228,9 @@ WidgetManagerErrorCode; Returns true if specified url is a scalar URL, typically https://scalar.vector.im/api @param urlString the URL to check. + @param userId the user id. @return YES if specified URL is a scalar URL. */ -+ (BOOL)isScalarUrl:(NSString*)urlString; +- (BOOL)isScalarUrl:(NSString*)urlString forUser:(NSString*)userId; @end diff --git a/Riot/Managers/Widgets/WidgetManager.m b/Riot/Managers/Widgets/WidgetManager.m index 8f761bf080..683559abc5 100644 --- a/Riot/Managers/Widgets/WidgetManager.m +++ b/Riot/Managers/Widgets/WidgetManager.m @@ -1,5 +1,6 @@ /* Copyright 2017 Vector Creations Ltd + Copyright 2019 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. @@ -16,6 +17,8 @@ #import "WidgetManager.h" +#import "Riot-Swift.h" + #import #pragma mark - Contants @@ -46,7 +49,7 @@ @interface WidgetManager () NSMutableDictionary*> *failureBlockForWidgetCreation; // User id -> scalar token - NSMutableDictionary *scalarTokens; + NSMutableDictionary *configs; } @end @@ -74,12 +77,7 @@ - (instancetype)init successBlockForWidgetCreation = [NSMutableDictionary dictionary]; failureBlockForWidgetCreation = [NSMutableDictionary dictionary]; - [self load]; - - if (!scalarTokens) - { - scalarTokens = [NSMutableDictionary dictionary]; - } + [self loadConfigs]; } return self; } @@ -261,6 +259,15 @@ - (MXHTTPOperation *)createJitsiWidgetInRoom:(MXRoom*)room success:(void (^)(Widget *jitsiWidget))success failure:(void (^)(NSError *error))failure { + NSString *userId = room.mxSession.myUser.userId; + WidgetManagerConfig *config = [self configForUser:userId]; + if (!config.hasUrls) + { + NSLog(@"[WidgetManager] createJitsiWidgetInRoom: Error: no Integrations Manager API URL for user %@", userId); + failure(nil); + return nil; + } + // Build data for a jitsi widget NSString *widgetId = [NSString stringWithFormat:@"%@_%@_%@", kWidgetTypeJitsi, room.mxSession.myUser.userId, @((uint64_t)([[NSDate date] timeIntervalSince1970] * 1000))]; @@ -274,8 +281,7 @@ - (MXHTTPOperation *)createJitsiWidgetInRoom:(MXRoom*)room // TODO: This url should come from modular API // Note: this url can be used as is inside a web container (like iframe for Riot-web) // Riot-iOS does not directly use it but extracts params from it (see `[JitsiViewController openWidget:withVideo:]`) - NSString *modularRestUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsRestUrl"]; - NSString *url = [NSString stringWithFormat:@"%@/widgets/jitsi.html?confId=%@&isAudioConf=%@&displayName=$matrix_display_name&avatarUrl=$matrix_avatar_url&email=$matrix_user_id@", modularRestUrl, confId, video ? @"false" : @"true"]; + NSString *url = [NSString stringWithFormat:@"%@/widgets/jitsi.html?confId=%@&isAudioConf=%@&displayName=$matrix_display_name&avatarUrl=$matrix_avatar_url&email=$matrix_user_id@", config.apiUrl, confId, video ? @"false" : @"true"]; return [self createWidget:widgetId withContent:@{ @@ -435,12 +441,30 @@ - (void)removeMatrixSession:(MXSession *)mxSession - (void)deleteDataForUser:(NSString *)userId { - [scalarTokens removeObjectForKey:userId]; - [self save]; + [configs removeObjectForKey:userId]; + [self saveConfigs]; } #pragma mark - Modular interface +- (WidgetManagerConfig*)configForUser:(NSString*)userId +{ + // Return a default config by default + return configs[userId] ? configs[userId] : [WidgetManagerConfig new]; +} + +- (BOOL)hasIntegrationManagerForUser:(NSString*)userId +{ + return [self configForUser:userId].hasUrls; +} + +- (void)setConfig:(WidgetManagerConfig*)config forUser:(NSString*)userId +{ + configs[userId] = config; + [self saveConfigs]; +} + + - (MXHTTPOperation *)getScalarTokenForMXSession:(MXSession*)mxSession validate:(BOOL)validate success:(void (^)(NSString *scalarToken))success @@ -487,15 +511,22 @@ - (MXHTTPOperation *)registerForScalarToken:(MXSession*)mxSession failure:(void (^)(NSError *error))failure { MXHTTPOperation *operation; + NSString *userId = mxSession.myUser.userId; + + WidgetManagerConfig *config = [self configForUser:userId]; + if (!config.hasUrls) + { + NSLog(@"[WidgetManager] registerForScalarToken: Error: no Integrations Manager API URL for user %@", mxSession.myUser.userId); + failure(nil); + return nil; + } MXWeakify(self); operation = [mxSession.matrixRestClient openIdToken:^(MXOpenIdToken *tokenObject) { MXStrongifyAndReturnIfNil(self); // Exchange the token for a scalar token - NSString *modularRestUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsRestUrl"]; - - MXHTTPClient *httpClient = [[MXHTTPClient alloc] initWithBaseURL:modularRestUrl andOnUnrecognizedCertificateBlock:nil]; + MXHTTPClient *httpClient = [[MXHTTPClient alloc] initWithBaseURL:config.apiUrl andOnUnrecognizedCertificateBlock:nil]; MXHTTPOperation *operation2 = [httpClient requestWithMethod:@"POST" @@ -506,9 +537,10 @@ - (MXHTTPOperation *)registerForScalarToken:(MXSession*)mxSession NSString *scalarToken; MXJSONModelSetString(scalarToken, JSONResponse[@"scalar_token"]) - self->scalarTokens[mxSession.myUser.userId] = scalarToken; + config.scalarToken = scalarToken; - [self save]; + self->configs[userId] = config; + [self saveConfigs]; if (success) { @@ -542,8 +574,17 @@ - (MXHTTPOperation *)validateScalarToken:(NSString*)scalarToken forMXSession:(MX complete:(void (^)(BOOL valid))complete failure:(void (^)(NSError *error))failure { - NSString *modularRestUrl = [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsRestUrl"]; - MXHTTPClient *httpClient = [[MXHTTPClient alloc] initWithBaseURL:modularRestUrl andOnUnrecognizedCertificateBlock:nil]; + NSString *userId = mxSession.myUser.userId; + + WidgetManagerConfig *config = [self configForUser:userId]; + if (!config.hasUrls) + { + NSLog(@"[WidgetManager] validateScalarToken: Error: no Integrations Manager API URL for user %@", mxSession.myUser.userId); + failure(nil); + return nil; + } + + MXHTTPClient *httpClient = [[MXHTTPClient alloc] initWithBaseURL:config.apiUrl andOnUnrecognizedCertificateBlock:nil]; return [httpClient requestWithMethod:@"GET" path:[NSString stringWithFormat:@"account?v=1.1&scalar_token=%@", scalarToken] @@ -579,14 +620,19 @@ - (MXHTTPOperation *)validateScalarToken:(NSString*)scalarToken forMXSession:(MX }]; } -+ (BOOL)isScalarUrl:(NSString *)urlString +- (BOOL)isScalarUrl:(NSString *)urlString forUser:(NSString*)userId { BOOL isScalarUrl = NO; + // TODO: Do we need to add `integrationsWidgetsUrls` to `WidgetManagerConfig`? NSArray *scalarUrlStrings = [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsWidgetsUrls"]; if (scalarUrlStrings.count == 0) { - scalarUrlStrings = @[[[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsRestUrl"]]; + NSString *apiUrl = [self configForUser:userId].apiUrl; + if (apiUrl) + { + scalarUrlStrings = @[apiUrl]; + } } for (NSString *scalarUrlString in scalarUrlStrings) @@ -605,20 +651,53 @@ + (BOOL)isScalarUrl:(NSString *)urlString - (NSString *)scalarTokenForMXSession:(MXSession *)mxSession { - return scalarTokens[mxSession.myUser.userId]; + return configs[mxSession.myUser.userId].scalarToken; } -- (void)load +- (void)loadConfigs { NSUserDefaults *userDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults; - scalarTokens = [NSMutableDictionary dictionaryWithDictionary:[userDefaults objectForKey:@"scalarTokens"]]; + + NSDictionary *scalarTokens = [userDefaults objectForKey:@"scalarTokens"]; + if (scalarTokens) + { + // Manage migration to WidgetManagerConfig + configs = [NSMutableDictionary dictionary]; + for (NSString *userId in scalarTokens) + { + NSString *scalarToken = scalarTokens[userId]; + + NSLog(@"[WidgetManager] migrate scalarTokens to integrationManagerConfigs for %@", userId); + + WidgetManagerConfig *config = [WidgetManagerConfig new]; + config.scalarToken = scalarToken; + + configs[userId] = config; + } + + [self saveConfigs]; + [userDefaults removeObjectForKey:@"scalarTokens"]; + } + else + { + NSData *configsData = [userDefaults objectForKey:@"integrationManagerConfigs"]; + if (configsData) + { + configs = [NSMutableDictionary dictionaryWithDictionary:[NSKeyedUnarchiver unarchiveObjectWithData:configsData]]; + } + + if (!configs) + { + configs = [NSMutableDictionary dictionary]; + } + } } -- (void)save +- (void)saveConfigs { NSUserDefaults *userDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults; - - [userDefaults setObject:scalarTokens forKey:@"scalarTokens"]; + [userDefaults setObject:[NSKeyedArchiver archivedDataWithRootObject:configs] + forKey:@"integrationManagerConfigs"]; } @end diff --git a/Riot/Managers/Widgets/WidgetManagerConfig.swift b/Riot/Managers/Widgets/WidgetManagerConfig.swift new file mode 100644 index 0000000000..ba6c3ac36a --- /dev/null +++ b/Riot/Managers/Widgets/WidgetManagerConfig.swift @@ -0,0 +1,77 @@ +/* + Copyright 2019 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 + +/// Configuration for an integration manager. +/// By default, it uses URLs defined in the app settings but they can be overidden. +@objcMembers +class WidgetManagerConfig: NSObject, NSCoding { + + /// The URL for the REST api + let apiUrl: NSString? + /// The URL of the integration manager interface + let uiUrl: NSString? + /// The token if the user has been authenticated + var scalarToken: NSString? + + var hasUrls: Bool { + if apiUrl != nil && uiUrl != nil { + return true + } else { + return false + } + } + + init(apiUrl: NSString?, uiUrl: NSString?) { + self.apiUrl = apiUrl + self.uiUrl = uiUrl + + super.init() + } + + override convenience init () { + // Use app settings as default + let apiUrl = UserDefaults.standard.object(forKey: "integrationsRestUrl") as? NSString + let uiUrl = UserDefaults.standard.object(forKey: "integrationsUiUrl") as? NSString + + self.init(apiUrl: apiUrl, uiUrl: uiUrl) + } + + + /// MARK: - NSCoding + + enum CodingKeys: String { + case apiUrl = "apiUrl" + case uiUrl = "uiUrl" + case scalarToken = "scalarToken" + } + + func encode(with aCoder: NSCoder) { + aCoder.encode(self.apiUrl, forKey: CodingKeys.apiUrl.rawValue) + aCoder.encode(self.uiUrl, forKey: CodingKeys.uiUrl.rawValue) + aCoder.encode(self.scalarToken, forKey: CodingKeys.scalarToken.rawValue) + } + + convenience required init?(coder aDecoder: NSCoder) { + let apiUrl = aDecoder.decodeObject(forKey: CodingKeys.apiUrl.rawValue) as? NSString + let uiUrl = aDecoder.decodeObject(forKey: CodingKeys.uiUrl.rawValue) as? NSString + let scalarToken = aDecoder.decodeObject(forKey: CodingKeys.scalarToken.rawValue) as? NSString + + self.init(apiUrl: apiUrl, uiUrl: uiUrl) + self.scalarToken = scalarToken + } +} diff --git a/Riot/Modules/Integrations/IntegrationManagerViewController.m b/Riot/Modules/Integrations/IntegrationManagerViewController.m index 37bab65fa7..3db2abbc9b 100644 --- a/Riot/Modules/Integrations/IntegrationManagerViewController.m +++ b/Riot/Modules/Integrations/IntegrationManagerViewController.m @@ -1,5 +1,6 @@ /* Copyright 2017 Vector Creations Ltd + Copyright 2019 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. @@ -17,6 +18,7 @@ #import "IntegrationManagerViewController.h" #import "WidgetManager.h" +#import "Riot-Swift.h" NSString *const kIntegrationManagerMainScreen = nil; NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ"; @@ -100,10 +102,12 @@ - (NSString *)interfaceUrl { NSMutableString *url; + NSString *integrationsUiUrl = [[WidgetManager sharedManager] configForUser:mxSession.myUser.userId].uiUrl; + if (scalarToken) { url = [NSMutableString stringWithFormat:@"%@?scalar_token=%@&room_id=%@", - [[NSUserDefaults standardUserDefaults] objectForKey:@"integrationsUiUrl"], + integrationsUiUrl, [MXTools encodeURIComponent:scalarToken], [MXTools encodeURIComponent:roomId] ]; diff --git a/Riot/Modules/Integrations/Widgets/WidgetViewController.m b/Riot/Modules/Integrations/Widgets/WidgetViewController.m index f67456292f..f7ad5c0bdd 100644 --- a/Riot/Modules/Integrations/Widgets/WidgetViewController.m +++ b/Riot/Modules/Integrations/Widgets/WidgetViewController.m @@ -190,7 +190,7 @@ - (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNaviga NSLog(@"[WidgetVC] decidePolicyForNavigationResponse: statusCode: %@", @(response.statusCode)); } - if (response.statusCode == 403 && [WidgetManager isScalarUrl:self.URL]) + if (response.statusCode == 403 && [[WidgetManager sharedManager] isScalarUrl:self.URL forUser:self.widget.mxSession.myUser.userId]) { [self fixScalarToken]; } From 948105b03d7d17ef8ffbaeddec9c200682fa1443 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 21 Jun 2019 12:12:32 +0200 Subject: [PATCH 188/266] Integrations: Manage the error of no configured integrations server --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 ++++ Riot/Managers/Widgets/WidgetManager.h | 3 ++- Riot/Managers/Widgets/WidgetManager.m | 16 +++++++++++++--- .../IntegrationManagerViewController.m | 8 ++++++++ 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index e225169c64..b783eae176 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -634,6 +634,7 @@ "bug_report_send" = "Send"; // Widget +"widget_no_integrations_server_configured" = "No integrations server configured"; "widget_no_power_to_manage" = "You need permission to manage widgets in this room"; "widget_creation_failure" = "Widget creation has failed"; "widget_sticker_picker_no_stickerpacks_alert" = "You don't currently have any stickerpacks enabled."; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 8e7aa57b31..610984a569 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2834,6 +2834,10 @@ internal enum VectorL10n { internal static var widgetIntegrationUnableToCreate: String { return VectorL10n.tr("Vector", "widget_integration_unable_to_create") } + /// No integrations server configured + internal static var widgetNoIntegrationsServerConfigured: String { + return VectorL10n.tr("Vector", "widget_no_integrations_server_configured") + } /// You need permission to manage widgets in this room internal static var widgetNoPowerToManage: String { return VectorL10n.tr("Vector", "widget_no_power_to_manage") diff --git a/Riot/Managers/Widgets/WidgetManager.h b/Riot/Managers/Widgets/WidgetManager.h index 7f94977f72..008a48c19e 100644 --- a/Riot/Managers/Widgets/WidgetManager.h +++ b/Riot/Managers/Widgets/WidgetManager.h @@ -54,7 +54,8 @@ FOUNDATION_EXPORT NSString *const WidgetManagerErrorDomain; typedef enum : NSUInteger { WidgetManagerErrorCodeNotEnoughPower, - WidgetManagerErrorCodeCreationFailed + WidgetManagerErrorCodeCreationFailed, + WidgetManagerErrorCodeNoIntegrationsServerConfigured } WidgetManagerErrorCode; diff --git a/Riot/Managers/Widgets/WidgetManager.m b/Riot/Managers/Widgets/WidgetManager.m index 683559abc5..d10c459953 100644 --- a/Riot/Managers/Widgets/WidgetManager.m +++ b/Riot/Managers/Widgets/WidgetManager.m @@ -264,7 +264,7 @@ - (MXHTTPOperation *)createJitsiWidgetInRoom:(MXRoom*)room if (!config.hasUrls) { NSLog(@"[WidgetManager] createJitsiWidgetInRoom: Error: no Integrations Manager API URL for user %@", userId); - failure(nil); + failure(self.errorForNonConfiguredIntegrationManager); return nil; } @@ -517,7 +517,7 @@ - (MXHTTPOperation *)registerForScalarToken:(MXSession*)mxSession if (!config.hasUrls) { NSLog(@"[WidgetManager] registerForScalarToken: Error: no Integrations Manager API URL for user %@", mxSession.myUser.userId); - failure(nil); + failure(self.errorForNonConfiguredIntegrationManager); return nil; } @@ -580,7 +580,7 @@ - (MXHTTPOperation *)validateScalarToken:(NSString*)scalarToken forMXSession:(MX if (!config.hasUrls) { NSLog(@"[WidgetManager] validateScalarToken: Error: no Integrations Manager API URL for user %@", mxSession.myUser.userId); - failure(nil); + failure(self.errorForNonConfiguredIntegrationManager); return nil; } @@ -700,4 +700,14 @@ - (void)saveConfigs forKey:@"integrationManagerConfigs"]; } + +#pragma mark - Errors + +- (NSError*)errorForNonConfiguredIntegrationManager +{ + return [NSError errorWithDomain:WidgetManagerErrorDomain + code:WidgetManagerErrorCodeNoIntegrationsServerConfigured + userInfo:@{NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"widget_no_integrations_server_configured", @"Vector", nil)}]; +} + @end diff --git a/Riot/Modules/Integrations/IntegrationManagerViewController.m b/Riot/Modules/Integrations/IntegrationManagerViewController.m index 3db2abbc9b..ff9df08fe0 100644 --- a/Riot/Modules/Integrations/IntegrationManagerViewController.m +++ b/Riot/Modules/Integrations/IntegrationManagerViewController.m @@ -18,6 +18,8 @@ #import "IntegrationManagerViewController.h" #import "WidgetManager.h" + +#import "AppDelegate.h" #import "Riot-Swift.h" NSString *const kIntegrationManagerMainScreen = nil; @@ -87,8 +89,14 @@ - (void)viewWillAppear:(BOOL)animated } failure:^(NSError *error) { MXStrongifyAndReturnIfNil(self); + NSLog(@"[IntegraionManagerVS] Cannot open due to missing scalar token. Error: %@", error); + self->operation = nil; [self stopActivityIndicator]; + + [self withdrawViewControllerAnimated:YES completion:^{ + [[AppDelegate theDelegate] showErrorAsAlert:error]; + }]; }]; } } From 918adc5b7a04ca9829f66f4d2582c7a24fb8a27e Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 21 Jun 2019 15:06:41 +0200 Subject: [PATCH 189/266] Integrations: Manage the error of provided integrations server not responding --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 ++++ Riot/Managers/Widgets/WidgetManager.h | 3 ++- Riot/Managers/Widgets/WidgetManager.m | 9 ++++++++- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index b783eae176..f65459c334 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -635,6 +635,7 @@ // Widget "widget_no_integrations_server_configured" = "No integrations server configured"; +"widget_integrations_server_failed_to_connect" = "Failed to connect to integrations server"; "widget_no_power_to_manage" = "You need permission to manage widgets in this room"; "widget_creation_failure" = "Widget creation has failed"; "widget_sticker_picker_no_stickerpacks_alert" = "You don't currently have any stickerpacks enabled."; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 610984a569..89914b8e94 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -2834,6 +2834,10 @@ internal enum VectorL10n { internal static var widgetIntegrationUnableToCreate: String { return VectorL10n.tr("Vector", "widget_integration_unable_to_create") } + /// Failed to connect to integrations server + internal static var widgetIntegrationsServerFailedToConnect: String { + return VectorL10n.tr("Vector", "widget_integrations_server_failed_to_connect") + } /// No integrations server configured internal static var widgetNoIntegrationsServerConfigured: String { return VectorL10n.tr("Vector", "widget_no_integrations_server_configured") diff --git a/Riot/Managers/Widgets/WidgetManager.h b/Riot/Managers/Widgets/WidgetManager.h index 008a48c19e..a159287891 100644 --- a/Riot/Managers/Widgets/WidgetManager.h +++ b/Riot/Managers/Widgets/WidgetManager.h @@ -55,7 +55,8 @@ typedef enum : NSUInteger { WidgetManagerErrorCodeNotEnoughPower, WidgetManagerErrorCodeCreationFailed, - WidgetManagerErrorCodeNoIntegrationsServerConfigured + WidgetManagerErrorCodeNoIntegrationsServerConfigured, + WidgetManagerErrorCodeFailedToConnectToIntegrationsServer } WidgetManagerErrorCode; diff --git a/Riot/Managers/Widgets/WidgetManager.m b/Riot/Managers/Widgets/WidgetManager.m index d10c459953..a6d2bdec16 100644 --- a/Riot/Managers/Widgets/WidgetManager.m +++ b/Riot/Managers/Widgets/WidgetManager.m @@ -548,10 +548,17 @@ - (MXHTTPOperation *)registerForScalarToken:(MXSession*)mxSession } } failure:^(NSError *error) { - NSLog(@"[WidgetManager] registerForScalarToken. Error in modular/register request"); + NSLog(@"[WidgetManager] registerForScalarToken: Failed to register. Error: %@", error); if (failure) { + // Specialise the error + NSError *error = [NSError errorWithDomain:WidgetManagerErrorDomain + code:WidgetManagerErrorCodeFailedToConnectToIntegrationsServer + userInfo:@{ + NSLocalizedDescriptionKey: NSLocalizedStringFromTable(@"widget_integrations_server_failed_to_connect", @"Vector", nil) + }]; + failure(error); } }]; From 330f7a03bd81dcfdc7adfb3fc841d2794fbb7aed Mon Sep 17 00:00:00 2001 From: Osoitz Date: Sun, 23 Jun 2019 12:55:38 +0000 Subject: [PATCH 190/266] Translated using Weblate (Basque) Currently translated at 100.0% (711 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eu/ --- Riot/Assets/eu.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index 32f5706dc0..6ba1132a1c 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -39,7 +39,7 @@ "bug_report_description" = "Azaldu akatsa. Zer egin duzu? Zer uste zenuen gertatuko zela? Zer gertatu da benetan?"; "bug_report_logs_description" = "Arazoak aztertzeari begira, bezero honen egunkariak arazte-txosten honekin batera bidaliko dira. Goiko testua besterik ez baduzu bidali nahi, desmarkatu:"; "rage_shake_prompt" = "Telefonoa amorruz astintzen zabiltzala dirudi. Akats baten berri eman nahi duzu?"; -"bug_report_prompt" = "Azken aldian aplikazioa kraskatu da. Kraskatze-txostena bidali nahi duzu?"; +"bug_report_prompt" = "Aurrekoan aplikazioa kraskatu da. Kraskatze-txostena bidali nahi duzu?"; "auth_register" = "Erregistratu"; // Authentication "auth_login" = "Hasi saioa"; From 6ff7530d8a0cda27014453de5707fe388c4a74bd Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 24 Jun 2019 15:46:16 +0200 Subject: [PATCH 191/266] =?UTF-8?q?Device=20Verification:=20Name=20for=20?= =?UTF-8?q?=F0=9F=94=92=20is=20"Lock"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #2526 --- CHANGES.rst | 1 + Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 4726fcb91e..7f6826381e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -16,6 +16,7 @@ Improvements: Bug fix: * Device Verification: Fix user display name and device id colors in dark theme + * Device Verification: Name for 🔒 is "Lock" (#2526). * Registration with an email is broken (#2417). * Reactions: Bad position (#2462). * Reactions: It lets you react to join/leave events (#2476). diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index f65459c334..2a920ef5c8 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -881,7 +881,7 @@ "device_verification_emoji_pencil" = "Pencil"; "device_verification_emoji_paperclip" = "Paperclip"; "device_verification_emoji_scissors" = "Scissors"; -"device_verification_emoji_padlock" = "Padlock"; +"device_verification_emoji_lock" = "Lock"; "device_verification_emoji_key" = "Key"; "device_verification_emoji_hammer" = "Hammer"; "device_verification_emoji_telephone" = "Telephone"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 89914b8e94..181d63de0f 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -574,6 +574,10 @@ internal enum VectorL10n { internal static var deviceVerificationEmojiLion: String { return VectorL10n.tr("Vector", "device_verification_emoji_lion") } + /// Lock + internal static var deviceVerificationEmojiLock: String { + return VectorL10n.tr("Vector", "device_verification_emoji_lock") + } /// Moon internal static var deviceVerificationEmojiMoon: String { return VectorL10n.tr("Vector", "device_verification_emoji_moon") @@ -586,10 +590,6 @@ internal enum VectorL10n { internal static var deviceVerificationEmojiOctopus: String { return VectorL10n.tr("Vector", "device_verification_emoji_octopus") } - /// Padlock - internal static var deviceVerificationEmojiPadlock: String { - return VectorL10n.tr("Vector", "device_verification_emoji_padlock") - } /// Panda internal static var deviceVerificationEmojiPanda: String { return VectorL10n.tr("Vector", "device_verification_emoji_panda") From 4c5af7ea105cacb21d1b221d6bb10377adba79d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Mon, 24 Jun 2019 13:16:11 +0000 Subject: [PATCH 192/266] Translated using Weblate (French) Currently translated at 100.0% (713 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index 2671d6211b..3481bf0845 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -777,3 +777,6 @@ "device_verification_emoji_folder" = "Dossier"; "device_verification_emoji_pin" = "Épingle"; "event_formatter_message_edited_mention" = "(Édité)"; +// Widget +"widget_no_integrations_server_configured" = "Aucun serveur d’intégrations n’est configuré"; +"widget_integrations_server_failed_to_connect" = "Échec de connexion au serveur d’intégrations"; From af92b980658c8b70600a3a51fc6c0cbb7e030ba0 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 25 Jun 2019 12:06:54 +0200 Subject: [PATCH 193/266] Update ReactionsMenuButton UI --- .../ReactionsMenu/ReactionsMenuButton.swift | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift index ae12573ecf..6da7eb19bf 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuButton.swift @@ -18,7 +18,14 @@ import UIKit class ReactionsMenuButton: UIButton, Themable { - // MARK: Private + // MARK: - Constants + + private enum Constants { + static let borderWidthSelected: CGFloat = 1/UIScreen.main.scale + static let borderColorAlpha: CGFloat = 0.15 + } + + // MARK: - Properties private var theme: Theme! @@ -38,8 +45,8 @@ class ReactionsMenuButton: UIButton, Themable { override func layoutSubviews() { super.layoutSubviews() - self.layer.cornerRadius = self.frame.size.height / 2 - self.layer.borderWidth = self.isSelected ? 1 : 0 + self.layer.cornerRadius = self.frame.size.height / 3 + self.layer.borderWidth = self.isSelected ? Constants.borderWidthSelected : 0 } // MARK: - Private @@ -59,16 +66,15 @@ class ReactionsMenuButton: UIButton, Themable { func update(theme: Theme) { self.theme = theme - - // TODO: Color for black theme + self.setTitleColor(self.theme.textPrimaryColor, for: .normal) self.setTitleColor(self.theme.textPrimaryColor, for: .selected) - self.layer.borderColor = self.theme.tintColor.cgColor + self.layer.borderColor = self.theme.tintColor.withAlphaComponent(Constants.borderColorAlpha).cgColor } private func updateView() { - backgroundColor = isSelected ? self.theme.tintBackgroundColor : self.theme.headerBackgroundColor + backgroundColor = isSelected ? self.theme.tintBackgroundColor : UIColor.clear } override open var isSelected: Bool { From 833ba360ee664a7e8731705df7d2d5c3777c2938 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 25 Jun 2019 13:23:07 +0200 Subject: [PATCH 194/266] Update ReactionsMenuViewModel to handle a list of reactions. --- ...n.swift => ReactionMenuItemViewData.swift} | 14 +- .../ReactionsMenuViewAction.swift | 3 +- .../ReactionsMenuViewModel.swift | 171 +++++------------- .../ReactionsMenuViewModelType.swift | 16 +- .../ReactionsMenuViewState.swift | 22 +++ 5 files changed, 77 insertions(+), 149 deletions(-) rename Riot/Modules/Room/ContextualMenu/ReactionsMenu/{ReactionsMenuReaction.swift => ReactionMenuItemViewData.swift} (80%) create mode 100644 Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewState.swift diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuReaction.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionMenuItemViewData.swift similarity index 80% rename from Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuReaction.swift rename to Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionMenuItemViewData.swift index 93de3403db..df9f4ed27c 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuReaction.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionMenuItemViewData.swift @@ -1,12 +1,12 @@ /* Copyright 2019 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. @@ -16,9 +16,7 @@ import Foundation -enum ReactionsMenuReaction: String { - case agree = "👍" - case disagree = "👎" - case like = "🙂" - case dislike = "😔" +struct ReactionMenuItemViewData { + let emoji: String + let isSelected: Bool } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewAction.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewAction.swift index b98ac829ae..3608a91876 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewAction.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewAction.swift @@ -18,5 +18,6 @@ import UIKit /// Action chosen by the user enum ReactionsMenuViewAction { - case toggleReaction(ReactionsMenuReaction) + case loadData + case tap(reaction: String) } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index 846ce20491..c2ec998301 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -14,158 +14,71 @@ limitations under the License. */ -import UIKit +import Foundation @objc final class ReactionsMenuViewModel: NSObject, ReactionsMenuViewModelType { - + // MARK: - Properties - + + private let reactions = ["👍", "👎", "😄", "🎉", "😕", "❤️", "🚀", "👀"] + private var currentViewDatas: [ReactionMenuItemViewData] = [] + // MARK: Private - private let aggregations: MXAggregations - private let roomId: String + + private let aggregatedReactions: MXAggregatedReactions? + private let reactionsViewData: [ReactionMenuItemViewData] = [] private let eventId: String - + // MARK: Public - - private(set) var isAgreeButtonSelected: Bool = false - private(set) var isDisagreeButtonSelected: Bool = false - private(set) var isLikeButtonSelected: Bool = false - private(set) var isDislikeButtonSelected: Bool = false - - weak var viewDelegate: ReactionsMenuViewModelDelegate? - @objc weak var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? - + + @objc weak var viewModelDelegate: ReactionsMenuViewModelCoordinatorDelegate? + weak var viewDelegate: ReactionsMenuViewModelViewDelegate? + // MARK: - Setup - - @objc init(aggregations: MXAggregations, roomId: String, eventId: String) { - self.aggregations = aggregations - self.roomId = roomId + + @objc init(aggregatedReactions: MXAggregatedReactions?, + eventId: String) { + self.aggregatedReactions = aggregatedReactions self.eventId = eventId - - super.init() - - self.loadData() - self.listenToDataUpdate() } - + // MARK: - Public - + func process(viewAction: ReactionsMenuViewAction) { - var reaction: ReactionsMenuReaction? - var newState: Bool? - switch viewAction { - case .toggleReaction(let menuReaction): - reaction = menuReaction - - switch menuReaction { - case .agree: - newState = !self.isAgreeButtonSelected - case .disagree: - newState = !self.isDisagreeButtonSelected - case .like: - newState = !self.isLikeButtonSelected - case .dislike: - newState = !self.isDislikeButtonSelected + case .loadData: + self.loadData() + case .tap(let reaction): + if let viewData = self.currentViewDatas.first(where: { $0.emoji == reaction }) { + if viewData.isSelected { + self.coordinatorDelegate?.reactionsMenuViewModel(self, didRemoveReaction: reaction, forEventId: self.eventId) + } else { + self.coordinatorDelegate?.reactionsMenuViewModel(self, didAddReaction: reaction, forEventId: self.eventId) + } } } - - guard let theReaction = reaction, let theNewState = newState else { - return - } - - self.react(withReaction: theReaction, selected: theNewState) } - + // MARK: - Private - - private func resetData() { - self.isAgreeButtonSelected = false - self.isDisagreeButtonSelected = false - self.isLikeButtonSelected = false - self.isDislikeButtonSelected = false - } - + private func loadData() { - guard let reactionCounts = self.aggregations.aggregatedReactions(onEvent: self.eventId, inRoom: self.roomId)?.withNonZeroCount()?.reactions else { - return - } - - self.resetData() + let reactionCounts = self.aggregatedReactions?.withNonZeroCount()?.reactions ?? [] + + var quickReactionsWithUserReactedFlag: [String: Bool] = Dictionary(uniqueKeysWithValues: self.reactions.map { ($0, false) }) + reactionCounts.forEach { (reactionCount) in - if reactionCount.myUserHasReacted { - if let reaction = ReactionsMenuReaction(rawValue: reactionCount.reaction) { - switch reaction { - case .agree: - self.isAgreeButtonSelected = true - case .disagree: - self.isDisagreeButtonSelected = true - case .like: - self.isLikeButtonSelected = true - case .dislike: - self.isDislikeButtonSelected = true - } - } + if let hasUserReacted = quickReactionsWithUserReactedFlag[reactionCount.reaction], hasUserReacted == false { + quickReactionsWithUserReactedFlag[reactionCount.reaction] = reactionCount.myUserHasReacted } } - - self.viewDelegate?.reactionsMenuViewModelDidUpdate(self) - } - - private func listenToDataUpdate() { - self.aggregations.listenToReactionCountUpdate(inRoom: self.roomId) { [weak self] (changes) in - - guard let sself = self else { - return - } - - if changes[sself.eventId] != nil { - sself.loadData() - } - } - } - - private func react(withReaction reaction: ReactionsMenuReaction, selected: Bool) { - // If required, unreact first - if selected { - self.ensure3StateButtons(withReaction: reaction) + let reactionMenuItemViewDatas: [ReactionMenuItemViewData] = self.reactions.map { reaction -> ReactionMenuItemViewData in + let isSelected = quickReactionsWithUserReactedFlag[reaction] ?? false + return ReactionMenuItemViewData(emoji: reaction, isSelected: isSelected) } - let reactionString = reaction.rawValue + self.currentViewDatas = reactionMenuItemViewDatas - if selected { - self.coordinatorDelegate?.reactionsMenuViewModel(self, didAddReaction: reactionString, forEventId: self.eventId) - } else { - self.coordinatorDelegate?.reactionsMenuViewModel(self, didRemoveReaction: reactionString, forEventId: self.eventId) - } - } - - // We can like, dislike, be indifferent but we cannot like & dislike at the same time - private func ensure3StateButtons(withReaction reaction: ReactionsMenuReaction) { - var unreaction: ReactionsMenuReaction? - - switch reaction { - case .agree: - if isDisagreeButtonSelected { - unreaction = .disagree - } - case .disagree: - if isAgreeButtonSelected { - unreaction = .agree - } - case .like: - if isDislikeButtonSelected { - unreaction = .dislike - } - case .dislike: - if isLikeButtonSelected { - unreaction = .like - } - } - - if let unreaction = unreaction { - self.react(withReaction: unreaction, selected: false) - } + self.viewDelegate?.reactionsMenuViewModel(self, didUpdateViewState: ReactionsMenuViewState.loaded(reactionsViewData: reactionMenuItemViewDatas)) } } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift index 111c01d03f..0e162f6c1f 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModelType.swift @@ -16,8 +16,8 @@ import Foundation -protocol ReactionsMenuViewModelDelegate: class { - func reactionsMenuViewModelDidUpdate(_ viewModel: ReactionsMenuViewModelType) +protocol ReactionsMenuViewModelViewDelegate: class { + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didUpdateViewState viewState: ReactionsMenuViewState) } @objc protocol ReactionsMenuViewModelCoordinatorDelegate: class { @@ -25,16 +25,10 @@ protocol ReactionsMenuViewModelDelegate: class { func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didRemoveReaction reaction: String, forEventId eventId: String) } - protocol ReactionsMenuViewModelType { - - var isAgreeButtonSelected: Bool { get } - var isDisagreeButtonSelected: Bool { get } - var isLikeButtonSelected: Bool { get } - var isDislikeButtonSelected: Bool { get } - - var viewDelegate: ReactionsMenuViewModelDelegate? { get set } + var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? { get set } - + var viewDelegate: ReactionsMenuViewModelViewDelegate? { get set } + func process(viewAction: ReactionsMenuViewAction) } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewState.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewState.swift new file mode 100644 index 0000000000..e74a77ef7b --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewState.swift @@ -0,0 +1,22 @@ +/* + Copyright 2019 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 + +/// ReactionsMenuView view state +enum ReactionsMenuViewState { + case loaded(reactionsViewData: [ReactionMenuItemViewData]) +} From 96c771bbfeee09805a317868439daa7f1bd50c44 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 25 Jun 2019 13:24:03 +0200 Subject: [PATCH 195/266] Update ReactionsMenuView layout. --- .../ReactionsMenu/ReactionsMenuView.swift | 146 +++++++------- .../ReactionsMenu/ReactionsMenuView.xib | 180 +++++++++--------- 2 files changed, 174 insertions(+), 152 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift index d4f54bd39e..ce4d91cf74 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift @@ -17,93 +17,111 @@ import UIKit import Reusable -final class ReactionsMenuView: UIView, NibOwnerLoadable { - +final class ReactionsMenuView: UIView, Themable, NibLoadable { + + // MARK: - Constants + + private enum Constants { + static let selectedReactionAnimationScale: CGFloat = 1.2 + } + // MARK: - Properties - + // MARK: Outlets - @IBOutlet weak var agreeButton: UIButton! - @IBOutlet weak var disagreeButton: UIButton! - @IBOutlet weak var likeButton: UIButton! - @IBOutlet weak var dislikeButton: UIButton! + @IBOutlet private weak var reactionsBackgroundView: UIView! + @IBOutlet private weak var reactionsStackView: UIStackView! + // MARK: Private - + + private var reactionViewDatas: [ReactionMenuItemViewData] = [] + private var reactionButtons: [ReactionsMenuButton] = [] + private var tappedReactionButton: ReactionsMenuButton? + // MARK: Public - + var viewModel: ReactionsMenuViewModelType? { didSet { - self.updateView() self.viewModel?.viewDelegate = self + self.viewModel?.process(viewAction: .loadData) } } - - // MARK: - Setup - - required init?(coder aDecoder: NSCoder) { - super.init(coder: aDecoder) - self.loadNibContent() - self.commonInit() + + var reactionHasBeenTapped: Bool { + return self.tappedReactionButton != nil } - - override init(frame: CGRect) { - super.init(frame: frame) - self.loadNibContent() - self.commonInit() + + // MARK: - Life cycle + + override func awakeFromNib() { + super.awakeFromNib() + + self.reactionsBackgroundView.layer.masksToBounds = true + self.update(theme: ThemeService.shared().theme) } - - // MARK: - Actions - - @IBAction private func agreeButtonAction(_ sender: Any) { - self.viewModel?.process(viewAction: .toggleReaction(.agree)) + + override func layoutSubviews() { + super.layoutSubviews() + + self.reactionsBackgroundView.layer.cornerRadius = self.reactionsBackgroundView.frame.size.height/2 } - - @IBAction private func disagreeButtonAction(_ sender: Any) { - self.viewModel?.process(viewAction: .toggleReaction(.disagree)) + + // MARK: - Public + + func update(theme: Theme) { + self.reactionsBackgroundView.backgroundColor = theme.headerBackgroundColor } - - @IBAction private func likeButtonAction(_ sender: Any) { - self.viewModel?.process(viewAction: .toggleReaction(.like)) + + func selectionAnimationInstructionPart1() { + guard let tappedButton = self.tappedReactionButton else { + return + } + let scale = Constants.selectedReactionAnimationScale + tappedButton.superview?.bringSubviewToFront(tappedButton) + tappedButton.transform = CGAffineTransform(scaleX: scale, y: scale) } - - @IBAction private func dislikeButtonAction(_ sender: Any) { - self.viewModel?.process(viewAction: .toggleReaction(.dislike)) + + func selectionAnimationInstructionPart2() { + guard let tappedButton = self.tappedReactionButton else { + return + } + tappedButton.transform = CGAffineTransform.identity + tappedButton.isSelected.toggle() } // MARK: - Private - - private func commonInit() { - - agreeButton.setTitle(VectorL10n.roomEventActionReactionAgree(ReactionsMenuReaction.agree.rawValue), for: .normal) - agreeButton.setTitle(VectorL10n.roomEventActionReactionAgree(ReactionsMenuReaction.agree.rawValue), for: .highlighted) - disagreeButton.setTitle(VectorL10n.roomEventActionReactionDisagree(ReactionsMenuReaction.disagree.rawValue), for: .normal) - disagreeButton.setTitle(VectorL10n.roomEventActionReactionDisagree(ReactionsMenuReaction.disagree.rawValue), for: .highlighted) - likeButton.setTitle(VectorL10n.roomEventActionReactionLike(ReactionsMenuReaction.like.rawValue), for: .normal) - likeButton.setTitle(VectorL10n.roomEventActionReactionLike(ReactionsMenuReaction.like.rawValue), for: .highlighted) - dislikeButton.setTitle(VectorL10n.roomEventActionReactionDislike(ReactionsMenuReaction.dislike.rawValue), for: .normal) - dislikeButton.setTitle(VectorL10n.roomEventActionReactionDislike(ReactionsMenuReaction.dislike.rawValue), for: .highlighted) - - customizeViewRendering() - } - - private func customizeViewRendering() { - self.backgroundColor = UIColor.clear + + private func fill(reactionsMenuViewDatas: [ReactionMenuItemViewData]) { + self.reactionViewDatas = reactionsMenuViewDatas + + self.reactionsStackView.vc_removeAllSubviews() + + for reactionViewData in self.reactionViewDatas { + let reactionsMenuButton = ReactionsMenuButton() + reactionsMenuButton.setTitle(reactionViewData.emoji, for: .normal) + reactionsMenuButton.isSelected = reactionViewData.isSelected + reactionsMenuButton.addTarget(self, action: #selector(reactionButtonAction), for: .touchUpInside) + self.reactionsStackView.addArrangedSubview(reactionsMenuButton) + self.reactionButtons.append(reactionsMenuButton) + } } - - private func updateView() { - guard let viewModel = self.viewModel else { + + @objc private func reactionButtonAction(_ sender: ReactionsMenuButton) { + guard let tappedReaction = sender.titleLabel?.text else { return } - - agreeButton.isSelected = viewModel.isAgreeButtonSelected - disagreeButton.isSelected = viewModel.isDisagreeButtonSelected - likeButton.isSelected = viewModel.isLikeButtonSelected - dislikeButton.isSelected = viewModel.isDislikeButtonSelected + self.tappedReactionButton = sender + self.viewModel?.process(viewAction: .tap(reaction: tappedReaction)) } } -extension ReactionsMenuView: ReactionsMenuViewModelDelegate { - func reactionsMenuViewModelDidUpdate(_ viewModel: ReactionsMenuViewModelType) { - self.updateView() +// MARK: - ReactionsMenuViewModelViewDelegate +extension ReactionsMenuView: ReactionsMenuViewModelViewDelegate { + + func reactionsMenuViewModel(_ viewModel: ReactionsMenuViewModel, didUpdateViewState viewState: ReactionsMenuViewState) { + switch viewState { + case .loaded(reactionsViewData: let reactionsViewData): + self.fill(reactionsMenuViewDatas: reactionsViewData) + } } } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib index f8ca5acd4e..f2920d92b3 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib @@ -1,113 +1,117 @@ - + - - - - - - - - - + - - + + - - - - + + + + + + + + + + + - - - - - - + + + + - - - - - - - - + - - - - - - - - + + + + - - + + + + + From 77d27d5fce7665d02455bd9e4357e284d5eb70b1 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 25 Jun 2019 13:48:16 +0200 Subject: [PATCH 196/266] RoomContextualMenuViewController: Handle updated ReactionsMenuView and new animations instructions. --- .../ReactionsMenuViewModel.swift | 2 +- ...oomContextualMenuViewController.storyboard | 17 ++- .../RoomContextualMenuViewController.swift | 124 +++++++++++++++--- 3 files changed, 115 insertions(+), 28 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift index c2ec998301..c35678cdb6 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuViewModel.swift @@ -31,7 +31,7 @@ import Foundation // MARK: Public - @objc weak var viewModelDelegate: ReactionsMenuViewModelCoordinatorDelegate? + @objc weak var coordinatorDelegate: ReactionsMenuViewModelCoordinatorDelegate? weak var viewDelegate: ReactionsMenuViewModelViewDelegate? // MARK: - Setup diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard index 711ebfcfb4..f473449964 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.storyboard @@ -21,19 +21,20 @@ - - - + + + - + + - - + + @@ -49,8 +50,10 @@ + + @@ -61,7 +64,7 @@ - + diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift index d4d9284006..72594e2480 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift @@ -18,12 +18,18 @@ import UIKit @objc protocol RoomContextualMenuViewControllerDelegate: class { func roomContextualMenuViewControllerDidTapBackgroundOverlay(_ viewController: RoomContextualMenuViewController) - func roomContextualMenuViewControllerDidReaction(_ viewController: RoomContextualMenuViewController) } @objcMembers final class RoomContextualMenuViewController: UIViewController, Themable { + // MARK: - Constants + + private enum Constants { + static let reactionsMenuViewVerticalMargin: CGFloat = 10.0 + static let reactionsMenuViewHiddenScale: CGFloat = 0.97 + } + // MARK: - Properties // MARK: Outlets @@ -34,15 +40,20 @@ final class RoomContextualMenuViewController: UIViewController, Themable { @IBOutlet private weak var menuToolbarViewHeightConstraint: NSLayoutConstraint! @IBOutlet private weak var menuToolbarViewBottomConstraint: NSLayoutConstraint! - @IBOutlet private weak var reactionsMenuView: ReactionsMenuView! + @IBOutlet private weak var reactionsMenuContainerView: UIView! @IBOutlet private weak var reactionsMenuViewHeightConstraint: NSLayoutConstraint! @IBOutlet private weak var reactionsMenuViewBottomConstraint: NSLayoutConstraint! // MARK: Private private var theme: Theme! - private var errorPresenter: MXKErrorPresentation! - private var contextualMenuItems: [RoomContextualMenuItem] = [] + private var contextualMenuItems: [RoomContextualMenuItem] = [] + private var reactionsMenuViewModel: ReactionsMenuViewModel? + + private weak var reactionsMenuView: ReactionsMenuView? + + private var reactionsMenuViewBottomStartConstraintConstant: CGFloat? + private var reactionsMenuViewBottomEndConstraintConstant: CGFloat? private var hiddenToolbarViewBottomConstant: CGFloat { let bottomSafeAreaHeight: CGFloat @@ -58,30 +69,40 @@ final class RoomContextualMenuViewController: UIViewController, Themable { // MARK: Public + var contentToReactFrame: CGRect? + var shouldPerformTappedReactionAnimation: Bool { + return self.reactionsMenuView?.reactionHasBeenTapped ?? false + } + weak var delegate: RoomContextualMenuViewControllerDelegate? // MARK: - Setup - class func instantiate(with contextualMenuItems: [RoomContextualMenuItem]) -> RoomContextualMenuViewController { + class func instantiate(with contextualMenuItems: [RoomContextualMenuItem], + reactionsMenuViewModel: ReactionsMenuViewModel?) -> RoomContextualMenuViewController { let viewController = StoryboardScene.RoomContextualMenuViewController.initialScene.instantiate() viewController.theme = ThemeService.shared().theme viewController.contextualMenuItems = contextualMenuItems + viewController.reactionsMenuViewModel = reactionsMenuViewModel return viewController } + // MARK: - Life cycle override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. - self.reactionsMenuView.isHidden = true + self.reactionsMenuContainerView.isHidden = true + + if let reactionsMenuViewModel = self.reactionsMenuViewModel { + self.setupReactionsMenu(with: reactionsMenuViewModel) + } self.backgroundOverlayView.isUserInteractionEnabled = true self.menuToolbarView.fill(contextualMenuItems: self.contextualMenuItems) self.setupBackgroundOverlayGestureRecognizers() - - self.errorPresenter = MXKErrorAlertPresentation() self.registerThemeServiceDidChangeThemeNotification() self.update(theme: self.theme) @@ -91,31 +112,87 @@ final class RoomContextualMenuViewController: UIViewController, Themable { func showMenuToolbar() { self.menuToolbarViewBottomConstraint.constant = 0 + self.menuToolbarView.alpha = 1 } func hideMenuToolbar() { self.menuToolbarViewBottomConstraint.constant = self.hiddenToolbarViewBottomConstant + self.menuToolbarView.alpha = 0 } - - func showReactionsMenu(withViewModel viewModel: ReactionsMenuViewModel, aroundFrame frame: CGRect) { - self.reactionsMenuView.viewModel = viewModel - self.reactionsMenuView.isHidden = false - + + func prepareReactionsMenuAnimations() { + guard let frame = self.contentToReactFrame else { + return + } + let menuHeight = self.reactionsMenuViewHeightConstraint.constant - + let verticalMargin = Constants.reactionsMenuViewVerticalMargin + + let reactionsMenuViewBottomStartConstraintConstant: CGFloat? + let reactionsMenuViewBottomEndConstraintConstant: CGFloat? + // Try to display the menu at the top of the message first // Then, try at the bottom // Else, keep the position defined in the storyboard - if frame.origin.y >= self.reactionsMenuViewHeightConstraint.constant { - self.reactionsMenuViewBottomConstraint.constant = frame.origin.y + if frame.origin.y - verticalMargin >= menuHeight { + let menuViewBottomY = frame.origin.y - verticalMargin + reactionsMenuViewBottomStartConstraintConstant = menuViewBottomY + menuHeight/2 + reactionsMenuViewBottomEndConstraintConstant = menuViewBottomY } else { - let frameBottomY = frame.origin.y + frame.size.height + let frameBottomY = frame.origin.y + frame.size.height + verticalMargin let visibleViewHeight = self.view.frame.size.height - self.menuToolbarView.frame.size.height - + if frameBottomY + menuHeight < visibleViewHeight { - self.reactionsMenuViewBottomConstraint.constant = frameBottomY + menuHeight + let menuViewBottomY = frameBottomY + menuHeight + + reactionsMenuViewBottomEndConstraintConstant = menuViewBottomY + reactionsMenuViewBottomStartConstraintConstant = menuViewBottomY - menuHeight/2 + } else { + reactionsMenuViewBottomEndConstraintConstant = nil + reactionsMenuViewBottomStartConstraintConstant = nil } } + + self.reactionsMenuViewBottomStartConstraintConstant = reactionsMenuViewBottomStartConstraintConstant + self.reactionsMenuViewBottomEndConstraintConstant = reactionsMenuViewBottomEndConstraintConstant + + self.reactionsMenuContainerView.isHidden = false + } + + func showReactionsMenu() { + guard let reactionsMenuView = self.reactionsMenuView else { + return + } + + if let reactionsMenuViewBottomEndConstraintConstant = self.reactionsMenuViewBottomEndConstraintConstant { + self.reactionsMenuViewBottomConstraint.constant = reactionsMenuViewBottomEndConstraintConstant + } + + reactionsMenuView.alpha = 1 + reactionsMenuContainerView.transform = CGAffineTransform.identity + } + + func hideReactionsMenu() { + guard let reactionsMenuView = self.reactionsMenuView else { + return + } + + if let reactionsMenuViewBottomStartConstraintConstant = self.reactionsMenuViewBottomStartConstraintConstant { + self.reactionsMenuViewBottomConstraint.constant = reactionsMenuViewBottomStartConstraintConstant + } + + reactionsMenuView.alpha = 0 + + let transformScale = Constants.reactionsMenuViewHiddenScale + self.reactionsMenuContainerView.transform = CGAffineTransform(scaleX: transformScale, y: transformScale) + } + + func selectedReactionAnimationsIntructionsPart1() { + self.reactionsMenuView?.selectionAnimationInstructionPart1() + } + + func selectedReactionAnimationsIntructionsPart2() { + self.reactionsMenuView?.selectionAnimationInstructionPart2() } func update(theme: Theme) { @@ -124,6 +201,13 @@ final class RoomContextualMenuViewController: UIViewController, Themable { // MARK: - Private + private func setupReactionsMenu(with viewModel: ReactionsMenuViewModel) { + let reactionsMenuView = ReactionsMenuView.loadFromNib() + self.reactionsMenuContainerView.vc_addSubViewMatchingParent(reactionsMenuView) + reactionsMenuView.viewModel = viewModel + self.reactionsMenuView = reactionsMenuView + } + private func setupBackgroundOverlayGestureRecognizers() { let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handle(gestureRecognizer:))) @@ -155,6 +239,6 @@ extension RoomContextualMenuViewController: UIGestureRecognizerDelegate { // Avoid triggering background overlay gesture recognizers when touching reactions menu func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldReceive touch: UITouch) -> Bool { - return touch.vc_isInside(view: self.reactionsMenuView) == false + return touch.vc_isInside(view: self.reactionsMenuContainerView) == false } } From dab7d224ebda30e33cc139fe2775fd60a119f1c2 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 25 Jun 2019 13:56:44 +0200 Subject: [PATCH 197/266] RoomContextualMenuPresenter: Handle new reactions menu animations, reduce context menu show/hide animation duration. --- .../RoomContextualMenuPresenter.swift | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift index 5e0fbbc7e2..58e9e2bd48 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift @@ -22,7 +22,7 @@ final class RoomContextualMenuPresenter: NSObject { // MARK: - Constants private enum Constants { - static let animationDuration: TimeInterval = 0.3 + static let animationDuration: TimeInterval = 0.2 } // MARK: - Properties @@ -38,28 +38,32 @@ final class RoomContextualMenuPresenter: NSObject { } // MARK: - Public - + func present(roomContextualMenuViewController: RoomContextualMenuViewController, from viewController: UIViewController, on view: UIView, + contentToReactFrame: CGRect, // Not nullable for compatibility with Obj-C animated: Bool, completion: (() -> Void)?) { guard self.roomContextualMenuViewController == nil else { return } - roomContextualMenuViewController.view.alpha = 0 - viewController.vc_addChildViewController(viewController: roomContextualMenuViewController, onView: view) self.roomContextualMenuViewController = roomContextualMenuViewController + roomContextualMenuViewController.contentToReactFrame = contentToReactFrame + roomContextualMenuViewController.hideMenuToolbar() + roomContextualMenuViewController.prepareReactionsMenuAnimations() + roomContextualMenuViewController.hideReactionsMenu() + roomContextualMenuViewController.view.layoutIfNeeded() let animationInstructions: (() -> Void) = { roomContextualMenuViewController.showMenuToolbar() - roomContextualMenuViewController.view.alpha = 1 + roomContextualMenuViewController.showReactionsMenu() roomContextualMenuViewController.view.layoutIfNeeded() } @@ -77,37 +81,43 @@ final class RoomContextualMenuPresenter: NSObject { func hideContextualMenu(animated: Bool, completion: (() -> Void)?) { guard let roomContextualMenuViewController = self.roomContextualMenuViewController else { + completion?() return } let animationInstructions: (() -> Void) = { roomContextualMenuViewController.hideMenuToolbar() - roomContextualMenuViewController.view.alpha = 0 + roomContextualMenuViewController.hideReactionsMenu() roomContextualMenuViewController.view.layoutIfNeeded() } let animationCompletionInstructions: (() -> Void) = { roomContextualMenuViewController.vc_removeFromParent() - - // TODO: To remove once the retain cycle caused by reactionsMenuViewModel is fixed - self.roomContextualMenuViewController = nil - completion?() } if animated { - UIView.animate(withDuration: Constants.animationDuration, animations: { - animationInstructions() - }, completion: { completed in - animationCompletionInstructions() - }) + if roomContextualMenuViewController.shouldPerformTappedReactionAnimation { + UIView.animate(withDuration: 0.15, animations: { + roomContextualMenuViewController.selectedReactionAnimationsIntructionsPart1() + }, completion: { _ in + UIView.animate(withDuration: Constants.animationDuration, animations: { + roomContextualMenuViewController.selectedReactionAnimationsIntructionsPart2() + animationInstructions() + }, completion: { completed in + animationCompletionInstructions() + }) + }) + } else { + UIView.animate(withDuration: Constants.animationDuration, animations: { + animationInstructions() + }, completion: { completed in + animationCompletionInstructions() + }) + } } else { animationInstructions() animationCompletionInstructions() } } - - func showReactionsMenu(reactionsMenuViewModel: ReactionsMenuViewModel, aroundFrame frame: CGRect) { - self.roomContextualMenuViewController?.showReactionsMenu(withViewModel: reactionsMenuViewModel, aroundFrame: frame) - } } From cd57ed1f9b38dfa186226409868205c407aadf74 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 25 Jun 2019 14:00:12 +0200 Subject: [PATCH 198/266] RoomViewController: Handle updated RoomContextualMenuViewController. --- Riot/Modules/Room/RoomViewController.m | 76 +++++++++++++------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index e6c7b2b95d..fab2cd0559 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5158,21 +5158,13 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce return; } - [self selectEventWithId:event.eventId]; + NSString *selectedEventId = event.eventId; - NSArray* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell]; - - RoomContextualMenuViewController *roomContextualMenuViewController = [RoomContextualMenuViewController instantiateWith:contextualMenuItems]; - roomContextualMenuViewController.delegate = self; + [self selectEventWithId:selectedEventId]; - [self enableOverlayContainerUserInteractions:YES]; - - [self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:roomContextualMenuViewController - from:self - on:self.overlayContainerView - animated:YES - completion:^{ - }]; + NSArray* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell]; + ReactionsMenuViewModel *reactionsMenuViewModel; + CGRect bubbleComponentFrameInOverlayView = CGRectNull; if (RiotSettings.shared.messageReaction && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && [self.roomDataSource canReactToEventWithId:event.eventId]) { @@ -5181,14 +5173,14 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce NSArray *bubbleComponents = bubbleCellData.bubbleComponents; NSInteger foundComponentIndex = [bubbleComponents indexOfObjectPassingTest:^BOOL(MXKRoomBubbleComponent * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { - if (obj.event.eventId == event.eventId) + if (obj.event.eventId == selectedEventId) { *stop = YES; return YES; } return NO; }]; - + CGRect bubbleComponentFrame; if (bubbleComponents.count > 0) @@ -5201,16 +5193,28 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce bubbleComponentFrame = roomBubbleTableViewCell.frame; } - CGRect bubbleComponentFrameInOverlayView = [self.bubblesTableView convertRect:bubbleComponentFrame toView:self.overlayContainerView]; + bubbleComponentFrameInOverlayView = [self.bubblesTableView convertRect:bubbleComponentFrame toView:self.overlayContainerView]; NSString *roomId = self.roomDataSource.roomId; MXAggregations *aggregations = self.mainSession.aggregations; + MXAggregatedReactions *aggregatedReactions = [aggregations aggregatedReactionsOnEvent:selectedEventId inRoom:roomId]; - ReactionsMenuViewModel *reactionsMenuViewModel = [[ReactionsMenuViewModel alloc] initWithAggregations:aggregations roomId:roomId eventId:event.eventId]; + reactionsMenuViewModel = [[ReactionsMenuViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:selectedEventId]; reactionsMenuViewModel.coordinatorDelegate = self; - - [self.roomContextualMenuPresenter showReactionsMenuWithReactionsMenuViewModel:reactionsMenuViewModel aroundFrame:bubbleComponentFrameInOverlayView]; } + + RoomContextualMenuViewController *roomContextualMenuViewController = [RoomContextualMenuViewController instantiateWith:contextualMenuItems reactionsMenuViewModel:reactionsMenuViewModel]; + roomContextualMenuViewController.delegate = self; + + [self enableOverlayContainerUserInteractions:YES]; + + [self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:roomContextualMenuViewController + from:self + on:self.overlayContainerView + contentToReactFrame:bubbleComponentFrameInOverlayView + animated:YES + completion:^{ + }]; } - (void)hideContextualMenuAnimated:(BOOL)animated @@ -5259,41 +5263,39 @@ - (void)roomContextualMenuViewControllerDidTapBackgroundOverlay:(RoomContextualM [self hideContextualMenuAnimated:YES]; } -- (void)roomContextualMenuViewControllerDidReaction:(RoomContextualMenuViewController *)viewController -{ - [self hideContextualMenuAnimated:YES]; -} - #pragma mark - ReactionsMenuViewModelCoordinatorDelegate - (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didAddReaction:(NSString *)reaction forEventId:(NSString *)eventId { MXWeakify(self); - [self.roomDataSource addReaction:reaction forEventId:eventId success:^{ + [self hideContextualMenuAnimated:YES completion:^{ - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); - - [self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil]; + [self.roomDataSource addReaction:reaction forEventId:eventId success:^{ + + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + + [self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil]; + }]; }]; - - [self hideContextualMenuAnimated:YES]; } - (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didRemoveReaction:(NSString *)reaction forEventId:(NSString *)eventId { MXWeakify(self); - [self.roomDataSource removeReaction:reaction forEventId:eventId success:^{ + [self hideContextualMenuAnimated:YES completion:^{ - } failure:^(NSError *error) { - MXStrongifyAndReturnIfNil(self); + [self.roomDataSource removeReaction:reaction forEventId:eventId success:^{ + + } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + + [self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil]; + }]; - [self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil]; }]; - - [self hideContextualMenuAnimated:YES]; } @end From 8c304d521eda8c480ea057294c88dd146c703304 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 25 Jun 2019 14:00:20 +0200 Subject: [PATCH 199/266] Update pbxproj --- Riot.xcodeproj/project.pbxproj | 44 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 6f8412cefb..21d874c542 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -76,13 +76,8 @@ 32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */; }; 32891D76226728EF00C82226 /* DeviceVerificationDataLoadingViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */; }; 32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32B1FEDA21A46F2C00637127 /* TermsView.xib */; }; - 32B94DF8228EC26400716A26 /* ReactionsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF1228EC26400716A26 /* ReactionsMenuView.swift */; }; 32B94DF9228EC26400716A26 /* ReactionsMenuViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */; }; 32B94DFA228EC26400716A26 /* ReactionsMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */; }; - 32B94DFB228EC26400716A26 /* ReactionsMenuViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF4228EC26400716A26 /* ReactionsMenuViewModelType.swift */; }; - 32B94DFC228EC26400716A26 /* ReactionsMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF5228EC26400716A26 /* ReactionsMenuViewModel.swift */; }; - 32B94DFD228EC26400716A26 /* ReactionsMenuReaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF6228EC26400716A26 /* ReactionsMenuReaction.swift */; }; - 32B94DFE228EC26400716A26 /* ReactionsMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32B94DF7228EC26400716A26 /* ReactionsMenuView.xib */; }; 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF994E21FA29A400698084 /* SettingsKeyBackupViewModel.swift */; }; 32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995021FA29DC00698084 /* SettingsKeyBackupViewModelType.swift */; }; 32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32BF995221FA2A1300698084 /* SettingsKeyBackupViewState.swift */; }; @@ -460,6 +455,12 @@ B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2821EF692B000D1D89 /* UIView.swift */; }; B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */; }; B1CE9F062216FB09000FAE6A /* EncryptionKeysExportPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE9F052216FB09000FAE6A /* EncryptionKeysExportPresenter.swift */; }; + B1D1BDA622BBAFB500831367 /* ReactionsMenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D1BDA522BBAFB500831367 /* ReactionsMenuView.swift */; }; + B1D1BDA822BBAFC900831367 /* ReactionsMenuView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1D1BDA722BBAFC900831367 /* ReactionsMenuView.xib */; }; + B1D211E222BD193C00D939BD /* ReactionsMenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D211E122BD193C00D939BD /* ReactionsMenuViewModel.swift */; }; + B1D211E422C18E3800D939BD /* ReactionsMenuViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D211E322C18E3800D939BD /* ReactionsMenuViewModelType.swift */; }; + B1D211E622C194A200D939BD /* ReactionsMenuViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D211E522C194A200D939BD /* ReactionsMenuViewState.swift */; }; + B1D211E822C195B400D939BD /* ReactionMenuItemViewData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D211E722C195B400D939BD /* ReactionMenuItemViewData.swift */; }; B1D250D82118AA0A000F4E93 /* RoomPredecessorBubbleCell.m in Sources */ = {isa = PBXBuildFile; fileRef = B1D250D72118AA0A000F4E93 /* RoomPredecessorBubbleCell.m */; }; B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D4752521EE4E620067973F /* KeyboardAvoider.swift */; }; B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1D4752621EE4E620067973F /* KeyboardNotification.swift */; }; @@ -607,13 +608,8 @@ 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewController.swift; sourceTree = ""; }; 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DeviceVerificationDataLoadingViewController.storyboard; sourceTree = ""; }; 32B1FEDA21A46F2C00637127 /* TermsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TermsView.xib; sourceTree = ""; }; - 32B94DF1228EC26400716A26 /* ReactionsMenuView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuView.swift; sourceTree = ""; }; 32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewAction.swift; sourceTree = ""; }; 32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuButton.swift; sourceTree = ""; }; - 32B94DF4228EC26400716A26 /* ReactionsMenuViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModelType.swift; sourceTree = ""; }; - 32B94DF5228EC26400716A26 /* ReactionsMenuViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModel.swift; sourceTree = ""; }; - 32B94DF6228EC26400716A26 /* ReactionsMenuReaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuReaction.swift; sourceTree = ""; }; - 32B94DF7228EC26400716A26 /* ReactionsMenuView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ReactionsMenuView.xib; sourceTree = ""; }; 32BDC9A1211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/InfoPlist.strings; sourceTree = ""; }; 32BDC9A2211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/Localizable.strings; sourceTree = ""; }; 32BDC9A3211C2C870064AF51 /* zh_Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh_Hant; path = zh_Hant.lproj/Vector.strings; sourceTree = ""; }; @@ -1197,6 +1193,12 @@ B1CA3A2821EF692B000D1D89 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = ""; }; B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignOutAlertPresenter.swift; sourceTree = ""; }; B1CE9F052216FB09000FAE6A /* EncryptionKeysExportPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptionKeysExportPresenter.swift; sourceTree = ""; }; + B1D1BDA522BBAFB500831367 /* ReactionsMenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuView.swift; sourceTree = ""; }; + B1D1BDA722BBAFC900831367 /* ReactionsMenuView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReactionsMenuView.xib; sourceTree = ""; }; + B1D211E122BD193C00D939BD /* ReactionsMenuViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModel.swift; sourceTree = ""; }; + B1D211E322C18E3800D939BD /* ReactionsMenuViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewModelType.swift; sourceTree = ""; }; + B1D211E522C194A200D939BD /* ReactionsMenuViewState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewState.swift; sourceTree = ""; }; + B1D211E722C195B400D939BD /* ReactionMenuItemViewData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReactionMenuItemViewData.swift; sourceTree = ""; }; B1D250D62118AA0A000F4E93 /* RoomPredecessorBubbleCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomPredecessorBubbleCell.h; sourceTree = ""; }; B1D250D72118AA0A000F4E93 /* RoomPredecessorBubbleCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomPredecessorBubbleCell.m; sourceTree = ""; }; B1D4752521EE4E620067973F /* KeyboardAvoider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyboardAvoider.swift; sourceTree = ""; }; @@ -1510,13 +1512,14 @@ 32B94DF0228EC26400716A26 /* ReactionsMenu */ = { isa = PBXGroup; children = ( - 32B94DF1228EC26400716A26 /* ReactionsMenuView.swift */, + B1D211E322C18E3800D939BD /* ReactionsMenuViewModelType.swift */, + B1D211E122BD193C00D939BD /* ReactionsMenuViewModel.swift */, + B1D211E722C195B400D939BD /* ReactionMenuItemViewData.swift */, + B1D1BDA522BBAFB500831367 /* ReactionsMenuView.swift */, + B1D1BDA722BBAFC900831367 /* ReactionsMenuView.xib */, + B1D211E522C194A200D939BD /* ReactionsMenuViewState.swift */, 32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */, 32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */, - 32B94DF4228EC26400716A26 /* ReactionsMenuViewModelType.swift */, - 32B94DF5228EC26400716A26 /* ReactionsMenuViewModel.swift */, - 32B94DF6228EC26400716A26 /* ReactionsMenuReaction.swift */, - 32B94DF7228EC26400716A26 /* ReactionsMenuView.xib */, ); path = ReactionsMenu; sourceTree = ""; @@ -3578,7 +3581,6 @@ B1107ECA2200B09F0038014B /* KeyBackupRecoverSuccessViewController.storyboard in Resources */, B1B5579C20EF575B00210D55 /* ForgotPasswordInputsView.xib in Resources */, F083BE011E7009ED00A9B29C /* third_party_licenses.html in Resources */, - 32B94DFE228EC26400716A26 /* ReactionsMenuView.xib in Resources */, B1098BFC21ECFE65000DDA48 /* PasswordStrengthView.xib in Resources */, B1B5573720EE6C4D00210D55 /* GroupParticipantsViewController.xib in Resources */, B110872421F098F0003554A5 /* ActivityIndicatorView.xib in Resources */, @@ -3601,6 +3603,7 @@ 3232AB1422564D9100AD6A5C /* swiftgen-config.yml in Resources */, 324A204F225FC571004FE8B0 /* DeviceVerificationIncomingViewController.storyboard in Resources */, 3232AB4B2256558300AD6A5C /* TemplateScreenViewController.storyboard in Resources */, + B1D1BDA822BBAFC900831367 /* ReactionsMenuView.xib in Resources */, 32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */, B1B5578E20EF568D00210D55 /* GroupInviteTableViewCell.xib in Resources */, B1B5582020EF625800210D55 /* SimpleRoomTitleView.xib in Resources */, @@ -3906,6 +3909,7 @@ B1B558C320EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderNameBubbleCell.m in Sources */, B110872521F098F0003554A5 /* ActivityIndicatorPresenter.swift in Sources */, 32242F1521E8FBA900725742 /* DarkTheme.swift in Sources */, + B1D211E222BD193C00D939BD /* ReactionsMenuViewModel.swift in Sources */, B140B4A621F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift in Sources */, B1B5577420EE702900210D55 /* WidgetViewController.m in Sources */, B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */, @@ -3942,7 +3946,6 @@ B1B5594520EF7BD000210D55 /* TableViewCellWithCollectionView.m in Sources */, 32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */, 32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */, - 32B94DFB228EC26400716A26 /* ReactionsMenuViewModelType.swift in Sources */, F083BDEF1E7009ED00A9B29C /* UINavigationController+Riot.m in Sources */, B1B5581F20EF625800210D55 /* SimpleRoomTitleView.m in Sources */, B1C562E2228C7C8D0037F12A /* RoomContextualMenuViewController.swift in Sources */, @@ -3959,6 +3962,7 @@ 3232ABA7225730E100AD6A5C /* DeviceVerificationStartCoordinator.swift in Sources */, B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */, B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */, + B1D1BDA622BBAFB500831367 /* ReactionsMenuView.swift in Sources */, B1B5573C20EE6C4D00210D55 /* MasterTabBarController.m in Sources */, 32F6B96E2270623100BBA352 /* DeviceVerificationDataLoadingViewModelType.swift in Sources */, B1B5592C20EF7A5D00210D55 /* TableViewCellWithButton.m in Sources */, @@ -3998,14 +4002,12 @@ 3232ABAB225730E100AD6A5C /* DeviceVerificationCoordinator.swift in Sources */, B1B5583E20EF6E7F00210D55 /* GroupRoomTableViewCell.m in Sources */, B14F143522144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.swift in Sources */, - 32B94DF8228EC26400716A26 /* ReactionsMenuView.swift in Sources */, B1B5574F20EE6C4D00210D55 /* RoomsViewController.m in Sources */, B1B5572520EE6C4D00210D55 /* RoomMessagesSearchViewController.m in Sources */, B139C22121FE5D9D00BB68EC /* KeyBackupRecoverFromPassphraseViewState.swift in Sources */, B1B5579120EF568D00210D55 /* GroupInviteTableViewCell.m in Sources */, B1B5579A20EF575B00210D55 /* ForgotPasswordInputsView.m in Sources */, B1B12B2922942315002CB419 /* UITouch.swift in Sources */, - 32B94DFD228EC26400716A26 /* ReactionsMenuReaction.swift in Sources */, B1B558CC20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B1B5571D20EE6C4D00210D55 /* HomeViewController.m in Sources */, 3232ABA6225730E100AD6A5C /* DeviceVerificationStartViewController.swift in Sources */, @@ -4063,6 +4065,7 @@ B140B4A221F87F7100E3F5FE /* OperationQueue.swift in Sources */, B1B5575120EE6C4D00210D55 /* AuthenticationViewController.m in Sources */, B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */, + B1D211E622C194A200D939BD /* ReactionsMenuViewState.swift in Sources */, B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */, B1098BE121ECE09F000DDA48 /* Images.swift in Sources */, 3232ABA4225730E100AD6A5C /* DeviceVerificationStartViewAction.swift in Sources */, @@ -4120,6 +4123,7 @@ B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */, F083BDF01E7009ED00A9B29C /* UIViewController+RiotSearch.m in Sources */, F083BDF91E7009ED00A9B29C /* RoomEmailInvitation.m in Sources */, + B1D211E422C18E3800D939BD /* ReactionsMenuViewModelType.swift in Sources */, 324A2055225FC571004FE8B0 /* DeviceVerificationIncomingViewModelType.swift in Sources */, B1B5572C20EE6C4D00210D55 /* RoomParticipantsViewController.m in Sources */, B1B558EE20EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.m in Sources */, @@ -4127,12 +4131,12 @@ 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */, B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */, B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */, + B1D211E822C195B400D939BD /* ReactionMenuItemViewData.swift in Sources */, B1DB4F0B223131600065DBFA /* String.swift in Sources */, 3232AB522256558300AD6A5C /* TemplateScreenViewModel.swift in Sources */, B1B5575B20EE6C4D00210D55 /* HomeFilesSearchViewController.m in Sources */, B139C22521FF01C100BB68EC /* KeyBackupRecoverFromPassphraseCoordinator.swift in Sources */, B1098BFD21ECFE65000DDA48 /* PasswordStrengthManager.swift in Sources */, - 32B94DFC228EC26400716A26 /* ReactionsMenuViewModel.swift in Sources */, B1B558F520EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.m in Sources */, 3232AB482256558300AD6A5C /* FlowTemplateCoordinatorType.swift in Sources */, B1B558F820EF768F00210D55 /* RoomIncomingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, From bdae76225c27dda8cd7cd3db97ae74fce77a37bb Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 25 Jun 2019 14:04:54 +0200 Subject: [PATCH 200/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7f6826381e..f291f041ca 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -13,6 +13,7 @@ Improvements: * Message Editing: Editing in the timeline (#2404). * Read receipts: They are now counted at the MatrixKit level. * Migrate to Swift 5.0. + * Reactions: Update quick reactions (#2459). Bug fix: * Device Verification: Fix user display name and device id colors in dark theme From ab02eeec8707b4b3f462ebaa1065942d448f0467 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Tue, 25 Jun 2019 09:24:46 +0000 Subject: [PATCH 201/266] Translated using Weblate (Albanian) Currently translated at 99.2% (707 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index faeb381f18..c6056007f0 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -771,3 +771,6 @@ "device_verification_emoji_headphones" = "Kufje"; "device_verification_emoji_folder" = "Dosje"; "event_formatter_message_edited_mention" = "(U përpunua)"; +// Widget +"widget_no_integrations_server_configured" = "S’ka të formësuar shërbyes integrimesh"; +"widget_integrations_server_failed_to_connect" = "S’u arrit të lidhej me shërbyes integrimesh"; From 46626a62fc6281f505b103c6394ac73b8cdc7284 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Mon, 24 Jun 2019 20:01:19 +0000 Subject: [PATCH 202/266] Translated using Weblate (Basque) Currently translated at 100.0% (713 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eu/ --- Riot/Assets/eu.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index 6ba1132a1c..aca76b965d 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -765,3 +765,7 @@ "device_verification_emoji_headphones" = "Aurikularrak"; "device_verification_emoji_folder" = "Karpeta"; "device_verification_emoji_pin" = "Txintxeta"; +// Widget +"widget_no_integrations_server_configured" = "Ez da integrazio zerbitzaririk konfiguratu"; +"widget_integrations_server_failed_to_connect" = "Huts egin du integrazioen zerbitzarira konektatzean"; +"device_verification_emoji_lock" = "Giltzarrapoa"; From fcb26d65f5d1b042834b69d881e3194b804b653c Mon Sep 17 00:00:00 2001 From: Nathan Follens Date: Tue, 25 Jun 2019 12:04:56 +0000 Subject: [PATCH 203/266] Translated using Weblate (Dutch) Currently translated at 100.0% (713 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/nl/ --- Riot/Assets/nl.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/nl.lproj/Vector.strings b/Riot/Assets/nl.lproj/Vector.strings index 0b9471acea..4038b6d7ea 100644 --- a/Riot/Assets/nl.lproj/Vector.strings +++ b/Riot/Assets/nl.lproj/Vector.strings @@ -785,3 +785,7 @@ "device_verification_emoji_folder" = "Map"; "device_verification_emoji_pin" = "Speld"; "event_formatter_message_edited_mention" = "(Bewerkt)"; +// Widget +"widget_no_integrations_server_configured" = "Geen integratieserver geconfigureerd"; +"widget_integrations_server_failed_to_connect" = "Verbinden met integratieserver mislukt"; +"device_verification_emoji_lock" = "Slot"; From 3bd8e13a63432838d30e70f08e40b37c803c4840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Tue, 25 Jun 2019 08:10:55 +0000 Subject: [PATCH 204/266] Translated using Weblate (French) Currently translated at 100.0% (713 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index 3481bf0845..91eb54c3f9 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -780,3 +780,4 @@ // Widget "widget_no_integrations_server_configured" = "Aucun serveur d’intégrations n’est configuré"; "widget_integrations_server_failed_to_connect" = "Échec de connexion au serveur d’intégrations"; +"device_verification_emoji_lock" = "Cadenas"; From af2a6a9f871ddbcd8b1b8819021f38af0bbb453a Mon Sep 17 00:00:00 2001 From: Szimszon Date: Mon, 24 Jun 2019 14:34:34 +0000 Subject: [PATCH 205/266] Translated using Weblate (Hungarian) Currently translated at 100.0% (713 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index 9d20c08494..f1556d0090 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -782,3 +782,7 @@ "device_verification_emoji_folder" = "Mappa"; "device_verification_emoji_pin" = "Rajszeg"; "event_formatter_message_edited_mention" = "(Szerkesztve)"; +// Widget +"widget_no_integrations_server_configured" = "Integrációs szerver nincs beállítva"; +"widget_integrations_server_failed_to_connect" = "Az integrációs szerverhez nem lehet csatlakozni"; +"device_verification_emoji_lock" = "Zár"; From 066927c4d49838016f4e264dba040d15ecbfcb7a Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 25 Jun 2019 16:21:48 +0200 Subject: [PATCH 206/266] Bubble cell reactions: Fix BubbleReactionViewCell clipped at bottom in selected state (Fix #2515). --- .../BubbleReactions/BubbleReactionViewCell.xib | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib index 35e41eec86..db90d9281d 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib @@ -20,16 +20,16 @@ - + - - - - + + + + From 3db1cffe28e51d632ebd9821ec49c1b68ac7ce0e Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 25 Jun 2019 18:32:34 +0200 Subject: [PATCH 207/266] Edits: Support edits in e2e rooms --- .../Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m b/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m index 77df69f6b6..ab4c455e5d 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m @@ -29,7 +29,8 @@ + (UIImage*)encryptionIconForEvent:(MXEvent*)event andSession:(MXSession*)sessio { encryptionIcon = @"e2e_unencrypted"; - if (event.isLocalEvent) + if (event.isLocalEvent + || event.contentHasBeenEdited) // Local echo for an edit is clear but uses a true event id, the one of the edited event { // Patch: Display the verified icon by default on pending outgoing messages in the encrypted rooms when the encryption is enabled MXRoom *room = [session roomWithRoomId:event.roomId]; From 32de513df596b3c49c04bef413925ca4e58ea106 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 27 Jun 2019 10:23:20 +0200 Subject: [PATCH 208/266] RoomViewController: Improve additional event actions menu behavior. Remove more action. --- Riot/Modules/Room/RoomViewController.m | 653 ++++++++++++------------- 1 file changed, 312 insertions(+), 341 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index fab2cd0559..a5c6a9c214 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2101,7 +2101,7 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac if (selectedEvent) { - [self showEditButtonAlertMenuForEvent:selectedEvent inCell:cell level:0]; + [self showContextualMenuForEvent:selectedEvent cell:cell animated:YES]; } } else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAttachmentView]) @@ -2178,8 +2178,8 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac } } -// Display the edit menu on 2 pages/levels. -- (void)showEditButtonAlertMenuForEvent:(MXEvent*)selectedEvent inCell:(id)cell level:(NSUInteger)level; +// Display the additiontal event actions menu +- (void)showAdditionalActionsMenuForEvent:(MXEvent*)selectedEvent inCell:(id)cell animated:(BOOL)animated { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell *)cell; MXKAttachment *attachment = roomBubbleTableViewCell.bubbleData.attachment; @@ -2193,42 +2193,39 @@ - (void)showEditButtonAlertMenuForEvent:(MXEvent*)selectedEvent inCell:(idcurrentAlert = nil; - - // Cancel and remove the outgoing message - [self.roomDataSource.room cancelSendingOperation:selectedEvent.eventId]; - [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; - - [self cancelEventSelection]; - } - }]]; - } - } - - if (level == 0) + // Check status of the selected event + if (selectedEvent.sentState == MXEventSentStatePreparing || + selectedEvent.sentState == MXEventSentStateEncrypting || + selectedEvent.sentState == MXEventSentStateSending) { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_quote", @"Vector", nil) + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_cancel_send", @"Vector", nil) style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + handler:^(UIAlertAction * action) + { + if (weakSelf) + { + typeof(self) self = weakSelf; + + self->currentAlert = nil; + + // Cancel and remove the outgoing message + [self.roomDataSource.room cancelSendingOperation:selectedEvent.eventId]; + [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; + + [self cancelEventSelection]; + } + + }]]; + } + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_quote", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; - if (weakSelf) + [self cancelEventSelection]; + + // Quote the message a la Markdown into the input toolbar composer + self.inputToolbarView.textMessage = [NSString stringWithFormat:@"%@\n>%@\n\n", self.inputToolbarView.textMessage, selectedComponent.textMessage]; + + // And display the keyboard + [self.inputToolbarView becomeFirstResponder]; + } + + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_share", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + NSArray *activityItems = @[selectedComponent.textMessage]; + + UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil]; + + if (activityViewController) { - typeof(self) self = weakSelf; + activityViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; + activityViewController.popoverPresentationController.sourceView = roomBubbleTableViewCell; + activityViewController.popoverPresentationController.sourceRect = roomBubbleTableViewCell.bounds; - [self cancelEventSelection]; - - // Quote the message a la Markdown into the input toolbar composer - self.inputToolbarView.textMessage = [NSString stringWithFormat:@"%@\n>%@\n\n", self.inputToolbarView.textMessage, selectedComponent.textMessage]; - - // And display the keyboard - [self.inputToolbarView becomeFirstResponder]; + [self presentViewController:activityViewController animated:YES completion:nil]; } - - }]]; - } - - if (level == 1) + } + + }]]; + } + else // Add action for attachment + { + if (attachment.type == MXKAttachmentTypeImage || attachment.type == MXKAttachmentTypeVideo) { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_share", @"Vector", nil) + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_save", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { @@ -2308,109 +2328,75 @@ - (void)showEditButtonAlertMenuForEvent:(MXEvent*)selectedEvent inCell:(idcurrentAlert = nil; - [self startActivityIndicator]; + // Remove the outgoing message and its related cached file. + [[NSFileManager defaultManager] removeItemAtPath:roomBubbleTableViewCell.bubbleData.attachment.cacheFilePath error:nil]; + [[NSFileManager defaultManager] removeItemAtPath:roomBubbleTableViewCell.bubbleData.attachment.thumbnailCachePath error:nil]; - [attachment save:^{ - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - } failure:^(NSError *error) { - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - //Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; - - }]; + // Cancel and remove the outgoing message + [self.roomDataSource.room cancelSendingOperation:selectedEvent.eventId]; + [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; - // Start animation in case of download during attachment preparing - [roomBubbleTableViewCell startProgressUI]; + [self cancelEventSelection]; } }]]; } - - // Check status of the selected event - if (selectedEvent.sentState == MXEventSentStatePreparing || - selectedEvent.sentState == MXEventSentStateEncrypting || - selectedEvent.sentState == MXEventSentStateUploading || - selectedEvent.sentState == MXEventSentStateSending) - { - // Upload id is stored in attachment url (nasty trick) - NSString *uploadId = roomBubbleTableViewCell.bubbleData.attachment.contentURL; - if ([MXMediaManager existingUploaderWithId:uploadId]) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_cancel_send", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - // Get again the loader - MXMediaLoader *loader = [MXMediaManager existingUploaderWithId:uploadId]; - if (loader) - { - [loader cancel]; - } - // Hide the progress animation - roomBubbleTableViewCell.progressView.hidden = YES; - - if (weakSelf) - { - typeof(self) self = weakSelf; - - self->currentAlert = nil; - - // Remove the outgoing message and its related cached file. - [[NSFileManager defaultManager] removeItemAtPath:roomBubbleTableViewCell.bubbleData.attachment.cacheFilePath error:nil]; - [[NSFileManager defaultManager] removeItemAtPath:roomBubbleTableViewCell.bubbleData.attachment.thumbnailCachePath error:nil]; - - // Cancel and remove the outgoing message - [self.roomDataSource.room cancelSendingOperation:selectedEvent.eventId]; - [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; - - [self cancelEventSelection]; - } - - }]]; - } - } } - - if (level == 1 && (attachment.type != MXKAttachmentTypeSticker)) + + if (attachment.type != MXKAttachmentTypeSticker) { [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_share", @"Vector", nil) style:UIAlertActionStyleDefault @@ -2455,7 +2441,7 @@ - (void)showEditButtonAlertMenuForEvent:(MXEvent*)selectedEvent inCell:(idcurrentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_event_action_report_prompt_reason", @"Vector", nil) message:nil preferredStyle:UIAlertControllerStyleAlert]; - - [self->currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) { - textField.secureTextEntry = NO; - textField.placeholder = nil; - textField.keyboardType = UIKeyboardTypeDefault; - }]; + [self cancelEventSelection]; + + // Prompt user to enter a description of the problem content. + self->currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_event_action_report_prompt_reason", @"Vector", nil) message:nil preferredStyle:UIAlertControllerStyleAlert]; + + [self->currentAlert addTextFieldWithConfigurationHandler:^(UITextField *textField) { + textField.secureTextEntry = NO; + textField.placeholder = nil; + textField.keyboardType = UIKeyboardTypeDefault; + }]; + + [self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - [self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"ok"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + if (weakSelf) + { + typeof(self) self = weakSelf; + NSString *text = [self->currentAlert textFields].firstObject.text; + self->currentAlert = nil; - if (weakSelf) - { - typeof(self) self = weakSelf; - NSString *text = [self->currentAlert textFields].firstObject.text; - self->currentAlert = nil; + [self startActivityIndicator]; + + [self.roomDataSource.room reportEvent:selectedEvent.eventId score:-100 reason:text success:^{ - [self startActivityIndicator]; + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; - [self.roomDataSource.room reportEvent:selectedEvent.eventId score:-100 reason:text success:^{ - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - // Prompt user to ignore content from this user - self->currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_event_action_report_prompt_ignore_user", @"Vector", nil) message:nil preferredStyle:UIAlertControllerStyleAlert]; + // Prompt user to ignore content from this user + self->currentAlert = [UIAlertController alertControllerWithTitle:NSLocalizedStringFromTable(@"room_event_action_report_prompt_ignore_user", @"Vector", nil) message:nil preferredStyle:UIAlertControllerStyleAlert]; + + [self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"yes"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - [self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"yes"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + + [self startActivityIndicator]; - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; + // Add the user to the blacklist: ignored users + [self.mainSession ignoreUsers:@[selectedEvent.sender] success:^{ - [self startActivityIndicator]; + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; - // Add the user to the blacklist: ignored users - [self.mainSession ignoreUsers:@[selectedEvent.sender] success:^{ - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - } failure:^(NSError *error) { - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - NSLog(@"[RoomVC] Ignore user (%@) failed", selectedEvent.sender); - //Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; - - }]; - } - - }]]; - - [self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"no"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - - }]]; - - [self presentViewController:self->currentAlert animated:YES completion:nil]; - - } failure:^(NSError *error) { + } failure:^(NSError *error) { + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + NSLog(@"[RoomVC] Ignore user (%@) failed", selectedEvent.sender); + //Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; + }]]; + + [self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"no"] style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { - NSLog(@"[RoomVC] Report event (%@) failed", selectedEvent.eventId); - //Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } - }]; - } - - }]]; + }]]; + + [self presentViewController:self->currentAlert animated:YES completion:nil]; + + } failure:^(NSError *error) { + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + NSLog(@"[RoomVC] Report event (%@) failed", selectedEvent.eventId); + //Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; + } - [self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = nil; - } - - }]]; + }]]; + + [self->currentAlert addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:UIAlertActionStyleCancel handler:^(UIAlertAction * action) { - [self presentViewController:self->currentAlert animated:YES completion:nil]; - } + if (weakSelf) + { + typeof(self) self = weakSelf; + self->currentAlert = nil; + } + + }]]; - }]]; - } - - if (level == 1 && self.roomDataSource.room.summary.isEncrypted) + [self presentViewController:self->currentAlert animated:YES completion:nil]; + } + + }]]; + + if (self.roomDataSource.room.summary.isEncrypted) { [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_view_encryption", @"Vector", nil) style:UIAlertActionStyleDefault @@ -2711,25 +2686,6 @@ - (void)showEditButtonAlertMenuForEvent:(MXEvent*)selectedEvent inCell:(idcurrentAlert = nil; - - // Show the next level of options - [self showEditButtonAlertMenuForEvent:selectedEvent inCell:cell level:1]; - } - - }]]; - } } [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"cancel", @"Vector", nil) @@ -2747,10 +2703,24 @@ - (void)showEditButtonAlertMenuForEvent:(MXEvent*)selectedEvent inCell:(id 1) { + NSArray *components = roomBubbleTableViewCell.bubbleData.bubbleComponents; + + NSInteger index = 0; + for (MXKRoomBubbleComponent *component in components) + { + if ([component.event.eventId isEqualToString:selectedEvent.eventId]) + { + break; + } + index++; + } + + CGRect sourceRect = [roomBubbleTableViewCell componentFrameInContentViewForIndex:index]; + [currentAlert mxk_setAccessibilityIdentifier:@"RoomVCEventMenuAlert"]; [currentAlert popoverPresentationController].sourceView = roomBubbleTableViewCell; - [currentAlert popoverPresentationController].sourceRect = roomBubbleTableViewCell.bounds; - [self presentViewController:currentAlert animated:YES completion:nil]; + [currentAlert popoverPresentationController].sourceRect = sourceRect; + [self presentViewController:currentAlert animated:animated completion:nil]; } else { @@ -5136,7 +5106,8 @@ - (void)removeMXSessionStateChangeNotificationsListener RoomContextualMenuItem *moreMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionMore]; moreMenuItem.action = ^{ MXStrongifyAndReturnIfNil(self); - [self showEditButtonAlertMenuForEvent:event inCell:cell level:0]; + [self hideContextualMenuAnimated:YES completion:nil]; + [self showAdditionalActionsMenuForEvent:event inCell:cell animated:YES]; }; // Actions list From 392d2ae97234edfafb80c30dbd7376d6f1c30203 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 27 Jun 2019 11:41:25 +0200 Subject: [PATCH 209/266] RoomVC: Reduce animation duration when presenting contextual menu. Make animation quicker for a single tap. --- .../RoomContextualMenuPresenter.swift | 18 ++++++++++++------ Riot/Modules/Room/RoomViewController.m | 15 ++++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift index 58e9e2bd48..fb7b17398b 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift @@ -21,8 +21,11 @@ final class RoomContextualMenuPresenter: NSObject { // MARK: - Constants - private enum Constants { - static let animationDuration: TimeInterval = 0.2 + private enum AnimationDurations { + static let showMenu: TimeInterval = 0.15 + static let showMenuFromSingleTap: TimeInterval = 0.1 + static let hideMenu: TimeInterval = 0.2 + static let selectedReaction: TimeInterval = 0.15 } // MARK: - Properties @@ -43,6 +46,7 @@ final class RoomContextualMenuPresenter: NSObject { from viewController: UIViewController, on view: UIView, contentToReactFrame: CGRect, // Not nullable for compatibility with Obj-C + fromSingleTapGesture usedSingleTapGesture: Bool, animated: Bool, completion: (() -> Void)?) { guard self.roomContextualMenuViewController == nil else { @@ -68,7 +72,9 @@ final class RoomContextualMenuPresenter: NSObject { } if animated { - UIView.animate(withDuration: Constants.animationDuration, animations: { + let animationDuration = usedSingleTapGesture ? AnimationDurations.showMenuFromSingleTap : AnimationDurations.showMenu + + UIView.animate(withDuration: animationDuration, animations: { animationInstructions() }, completion: { completed in completion?() @@ -98,10 +104,10 @@ final class RoomContextualMenuPresenter: NSObject { if animated { if roomContextualMenuViewController.shouldPerformTappedReactionAnimation { - UIView.animate(withDuration: 0.15, animations: { + UIView.animate(withDuration: AnimationDurations.selectedReaction, animations: { roomContextualMenuViewController.selectedReactionAnimationsIntructionsPart1() }, completion: { _ in - UIView.animate(withDuration: Constants.animationDuration, animations: { + UIView.animate(withDuration: AnimationDurations.hideMenu, animations: { roomContextualMenuViewController.selectedReactionAnimationsIntructionsPart2() animationInstructions() }, completion: { completed in @@ -109,7 +115,7 @@ final class RoomContextualMenuPresenter: NSObject { }) }) } else { - UIView.animate(withDuration: Constants.animationDuration, animations: { + UIView.animate(withDuration: AnimationDurations.hideMenu, animations: { animationInstructions() }, completion: { completed in animationCompletionInstructions() diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index a5c6a9c214..0dc88ed521 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -1519,7 +1519,7 @@ - (void)handleLongPressFromCell:(id)cell withTappedEvent:(MXEv { if (event && !customizedRoomDataSource.selectedEventId) { - [self showContextualMenuForEvent:event cell:cell animated:YES]; + [self showContextualMenuForEvent:event fromSingleTapGesture:NO cell:cell animated:YES]; } } @@ -2083,7 +2083,7 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac } else { - [self showContextualMenuForEvent:tappedEvent cell:cell animated:YES]; + [self showContextualMenuForEvent:tappedEvent fromSingleTapGesture:YES cell:cell animated:YES]; } } } @@ -2101,7 +2101,7 @@ - (void)dataSource:(MXKDataSource *)dataSource didRecognizeAction:(NSString *)ac if (selectedEvent) { - [self showContextualMenuForEvent:selectedEvent cell:cell animated:YES]; + [self showContextualMenuForEvent:selectedEvent fromSingleTapGesture:YES cell:cell animated:YES]; } } else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnAttachmentView]) @@ -5122,7 +5122,7 @@ - (void)removeMXSessionStateChangeNotificationsListener return actionItems; } -- (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)cell animated:(BOOL)animated +- (void)showContextualMenuForEvent:(MXEvent*)event fromSingleTapGesture:(BOOL)usedSingleTapGesture cell:(id)cell animated:(BOOL)animated { if (self.roomContextualMenuPresenter.isPresenting) { @@ -5131,8 +5131,6 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce NSString *selectedEventId = event.eventId; - [self selectEventWithId:selectedEventId]; - NSArray* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell]; ReactionsMenuViewModel *reactionsMenuViewModel; CGRect bubbleComponentFrameInOverlayView = CGRectNull; @@ -5183,9 +5181,12 @@ - (void)showContextualMenuForEvent:(MXEvent*)event cell:(id)ce from:self on:self.overlayContainerView contentToReactFrame:bubbleComponentFrameInOverlayView - animated:YES + fromSingleTapGesture:usedSingleTapGesture + animated:animated completion:^{ }]; + + [self selectEventWithId:selectedEventId]; } - (void)hideContextualMenuAnimated:(BOOL)animated From 3eda67435e6202105bcd50472bb9dc58d0e9cba9 Mon Sep 17 00:00:00 2001 From: manuroe Date: Thu, 27 Jun 2019 12:25:01 +0200 Subject: [PATCH 210/266] BF: Edits: editing an unsent msg gets cancelled if the original msg send completes during the edit #2495 --- Riot/Modules/Room/RoomViewController.m | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index fab2cd0559..b26c58775b 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -287,6 +287,7 @@ - (void)finalizeInit // Listen to the event sent state changes [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeSentState:) name:kMXEventDidChangeSentStateNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(eventDidChangeIdentifier:) name:kMXEventDidChangeIdentifierNotification object:nil]; } - (void)viewDidLoad @@ -1221,6 +1222,7 @@ - (void)destroy missedDiscussionsBadgeLabel = nil; [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXEventDidChangeSentStateNotification object:nil]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:kMXEventDidChangeIdentifierNotification object:nil]; [super destroy]; } @@ -4564,6 +4566,18 @@ - (void)eventDidChangeSentState:(NSNotification *)notif } } +- (void)eventDidChangeIdentifier:(NSNotification *)notif +{ + MXEvent *event = notif.object; + NSString *previousId = notif.userInfo[kMXEventIdentifierKey]; + + if ([customizedRoomDataSource.selectedEventId isEqualToString:previousId]) + { + NSLog(@"[RoomVC] eventDidChangeIdentifier: Update selectedEventId"); + customizedRoomDataSource.selectedEventId = event.eventId; + } +} + - (void)resendAllUnsentMessages { From cfad998342396018e317336a68bea46cc676a279 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Thu, 27 Jun 2019 09:35:51 +0000 Subject: [PATCH 211/266] Translated using Weblate (Bulgarian) Currently translated at 100.0% (713 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/bg/ --- Riot/Assets/bg.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/bg.lproj/Vector.strings b/Riot/Assets/bg.lproj/Vector.strings index b6247fda09..404ba0d083 100644 --- a/Riot/Assets/bg.lproj/Vector.strings +++ b/Riot/Assets/bg.lproj/Vector.strings @@ -776,3 +776,7 @@ "device_verification_emoji_folder" = "Папка"; "device_verification_emoji_pin" = "Карфица"; "event_formatter_message_edited_mention" = "(Редактирано)"; +// Widget +"widget_no_integrations_server_configured" = "Не е конфигуриран сървър за интеграции"; +"widget_integrations_server_failed_to_connect" = "Неуспешна връзка със сървъра за интеграции"; +"device_verification_emoji_lock" = "Катинар"; From 9a217a10987a70f466943d485a55dab64348074c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 27 Jun 2019 14:37:17 +0200 Subject: [PATCH 212/266] RoomVC: Improve RoomContextualMenuPresenter loading speed. --- .../ReactionsMenu/ReactionsMenuView.swift | 27 ++++++-- .../ReactionsMenu/ReactionsMenuView.xib | 66 ------------------- .../RoomContextualMenuPresenter.swift | 7 +- .../RoomContextualMenuViewController.swift | 63 ++++++++++++------ Riot/Modules/Room/RoomViewController.m | 13 +++- 5 files changed, 80 insertions(+), 96 deletions(-) diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift index ce4d91cf74..5ceb40d9b2 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.swift @@ -96,13 +96,32 @@ final class ReactionsMenuView: UIView, Themable, NibLoadable { self.reactionsStackView.vc_removeAllSubviews() + let reactionsStackViewCount = self.reactionsStackView.arrangedSubviews.count + + // Remove all menu buttons if reactions count has changed + if reactionsStackViewCount != self.reactionViewDatas.count { + self.reactionsStackView.vc_removeAllSubviews() + } + + var index = 0 + for reactionViewData in self.reactionViewDatas { - let reactionsMenuButton = ReactionsMenuButton() + + let reactionsMenuButton: ReactionsMenuButton + + if index < reactionsStackViewCount, let foundReactionsMenuButton = self.reactionsStackView.arrangedSubviews[index] as? ReactionsMenuButton { + reactionsMenuButton = foundReactionsMenuButton + } else { + reactionsMenuButton = ReactionsMenuButton() + reactionsMenuButton.addTarget(self, action: #selector(reactionButtonAction), for: .touchUpInside) + self.reactionsStackView.addArrangedSubview(reactionsMenuButton) + self.reactionButtons.append(reactionsMenuButton) + } + reactionsMenuButton.setTitle(reactionViewData.emoji, for: .normal) reactionsMenuButton.isSelected = reactionViewData.isSelected - reactionsMenuButton.addTarget(self, action: #selector(reactionButtonAction), for: .touchUpInside) - self.reactionsStackView.addArrangedSubview(reactionsMenuButton) - self.reactionButtons.append(reactionsMenuButton) + + index+=1 } } diff --git a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib index f2920d92b3..f9c6c01133 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib @@ -20,72 +20,6 @@ - - - - - - - - - - diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift index fb7b17398b..04d87ad924 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuPresenter.swift @@ -37,7 +37,7 @@ final class RoomContextualMenuPresenter: NSObject { // MARK: Public var isPresenting: Bool { - return self.roomContextualMenuViewController != nil + return self.roomContextualMenuViewController?.parent != nil } // MARK: - Public @@ -49,7 +49,7 @@ final class RoomContextualMenuPresenter: NSObject { fromSingleTapGesture usedSingleTapGesture: Bool, animated: Bool, completion: (() -> Void)?) { - guard self.roomContextualMenuViewController == nil else { + guard self.isPresenting == false else { return } @@ -86,7 +86,7 @@ final class RoomContextualMenuPresenter: NSObject { } func hideContextualMenu(animated: Bool, completion: (() -> Void)?) { - guard let roomContextualMenuViewController = self.roomContextualMenuViewController else { + guard let roomContextualMenuViewController = self.roomContextualMenuViewController, self.isPresenting else { completion?() return } @@ -99,6 +99,7 @@ final class RoomContextualMenuPresenter: NSObject { let animationCompletionInstructions: (() -> Void) = { roomContextualMenuViewController.vc_removeFromParent() + self.roomContextualMenuViewController = nil completion?() } diff --git a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift index 72594e2480..baba7f279c 100644 --- a/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuViewController.swift @@ -67,6 +67,10 @@ final class RoomContextualMenuViewController: UIViewController, Themable { return -(self.menuToolbarViewHeightConstraint.constant + bottomSafeAreaHeight) } + private var shouldPresentReactionsMenu: Bool { + return self.reactionsMenuContainerView.isHidden == false + } + // MARK: Public var contentToReactFrame: CGRect? @@ -78,38 +82,38 @@ final class RoomContextualMenuViewController: UIViewController, Themable { // MARK: - Setup - class func instantiate(with contextualMenuItems: [RoomContextualMenuItem], - reactionsMenuViewModel: ReactionsMenuViewModel?) -> RoomContextualMenuViewController { + class func instantiate() -> RoomContextualMenuViewController { let viewController = StoryboardScene.RoomContextualMenuViewController.initialScene.instantiate() viewController.theme = ThemeService.shared().theme - viewController.contextualMenuItems = contextualMenuItems - viewController.reactionsMenuViewModel = reactionsMenuViewModel return viewController } - // MARK: - Life cycle override func viewDidLoad() { super.viewDidLoad() - // Do any additional setup after loading the view. - self.reactionsMenuContainerView.isHidden = true - - if let reactionsMenuViewModel = self.reactionsMenuViewModel { - self.setupReactionsMenu(with: reactionsMenuViewModel) - } + // Do any additional setup after loading the view. self.backgroundOverlayView.isUserInteractionEnabled = true - self.menuToolbarView.fill(contextualMenuItems: self.contextualMenuItems) self.setupBackgroundOverlayGestureRecognizers() + self.updateViews() + self.registerThemeServiceDidChangeThemeNotification() self.update(theme: self.theme) } // MARK: - Public + func update(contextualMenuItems: [RoomContextualMenuItem], reactionsMenuViewModel: ReactionsMenuViewModel?) { + self.contextualMenuItems = contextualMenuItems + self.reactionsMenuViewModel = reactionsMenuViewModel + if self.isViewLoaded { + self.updateViews() + } + } + func showMenuToolbar() { self.menuToolbarViewBottomConstraint.constant = 0 self.menuToolbarView.alpha = 1 @@ -121,7 +125,7 @@ final class RoomContextualMenuViewController: UIViewController, Themable { } func prepareReactionsMenuAnimations() { - guard let frame = self.contentToReactFrame else { + guard let frame = self.contentToReactFrame, frame.equalTo(CGRect.null) == false else { return } @@ -160,7 +164,7 @@ final class RoomContextualMenuViewController: UIViewController, Themable { } func showReactionsMenu() { - guard let reactionsMenuView = self.reactionsMenuView else { + guard self.shouldPresentReactionsMenu, let reactionsMenuView = self.reactionsMenuView else { return } @@ -173,7 +177,7 @@ final class RoomContextualMenuViewController: UIViewController, Themable { } func hideReactionsMenu() { - guard let reactionsMenuView = self.reactionsMenuView else { + guard self.shouldPresentReactionsMenu, let reactionsMenuView = self.reactionsMenuView else { return } @@ -201,11 +205,30 @@ final class RoomContextualMenuViewController: UIViewController, Themable { // MARK: - Private - private func setupReactionsMenu(with viewModel: ReactionsMenuViewModel) { - let reactionsMenuView = ReactionsMenuView.loadFromNib() - self.reactionsMenuContainerView.vc_addSubViewMatchingParent(reactionsMenuView) - reactionsMenuView.viewModel = viewModel - self.reactionsMenuView = reactionsMenuView + private func updateViews() { + self.menuToolbarView.fill(contextualMenuItems: self.contextualMenuItems) + + let hideReactionMenu: Bool + + if let reactionsMenuViewModel = self.reactionsMenuViewModel { + hideReactionMenu = false + self.updateReactionsMenu(with: reactionsMenuViewModel) + } else { + hideReactionMenu = true + } + + self.reactionsMenuContainerView.isHidden = hideReactionMenu + } + + private func updateReactionsMenu(with viewModel: ReactionsMenuViewModel) { + + if self.reactionsMenuContainerView.subviews.isEmpty { + let reactionsMenuView = ReactionsMenuView.loadFromNib() + self.reactionsMenuContainerView.vc_addSubViewMatchingParent(reactionsMenuView) + self.reactionsMenuView = reactionsMenuView + } + + self.reactionsMenuView?.viewModel = viewModel } private func setupBackgroundOverlayGestureRecognizers() { diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 0dc88ed521..fdcd5db980 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -216,6 +216,8 @@ @interface RoomViewController () Date: Fri, 28 Jun 2019 18:16:27 +0200 Subject: [PATCH 213/266] Edits: Start history --- Riot.xcodeproj/project.pbxproj | 48 +++++ Riot/Generated/Storyboards.swift | 5 + .../EditHistory/EditHistoryCoordinator.swift | 67 +++++++ ...ditHistoryCoordinatorBridgePresenter.swift | 89 +++++++++ .../EditHistoryCoordinatorType.swift | 28 +++ .../Room/EditHistory/EditHistoryMessage.swift | 22 +++ .../EditHistory/EditHistoryViewAction.swift | 25 +++ .../EditHistoryViewController.storyboard | 92 +++++++++ .../EditHistoryViewController.swift | 185 ++++++++++++++++++ .../EditHistory/EditHistoryViewModel.swift | 128 ++++++++++++ .../EditHistoryViewModelType.swift | 38 ++++ .../EditHistory/EditHistoryViewState.swift | 26 +++ 12 files changed, 753 insertions(+) create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryCoordinatorType.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryMessage.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryViewAction.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryViewController.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryViewState.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 21d874c542..5271276e58 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -75,6 +75,16 @@ 32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D6F2264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift */; }; 32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */; }; 32891D76226728EF00C82226 /* DeviceVerificationDataLoadingViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */; }; + 32A6001622C661100042C1D9 /* EditHistoryViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6000D22C661100042C1D9 /* EditHistoryViewState.swift */; }; + 32A6001722C661100042C1D9 /* EditHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6000E22C661100042C1D9 /* EditHistoryViewController.swift */; }; + 32A6001822C661100042C1D9 /* EditHistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6000F22C661100042C1D9 /* EditHistoryViewModel.swift */; }; + 32A6001922C661100042C1D9 /* EditHistoryViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6001022C661100042C1D9 /* EditHistoryViewModelType.swift */; }; + 32A6001A22C661100042C1D9 /* EditHistoryCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6001122C661100042C1D9 /* EditHistoryCoordinator.swift */; }; + 32A6001B22C661100042C1D9 /* EditHistoryViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6001222C661100042C1D9 /* EditHistoryViewAction.swift */; }; + 32A6001C22C661100042C1D9 /* EditHistoryViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32A6001322C661100042C1D9 /* EditHistoryViewController.storyboard */; }; + 32A6001D22C661100042C1D9 /* EditHistoryCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6001422C661100042C1D9 /* EditHistoryCoordinatorType.swift */; }; + 32A6001E22C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6001522C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift */; }; + 32A6002022C66FCF0042C1D9 /* EditHistoryMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6001F22C66FCF0042C1D9 /* EditHistoryMessage.swift */; }; 32B1FEDB21A46F2C00637127 /* TermsView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32B1FEDA21A46F2C00637127 /* TermsView.xib */; }; 32B94DF9228EC26400716A26 /* ReactionsMenuViewAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */; }; 32B94DFA228EC26400716A26 /* ReactionsMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */; }; @@ -607,6 +617,16 @@ 32891D6F2264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifiedViewController.swift; sourceTree = ""; }; 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewController.swift; sourceTree = ""; }; 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DeviceVerificationDataLoadingViewController.storyboard; sourceTree = ""; }; + 32A6000D22C661100042C1D9 /* EditHistoryViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryViewState.swift; sourceTree = ""; }; + 32A6000E22C661100042C1D9 /* EditHistoryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryViewController.swift; sourceTree = ""; }; + 32A6000F22C661100042C1D9 /* EditHistoryViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryViewModel.swift; sourceTree = ""; }; + 32A6001022C661100042C1D9 /* EditHistoryViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryViewModelType.swift; sourceTree = ""; }; + 32A6001122C661100042C1D9 /* EditHistoryCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryCoordinator.swift; sourceTree = ""; }; + 32A6001222C661100042C1D9 /* EditHistoryViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryViewAction.swift; sourceTree = ""; }; + 32A6001322C661100042C1D9 /* EditHistoryViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = EditHistoryViewController.storyboard; sourceTree = ""; }; + 32A6001422C661100042C1D9 /* EditHistoryCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryCoordinatorType.swift; sourceTree = ""; }; + 32A6001522C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryCoordinatorBridgePresenter.swift; sourceTree = ""; }; + 32A6001F22C66FCF0042C1D9 /* EditHistoryMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistoryMessage.swift; sourceTree = ""; }; 32B1FEDA21A46F2C00637127 /* TermsView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TermsView.xib; sourceTree = ""; }; 32B94DF2228EC26400716A26 /* ReactionsMenuViewAction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuViewAction.swift; sourceTree = ""; }; 32B94DF3228EC26400716A26 /* ReactionsMenuButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReactionsMenuButton.swift; sourceTree = ""; }; @@ -1509,6 +1529,23 @@ path = js; sourceTree = ""; }; + 32A6000C22C661100042C1D9 /* EditHistory */ = { + isa = PBXGroup; + children = ( + 32A6000D22C661100042C1D9 /* EditHistoryViewState.swift */, + 32A6000E22C661100042C1D9 /* EditHistoryViewController.swift */, + 32A6000F22C661100042C1D9 /* EditHistoryViewModel.swift */, + 32A6001022C661100042C1D9 /* EditHistoryViewModelType.swift */, + 32A6001122C661100042C1D9 /* EditHistoryCoordinator.swift */, + 32A6001222C661100042C1D9 /* EditHistoryViewAction.swift */, + 32A6001322C661100042C1D9 /* EditHistoryViewController.storyboard */, + 32A6001422C661100042C1D9 /* EditHistoryCoordinatorType.swift */, + 32A6001522C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift */, + 32A6001F22C66FCF0042C1D9 /* EditHistoryMessage.swift */, + ); + path = EditHistory; + sourceTree = ""; + }; 32B94DF0228EC26400716A26 /* ReactionsMenu */ = { isa = PBXGroup; children = ( @@ -2085,6 +2122,7 @@ B1B5568E20EE6C4C00210D55 /* Room */ = { isa = PBXGroup; children = ( + 32A6000C22C661100042C1D9 /* EditHistory */, B1B5568F20EE6C4C00210D55 /* RoomViewController.h */, B1B556A020EE6C4C00210D55 /* RoomViewController.m */, B1B5569620EE6C4C00210D55 /* RoomViewController.xib */, @@ -3621,6 +3659,7 @@ B1B557C020EF5B4500210D55 /* RoomInputToolbarView.xib in Resources */, B1B5583D20EF6E7F00210D55 /* GroupRoomTableViewCell.xib in Resources */, B1B5572D20EE6C4D00210D55 /* RoomParticipantsViewController.xib in Resources */, + 32A6001C22C661100042C1D9 /* EditHistoryViewController.storyboard in Resources */, B1B5577220EE702800210D55 /* JitsiViewController.xib in Resources */, B1B557D720EF5EA900210D55 /* RoomActivitiesView.xib in Resources */, B1098BF821ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard in Resources */, @@ -3913,6 +3952,7 @@ B140B4A621F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift in Sources */, B1B5577420EE702900210D55 /* WidgetViewController.m in Sources */, B139C21B21FE5B9200BB68EC /* KeyBackupRecoverFromPassphraseViewModel.swift in Sources */, + 32A6001E22C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift in Sources */, B1B5574A20EE6C4D00210D55 /* MediaPickerViewController.m in Sources */, B1B5598520EFC3E000210D55 /* RageShakeManager.m in Sources */, 3232ABA8225730E100AD6A5C /* DeviceVerificationStartViewState.swift in Sources */, @@ -3959,6 +3999,7 @@ B1B558DD20EF768F00210D55 /* RoomIncomingEncryptedTextMsgBubbleCell.m in Sources */, B1098BE521ECE1FC000DDA48 /* Storyboards.swift in Sources */, 3232ABC2225B996200AD6A5C /* Themable.swift in Sources */, + 32A6001B22C661100042C1D9 /* EditHistoryViewAction.swift in Sources */, 3232ABA7225730E100AD6A5C /* DeviceVerificationStartCoordinator.swift in Sources */, B1D4752721EE4E630067973F /* KeyboardAvoider.swift in Sources */, B1D4752821EE4E630067973F /* KeyboardNotification.swift in Sources */, @@ -4024,6 +4065,7 @@ B1B5572620EE6C4D00210D55 /* RoomFilesSearchViewController.m in Sources */, B1B5583120EF66BA00210D55 /* RoomIdOrAliasTableViewCell.m in Sources */, B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */, + 32A6001D22C661100042C1D9 /* EditHistoryCoordinatorType.swift in Sources */, F083BDFA1E7009ED00A9B29C /* RoomPreviewData.m in Sources */, B1B557B420EF5AEF00210D55 /* EventDetailsView.m in Sources */, B1B5577E20EE84BF00210D55 /* IncomingCallView.m in Sources */, @@ -4041,6 +4083,7 @@ B1C562CA2289C2690037F12A /* UIGestureRecognizer.swift in Sources */, B1C562CC228AB3510037F12A /* UIStackView.swift in Sources */, B1B557BE20EF5B4500210D55 /* RoomInputToolbarView.m in Sources */, + 32A6001922C661100042C1D9 /* EditHistoryViewModelType.swift in Sources */, B1B5573B20EE6C4D00210D55 /* FavouritesViewController.m in Sources */, B1B5579920EF575B00210D55 /* AuthInputsView.m in Sources */, B1B5597520EFB02A00210D55 /* InviteRecentTableViewCell.m in Sources */, @@ -4049,8 +4092,10 @@ B1B5573520EE6C4D00210D55 /* GroupDetailsViewController.m in Sources */, B10B3B5B2201DD740072C76B /* KeyBackupBannerCell.swift in Sources */, B1963B32228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift in Sources */, + 32A6001722C661100042C1D9 /* EditHistoryViewController.swift in Sources */, B1098BFA21ECFE65000DDA48 /* KeyBackupSetupPassphraseViewModel.swift in Sources */, B1B5575220EE6C4D00210D55 /* RoomKeyRequestViewController.m in Sources */, + 32A6001A22C661100042C1D9 /* EditHistoryCoordinator.swift in Sources */, F083BD1E1E7009ED00A9B29C /* AppDelegate.m in Sources */, B1B558E620EF768F00210D55 /* RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m in Sources */, B1098BFB21ECFE65000DDA48 /* KeyBackupSetupCoordinatorType.swift in Sources */, @@ -4060,6 +4105,7 @@ B1B557D820EF5EA900210D55 /* RoomActivitiesView.m in Sources */, B1B5596620EF9E9B00210D55 /* RoomTableViewCell.m in Sources */, B14F143322144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModel.swift in Sources */, + 32A6001822C661100042C1D9 /* EditHistoryViewModel.swift in Sources */, B1B558D020EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.m in Sources */, B1B558CF20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithPaginationTitleBubbleCell.m in Sources */, B140B4A221F87F7100E3F5FE /* OperationQueue.swift in Sources */, @@ -4129,6 +4175,7 @@ B1B558EE20EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.m in Sources */, 3232ABB52257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift in Sources */, 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */, + 32A6002022C66FCF0042C1D9 /* EditHistoryMessage.swift in Sources */, B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */, B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */, B1D211E822C195B400D939BD /* ReactionMenuItemViewData.swift in Sources */, @@ -4154,6 +4201,7 @@ B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */, B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */, B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */, + 32A6001622C661100042C1D9 /* EditHistoryViewState.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Riot/Generated/Storyboards.swift b/Riot/Generated/Storyboards.swift index b2aa4fda87..b8647542a8 100644 --- a/Riot/Generated/Storyboards.swift +++ b/Riot/Generated/Storyboards.swift @@ -37,6 +37,11 @@ internal enum StoryboardScene { internal static let initialScene = InitialSceneType(storyboard: DeviceVerificationVerifyViewController.self) } + internal enum EditHistoryViewController: StoryboardType { + internal static let storyboardName = "EditHistoryViewController" + + internal static let initialScene = InitialSceneType(storyboard: EditHistoryViewController.self) + } internal enum KeyBackupRecoverFromPassphraseViewController: StoryboardType { internal static let storyboardName = "KeyBackupRecoverFromPassphraseViewController" diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift new file mode 100644 index 0000000000..29dcb6500b --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift @@ -0,0 +1,67 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/EditHistory EditHistory +/* + Copyright 2019 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 + +final class EditHistoryCoordinator: EditHistoryCoordinatorType { + + // MARK: - Properties + + // MARK: Private + + private var editHistoryViewModel: EditHistoryViewModelType + private let editHistoryViewController: EditHistoryViewController + + // MARK: Public + + // Must be used only internally + var childCoordinators: [Coordinator] = [] + + weak var delegate: EditHistoryCoordinatorDelegate? + + // MARK: - Setup + + init(aggregations: MXAggregations, + roomId: String, + eventId: String) { + + let editHistoryViewModel = EditHistoryViewModel(aggregations: aggregations, roomId: roomId, eventId: eventId) + let editHistoryViewController = EditHistoryViewController.instantiate(with: editHistoryViewModel) + self.editHistoryViewModel = editHistoryViewModel + self.editHistoryViewController = editHistoryViewController + } + + // MARK: - Public methods + + func start() { + self.editHistoryViewModel.coordinatorDelegate = self + } + + func toPresentable() -> UIViewController { + return self.editHistoryViewController + } +} + +// MARK: - EditHistoryViewModelCoordinatorDelegate +extension EditHistoryCoordinator: EditHistoryViewModelCoordinatorDelegate { + + func editHistoryViewModelDidClose(_ viewModel: EditHistoryViewModelType) { + self.delegate?.editHistoryCoordinatorDidComplete(self) + } +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift new file mode 100644 index 0000000000..1ce1d524fa --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift @@ -0,0 +1,89 @@ +// File created from FlowTemplate +// $ createRootCoordinator.sh Room/EditHistory EditHistory +/* + Copyright 2019 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 + +@objc protocol EditHistoryCoordinatorBridgePresenterDelegate { + func editHistoryCoordinatorBridgePresenterDelegateDidComplete(_ coordinatorBridgePresenter: EditHistoryCoordinatorBridgePresenter) +} + +/// EditHistoryCoordinatorBridgePresenter enables to start EditHistoryCoordinator from a view controller. +/// This bridge is used while waiting for global usage of coordinator pattern. +@objcMembers +final class EditHistoryCoordinatorBridgePresenter: NSObject { + + // MARK: - Properties + + // MARK: Private + + private let aggregations: MXAggregations + private let roomId: String + private let eventId: String + private var coordinator: EditHistoryCoordinator? + + // MARK: Public + + weak var delegate: EditHistoryCoordinatorBridgePresenterDelegate? + + // MARK: - Setup + + init(aggregations: MXAggregations, + roomId: String, + eventId: String) { + self.aggregations = aggregations + self.roomId = roomId + self.eventId = eventId + super.init() + } + + // MARK: - Public + + // NOTE: Default value feature is not compatible with Objective-C. + // func present(from viewController: UIViewController, animated: Bool) { + // self.present(from: viewController, animated: animated) + // } + + func present(from viewController: UIViewController, animated: Bool) { + let editHistoryCoordinator = EditHistoryCoordinator(aggregations: self.aggregations, roomId: self.roomId, eventId: self.eventId) + editHistoryCoordinator.delegate = self + viewController.present(editHistoryCoordinator.toPresentable(), animated: animated, completion: nil) + editHistoryCoordinator.start() + + self.coordinator = editHistoryCoordinator + } + + func dismiss(animated: Bool, completion: (() -> Void)?) { + guard let coordinator = self.coordinator else { + return + } + coordinator.toPresentable().dismiss(animated: animated) { + self.coordinator = nil + + if let completion = completion { + completion() + } + } + } +} + +// MARK: - EditHistoryCoordinatorDelegate +extension EditHistoryCoordinatorBridgePresenter: EditHistoryCoordinatorDelegate { + func editHistoryCoordinatorDidComplete(_ coordinator: EditHistoryCoordinatorType) { + self.delegate?.editHistoryCoordinatorBridgePresenterDelegateDidComplete(self) + } +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorType.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorType.swift new file mode 100644 index 0000000000..2f02bd705f --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorType.swift @@ -0,0 +1,28 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/EditHistory EditHistory +/* + Copyright 2019 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 EditHistoryCoordinatorDelegate: class { + func editHistoryCoordinatorDidComplete(_ coordinator: EditHistoryCoordinatorType) +} + +/// `EditHistoryCoordinatorType` is a protocol describing a Coordinator that handle keybackup setup navigation flow. +protocol EditHistoryCoordinatorType: Coordinator, Presentable { + var delegate: EditHistoryCoordinatorDelegate? { get } +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryMessage.swift b/Riot/Modules/Room/EditHistory/EditHistoryMessage.swift new file mode 100644 index 0000000000..f338649602 --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryMessage.swift @@ -0,0 +1,22 @@ +/* + Copyright 2019 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 + +struct EditHistoryMessage { + let date: Date + let message: NSAttributedString +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewAction.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewAction.swift new file mode 100644 index 0000000000..d1fed7fc51 --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewAction.swift @@ -0,0 +1,25 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/EditHistory EditHistory +/* + Copyright 2019 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 + +/// EditHistoryViewController view actions exposed to view model +enum EditHistoryViewAction { + case loadMore + case close +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard b/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard new file mode 100644 index 0000000000..c8f3b006a3 --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift new file mode 100644 index 0000000000..4519918816 --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift @@ -0,0 +1,185 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/EditHistory EditHistory +/* + Copyright 2019 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 + +final class EditHistoryViewController: UIViewController { + + // MARK: - Constants + + private enum Constants { + static let aConstant: Int = 666 + } + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var scrollView: UIScrollView! + + @IBOutlet private weak var messageLabel: UILabel! + @IBOutlet private weak var okButton: UIButton! + + // MARK: Private + + private var viewModel: EditHistoryViewModelType! + private var theme: Theme! + private var keyboardAvoider: KeyboardAvoider? + private var errorPresenter: MXKErrorPresentation! + private var activityPresenter: ActivityIndicatorPresenter! + + // MARK: - Setup + + class func instantiate(with viewModel: EditHistoryViewModelType) -> EditHistoryViewController { + let viewController = StoryboardScene.EditHistoryViewController.initialScene.instantiate() + viewController.viewModel = viewModel + viewController.theme = ThemeService.shared().theme + return viewController + } + + // MARK: - Life cycle + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + + self.title = "Template" + + self.setupViews() + self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) + self.activityPresenter = ActivityIndicatorPresenter() + self.errorPresenter = MXKErrorAlertPresentation() + + self.registerThemeServiceDidChangeThemeNotification() + self.update(theme: self.theme) + + self.viewModel.viewDelegate = self + + self.viewModel.process(viewAction: .loadMore) + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + + self.keyboardAvoider?.startAvoiding() + } + + override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + + self.keyboardAvoider?.stopAvoiding() + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return self.theme.statusBarStyle + } + + // MARK: - Private + + private func update(theme: Theme) { + self.theme = theme + + self.view.backgroundColor = theme.headerBackgroundColor + + if let navigationBar = self.navigationController?.navigationBar { + theme.applyStyle(onNavigationBar: navigationBar) + } + + + // TODO: + self.messageLabel.textColor = theme.textPrimaryColor + + self.okButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.okButton) + } + + private func registerThemeServiceDidChangeThemeNotification() { + NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil) + } + + @objc private func themeDidChange() { + self.update(theme: ThemeService.shared().theme) + } + + private func setupViews() { + let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in + self?.cancelButtonAction() + } + + self.navigationItem.rightBarButtonItem = cancelBarButtonItem + + self.scrollView.keyboardDismissMode = .interactive + + self.messageLabel.text = "VectorL10n.editHistoryTitle" + self.messageLabel.isHidden = true + } + + private func render(viewState: EditHistoryViewState) { + switch viewState { + case .loading: + self.renderLoading() + case .loaded(let messages, let addedCount): + self.renderLoaded(messages: messages, addedCount: addedCount) + case .error(let error): + self.render(error: error) + } + } + + private func renderLoading() { + self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + } + + private func renderLoaded(messages: [EditHistoryMessage], addedCount: Int) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + + let attributedText = NSMutableAttributedString() + for message in messages { + attributedText.append(message.message) + attributedText.append(NSAttributedString(string: "\n")) + } + + self.messageLabel.attributedText = attributedText + self.messageLabel.isHidden = false + } + + private func render(error: Error) { + self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) + } + + + // MARK: - Actions + + @IBAction private func okButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .close) + } + + private func cancelButtonAction() { + self.viewModel.process(viewAction: .close) + } +} + + +// MARK: - EditHistoryViewModelViewDelegate +extension EditHistoryViewController: EditHistoryViewModelViewDelegate { + + func editHistoryViewModel(_ viewModel: EditHistoryViewModelType, didUpdateViewState viewSate: EditHistoryViewState) { + self.render(viewState: viewSate) + } +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift new file mode 100644 index 0000000000..f4b1348a8d --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift @@ -0,0 +1,128 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/EditHistory EditHistory +/* + Copyright 2019 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 + +final class EditHistoryViewModel: EditHistoryViewModelType { + + // MARK: - Properties + + // MARK: Private + + private let aggregations: MXAggregations + private let roomId: String + private let eventId: String + private let messageFormattingQueue: DispatchQueue + + private var nextBatch: String? + + // MARK: Public + + var messages: [EditHistoryMessage] = [] + var operation: MXHTTPOperation? + + weak var viewDelegate: EditHistoryViewModelViewDelegate? + weak var coordinatorDelegate: EditHistoryViewModelCoordinatorDelegate? + + + // MARK: - Setup + + init(aggregations: MXAggregations, + roomId: String, + eventId: String) { + self.aggregations = aggregations + self.roomId = roomId + self.eventId = eventId + self.messageFormattingQueue = DispatchQueue(label: "\(type(of: self)).messageFormattingQueue") + } + + deinit { + } + + // MARK: - Public + + func process(viewAction: EditHistoryViewAction) { + switch viewAction { + case .loadMore: + self.loadMoreHistory() + case .close: + self.coordinatorDelegate?.editHistoryViewModelDidClose(self) + } + } + + // MARK: - Private + + func loadMoreHistory() { + if self.operation != nil { + print("[EditHistoryViewModel] loadMoreHistory: operation already pending") + return + } + + self.update(viewState: .loading) + self.operation = self.aggregations.replaceEvents(forEvent: self.eventId, inRoom: self.roomId, from: self.nextBatch, limit: 10, success: { [weak self] (response) in + guard let sself = self else { + return + } + + sself.nextBatch = response.nextBatch + sself.operation = nil + + sself.process(editEvents: response.chunk) + + }, failure: { [weak self] error in + guard let sself = self else { + return + } + + sself.operation = nil + sself.update(viewState: .error(error)) + }) + } + + func process(editEvents: [MXEvent]) { + self.messageFormattingQueue.async { + + let newMessages = editEvents.reversed() + .compactMap { (editEvent) -> EditHistoryMessage? in + return self.process(editEvent: editEvent) + } + + if newMessages.count > 0 { + DispatchQueue.main.async { + self.messages = newMessages + self.messages + self.update(viewState: .loaded(messages: self.messages, addedCount: newMessages.count)) + } + } + } + } + + func process(editEvent: MXEvent) -> EditHistoryMessage? { + + guard let body: String = (editEvent.content?["m.new_content"] as? [String: Any])?["body"] as? String else { + print("[EditHistoryViewModel] processEditEvent: invalid edit event: \(editEvent.eventId ?? "")") + return nil + } + + // TODO: Using MXKEventFormatter + return EditHistoryMessage(date: Date(), message: NSAttributedString(string: body)) + } + + private func update(viewState: EditHistoryViewState) { + self.viewDelegate?.editHistoryViewModel(self, didUpdateViewState: viewState) + } +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift new file mode 100644 index 0000000000..d0322c85f0 --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift @@ -0,0 +1,38 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/EditHistory EditHistory +/* + Copyright 2019 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 EditHistoryViewModelViewDelegate: class { + func editHistoryViewModel(_ viewModel: EditHistoryViewModelType, didUpdateViewState viewSate: EditHistoryViewState) +} + +protocol EditHistoryViewModelCoordinatorDelegate: class { + func editHistoryViewModelDidClose(_ viewModel: EditHistoryViewModelType) +} + +/// Protocol describing the view model used by `EditHistoryViewController` +protocol EditHistoryViewModelType { + + var messages: [EditHistoryMessage] { get set } + + var viewDelegate: EditHistoryViewModelViewDelegate? { get set } + var coordinatorDelegate: EditHistoryViewModelCoordinatorDelegate? { get set } + + func process(viewAction: EditHistoryViewAction) +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift new file mode 100644 index 0000000000..62f75431b2 --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift @@ -0,0 +1,26 @@ +// File created from ScreenTemplate +// $ createScreen.sh Room/EditHistory EditHistory +/* + Copyright 2019 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 + +/// EditHistoryViewController view state +enum EditHistoryViewState { + case loading + case loaded(messages: [EditHistoryMessage], addedCount: Int) + case error(Error) +} From 54e3155763e314e7309c69e06c1b25fff70cf42e Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 1 Jul 2019 10:34:02 +0200 Subject: [PATCH 214/266] Edits history: Fix missing navigation bar --- .../EditHistory/EditHistoryCoordinatorBridgePresenter.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift index 1ce1d524fa..fb52c31c5a 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift @@ -61,7 +61,11 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject { func present(from viewController: UIViewController, animated: Bool) { let editHistoryCoordinator = EditHistoryCoordinator(aggregations: self.aggregations, roomId: self.roomId, eventId: self.eventId) editHistoryCoordinator.delegate = self - viewController.present(editHistoryCoordinator.toPresentable(), animated: animated, completion: nil) + + let navigationController = UINavigationController() + navigationController.addChild(editHistoryCoordinator.toPresentable()) + viewController.present(navigationController, animated: animated, completion: nil) + editHistoryCoordinator.start() self.coordinator = editHistoryCoordinator From 4f6986c0eae32dcbc3b8ea6ca238ce3c41508636 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 1 Jul 2019 10:51:07 +0200 Subject: [PATCH 215/266] Edits history: Improve temporary UX a bit --- .../EditHistoryViewController.storyboard | 12 ++++---- .../EditHistoryViewController.swift | 29 +++++++++++++------ .../EditHistory/EditHistoryViewModel.swift | 14 +++++++-- .../EditHistory/EditHistoryViewState.swift | 1 + 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard b/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard index c8f3b006a3..7a2d5f148d 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard @@ -10,7 +10,7 @@ - + @@ -27,8 +27,8 @@ - @@ -79,8 +79,8 @@ + - diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift index 4519918816..843bb31b40 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift @@ -33,7 +33,7 @@ final class EditHistoryViewController: UIViewController { @IBOutlet private weak var scrollView: UIScrollView! @IBOutlet private weak var messageLabel: UILabel! - @IBOutlet private weak var okButton: UIButton! + @IBOutlet private weak var loadMoreButton: UIButton! // MARK: Private @@ -105,8 +105,8 @@ final class EditHistoryViewController: UIViewController { // TODO: self.messageLabel.textColor = theme.textPrimaryColor - self.okButton.backgroundColor = theme.backgroundColor - theme.applyStyle(onButton: self.okButton) + self.loadMoreButton.backgroundColor = theme.backgroundColor + theme.applyStyle(onButton: self.loadMoreButton) } private func registerThemeServiceDidChangeThemeNotification() { @@ -118,11 +118,11 @@ final class EditHistoryViewController: UIViewController { } private func setupViews() { - let cancelBarButtonItem = MXKBarButtonItem(title: VectorL10n.cancel, style: .plain) { [weak self] in - self?.cancelButtonAction() + let closeBarButtonItem = MXKBarButtonItem(title: "Close", style: .plain) { [weak self] in + self?.closeButtonAction() } - self.navigationItem.rightBarButtonItem = cancelBarButtonItem + self.navigationItem.rightBarButtonItem = closeBarButtonItem self.scrollView.keyboardDismissMode = .interactive @@ -136,6 +136,8 @@ final class EditHistoryViewController: UIViewController { self.renderLoading() case .loaded(let messages, let addedCount): self.renderLoaded(messages: messages, addedCount: addedCount) + case .allLoaded: + self.renderAllLoaded() case .error(let error): self.render(error: error) } @@ -148,8 +150,13 @@ final class EditHistoryViewController: UIViewController { private func renderLoaded(messages: [EditHistoryMessage], addedCount: Int) { self.activityPresenter.removeCurrentActivityIndicator(animated: true) + let calendar = Calendar.current + let attributedText = NSMutableAttributedString() for message in messages { + let time=calendar.dateComponents([.hour, .minute], from: Date()) + attributedText.append(NSAttributedString(string: "\(time.hour!):\(time.minute!)")) + attributedText.append(NSAttributedString(string: " - ")) attributedText.append(message.message) attributedText.append(NSAttributedString(string: "\n")) } @@ -157,6 +164,10 @@ final class EditHistoryViewController: UIViewController { self.messageLabel.attributedText = attributedText self.messageLabel.isHidden = false } + + private func renderAllLoaded() { + self.loadMoreButton.isHidden = true + } private func render(error: Error) { self.activityPresenter.removeCurrentActivityIndicator(animated: true) @@ -166,11 +177,11 @@ final class EditHistoryViewController: UIViewController { // MARK: - Actions - @IBAction private func okButtonAction(_ sender: Any) { - self.viewModel.process(viewAction: .close) + @IBAction private func loadMoreButtonAction(_ sender: Any) { + self.viewModel.process(viewAction: .loadMore) } - private func cancelButtonAction() { + private func closeButtonAction() { self.viewModel.process(viewAction: .close) } } diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift index f4b1348a8d..3a156733e0 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift @@ -19,7 +19,13 @@ import Foundation final class EditHistoryViewModel: EditHistoryViewModelType { - + + // MARK: - Constants + + private enum Pagination { + static let count: UInt = 2 + } + // MARK: - Properties // MARK: Private @@ -74,7 +80,7 @@ final class EditHistoryViewModel: EditHistoryViewModelType { } self.update(viewState: .loading) - self.operation = self.aggregations.replaceEvents(forEvent: self.eventId, inRoom: self.roomId, from: self.nextBatch, limit: 10, success: { [weak self] (response) in + self.operation = self.aggregations.replaceEvents(forEvent: self.eventId, inRoom: self.roomId, from: self.nextBatch, limit: Pagination.count, success: { [weak self] (response) in guard let sself = self else { return } @@ -84,6 +90,10 @@ final class EditHistoryViewModel: EditHistoryViewModelType { sself.process(editEvents: response.chunk) + if sself.nextBatch == nil { + sself.update(viewState: .allLoaded) + } + }, failure: { [weak self] error in guard let sself = self else { return diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift index 62f75431b2..112451335e 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift @@ -22,5 +22,6 @@ import Foundation enum EditHistoryViewState { case loading case loaded(messages: [EditHistoryMessage], addedCount: Int) + case allLoaded case error(Error) } From 26a8a6fd8d486fa96461032fc4d32244ee98e4af Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 1 Jul 2019 12:34:20 +0200 Subject: [PATCH 216/266] Edits history: Use a dedicated event formatter --- .../EditHistory/EditHistoryCoordinator.swift | 6 +-- ...ditHistoryCoordinatorBridgePresenter.swift | 44 ++++++++++++++----- .../EditHistory/EditHistoryViewModel.swift | 27 +++++++----- Riot/SupportingFiles/Riot-Bridging-Header.h | 1 + 4 files changed, 55 insertions(+), 23 deletions(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift index 29dcb6500b..760b5ccf63 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift @@ -38,10 +38,10 @@ final class EditHistoryCoordinator: EditHistoryCoordinatorType { // MARK: - Setup init(aggregations: MXAggregations, - roomId: String, - eventId: String) { + formatter: MXKEventFormatter, + event: MXEvent) { - let editHistoryViewModel = EditHistoryViewModel(aggregations: aggregations, roomId: roomId, eventId: eventId) + let editHistoryViewModel = EditHistoryViewModel(aggregations: aggregations, formatter: formatter, event: event) let editHistoryViewController = EditHistoryViewController.instantiate(with: editHistoryViewModel) self.editHistoryViewModel = editHistoryViewModel self.editHistoryViewController = editHistoryViewController diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift index fb52c31c5a..818773e2bc 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift @@ -31,9 +31,8 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject { // MARK: Private - private let aggregations: MXAggregations - private let roomId: String - private let eventId: String + private let session: MXSession + private let event: MXEvent private var coordinator: EditHistoryCoordinator? // MARK: Public @@ -42,12 +41,10 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject { // MARK: - Setup - init(aggregations: MXAggregations, - roomId: String, - eventId: String) { - self.aggregations = aggregations - self.roomId = roomId - self.eventId = eventId + init(session: MXSession, + event: MXEvent) { + self.session = session + self.event = event super.init() } @@ -59,7 +56,13 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject { // } func present(from viewController: UIViewController, animated: Bool) { - let editHistoryCoordinator = EditHistoryCoordinator(aggregations: self.aggregations, roomId: self.roomId, eventId: self.eventId) + + guard let formatter = self.createEventFormatter(session: self.session) else { + //s das + return + } + + let editHistoryCoordinator = EditHistoryCoordinator(aggregations: self.session.aggregations, formatter: formatter, event: self.event) editHistoryCoordinator.delegate = self let navigationController = UINavigationController() @@ -83,6 +86,27 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject { } } } + + // MARK: - Private + + func createEventFormatter(session: MXSession) -> EventFormatter? { + guard let formatter = EventFormatter(matrixSession: session) else { + print("[EditHistoryCoordinatorBridgePresenter] createEventFormatter: Cannot build formatter") + return nil + } + + // Use the same event formatter settings as RoomDataSource + formatter.treatMatrixUserIdAsLink = true + formatter.treatMatrixRoomIdAsLink = true + formatter.treatMatrixRoomAliasAsLink = true + formatter.treatMatrixGroupIdAsLink = true + formatter.eventTypesFilterForMessages = MXKAppSettings.standard()?.eventsFilterForMessages + + // But do not display "...(edited)" + // TODO + + return formatter + } } // MARK: - EditHistoryCoordinatorDelegate diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift index 3a156733e0..3229a92182 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift @@ -31,8 +31,9 @@ final class EditHistoryViewModel: EditHistoryViewModelType { // MARK: Private private let aggregations: MXAggregations + private let formatter: MXKEventFormatter private let roomId: String - private let eventId: String + private let event: MXEvent private let messageFormattingQueue: DispatchQueue private var nextBatch: String? @@ -49,11 +50,12 @@ final class EditHistoryViewModel: EditHistoryViewModelType { // MARK: - Setup init(aggregations: MXAggregations, - roomId: String, - eventId: String) { + formatter: MXKEventFormatter, + event: MXEvent) { self.aggregations = aggregations - self.roomId = roomId - self.eventId = eventId + self.formatter = formatter + self.event = event + self.roomId = event.roomId self.messageFormattingQueue = DispatchQueue(label: "\(type(of: self)).messageFormattingQueue") } @@ -80,7 +82,7 @@ final class EditHistoryViewModel: EditHistoryViewModelType { } self.update(viewState: .loading) - self.operation = self.aggregations.replaceEvents(forEvent: self.eventId, inRoom: self.roomId, from: self.nextBatch, limit: Pagination.count, success: { [weak self] (response) in + self.operation = self.aggregations.replaceEvents(forEvent: self.event.eventId, inRoom: self.roomId, from: self.nextBatch, limit: Pagination.count, success: { [weak self] (response) in guard let sself = self else { return } @@ -122,14 +124,19 @@ final class EditHistoryViewModel: EditHistoryViewModelType { } func process(editEvent: MXEvent) -> EditHistoryMessage? { + // Create a temporary MXEvent that represents this edition + guard let editedEvent = self.event.editedEvent(fromReplacementEvent: editEvent) else { + print("[EditHistoryViewModel] processEditEvent: Cannot build edited event: \(editEvent.eventId ?? "")") + return nil + } - guard let body: String = (editEvent.content?["m.new_content"] as? [String: Any])?["body"] as? String else { - print("[EditHistoryViewModel] processEditEvent: invalid edit event: \(editEvent.eventId ?? "")") + let formatterError = UnsafeMutablePointer.allocate(capacity: 1) + guard let message = self.formatter.attributedString(from: editedEvent, with: nil, error: formatterError) else { + print("[EditHistoryViewModel] processEditEvent: cannot format(error: \(formatterError)) edited event: \(editEvent.eventId ?? "")") return nil } - // TODO: Using MXKEventFormatter - return EditHistoryMessage(date: Date(), message: NSAttributedString(string: body)) + return EditHistoryMessage(date: Date(), message: message) } private func update(viewState: EditHistoryViewState) { diff --git a/Riot/SupportingFiles/Riot-Bridging-Header.h b/Riot/SupportingFiles/Riot-Bridging-Header.h index 7467821ebb..68348718c3 100644 --- a/Riot/SupportingFiles/Riot-Bridging-Header.h +++ b/Riot/SupportingFiles/Riot-Bridging-Header.h @@ -12,3 +12,4 @@ #import "RecentsDataSource.h" #import "AvatarGenerator.h" #import "EncryptionInfoView.h" +#import "EventFormatter.h" From 4c7a652fd9f03556554772b544b1e2155ccd1808 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 1 Jul 2019 12:42:42 +0200 Subject: [PATCH 217/266] EventFormatter: add showEditionMention setting --- .../EditHistory/EditHistoryCoordinatorBridgePresenter.swift | 4 ++-- Riot/Utils/EventFormatter.h | 6 ++++++ Riot/Utils/EventFormatter.m | 3 ++- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift index 818773e2bc..a566c959ba 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift @@ -102,8 +102,8 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject { formatter.treatMatrixGroupIdAsLink = true formatter.eventTypesFilterForMessages = MXKAppSettings.standard()?.eventsFilterForMessages - // But do not display "...(edited)" - // TODO + // But do not display "...(Edited)" + formatter.showEditionMention = false return formatter } diff --git a/Riot/Utils/EventFormatter.h b/Riot/Utils/EventFormatter.h index 67abcd419d..1721750798 100644 --- a/Riot/Utils/EventFormatter.h +++ b/Riot/Utils/EventFormatter.h @@ -36,6 +36,12 @@ FOUNDATION_EXPORT NSString *const EventFormatterEditedEventLinkAction; */ @interface EventFormatter : MXKEventFormatter +/** + Add a "(Edited)" mention to edited message. + Default is YES. + */ +@property (nonatomic) BOOL showEditionMention; + /** Text color used to display message edited mention. Default is `textSecondaryColor`. diff --git a/Riot/Utils/EventFormatter.m b/Riot/Utils/EventFormatter.m index 8b8eeba271..3ba09e3183 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -182,7 +182,7 @@ - (NSAttributedString *)attributedStringFromEvent:(MXEvent *)event withRoomState attributedString = attributedStringWithRerequestMessage; } } - else if (event.contentHasBeenEdited) + else if (self.showEditionMention && event.contentHasBeenEdited) { NSMutableAttributedString *attributedStringWithEditMention = [attributedString mutableCopy]; @@ -255,6 +255,7 @@ - (instancetype)initWithMatrixSession:(MXSession *)matrixSession self.encryptingTextColor = ThemeService.shared.theme.tintColor; self.sendingTextColor = ThemeService.shared.theme.textSecondaryColor; self.errorTextColor = ThemeService.shared.theme.warningColor; + self.showEditionMention = YES; self.editionMentionTextColor = ThemeService.shared.theme.textSecondaryColor; self.defaultTextFont = [UIFont systemFontOfSize:15]; From 63aca2b1309f341ea3c6d3947056093007c1c2cd Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 1 Jul 2019 14:39:39 +0200 Subject: [PATCH 218/266] Edits history: Manage date --- Riot/Modules/Room/EditHistory/EditHistoryViewController.swift | 4 ++-- Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift index 843bb31b40..59ae30e106 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift @@ -59,7 +59,7 @@ final class EditHistoryViewController: UIViewController { // Do any additional setup after loading the view. - self.title = "Template" + self.title = "Edits history" self.setupViews() self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) @@ -154,7 +154,7 @@ final class EditHistoryViewController: UIViewController { let attributedText = NSMutableAttributedString() for message in messages { - let time=calendar.dateComponents([.hour, .minute], from: Date()) + let time=calendar.dateComponents([.hour, .minute], from: message.date) attributedText.append(NSAttributedString(string: "\(time.hour!):\(time.minute!)")) attributedText.append(NSAttributedString(string: " - ")) attributedText.append(message.message) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift index 3229a92182..2a9f553bf8 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift @@ -136,7 +136,9 @@ final class EditHistoryViewModel: EditHistoryViewModelType { return nil } - return EditHistoryMessage(date: Date(), message: message) + let date = Date(timeIntervalSince1970: TimeInterval(editEvent.originServerTs) / 1000) + + return EditHistoryMessage(date: date, message: message) } private func update(viewState: EditHistoryViewState) { From 6bdcd823c7765597f52142ee82e58eb9fcfc1fe0 Mon Sep 17 00:00:00 2001 From: KenjiKenjiIcantLetYouGo Date: Mon, 1 Jul 2019 14:26:11 +0000 Subject: [PATCH 219/266] Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (28 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/pt_BR/ --- Riot/Assets/pt_BR.lproj/Localizable.strings | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/pt_BR.lproj/Localizable.strings b/Riot/Assets/pt_BR.lproj/Localizable.strings index 767bb6cc36..fdfd667eb9 100644 --- a/Riot/Assets/pt_BR.lproj/Localizable.strings +++ b/Riot/Assets/pt_BR.lproj/Localizable.strings @@ -1,5 +1,5 @@ /* New message from a specific person, not referencing a room */ -"MSG_FROM_USER" = "Mensagem de %@"; +"MSG_FROM_USER" = "%@ enviou uma mensagem"; /* New message from a specific person in a named room */ "MSG_FROM_USER_IN_ROOM" = "%@ enviou mensagem em %@"; /* New message from a specific person, not referencing a room. Content included. */ @@ -11,7 +11,7 @@ /* New action message from a specific person in a named room. */ "ACTION_FROM_USER_IN_ROOM" = "%@ : * %@ %@"; /* New action message from a specific person, not referencing a room. */ -"IMAGE_FROM_USER" = "%@ enviou a você uma imagem %@"; +"IMAGE_FROM_USER" = "%@ enviou uma imagem %@"; /* New action message from a specific person in a named room. */ "IMAGE_FROM_USER_IN_ROOM" = "%@ enviou uma imagem %@ na sala %@"; /* A single unread message in a room */ @@ -50,3 +50,7 @@ "VOICE_CONF_NAMED_FROM_USER" = "Vídeo chamada coletiva de %@: '%@'"; /* Incoming named video conference invite from a specific person */ "VIDEO_CONF_NAMED_FROM_USER" = "Vídeo-chamada coletiva de %@: '%@'"; +/* Message title for a specific person in a named room */ +"MSG_FROM_USER_IN_ROOM_TITLE" = "%@ em %@"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@ enviou uma figurinha"; From 78351941e4d7171d2dad6a6e2d4f4c51ee8860ff Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 3 Jul 2019 11:13:53 +0200 Subject: [PATCH 220/266] Message editing: Handle reply edition. --- Riot/Modules/Room/RoomViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index ac1bdff5ca..e0bc7ff220 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2920,7 +2920,7 @@ - (void)editEventContentWithId:(NSString*)eventId if (roomInputToolbarView) { self.textMessageBeforeEditing = roomInputToolbarView.textMessage; - roomInputToolbarView.textMessage = event.content[@"body"]; + roomInputToolbarView.textMessage = [self.roomDataSource editableTextMessageForEvent:event]; } [self selectEventWithId:eventId inputToolBarSendMode:RoomInputToolbarViewSendModeEdit showTimestamp:YES]; From 7396980e3377f8dcaa2a722dc94ab93c8a1198e3 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Wed, 3 Jul 2019 11:20:43 +0200 Subject: [PATCH 221/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index f291f041ca..d2bd2d6a06 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -14,6 +14,7 @@ Improvements: * Read receipts: They are now counted at the MatrixKit level. * Migrate to Swift 5.0. * Reactions: Update quick reactions (#2459). + * Message Editing: Handle reply edition (#2492). Bug fix: * Device Verification: Fix user display name and device id colors in dark theme From 2a22e4c0a9ad425569fae9892fae4e5827e277db Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 3 Jul 2019 18:05:20 +0200 Subject: [PATCH 222/266] Reactions: Show at most 8 reactions #2510 --- Riot.xcodeproj/project.pbxproj | 8 ++ Riot/Assets/en.lproj/Vector.strings | 6 +- Riot/Generated/Strings.swift | 20 ++--- .../BubbleReactionActionViewCell.swift | 68 +++++++++++++++++ .../BubbleReactionActionViewCell.xib | 56 ++++++++++++++ .../BubbleReactions/BubbleReactionsView.swift | 74 +++++++++++++++---- .../BubbleReactionsViewModel.swift | 45 +++++++++-- .../BubbleReactionsViewModelType.swift | 16 +++- .../Room/CellData/RoomBubbleCellData.h | 6 ++ .../Room/CellData/RoomBubbleCellData.m | 41 +++++++++- .../Modules/Room/DataSources/RoomDataSource.m | 29 +++++++- 11 files changed, 325 insertions(+), 44 deletions(-) create mode 100644 Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.swift create mode 100644 Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.xib diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 5271276e58..98817bc8ef 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -75,6 +75,8 @@ 32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D6F2264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift */; }; 32891D75226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */; }; 32891D76226728EF00C82226 /* DeviceVerificationDataLoadingViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */; }; + 329E746622CD02EA006F9797 /* BubbleReactionActionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 329E746422CD02EA006F9797 /* BubbleReactionActionViewCell.xib */; }; + 329E746722CD02EA006F9797 /* BubbleReactionActionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 329E746522CD02EA006F9797 /* BubbleReactionActionViewCell.swift */; }; 32A6001622C661100042C1D9 /* EditHistoryViewState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6000D22C661100042C1D9 /* EditHistoryViewState.swift */; }; 32A6001722C661100042C1D9 /* EditHistoryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6000E22C661100042C1D9 /* EditHistoryViewController.swift */; }; 32A6001822C661100042C1D9 /* EditHistoryViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32A6000F22C661100042C1D9 /* EditHistoryViewModel.swift */; }; @@ -617,6 +619,8 @@ 32891D6F2264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationVerifiedViewController.swift; sourceTree = ""; }; 32891D73226728EE00C82226 /* DeviceVerificationDataLoadingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceVerificationDataLoadingViewController.swift; sourceTree = ""; }; 32891D74226728EE00C82226 /* DeviceVerificationDataLoadingViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = DeviceVerificationDataLoadingViewController.storyboard; sourceTree = ""; }; + 329E746422CD02EA006F9797 /* BubbleReactionActionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BubbleReactionActionViewCell.xib; sourceTree = ""; }; + 329E746522CD02EA006F9797 /* BubbleReactionActionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionActionViewCell.swift; sourceTree = ""; }; 32A6000D22C661100042C1D9 /* EditHistoryViewState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryViewState.swift; sourceTree = ""; }; 32A6000E22C661100042C1D9 /* EditHistoryViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryViewController.swift; sourceTree = ""; }; 32A6000F22C661100042C1D9 /* EditHistoryViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditHistoryViewModel.swift; sourceTree = ""; }; @@ -2001,6 +2005,8 @@ B1963B24228F1C4800CBA17F /* BubbleReactions */ = { isa = PBXGroup; children = ( + 329E746522CD02EA006F9797 /* BubbleReactionActionViewCell.swift */, + 329E746422CD02EA006F9797 /* BubbleReactionActionViewCell.xib */, B1963B31228F1C6B00CBA17F /* BubbleReactionsViewModelType.swift */, B1963B27228F1C4800CBA17F /* BubbleReactionsViewModel.swift */, B1963B25228F1C4800CBA17F /* BubbleReactionsView.swift */, @@ -3607,6 +3613,7 @@ B1B5574320EE6C4D00210D55 /* CallViewController.xib in Resources */, F083BDEA1E7009ED00A9B29C /* ringback.mp3 in Resources */, F083BDF21E7009ED00A9B29C /* GoogleService-Info.plist in Resources */, + 329E746622CD02EA006F9797 /* BubbleReactionActionViewCell.xib in Resources */, B1B558E320EF768F00210D55 /* RoomEmptyBubbleCell.xib in Resources */, B1E5368F21FB7258001F3AFF /* KeyBackupRecoverFromPassphraseViewController.storyboard in Resources */, B1B5590420EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.xib in Resources */, @@ -4098,6 +4105,7 @@ 32A6001A22C661100042C1D9 /* EditHistoryCoordinator.swift in Sources */, F083BD1E1E7009ED00A9B29C /* AppDelegate.m in Sources */, B1B558E620EF768F00210D55 /* RoomIncomingAttachmentWithoutSenderInfoBubbleCell.m in Sources */, + 329E746722CD02EA006F9797 /* BubbleReactionActionViewCell.swift in Sources */, B1098BFB21ECFE65000DDA48 /* KeyBackupSetupCoordinatorType.swift in Sources */, B1098BF721ECFE65000DDA48 /* PasswordStrength.swift in Sources */, 324A2052225FC571004FE8B0 /* DeviceVerificationIncomingViewAction.swift in Sources */, diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 2a920ef5c8..2ccf5624dc 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -280,10 +280,8 @@ "room_event_action_view_encryption" = "Encryption Information"; "room_event_action_reply" = "Reply"; "room_event_action_edit" = "Edit"; -"room_event_action_reaction_agree" = "Agree %@"; -"room_event_action_reaction_disagree" = "Disagree %@"; -"room_event_action_reaction_like" = "Like %@"; -"room_event_action_reaction_dislike" = "Dislike %@"; +"room_event_action_reaction_show_all" = "Show all"; +"room_event_action_reaction_show_less" = "Show less"; "room_warning_about_encryption" = "End-to-end encryption is in beta and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nEncrypted messages will not be visible on clients that do not yet implement encryption."; "room_event_failed_to_send" = "Failed to send"; "room_action_send_photo_or_video" = "Send photo or video"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 181d63de0f..73988e0e66 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1722,21 +1722,13 @@ internal enum VectorL10n { internal static var roomEventActionQuote: String { return VectorL10n.tr("Vector", "room_event_action_quote") } - /// Agree %@ - internal static func roomEventActionReactionAgree(_ p1: String) -> String { - return VectorL10n.tr("Vector", "room_event_action_reaction_agree", p1) + /// Show all + internal static var roomEventActionReactionShowAll: String { + return VectorL10n.tr("Vector", "room_event_action_reaction_show_all") } - /// Disagree %@ - internal static func roomEventActionReactionDisagree(_ p1: String) -> String { - return VectorL10n.tr("Vector", "room_event_action_reaction_disagree", p1) - } - /// Dislike %@ - internal static func roomEventActionReactionDislike(_ p1: String) -> String { - return VectorL10n.tr("Vector", "room_event_action_reaction_dislike", p1) - } - /// Like %@ - internal static func roomEventActionReactionLike(_ p1: String) -> String { - return VectorL10n.tr("Vector", "room_event_action_reaction_like", p1) + /// Show less + internal static var roomEventActionReactionShowLess: String { + return VectorL10n.tr("Vector", "room_event_action_reaction_show_less") } /// Remove internal static var roomEventActionRedact: String { diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.swift new file mode 100644 index 0000000000..5fbe5de8a5 --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.swift @@ -0,0 +1,68 @@ +/* + Copyright 2019 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 +import Reusable + +final class BubbleReactionActionViewCell: UICollectionViewCell, NibReusable, Themable { + + // MARK: - Constants + + // MARK: - Properties + + // MARK: Outlets + + @IBOutlet private weak var actionLabel: UILabel! + + // MARK: Private + + private var theme: Theme? + + // MARK: Public + + // MARK: - Life cycle + + override func preferredLayoutAttributesFitting(_ layoutAttributes: UICollectionViewLayoutAttributes) -> UICollectionViewLayoutAttributes { + if #available(iOS 12.0, *) { + /* + On iOS 12, there are issues with self-sizing cells as described in Apple release notes (https://developer.apple.com/documentation/ios_release_notes/ios_12_release_notes) : + "You might encounter issues with systemLayoutSizeFitting(_:) when using a UICollectionViewCell subclass that requires updateConstraints(). + (42138227) — Workaround: Don't call the cell's setNeedsUpdateConstraints() method unless you need to support live constraint changes. + If you need to support live constraint changes, call updateConstraintsIfNeeded() before calling systemLayoutSizeFitting(_:)." + */ + self.updateConstraintsIfNeeded() + } + return super.preferredLayoutAttributesFitting(layoutAttributes) + } + + // MARK: - Public + + func fill(actionString: String) { + self.actionLabel.text = actionString + self.updateViews() + } + + func update(theme: Theme) { + self.theme = theme + self.updateViews() + } + + // MARK: - Private + + private func updateViews() { + self.actionLabel.textColor = self.theme?.tintColor + } +} diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.xib b/Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.xib new file mode 100644 index 0000000000..fa3e5bdb75 --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionActionViewCell.xib @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift index 33eaf486c3..a3238b8096 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift @@ -38,6 +38,7 @@ final class BubbleReactionsView: UIView, NibOwnerLoadable { // MARK: Private private var reactionsViewData: [BubbleReactionViewData] = [] + private var showAllButtonState: BubbleReactionsViewState.ShowAllButtonState = .none private var theme: Theme? // MARK: Public @@ -65,6 +66,7 @@ final class BubbleReactionsView: UIView, NibOwnerLoadable { } self.collectionView.register(cellType: BubbleReactionViewCell.self) + self.collectionView.register(cellType: BubbleReactionActionViewCell.self) self.collectionView.reloadData() } @@ -90,31 +92,64 @@ final class BubbleReactionsView: UIView, NibOwnerLoadable { self.theme = theme self.collectionView.reloadData() } + + // MARK: - Private - func fill(reactionsViewData: [BubbleReactionViewData]) { + private func fill(reactionsViewData: [BubbleReactionViewData], showAllButtonState: BubbleReactionsViewState.ShowAllButtonState) { self.reactionsViewData = reactionsViewData + self.showAllButtonState = showAllButtonState self.collectionView.reloadData() } + + private func actionButtonString() -> String { + let actionString: String + switch self.showAllButtonState { + case .showAll: + actionString = VectorL10n.roomEventActionReactionShowAll + case .showLess: + actionString = VectorL10n.roomEventActionReactionShowLess + case .none: + actionString = "" + } + + return actionString + } } // MARK: - UICollectionViewDataSource extension BubbleReactionsView: UICollectionViewDataSource { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { - return self.reactionsViewData.count + // "Show all" or "Show less" is a cell in the same section as reactions cells + let additionalItems = self.showAllButtonState == .none ? 0 : 1 + + return self.reactionsViewData.count + additionalItems } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - let cell: BubbleReactionViewCell = collectionView.dequeueReusableCell(for: indexPath) - - if let theme = self.theme { - cell.update(theme: theme) + if indexPath.row < self.reactionsViewData.count { + let cell: BubbleReactionViewCell = collectionView.dequeueReusableCell(for: indexPath) + + if let theme = self.theme { + cell.update(theme: theme) + } + + let viewData = self.reactionsViewData[indexPath.row] + cell.fill(viewData: viewData) + + return cell + } else { + let cell: BubbleReactionActionViewCell = collectionView.dequeueReusableCell(for: indexPath) + + if let theme = self.theme { + cell.update(theme: theme) + } + + let actionString = self.actionButtonString() + cell.fill(actionString: actionString) + + return cell } - - let viewData = self.reactionsViewData[indexPath.row] - cell.fill(viewData: viewData) - - return cell } } @@ -122,7 +157,18 @@ extension BubbleReactionsView: UICollectionViewDataSource { extension BubbleReactionsView: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - self.viewModel?.process(viewAction: .tapReaction(index: indexPath.row)) + if indexPath.row < self.reactionsViewData.count { + self.viewModel?.process(viewAction: .tapReaction(index: indexPath.row)) + } else { + switch self.showAllButtonState { + case .showAll: + self.viewModel?.process(viewAction: .tapShowAction(action: .showAll)) + case .showLess: + self.viewModel?.process(viewAction: .tapShowAction(action: .showLess)) + case .none: + break + } + } } } @@ -131,8 +177,8 @@ extension BubbleReactionsView: BubbleReactionsViewModelViewDelegate { func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didUpdateViewState viewState: BubbleReactionsViewState) { switch viewState { - case .loaded(reactionsViewData: let reactionsViewData): - self.fill(reactionsViewData: reactionsViewData) + case .loaded(reactionsViewData: let reactionsViewData, showAllButtonState: let showAllButtonState): + self.fill(reactionsViewData: reactionsViewData, showAllButtonState: showAllButtonState) } } } diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift index 339b009872..1a79b6edbe 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModel.swift @@ -17,14 +17,20 @@ import Foundation @objc final class BubbleReactionsViewModel: NSObject, BubbleReactionsViewModelType { - + + // MARK: - Constants + + private enum Constants { + static let maxItemsWhenLimited: Int = 8 + } + // MARK: - Properties // MARK: Private private let aggregatedReactions: MXAggregatedReactions - private let reactionsViewData: [BubbleReactionViewData] private let eventId: String + private let showAll: Bool // MARK: Public @@ -34,13 +40,11 @@ import Foundation // MARK: - Setup @objc init(aggregatedReactions: MXAggregatedReactions, - eventId: String) { + eventId: String, + showAll: Bool) { self.aggregatedReactions = aggregatedReactions self.eventId = eventId - - self.reactionsViewData = aggregatedReactions.reactions.map { (reactionCount) -> BubbleReactionViewData in - return BubbleReactionViewData(emoji: reactionCount.reaction, countString: "\(reactionCount.count)", isCurrentUserReacted: reactionCount.myUserHasReacted) - } + self.showAll = showAll } // MARK: - Public @@ -48,7 +52,7 @@ import Foundation func process(viewAction: BubbleReactionsViewAction) { switch viewAction { case .loadData: - self.viewDelegate?.bubbleReactionsViewModel(self, didUpdateViewState: .loaded(reactionsViewData: self.reactionsViewData)) + self.loadData() case .tapReaction(let index): guard index < self.aggregatedReactions.reactions.count else { return @@ -61,6 +65,31 @@ import Foundation } case .addNewReaction: break + case .tapShowAction(.showAll): + self.viewModelDelegate?.bubbleReactionsViewModel(self, didShowAllTappedForEventId: self.eventId) + case .tapShowAction(.showLess): + self.viewModelDelegate?.bubbleReactionsViewModel(self, didShowLessTappedForEventId: self.eventId) } } + + func loadData() { + var reactions = self.aggregatedReactions.reactions + var showAllButtonState: BubbleReactionsViewState.ShowAllButtonState = .none + + // Limit displayed reactions if required + if reactions.count > Constants.maxItemsWhenLimited { + if self.showAll == true { + showAllButtonState = .showLess + } else { + reactions = Array(reactions[0.. BubbleReactionViewData in + return BubbleReactionViewData(emoji: reactionCount.reaction, countString: "\(reactionCount.count)", isCurrentUserReacted: reactionCount.myUserHasReacted) + } + + self.viewDelegate?.bubbleReactionsViewModel(self, didUpdateViewState: .loaded(reactionsViewData: reactionsViewData, showAllButtonState: showAllButtonState)) + } } diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift index 04a30726a8..3496854dfa 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsViewModelType.swift @@ -20,15 +20,29 @@ enum BubbleReactionsViewAction { case loadData case tapReaction(index: Int) case addNewReaction + case tapShowAction(action: ShowAction) + + enum ShowAction { + case showAll + case showLess + } } enum BubbleReactionsViewState { - case loaded(reactionsViewData: [BubbleReactionViewData]) + case loaded(reactionsViewData: [BubbleReactionViewData], showAllButtonState: ShowAllButtonState) + + enum ShowAllButtonState { + case none + case showAll + case showLess + } } @objc protocol BubbleReactionsViewModelDelegate: class { func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didAddReaction reactionCount: MXReactionCount, forEventId eventId: String) func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didRemoveReaction reactionCount: MXReactionCount, forEventId eventId: String) + func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didShowAllTappedForEventId eventId: String) + func bubbleReactionsViewModel(_ viewModel: BubbleReactionsViewModel, didShowLessTappedForEventId eventId: String) } protocol BubbleReactionsViewModelViewDelegate: class { diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.h b/Riot/Modules/Room/CellData/RoomBubbleCellData.h index a8317df00c..e03035abb9 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.h +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.h @@ -80,4 +80,10 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag) */ - (void)updateAdditionalContentHeightIfNeeded; + +#pragma mark - Show all reactions + +- (BOOL)showAllReactionsForEvent:(NSString*)eventId; +- (void)setShowAllReactions:(BOOL)showAllReactions forEvent:(NSString*)eventId; + @end diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 9a0d8b8fee..44f219b2ff 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -32,6 +32,9 @@ @interface RoomBubbleCellData() @property(nonatomic, readwrite) CGFloat additionalContentHeight; @property(nonatomic) BOOL shouldUpdateAdditionalContentHeight; +// Flags to "Show All" reactions for an event +@property(nonatomic) NSMutableSet *eventsToShowAllReactions; + @end @implementation RoomBubbleCellData @@ -43,6 +46,16 @@ - (BOOL)addVerticalWhitespaceForSelectedComponentTimestamp #pragma mark - Override MXKRoomBubbleCellData +- (instancetype)init +{ + self = [super init]; + if (self) + { + _eventsToShowAllReactions = [NSMutableSet set]; + } + return self; +} + - (instancetype)initWithEvent:(MXEvent *)event andRoomState:(MXRoomState *)roomState andRoomDataSource:(MXKRoomDataSource *)roomDataSource2 { self = [super initWithEvent:event andRoomState:roomState andRoomDataSource:roomDataSource2]; @@ -368,9 +381,11 @@ - (void)addVerticalWhitespaceToString:(NSMutableAttributedString *)attributedStr dispatch_once(&onceToken, ^{ bubbleReactionsView = [BubbleReactionsView new]; }); + + BOOL showAllReactions = [self.eventsToShowAllReactions containsObject:eventId]; bubbleReactionsView.frame = CGRectMake(0, 0, bubbleReactionsViewWidth, 1.0); - BubbleReactionsViewModel *viemModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:eventId]; + BubbleReactionsViewModel *viemModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:eventId showAll:showAllReactions]; bubbleReactionsView.viewModel = viemModel; [bubbleReactionsView setNeedsLayout]; [bubbleReactionsView layoutIfNeeded]; @@ -455,9 +470,11 @@ - (CGFloat)reactionHeightForEventId:(NSString*)eventId dispatch_once(&onceToken, ^{ bubbleReactionsView = [BubbleReactionsView new]; }); + + BOOL showAllReactions = [self.eventsToShowAllReactions containsObject:eventId]; bubbleReactionsView.frame = CGRectMake(0, 0, bubbleReactionsViewWidth, 1.0); - BubbleReactionsViewModel *viemModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:eventId]; + BubbleReactionsViewModel *viemModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:aggregatedReactions eventId:eventId showAll:showAllReactions]; bubbleReactionsView.viewModel = viemModel; [bubbleReactionsView setNeedsLayout]; [bubbleReactionsView layoutIfNeeded]; @@ -636,4 +653,24 @@ - (BOOL)addEvent:(MXEvent*)event andRoomState:(MXRoomState*)roomState return [super addEvent:event andRoomState:roomState]; } + +#pragma mark - Show all reactions + +- (BOOL)showAllReactionsForEvent:(NSString*)eventId +{ + return [self.eventsToShowAllReactions containsObject:eventId]; +} + +- (void)setShowAllReactions:(BOOL)showAllReactions forEvent:(NSString*)eventId +{ + if (showAllReactions) + { + [self.eventsToShowAllReactions addObject:eventId]; + } + else + { + [self.eventsToShowAllReactions removeObject:eventId]; + } +} + @end diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index c7b642fb68..7282bd4eb9 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -244,7 +244,10 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N if (reactions && !isCollapsableCellCollapsed) { - BubbleReactionsViewModel *bubbleReactionsViewModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:reactions eventId:componentEventId]; + BOOL showAllReactions = [cellData showAllReactionsForEvent:componentEventId]; + BubbleReactionsViewModel *bubbleReactionsViewModel = [[BubbleReactionsViewModel alloc] initWithAggregatedReactions:reactions + eventId:componentEventId + showAll:showAllReactions]; reactionsView = [BubbleReactionsView new]; reactionsView.viewModel = bubbleReactionsViewModel; @@ -572,4 +575,28 @@ - (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didRemove }]; } +- (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didShowAllTappedForEventId:(NSString * _Nonnull)eventId +{ + [self setShowAllReactions:YES forEvent:eventId]; +} + +- (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didShowLessTappedForEventId:(NSString * _Nonnull)eventId +{ + [self setShowAllReactions:NO forEvent:eventId]; +} + +- (void)setShowAllReactions:(BOOL)showAllReactions forEvent:(NSString*)eventId +{ + id cellData = [self cellDataOfEventWithEventId:eventId]; + if ([cellData isKindOfClass:[RoomBubbleCellData class]]) + { + RoomBubbleCellData *roomBubbleCellData = (RoomBubbleCellData*)cellData; + + [roomBubbleCellData setShowAllReactions:showAllReactions forEvent:eventId]; + [self updateCellDataReactions:roomBubbleCellData forEventId:eventId]; + + [self.delegate dataSource:self didCellChange:nil]; + } +} + @end From 393340a7878e5dac4c11fd91c8222eeb81b9525c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 4 Jul 2019 19:02:17 +0200 Subject: [PATCH 223/266] Message edits history: Add strings. --- Riot/Assets/en.lproj/Vector.strings | 2 ++ Riot/Generated/Strings.swift | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 2ccf5624dc..18d956eb1b 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -56,6 +56,7 @@ "collapse" = "collapse"; "send_to" = "Send to %@"; "sending" = "Sending"; +"close" = "Close"; // Authentication "auth_login" = "Log in"; @@ -298,6 +299,7 @@ "room_resource_usage_limit_reached_message_1_monthly_active_user" = "This homeserver has hit its Monthly Active User limit so "; "room_resource_usage_limit_reached_message_2" = "some users will not be able to log in."; "room_resource_usage_limit_reached_message_contact_3" = " to get this limit increased."; +"room_message_edits_history_title" = "Message edits"; // Unknown devices "unknown_devices_alert_title" = "Room contains unknown devices"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 73988e0e66..2caf52f437 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -334,6 +334,10 @@ internal enum VectorL10n { internal static var cancel: String { return VectorL10n.tr("Vector", "cancel") } + /// Close + internal static var close: String { + return VectorL10n.tr("Vector", "close") + } /// collapse internal static var collapse: String { return VectorL10n.tr("Vector", "collapse") @@ -1786,6 +1790,10 @@ internal enum VectorL10n { internal static func roomManyUsersAreTyping(_ p1: String, _ p2: String) -> String { return VectorL10n.tr("Vector", "room_many_users_are_typing", p1, p2) } + /// Message edits + internal static var roomMessageEditsHistoryTitle: String { + return VectorL10n.tr("Vector", "room_message_edits_history_title") + } /// Send a message (unencrypted)… internal static var roomMessagePlaceholder: String { return VectorL10n.tr("Vector", "room_message_placeholder") From a943361051e55b86d768ccdf1f6d465778818636 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 4 Jul 2019 19:02:54 +0200 Subject: [PATCH 224/266] Message edits history: Create edit message cell. --- .../Room/EditHistory/EditHistoryCell.swift | 38 +++++++++++++ .../Room/EditHistory/EditHistoryCell.xib | 55 +++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryCell.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryCell.xib diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCell.swift b/Riot/Modules/Room/EditHistory/EditHistoryCell.swift new file mode 100644 index 0000000000..0d05ef6c2c --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryCell.swift @@ -0,0 +1,38 @@ +/* + Copyright 2019 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 +import Reusable + +final class EditHistoryCell: UITableViewCell, NibReusable, Themable { + + // MARK: - Properties + + @IBOutlet private weak var timestampLabel: UILabel! + @IBOutlet private weak var messageLabel: UILabel! + + // MARK: - Public + + func fill(with timeString: String, and attributedMessage: NSAttributedString) { + self.timestampLabel.text = timeString + self.messageLabel.attributedText = attributedMessage + } + + func update(theme: Theme) { + self.backgroundColor = theme.backgroundColor + self.timestampLabel.textColor = theme.textSecondaryColor + } +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCell.xib b/Riot/Modules/Room/EditHistory/EditHistoryCell.xib new file mode 100644 index 0000000000..7780255db8 --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryCell.xib @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 884f47b33e51c3980abd05a58888e8597f5c1448 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 4 Jul 2019 19:03:39 +0200 Subject: [PATCH 225/266] Message edits history: Create header view. --- .../EditHistory/EditHistoryHeaderView.swift | 36 ++++++++++++++++ .../EditHistory/EditHistoryHeaderView.xib | 43 +++++++++++++++++++ 2 files changed, 79 insertions(+) create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryHeaderView.swift create mode 100644 Riot/Modules/Room/EditHistory/EditHistoryHeaderView.xib diff --git a/Riot/Modules/Room/EditHistory/EditHistoryHeaderView.swift b/Riot/Modules/Room/EditHistory/EditHistoryHeaderView.swift new file mode 100644 index 0000000000..a547024d1e --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryHeaderView.swift @@ -0,0 +1,36 @@ +/* + Copyright 2019 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 +import Reusable + +final class EditHistoryHeaderView: UITableViewHeaderFooterView, NibLoadable, Reusable, Themable { + + // MARK: - Properties + + @IBOutlet private weak var dateLabel: UILabel! + + // MARK: - Public + + func update(theme: Theme) { + self.contentView.backgroundColor = theme.backgroundColor + self.dateLabel.textColor = theme.headerTextPrimaryColor + } + + func fill(with dateString: String) { + self.dateLabel.text = dateString + } +} diff --git a/Riot/Modules/Room/EditHistory/EditHistoryHeaderView.xib b/Riot/Modules/Room/EditHistory/EditHistoryHeaderView.xib new file mode 100644 index 0000000000..e0a28bcf58 --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryHeaderView.xib @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 8fe0213c442c0085d8107d4410a534937725ed65 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 4 Jul 2019 19:04:53 +0200 Subject: [PATCH 226/266] Message edits history: Create edit history section struct. --- Riot.xcodeproj/project.pbxproj | 22 ++++++++++++++++++- .../Room/EditHistory/EditHistorySection.swift | 22 +++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 Riot/Modules/Room/EditHistory/EditHistorySection.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 98817bc8ef..13306ca789 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -153,6 +153,8 @@ B140B4A221F87F7100E3F5FE /* OperationQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A121F87F7100E3F5FE /* OperationQueue.swift */; }; B140B4A621F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A521F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift */; }; B140B4A821F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */; }; + B142317A22CCFA2000FFA96A /* EditHistoryCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = B142317822CCFA2000FFA96A /* EditHistoryCell.swift */; }; + B142317B22CCFA2000FFA96A /* EditHistoryCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B142317922CCFA2000FFA96A /* EditHistoryCell.xib */; }; B14F142E22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */; }; B14F142F22144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */; }; B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */; }; @@ -202,6 +204,9 @@ B169331720F3CBE000746532 /* RecentCellData.m in Sources */ = {isa = PBXBuildFile; fileRef = B16932F920F3C51900746532 /* RecentCellData.m */; }; B17982FF2119FED2001FD722 /* GDPRConsentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */; }; B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1798301211B13B3001FD722 /* OnBoardingManager.swift */; }; + B190F55922CE356800AEB493 /* EditHistoryHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B190F55822CE356800AEB493 /* EditHistoryHeaderView.swift */; }; + B190F55B22CE35FD00AEB493 /* EditHistoryHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B190F55A22CE35FD00AEB493 /* EditHistoryHeaderView.xib */; }; + B190F55D22CE5A9700AEB493 /* EditHistorySection.swift in Sources */ = {isa = PBXBuildFile; fileRef = B190F55C22CE5A9600AEB493 /* EditHistorySection.swift */; }; B1963B2B228F1C4900CBA17F /* BubbleReactionsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B25228F1C4800CBA17F /* BubbleReactionsView.swift */; }; B1963B2C228F1C4900CBA17F /* BubbleReactionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1963B26228F1C4800CBA17F /* BubbleReactionViewCell.xib */; }; B1963B2D228F1C4900CBA17F /* BubbleReactionsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1963B27228F1C4800CBA17F /* BubbleReactionsViewModel.swift */; }; @@ -719,6 +724,8 @@ B140B4A121F87F7100E3F5FE /* OperationQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OperationQueue.swift; sourceTree = ""; }; B140B4A521F89E7600E3F5FE /* KeyBackupSetupCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupSetupCoordinatorBridgePresenter.swift; sourceTree = ""; }; B140B4A721F8AB4600E3F5FE /* KeyBackupRecoverCoordinatorBridgePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorBridgePresenter.swift; sourceTree = ""; }; + B142317822CCFA2000FFA96A /* EditHistoryCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistoryCell.swift; sourceTree = ""; }; + B142317922CCFA2000FFA96A /* EditHistoryCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditHistoryCell.xib; sourceTree = ""; }; B14F142622144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = KeyBackupRecoverFromRecoveryKeyViewController.storyboard; sourceTree = ""; }; B14F142722144F6400FA0595 /* KeyBackupRecoverFromRecoveryKeyViewModelType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyViewModelType.swift; sourceTree = ""; }; B14F142822144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift; sourceTree = ""; }; @@ -816,6 +823,9 @@ B169331320F3CAFC00746532 /* PublicRoomsDirectoryDataSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PublicRoomsDirectoryDataSource.h; sourceTree = ""; }; B17982FE2119FED2001FD722 /* GDPRConsentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GDPRConsentViewController.swift; sourceTree = ""; }; B1798301211B13B3001FD722 /* OnBoardingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnBoardingManager.swift; sourceTree = ""; }; + B190F55822CE356800AEB493 /* EditHistoryHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistoryHeaderView.swift; sourceTree = ""; }; + B190F55A22CE35FD00AEB493 /* EditHistoryHeaderView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = EditHistoryHeaderView.xib; sourceTree = ""; }; + B190F55C22CE5A9600AEB493 /* EditHistorySection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditHistorySection.swift; sourceTree = ""; }; B1963B25228F1C4800CBA17F /* BubbleReactionsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionsView.swift; sourceTree = ""; }; B1963B26228F1C4800CBA17F /* BubbleReactionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BubbleReactionViewCell.xib; sourceTree = ""; }; B1963B27228F1C4800CBA17F /* BubbleReactionsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BubbleReactionsViewModel.swift; sourceTree = ""; }; @@ -1538,13 +1548,18 @@ children = ( 32A6000D22C661100042C1D9 /* EditHistoryViewState.swift */, 32A6000E22C661100042C1D9 /* EditHistoryViewController.swift */, + 32A6001322C661100042C1D9 /* EditHistoryViewController.storyboard */, + B190F55822CE356800AEB493 /* EditHistoryHeaderView.swift */, + B190F55A22CE35FD00AEB493 /* EditHistoryHeaderView.xib */, + B142317822CCFA2000FFA96A /* EditHistoryCell.swift */, + B142317922CCFA2000FFA96A /* EditHistoryCell.xib */, 32A6000F22C661100042C1D9 /* EditHistoryViewModel.swift */, 32A6001022C661100042C1D9 /* EditHistoryViewModelType.swift */, 32A6001122C661100042C1D9 /* EditHistoryCoordinator.swift */, 32A6001222C661100042C1D9 /* EditHistoryViewAction.swift */, - 32A6001322C661100042C1D9 /* EditHistoryViewController.storyboard */, 32A6001422C661100042C1D9 /* EditHistoryCoordinatorType.swift */, 32A6001522C661100042C1D9 /* EditHistoryCoordinatorBridgePresenter.swift */, + B190F55C22CE5A9600AEB493 /* EditHistorySection.swift */, 32A6001F22C66FCF0042C1D9 /* EditHistoryMessage.swift */, ); path = EditHistory; @@ -3603,7 +3618,9 @@ B1B558DA20EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib in Resources */, B1B558D620EF768F00210D55 /* RoomOutgoingEncryptedTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib in Resources */, B1B5593720EF7BAC00210D55 /* TableViewCellWithCheckBoxAndLabel.xib in Resources */, + B190F55B22CE35FD00AEB493 /* EditHistoryHeaderView.xib in Resources */, B1B5579020EF568D00210D55 /* GroupTableViewCell.xib in Resources */, + B142317B22CCFA2000FFA96A /* EditHistoryCell.xib in Resources */, B1B5581B20EF625800210D55 /* ExpandedRoomTitleView.xib in Resources */, B1B558C220EF768F00210D55 /* RoomIncomingEncryptedTextMsgBubbleCell.xib in Resources */, B1B558F620EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.xib in Resources */, @@ -4022,6 +4039,7 @@ B1B558E420EF768F00210D55 /* RoomMembershipWithPaginationTitleBubbleCell.m in Sources */, B1B5573620EE6C4D00210D55 /* GroupsViewController.m in Sources */, 3232ABB82257BE6500AD6A5C /* DeviceVerificationVerifyCoordinator.swift in Sources */, + B142317A22CCFA2000FFA96A /* EditHistoryCell.swift in Sources */, B1B5572A20EE6C4D00210D55 /* RoomMemberDetailsViewController.m in Sources */, B1B5590120EF768F00210D55 /* RoomMembershipExpandedWithPaginationTitleBubbleCell.m in Sources */, 32F6B96B2270623100BBA352 /* DeviceVerificationDataLoadingViewAction.swift in Sources */, @@ -4171,6 +4189,7 @@ B16932EE20F3C3C900746532 /* FilesSearchCellData.m in Sources */, B1B558E520EF768F00210D55 /* RoomMembershipExpandedBubbleCell.m in Sources */, 32BF995121FA29DC00698084 /* SettingsKeyBackupViewModelType.swift in Sources */, + B190F55922CE356800AEB493 /* EditHistoryHeaderView.swift in Sources */, 32F6B96A2270623100BBA352 /* DeviceVerificationDataLoadingViewState.swift in Sources */, 32BF995321FA2A1300698084 /* SettingsKeyBackupViewState.swift in Sources */, 32B94DF9228EC26400716A26 /* ReactionsMenuViewAction.swift in Sources */, @@ -4183,6 +4202,7 @@ B1B558EE20EF768F00210D55 /* RoomOutgoingAttachmentBubbleCell.m in Sources */, 3232ABB52257BE6400AD6A5C /* DeviceVerificationVerifyCoordinatorType.swift in Sources */, 32BF994F21FA29A400698084 /* SettingsKeyBackupViewModel.swift in Sources */, + B190F55D22CE5A9700AEB493 /* EditHistorySection.swift in Sources */, 32A6002022C66FCF0042C1D9 /* EditHistoryMessage.swift in Sources */, B1B5574920EE6C4D00210D55 /* RiotSplitViewController.m in Sources */, B1B5574E20EE6C4D00210D55 /* DirectoryServerPickerViewController.m in Sources */, diff --git a/Riot/Modules/Room/EditHistory/EditHistorySection.swift b/Riot/Modules/Room/EditHistory/EditHistorySection.swift new file mode 100644 index 0000000000..550378541b --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistorySection.swift @@ -0,0 +1,22 @@ +/* + Copyright 2019 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 + +struct EditHistorySection { + let date: Date + let messages: [EditHistoryMessage] +} From 8ae74d507d79673a1a67895d27c1d4d11ac3d239 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 4 Jul 2019 19:18:13 +0200 Subject: [PATCH 227/266] Message edits history: Update view model to handle message history grouping by day. --- .../EditHistory/EditHistoryViewModel.swift | 97 +++++++++++++++---- .../EditHistoryViewModelType.swift | 2 - .../EditHistory/EditHistoryViewState.swift | 3 +- 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift index 2a9f553bf8..541491a939 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift @@ -23,7 +23,7 @@ final class EditHistoryViewModel: EditHistoryViewModelType { // MARK: - Constants private enum Pagination { - static let count: UInt = 2 + static let count: UInt = 30 } // MARK: - Properties @@ -36,17 +36,16 @@ final class EditHistoryViewModel: EditHistoryViewModelType { private let event: MXEvent private let messageFormattingQueue: DispatchQueue + private var messages: [EditHistoryMessage] = [] + private var operation: MXHTTPOperation? private var nextBatch: String? + private var viewState: EditHistoryViewState? // MARK: Public - - var messages: [EditHistoryMessage] = [] - var operation: MXHTTPOperation? weak var viewDelegate: EditHistoryViewModelViewDelegate? weak var coordinatorDelegate: EditHistoryViewModelCoordinatorDelegate? - // MARK: - Setup init(aggregations: MXAggregations, @@ -59,9 +58,6 @@ final class EditHistoryViewModel: EditHistoryViewModelType { self.messageFormattingQueue = DispatchQueue(label: "\(type(of: self)).messageFormattingQueue") } - deinit { - } - // MARK: - Public func process(viewAction: EditHistoryViewAction) { @@ -75,12 +71,36 @@ final class EditHistoryViewModel: EditHistoryViewModelType { // MARK: - Private - func loadMoreHistory() { - if self.operation != nil { + private func canLoadMoreHistory() -> Bool { + guard let viewState = self.viewState else { + return true + } + + let canLoadMoreHistory: Bool + + switch viewState { + case .loading: + canLoadMoreHistory = false + case .loaded(sections: _, addedCount: _, allDataLoaded: let allLoaded): + canLoadMoreHistory = !allLoaded + default: + canLoadMoreHistory = true + } + + return canLoadMoreHistory + } + + private func loadMoreHistory() { + guard self.canLoadMoreHistory() else { + print("[EditHistoryViewModel] loadMoreHistory: pending loading or all data loaded") + return + } + + guard self.operation == nil else { print("[EditHistoryViewModel] loadMoreHistory: operation already pending") return } - + self.update(viewState: .loading) self.operation = self.aggregations.replaceEvents(forEvent: self.event.eventId, inRoom: self.roomId, from: self.nextBatch, limit: Pagination.count, success: { [weak self] (response) in guard let sself = self else { @@ -91,12 +111,8 @@ final class EditHistoryViewModel: EditHistoryViewModelType { sself.operation = nil sself.process(editEvents: response.chunk) - - if sself.nextBatch == nil { - sself.update(viewState: .allLoaded) - } - - }, failure: { [weak self] error in + + }, failure: { [weak self] error in guard let sself = self else { return } @@ -106,7 +122,7 @@ final class EditHistoryViewModel: EditHistoryViewModelType { }) } - func process(editEvents: [MXEvent]) { + private func process(editEvents: [MXEvent]) { self.messageFormattingQueue.async { let newMessages = editEvents.reversed() @@ -115,15 +131,53 @@ final class EditHistoryViewModel: EditHistoryViewModelType { } if newMessages.count > 0 { + self.messages = newMessages + self.messages + let allDataLoaded = self.nextBatch == nil + + let editHistorySections = self.editHistorySections(from: self.messages) + DispatchQueue.main.async { - self.messages = newMessages + self.messages - self.update(viewState: .loaded(messages: self.messages, addedCount: newMessages.count)) + self.update(viewState: .loaded(sections: editHistorySections, addedCount: newMessages.count, allDataLoaded: allDataLoaded)) } } } } + + private func editHistorySections(from editHistoryMessages: [EditHistoryMessage]) -> [EditHistorySection] { + + // Group edit messages by day + + let initial: [Date: [EditHistoryMessage]] = [:] + let dateComponents: Set = [.day, .month, .year] + let calendar = Calendar.current + + let messagesGroupedByDay = editHistoryMessages.reduce(into: initial) { messagesByDay, message in + let components = calendar.dateComponents(dateComponents, from: message.date) + if let date = calendar.date(from: components) { + var messages = messagesByDay[date] ?? [] + messages.append(message) + messagesByDay[date] = messages + } + } + + // Create edit sections + + var sections: [EditHistorySection] = [] + + for (date, messages) in messagesGroupedByDay { + // Sort messages descending (most recent first) + let sortedMessages = messages.sorted { $0.date.compare($1.date) == .orderedDescending } + let section = EditHistorySection(date: date, messages: sortedMessages) + sections.append(section) + } + + // Sort sections descending (most recent first) + let sortedSections = sections.sorted { $0.date.compare($1.date) == .orderedDescending } + + return sortedSections + } - func process(editEvent: MXEvent) -> EditHistoryMessage? { + private func process(editEvent: MXEvent) -> EditHistoryMessage? { // Create a temporary MXEvent that represents this edition guard let editedEvent = self.event.editedEvent(fromReplacementEvent: editEvent) else { print("[EditHistoryViewModel] processEditEvent: Cannot build edited event: \(editEvent.eventId ?? "")") @@ -142,6 +196,7 @@ final class EditHistoryViewModel: EditHistoryViewModelType { } private func update(viewState: EditHistoryViewState) { + self.viewState = viewState self.viewDelegate?.editHistoryViewModel(self, didUpdateViewState: viewState) } } diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift index d0322c85f0..4ed9ff7078 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModelType.swift @@ -28,8 +28,6 @@ protocol EditHistoryViewModelCoordinatorDelegate: class { /// Protocol describing the view model used by `EditHistoryViewController` protocol EditHistoryViewModelType { - - var messages: [EditHistoryMessage] { get set } var viewDelegate: EditHistoryViewModelViewDelegate? { get set } var coordinatorDelegate: EditHistoryViewModelCoordinatorDelegate? { get set } diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift index 112451335e..bb48192ee1 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewState.swift @@ -21,7 +21,6 @@ import Foundation /// EditHistoryViewController view state enum EditHistoryViewState { case loading - case loaded(messages: [EditHistoryMessage], addedCount: Int) - case allLoaded + case loaded(sections: [EditHistorySection], addedCount: Int, allDataLoaded: Bool) case error(Error) } From 11012933ae3a29ee8bf342308c80a4de8ab13e0c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 4 Jul 2019 19:22:29 +0200 Subject: [PATCH 228/266] Message edits history: Update EditHistoryViewController to use UITableView to display messages and dates. --- .../EditHistoryViewController.storyboard | 68 ++------ .../EditHistoryViewController.swift | 164 +++++++++++------- 2 files changed, 110 insertions(+), 122 deletions(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard b/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard index 7a2d5f148d..059dae3098 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewController.storyboard @@ -18,70 +18,26 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - + + + + - - - + diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift index 59ae30e106..88c254182b 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewController.swift @@ -17,31 +17,47 @@ */ import UIKit +import Reusable final class EditHistoryViewController: UIViewController { // MARK: - Constants private enum Constants { - static let aConstant: Int = 666 + static let estimatedRowHeight: CGFloat = 38.0 + static let estimatedSectionHeaderHeight: CGFloat = 28.0 + static let editHistoryMessageTimeFormat = "HH:mm" } // MARK: - Properties // MARK: Outlets - - @IBOutlet private weak var scrollView: UIScrollView! - @IBOutlet private weak var messageLabel: UILabel! - @IBOutlet private weak var loadMoreButton: UIButton! + @IBOutlet private weak var tableView: UITableView! // MARK: Private private var viewModel: EditHistoryViewModelType! private var theme: Theme! - private var keyboardAvoider: KeyboardAvoider? private var errorPresenter: MXKErrorPresentation! - private var activityPresenter: ActivityIndicatorPresenter! + private var activityIndicatorPresenter: ActivityIndicatorPresenter! + + private var editHistorySections: [EditHistorySection] = [] + + private lazy var sectionDateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .full + dateFormatter.timeStyle = .none + dateFormatter.doesRelativeDateFormatting = true + return dateFormatter + }() + + private lazy var messageDateFormatter: DateFormatter = { + let dateFormatter = DateFormatter() + dateFormatter.dateFormat = Constants.editHistoryMessageTimeFormat + dateFormatter.locale = Locale(identifier: "en_US_POSIX") + return dateFormatter + }() // MARK: - Setup @@ -59,11 +75,10 @@ final class EditHistoryViewController: UIViewController { // Do any additional setup after loading the view. - self.title = "Edits history" + self.title = VectorL10n.roomMessageEditsHistoryTitle self.setupViews() - self.keyboardAvoider = KeyboardAvoider(scrollViewContainerView: self.view, scrollView: self.scrollView) - self.activityPresenter = ActivityIndicatorPresenter() + self.activityIndicatorPresenter = ActivityIndicatorPresenter() self.errorPresenter = MXKErrorAlertPresentation() self.registerThemeServiceDidChangeThemeNotification() @@ -73,18 +88,6 @@ final class EditHistoryViewController: UIViewController { self.viewModel.process(viewAction: .loadMore) } - - override func viewWillAppear(_ animated: Bool) { - super.viewWillAppear(animated) - - self.keyboardAvoider?.startAvoiding() - } - - override func viewDidDisappear(_ animated: Bool) { - super.viewDidDisappear(animated) - - self.keyboardAvoider?.stopAvoiding() - } override var preferredStatusBarStyle: UIStatusBarStyle { return self.theme.statusBarStyle @@ -95,18 +98,12 @@ final class EditHistoryViewController: UIViewController { private func update(theme: Theme) { self.theme = theme - self.view.backgroundColor = theme.headerBackgroundColor + self.view.backgroundColor = theme.backgroundColor + self.tableView.backgroundColor = theme.backgroundColor if let navigationBar = self.navigationController?.navigationBar { theme.applyStyle(onNavigationBar: navigationBar) } - - - // TODO: - self.messageLabel.textColor = theme.textPrimaryColor - - self.loadMoreButton.backgroundColor = theme.backgroundColor - theme.applyStyle(onButton: self.loadMoreButton) } private func registerThemeServiceDidChangeThemeNotification() { @@ -118,74 +115,109 @@ final class EditHistoryViewController: UIViewController { } private func setupViews() { - let closeBarButtonItem = MXKBarButtonItem(title: "Close", style: .plain) { [weak self] in + let closeBarButtonItem = MXKBarButtonItem(title: VectorL10n.close, style: .plain) { [weak self] in self?.closeButtonAction() } self.navigationItem.rightBarButtonItem = closeBarButtonItem - self.scrollView.keyboardDismissMode = .interactive + self.setupTableView() + } + + private func setupTableView() { + self.tableView.rowHeight = UITableView.automaticDimension + self.tableView.estimatedRowHeight = Constants.estimatedRowHeight + self.tableView.register(cellType: EditHistoryCell.self) + + self.tableView.sectionHeaderHeight = UITableView.automaticDimension + self.tableView.estimatedSectionHeaderHeight = Constants.estimatedSectionHeaderHeight + self.tableView.register(headerFooterViewType: EditHistoryHeaderView.self) - self.messageLabel.text = "VectorL10n.editHistoryTitle" - self.messageLabel.isHidden = true + self.tableView.tableFooterView = UIView() } private func render(viewState: EditHistoryViewState) { switch viewState { case .loading: self.renderLoading() - case .loaded(let messages, let addedCount): - self.renderLoaded(messages: messages, addedCount: addedCount) - case .allLoaded: - self.renderAllLoaded() + case .loaded(let sections, let addedCount, let allDataLoaded): + self.renderLoaded(sections: sections, addedCount: addedCount, allDataLoaded: allDataLoaded) case .error(let error): - self.render(error: error) + self.render(error: error) } } private func renderLoading() { - self.activityPresenter.presentActivityIndicator(on: self.view, animated: true) + self.activityIndicatorPresenter.presentActivityIndicator(on: self.view, animated: true) } - private func renderLoaded(messages: [EditHistoryMessage], addedCount: Int) { - self.activityPresenter.removeCurrentActivityIndicator(animated: true) - - let calendar = Calendar.current - - let attributedText = NSMutableAttributedString() - for message in messages { - let time=calendar.dateComponents([.hour, .minute], from: message.date) - attributedText.append(NSAttributedString(string: "\(time.hour!):\(time.minute!)")) - attributedText.append(NSAttributedString(string: " - ")) - attributedText.append(message.message) - attributedText.append(NSAttributedString(string: "\n")) - } - - self.messageLabel.attributedText = attributedText - self.messageLabel.isHidden = false - } - - private func renderAllLoaded() { - self.loadMoreButton.isHidden = true + private func renderLoaded(sections: [EditHistorySection], addedCount: Int, allDataLoaded: Bool) { + self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true) + self.editHistorySections = sections + self.tableView.reloadData() } private func render(error: Error) { - self.activityPresenter.removeCurrentActivityIndicator(animated: true) + self.activityIndicatorPresenter.removeCurrentActivityIndicator(animated: true) self.errorPresenter.presentError(from: self, forError: error, animated: true, handler: nil) } - // MARK: - Actions - @IBAction private func loadMoreButtonAction(_ sender: Any) { - self.viewModel.process(viewAction: .loadMore) - } - private func closeButtonAction() { self.viewModel.process(viewAction: .close) } } +// MARK: - UITableViewDataSource +extension EditHistoryViewController: UITableViewDataSource { + + func numberOfSections(in tableView: UITableView) -> Int { + return self.editHistorySections.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return self.editHistorySections[section].messages.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let editHistoryCell = tableView.dequeueReusableCell(for: indexPath, cellType: EditHistoryCell.self) + + let editHistoryMessage = self.editHistorySections[indexPath.section].messages[indexPath.row] + + let timeString = self.messageDateFormatter.string(from: editHistoryMessage.date) + + editHistoryCell.update(theme: self.theme) + editHistoryCell.fill(with: timeString, and: editHistoryMessage.message) + + return editHistoryCell + } + + func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { + guard let editHistoryHeaderView: EditHistoryHeaderView = tableView.dequeueReusableHeaderFooterView() else { + return nil + } + let editHistorySection = self.editHistorySections[section] + let dateString = self.sectionDateFormatter.string(from: editHistorySection.date) + + editHistoryHeaderView.update(theme: self.theme) + editHistoryHeaderView.fill(with: dateString) + return editHistoryHeaderView + } +} + +// MARK: - UITableViewDelegate +extension EditHistoryViewController: UITableViewDelegate { + + func scrollViewDidScroll(_ scrollView: UIScrollView) { + + // Check if a scroll beyond scroll view content occurs + let distanceFromBottom = scrollView.contentSize.height - scrollView.contentOffset.y + if distanceFromBottom < scrollView.frame.size.height { + self.viewModel.process(viewAction: .loadMore) + } + } +} // MARK: - EditHistoryViewModelViewDelegate extension EditHistoryViewController: EditHistoryViewModelViewDelegate { From 3a8122bdb13c8702809c04a3fc1d5b1a339e33fa Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 4 Jul 2019 19:24:15 +0200 Subject: [PATCH 229/266] Message edits history: Handle message edits history display from RoomViewController. --- ...ditHistoryCoordinatorBridgePresenter.swift | 3 +- Riot/Modules/Room/RoomViewController.m | 29 +++++++++++++++---- 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift index a566c959ba..ecd2a3b73e 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift @@ -65,7 +65,8 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject { let editHistoryCoordinator = EditHistoryCoordinator(aggregations: self.session.aggregations, formatter: formatter, event: self.event) editHistoryCoordinator.delegate = self - let navigationController = UINavigationController() + let navigationController = RiotNavigationController() + navigationController.modalPresentationStyle = .formSheet navigationController.addChild(editHistoryCoordinator.toPresentable()) viewController.present(navigationController, animated: animated, completion: nil) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index e0bc7ff220..c7c0818a8a 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -124,7 +124,7 @@ #import "Riot-Swift.h" @interface RoomViewController () + ReactionsMenuViewModelCoordinatorDelegate, EditHistoryCoordinatorBridgePresenterDelegate> { // The expanded header ExpandedRoomTitleView *expandedHeader; @@ -221,6 +221,7 @@ @interface RoomViewController () *arguments = [absoluteURLString componentsSeparatedByString:EventFormatterLinkActionSeparator]; if (arguments.count > 1) { - // TODO: Handle event edition history. - NSString *eventId = arguments[1]; - - NSLog(@"[RoomViewController] Did tap edited mention for eventId: %@", eventId); + [self showEditHistoryForEventId:eventId animated:YES]; } shouldDoAction = NO; } @@ -5291,5 +5289,26 @@ - (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didRemoveReac }]; } +#pragma mark - + +- (void)showEditHistoryForEventId:(NSString*)eventId animated:(BOOL)animated +{ + MXEvent *event = [self.roomDataSource eventWithEventId:eventId]; + EditHistoryCoordinatorBridgePresenter *presenter = [[EditHistoryCoordinatorBridgePresenter alloc] initWithSession:self.roomDataSource.mxSession event:event]; + + presenter.delegate = self; + [presenter presentFrom:self animated:animated]; + + self.editHistoryPresenter = presenter; +} + +#pragma mark - EditHistoryCoordinatorBridgePresenterDelegate + +- (void)editHistoryCoordinatorBridgePresenterDelegateDidComplete:(EditHistoryCoordinatorBridgePresenter *)coordinatorBridgePresenter +{ + [coordinatorBridgePresenter dismissWithAnimated:YES completion:nil]; + self.editHistoryPresenter = nil; +} + @end From e5558c980c21c0a389573e68467b658670d825a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Thu, 4 Jul 2019 12:30:16 +0000 Subject: [PATCH 230/266] Translated using Weblate (French) Currently translated at 100.0% (711 of 711 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index 91eb54c3f9..c91bb3b548 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -781,3 +781,5 @@ "widget_no_integrations_server_configured" = "Aucun serveur d’intégrations n’est configuré"; "widget_integrations_server_failed_to_connect" = "Échec de connexion au serveur d’intégrations"; "device_verification_emoji_lock" = "Cadenas"; +"room_event_action_reaction_show_all" = "Tout afficher"; +"room_event_action_reaction_show_less" = "Réduire"; From 12896ffc36939f1489c3fa07823b04120d0101b7 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 5 Jul 2019 14:14:46 +0200 Subject: [PATCH 231/266] Message editing: Handle encrypted message edits history. --- .../EditHistory/EditHistoryCoordinator.swift | 4 +- ...ditHistoryCoordinatorBridgePresenter.swift | 2 +- .../EditHistory/EditHistoryViewModel.swift | 38 +++++++++++++------ 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift index 760b5ccf63..7cabf0a738 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift @@ -37,11 +37,11 @@ final class EditHistoryCoordinator: EditHistoryCoordinatorType { // MARK: - Setup - init(aggregations: MXAggregations, + init(session: MXSession, formatter: MXKEventFormatter, event: MXEvent) { - let editHistoryViewModel = EditHistoryViewModel(aggregations: aggregations, formatter: formatter, event: event) + let editHistoryViewModel = EditHistoryViewModel(session: session, formatter: formatter, event: event) let editHistoryViewController = EditHistoryViewController.instantiate(with: editHistoryViewModel) self.editHistoryViewModel = editHistoryViewModel self.editHistoryViewController = editHistoryViewController diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift index ecd2a3b73e..f6b20b5e54 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryCoordinatorBridgePresenter.swift @@ -62,7 +62,7 @@ final class EditHistoryCoordinatorBridgePresenter: NSObject { return } - let editHistoryCoordinator = EditHistoryCoordinator(aggregations: self.session.aggregations, formatter: formatter, event: self.event) + let editHistoryCoordinator = EditHistoryCoordinator(session: self.session, formatter: formatter, event: self.event) editHistoryCoordinator.delegate = self let navigationController = RiotNavigationController() diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift index 541491a939..47948eb98c 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift @@ -30,6 +30,7 @@ final class EditHistoryViewModel: EditHistoryViewModelType { // MARK: Private + private let session: MXSession private let aggregations: MXAggregations private let formatter: MXKEventFormatter private let roomId: String @@ -48,10 +49,11 @@ final class EditHistoryViewModel: EditHistoryViewModelType { // MARK: - Setup - init(aggregations: MXAggregations, + init(session: MXSession, formatter: MXKEventFormatter, event: MXEvent) { - self.aggregations = aggregations + self.session = session + self.aggregations = session.aggregations self.formatter = formatter self.event = event self.roomId = event.roomId @@ -102,7 +104,8 @@ final class EditHistoryViewModel: EditHistoryViewModelType { } self.update(viewState: .loading) - self.operation = self.aggregations.replaceEvents(forEvent: self.event.eventId, inRoom: self.roomId, from: self.nextBatch, limit: Pagination.count, success: { [weak self] (response) in + + self.operation = self.aggregations.replaceEvents(forEvent: self.event.eventId, isEncrypted: self.event.isEncrypted, inRoom: self.roomId, from: self.nextBatch, limit: Pagination.count, success: { [weak self] (response) in guard let sself = self else { return } @@ -129,16 +132,21 @@ final class EditHistoryViewModel: EditHistoryViewModelType { .compactMap { (editEvent) -> EditHistoryMessage? in return self.process(editEvent: editEvent) } + + let allDataLoaded = self.nextBatch == nil + let addedCount: Int if newMessages.count > 0 { - self.messages = newMessages + self.messages - let allDataLoaded = self.nextBatch == nil - - let editHistorySections = self.editHistorySections(from: self.messages) - - DispatchQueue.main.async { - self.update(viewState: .loaded(sections: editHistorySections, addedCount: newMessages.count, allDataLoaded: allDataLoaded)) - } + self.messages.append(contentsOf: newMessages) + addedCount = newMessages.count + } else { + addedCount = 0 + } + + let editHistorySections = self.editHistorySections(from: self.messages) + + DispatchQueue.main.async { + self.update(viewState: .loaded(sections: editHistorySections, addedCount: addedCount, allDataLoaded: allDataLoaded)) } } } @@ -183,10 +191,16 @@ final class EditHistoryViewModel: EditHistoryViewModelType { print("[EditHistoryViewModel] processEditEvent: Cannot build edited event: \(editEvent.eventId ?? "")") return nil } + + if editedEvent.isEncrypted && editedEvent.clear == nil { + if self.session.decryptEvent(editedEvent, inTimeline: nil) == false { + print("[EditHistoryViewModel] processEditEvent: Fail to decrypt event: \(editedEvent.eventId ?? "")") + } + } let formatterError = UnsafeMutablePointer.allocate(capacity: 1) guard let message = self.formatter.attributedString(from: editedEvent, with: nil, error: formatterError) else { - print("[EditHistoryViewModel] processEditEvent: cannot format(error: \(formatterError)) edited event: \(editEvent.eventId ?? "")") + print("[EditHistoryViewModel] processEditEvent: cannot format(error: \(formatterError)) edited event: \(editedEvent.eventId ?? "")") return nil } From 13d8d66024171f61360e67e2b5e64f7bde6179a1 Mon Sep 17 00:00:00 2001 From: Besnik Bleta Date: Fri, 5 Jul 2019 10:12:04 +0000 Subject: [PATCH 232/266] Translated using Weblate (Albanian) Currently translated at 99.2% (707 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/sq/ --- Riot/Assets/sq.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/sq.lproj/Vector.strings b/Riot/Assets/sq.lproj/Vector.strings index c6056007f0..f1ba0864e9 100644 --- a/Riot/Assets/sq.lproj/Vector.strings +++ b/Riot/Assets/sq.lproj/Vector.strings @@ -774,3 +774,7 @@ // Widget "widget_no_integrations_server_configured" = "S’ka të formësuar shërbyes integrimesh"; "widget_integrations_server_failed_to_connect" = "S’u arrit të lidhej me shërbyes integrimesh"; +"close" = "Mbylle"; +"room_event_action_reaction_show_all" = "Shfaqi krejt"; +"room_event_action_reaction_show_less" = "Shfaq më pak"; +"room_message_edits_history_title" = "Përpunime mesazhi"; From 201a87ff11691d4a790d040a4980544ff8d09607 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 5 Jul 2019 11:24:57 +0000 Subject: [PATCH 233/266] Translated using Weblate (French) Currently translated at 100.0% (713 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index c91bb3b548..c63e348ada 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -783,3 +783,5 @@ "device_verification_emoji_lock" = "Cadenas"; "room_event_action_reaction_show_all" = "Tout afficher"; "room_event_action_reaction_show_less" = "Réduire"; +"close" = "Fermer"; +"room_message_edits_history_title" = "Éditions de message"; From 4f6f5a771f1c3dc0a2d0c837f77d0ca0aad4a91a Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Fri, 5 Jul 2019 20:56:37 +0200 Subject: [PATCH 234/266] Reactions: Fix unexpected padding after event selection (Fix #2548). --- Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift index a3238b8096..4ac8923e4e 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.swift @@ -99,6 +99,7 @@ final class BubbleReactionsView: UIView, NibOwnerLoadable { self.reactionsViewData = reactionsViewData self.showAllButtonState = showAllButtonState self.collectionView.reloadData() + self.collectionView.collectionViewLayout.invalidateLayout() } private func actionButtonString() -> String { From 87b1e15f3628244f4232fd73636ac5e65865ce41 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 8 Jul 2019 11:15:44 +0200 Subject: [PATCH 235/266] Join Room: Support via parameters to better handle federation #2547 --- CHANGES.rst | 1 + Riot/AppDelegate.m | 19 +++++++++++++++++-- Riot/Model/Room/RoomPreviewData.h | 6 ++++++ .../Common/Recents/RecentsViewController.m | 5 +++-- Riot/Modules/Room/RoomViewController.m | 5 +++-- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d2bd2d6a06..7427fef559 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,7 @@ Changes in 0.8.7 (2019-xx-xx) Improvements: * RoomVC: When replying, use a "Reply" button instead of "Send". * RoomVC: New message actions (#2394). + * Join Room: Support via parameters to better handle federation (#2547). * Reactions: Display existing reactions below the message (#2396). * Menu actions: Display message time (#2463). * Reactions Menu: Fix position (#2447). diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index bc8c22bdd2..276c6657de 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -2324,6 +2324,7 @@ - (BOOL)handleUniversalLinkFragment:(NSString*)fragment [homeViewController stopActivityIndicator]; roomPreviewData = [[RoomPreviewData alloc] initWithRoomId:roomIdOrAlias emailInvitationParams:queryParams andSession:account.mxSession]; + roomPreviewData.viaServers = queryParams[@"via"]; [self showRoomPreview:roomPreviewData]; } else @@ -2522,8 +2523,22 @@ - (void)parseUniversalLinkFragment:(NSString*)fragment outPathParams:(NSArray *viaServers; + /** Preview information. */ diff --git a/Riot/Modules/Common/Recents/RecentsViewController.m b/Riot/Modules/Common/Recents/RecentsViewController.m index 03e582aeb8..30a754803d 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.m +++ b/Riot/Modules/Common/Recents/RecentsViewController.m @@ -1815,8 +1815,9 @@ - (void)joinARoom self->currentAlert = nil; [self.activityIndicator startAnimating]; - - self->currentRequest = [self.mainSession joinRoom:roomAliasOrId success:^(MXRoom *room) { + + // TODO + self->currentRequest = [self.mainSession joinRoom:roomAliasOrId viaServers:nil success:^(MXRoom *room) { self->currentRequest = nil; [self.activityIndicator stopAnimating]; diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index c7c0818a8a..ceafd6b03c 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -1047,7 +1047,8 @@ - (BOOL)isIRCStyleCommand:(NSString*)string // Check if (roomAlias.length) { - [self.mainSession joinRoom:roomAlias success:^(MXRoom *room) { + // TODO: /join command does not support via parameters yet + [self.mainSession joinRoom:roomAlias viaServers:nil success:^(MXRoom *room) { // Show the room [[AppDelegate theDelegate] showRoom:room.roomId andEventId:nil withMatrixSession:self.mainSession]; @@ -3685,7 +3686,7 @@ - (void)roomTitleView:(RoomTitleView*)titleView recognizeTapGesture:(UITapGestur } // Note in case of simple link to a room the signUrl param is nil - [self joinRoomWithRoomIdOrAlias:roomIdOrAlias andSignUrl:roomPreviewData.emailInvitation.signUrl completion:^(BOOL succeed) { + [self joinRoomWithRoomIdOrAlias:roomIdOrAlias viaServers:roomPreviewData.viaServers andSignUrl:roomPreviewData.emailInvitation.signUrl completion:^(BOOL succeed) { if (succeed) { From 0d5eac4cf630fddd7fca1d7c7fa789bf7105e3fe Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 8 Jul 2019 11:46:10 +0200 Subject: [PATCH 236/266] Room upgrade: Use the `server_name` parameter when joining the new room #2550 --- CHANGES.rst | 1 + Riot/Modules/Room/RoomViewController.m | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 7427fef559..de78b0c24a 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,7 @@ Changes in 0.8.7 (2019-xx-xx) Improvements: * RoomVC: When replying, use a "Reply" button instead of "Send". * RoomVC: New message actions (#2394). + * Room upgrade: Use the `server_name` parameter when joining the new room (#2550). * Join Room: Support via parameters to better handle federation (#2547). * Reactions: Display existing reactions below the message (#2396). * Menu actions: Display message time (#2463). diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index ceafd6b03c..84961aa837 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -4048,8 +4048,15 @@ - (void)refreshActivitiesViewDisplay } else if (customizedRoomDataSource.roomState.isObsolete) { + // Try to join via the server that sent the event + MXEvent *stoneTombEvent = [customizedRoomDataSource.roomState stateEventsWithType:kMXEventTypeStringRoomTombStone].lastObject; + NSString *viaSenderServer = [MXTools serverNameInMatrixIdentifier:stoneTombEvent.sender]; + NSString *replacementRoomId = customizedRoomDataSource.roomState.tombStoneContent.replacementRoomId; - NSString *roomLinkFragment = [NSString stringWithFormat:@"/room/%@", [MXTools encodeURIComponent:replacementRoomId]]; + NSString *roomLinkFragment = [NSString stringWithFormat:@"/room/%@?via=%@", + [MXTools encodeURIComponent:replacementRoomId], + viaSenderServer + ]; [roomActivitiesView displayRoomReplacementWithRoomLinkTappedHandler:^{ [[AppDelegate theDelegate] handleUniversalLinkFragment:roomLinkFragment]; From bb6ddd3fb238d4579eeeb8ffcec53c249d183264 Mon Sep 17 00:00:00 2001 From: IMIN <2reeseenmin@gmail.com> Date: Sun, 7 Jul 2019 20:57:58 +0000 Subject: [PATCH 237/266] Translated using Weblate (Korean) Currently translated at 100.0% (5 of 5 strings) Translation: Riot iOS/Riot iOS (Dialogs) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-dialogs/ko/ --- Riot/Assets/ko.lproj/InfoPlist.strings | 1 + 1 file changed, 1 insertion(+) diff --git a/Riot/Assets/ko.lproj/InfoPlist.strings b/Riot/Assets/ko.lproj/InfoPlist.strings index d253e9c238..5ce0ebe82a 100644 --- a/Riot/Assets/ko.lproj/InfoPlist.strings +++ b/Riot/Assets/ko.lproj/InfoPlist.strings @@ -3,3 +3,4 @@ "NSPhotoLibraryUsageDescription" = "포토 라이브러리는 사진과 영상을 보내는데 쓰입니다."; "NSMicrophoneUsageDescription" = "마이크는 영상 촬영, 통화에 쓰입니다."; "NSContactsUsageDescription" = "연락처에 있는 상대가 이미 Riot이나 Matrix를 이용하고 있다는 걸 보여주기 위해 연락처의 이메일 주소와 전화번호를 Matrix 아이덴티티 서버로 보낼 수 있습니다. 새 Vector는 이 자료를 저장하거나 다른 용도로 사용하지 않습니다. 자세한 내용은 애플리케이션 설정에 있는 개인정보 보호정책을 읽어주세요."; +"NSCalendarsUsageDescription" = "앱에서 예정된 회의를 봅니다."; From aa0818afe79d989b7864867203cf673a51fed734 Mon Sep 17 00:00:00 2001 From: IMIN <2reeseenmin@gmail.com> Date: Sun, 7 Jul 2019 20:58:38 +0000 Subject: [PATCH 238/266] Translated using Weblate (Korean) Currently translated at 60.7% (17 of 28 strings) Translation: Riot iOS/Riot iOS (Push) Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios-push/ko/ --- Riot/Assets/ko.lproj/Localizable.strings | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Riot/Assets/ko.lproj/Localizable.strings b/Riot/Assets/ko.lproj/Localizable.strings index 09d3383716..779227e7f5 100644 --- a/Riot/Assets/ko.lproj/Localizable.strings +++ b/Riot/Assets/ko.lproj/Localizable.strings @@ -13,13 +13,13 @@ /* New action message from a specific person, not referencing a room. */ "IMAGE_FROM_USER" = "%@ 보낸 사진 %@"; /* A single unread message in a room */ -"SINGLE_UNREAD_IN_ROOM" = "%@에서 메시지를 받았습니다."; +"SINGLE_UNREAD_IN_ROOM" = "%@에서 메시지를 받았습니다"; /* A single unread message */ -"SINGLE_UNREAD" = "메시지를 받았습니다."; +"SINGLE_UNREAD" = "메시지를 받았습니다"; /* A user has invited you to a chat */ -"USER_INVITE_TO_CHAT" = "%@가 대화에 당신을 초대했습니다."; +"USER_INVITE_TO_CHAT" = "%@가 대화에 당신을 초대했습니다"; /* A user has invited you to an (unamed) group chat */ -"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@가 그룹 대화에 당신을 초대했습니다."; +"USER_INVITE_TO_CHAT_GROUP_CHAT" = "%@가 그룹 대화에 당신을 초대했습니다"; /* Incoming one-to-one voice call */ "VOICE_CALL_FROM_USER" = "%@이 건 통화"; /* Incoming one-to-one video call */ @@ -28,3 +28,7 @@ "VOICE_CONF_FROM_USER" = "%@이 건 그룹통화"; /* Incoming unnamed video conference invite from a specific person */ "VIDEO_CONF_FROM_USER" = "%@이 건 영상그룹통화"; +/* Sticker from a specific person, not referencing a room. */ +"STICKER_FROM_USER" = "%@가 스티커를 보냈습니다"; +/* A user has invited you to a named room */ +"USER_INVITE_TO_NAMED_ROOM" = "%@가 %@로 당신을 초대했습니다"; From 593e3b376505729faf4a573a35653b40882a9c2c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 8 Jul 2019 18:34:08 +0200 Subject: [PATCH 239/266] RoomBubbleCellData: Add a method to get first visible component index. --- .../Room/CellData/RoomBubbleCellData.h | 4 +++ .../Room/CellData/RoomBubbleCellData.m | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.h b/Riot/Modules/Room/CellData/RoomBubbleCellData.h index e03035abb9..777063a598 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.h +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.h @@ -80,6 +80,10 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag) */ - (void)updateAdditionalContentHeightIfNeeded; +/** + The index of the first visible component. NSNotFound by default. + */ +- (NSInteger)firstVisibleComponentIndex; #pragma mark - Show all reactions diff --git a/Riot/Modules/Room/CellData/RoomBubbleCellData.m b/Riot/Modules/Room/CellData/RoomBubbleCellData.m index 44f219b2ff..5fda69f74d 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -277,6 +277,31 @@ - (NSAttributedString*)refreshAttributedTextMessage return currentAttributedTextMsg; } +- (NSInteger)firstVisibleComponentIndex +{ + __block NSInteger firstVisibleComponentIndex = NSNotFound; + + if (self.attachment && self.bubbleComponents.count) + { + firstVisibleComponentIndex = 0; + } + else + { + [self.bubbleComponents enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) { + + MXKRoomBubbleComponent *component = (MXKRoomBubbleComponent*)obj; + + if (component.attributedTextMessage) + { + firstVisibleComponentIndex = idx; + *stop = YES; + } + }]; + } + + return firstVisibleComponentIndex; +} + - (void)refreshBubbleComponentsPosition { // CAUTION: This method must be called on the main thread. From 85e8d6eb27b5494a501ca676227286a6bc24c0fe Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 8 Jul 2019 18:42:32 +0200 Subject: [PATCH 240/266] MXKRoomBubbleTableViewCell: Add a method to get surrounding bubble component frame. --- .../MXKRoomBubbleTableViewCell+Riot.h | 8 +++ .../MXKRoomBubbleTableViewCell+Riot.m | 60 +++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h index c9f58e2aa1..260d79c51f 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.h @@ -90,6 +90,14 @@ extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer; */ - (CGRect)componentFrameInTableViewForIndex:(NSInteger)componentIndex; +/** + Calculate surrounding component frame in table view. This frame goes over user name for first visible component for example. + + @param componentIndex index of the component in bubble message data + @return Component surrounding frame in table view if component exist or CGRectNull. + */ +- (CGRect)surroundingFrameInTableViewForComponentIndex:(NSInteger)componentIndex; + /** Calculate the component frame in the contentView of the tableview cell. diff --git a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m index 2efb265c67..82389e987a 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -473,6 +473,66 @@ - (CGRect)componentFrameInTableViewForIndex:(NSInteger)componentIndex return [self.contentView convertRect:componentFrameInContentView toView:self.superview]; } +- (CGRect)surroundingFrameInTableViewForComponentIndex:(NSInteger)componentIndex +{ + CGRect surroundingFrame; + + CGRect componentFrameInContentView = [self componentFrameInContentViewForIndex:componentIndex]; + MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = self; + MXKRoomBubbleCellData *bubbleCellData = roomBubbleTableViewCell.bubbleData; + + NSInteger firstVisibleComponentIndex = NSNotFound; + NSInteger lastMostRecentComponentIndex = NSNotFound; + + if ([bubbleCellData isKindOfClass:[RoomBubbleCellData class]]) + { + RoomBubbleCellData *roomBubbleCellData = (RoomBubbleCellData*)bubbleCellData; + firstVisibleComponentIndex = [roomBubbleCellData firstVisibleComponentIndex]; + + if (roomBubbleCellData.containsLastMessage + && roomBubbleCellData.mostRecentComponentIndex != NSNotFound + && roomBubbleCellData.firstVisibleComponentIndex != roomBubbleCellData.mostRecentComponentIndex + && componentIndex == roomBubbleCellData.mostRecentComponentIndex) + { + lastMostRecentComponentIndex = roomBubbleCellData.mostRecentComponentIndex; + } + } + + // Do not overlap timestamp for last message + if (lastMostRecentComponentIndex != NSNotFound) + { + CGFloat componentBottomY = componentFrameInContentView.origin.y + componentFrameInContentView.size.height; + + CGFloat x = 0; + CGFloat y = componentFrameInContentView.origin.y - RoomBubbleCellLayout.timestampLabelHeight; + CGFloat width = roomBubbleTableViewCell.contentView.frame.size.width; + CGFloat height = componentBottomY - y; + + surroundingFrame = CGRectMake(x, y, width, height); + } // Do not overlap user name label for first visible component + else if (!CGRectEqualToRect(componentFrameInContentView, CGRectNull) + && firstVisibleComponentIndex != NSNotFound + && componentIndex <= firstVisibleComponentIndex + && roomBubbleTableViewCell.userNameLabel + && roomBubbleTableViewCell.userNameLabel.isHidden == NO) + { + CGFloat componentBottomY = componentFrameInContentView.origin.y + componentFrameInContentView.size.height; + + CGFloat x = 0; + CGFloat y = roomBubbleTableViewCell.userNameLabel.frame.origin.y; + CGFloat width = roomBubbleTableViewCell.contentView.frame.size.width; + CGFloat height = componentBottomY - y; + + surroundingFrame = CGRectMake(x, y, width, height); + } + else + { + surroundingFrame = componentFrameInContentView; + } + + return [self.contentView convertRect:surroundingFrame toView:self.superview]; +} + - (CGRect)componentFrameInContentViewForIndex:(NSInteger)componentIndex { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = self; From 24f6d03e4c6c71dcd90c093cae78318bcfd982ef Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Mon, 8 Jul 2019 18:43:55 +0200 Subject: [PATCH 241/266] RoomVC: Fix reactions menu timestamp and display name overlap (Fix #2538). --- Riot/Modules/Room/RoomViewController.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index c7c0818a8a..5c9bc191ee 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5169,7 +5169,7 @@ - (void)showContextualMenuForEvent:(MXEvent*)event fromSingleTapGesture:(BOOL)us if (bubbleComponents.count > 0) { NSInteger selectedComponentIndex = foundComponentIndex != NSNotFound ? foundComponentIndex : 0; - bubbleComponentFrame = [roomBubbleTableViewCell componentFrameInTableViewForIndex:selectedComponentIndex]; + bubbleComponentFrame = [roomBubbleTableViewCell surroundingFrameInTableViewForComponentIndex:selectedComponentIndex]; } else { From 3b45885143dd30546f676bbc31586fa314c300ec Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 9 Jul 2019 11:02:52 +0200 Subject: [PATCH 242/266] Reactions: Limit Emoji string length in reaction bubble. --- .../BubbleReactionViewCell.xib | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib index db90d9281d..17552af695 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib @@ -13,23 +13,26 @@ - + - + - + - From 012ea5b9c352baf1391ef88abf6dbf1508a57c3c Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 9 Jul 2019 11:23:46 +0200 Subject: [PATCH 243/266] Bubble reaction view: Handle emoji label theme color. --- .../Room/BubbleReactions/BubbleReactionViewCell.swift | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift index 3b3d46a03b..c6e2af6269 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.swift @@ -81,8 +81,9 @@ final class BubbleReactionViewCell: UICollectionViewCell, NibReusable, Themable func update(theme: Theme) { self.theme = theme - self.reactionBackgroundView.layer.borderColor = self.theme?.tintColor.cgColor - self.countLabel.textColor = self.theme?.textPrimaryColor + self.reactionBackgroundView.layer.borderColor = theme.tintColor.cgColor + self.emojiLabel.textColor = theme.textPrimaryColor + self.countLabel.textColor = theme.textPrimaryColor self.updateViews() } From 436f501a91ca68dde7668d31158e160c409324ae Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Tue, 9 Jul 2019 13:51:14 +0200 Subject: [PATCH 244/266] LABS: Remove reaction settings, reactions are enabled by default. --- Riot/Managers/Settings/RiotSettings.swift | 9 ------ .../Modules/Room/DataSources/RoomDataSource.m | 2 +- Riot/Modules/Room/RoomViewController.m | 2 +- .../Modules/Settings/SettingsViewController.m | 32 +------------------ 4 files changed, 3 insertions(+), 42 deletions(-) diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index 09d5220ff7..6db8ec7b3f 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -26,7 +26,6 @@ final class RiotSettings: NSObject { static let enableCrashReport = "enableCrashReport" static let enableRageShake = "enableRageShake" static let createConferenceCallsWithJitsi = "createConferenceCallsWithJitsi" - static let messageReaction = "messageReaction" static let userInterfaceTheme = "userInterfaceTheme" static let notificationsShowDecryptedContent = "showDecryptedContent" static let pinRoomsWithMissedNotifications = "pinRoomsWithMissedNotif" @@ -120,12 +119,4 @@ final class RiotSettings: NSObject { UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.createConferenceCallsWithJitsi) } } - - var messageReaction: Bool { - get { - return UserDefaults.standard.bool(forKey: UserDefaultsKeys.messageReaction) - } set { - UserDefaults.standard.set(newValue, forKey: UserDefaultsKeys.messageReaction) - } - } } diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 7282bd4eb9..75208dbfc0 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -61,7 +61,7 @@ - (instancetype)initWithRoomId:(NSString *)roomId andMatrixSession:(MXSession *) self.markTimelineInitialEvent = NO; self.showBubbleDateTimeOnSelection = YES; - self.showReactions = RiotSettings.shared.messageReaction; + self.showReactions = YES; // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 106c5a99aa..780f7adc85 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5157,7 +5157,7 @@ - (void)showContextualMenuForEvent:(MXEvent*)event fromSingleTapGesture:(BOOL)us ReactionsMenuViewModel *reactionsMenuViewModel; CGRect bubbleComponentFrameInOverlayView = CGRectNull; - if (RiotSettings.shared.messageReaction && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && [self.roomDataSource canReactToEventWithId:event.eventId]) + if ([cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && [self.roomDataSource canReactToEventWithId:event.eventId]) { MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell*)cell; MXKRoomBubbleCellData *bubbleCellData = roomBubbleTableViewCell.bubbleData; diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index b397757e1c..4cccbedf25 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -120,7 +120,6 @@ { LABS_USE_ROOM_MEMBERS_LAZY_LOADING_INDEX = 0, LABS_USE_JITSI_WIDGET_INDEX, - LABS_USE_MESSAGE_REACTION_INDEX, LABS_CRYPTO_INDEX, LABS_COUNT }; @@ -2136,20 +2135,7 @@ - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(N [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleJitsiForConference:) forControlEvents:UIControlEventTouchUpInside]; cell = labelAndSwitchCell; - } - else if (row == LABS_USE_MESSAGE_REACTION_INDEX) - { - MXKTableViewCellWithLabelAndSwitch* labelAndSwitchCell = [self getLabelAndSwitchCell:tableView forIndexPath:indexPath]; - - labelAndSwitchCell.mxkLabel.text = NSLocalizedStringFromTable(@"settings_labs_message_reaction", @"Vector", nil); - labelAndSwitchCell.mxkSwitch.on = RiotSettings.shared.messageReaction; - labelAndSwitchCell.mxkSwitch.onTintColor = ThemeService.shared.theme.tintColor; - labelAndSwitchCell.mxkSwitch.enabled = YES; - - [labelAndSwitchCell.mxkSwitch addTarget:self action:@selector(toggleMessageReaction:) forControlEvents:UIControlEventTouchUpInside]; - - cell = labelAndSwitchCell; - } + } else if (row == LABS_CRYPTO_INDEX) { MXSession* session = [AppDelegate theDelegate].mxSessions[0]; @@ -3049,22 +3035,6 @@ - (void)toggleJitsiForConference:(id)sender } } -- (void)toggleMessageReaction:(id)sender -{ - if (sender && [sender isKindOfClass:UISwitch.class]) - { - UISwitch *switchButton = (UISwitch*)sender; - - RiotSettings.shared.messageReaction = switchButton.isOn; - - // Reset cached room data sources - MXKRoomDataSourceManager *roomDataSourceManager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:self.mainSession]; - [roomDataSourceManager reset]; - - [self.tableView reloadData]; - } -} - - (void)toggleLabsEndToEndEncryption:(id)sender { if (sender && [sender isKindOfClass:UISwitch.class]) From c09faea091dfdabeb4cbe7f1c9c06da9b46f45fd Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 10 Jul 2019 08:20:05 +0200 Subject: [PATCH 245/266] Edits history: Display original event #2559 --- Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift index 47948eb98c..461f32699a 100644 --- a/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift +++ b/Riot/Modules/Room/EditHistory/EditHistoryViewModel.swift @@ -114,6 +114,15 @@ final class EditHistoryViewModel: EditHistoryViewModelType { sself.operation = nil sself.process(editEvents: response.chunk) + + if response.nextBatch == nil { + // Append the original event when hitting the end of the edits history + if let originalEvent = response.originalEvent { + sself.process(editEvents: [originalEvent]) + } else { + print("[EditHistoryViewModel] loadMoreHistory: The homeserver did not return the original event") + } + } }, failure: { [weak self] error in guard let sself = self else { From 54af44d2ad3525160b89be165c3c41f0a7cd20a3 Mon Sep 17 00:00:00 2001 From: manuroe Date: Wed, 10 Jul 2019 14:44:48 +0200 Subject: [PATCH 246/266] Room upgrade: Autojoin the upgraded room when the user taps on the tombstone banner #2486 --- CHANGES.rst | 1 + Riot/Modules/Room/RoomViewController.m | 47 ++++++++++++++++++++------ 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index de78b0c24a..7daae55b1e 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,6 +4,7 @@ Changes in 0.8.7 (2019-xx-xx) Improvements: * RoomVC: When replying, use a "Reply" button instead of "Send". * RoomVC: New message actions (#2394). + * Room upgrade: Autojoin the upgraded room when the user taps on the tombstone banner (#2486). * Room upgrade: Use the `server_name` parameter when joining the new room (#2550). * Join Room: Support via parameters to better handle federation (#2547). * Reactions: Display existing reactions below the message (#2396). diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 780f7adc85..b0d4f54c14 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -4048,18 +4048,43 @@ - (void)refreshActivitiesViewDisplay } else if (customizedRoomDataSource.roomState.isObsolete) { - // Try to join via the server that sent the event - MXEvent *stoneTombEvent = [customizedRoomDataSource.roomState stateEventsWithType:kMXEventTypeStringRoomTombStone].lastObject; - NSString *viaSenderServer = [MXTools serverNameInMatrixIdentifier:stoneTombEvent.sender]; - - NSString *replacementRoomId = customizedRoomDataSource.roomState.tombStoneContent.replacementRoomId; - NSString *roomLinkFragment = [NSString stringWithFormat:@"/room/%@?via=%@", - [MXTools encodeURIComponent:replacementRoomId], - viaSenderServer - ]; - + MXWeakify(self); [roomActivitiesView displayRoomReplacementWithRoomLinkTappedHandler:^{ - [[AppDelegate theDelegate] handleUniversalLinkFragment:roomLinkFragment]; + MXStrongifyAndReturnIfNil(self); + + MXEvent *stoneTombEvent = [self->customizedRoomDataSource.roomState stateEventsWithType:kMXEventTypeStringRoomTombStone].lastObject; + + NSString *replacementRoomId = self->customizedRoomDataSource.roomState.tombStoneContent.replacementRoomId; + if ([self.roomDataSource.mxSession roomWithRoomId:replacementRoomId]) + { + // Open the room if it is already joined + [[AppDelegate theDelegate] showRoom:replacementRoomId andEventId:nil withMatrixSession:self.roomDataSource.mxSession]; + } + else + { + // Else auto join it via the server that sent the event + NSLog(@"[RoomVC] Auto join an upgraded room: %@ -> %@. Sender: %@", self->customizedRoomDataSource.roomState.roomId, + replacementRoomId, stoneTombEvent.sender); + + NSString *viaSenderServer = [MXTools serverNameInMatrixIdentifier:stoneTombEvent.sender]; + + if (viaSenderServer) + { + [self startActivityIndicator]; + [self.roomDataSource.mxSession joinRoom:replacementRoomId viaServers:@[viaSenderServer] success:^(MXRoom *room) { + [self stopActivityIndicator]; + + [[AppDelegate theDelegate] showRoom:replacementRoomId andEventId:nil withMatrixSession:self.roomDataSource.mxSession]; + + } failure:^(NSError *error) { + [self stopActivityIndicator]; + + NSLog(@"[RoomVC] Failed to join an upgraded room. Error: %@", + error); + [[AppDelegate theDelegate] showErrorAsAlert:error]; + }]; + } + } }]; } else if (customizedRoomDataSource.roomState.isOngoingConferenceCall) From 928eb0c69e2a3eaf7ae0e951b13aec58c803eb71 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 11 Jul 2019 19:03:32 +0200 Subject: [PATCH 247/266] RoomInputToolbarView: Add file upload action. --- Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Strings.swift | 4 ++++ .../Room/Views/InputToolbar/RoomInputToolbarView.h | 7 +++++++ .../Room/Views/InputToolbar/RoomInputToolbarView.m | 13 +++++++++++++ 4 files changed, 25 insertions(+) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 18d956eb1b..4ea5f237e9 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -287,6 +287,7 @@ "room_event_failed_to_send" = "Failed to send"; "room_action_send_photo_or_video" = "Send photo or video"; "room_action_send_sticker" = "Send sticker"; +"room_action_send_file" = "Send file"; "room_action_reply" = "Reply"; "room_replacement_information" = "This room has been replaced and is no longer active."; "room_replacement_link" = "The conversation continues here."; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 2caf52f437..9e37dbfa95 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -1350,6 +1350,10 @@ internal enum VectorL10n { internal static var roomActionReply: String { return VectorL10n.tr("Vector", "room_action_reply") } + /// Send file + internal static var roomActionSendFile: String { + return VectorL10n.tr("Vector", "room_action_send_file") + } /// Send photo or video internal static var roomActionSendPhotoOrVideo: String { return VectorL10n.tr("Vector", "room_action_send_photo_or_video") diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h index c548f48cfb..fdff609e19 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h @@ -38,6 +38,13 @@ typedef enum : NSUInteger */ - (void)roomInputToolbarViewPresentStickerPicker:(MXKRoomInputToolbarView*)toolbarView; +/** + Tells the delegate that the user wants to send external files. + + @param toolbarView the room input toolbar view + */ +- (void)roomInputToolbarViewDidTapFileUpload:(MXKRoomInputToolbarView*)toolbarView; + @end /** diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index 2b9ff11e01..f09f0ba8fe 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -349,6 +349,19 @@ - (IBAction)onTouchUpInside:(UIButton*)button } }]]; + + [actionSheet addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_action_send_file", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + self->actionSheet = nil; + + [self.delegate roomInputToolbarViewDidTapFileUpload:self]; + } + }]]; [actionSheet addAction:[UIAlertAction actionWithTitle:[NSBundle mxk_localizedStringForKey:@"cancel"] style:UIAlertActionStyleCancel From 20458bca20ee13fc8c8a0a97c9cd9eae5fcbaa20 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 11 Jul 2019 19:07:45 +0200 Subject: [PATCH 248/266] MXKRoomDataSource: Handle video thumbnail generation with MXKVideoThumbnailGenerator. --- Riot/Modules/Room/DataSources/RoomDataSource.h | 16 ++++++++++++++++ Riot/Modules/Room/DataSources/RoomDataSource.m | 8 ++++++++ 2 files changed, 24 insertions(+) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.h b/Riot/Modules/Room/DataSources/RoomDataSource.h index cfc62b45d8..6e3230a07e 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.h +++ b/Riot/Modules/Room/DataSources/RoomDataSource.h @@ -46,4 +46,20 @@ */ - (Widget *)jitsiWidget; +/** + Send an video to the room. + Note: Move this method to MatrixKit when MatrixKit project will handle Swift module. + + While sending, a fake event will be echoed in the messages list. + Once complete, this local echo will be replaced by the event saved by the homeserver. + + @param videoLocalURL the local filesystem path of the video to send. + @param success A block object called when the operation succeeds. It returns + the event id of the event generated on the homeserver + @param failure A block object called when the operation fails. + */ +- (void)sendVideo:(NSURL*)videoLocalURL + success:(void (^)(NSString *eventId))success + failure:(void (^)(NSError *error))failure; + @end diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.m b/Riot/Modules/Room/DataSources/RoomDataSource.m index 75208dbfc0..fa7034b453 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -555,6 +555,14 @@ - (Widget *)jitsiWidget return jitsiWidget; } +- (void)sendVideo:(NSURL*)videoLocalURL + success:(void (^)(NSString *eventId))success + failure:(void (^)(NSError *error))failure +{ + UIImage *videoThumbnail = [MXKVideoThumbnailGenerator.shared generateThumbnailFrom:videoLocalURL]; + [self sendVideo:videoLocalURL withThumbnail:videoThumbnail success:success failure:failure]; +} + #pragma mark - BubbleReactionsViewModelDelegate - (void)bubbleReactionsViewModel:(BubbleReactionsViewModel *)viewModel didAddReaction:(MXReactionCount *)reactionCount forEventId:(NSString *)eventId From 6d63d704f72ef3878d71256239bc358dfa524c82 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 11 Jul 2019 19:17:55 +0200 Subject: [PATCH 249/266] RoomVC: Handle external file upload. --- Riot/Assets/en.lproj/Vector.strings | 4 +- Riot/Modules/Room/RoomViewController.m | 60 +++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 2 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 4ea5f237e9..ff0f9d47f9 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -901,4 +901,6 @@ "device_verification_emoji_folder" = "Folder"; "device_verification_emoji_pin" = "Pin"; - +// MARK: File upload +"file_upload_error_title" = "File upload"; +"file_upload_error_unsupported_file_type_message" = "File type not supported."; diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index b0d4f54c14..5b574c56f8 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -124,7 +124,7 @@ #import "Riot-Swift.h" @interface RoomViewController () + ReactionsMenuViewModelCoordinatorDelegate, EditHistoryCoordinatorBridgePresenterDelegate, MXKDocumentPickerPresenterDelegate> { // The expanded header ExpandedRoomTitleView *expandedHeader; @@ -222,6 +222,7 @@ @interface RoomViewController () *allowedUTIs = @[MXKUTI.data]; + [documentPickerPresenter presentDocumentPickerWith:allowedUTIs from:self animated:YES completion:nil]; + + self.documentPickerPresenter = documentPickerPresenter; +} + #pragma mark - RoomParticipantsViewControllerDelegate - (void)roomParticipantsViewController:(RoomParticipantsViewController *)roomParticipantsViewController mention:(MXRoomMember*)member @@ -5343,5 +5355,51 @@ - (void)editHistoryCoordinatorBridgePresenterDelegateDidComplete:(EditHistoryCoo self.editHistoryPresenter = nil; } +#pragma mark - DocumentPickerPresenterDelegate + +- (void)documentPickerPresenterWasCancelled:(MXKDocumentPickerPresenter *)presenter +{ + self.documentPickerPresenter = nil; +} + +- (void)documentPickerPresenter:(MXKDocumentPickerPresenter *)presenter didPickDocumentsAt:(NSURL *)url +{ + self.documentPickerPresenter = nil; + + MXKUTI *fileUTI = [[MXKUTI alloc] initWithLocalFileURL:url]; + NSString *mimeType = fileUTI.mimeType; + + if (fileUTI.isImage) + { + NSData *imageData = [[NSData alloc] initWithContentsOfURL:url]; + + [self.roomDataSource sendImage:imageData mimeType:mimeType success:nil failure:^(NSError *error) { + // Nothing to do. The image is marked as unsent in the room history by the datasource + NSLog(@"[MXKRoomViewController] sendImage failed."); + }]; + } + else if (fileUTI.isVideo) + { + [(RoomDataSource*)self.roomDataSource sendVideo:url success:nil failure:^(NSError *error) { + // Nothing to do. The video is marked as unsent in the room history by the datasource + NSLog(@"[MXKRoomViewController] sendVideo failed."); + }]; + } + else if (fileUTI.isFile) + { + [self.roomDataSource sendFile:url mimeType:mimeType success:nil failure:^(NSError *error) { + // Nothing to do. The file is marked as unsent in the room history by the datasource + NSLog(@"[MXKRoomViewController] sendFile failed."); + }]; + } + else + { + NSLog(@"[MXKRoomViewController] File upload using MIME type %@ is not supported.", mimeType); + + [[AppDelegate theDelegate] showAlertWithTitle:NSLocalizedStringFromTable(@"file_upload_error_title", @"Vector", nil) + message:NSLocalizedStringFromTable(@"file_upload_error_unsupported_file_type_message", @"Vector", nil)]; + } +} + @end From 541d07c9c2cefaada08c49c92201aa3ccffd0cc9 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 11 Jul 2019 19:32:53 +0200 Subject: [PATCH 250/266] Podfile: Use SwiftUTI fork and force Swift version to 5.0. --- Podfile | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Podfile b/Podfile index fbab2ca18f..652529affa 100644 --- a/Podfile +++ b/Podfile @@ -61,6 +61,7 @@ abstract_target 'RiotPods' do pod 'GBDeviceInfo', '~> 5.2.0' pod 'Reusable', '~> 4.1' + pod 'SwiftUTI', :git => 'https://github.com/speramusinc/SwiftUTI.git', :branch => 'master' # Piwik for analytics pod 'MatomoTracker', '~> 6.0.1' @@ -99,6 +100,11 @@ post_install do |installer| # Plus the app does not enable it target.build_configurations.each do |config| config.build_settings['ENABLE_BITCODE'] = 'NO' + + # Force SwiftUTI Swift version to 5.0 (as there is no code changes to perform for SwiftUTI fork using Swift 4.2) + if target.name.include? 'SwiftUTI' + config.build_settings['SWIFT_VERSION'] = '5.0' + end end end end From e963f1f8dadc906997057397093d2c89cb7f140b Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 11 Jul 2019 19:33:01 +0200 Subject: [PATCH 251/266] Update strings --- Riot/Generated/Strings.swift | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 9e37dbfa95..e38028ae33 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -910,6 +910,14 @@ internal enum VectorL10n { internal static func eventFormatterWidgetRemoved(_ p1: String, _ p2: String) -> String { return VectorL10n.tr("Vector", "event_formatter_widget_removed", p1, p2) } + /// File upload + internal static var fileUploadErrorTitle: String { + return VectorL10n.tr("Vector", "file_upload_error_title") + } + /// File type not supported. + internal static var fileUploadErrorUnsupportedFileTypeMessage: String { + return VectorL10n.tr("Vector", "file_upload_error_unsupported_file_type_message") + } /// To continue using the %@ homeserver you must review and agree to the terms and conditions. internal static func gdprConsentNotGivenAlertMessage(_ p1: String) -> String { return VectorL10n.tr("Vector", "gdpr_consent_not_given_alert_message", p1) From 43e3aea02babeaa5d0cecc69406f241be059abee Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare Date: Thu, 11 Jul 2019 19:44:54 +0200 Subject: [PATCH 252/266] Update changes --- CHANGES.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.rst b/CHANGES.rst index 7daae55b1e..1a2b6a02af 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -18,6 +18,7 @@ Improvements: * Migrate to Swift 5.0. * Reactions: Update quick reactions (#2459). * Message Editing: Handle reply edition (#2492). + * RoomVC: Add ability to upload a file that comes from outside the app’s sandbox (#2019). Bug fix: * Device Verification: Fix user display name and device id colors in dark theme From 5e0b61b034c09adae4f5d27590f8db5359fcf60a Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 12 Jul 2019 09:51:12 +0200 Subject: [PATCH 253/266] Update Riot/Modules/Room/DataSources/RoomDataSource.h --- Riot/Modules/Room/DataSources/RoomDataSource.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.h b/Riot/Modules/Room/DataSources/RoomDataSource.h index 6e3230a07e..1c7dfda5ac 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.h +++ b/Riot/Modules/Room/DataSources/RoomDataSource.h @@ -47,7 +47,7 @@ - (Widget *)jitsiWidget; /** - Send an video to the room. + Send a video to the room. Note: Move this method to MatrixKit when MatrixKit project will handle Swift module. While sending, a fake event will be echoed in the messages list. From 39cdb579393966d8cbc64817e15c28d285b2d6e1 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Thu, 11 Jul 2019 13:35:56 +0000 Subject: [PATCH 254/266] Translated using Weblate (Basque) Currently translated at 100.0% (713 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eu/ --- Riot/Assets/eu.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index aca76b965d..aa69a920ce 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -769,3 +769,7 @@ "widget_no_integrations_server_configured" = "Ez da integrazio zerbitzaririk konfiguratu"; "widget_integrations_server_failed_to_connect" = "Huts egin du integrazioen zerbitzarira konektatzean"; "device_verification_emoji_lock" = "Giltzarrapoa"; +"close" = "Itxi"; +"room_event_action_reaction_show_all" = "Erakutsi denak"; +"room_event_action_reaction_show_less" = "Erakutsi gutxiago"; +"room_message_edits_history_title" = "Mezuaren edizioak"; From 5f0470beaff5707092d55158e250e3c6f3600ad5 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Thu, 11 Jul 2019 19:50:12 +0000 Subject: [PATCH 255/266] Translated using Weblate (Hungarian) Currently translated at 100.0% (713 of 713 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index f1556d0090..be799172c2 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -786,3 +786,7 @@ "widget_no_integrations_server_configured" = "Integrációs szerver nincs beállítva"; "widget_integrations_server_failed_to_connect" = "Az integrációs szerverhez nem lehet csatlakozni"; "device_verification_emoji_lock" = "Zár"; +"close" = "Bezár"; +"room_event_action_reaction_show_all" = "Mindet mutat"; +"room_event_action_reaction_show_less" = "Kevesebbet mutat"; +"room_message_edits_history_title" = "Üzenet szerkesztések"; From 934a6a62a8e85837de0e7325aaa985d88cabc264 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 12 Jul 2019 12:58:56 +0200 Subject: [PATCH 256/266] Share extension: Enable any file upload (max 5). --- CHANGES.rst | 1 + RiotShareExtension/SupportingFiles/Info.plist | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES.rst b/CHANGES.rst index 1a2b6a02af..57dcf2d705 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -19,6 +19,7 @@ Improvements: * Reactions: Update quick reactions (#2459). * Message Editing: Handle reply edition (#2492). * RoomVC: Add ability to upload a file that comes from outside the app’s sandbox (#2019). + * Share extension: Enable any file upload (max 5). Bug fix: * Device Verification: Fix user display name and device id colors in dark theme diff --git a/RiotShareExtension/SupportingFiles/Info.plist b/RiotShareExtension/SupportingFiles/Info.plist index 4a353b6866..a1c29526de 100644 --- a/RiotShareExtension/SupportingFiles/Info.plist +++ b/RiotShareExtension/SupportingFiles/Info.plist @@ -28,6 +28,8 @@ NSExtensionActivationDictionaryVersion 2 + NSExtensionActivationSupportsFileWithMaxCount + 5 NSExtensionActivationSupportsImageWithMaxCount 5 NSExtensionActivationSupportsMovieWithMaxCount From 097698c2223168de3e361e6358980c98ecc51e65 Mon Sep 17 00:00:00 2001 From: manuroe Date: Fri, 12 Jul 2019 13:18:39 +0200 Subject: [PATCH 257/266] Edits: "(Edited)" -> "(edited)" --- Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 2 +- Riot/Utils/EventFormatter.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index ff0f9d47f9..0bfac7c339 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -580,7 +580,7 @@ "event_formatter_jitsi_widget_removed" = "VoIP conference removed by %@"; "event_formatter_rerequest_keys_part1_link" = "Re-request encryption keys"; "event_formatter_rerequest_keys_part2" = " from your other devices."; -"event_formatter_message_edited_mention" = "(Edited)"; +"event_formatter_message_edited_mention" = "(edited)"; // Others "or" = "or"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index e38028ae33..02182943f7 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -890,7 +890,7 @@ internal enum VectorL10n { internal static func eventFormatterMemberUpdates(_ p1: Int) -> String { return VectorL10n.tr("Vector", "event_formatter_member_updates", p1) } - /// (Edited) + /// (edited) internal static var eventFormatterMessageEditedMention: String { return VectorL10n.tr("Vector", "event_formatter_message_edited_mention") } diff --git a/Riot/Utils/EventFormatter.h b/Riot/Utils/EventFormatter.h index 1721750798..4952f14412 100644 --- a/Riot/Utils/EventFormatter.h +++ b/Riot/Utils/EventFormatter.h @@ -37,7 +37,7 @@ FOUNDATION_EXPORT NSString *const EventFormatterEditedEventLinkAction; @interface EventFormatter : MXKEventFormatter /** - Add a "(Edited)" mention to edited message. + Add a "(edited)" mention to edited message. Default is YES. */ @property (nonatomic) BOOL showEditionMention; From 5720bff8ff81bb113a191401fc44bec7da28bbf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 12 Jul 2019 07:57:04 +0000 Subject: [PATCH 258/266] Translated using Weblate (French) Currently translated at 100.0% (716 of 716 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index c63e348ada..0e15b14cd1 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -785,3 +785,7 @@ "room_event_action_reaction_show_less" = "Réduire"; "close" = "Fermer"; "room_message_edits_history_title" = "Éditions de message"; +"room_action_send_file" = "Envoyer un fichier"; +// MARK: File upload +"file_upload_error_title" = "Envoi de fichier"; +"file_upload_error_unsupported_file_type_message" = "Type de fichier non pris en charge."; From 0171cc472d337843dde3a02b4c5e4cb0ea16d83b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20C?= Date: Fri, 12 Jul 2019 13:15:39 +0000 Subject: [PATCH 259/266] Translated using Weblate (French) Currently translated at 100.0% (716 of 716 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/fr/ --- Riot/Assets/fr.lproj/Vector.strings | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Assets/fr.lproj/Vector.strings b/Riot/Assets/fr.lproj/Vector.strings index 0e15b14cd1..ec1d923b5d 100644 --- a/Riot/Assets/fr.lproj/Vector.strings +++ b/Riot/Assets/fr.lproj/Vector.strings @@ -776,7 +776,7 @@ "device_verification_emoji_headphones" = "Écouteurs"; "device_verification_emoji_folder" = "Dossier"; "device_verification_emoji_pin" = "Épingle"; -"event_formatter_message_edited_mention" = "(Édité)"; +"event_formatter_message_edited_mention" = "(édité)"; // Widget "widget_no_integrations_server_configured" = "Aucun serveur d’intégrations n’est configuré"; "widget_integrations_server_failed_to_connect" = "Échec de connexion au serveur d’intégrations"; From babfef0acfa7811e9b4be4670036a6160e9cc2c9 Mon Sep 17 00:00:00 2001 From: Szimszon Date: Sat, 13 Jul 2019 07:18:44 +0000 Subject: [PATCH 260/266] Translated using Weblate (Hungarian) Currently translated at 100.0% (716 of 716 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/hu/ --- Riot/Assets/hu.lproj/Vector.strings | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/hu.lproj/Vector.strings b/Riot/Assets/hu.lproj/Vector.strings index be799172c2..668b332b20 100644 --- a/Riot/Assets/hu.lproj/Vector.strings +++ b/Riot/Assets/hu.lproj/Vector.strings @@ -781,7 +781,7 @@ "device_verification_emoji_headphones" = "Fejhallgató"; "device_verification_emoji_folder" = "Mappa"; "device_verification_emoji_pin" = "Rajszeg"; -"event_formatter_message_edited_mention" = "(Szerkesztve)"; +"event_formatter_message_edited_mention" = "(szerkesztve)"; // Widget "widget_no_integrations_server_configured" = "Integrációs szerver nincs beállítva"; "widget_integrations_server_failed_to_connect" = "Az integrációs szerverhez nem lehet csatlakozni"; @@ -790,3 +790,7 @@ "room_event_action_reaction_show_all" = "Mindet mutat"; "room_event_action_reaction_show_less" = "Kevesebbet mutat"; "room_message_edits_history_title" = "Üzenet szerkesztések"; +"room_action_send_file" = "Fájl küldés"; +// MARK: File upload +"file_upload_error_title" = "Fájl feltöltés"; +"file_upload_error_unsupported_file_type_message" = "A fájl típusa nem támogatott."; From 42e5999bdd6d8c4b37af1db64049f12bf2380081 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 15 Jul 2019 10:11:27 +0200 Subject: [PATCH 261/266] Tools: Create filterCryptoLogs.sh to filter logs related to e2ee from Riot logs --- CHANGES.rst | 1 + Tools/Logs/filterCryptoLogs.sh | 59 ++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100755 Tools/Logs/filterCryptoLogs.sh diff --git a/CHANGES.rst b/CHANGES.rst index 57dcf2d705..11148056bd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -20,6 +20,7 @@ Improvements: * Message Editing: Handle reply edition (#2492). * RoomVC: Add ability to upload a file that comes from outside the app’s sandbox (#2019). * Share extension: Enable any file upload (max 5). + * Tools: Create filterCryptoLogs.sh to filter logs related to e2ee from Riot logs. Bug fix: * Device Verification: Fix user display name and device id colors in dark theme diff --git a/Tools/Logs/filterCryptoLogs.sh b/Tools/Logs/filterCryptoLogs.sh new file mode 100755 index 0000000000..72363f7835 --- /dev/null +++ b/Tools/Logs/filterCryptoLogs.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +# Filter Riot logs to extract only logs related to end-to-end encryption. +# The output is colorised according to the cryto sub module. +# +# Usage: +# ./filterCryptoLogs.sh console.log + +FILES=$1 + +if [ ! -n "$FILES" ]; then + FILES="*" +fi + +grep -iE 'crypto|MXDevice|olm|error|MXKey|KeyRequest' $FILES \ + | grep -viE 'MXJSONModels|MXOlmSessionResult|MXRealmCryptoStore|NSCocoaErrorDomain|olm_keys_not_sent_error' \ + | awk '{ + # Errors in red (I failed to make a gsub case insensitive) + gsub(".*error.*", "\033[0;31m&\033[0m"); + gsub(".*Error.*", "\033[0;31m&\033[0m"); + gsub(".*ERROR.*", "\033[0;31m&\033[0m"); + + # Isolate each encryption of a message + gsub(".*\\[MXRoom] sendEventOfType\\(MXCrypto\\)\\: Encrypting event.*", + "\n\n\n\n++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n&"); + gsub(".*\\[MXRoom] sendEventOfType\\(MXCrypto\\)\\: Send event.*", + "&\n----------------------------------------------------------------------------------------------------------------\n\n\n\n"); + gsub(".*\\[MXRoom] sendEventOfType\\(MXCrypto\\)\\: Cannot encrypt.*", + "&\n----------------------------------------------------------------------------------------------------------------\n\n\n\n"); + + gsub("\\[MXCrypto\\]", "\033[0;32m&\033[0m"); + gsub("\\[MXOlmDevice\\]", "\033[0;33m&\033[0m"); + + gsub("\\[MXMegolmEncryption\\]", "\033[0;36m&\033[0m"); + + gsub("\\[MXOlmInboundGroupSession\\]", "\033[1;34m&\033[0m"); + gsub("\\[MXMegolmDecryption\\]", "\033[0;34m&\033[0m"); + + gsub("\\[MXOlmDecryption\\]", "\033[0;34m&\033[0m"); + + gsub("\\[MXDeviceList\\]", "\033[0;36m&\033[0m"); + gsub("\\[MXDeviceListOperationsPool\\]", "\033[1;36m&\033[0m"); + + gsub("\\[MXKey\\]", "\033[1;35m&\033[0m"); + gsub("\\[MXKeyBackup\\]", "\033[1;35m&\033[0m"); + gsub("\\[MXKeyBackupPassword\\]", "\033[1;35m&\033[0m"); + gsub("\\[MXMegolmExportEncryption\\]", "\033[1;35m&\033[0m"); + gsub("\\[MXKeyBackupPassword\\]", "\033[1;35m&\033[0m"); + + gsub("\\[MXOutgoingRoomKeyRequestManager\\]", "\033[1;35m&\033[0m"); + gsub("\\[MXIncomingRoomKeyRequestManager\\]", "\033[1;34m&\033[0m"); + + gsub("\\[MXDeviceVerificationTransaction\\]", "\033[0;36m&\033[0m"); + gsub("\\[MXKeyVerification\\]", "\033[0;36m&\033[0m"); + + gsub("\\[MXRealmCryptoStore\\]", "\033[0;37m&\033[0m"); + + print + }' From 2bbc4b43654528bb11c932d78aa9197e47099385 Mon Sep 17 00:00:00 2001 From: Osoitz Date: Sun, 14 Jul 2019 16:57:12 +0000 Subject: [PATCH 262/266] Translated using Weblate (Basque) Currently translated at 100.0% (716 of 716 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/eu/ --- Riot/Assets/eu.lproj/Vector.strings | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/eu.lproj/Vector.strings b/Riot/Assets/eu.lproj/Vector.strings index aa69a920ce..a605cebfbd 100644 --- a/Riot/Assets/eu.lproj/Vector.strings +++ b/Riot/Assets/eu.lproj/Vector.strings @@ -735,7 +735,7 @@ "device_verification_emoji_thumbs up" = "Ederto"; "device_verification_emoji_umbrella" = "Aterkia"; "settings_labs_message_reaction" = "Erreakzionatu mezuei emojiekin"; -"event_formatter_message_edited_mention" = "(Editatua)"; +"event_formatter_message_edited_mention" = "(editatua)"; "device_verification_security_advice" = "Segurtasun gehiagorako, hau aurrez aurre edo bestelako komunikazio bide fidagarriak erabiliz egitea aholkatzen dizugu"; "device_verification_cancelled" = "Beste aldeak egiaztaketa ezeztatu du."; "device_verification_cancelled_by_me" = "Egiaztaketa ezeztatu da. Arrazoia: %@"; @@ -773,3 +773,7 @@ "room_event_action_reaction_show_all" = "Erakutsi denak"; "room_event_action_reaction_show_less" = "Erakutsi gutxiago"; "room_message_edits_history_title" = "Mezuaren edizioak"; +"room_action_send_file" = "Bidali fitxategia"; +// MARK: File upload +"file_upload_error_title" = "Fitxategi igoera"; +"file_upload_error_unsupported_file_type_message" = "Fitxategi mota ez onartua."; From 277f40fa1a9118bf331fc22f94c77f3ace743fc0 Mon Sep 17 00:00:00 2001 From: Slavi Pantaleev Date: Mon, 15 Jul 2019 04:43:05 +0000 Subject: [PATCH 263/266] Translated using Weblate (Bulgarian) Currently translated at 100.0% (716 of 716 strings) Translation: Riot iOS/Riot iOS Translate-URL: https://translate.riot.im/projects/riot-ios/riot-ios/bg/ --- Riot/Assets/bg.lproj/Vector.strings | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Riot/Assets/bg.lproj/Vector.strings b/Riot/Assets/bg.lproj/Vector.strings index 404ba0d083..aa681d10b5 100644 --- a/Riot/Assets/bg.lproj/Vector.strings +++ b/Riot/Assets/bg.lproj/Vector.strings @@ -775,8 +775,16 @@ "device_verification_emoji_headphones" = "Слушалки"; "device_verification_emoji_folder" = "Папка"; "device_verification_emoji_pin" = "Карфица"; -"event_formatter_message_edited_mention" = "(Редактирано)"; +"event_formatter_message_edited_mention" = "(редактирано)"; // Widget "widget_no_integrations_server_configured" = "Не е конфигуриран сървър за интеграции"; "widget_integrations_server_failed_to_connect" = "Неуспешна връзка със сървъра за интеграции"; "device_verification_emoji_lock" = "Катинар"; +"close" = "Затвори"; +"room_event_action_reaction_show_all" = "Покажи всички"; +"room_event_action_reaction_show_less" = "Покажи по-малко"; +"room_action_send_file" = "Изпрати файл"; +"room_message_edits_history_title" = "Редакции на съобщението"; +// MARK: File upload +"file_upload_error_title" = "Качване на файл"; +"file_upload_error_unsupported_file_type_message" = "Типът файл не се поддържа."; From fe4611d9a5d7b95e80f59debc9205ff9ed9f67c1 Mon Sep 17 00:00:00 2001 From: manuroe Date: Mon, 15 Jul 2019 14:06:12 +0200 Subject: [PATCH 264/266] =?UTF-8?q?Device=20Verification:=20Name=20for=20?= =?UTF-8?q?=E2=8F=B0=20is=20"Clock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit according to https://github.com/matrix-org/matrix-doc/blob/master/data-definitions/sas-emoji.json#L41 --- CHANGES.rst | 1 + Riot/Assets/en.lproj/Vector.strings | 2 +- Riot/Generated/Strings.swift | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 11148056bd..d4530ba87d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -25,6 +25,7 @@ Improvements: Bug fix: * Device Verification: Fix user display name and device id colors in dark theme * Device Verification: Name for 🔒 is "Lock" (#2526). + * Device Verification: Name for ⏰ is "Clock. * Registration with an email is broken (#2417). * Reactions: Bad position (#2462). * Reactions: It lets you react to join/leave events (#2476). diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index 0bfac7c339..f13c54309d 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -875,7 +875,7 @@ "device_verification_emoji_thumbs up" = "Thumbs up"; "device_verification_emoji_umbrella" = "Umbrella"; "device_verification_emoji_hourglass" = "Hourglass"; -"device_verification_emoji_clock" = "Class"; +"device_verification_emoji_clock" = "Clock"; "device_verification_emoji_gift" = "Gift"; "device_verification_emoji_light bulb" = "Light Bulb"; "device_verification_emoji_book" = "Book"; diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 02182943f7..0738f36855 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -486,7 +486,7 @@ internal enum VectorL10n { internal static var deviceVerificationEmojiCat: String { return VectorL10n.tr("Vector", "device_verification_emoji_cat") } - /// Class + /// Clock internal static var deviceVerificationEmojiClock: String { return VectorL10n.tr("Vector", "device_verification_emoji_clock") } From 8ecf5e73ccbc426fb9b6949de8721e6312c2d784 Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 16 Jul 2019 14:42:25 +0200 Subject: [PATCH 265/266] Push: Update code to follow API break #2348 --- Riot/AppDelegate.m | 2 +- Riot/Modules/Settings/SettingsViewController.m | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 276c6657de..db380b2ec9 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -2742,7 +2742,7 @@ - (void)initMatrixSessions if (isPushRegistered) { // Enable push notifications by default on new added account - account.enablePushKitNotifications = YES; + [account enablePushKitNotifications:YES success:nil failure:nil]; } else { diff --git a/Riot/Modules/Settings/SettingsViewController.m b/Riot/Modules/Settings/SettingsViewController.m index 4cccbedf25..b8e545a09c 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -2874,7 +2874,11 @@ - (void)togglePushNotifications:(id)sender if (accountManager.pushDeviceToken) { - [account setEnablePushKitNotifications:!account.isPushKitNotificationActive]; + [account enablePushKitNotifications:!account.isPushKitNotificationActive success:^{ + [self stopActivityIndicator]; + } failure:^(NSError *error) { + [self stopActivityIndicator]; + }]; } else { @@ -2887,7 +2891,11 @@ - (void)togglePushNotifications:(id)sender } else { - [account setEnablePushKitNotifications:YES]; + [account enablePushKitNotifications:YES success:^{ + [self stopActivityIndicator]; + } failure:^(NSError *error) { + [self stopActivityIndicator]; + }]; } }]; } From d9811f9ff41878e4342eaa98bee6d293cb360bcd Mon Sep 17 00:00:00 2001 From: manuroe Date: Tue, 16 Jul 2019 18:48:41 +0200 Subject: [PATCH 266/266] version++ --- CHANGES.rst | 4 +- Podfile | 2 +- Podfile.lock | 50 ++++++++++++------- Riot/SupportingFiles/Info.plist | 4 +- RiotShareExtension/SupportingFiles/Info.plist | 4 +- SiriIntents/Info.plist | 4 +- 6 files changed, 43 insertions(+), 25 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index d4530ba87d..c08d93e5ed 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,7 +1,9 @@ -Changes in 0.8.7 (2019-xx-xx) +Changes in 0.9.0 (2019-07-16) =============================================== Improvements: + * Upgrade MatrixKit version ([v0.10.1](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.10.1)). + * Upgrade MatrixKit version ([v0.10.0](https://github.com/matrix-org/matrix-ios-kit/releases/tag/v0.10.0)). * RoomVC: When replying, use a "Reply" button instead of "Send". * RoomVC: New message actions (#2394). * Room upgrade: Autojoin the upgraded room when the user taps on the tombstone banner (#2486). diff --git a/Podfile b/Podfile index 652529affa..c75eeeea71 100644 --- a/Podfile +++ b/Podfile @@ -7,7 +7,7 @@ use_frameworks! # Different flavours of pods to MatrixKit # The current MatrixKit pod version -$matrixKitVersion = '0.9.9' +$matrixKitVersion = '0.10.1' # The develop branch version #$matrixKitVersion = 'develop' diff --git a/Podfile.lock b/Podfile.lock index 1b8bd8dfce..664d1aadf5 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -49,38 +49,41 @@ PODS: - MatomoTracker (6.0.1): - MatomoTracker/Core (= 6.0.1) - MatomoTracker/Core (6.0.1) - - MatrixKit (0.9.9): + - MatrixKit (0.10.1): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixKit/Core (= 0.9.9) - - MatrixSDK (= 0.12.5) - - MatrixKit/AppExtension (0.9.9): + - MatrixKit/Core (= 0.10.1) + - MatrixSDK (= 0.13.0) + - SwiftUTI (~> 1.0.6) + - MatrixKit/AppExtension (0.10.1): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) - DTCoreText/Extension - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.12.5) - - MatrixKit/Core (0.9.9): + - MatrixSDK (= 0.13.0) + - SwiftUTI (~> 1.0.6) + - MatrixKit/Core (0.10.1): - cmark (~> 0.24.1) - DTCoreText (~> 1.6.21) - HPGrowingTextView (~> 1.1) - libPhoneNumber-iOS (~> 0.9.13) - - MatrixSDK (= 0.12.5) - - MatrixSDK (0.12.5): - - MatrixSDK/Core (= 0.12.5) - - MatrixSDK/Core (0.12.5): + - MatrixSDK (= 0.13.0) + - SwiftUTI (~> 1.0.6) + - MatrixSDK (0.13.0): + - MatrixSDK/Core (= 0.13.0) + - MatrixSDK/Core (0.13.0): - AFNetworking (~> 3.2.0) - GZIP (~> 1.2.2) - libbase58 (~> 0.1.4) - OLMKit (~> 3.1.0) - Realm (~> 3.13.1) - - MatrixSDK/JingleCallStack (0.12.5): + - MatrixSDK/JingleCallStack (0.13.0): - JitsiMeetSDK (~> 2.1.0) - MatrixSDK/Core - - MatrixSDK/SwiftSupport (0.12.5): + - MatrixSDK/SwiftSupport (0.13.0): - MatrixSDK/Core - OLMKit (3.1.0): - OLMKit/olmc (= 3.1.0) @@ -97,6 +100,7 @@ PODS: - Reusable/View (4.1.0) - SwiftGen (6.1.0) - SwiftLint (0.33.0) + - SwiftUTI (1.0.7) - zxcvbn-ios (1.0.4) DEPENDENCIES: @@ -105,14 +109,15 @@ DEPENDENCIES: - DTCoreText - GBDeviceInfo (~> 5.2.0) - MatomoTracker (~> 6.0.1) - - MatrixKit (= 0.9.9) - - MatrixKit/AppExtension (= 0.9.9) + - MatrixKit (= 0.10.1) + - MatrixKit/AppExtension (= 0.10.1) - MatrixSDK/JingleCallStack - MatrixSDK/SwiftSupport - OLMKit - Reusable (~> 4.1) - SwiftGen (~> 6.1) - SwiftLint (~> 0.33.0) + - SwiftUTI (from `https://github.com/speramusinc/SwiftUTI.git`, branch `master`) - zxcvbn-ios SPEC REPOS: @@ -138,6 +143,16 @@ SPEC REPOS: - SwiftLint - zxcvbn-ios +EXTERNAL SOURCES: + SwiftUTI: + :branch: master + :git: https://github.com/speramusinc/SwiftUTI.git + +CHECKOUT OPTIONS: + SwiftUTI: + :commit: c21237f13e9fb31a07f3fcd5243c5cf79d75901c + :git: https://github.com/speramusinc/SwiftUTI.git + SPEC CHECKSUMS: AFNetworking: b6f891fdfaed196b46c7a83cf209e09697b94057 cmark: 1d9ad0375e3b9fa281732e992467903606015520 @@ -151,15 +166,16 @@ SPEC CHECKSUMS: libbase58: 7c040313537b8c44b6e2d15586af8e21f7354efd libPhoneNumber-iOS: 0a32a9525cf8744fe02c5206eb30d571e38f7d75 MatomoTracker: 3ae4f65a1f5ace8043bda7244888fee28a734de5 - MatrixKit: 6f553797e1ad42794b5336afb5cecb975ec69daa - MatrixSDK: ed0d0cee4877955052f19730bb3ee727e01ec948 + MatrixKit: f8224de32ca8b6e4c54a2654369cedec7744dc6d + MatrixSDK: 6886e7234c650408db5876b44a7f7608c865ce30 OLMKit: 4ee0159d63feeb86d836fdcfefe418e163511639 Realm: 50071da38fe079e0735e47c9f2eae738c68c5996 Reusable: 82be188f29d96dc5eff0db7b2393bcc08d2cdd5b SwiftGen: f872ca75cbd17bf7103c17f13dcfa0d9a15667b0 SwiftLint: fed9c66336e41fc74dc48a73678380718f0c8b0e + SwiftUTI: 917993c124f8eac25e88ced0202fc58d7eb50fa8 zxcvbn-ios: fef98b7c80f1512ff0eec47ac1fa399fc00f7e3c -PODFILE CHECKSUM: 072b76692c9c8cb162ce0267ad1bf0f179dd4d0d +PODFILE CHECKSUM: 6b3ff49b9c446763a5629e71bdde3fe8da7ba93f COCOAPODS: 1.7.2 diff --git a/Riot/SupportingFiles/Info.plist b/Riot/SupportingFiles/Info.plist index bc44ef80da..b69ed44c82 100644 --- a/Riot/SupportingFiles/Info.plist +++ b/Riot/SupportingFiles/Info.plist @@ -17,11 +17,11 @@ CFBundlePackageType APPL CFBundleShortVersionString - 0.8.7 + 0.9.0 CFBundleSignature ???? CFBundleVersion - 0.8.7 + 0.9.0 ITSAppUsesNonExemptEncryption ITSEncryptionExportComplianceCode diff --git a/RiotShareExtension/SupportingFiles/Info.plist b/RiotShareExtension/SupportingFiles/Info.plist index a1c29526de..981a683662 100644 --- a/RiotShareExtension/SupportingFiles/Info.plist +++ b/RiotShareExtension/SupportingFiles/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.8.7 + 0.9.0 CFBundleVersion - 0.8.7 + 0.9.0 NSExtension NSExtensionAttributes diff --git a/SiriIntents/Info.plist b/SiriIntents/Info.plist index 9eb31162f8..b31f06aa7b 100644 --- a/SiriIntents/Info.plist +++ b/SiriIntents/Info.plist @@ -17,9 +17,9 @@ CFBundlePackageType XPC! CFBundleShortVersionString - 0.8.7 + 0.9.0 CFBundleVersion - 0.8.7 + 0.9.0 NSExtension NSExtensionAttributes