RoomViewController: Show contextual menu toolbar on long press.

This commit is contained in:
SBiOSoftWhare
2019-05-15 23:24:34 +02:00
parent a603de3573
commit 64c43b48c2
2 changed files with 235 additions and 76 deletions
+221 -71
View File
@@ -123,7 +123,7 @@
#import "Riot-Swift.h"
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate>
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate, RoomContextualMenuViewControllerDelegate>
{
// 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<MXKCellRendering>)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<RoomContextualMenuItem*>*)contextualMenuItemsForEvent:(MXEvent*)event andCell:(id<MXKCellRendering>)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<RoomContextualMenuItem*> *actionItems = @[
copyMenuItem,
replyMenuItem,
editMenuItem,
moreMenuItem
];
return actionItems;
}
- (void)showContextualMenuForEvent:(MXEvent*)event cell:(id<MXKCellRendering>)cell animated:(BOOL)animated
{
if (self.roomContextualMenuPresenter.isPresenting)
{
return;
}
[self selectEventWithId:event.eventId enableReplyMode:NO showTimestamp:NO];
NSArray<RoomContextualMenuItem*>* 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