From 16687b865f0fa7000cd5a68e8eccb4b42e0b152f 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 49440b8da..97ec2831e 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -206,9 +206,6 @@ // Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change. id kThemeServiceDidChangeThemeNotificationObserver; - // Tell whether the input text field is in send reply mode. If true typed message will be sent to highlighted event. - BOOL isInReplyMode; - // Listener for `m.room.tombstone` event type id tombstoneEventNotificationsListener; @@ -1098,7 +1095,7 @@ - (void)sendTextMessage:(NSString*)msgTxt { - if (isInReplyMode && customizedRoomDataSource.selectedEventId) + if (self.inputToolBarSendMode == RoomInputToolbarViewSendModeReply && customizedRoomDataSource.selectedEventId) { [self.roomDataSource sendReplyToEventWithId:customizedRoomDataSource.selectedEventId withTextMessage:msgTxt success:nil failure:^(NSError *error) { // Just log the error. The message will be displayed in red in the room history @@ -1440,17 +1437,27 @@ } } -- (void)enableReplyMode:(BOOL)enable +- (void)setInputToolBarSendMode:(RoomInputToolbarViewSendMode)sendMode { - isInReplyMode = enable; - if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:[RoomInputToolbarView class]]) { RoomInputToolbarView *roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView; - roomInputToolbarView.replyToEnabled = enable; + roomInputToolbarView.sendMode = sendMode; } } +- (RoomInputToolbarViewSendMode)inputToolBarSendMode +{ + RoomInputToolbarViewSendMode sendMode = RoomInputToolbarViewSendModeSend; + if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:[RoomInputToolbarView class]]) + { + RoomInputToolbarView *roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView; + sendMode = roomInputToolbarView.sendMode; + } + + return sendMode; +} + - (void)onSwipeGesture:(UISwipeGestureRecognizer*)swipeGestureRecognizer { UIView *view = swipeGestureRecognizer.view; @@ -2871,16 +2878,16 @@ - (void)selectEventWithId:(NSString*)eventId { - BOOL shouldEnableReplyMode = [self.roomDataSource canReplyToEventWithId:eventId];; - - [self enableReplyMode:shouldEnableReplyMode]; - + BOOL shouldEnableReplyMode = [self.roomDataSource canReplyToEventWithId:eventId]; + + [self setInputToolBarSendMode: shouldEnableReplyMode ? RoomInputToolbarViewSendModeReply : RoomInputToolbarViewSendModeSend]; + customizedRoomDataSource.selectedEventId = eventId; } - (void)cancelEventSelection { - [self enableReplyMode:NO]; + [self setInputToolBarSendMode:RoomInputToolbarViewSendModeSend]; if (currentAlert) { diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h index 20c66fcaf..429727a96 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h @@ -18,6 +18,16 @@ #import "MediaPickerViewController.h" +/** + Destination of the message in the composer + */ +typedef enum : NSUInteger +{ + RoomInputToolbarViewSendModeSend, + RoomInputToolbarViewSendModeReply, +} RoomInputToolbarViewSendMode; + + @protocol RoomInputToolbarViewDelegate /** @@ -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 0b483bd5c..5ddca56b2 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -69,6 +69,7 @@ [super awakeFromNib]; _supportCallOption = YES; + _sendMode = RoomInputToolbarViewSendModeSend; self.rightInputToolbarButton.hidden = YES; @@ -154,10 +155,10 @@ self.placeholder = placeholder; } -- (void)setReplyToEnabled:(BOOL)isReplyToEnabled +- (void)setSendMode:(RoomInputToolbarViewSendMode)sendMode { - _replyToEnabled = isReplyToEnabled; - + _sendMode = sendMode; + [self updatePlaceholder]; } @@ -172,17 +173,44 @@ 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 948946727d01fd28c93b0211087722ff73d7f2c5 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 d8128c045..4e072709e 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 adc590650..9e580c24f 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 1239b656b..5932c9364 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 5ddca56b2..66c182cc5 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -160,6 +160,26 @@ _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 49c946adade65ac5d0a7aa4ea31dff3a9d248aaa 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 5162457f2..9063887dc 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 2e025b1ac..2560f91e8 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -137,6 +137,11 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT } - (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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT } } - // Add the edit button - [self addEditButtonForComponent:componentIndex completion:nil]; + if (showEditButton) + { + // Add the edit button + [self addEditButtonForComponent:componentIndex completion:nil]; + } } } From 5a5b80bac1cc5a9a74b1cbe4037d1f433d07276d 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 65b7ffdd3..bc8c22bdd 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -2555,8 +2555,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // 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 c86a05f97c2851691ec06b59e6e3d13128f856e7 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 ede69c765..680c8399d 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 a1123eb96329b67d5afaf6192b1dcd646976b3d9 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 d81fc329d..2f756c814 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 65fc01fdac65bd3fbdc3146e387ff12e639389f4 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 10883cd3c..ecca3ab6c 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 0117f6953925e550c85430309e02a67482473a90 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 62fa60059..046971b31 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 f633fabf0bbf12d0a4d830695900dff17d4d875b 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 04ea808f3..52921d82d 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 0a7699cbb1b0069976f95ca6c5c5a7fc43506db0 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 c8a433c96..9239d91ec 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 052887a867c574a411b05dfe1deba1d524db4fed 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 3ca46524d..daa2aefdd 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 81140f92e30de6e681f3e4aa375d640cfc026d39 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 9bb795aac..5d99aa97c 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 b83a167a9d7cb461d88f41e16523287f9b2a91cf 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 aed22a6bf..c4c986498 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 9311726d0ffabe8cec2cf544893a799ac07b6b14 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 72b237636..1c6cbc40e 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 28e8c54a341850807de52940d98c80e245352155 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 6ae6798fd..d3b8ffd71 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 3b5c057e0d5344cd9dbf4cb530061c9b3f170db3 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 249422b2d..0db8211c4 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 fd23b8d3b179d45c4994f16a64b5072074730a70 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 e23b35b4b..c5c5d48e9 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 11cf7979f5ea26299f42e669ad76a29098123f65 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 08f2bccf2..50fe5f59a 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 000000000..39bfd2147 --- /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 000000000..12a54ae3b --- /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 000000000..391756a72 --- /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 000000000..a35cf55e3 --- /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 000000000..819ce6ec0 --- /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 000000000..bbdfbf5be --- /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 7929b57bd0b313a4189053de61fb435ee781d33f 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 9e580c24f..369c820ae 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 5932c9364..de76c9b76 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 391756a72..879847c82 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 a35cf55e3..08c030159 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib @@ -10,7 +10,14 @@ - + + + + + + + + From 1323c618ac09784bd677c034e7a025b2c183f9f9 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 000000000..da4a164c9 --- /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 000000000..a5230d6fe --- /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 000000000..1290bc2c4 --- /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 000000000..aba738f89 --- /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 000000000..caedf0179 --- /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 486035dc0..5007a2609 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 cc945f39d1dc1598b33da04bc7e8e75a688e0dcc 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 9e580c24f..75c74b9df 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 5932c9364..59c64bda3 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 8a9f58c0ccc0864a7af4d5e193d0fffc63e2e092 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 9063887dc..dade6d2ee 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 2560f91e8..019756683 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -138,15 +138,18 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT - (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 0a23117963defbb8ba25a134e5e3132706607608 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 3764b24b7..f4f59f1e9 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 8f3dfec2d1637fc88b62a4c805e2ee3380391dcb 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 000000000..26dd05082 --- /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 000000000..9c6847645 --- /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 000000000..e1ebb800b --- /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 31828b70134b0eee7a6ad6f940aeb6ca367d2b6c 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 dc59bf3a6..922fea039 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 81cbef55f..2b953e464 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -199,7 +199,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; } // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; } // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; { // 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 8e03ebb56d301835c52fc6a78791d98d3e59b244 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 dc695ba7c..cfc62b45d 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 49d189283..c7f2e74aa 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -58,6 +58,8 @@ 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 @@ 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 @@ { 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 367026794776552b18cb546d7b00a6d1b852fea6 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 000000000..f4898fca0 --- /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 a75d6eb942202eee64e1d8e6d44a5202c69d324d 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 000000000..a76537140 --- /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 000000000..9c2ce9ac5 --- /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 000000000..ba32691f3 --- /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 000000000..c4c389372 --- /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 000000000..42b2b9ab3 --- /dev/null +++ b/Riot/Modules/Room/ContextualMenu/RoomContextualMenuToolbarView.xib @@ -0,0 +1,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From b05f7447daf80ee2a34db17554603cad804098d9 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 5db46ea18..b2aa4fda8 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 000000000..a06eecc81 --- /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 000000000..7492a1e5e --- /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 e12c5f64b01a627aee3ec5997cb50a992f37409e 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 000000000..b066de8eb --- /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 06fe9bf8ae0e3e0fb6fa719a5fc78442c72f9158 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 97ec2831e..7f4ad5a59 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 @@ MXServerNotices *serverNotices; } +@property (nonatomic, weak) IBOutlet UIView *overlayContainerView; + +@property (nonatomic, strong) RoomContextualMenuPresenter *roomContextualMenuPresenter; + @end @implementation RoomViewController @@ -404,6 +408,8 @@ [self refreshRoomInputToolbar]; } + self.roomContextualMenuPresenter = [RoomContextualMenuPresenter new]; + // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { @@ -589,6 +595,9 @@ { [super viewDidDisappear:animated]; + // Hide contextual menu if needed + [self hideContextualMenuAnimated:NO]; + // Reset visible room id [AppDelegate theDelegate].visibleRoomId = nil; @@ -936,6 +945,8 @@ - (void)updateRoomInputToolbarViewClassIfNeeded { Class roomInputToolbarViewClass = RoomInputToolbarView.class; + + BOOL shouldDismissContextualMenu = NO; // Check the user has enough power to post message if (self.roomDataSource.roomState) @@ -950,10 +961,12 @@ if (isRoomObsolete || isResourceLimitExceeded) { roomInputToolbarViewClass = nil; + shouldDismissContextualMenu = YES; } else if (!canSend) { roomInputToolbarViewClass = DisabledRoomInputToolbarView.class; + shouldDismissContextualMenu = YES; } } @@ -961,6 +974,12 @@ if (self.isRoomPreview) { roomInputToolbarViewClass = nil; + shouldDismissContextualMenu = YES; + } + + if (shouldDismissContextualMenu) + { + [self hideContextualMenuAnimated:NO]; } // Change inputToolbarView class only if given class is different from current one @@ -978,7 +997,7 @@ if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { - height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant; + height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarHeightConstraint.constant; } else if ([self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class]) { @@ -1485,6 +1504,14 @@ [UIView setAnimationsEnabled:YES]; } +- (void)handleLongPressFromCell:(id)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 @@ 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 @@ [self selectEventWithId:tappedEvent.eventId]; } } - - // Force table refresh - [self dataSource:self.roomDataSource didCellChange:nil]; } else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnOverlayContainer]) { @@ -2073,9 +2100,6 @@ // 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 @@ [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 @@ }]]; } } - - if (level == 0) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_copy", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - [[UIPasteboard generalPasteboard] setString:selectedComponent.textMessage]; - } - - }]]; - } if (level == 0) { @@ -2323,42 +2334,6 @@ }]]; } - if (attachment.type != MXKAttachmentTypeSticker) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_copy", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - [self startActivityIndicator]; - - [attachment copy:^{ - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - } failure:^(NSError *error) { - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - //Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; - - }]; - - // Start animation in case of download during attachment preparing - [roomBubbleTableViewCell startProgressUI]; - } - - }]]; - } - // Check status of the selected event if (selectedEvent.sentState == MXEventSentStatePreparing || selectedEvent.sentState == MXEventSentStateEncrypting || @@ -2733,7 +2708,7 @@ if (weakSelf) { typeof(self) self = weakSelf; - [self cancelEventSelection]; + [self hideContextualMenuAnimated:YES]; } }]]; @@ -2843,10 +2818,8 @@ else if (url && urlItemInteractionValue) { // Fallback case for external links - - // TODO: Use UITextItemInteraction enum when minimum deployement target will be iOS 10 switch (urlItemInteractionValue.integerValue) { - case 0: //UITextItemInteractionInvokeDefaultAction + case UITextItemInteractionInvokeDefaultAction: { [[UIApplication sharedApplication] vc_open:url completionHandler:^(BOOL success) { if (!success) @@ -2857,10 +2830,13 @@ shouldDoAction = NO; } break; - case 1: //UITextItemInteractionPresentActions - // Long press on link, let MXKRoomBubbleTableViewCell UITextView present the default contextual menu. + case UITextItemInteractionPresentActions: + { + // Long press on link, present room contextual menu. + shouldDoAction = NO; + } break; - case 2: //UITextItemInteractionPreview + case UITextItemInteractionPreview: // Force touch on link, let MXKRoomBubbleTableViewCell UITextView use default peek and pop behavior. break; default: @@ -2879,10 +2855,19 @@ - (void)selectEventWithId:(NSString*)eventId { BOOL shouldEnableReplyMode = [self.roomDataSource canReplyToEventWithId:eventId]; + + [self selectEventWithId:eventId enableReplyMode:shouldEnableReplyMode showTimestamp:YES]; +} - [self setInputToolBarSendMode: shouldEnableReplyMode ? RoomInputToolbarViewSendModeReply : RoomInputToolbarViewSendModeSend]; - +- (void)selectEventWithId:(NSString*)eventId enableReplyMode:(BOOL)enableReplyMode showTimestamp:(BOOL)showTimestamp +{ + [self setInputToolBarSendMode: enableReplyMode ? RoomInputToolbarViewSendModeReply : RoomInputToolbarViewSendModeSend]; + + customizedRoomDataSource.showBubbleDateTimeOnSelection = showTimestamp; customizedRoomDataSource.selectedEventId = eventId; + + // Force table refresh + [self dataSource:self.roomDataSource didCellChange:nil]; } - (void)cancelEventSelection @@ -2895,6 +2880,7 @@ currentAlert = nil; } + customizedRoomDataSource.showBubbleDateTimeOnSelection = YES; customizedRoomDataSource.selectedEventId = nil; // Force table refresh @@ -4976,5 +4962,169 @@ } } +#pragma mark - Contextual Menu + +- (NSArray*)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 0a8a14425..6ea9ec67f 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 8e7dd40df7efeeae1e6890c55c1009e86dbb6d83 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 08f2bccf2..0a23f01f5 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 1db09d6febeda6288e29cce23951beea608c011b 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 7f4ad5a59..c81d636d6 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2854,9 +2854,7 @@ - (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 d9360d9530b455457d9fd1054f66c808c2e16142 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 4e072709e..ae4a5aa13 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 e4e7261360cab4cc0972c56dd56d1e618df182e6 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 50fe5f59a..7c626871d 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 375dd3913..c8a10674a 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 1b3651a6e..3c939e2c1 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 d1bcaf9f8..19b1ba109 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 000000000..b68f07e04 --- /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 08c030159..9b81ffaeb 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 000000000..b98ac829a --- /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 819ce6ec0..43b94d0c9 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 bbdfbf5be..81b52fd0d 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 1da2534834de3a1bc07d2a774c2b9e4805f44a6b 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 8bd2e1c78..74c5f7fb5 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 39bfd2147..000000000 --- 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 43b94d0c9..bb8759ca7 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 81b52fd0d..777376a4a 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 c6f782ca0ab490bb1e82ff7812ac46e61d39ab43 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 b1f0e155b..5f067b63d 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 bb8759ca7..6f2d6aff2 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 58ce1f1744a028b2812b72f3dcb55fcd57bf17c1 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 000000000..8d9ff4221 --- /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 b58b475f259621969a5b057668386525759e69b7 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 8d9ff4221..95ce74958 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 0f93c2c9c6c40c42f9a014883fcd211e4d7fd854 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 95ce74958..d394ecf32 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 b3b010fc4ca1288eea7b87f01cc3b6ba42047037 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 d394ecf32..8d9ff4221 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 4a965ebf233c0491194a2f14252ba6dba1137976 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 b68f07e04..ae12573ec 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 e7290d0c2..d4f54bd39 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 6f2d6aff2..ad6ba09fe 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 cc8862b6f19a59dc5657f83d3f1534a429934bde 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 ad6ba09fe..ecfaaaec7 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 a466493f7b5bcde95f11c5227e026536b9f1528e 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 2b953e464..edc154f3f 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; } } +- (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 d22e9beda601155d946ea631b3cdbc85e003915a 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 edc154f3f..df44185b8 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -211,7 +211,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; // Append attributed text [currentAttributedTextMsg appendAttributedString:componentString]; - [self addverticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId]; + [self addVerticalWhitespaceToString:currentAttributedTextMsg forEvent:component.event.eventId]; } } @@ -259,7 +259,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; } // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; } } -- (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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 c7f2e74aa..1dee9902f 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -265,7 +265,7 @@ // 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 @@ if (component.event.sentState != MXEventSentStateFailed) { + MXKReceiptSendersContainer* avatarsContainer; + // Handle read receipts (if any) if (self.showBubbleReceipts && cellData.readReceipts.count && !isCollapsableCellCollapsed) { @@ -307,7 +309,7 @@ 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 @@ [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 2401c7a129aa6b28be569377efaaca5090d14371 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 b066de8eb..2a4c39a53 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 a06eecc81..711ebfcfb 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 7492a1e5e..5a111508d 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 c81d636d6..6278e9523 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5076,6 +5076,18 @@ 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 @@ [self hideContextualMenuAnimated:YES]; } +- (void)roomContextualMenuViewControllerDidReaction:(RoomContextualMenuViewController *)viewController +{ + [self hideContextualMenuAnimated:YES]; +} + @end From 2d6c50831b50f8be120f15283ba31edb7ac56f27 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 caab1e0c2..3bc44f0de 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 3c512ee7e3f16c6b7542ec773b39dccd6b3632b4 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 ecfaaaec7..04d8e0407 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 c2bc0564151a7bf384abadbbcaf2c01b907b4a7e 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 86071d940..0ff5bba82 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 dc413de17bde7f97f2b03bd87150b7a15ae3adb2 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 d1c56e902..7e398492b 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 54e6f19694c8685d4485bc4f39f975959fa1e491 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 7013886db..2679fe7df 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 15d07c025ff5c56b134c27a4aba2f9b1d3b4b39b 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 2679fe7df..99874dc1e 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 9b6d39451..958965cc6 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 80e7136bf..61b4e2fd4 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 4d1e3b3c6..cc23f78f3 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 49275513f640b700f9e23219036c27bc68060055 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 6f580afbc..168193d98 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 71e8d104d645da02a897b983973af5e0256a6b85 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 04d8e0407..93ba3cb41 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 2824372877a15474c484f5c630b4253d809c9a01 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 93ba3cb41..58e05e011 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 2a4c39a53..7bf3be01b 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 5af992a2a44716921cf187d868b82005f3562802 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 58e05e011..593e2f79a 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 617c3f2d603a3870e44fde066b44c05b59e70cb2 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 13db82f5c..31ca8d5bf 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 44c885e30..6bfea7cf9 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 6db8ec7b3..09d5220ff 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 1dee9902f..02858a7aa 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -59,6 +59,7 @@ 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 6278e9523..fa8320696 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5077,7 +5077,7 @@ [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 cbda06f84..b397757e1 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -119,7 +119,8 @@ enum 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 @@ SignOutAlertPresenterDelegate> 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 @@ SignOutAlertPresenterDelegate> } } +- (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 6c405109a745465f47dc1ade72185693ee869c19 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 593e2f79a..93ba3cb41 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 7bf3be01b..2a4c39a53 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 6f85d8934e62dad09170594b3bcf4b1057ccbbd5 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 93ba3cb41..a4158f36f 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 b931a0484e5c54a9532c119586c009b80d013a32 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 a4158f36f..8fbf06106 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 9e6a7599aaec46166f0bc6674ba448dccd560137 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 c8a10674a..c913ffd65 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 3c939e2c1..1033dae9d 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 19b1ba109..87db69334 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 0adcff40fa54bf787c92c070b14b5ed0fc6c9800 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 000000000..10cdbf75c --- /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 58b72c64858c6305fab7c66cee9238beb4aa2153 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 000000000..34561134d --- /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 000000000..35e41eec8 --- /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 000000000..316f9c34b --- /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 e9d44307fcb5bd7169be1a8be06a30151c2b3a7a 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 000000000..339b00987 --- /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 000000000..04a30726a --- /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 3c2d1375c17e14e2e83067ee5a4d2e03506bd6bb 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 000000000..2e61c35e1 --- /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 000000000..7c5cedb2a --- /dev/null +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionsView.xib @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a8ee2e1fdabff06082007fe6ef3554a197b0d853 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 3bc44f0de..b33a4fdc6 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 862a79fab1efbd1ca8c0aa0422745dc0751c4241 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 df44185b8..bd7aef55a 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; { // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; return [[NSAttributedString alloc] initWithString:returnString attributes:@{NSForegroundColorAttributeName : [UIColor blackColor], - NSFontAttributeName: [UIFont systemFontOfSize:4]}]; + NSFontAttributeName: [UIFont systemFontOfSize:6]}]; } - (BOOL)hasSameSenderAsBubbleCellData:(id)bubbleCellData From b878c8fd6640a090d4aab1c4eb979c6d37ab0a9a 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 02858a7aa..29cbe5cd8 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 @@ while (index--) { MXKRoomBubbleComponent *component = bubbleComponents[index]; + NSString *componentEventId = component.event.eventId; if (component.event.sentState != MXEventSentStateFailed) { @@ -379,54 +384,46 @@ [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 @@ 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 @@ 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 069128df4c438c69ca162b73dd6b392d53fd387a 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 565ba5ebb..6a65585fa 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 2743ba45cc0c2e742321cf33cbd584ceb7caebce 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 280ff6c64..59ed9a2fe 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 b33a4fdc6..542e7eb2d 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 6417dde30c1cc5167f0f5721a9f6c2bb07971ea5 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 2e61c35e1..33eaf486c 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 da064aff988e0a2c13ef0964b5cc28d2b47ed813 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 c913ffd65..c8a10674a 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 1033dae9d..c98b27a24 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 87db69334..7f154f232 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 34561134d..3b3d46a03 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 05f31f1099f577b000a54e6bffc6a89ebb776166 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 f2e6a7621..bc44ef80d 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 e71361f1c..4a353b686 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 dfd30fd66..9eb31162f 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 738a43e76ff3d6090fcc677029c8b0e517cbd577 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 2a4c39a53..fa56d2de5 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 f0e47876bb7cb40a2dccdf2fc562773f75a17689 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 922fea039..20e6c8b86 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 bd7aef55a..bd6fad25a 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; // Reset attributedTextMessage to force reset MXKRoomCellData parameters self.attributedTextMessage = nil; + + self.displayTimestampForSelectedComponentOnLeftWhenPossible = YES; } return self; @@ -202,7 +215,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; } // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; } // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; { // 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 fb3094dab1f3a8a2561e76e92afc5cf39babf7cc 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 019756683..444278de6 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -36,8 +36,6 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT - (void)addTimestampLabelForComponent:(NSUInteger)componentIndex { - self.bubbleInfoContainer.hidden = NO; - MXKRoomBubbleComponent *component; NSArray *bubbleComponents = bubbleData.bubbleComponents; @@ -49,87 +47,125 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT if (component && component.date) { - // Check whether this is the first displayed component. BOOL isFirstDisplayedComponent = (componentIndex == 0); + BOOL isLastMessageMostRecentComponent = NO; + + RoomBubbleCellData *roomBubbleCellData; + if ([bubbleData isKindOfClass:RoomBubbleCellData.class]) { - isFirstDisplayedComponent = (componentIndex == ((RoomBubbleCellData*)bubbleData).oldestComponentIndex); + roomBubbleCellData = (RoomBubbleCellData*)bubbleData; + isFirstDisplayedComponent = (componentIndex == roomBubbleCellData.oldestComponentIndex); + isLastMessageMostRecentComponent = roomBubbleCellData.containsLastMessage && (componentIndex == roomBubbleCellData.mostRecentComponentIndex); } - 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)]; + // Display timestamp on the left for selected component when there is enough space + BOOL displayLabelOnLeft = roomBubbleCellData.displayTimestampForSelectedComponentOnLeftWhenPossible && !isFirstDisplayedComponent && !isLastMessageMostRecentComponent; - 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 - { - timeLabel.font = [UIFont systemFontOfSize:12]; - } - timeLabel.adjustsFontSizeToFitWidth = YES; + [self addTimestampLabelForComponent:component + isFirstDisplayedComponent:isFirstDisplayedComponent + viewTag:componentIndex + displayOnLeft:displayLabelOnLeft]; + } +} - timeLabel.tag = componentIndex; +- (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)); - [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: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]]; - - // 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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT - (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 81c424c985dd8e3cbc6e71675ddfcc67e3c38286 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 542e7eb2d..df79a4230 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 000000000..be7e8307e --- /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 4acea5074731042ab8556abd68de5891b7c15525 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 5a111508d..2318a0bb4 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 f2ea321b5144639b063dd40be000022dc3ad15a2 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 fa8320696..d61b97b98 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2013,6 +2013,15 @@ // 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 @@ } 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 @@ else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnEvent]) { MXEvent *tappedEvent = userInfo[kMXKRoomBubbleCellEventKey]; - [self handleLongPressFromCell:cell withTappedEvent:tappedEvent]; + + if (!bubbleData.collapsed) + { + [self handleLongPressFromCell:cell withTappedEvent:tappedEvent]; + } } else { From aa6af3957c564072802a00977a870b49fd89622d 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 7926f11cf..78136db18 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 @@ NSString *const kEventFormatterOnReRequestKeysLinkActionSeparator = @"/"; @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 d098b0a68c87b55fca436ee8cff6ce89afddc53e 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 6a65585fa..e1a3322d7 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 b913fdb093979fd8195bc823723952fd0ff3e9ca 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 d61b97b98..fcf11f81a 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5082,7 +5082,7 @@ 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 a34a8399d12cdbbff43945f198c71f24407ec9ef 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 20e6c8b86..fd62967b4 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 8ac4cfd450e6ce06b85c49e4c55e603cc5376fbd 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 444278de6..dd770de1a 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -59,7 +59,7 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT 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 e4112d60c68a2479bfd362693babdc5490b7ec43 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 dade6d2ee..c6d77e2ae 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 dd770de1a..1a7332b36 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -459,6 +459,80 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT } } +- (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 1e2d7550f7c6ec7cb0e459389e78961118012e14 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 fcf11f81a..f8f726572 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5096,17 +5096,37 @@ 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 c8909bb5744e642373c4953e6bf60b772182358d 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 e1a3322d7..179bc2b65 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 937fb9cec10a3643052dd8510f5efc72cf6291ea 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 79280d25d..5685a9f43 100644 --- a/Riot/Modules/Authentication/Views/AuthInputsView.m +++ b/Riot/Modules/Authentication/Views/AuthInputsView.m @@ -814,8 +814,24 @@ 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 5a4ecde6480d27804a6308ee98ea69731ceecd0a 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 179bc2b65..95f2671af 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 27f6b5dd1e5e3561d71d36dbcb09f220aa4e2b2c 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 1c6cbc40e..238f9724e 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 ece222ae5eac2351de167dbf19b1e42e57f2a29b 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 d9c0dfb36..b48803bd8 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 3ed0bf3d98ae2b7c7bbe2885fd3bbd6746edc1c7 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 a76537140..8a0147e5f 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 9c2ce9ac5..0032099d7 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 c4c389372..8d695c945 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 d3c8ebf59a95258e3a26eeb328b01c51d6e55824 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 95f2671af..1b066f1ce 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 d5128000e7687ce12440bbd75a38d2d2d991959e 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 78136db18..91819e781 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 @@ static NSString *const kEventFormatterTimeFormat = @"hh:mm"; { [super initDateTimeFormatters]; + timeFormatter.locale = [NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]; [timeFormatter setDateFormat:kEventFormatterTimeFormat]; } From 62e675e26cd1d964213659a36e796b717f4784de 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 59ed9a2fe..467fea952 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 952f5eb90..207baf23f 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 df79a4230..39b96c6e5 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 552e9e0ab..09a4108f8 100644 --- a/Riot/Managers/Analytics/Analytics.m +++ b/Riot/Managers/Analytics/Analytics.m @@ -31,7 +31,29 @@ NSString *const kAnalyticsE2eCategory = @"E2E"; 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 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; - (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 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; #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 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; 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 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; { 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 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; { 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 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; { 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 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; { NSString *action = [NSString stringWithFormat:kAnalyticsMetricsActionPattern, kMXAnalyticsStatsCategory]; - [[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory + [[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsMetricsCategory action:action name:kMXAnalyticsStatsRooms number:@(roomCount) @@ -178,7 +195,7 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; { for (NSString *reason in failuresCounts) { - [[PiwikTracker shared] trackWithEventWithCategory:kAnalyticsE2eCategory + [[MatomoTracker shared] trackWithEventWithCategory:kAnalyticsE2eCategory action:kAnalyticsE2eDecryptionFailureAction name:reason number:failuresCounts[reason] From 917b5d42b53972e17612d66bd0ddbfd711469d33 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 d61561705..e204657a9 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 5e7201e1bdf8c16112f2b742c0f167a838307abd 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 09a4108f8..84bc65b5d 100644 --- a/Riot/Managers/Analytics/Analytics.m +++ b/Riot/Managers/Analytics/Analytics.m @@ -36,7 +36,7 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; @interface MatomoTracker (MatomoTrackerMigration) + (MatomoTracker *)shared; -+ (void)migrateFromFourPointFourSharedInstance; +- (void)migrateFromFourPointFourSharedInstance; @end @implementation MatomoTracker (MatomoTrackerMigration) @@ -44,10 +44,11 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; { 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 55c2b799b045eaa0ffe1092654222d26229df685 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 467fea952..e0e6f35f5 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 78639fd327abbc0b4fb31a67ce7e69080f0782f9 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 84bc65b5d..4491c4158 100644 --- a/Riot/Managers/Analytics/Analytics.m +++ b/Riot/Managers/Analytics/Analytics.m @@ -51,7 +51,7 @@ NSString *const kAnalyticsE2eDecryptionFailureAction = @"Decryption failure"; - (void)migrateFromFourPointFourSharedInstance { if ([[NSUserDefaults standardUserDefaults] boolForKey:@"migratedFromFourPointFourSharedInstance"]) return; - + [self copyFromOldSharedInstance]; [[NSUserDefaults standardUserDefaults] setBool:true forKey:@"migratedFromFourPointFourSharedInstance"]; } @end From 684774b5d44dcf6ed918238142d5aa4698d0d25f 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 e0e6f35f5..a3b5996a7 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 207baf23f..c795bbc57 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 b6c3bba499f0be63b07dc04a46c94d3859d0998c 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 c795bbc57..7f3b5e2b7 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 12f782f419d787414d949e2294822818550b45b7 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 562310eb8..c17f172d0 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 441fe7f7b793ccb29c9bc9d2809abe44e0d0bf70 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 f8f726572..8ccb07ecc 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5089,12 +5089,13 @@ 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 @@ } [self.roomContextualMenuPresenter hideContextualMenuWithAnimated:animated completion:^{ - [self contextualMenuAnimationCompletionAfterBeingShown:NO]; + [self enableOverlayContainerUserInteractions:NO]; if (completion) { @@ -5162,11 +5163,11 @@ }]; } -- (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 52d5721a5f6f9d8dd8b7de1951abb8ee4709cf4b 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 1ca6636014b60f79ac85b685799cfaac2132be3d 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 d61561705..7115b80d1 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 3faca2e943dbd84179901812092ee9e06b2fb419 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 00e7f9498..675f3d391 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 e0ef41c7e7c8ccd9e144b9124e9844420c3fe586 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 21e8f42c2..aa5c6d53c 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 ad1e14ea4ddfed41ebbecdf6a0a685f82e70048f 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 9413c4955..fcca04acb 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 b7e0902cfe50877717cfc0079d48c1dc07a66f76 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 49c93d17c..bd8e2cfd6 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 4d42235ecbe4f4d242f8c3fd4f2860dc35eb8a9d 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 8b51db94b..7ad385fcb 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 9abc2d31d24ef9f0722feee0e54db906e4ef1c54 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 08b69bc1a..1b800160b 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 f9d7d45c73f9f2b7e9ccba37a4260c12a25b6940 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 6587a8ce3..d060cfcd8 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 75d7cea16d3f442e18328595367ab8b30a8efc99 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 501141eff..73cdf84ef 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 cf6ef443474a96406b5f206d0da6489c13eeab27 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 54b32d9a0..bcadaeb5f 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 346f46038a6134a3625ef5875a12b0404d124b51 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 52921d82d..f0a36c177 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 543ec400410e1def0b956aa36b0eb8dd4baafe23 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 0db8211c4..4d209376b 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 27f4fee95abc42afcbcbb5ebf05ca06de400b0de 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 df79a4230..f12b1f043 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 000000000..dee83d985 --- /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 6d5798fc04120af8d250b03bda1d4951d38e3433 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 c6d77e2ae..823d31924 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 1a7332b36..6dd46f431 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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT if (componentIndex < bubbleComponents.count) { - component = bubbleComponents[componentIndex]; + component = bubbleComponents[componentIndex]; } if (component && component.date) @@ -62,22 +60,26 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT // 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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT } 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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT 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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT 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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT 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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT } } -- (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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT 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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT // 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 3df39ea7bb41ceffb20425ebb53821a813555840 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 c4c986498..1cf3cde2b 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 78e0da4c8c07201b72d1c640454f83a12d002588 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 0ff5bba82..4df9c322f 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 487b5d3d1bd77ef196e0726be16d6762abc9ebae 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 bd6fad25a..e550c3616 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; if (reactionCount) { + CGFloat bubbleReactionsViewWidth = self.maxTextViewWidth - 4; + CGSize fittingSize = UILayoutFittingCompressedSize; - fittingSize.width = self.maxTextViewWidth; + fittingSize.width = bubbleReactionsViewWidth; static BubbleReactionsView *bubbleReactionsView; @@ -387,12 +379,13 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; // 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 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil; 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 25aec7b8a2e908575fccf751b4afd8b52084485c 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 29cbe5cd8..141802bed 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 @@ static CGFloat kBubbleReactionsViewRightMargin = 15.0; { // 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 @@ static CGFloat kBubbleReactionsViewRightMargin = 15.0; 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 @@ static CGFloat kBubbleReactionsViewRightMargin = 15.0; 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 @@ static CGFloat kBubbleReactionsViewRightMargin = 15.0; 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 @@ static CGFloat kBubbleReactionsViewRightMargin = 15.0; 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 @@ static CGFloat kBubbleReactionsViewRightMargin = 15.0; // 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 bd2a12ac627e5573f78ecd4f965779f048789896 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 f8f726572..048000f75 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5117,7 +5117,7 @@ if (bubbleComponents.count > 0) { NSInteger selectedComponentIndex = foundComponentIndex != NSNotFound ? foundComponentIndex : 0; - bubbleComponentFrame = [roomBubbleTableViewCell componentFrameForIndex:selectedComponentIndex]; + bubbleComponentFrame = [roomBubbleTableViewCell componentFrameInTableViewForIndex:selectedComponentIndex]; } else { From eb691f0d414441e1f9036409f470bb7198c3c662 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 6dd46f431..dc8636d9a 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -58,7 +58,9 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT } // 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 ffb45932ea9bd3ea31a8b0455f8a5f7e3ca44ac8 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 d61561705..fb713e5cd 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 b1c519bf6556ffa80230a3c84b6dd6fd24abd0e4 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 823d31924..9de1b0e88 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 a3220ab851da6710d6e04dd4749b97bcc058e278 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 9de1b0e88..e57ee45c5 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 f9c1f1d6895eb42ab07bdb7045ed48b24ad65a7e 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 dc8636d9a..684f793a4 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -60,7 +60,7 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT // 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 a2794e67a5efd8e146d6a98f32b4d5fe7d66bacb 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 1cf3cde2b..d83b3feb9 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 4494c97458a226c99ae088bb2ec8b8eac4d67643 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 b4ea620de..2b24813f1 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 f2f68768e5e1a70445a57048da471b07e53562c3 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 000000000..8b1378917 --- /dev/null +++ b/Riot/Assets/vls.lproj/Vector.strings @@ -0,0 +1 @@ + From 22d5b6e8ca7525811ccd0e1a4db3ecc830197e41 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 000000000..8b1378917 --- /dev/null +++ b/Riot/Assets/vls.lproj/InfoPlist.strings @@ -0,0 +1 @@ + From 4f455fb8c69799f8a5a51f514f7c9ff5d95064f1 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 000000000..8b1378917 --- /dev/null +++ b/Riot/Assets/vls.lproj/Localizable.strings @@ -0,0 +1 @@ + From a0bd9d916e4eb301f75ffe870a698cbcfbeecefd 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 8b1378917..14a79cad6 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 4de85fc6338e0c10d7358b59b2c4ff1fbb57b42b 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 8b1378917..8771d37a3 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 000ddbb25801534b7de3eb17cffd0cb187415aaf 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 c17f172d0..6dca9c4f5 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 72a5f3bc25dcceb4764a68b18542d4790e01af0b 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 8fbf06106..d204d51e9 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 777376a4a..111c01d03 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 fa56d2de5..5e0fbbc7e 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 2318a0bb4..d4d928400 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 e2c9de99e8bb0b85d7f7b05ad3ffaa46fc0b441e 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 141802bed..0696c8543 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -594,19 +594,19 @@ - (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 048000f75..a9439cea8 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 @@ 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 @@ [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 30cf348c65187d095655d5b347de5e2bf6ba062b 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 048000f75..1565a54bf 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5097,7 +5097,7 @@ [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 9c9a603d7a45a2570e463fa970fe97cb32917daf 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 fb713e5cd..79f7de7f2 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 18b3dfa20def05be865c55811613a563ee30b956 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 7e194a1e8..040de742b 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 14224b0c34de23d7451bc2fe2769042fd58e2866 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 6f2335ecf..7922a0459 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 89ffe6f983e0fd5a3fb7f76958e4b92dcf5f5ee2 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 2bf3f0c7f..da1195712 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 25a3b4b661564468de35a1ce639d69d59cb17c68 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 6786baf50..a17db7e88 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 27f97f00223b0da4c5e341bc0f57cee4aa3cd7ec 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 f6610a7cc..028fbdef0 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 5847410530837816b7255d4fe43f9ec7fae6b35b 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 9239d91ec..2de28aa8e 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 febb410418c8ccf6de63a63054d58bf811f2f4e8 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 a9439cea8..2de9abd39 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -217,6 +217,7 @@ @property (nonatomic, weak) IBOutlet UIView *overlayContainerView; @property (nonatomic, strong) RoomContextualMenuPresenter *roomContextualMenuPresenter; +@property (nonatomic, strong) MXKErrorAlertPresentation *errorPresenter; @end @@ -410,6 +411,7 @@ } self.roomContextualMenuPresenter = [RoomContextualMenuPresenter new]; + self.errorPresenter = [MXKErrorAlertPresentation new]; // Observe user interface theme change. kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) { @@ -5192,19 +5194,27 @@ - (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didAddReaction:(NSString *)reaction forEventId:(NSString *)eventId { + MXWeakify(self); + [self.roomDataSource addReaction:reaction forEventId:eventId success:^(NSString *eventId) { } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil]; }]; } - (void)reactionsMenuViewModel:(ReactionsMenuViewModel *)viewModel didRemoveReaction:(NSString *)reaction forEventId:(NSString *)eventId { + MXWeakify(self); + [self.roomDataSource removeReaction:reaction forEventId:eventId success:^{ } failure:^(NSError *error) { + MXStrongifyAndReturnIfNil(self); + [self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil]; }]; } From a3da7eabd75160aba977d31c35e2e8fc25851bc0 Mon Sep 17 00:00:00 2001 From: SBiOSoftWhare 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 22a7b6bd9..e225169c6 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 563bfc5bd..8e7aa57b3 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 809cf37423fd0a359da99322cc3b53cadcdbfbbc 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 d55c03257..67abcd419 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 91819e781..8b8eeba27 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 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; 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 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; 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 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; 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 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; self.callNoticesTextFont = [UIFont italicSystemFontOfSize:15]; self.encryptedMessagesTextFont = [UIFont italicSystemFontOfSize:15]; self.emojiOnlyTextFont = [UIFont systemFontOfSize:48]; + self.editionMentionTextFont = [UIFont systemFontOfSize:12]; } return self; } From c2e06140212d060003e7adde88969d395a12f585 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 7e696900a..5dd3cbf12 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2824,9 +2824,9 @@ 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 @@ } } } + 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 8fe9d132827839409a2feba040687344526b165e 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 79f7de7f2..d8bdd6649 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 310b39ca1fe67c493bb3e099649d777259393bc4 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 d204d51e9..846ce2049 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 10221beee29f3ef057d2a356bde35d264e025063 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 0696c8543..f569efc26 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -594,7 +594,7 @@ - (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 5dd3cbf12..52361488b 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5209,7 +5209,7 @@ { MXWeakify(self); - [self.roomDataSource addReaction:reaction forEventId:eventId success:^(NSString *eventId) { + [self.roomDataSource addReaction:reaction forEventId:eventId success:^{ } failure:^(NSError *error) { MXStrongifyAndReturnIfNil(self); From 44bc52b9136a7dd5e34cbcd9cfb0b7885da10044 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 429727a96..c548f48cf 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 66c182cc5..2b9ff11e0 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -172,7 +172,9 @@ 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 9619a021f292e9e8c21e7aaf2381399d827da428 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 5dd3cbf12..ec3815f27 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -218,6 +218,7 @@ @property (nonatomic, strong) RoomContextualMenuPresenter *roomContextualMenuPresenter; @property (nonatomic, strong) MXKErrorAlertPresentation *errorPresenter; +@property (nonatomic, strong) NSString *textMessageBeforeEditing; @end @@ -1124,6 +1125,13 @@ NSLog(@"[MXKRoomViewController] sendTextMessage failed."); }]; } + else if (self.inputToolBarSendMode == RoomInputToolbarViewSendModeEdit && customizedRoomDataSource.selectedEventId) + { + [self.roomDataSource replaceTextMessageForEventWithId:customizedRoomDataSource.selectedEventId withTextMessage:msgTxt success:nil failure:^(NSError *error) { + // Just log the error. The message will be displayed in red + NSLog(@"[MXKRoomViewController] sendTextMessage failed."); + }]; + } else { // Let the datasource send it and manage the local echo @@ -2890,12 +2898,12 @@ - (void)selectEventWithId:(NSString*)eventId { - [self selectEventWithId:eventId enableReplyMode:NO showTimestamp:YES]; + [self selectEventWithId:eventId inputToolBarSendMode:RoomInputToolbarViewSendModeSend showTimestamp:YES]; } -- (void)selectEventWithId:(NSString*)eventId enableReplyMode:(BOOL)enableReplyMode showTimestamp:(BOOL)showTimestamp +- (void)selectEventWithId:(NSString*)eventId inputToolBarSendMode:(RoomInputToolbarViewSendMode)inputToolBarSendMode showTimestamp:(BOOL)showTimestamp { - [self setInputToolBarSendMode: enableReplyMode ? RoomInputToolbarViewSendModeReply : RoomInputToolbarViewSendModeSend]; + [self setInputToolBarSendMode:inputToolBarSendMode]; customizedRoomDataSource.showBubbleDateTimeOnSelection = showTimestamp; customizedRoomDataSource.selectedEventId = eventId; @@ -2917,6 +2925,8 @@ customizedRoomDataSource.showBubbleDateTimeOnSelection = YES; customizedRoomDataSource.selectedEventId = nil; + [self restoreTextMessageBeforeEditing]; + // Force table refresh [self dataSource:self.roomDataSource didCellChange:nil]; } @@ -2927,6 +2937,45 @@ message:NSLocalizedStringFromTable(@"room_message_unable_open_link_error_message", @"Vector", nil)]; } +- (void)editEventContentWithId:(NSString*)eventId +{ + MXEvent *event = [self.roomDataSource eventWithEventId:eventId]; + + RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView]; + + if (roomInputToolbarView) + { + self.textMessageBeforeEditing = roomInputToolbarView.textMessage; + roomInputToolbarView.textMessage = event.content[@"body"]; + } + + [self selectEventWithId:eventId inputToolBarSendMode:RoomInputToolbarViewSendModeEdit showTimestamp:YES]; +} + +- (void)restoreTextMessageBeforeEditing +{ + RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView]; + + if (self.textMessageBeforeEditing) + { + roomInputToolbarView.textMessage = self.textMessageBeforeEditing; + } + + self.textMessageBeforeEditing = nil; +} + +- (RoomInputToolbarView*)inputToolbarViewAsRoomInputToolbarView +{ + RoomInputToolbarView *roomInputToolbarView; + + if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:[RoomInputToolbarView class]]) + { + roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView; + } + + return roomInputToolbarView; +} + #pragma mark - Segues - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender @@ -5062,14 +5111,19 @@ MXStrongifyAndReturnIfNil(self); [self hideContextualMenuAnimated:YES cancelEventSelection:NO completion:nil]; - [self selectEventWithId:eventId enableReplyMode:YES showTimestamp:NO]; + [self selectEventWithId:eventId inputToolBarSendMode:RoomInputToolbarViewSendModeReply showTimestamp:NO]; }; // Edit action RoomContextualMenuItem *editMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionEdit]; - // TODO: Handle edit action - editMenuItem.isEnabled = NO; + editMenuItem.action = ^{ + MXStrongifyAndReturnIfNil(self); + [self hideContextualMenuAnimated:YES cancelEventSelection:NO completion:nil]; + [self editEventContentWithId:eventId]; + }; + + editMenuItem.isEnabled = [self.roomDataSource canEditEventWithId:eventId]; // More action @@ -5098,7 +5152,7 @@ return; } - [self selectEventWithId:event.eventId enableReplyMode:NO showTimestamp:YES]; + [self selectEventWithId:event.eventId]; NSArray* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell]; From ed77ea0e27286710518ebb1e0d61a404720893bf 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 d8bdd6649..a923de8aa 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 84d3f844ab43cd842194c414de1b96f24f0a900d 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 a931b34de..2a6c23591 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 4f83f34322772b4b0c137c59b3942f012c5b93c4 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 2de28aa8e..32f5706dc 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 af8981d0656c4df4411b7b840712d378c6999a48 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 b48803bd8..2671d6211 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 3883e85b89a044d2cf8a0933f155c15d970721d0 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 da1195712..2bfa610c9 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 87b4233db61fa2de67d67572dc8ef921dfcf5bbe 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 c6af54045..33891e55c 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 f9396a82add808503115b433cedb4bf6d2d220a1 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 28435b73a..66a61219b 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5270,6 +5270,8 @@ [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 @@ [self.errorPresenter presentErrorFromViewController:self forError:error animated:YES handler:nil]; }]; + + [self hideContextualMenuAnimated:YES]; } @end From 343e76a13df3d0b669bd6ee3b9d3aa8a63df1303 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 66a61219b..590f8360f 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5112,6 +5112,9 @@ [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 @@ 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 dff699aa8112c3868672722c048d616ca0e2d287 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 f0a36c177..faeb381f1 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 5e279bf58fb5ccaf02ac0c98493482805ea64235 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 6dca9c4f5..b6247fda0 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 9b135075fa0e5a4caea44f82c169adcc75003394 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 d83b3feb9..0b9471ace 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 b500e10586c69d0864b9964fdee8a3f9d7e3a8d8 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 a923de8aa..9761acd5a 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 e550c3616..bf8ef7909 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -71,10 +71,6 @@ static NSAttributedString *timestampVerticalWhitespace = nil; // 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 @@ static NSAttributedString *timestampVerticalWhitespace = nil; 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 271cfc357894e27abdc17f79cd6f74758cf76ce4 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 f569efc26..ef62b1aa5 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -120,89 +120,6 @@ [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 e886fa0e6664509715ea5d62801aaf6ef9180439 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 684f793a4..2cb67277a 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -85,13 +85,24 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT 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 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT { 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 c7cab57c15b0a1df5dd71ca92babec5a9bd034df 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 e550c3616..6b62b990c 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -295,28 +295,11 @@ static NSAttributedString *timestampVerticalWhitespace = nil; // 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 3edf3c4578669349bec6c63c3c91dad220ae3bb1 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 e57ee45c5..c9f58e2aa 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 2cb67277a..2efb265c6 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -546,6 +546,48 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT 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 ccdb27861197300169c0c00c2d6b00375749d528 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 fd62967b4..a8317df00 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 6b62b990c..0637c96e1 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -29,6 +29,8 @@ static NSAttributedString *timestampVerticalWhitespace = nil; @interface RoomBubbleCellData() @property(nonatomic, readonly) BOOL addVerticalWhitespaceForSelectedComponentTimestamp; +@property(nonatomic, readwrite) CGFloat additionalContentHeight; +@property(nonatomic) BOOL shouldUpdateAdditionalContentHeight; @end @@ -106,6 +108,8 @@ static NSAttributedString *timestampVerticalWhitespace = nil; shouldUpdateComponentsPosition = NO; } + + [self updateAdditionalContentHeightIfNeeded]; } - (NSAttributedString*)attributedTextMessage @@ -380,6 +384,99 @@ static NSAttributedString *timestampVerticalWhitespace = nil; } } +- (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 9edbd4dd888b394927acfa073a8e99d99773c07b 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 f569efc26..af4e4a99a 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -170,6 +170,8 @@ { cellData.readReceipts[eventId] = nil; } + + [cellData setNeedsUpdateAdditionalContentHeight]; } } } @@ -192,6 +194,8 @@ // Ignore the read receipts on the events without an actual display. cellData.readReceipts[eventId] = nil; } + + [cellData setNeedsUpdateAdditionalContentHeight]; } } } @@ -203,6 +207,19 @@ }); } +- (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 @@ [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 @@ reactionsView.viewModel = bubbleReactionsViewModel; [reactionsView updateWithTheme:ThemeService.shared.theme]; + [temporaryViews addObject:reactionsView]; + bubbleReactionsViewModel.viewModelDelegate = self; reactionsView.translatesAutoresizingMaskIntoConstraints = NO; @@ -387,6 +408,8 @@ 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 @@ } } + // 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 00018fb4d..2f7e4d80a 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentBubbleCell.m @@ -39,4 +39,16 @@ [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 519bff872..16a614a46 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomIncomingAttachmentWithPaginationTitleBubbleCell.m @@ -45,4 +45,16 @@ } } ++ (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 712385152..5d63baf00 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 @@ 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 a50ffe6c9..e6417201b 100644 --- a/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/RoomOutgoingAttachmentBubbleCell.m @@ -62,4 +62,16 @@ } } ++ (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 b60dac677..30a50ae2d 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 @@ [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 7af59a00f5827b3cd7797b99824f95fbdd821eed 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 4d209376b..c59aa01c5 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 c5a76c12cd21d1bb8c0b875be2ce0b11fdab214f 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 2b24813f1..9d20c0849 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 2e80cc2a5ef2f8dbc12f75df135d8d2d021bb7c9 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 3bcdf9b5b..c7b642fb6 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -120,6 +120,31 @@ [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 5b8443c5cd8f6012b379fac9375c35b1d2d57d14 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 6106c88a4..02d42666c 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 c9f468216502b14c627866fc82bf6f24419c117b 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 4a3f5a3dd..2be1cd818 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 1f45d6485274502e998e8413dc45fb41ac2c7d54 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 a3b5996a7..40d75d409 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 cfa36ec21915b29078ff8340eb2e54bbbff425ca 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 02d42666c..4726fcb91 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 033894d7ed2077bb10a44c86f4bfaa6dcc9233f7 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 7f3b5e2b7..07ae2a664 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 2be1cd818..9612e4200 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 9b7efaa2a5f321b3e40adbd51a8990db034cb421 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 40d75d409..fbab2ca18 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 07ae2a664..a55e8c78e 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 f7cea12582be544d1b48d67c322d636fae7072d8 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 49ffe69fb..1498a6710 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 176b06f10..62c17a244 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 a55e8c78e..1b8bd8dfc 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 c69d19bb351ce9193e191bdce58cfd89c1b1f6e2 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 43eb3903d..9a0d8b8fe 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -270,8 +270,10 @@ static NSAttributedString *timestampVerticalWhitespace = nil; @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 @@ static NSAttributedString *timestampVerticalWhitespace = nil; 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 @@ static NSAttributedString *timestampVerticalWhitespace = nil; } // 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 @@ static NSAttributedString *timestampVerticalWhitespace = nil; 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 @@ static NSAttributedString *timestampVerticalWhitespace = nil; [self addVerticalWhitespaceToString:attributedString forEvent:component.event.eventId]; [attributedString appendAttributedString:[MXKRoomBubbleCellDataWithAppendingMode messageSeparator]]; + + visibleMessageIndex++; } else { From 089835f3f42beec6fea21c0671f11c72208bfa89 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 | 137 ++++++++++++++---- .../Widgets/WidgetManagerConfig.swift | 77 ++++++++++ .../IntegrationManagerViewController.m | 6 +- .../Widgets/WidgetViewController.m | 2 +- 7 files changed, 226 insertions(+), 33 deletions(-) create mode 100644 Riot/Managers/Widgets/WidgetManagerConfig.swift diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 9612e4200..6f8412cef 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 482f54e9c..1438701b3 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 @@ _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 83b41ac2f..7f94977f7 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 8f761bf08..683559abc 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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; NSMutableDictionary*> *failureBlockForWidgetCreation; // User id -> scalar token - NSMutableDictionary *scalarTokens; + NSMutableDictionary *configs; } @end @@ -74,12 +77,7 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; successBlockForWidgetCreation = [NSMutableDictionary dictionary]; failureBlockForWidgetCreation = [NSMutableDictionary dictionary]; - [self load]; - - if (!scalarTokens) - { - scalarTokens = [NSMutableDictionary dictionary]; - } + [self loadConfigs]; } return self; } @@ -261,6 +259,15 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; 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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; // 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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; - (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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; 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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; 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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; 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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; }]; } -+ (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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; - (NSString *)scalarTokenForMXSession:(MXSession *)mxSession { - return scalarTokens[mxSession.myUser.userId]; + return configs[mxSession.myUser.userId].scalarToken; } -- (void)load -{ - NSUserDefaults *userDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults; - scalarTokens = [NSMutableDictionary dictionaryWithDictionary:[userDefaults objectForKey:@"scalarTokens"]]; -} - -- (void)save +- (void)loadConfigs { NSUserDefaults *userDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults; - [userDefaults setObject:scalarTokens forKey:@"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)saveConfigs +{ + NSUserDefaults *userDefaults = [MXKAppSettings standardAppSettings].sharedUserDefaults; + [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 000000000..ba6c3ac36 --- /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 37bab65fa..3db2abbc9 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 *const kIntegrationManagerAddIntegrationScreen = @"add_integ"; { 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 f67456292..f7ad5c0bd 100644 --- a/Riot/Modules/Integrations/Widgets/WidgetViewController.m +++ b/Riot/Modules/Integrations/Widgets/WidgetViewController.m @@ -190,7 +190,7 @@ NSString *const kJavascriptSendResponseToPostMessageAPI = @"riotIOS.sendResponse 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 53afe41f9acd44008c345550a9a1c87600a9baac 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 e225169c6..b783eae17 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 8e7aa57b3..610984a56 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 7f94977f7..008a48c19 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 683559abc..d10c45995 100644 --- a/Riot/Managers/Widgets/WidgetManager.m +++ b/Riot/Managers/Widgets/WidgetManager.m @@ -264,7 +264,7 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; 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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; 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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; 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 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; 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 3db2abbc9..ff9df08fe 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 @@ NSString *const kIntegrationManagerAddIntegrationScreen = @"add_integ"; } 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 e910455e3ae0cdca8fb730984cca507989eb3865 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 b783eae17..f65459c33 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 610984a56..89914b8e9 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 008a48c19..a15928789 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 d10c45995..a6d2bdec1 100644 --- a/Riot/Managers/Widgets/WidgetManager.m +++ b/Riot/Managers/Widgets/WidgetManager.m @@ -548,10 +548,17 @@ NSString *const WidgetManagerErrorDomain = @"WidgetManagerErrorDomain"; } } 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 4e77ba4a21538b35feb6bd0d99c70c3945173f78 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 32f5706dc..6ba1132a1 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 d7ad10ebb4e58d1316a0021bd1f48b515b039495 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 4726fcb91..7f6826381 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 f65459c33..2a920ef5c 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 89914b8e9..181d63de0 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 b1c8ef33b6f6c4a971f19f08a01ec36fd117a169 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 2671d6211..3481bf084 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 8e2d1d6bc9e28d5ed0951d246d9756f307de6ef9 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 ae12573ec..6da7eb19b 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 86e11131dfd9d487b89b7a4bdc2958dc83a89c42 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 | 177 +++++------------- .../ReactionsMenuViewModelType.swift | 16 +- .../ReactionsMenuViewState.swift | 22 +++ 5 files changed, 80 insertions(+), 152 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 93de3403d..df9f4ed27 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 b98ac829a..3608a9187 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 846ce2049..c2ec99830 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 - } - } - - 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() - 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 - } + 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) } } } - - 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) { + // MARK: - Private + + private func loadData() { + let reactionCounts = self.aggregatedReactions?.withNonZeroCount()?.reactions ?? [] - // If required, unreact first - if selected { - self.ensure3StateButtons(withReaction: reaction) + var quickReactionsWithUserReactedFlag: [String: Bool] = Dictionary(uniqueKeysWithValues: self.reactions.map { ($0, false) }) + + reactionCounts.forEach { (reactionCount) in + if let hasUserReacted = quickReactionsWithUserReactedFlag[reactionCount.reaction], hasUserReacted == false { + quickReactionsWithUserReactedFlag[reactionCount.reaction] = reactionCount.myUserHasReacted + } } - let reactionString = reaction.rawValue + let reactionMenuItemViewDatas: [ReactionMenuItemViewData] = self.reactions.map { reaction -> ReactionMenuItemViewData in + let isSelected = quickReactionsWithUserReactedFlag[reaction] ?? false + return ReactionMenuItemViewData(emoji: reaction, isSelected: isSelected) + } - 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.currentViewDatas = reactionMenuItemViewDatas + + 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 111c01d03..0e162f6c1 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 000000000..e74a77ef7 --- /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 03cf6bef411ddd12be19d158a500d41c50b43b3d 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 d4f54bd39..ce4d91cf7 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 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 customizeViewRendering() { - self.backgroundColor = UIColor.clear - } - - 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 f8ca5acd4..f2920d92b 100644 --- a/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib +++ b/Riot/Modules/Room/ContextualMenu/ReactionsMenu/ReactionsMenuView.xib @@ -1,113 +1,117 @@ - + - - - - - - - - - + - - + + - + + + + + + + + + + + - - + + + + - - - - - - - - - - - + - - - - - - - - + + + + - - + + + + + From df05577aeba6dc135f26eece8e9582678347cfb7 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 c2ec99830..c35678cdb 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 711ebfcfb..f47344996 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 d4d928400..72594e248 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 69ece32a5d7680f2c3bf9fea0ae5e5b99d6d8267 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 5e0fbbc7e..58e9e2bd4 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 2c24c3ba27b40145c7b82ee9c4f656320c114cb6 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 e6c7b2b95..fab2cd055 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5158,21 +5158,13 @@ return; } - [self selectEventWithId:event.eventId]; + NSString *selectedEventId = event.eventId; + + [self selectEventWithId:selectedEventId]; NSArray* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell]; - - RoomContextualMenuViewController *roomContextualMenuViewController = [RoomContextualMenuViewController instantiateWith:contextualMenuItems]; - roomContextualMenuViewController.delegate = self; - - [self enableOverlayContainerUserInteractions:YES]; - - [self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:roomContextualMenuViewController - from:self - on:self.overlayContainerView - animated:YES - completion:^{ - }]; + ReactionsMenuViewModel *reactionsMenuViewModel; + CGRect bubbleComponentFrameInOverlayView = CGRectNull; if (RiotSettings.shared.messageReaction && [cell isKindOfClass:MXKRoomBubbleTableViewCell.class] && [self.roomDataSource canReactToEventWithId:event.eventId]) { @@ -5181,14 +5173,14 @@ 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 @@ 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 @@ [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 e157eefcb8330da190a836594da1c68f4e2f4f3b 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 6f8412cef..21d874c54 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 917d5907289d5381a9c1333596d7b6cfacc41b97 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 7f6826381..f291f041c 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 75df6d19ad3ce2d898b94507bdef985f64b3c0c6 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 faeb381f1..c6056007f 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 cec0bbe3888456412af45fe3c9a8b8466aa23317 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 6ba1132a1..aca76b965 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 01f347cb21cf163c2fbbb0c53ed51378782ac8b1 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 0b9471ace..4038b6d7e 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 d902d7669277dad4dcd50f7e0eadd6333017595a 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 3481bf084..91eb54c3f 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 eabea543aaf444fa56a879c54d246be65ad4a620 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 9d20c0849..f1556d009 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 456aa56474e62d31f221d7e7387367679655e1ae 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 35e41eec8..db90d9281 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib @@ -20,16 +20,16 @@ - + - - - - + + + + From f49941259979d8024d0f5a81b0b6ece92cebdc17 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 77df69f6b..ab4c455e5 100644 --- a/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m +++ b/Riot/Modules/Room/Views/BubbleCells/Encryption/RoomEncryptedDataBubbleCell.m @@ -29,7 +29,8 @@ NSString *const kRoomEncryptedDataBubbleCellTapOnEncryptionIcon = @"kRoomEncrypt { 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 bd60e966585418fcaf7a01180c7de51c76930f7e 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 | 713 ++++++++++++------------- 1 file changed, 342 insertions(+), 371 deletions(-) diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index fab2cd055..a5c6a9c21 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2101,7 +2101,7 @@ 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 @@ } } -// 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 @@ __weak __typeof(self) weakSelf = self; currentAlert = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:UIAlertControllerStyleActionSheet]; - if (level == 0) + // Add actions for a failed event + if (selectedEvent.sentState == MXEventSentStateFailed) { - // Add actions for a failed event - if (selectedEvent.sentState == MXEventSentStateFailed) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_resend", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_resend", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - // Let the datasource resend. It will manage local echo, etc. - [self.roomDataSource resendEventWithEventId:selectedEvent.eventId success:nil failure:nil]; - } + [self cancelEventSelection]; - }]]; - - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_delete", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + // Let the datasource resend. It will manage local echo, etc. + [self.roomDataSource resendEventWithEventId:selectedEvent.eventId success:nil failure:nil]; + } + + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_delete", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; - } + [self cancelEventSelection]; - }]]; - } + [self.roomDataSource removeEventWithEventId:selectedEvent.eventId]; + } + + }]]; } // Add actions for text message @@ -2246,171 +2243,160 @@ selectedComponent = nil; } - if (level == 0) + + // Check status of the selected event + if (selectedEvent.sentState == MXEventSentStatePreparing || + selectedEvent.sentState == MXEventSentStateEncrypting || + selectedEvent.sentState == MXEventSentStateSending) { - // Check status of the selected event - if (selectedEvent.sentState == MXEventSentStatePreparing || - selectedEvent.sentState == MXEventSentStateEncrypting || - selectedEvent.sentState == MXEventSentStateSending) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_cancel_send", @"Vector", nil) - style:UIAlertActionStyleDefault - 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]; - } - - }]]; - } - } - - if (level == 0) - { - [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) { - - if (weakSelf) - { - typeof(self) self = 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]; - } - - }]]; + 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]; + } + + }]]; } - - if (level == 1) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_share", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + + [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]; - - NSArray *activityItems = @[selectedComponent.textMessage]; - - UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil]; - - if (activityViewController) - { - activityViewController.modalTransitionStyle = UIModalTransitionStyleCoverVertical; - activityViewController.popoverPresentationController.sourceView = roomBubbleTableViewCell; - activityViewController.popoverPresentationController.sourceRect = roomBubbleTableViewCell.bounds; - - [self presentViewController:activityViewController animated:YES completion:nil]; - } + [self presentViewController:activityViewController animated:YES completion:nil]; } - - }]]; - } + } + + }]]; } else // Add action for attachment { - if (level == 0) + if (attachment.type == MXKAttachmentTypeImage || attachment.type == MXKAttachmentTypeVideo) { - if (attachment.type == MXKAttachmentTypeImage || attachment.type == MXKAttachmentTypeVideo) + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_save", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + [self startActivityIndicator]; + + [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]; + + }]; + + // Start animation in case of download during attachment preparing + [roomBubbleTableViewCell startProgressUI]; + } + + }]]; + } + + // 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_save", @"Vector", nil) + [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]; - - [self startActivityIndicator]; - - [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]; - - }]; - - // Start animation in case of download during attachment preparing - [roomBubbleTableViewCell startProgressUI]; } }]]; } - - // 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 @@ if (selectedEvent.sentState == MXEventSentStateSent) { // Check whether download is in progress - if (level == 0 && selectedEvent.isMediaAttachment) + if (selectedEvent.isMediaAttachment) { NSString *downloadId = roomBubbleTableViewCell.bubbleData.attachment.downloadId; if ([MXMediaManager existingDownloaderWithIdentifier:downloadId]) @@ -2484,48 +2470,11 @@ } } - if (level == 0) + // Do not allow to redact the event that enabled encryption (m.room.encryption) + // because it breaks everything + if (selectedEvent.eventType != MXEventTypeRoomEncryption) { - // Do not allow to redact the event that enabled encryption (m.room.encryption) - // because it breaks everything - if (selectedEvent.eventType != MXEventTypeRoomEncryption) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_redact", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - [self startActivityIndicator]; - - [self.roomDataSource.room redactEvent:selectedEvent.eventId reason:nil success:^{ - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - } failure:^(NSError *error) { - - __strong __typeof(weakSelf)self = weakSelf; - [self stopActivityIndicator]; - - NSLog(@"[RoomVC] Redact event (%@) failed", selectedEvent.eventId); - //Alert user - [[AppDelegate theDelegate] showErrorAsAlert:error]; - - }]; - } - - }]]; - } - } - - if (level == 1) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_permalink", @"Vector", nil) + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_redact", @"Vector", nil) style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { @@ -2535,37 +2484,65 @@ [self cancelEventSelection]; - // Create a matrix.to permalink that is common to all matrix clients - NSString *permalink = [MXTools permalinkToEvent:selectedEvent.eventId inRoom:selectedEvent.roomId]; + [self startActivityIndicator]; - // Create a room matrix.to permalink - [[UIPasteboard generalPasteboard] setString:permalink]; - } - - }]]; - } - - if (level == 1) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_view_source", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - - [self cancelEventSelection]; - - // Display event details - [self showEventDetails:selectedEvent]; + [self.roomDataSource.room redactEvent:selectedEvent.eventId reason:nil success:^{ + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + } failure:^(NSError *error) { + + __strong __typeof(weakSelf)self = weakSelf; + [self stopActivityIndicator]; + + NSLog(@"[RoomVC] Redact event (%@) failed", selectedEvent.eventId); + //Alert user + [[AppDelegate theDelegate] showErrorAsAlert:error]; + + }]; } }]]; } + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_permalink", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + // Create a matrix.to permalink that is common to all matrix clients + NSString *permalink = [MXTools permalinkToEvent:selectedEvent.eventId inRoom:selectedEvent.roomId]; + + // Create a room matrix.to permalink + [[UIPasteboard generalPasteboard] setString:permalink]; + } + + }]]; + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_view_source", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; + + [self cancelEventSelection]; + + // Display event details + [self showEventDetails:selectedEvent]; + } + + }]]; + // Add "View Decrypted Source" for e2ee event we can decrypt - if (level == 1 && selectedEvent.isEncrypted && selectedEvent.clearEvent) + if (selectedEvent.isEncrypted && selectedEvent.clearEvent) { [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_view_decrypted_source", @"Vector", nil) style:UIAlertActionStyleDefault @@ -2584,117 +2561,115 @@ }]]; } - if (level == 1) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_report", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { + + [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_report", @"Vector", nil) + style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + + if (weakSelf) + { + typeof(self) self = weakSelf; - if (weakSelf) - { - typeof(self) self = weakSelf; - - [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) { - - 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:^{ - - __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]; - - [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]; - - // 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) { - - __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 presentViewController:self->currentAlert animated:YES completion:nil]; - } + [self cancelEventSelection]; - }]]; - } - - if (level == 1 && self.roomDataSource.room.summary.isEncrypted) + // 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) { + + 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:^{ + + __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]; + + [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]; + + // 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) { + + __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 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 @@ }]]; } - - - if (level == 0) - { - [currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_more", @"Vector", nil) - style:UIAlertActionStyleDefault - handler:^(UIAlertAction * action) { - - if (weakSelf) - { - typeof(self) self = weakSelf; - self->currentAlert = 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 @@ // Do not display empty action sheet if (currentAlert.actions.count > 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 @@ 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 d408df348050f9a507993d2221f7571b5bfa1d21 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 58e9e2bd4..fb7b17398 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 a5c6a9c21..0dc88ed52 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -1519,7 +1519,7 @@ { if (event && !customizedRoomDataSource.selectedEventId) { - [self showContextualMenuForEvent:event cell:cell animated:YES]; + [self showContextualMenuForEvent:event fromSingleTapGesture:NO cell:cell animated:YES]; } } @@ -2083,7 +2083,7 @@ } else { - [self showContextualMenuForEvent:tappedEvent cell:cell animated:YES]; + [self showContextualMenuForEvent:tappedEvent fromSingleTapGesture:YES cell:cell animated:YES]; } } } @@ -2101,7 +2101,7 @@ 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 @@ 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 @@ NSString *selectedEventId = event.eventId; - [self selectEventWithId:selectedEventId]; - NSArray* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell]; ReactionsMenuViewModel *reactionsMenuViewModel; CGRect bubbleComponentFrameInOverlayView = CGRectNull; @@ -5183,9 +5181,12 @@ from:self on:self.overlayContainerView contentToReactFrame:bubbleComponentFrameInOverlayView - animated:YES + fromSingleTapGesture:usedSingleTapGesture + animated:animated completion:^{ }]; + + [self selectEventWithId:selectedEventId]; } - (void)hideContextualMenuAnimated:(BOOL)animated From 0584228bed96aacb452e2aaf078b2083c9d8a932 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 fab2cd055..b26c58775 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -287,6 +287,7 @@ // 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 @@ 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)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 25d7abdd00e05e2b45116d08696be09b366582e3 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 b6247fda0..404ba0d08 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 945c5a42ac36ff566ef6cac058d5ec94881642f9 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 ce4d91cf7..5ceb40d9b 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 f2920d92b..f9c6c0113 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 fb7b17398..04d87ad92 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 72594e248..baba7f279 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 0dc88ed52..fdcd5db98 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -216,6 +216,8 @@ @property (nonatomic, weak) IBOutlet UIView *overlayContainerView; + +@property (nonatomic, strong) RoomContextualMenuViewController *roomContextualMenuViewController; @property (nonatomic, strong) RoomContextualMenuPresenter *roomContextualMenuPresenter; @property (nonatomic, strong) MXKErrorAlertPresentation *errorPresenter; @property (nonatomic, strong) NSString *textMessageBeforeEditing; @@ -5172,12 +5174,17 @@ reactionsMenuViewModel.coordinatorDelegate = self; } - RoomContextualMenuViewController *roomContextualMenuViewController = [RoomContextualMenuViewController instantiateWith:contextualMenuItems reactionsMenuViewModel:reactionsMenuViewModel]; - roomContextualMenuViewController.delegate = self; + if (!self.roomContextualMenuViewController) + { + self.roomContextualMenuViewController = [RoomContextualMenuViewController instantiate]; + self.roomContextualMenuViewController.delegate = self; + } + + [self.roomContextualMenuViewController updateWithContextualMenuItems:contextualMenuItems reactionsMenuViewModel:reactionsMenuViewModel]; [self enableOverlayContainerUserInteractions:YES]; - [self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:roomContextualMenuViewController + [self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:self.roomContextualMenuViewController from:self on:self.overlayContainerView contentToReactFrame:bubbleComponentFrameInOverlayView From cc2ae8c6ba33fcac98f86a65fb852fe35243f7f2 Mon Sep 17 00:00:00 2001 From: manuroe 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 21d874c54..5271276e5 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 b2aa4fda8..b8647542a 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 000000000..29dcb6500 --- /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 000000000..1ce1d524f --- /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 000000000..2f02bd705 --- /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 000000000..f33864960 --- /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 000000000..d1fed7fc5 --- /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 000000000..c8f3b006a --- /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 000000000..451991881 --- /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 000000000..f4b1348a8 --- /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 000000000..d0322c85f --- /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 000000000..62f75431b --- /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 fe1f719284d16881266cf51e2625016e9bb3b11e 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 1ce1d524f..fb52c31c5 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 69966e36c01a79f8d242b76385ada8eeb44db1a7 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 c8f3b006a..7a2d5f148 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 451991881..843bb31b4 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 f4b1348a8..3a156733e 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 62f75431b..112451335 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 7cbb260992a1ccd89f81c953b10f91798fd2ecd5 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 | 29 +++++++----- Riot/SupportingFiles/Riot-Bridging-Header.h | 1 + 4 files changed, 56 insertions(+), 24 deletions(-) diff --git a/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift b/Riot/Modules/Room/EditHistory/EditHistoryCoordinator.swift index 29dcb6500..760b5ccf6 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 fb52c31c5..818773e2b 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 3a156733e..3229a9218 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? { - - guard let body: String = (editEvent.content?["m.new_content"] as? [String: Any])?["body"] as? String else { - print("[EditHistoryViewModel] processEditEvent: invalid edit event: \(editEvent.eventId ?? "")") + // 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 } - // TODO: Using MXKEventFormatter - return EditHistoryMessage(date: Date(), message: NSAttributedString(string: body)) + 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 + } + + 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 7467821eb..68348718c 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 c41fa083876696964931dc4de09ed29c5364741a 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 818773e2b..a566c959b 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 67abcd419..172175079 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 8b8eeba27..3ba09e318 100644 --- a/Riot/Utils/EventFormatter.m +++ b/Riot/Utils/EventFormatter.m @@ -182,7 +182,7 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; attributedString = attributedStringWithRerequestMessage; } } - else if (event.contentHasBeenEdited) + else if (self.showEditionMention && event.contentHasBeenEdited) { NSMutableAttributedString *attributedStringWithEditMention = [attributedString mutableCopy]; @@ -255,6 +255,7 @@ static NSString *const kEventFormatterTimeFormat = @"HH:mm"; 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 4835d816b2b76cd4adfa94d7668510b635a98dd8 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 843bb31b4..59ae30e10 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 3229a9218..2a9f553bf 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 b29a37c1c3aa08eefd5c516a7bf40916fbc7d529 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 767bb6cc3..fdfd667eb 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 22ea672f815706e4b1163c0b8287713c8ea5d540 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 ac1bdff5c..e0bc7ff22 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -2920,7 +2920,7 @@ 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 7f2d1e2ff49eaa8028a10bae671e2293269003f3 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 f291f041c..d2bd2d6a0 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 e4171cffa9be3356a3c9e372b2216be872d52233 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 5271276e5..98817bc8e 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 2a920ef5c..2ccf5624d 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 181d63de0..73988e0e6 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 000000000..5fbe5de8a --- /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 000000000..fa3e5bdb7 --- /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 33eaf486c..a3238b809 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 339b00987..1a79b6edb 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 04a30726a..3496854df 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 a8317df00..e03035abb 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 9a0d8b8fe..44f219b2f 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -32,6 +32,9 @@ static NSAttributedString *timestampVerticalWhitespace = nil; @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 @@ static NSAttributedString *timestampVerticalWhitespace = nil; #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 @@ static NSAttributedString *timestampVerticalWhitespace = nil; 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 @@ static NSAttributedString *timestampVerticalWhitespace = nil; 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 @@ static NSAttributedString *timestampVerticalWhitespace = nil; 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 c7b642fb6..7282bd4eb 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -244,7 +244,10 @@ 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 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 48f7c63e01a6d93d13bc194f251266d85bcf388c 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 2ccf5624d..18d956eb1 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 73988e0e6..2caf52f43 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 ed785171e79c09006e907647a5df8a9baeae55db 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 000000000..0d05ef6c2 --- /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 000000000..7780255db --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryCell.xib @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 882031ef7836abcec97ff6bd641a9b68ee6b5724 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 000000000..a547024d1 --- /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 000000000..e0a28bcf5 --- /dev/null +++ b/Riot/Modules/Room/EditHistory/EditHistoryHeaderView.xib @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From ce5073a93dd5aa1d2962ed4f328172fa1f9808c6 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 98817bc8e..13306ca78 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 000000000..550378541 --- /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 5f83d6c841a673a0b97637fc6ba0d4eaa46ac58e 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 2a9f553bf..541491a93 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 d0322c85f..4ed9ff707 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 112451335..bb48192ee 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 50007800f2a7e4331ed596aad8c239fe45918d60 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 7a2d5f148..059dae309 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 59ae30e10..88c254182 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.messageLabel.text = "VectorL10n.editHistoryTitle" - self.messageLabel.isHidden = true + self.tableView.sectionHeaderHeight = UITableView.automaticDimension + self.tableView.estimatedSectionHeaderHeight = Constants.estimatedSectionHeaderHeight + self.tableView.register(headerFooterViewType: EditHistoryHeaderView.self) + + 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 a361defb2cd55e34eeae1663f2a612b3bcf8eab4 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 a566c959b..ecd2a3b73 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 e0bc7ff22..c7c0818a8 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 @@ @property (nonatomic, strong) RoomContextualMenuPresenter *roomContextualMenuPresenter; @property (nonatomic, strong) MXKErrorAlertPresentation *errorPresenter; @property (nonatomic, strong) NSString *textMessageBeforeEditing; +@property (nonatomic, strong) EditHistoryCoordinatorBridgePresenter *editHistoryPresenter; @end @@ -2825,11 +2826,8 @@ 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); + [self showEditHistoryForEventId:eventId animated:YES]; } shouldDoAction = NO; } @@ -5291,5 +5289,26 @@ }]; } +#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 368411ae3a6802302cb2a7ad136343eb749877f1 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 91eb54c3f..c91bb3b54 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 57bc802c8e3b4f882d5747f467665d296509e170 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 760b5ccf6..7cabf0a73 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 ecd2a3b73..f6b20b5e5 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 541491a93..47948eb98 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 6b16dce9b19de6914c5be3674147802a6b63d877 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 c6056007f..f1ba0864e 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 7c88541a023cc1ad71998d3baa568677f880dd9a 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 c91bb3b54..c63e348ad 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 129c9d9bcc752388d9caff61336da54733c3c42e 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 a3238b809..4ac8923e4 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 728a522bb8f872761d570ea9286e3b198d43a40e 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 d2bd2d6a0..7427fef55 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 bc8c22bdd..276c6657d 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -2324,6 +2324,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN [homeViewController stopActivityIndicator]; roomPreviewData = [[RoomPreviewData alloc] initWithRoomId:roomIdOrAlias emailInvitationParams:queryParams andSession:account.mxSession]; + roomPreviewData.viaServers = queryParams[@"via"]; [self showRoomPreview:roomPreviewData]; } else @@ -2522,8 +2523,22 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN { value = [value stringByReplacingOccurrencesOfString:@"+" withString:@" "]; value = [value stringByRemovingPercentEncoding]; - - queryParams[key] = value; + + if ([key isEqualToString:@"via"]) + { + // Special case the via parameter + // As we can have several of them, store each value into an array + if (!queryParams[key]) + { + queryParams[key] = [NSMutableArray array]; + } + + [queryParams[key] addObject:value]; + } + else + { + queryParams[key] = value; + } } } } diff --git a/Riot/Model/Room/RoomPreviewData.h b/Riot/Model/Room/RoomPreviewData.h index 333912c8f..47797327c 100644 --- a/Riot/Model/Room/RoomPreviewData.h +++ b/Riot/Model/Room/RoomPreviewData.h @@ -50,6 +50,12 @@ */ @property (nonatomic) NSString *eventId; +/** + In case of preview, the server names to try and join through in addition to those + that are automatically chosen. + */ +@property (nonatomic) NSArray *viaServers; + /** Preview information. */ diff --git a/Riot/Modules/Common/Recents/RecentsViewController.m b/Riot/Modules/Common/Recents/RecentsViewController.m index 03e582aeb..30a754803 100644 --- a/Riot/Modules/Common/Recents/RecentsViewController.m +++ b/Riot/Modules/Common/Recents/RecentsViewController.m @@ -1815,8 +1815,9 @@ 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 c7c0818a8..ceafd6b03 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -1047,7 +1047,8 @@ // 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 @@ } // 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 95d5dd46a85f3e073ad335a30c674ec77805c1cf 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 7427fef55..de78b0c24 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 ceafd6b03..84961aa83 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -4048,8 +4048,15 @@ } 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 fb3b0e7f5833a3f78e6ff6a1850e51e10946e598 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 d253e9c23..5ce0ebe82 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 103c308fb2e11a900a0b665e46916ec7eda51c74 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 09d338371..779227e7f 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 086b6ab2b16df6d79c0cf23b90edc1a3d5aed0cc 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 e03035abb..777063a59 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 44f219b2f..5fda69f74 100644 --- a/Riot/Modules/Room/CellData/RoomBubbleCellData.m +++ b/Riot/Modules/Room/CellData/RoomBubbleCellData.m @@ -277,6 +277,31 @@ static NSAttributedString *timestampVerticalWhitespace = nil; 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 f4c30ed00ca8ce34a851c326d589d2b5c8c91857 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 c9f58e2aa..260d79c51 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 2efb265c6..82389e987 100644 --- a/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m +++ b/Riot/Categories/MXKRoomBubbleTableViewCell+Riot.m @@ -473,6 +473,66 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT 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 120284e484bd0f41d37d35a21ae805b3f94a7e03 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 c7c0818a8..5c9bc191e 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5169,7 +5169,7 @@ if (bubbleComponents.count > 0) { NSInteger selectedComponentIndex = foundComponentIndex != NSNotFound ? foundComponentIndex : 0; - bubbleComponentFrame = [roomBubbleTableViewCell componentFrameInTableViewForIndex:selectedComponentIndex]; + bubbleComponentFrame = [roomBubbleTableViewCell surroundingFrameInTableViewForComponentIndex:selectedComponentIndex]; } else { From c1d50ddc27c0207a1809a9a7a91bfa4973b38bae 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 db90d9281..17552af69 100644 --- a/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib +++ b/Riot/Modules/Room/BubbleReactions/BubbleReactionViewCell.xib @@ -13,23 +13,26 @@ - + - + - + - From ce830f55643ad5c8e25cc5382796fedc3d97b15b 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 3b3d46a03..c6e2af626 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 411f963033af0214ade79dc754e056bf94ee0cb7 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 09d5220ff..6db8ec7b3 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 7282bd4eb..75208dbfc 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -61,7 +61,7 @@ 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 106c5a99a..780f7adc8 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -5157,7 +5157,7 @@ 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 b397757e1..4cccbedf2 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -120,7 +120,6 @@ enum { 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 @@ SignOutAlertPresenterDelegate> [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 @@ SignOutAlertPresenterDelegate> } } -- (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 b683afce03dffe8e47df7a798adf386ba69b038a 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 47948eb98..461f32699 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 9b064d05195d0d4ad2c6da3ffca133b8e7096969 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 de78b0c24..7daae55b1 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 780f7adc8..b0d4f54c1 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -4048,18 +4048,43 @@ } 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 843ce9024fb4f5d4caaf97ee33b56ca2c73b859c 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 18d956eb1..4ea5f237e 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 2caf52f43..9e37dbfa9 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 c548f48cf..fdff609e1 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 2b9ff11e0..f09f0ba8f 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -349,6 +349,19 @@ } }]]; + + [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 41ddf88fcb34b06ae3e6f6b897eaf48751218796 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 cfc62b45d..6e3230a07 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 75208dbfc..fa7034b45 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.m +++ b/Riot/Modules/Room/DataSources/RoomDataSource.m @@ -555,6 +555,14 @@ 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 4a4e647360f73508f6a355444ca57a77a0d44584 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 4ea5f237e..ff0f9d47f 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 b0d4f54c1..5b574c56f 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 @@ @property (nonatomic, strong) MXKErrorAlertPresentation *errorPresenter; @property (nonatomic, strong) NSString *textMessageBeforeEditing; @property (nonatomic, strong) EditHistoryCoordinatorBridgePresenter *editHistoryPresenter; +@property (nonatomic, strong) MXKDocumentPickerPresenter *documentPickerPresenter; @end @@ -3356,6 +3357,17 @@ } } +- (void)roomInputToolbarViewDidTapFileUpload:(MXKRoomInputToolbarView *)toolbarView +{ + MXKDocumentPickerPresenter *documentPickerPresenter = [MXKDocumentPickerPresenter new]; + documentPickerPresenter.delegate = self; + + NSArray *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 @@ 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 8808ade76f6d8ddda71d367e3f30cc03d0b3e9c9 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 fbab2ca18..652529aff 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 cd67ca94243c42880cf3a671c86d718ada86f5b6 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 9e37dbfa9..e38028ae3 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 1102c79b44e79a1f7c80c6dc30178e5ab4789a3c 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 7daae55b1..1a2b6a02a 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 3dc6901f1a0e85b6f78eb5ccfb8617287c7651cd 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 6e3230a07..1c7dfda5a 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 1a990f984d1416c2474aa3588e4132b7974792d3 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 aca76b965..aa69a920c 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 17e4ce676f62d05fa65e7719c71d0712306f15a2 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 f1556d009..be799172c 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 d96d402dee890445b23ffbd868315ef3385e758b 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 1a2b6a02a..57dcf2d70 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 4a353b686..a1c29526d 100644 --- a/RiotShareExtension/SupportingFiles/Info.plist +++ b/RiotShareExtension/SupportingFiles/Info.plist @@ -28,6 +28,8 @@ NSExtensionActivationDictionaryVersion 2 + NSExtensionActivationSupportsFileWithMaxCount + 5 NSExtensionActivationSupportsImageWithMaxCount 5 NSExtensionActivationSupportsMovieWithMaxCount From 298332899396ea597b241a024f068545f663f876 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 ff0f9d47f..0bfac7c33 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 e38028ae3..02182943f 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 172175079..4952f1441 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 c07a7b5e2022fad52211841a09b2c0997f4fce2f 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 c63e348ad..0e15b14cd 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 031343fc20fda43d2516ad34ea3da18b8afc5a92 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 0e15b14cd..ec1d923b5 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 ce29e5272bfc047d45d15600d765c735785a074b 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 be799172c..668b332b2 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 794d3876294ad8614d8f5ffb06f8283c2f6b4413 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 57dcf2d70..11148056b 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 000000000..72363f783 --- /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 49503bc0995fc3bc465a6b441e60288c1c197278 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 aa69a920c..a605cebfb 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 768ab91407e2eb8d52a909c81d8f76e86036b32f 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 404ba0d08..aa681d10b 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 ef3f8a03efcc0bcf1385ffec52a3780ce9ff5023 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 11148056b..d4530ba87 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 0bfac7c33..f13c54309 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 02182943f..0738f3685 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 14cdc50c0dc599f87945a3dc0ac0570be836ae7b 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 276c6657d..db380b2ec 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -2742,7 +2742,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN 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 4cccbedf2..b8e545a09 100644 --- a/Riot/Modules/Settings/SettingsViewController.m +++ b/Riot/Modules/Settings/SettingsViewController.m @@ -2874,7 +2874,11 @@ SignOutAlertPresenterDelegate> if (accountManager.pushDeviceToken) { - [account setEnablePushKitNotifications:!account.isPushKitNotificationActive]; + [account enablePushKitNotifications:!account.isPushKitNotificationActive success:^{ + [self stopActivityIndicator]; + } failure:^(NSError *error) { + [self stopActivityIndicator]; + }]; } else { @@ -2887,7 +2891,11 @@ SignOutAlertPresenterDelegate> } else { - [account setEnablePushKitNotifications:YES]; + [account enablePushKitNotifications:YES success:^{ + [self stopActivityIndicator]; + } failure:^(NSError *error) { + [self stopActivityIndicator]; + }]; } }]; } From a422929a9469116801d7eeac91736e0c42ba6d01 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 d4530ba87..c08d93e5e 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 652529aff..c75eeeea7 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 1b8bd8dfc..664d1aadf 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 bc44ef80d..b69ed44c8 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 a1c29526d..981a68366 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 9eb31162f..b31f06aa7 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