Skip to content
This repository has been archived by the owner on Dec 12, 2022. It is now read-only.

Replies: Implement sending #452

Merged
merged 7 commits into from
Jul 24, 2018
7 changes: 7 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
Changes in MatrixKit in 0.7.16 ()
==========================================

Improvements:
* MXKRoomDataSource: Add send reply with text message (vector-im/riot-ios#1911).


Changes in MatrixKit in 0.7.15 (2018-07-03)
==========================================

Expand Down
6 changes: 6 additions & 0 deletions MatrixKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
B12C470C20BECAF8004FBF96 /* MXKErrorViewModel.m in Sources */ = {isa = PBXBuildFile; fileRef = B12C470920BECAF7004FBF96 /* MXKErrorViewModel.m */; };
B12C470D20BECAF8004FBF96 /* MXKErrorAlertPresentation.m in Sources */ = {isa = PBXBuildFile; fileRef = B12C470A20BECAF7004FBF96 /* MXKErrorAlertPresentation.m */; };
B12C471020BED312004FBF96 /* MXKErrorPresentableBuilder.m in Sources */ = {isa = PBXBuildFile; fileRef = B12C470F20BED312004FBF96 /* MXKErrorPresentableBuilder.m */; };
B164380C210603CD00DBB3FD /* MXKSendReplyEventStringLocalizations.m in Sources */ = {isa = PBXBuildFile; fileRef = B164380B210603CD00DBB3FD /* MXKSendReplyEventStringLocalizations.m */; };
CE14CA661E80122600E329A3 /* MXKAttachmentAnimator.m in Sources */ = {isa = PBXBuildFile; fileRef = CE14CA631E80122600E329A3 /* MXKAttachmentAnimator.m */; };
CE14CA671E80122600E329A3 /* MXKAttachmentInteractionController.m in Sources */ = {isa = PBXBuildFile; fileRef = CE14CA651E80122600E329A3 /* MXKAttachmentInteractionController.m */; };
F0026B661C91EED1001D2C04 /* MXKWebViewViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0026B651C91EED1001D2C04 /* MXKWebViewViewController.m */; };
Expand Down Expand Up @@ -331,6 +332,8 @@
B12C470B20BECAF7004FBF96 /* MXKErrorPresentable.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKErrorPresentable.h; sourceTree = "<group>"; };
B12C470E20BED312004FBF96 /* MXKErrorPresentableBuilder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKErrorPresentableBuilder.h; sourceTree = "<group>"; };
B12C470F20BED312004FBF96 /* MXKErrorPresentableBuilder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKErrorPresentableBuilder.m; sourceTree = "<group>"; };
B164380A210603CD00DBB3FD /* MXKSendReplyEventStringLocalizations.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXKSendReplyEventStringLocalizations.h; sourceTree = "<group>"; };
B164380B210603CD00DBB3FD /* MXKSendReplyEventStringLocalizations.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MXKSendReplyEventStringLocalizations.m; sourceTree = "<group>"; };
BFDF88630F521F8C5854F64F /* Pods-MatrixKitTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixKitTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixKitTests/Pods-MatrixKitTests.debug.xcconfig"; sourceTree = "<group>"; };
C1C97E32A7F1D6C4B99A47EF /* Pods-MatrixKitSample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MatrixKitSample.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MatrixKitSample/Pods-MatrixKitSample.debug.xcconfig"; sourceTree = "<group>"; };
CE14CA621E80122600E329A3 /* MXKAttachmentAnimator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MXKAttachmentAnimator.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1148,6 +1151,8 @@
F07E180C1ABC2EDA00DE3766 /* MXKRoomDataSource.m */,
3230A3731ACADC1800CC57F5 /* MXKRoomDataSourceManager.h */,
3230A3741ACADC1800CC57F5 /* MXKRoomDataSourceManager.m */,
B164380A210603CD00DBB3FD /* MXKSendReplyEventStringLocalizations.h */,
B164380B210603CD00DBB3FD /* MXKSendReplyEventStringLocalizations.m */,
);
path = Room;
sourceTree = "<group>";
Expand Down Expand Up @@ -1669,6 +1674,7 @@
F04356321B2EF6FD0096FA02 /* MXKRoomMemberDetailsViewController.m in Sources */,
F095E5101B25899F009606CE /* MXKEmail.m in Sources */,
321313C91AEFC2D500A9B035 /* MXKRoomIOSOutgoingBubbleTableViewCell.m in Sources */,
B164380C210603CD00DBB3FD /* MXKSendReplyEventStringLocalizations.m in Sources */,
F0FDF2671E53586A00D23C47 /* MXKCountryPickerViewController.m in Sources */,
B12C471020BED312004FBF96 /* MXKErrorPresentableBuilder.m in Sources */,
F00FA85C1C0867F900E25826 /* MXKRoomIncomingAttachmentBubbleCell.m in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,13 @@
"room_no_power_to_create_conference_call" = "You need permission to invite to start a conference in this room";
"room_no_conference_call_in_encrypted_rooms" = "Conference calls are not supported in encrypted rooms";

// Reply to message
"message_reply_to_sender_sent_an_image" = "sent an image.";
"message_reply_to_sender_sent_a_video" = "sent a video.";
"message_reply_to_sender_sent_an_audio_file" = "sent an audio file.";
"message_reply_to_sender_sent_a_file" = "sent a file.";
"message_reply_to_message_to_reply_to_prefix" = "In reply to";

// Room members
"room_member_ignore_prompt" = "Are you sure you want to hide all messages from this user?";
"room_member_power_level_prompt" = "You will not be able to undo this change as you are promoting the user to have the same power level as yourself.\nAre you sure?";
Expand Down
19 changes: 19 additions & 0 deletions MatrixKit/Models/Room/MXKRoomDataSource.h
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,25 @@ extern NSString *const kMXKRoomDataSourceTimelineErrorErrorKey;
success:(void (^)(NSString *eventId))success
failure:(void (^)(NSError *error))failure;

/**
Send a reply to an event with text message to the room.

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 eventIdToReply the id of event to reply.
@param text the text to send.
@param success A block object called when the operation succeeds. It returns
the event id of the event generated on the home server
@param failure A block object called when the operation fails.
*/
- (void)sendReplyToEventWithId:(NSString*)eventIdToReply
withTextMessage:(NSString *)text
success:(void (^)(NSString *))success
failure:(void (^)(NSError *))failure;

- (BOOL)canReplyToEventWithId:(NSString*)eventIdToReply;

/**
Send an image to the room.

Expand Down
107 changes: 83 additions & 24 deletions MatrixKit/Models/Room/MXKRoomDataSource.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@

#import "MXEncryptedAttachments.h"

#import "MXKSendReplyEventStringLocalizations.h"

#pragma mark - Constant definitions

NSString *const kMXKRoomBubbleCellDataIdentifier = @"kMXKRoomBubbleCellDataIdentifier";
Expand All @@ -38,6 +40,8 @@
NSString *const kMXKRoomDataSourceTimelineError = @"kMXKRoomDataSourceTimelineError";
NSString *const kMXKRoomDataSourceTimelineErrorErrorKey = @"kMXKRoomDataSourceTimelineErrorErrorKey";

static NSString *const kEmoteCommandString = @"/me ";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is probably the time to move kCmdEmote and siblings to MXKRoomDataSource.h.


@interface MXKRoomDataSource ()
{
/**
Expand Down Expand Up @@ -1175,6 +1179,63 @@ - (void)paginateToFillRect:(CGRect)rect direction:(MXTimelineDirection)direction
#pragma mark - Sending
- (void)sendTextMessage:(NSString *)text success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure
{
__block MXEvent *localEchoEvent = nil;

BOOL isEmote = [self isMessageAnEmote:text];
NSString *sanitizedText = [self sanitizedMessageText:text];
NSString *html = [self htmlMessageFromSanitizedText:sanitizedText];

// Make the request to the homeserver
if (isEmote)
{
[_room sendEmote:sanitizedText formattedText:html localEcho:&localEchoEvent success:success failure:failure];
}
else
{
[_room sendTextMessage:sanitizedText formattedText:html localEcho:&localEchoEvent success:success failure:failure];
}

if (localEchoEvent)
{
// Make the data source digest this fake local echo message
[self queueEventForProcessing:localEchoEvent withRoomState:_room.state direction:MXTimelineDirectionForwards];
[self processQueuedEvents:nil];
}
}

- (void)sendReplyToEventWithId:(NSString*)eventIdToReply
withTextMessage:(NSString *)text
success:(void (^)(NSString *))success
failure:(void (^)(NSError *))failure
{
MXEvent *eventToReply = [self eventWithEventId:eventIdToReply];

__block MXEvent *localEchoEvent = nil;

NSString *sanitizedText = [self sanitizedMessageText:text];
NSString *html = [self htmlMessageFromSanitizedText:sanitizedText];

id<MXSendReplyEventStringsLocalizable> stringLocalizations = [MXKSendReplyEventStringLocalizations new];

[_room sendReplyToEvent:eventToReply withTextMessage:sanitizedText formattedTextMessage:html stringLocalizations:stringLocalizations localEcho:&localEchoEvent success:success failure:failure];

if (localEchoEvent)
{
// Make the data source digest this fake local echo message
[self queueEventForProcessing:localEchoEvent withRoomState:_room.state direction:MXTimelineDirectionForwards];
[self processQueuedEvents:nil];
}
}

- (BOOL)isMessageAnEmote:(NSString*)text
{
return [text hasPrefix:kEmoteCommandString];
}

- (NSString*)sanitizedMessageText:(NSString*)rawText
{
NSString *text;

//Remove NULL bytes from the string, as they are likely to trip up many things later,
//including our own C-based Markdown-to-HTML convertor.
//
Expand All @@ -1187,44 +1248,36 @@ - (void)sendTextMessage:(NSString *)text success:(void (^)(NSString *))success f
//
//Even if a future iOS update fixes this,
//we'd better be defensive and always remove occurrences of NULL bytes from text messages.
text = [text stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%C", 0x00000000] withString:@""];

text = [rawText stringByReplacingOccurrencesOfString:[NSString stringWithFormat:@"%C", 0x00000000] withString:@""];
// Check whether the message is an emote
BOOL isEmote = NO;
if ([text hasPrefix:@"/me "])
if ([self isMessageAnEmote:text])
{
isEmote = YES;

// Remove "/me " string
text = [text substringFromIndex:4];
text = [text substringFromIndex:kEmoteCommandString.length];
}

return text;
}

- (NSString*)htmlMessageFromSanitizedText:(NSString*)sanitizedText
{
NSString *html;

// Did user use Markdown text?
NSString *html = [_eventFormatter htmlStringFromMarkdownString:text];
if ([html isEqualToString:text])
NSString *htmlStringFromMarkdown = [_eventFormatter htmlStringFromMarkdownString:sanitizedText];

if ([htmlStringFromMarkdown isEqualToString:sanitizedText])
{
// No formatted string
html = nil;
}

__block MXEvent *localEchoEvent = nil;

// Make the request to the homeserver
if (isEmote)
{
[_room sendEmote:text formattedText:html localEcho:&localEchoEvent success:success failure:failure];
}
else
{
[_room sendTextMessage:text formattedText:html localEcho:&localEchoEvent success:success failure:failure];
html = htmlStringFromMarkdown;
}

if (localEchoEvent)
{
// Make the data source digest this fake local echo message
[self queueEventForProcessing:localEchoEvent withRoomState:_room.state direction:MXTimelineDirectionForwards];
[self processQueuedEvents:nil];
}
return html;
}

- (void)sendImage:(UIImage *)image success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure
Expand All @@ -1251,6 +1304,12 @@ - (void)sendImage:(UIImage *)image success:(void (^)(NSString *))success failure
[self sendImageData:imageData withImageSize:image.size mimeType:mimetype andThumbnail:thumbnail success:success failure:failure];
}

- (BOOL)canReplyToEventWithId:(NSString*)eventIdToReply
{
MXEvent *eventToReply = [self eventWithEventId:eventIdToReply];
return [self.room canReplyToEvent:eventToReply];
}

- (void)sendImage:(NSData *)imageData mimeType:(NSString *)mimetype success:(void (^)(NSString *))success failure:(void (^)(NSError *))failure
{
UIImage *image = [UIImage imageWithData:imageData];
Expand Down
31 changes: 31 additions & 0 deletions MatrixKit/Models/Room/MXKSendReplyEventStringLocalizations.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
Copyright 2018 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/Foundation.h>
#import <MatrixSDK/MXSendReplyEventStringsLocalizable.h>

/**
A `MXKSendReplyEventStringLocalizations` instance represents string localizations used when send reply event to a message in a room.
*/
@interface MXKSendReplyEventStringLocalizations : NSObject<MXSendReplyEventStringsLocalizable>

@property (copy, readonly, nonnull) NSString *senderSentAnImage;
@property (copy, readonly, nonnull) NSString *senderSentAVideo;
@property (copy, readonly, nonnull) NSString *senderSentAnAudioFile;
@property (copy, readonly, nonnull) NSString *senderSentAFile;
@property (copy, readonly, nonnull) NSString *messageToReplyToPrefix;

@end
37 changes: 37 additions & 0 deletions MatrixKit/Models/Room/MXKSendReplyEventStringLocalizations.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
Copyright 2018 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 "MXKSendReplyEventStringLocalizations.h"

#import "NSBundle+MatrixKit.h"

@implementation MXKSendReplyEventStringLocalizations

- (instancetype)init
{
self = [super init];
if (self) {
_senderSentAnImage = [NSBundle mxk_localizedStringForKey:@"message_reply_to_sender_sent_an_image"];
_senderSentAVideo = [NSBundle mxk_localizedStringForKey:@"message_reply_to_sender_sent_a_video"];
_senderSentAnAudioFile = [NSBundle mxk_localizedStringForKey:@"message_reply_to_sender_sent_an_audio_file"];
_senderSentAFile = [NSBundle mxk_localizedStringForKey:@"message_reply_to_sender_sent_a_file"];
_messageToReplyToPrefix = [NSBundle mxk_localizedStringForKey:@"message_reply_to_message_to_reply_to_prefix"];
}
return self;
}

@end