2014-10-02 15:02:47 +00:00
/ *
Copyright 2014 OpenMarket Ltd
2017-03-07 14:40:07 +00:00
Copyright 2017 Vector Creations Ltd
2018-03-30 10:04:50 +00:00
Copyright 2018 New Vector Ltd
2017-07-14 14:41:25 +00:00
2014-10-02 15:02:47 +00:00
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 .
* /
2014-11-17 16:32:46 +00:00
2021-10-14 09:05:28 +00:00
@ import MobileCoreServices ;
2014-10-03 17:26:39 +00:00
# import "RoomViewController.h"
2015-03-31 13:09:17 +00:00
2015-12-11 13:01:56 +00:00
# import "RoomDataSource.h"
2017-06-01 15:20:08 +00:00
# import "RoomBubbleCellData.h"
2015-12-11 13:01:56 +00:00
2015-08-18 08:04:30 +00:00
# import "RoomInputToolbarView.h"
2018-02-21 15:10:38 +00:00
# import "DisabledRoomInputToolbarView.h"
2015-08-18 08:04:30 +00:00
2015-11-24 12:34:08 +00:00
# import "RoomActivitiesView.h"
2015-11-18 13:42:25 +00:00
2016-06-13 15:28:17 +00:00
# import "AttachmentsViewController.h"
2017-08-14 18:01:59 +00:00
# import "EventDetailsView.h"
2016-02-10 09:59:48 +00:00
# import "RoomAvatarTitleView.h"
# import "ExpandedRoomTitleView.h"
2016-03-04 13:14:08 +00:00
# import "SimpleRoomTitleView.h"
2016-04-14 00:34:30 +00:00
# import "PreviewRoomTitleView.h"
2015-11-30 13:34:12 +00:00
2016-05-03 21:30:46 +00:00
# import "RoomMemberDetailsViewController.h"
2016-08-26 14:00:32 +00:00
# import "ContactDetailsViewController.h"
2015-09-11 11:03:20 +00:00
2015-11-26 15:47:24 +00:00
# import "SegmentedViewController.h"
# import "RoomSettingsViewController.h"
2016-10-12 10:03:39 +00:00
# import "RoomFilesViewController.h"
2015-12-31 09:12:45 +00:00
# import "RoomSearchViewController.h"
2015-11-20 13:12:37 +00:00
2017-02-14 17:21:46 +00:00
# import "UsersDevicesViewController.h"
2017-06-21 18:28:16 +00:00
# import "ReadReceiptsViewController.h"
2017-08-04 11:47:50 +00:00
# import "JitsiViewController.h"
2017-06-13 15:43:32 +00:00
# import "RoomEmptyBubbleCell.h"
2017-07-07 09:11:42 +00:00
# import "RoomMembershipExpandedBubbleCell.h"
2017-03-09 10:18:18 +00:00
# import "MXKRoomBubbleTableViewCell+Riot.h"
2015-12-07 10:50:13 +00:00
2015-12-04 13:28:15 +00:00
# import "AvatarGenerator.h"
2016-04-29 14:19:13 +00:00
# import "Tools.h"
2017-08-04 11:47:50 +00:00
# import "WidgetManager.h"
2021-10-14 09:57:32 +00:00
# import "ShareManager.h"
2015-12-04 13:28:15 +00:00
2016-04-26 07:49:48 +00:00
# import "GBDeviceInfo_iOS.h"
2016-11-16 08:31:58 +00:00
# import "RoomEncryptedDataBubbleCell.h"
# import "EncryptionInfoView.h"
2017-04-18 13:55:51 +00:00
# import "MXRoom+Riot.h"
2017-09-15 11:27:13 +00:00
# import "IntegrationManagerViewController.h"
2017-09-27 13:21:38 +00:00
# import "WidgetPickerViewController.h"
2018-05-24 13:31:27 +00:00
# import "StickerPickerViewController.h"
2017-09-15 11:27:13 +00:00
2018-06-08 15:21:52 +00:00
# import "EventFormatter.h"
2021-02-02 10:31:21 +00:00
# import "SettingsViewController.h"
# import "SecurityViewController.h"
2021-04-07 10:26:51 +00:00
# import "TypingUserInfo.h"
2021-08-16 16:48:26 +00:00
# import "MXSDKOptions.h"
2022-01-20 16:09:34 +00:00
# import "RoomTimelineCellProvider.h"
2022-01-14 09:23:15 +00:00
2021-10-26 15:42:33 +00:00
# import "GeneratedInterface-Swift.h"
2018-07-02 12:51:47 +00:00
2021-02-09 21:17:43 +00:00
NSNotificationName const RoomCallTileTappedNotification = @ "RoomCallTileTappedNotification" ;
2021-03-17 16:41:31 +00:00
NSNotificationName const RoomGroupCallTileTappedNotification = @ "RoomGroupCallTileTappedNotification" ;
2021-04-08 10:11:36 +00:00
const NSTimeInterval kResizeComposerAnimationDuration = .05 ;
2021-01-21 11:27:32 +00:00
2019-09-19 15:21:03 +00:00
@ interface RoomViewController ( ) < UISearchBarDelegate , UIGestureRecognizerDelegate , UIScrollViewAccessibilityDelegate , RoomTitleViewTapGestureDelegate , RoomParticipantsViewControllerDelegate , MXKRoomMemberDetailsViewControllerDelegate , ContactsTableViewControllerDelegate , MXServerNoticesDelegate , RoomContextualMenuViewControllerDelegate ,
2019-07-30 15:18:39 +00:00
ReactionsMenuViewModelCoordinatorDelegate , EditHistoryCoordinatorBridgePresenterDelegate , MXKDocumentPickerPresenterDelegate , EmojiPickerCoordinatorBridgePresenterDelegate ,
2020-01-22 15:27:04 +00:00
ReactionHistoryCoordinatorBridgePresenterDelegate , CameraPresenterDelegate , MediaPickerCoordinatorBridgePresenterDelegate ,
2021-10-05 11:30:31 +00:00
RoomDataSourceDelegate , RoomCreationModalCoordinatorBridgePresenterDelegate , RoomInfoCoordinatorBridgePresenterDelegate , DialpadViewControllerDelegate , RemoveJitsiWidgetViewDelegate , VoiceMessageControllerDelegate , SpaceDetailPresenterDelegate , UserSuggestionCoordinatorBridgeDelegate >
2015-06-16 11:54:12 +00:00
{
2016-02-10 09:59:48 +00:00
2016-04-14 00:34:30 +00:00
// The preview header
2021-10-28 15:41:16 +00:00
__weak PreviewRoomTitleView * previewHeader ;
2016-04-14 00:34:30 +00:00
2015-12-11 13:01:56 +00:00
// The customized room data source for Vector
RoomDataSource * customizedRoomDataSource ;
2016-08-26 14:00:32 +00:00
// The user taps on a user id contained in a message
MXKContact * selectedContact ;
2017-07-14 14:41:25 +00:00
2015-11-18 08:38:55 +00:00
// List of members who are typing in the room .
NSArray * currentTypingUsers ;
// Typing notifications listener .
2021-10-28 15:41:16 +00:00
__weak id typingNotifListener ;
2016-04-06 07:41:23 +00:00
2016-04-20 12:09:14 +00:00
// The position of the first touch down event stored in case of scrolling when the expanded header is visible .
CGPoint startScrollingPoint ;
2016-05-19 15:22:29 +00:00
2016-08-31 18:57:08 +00:00
// Missed discussions badge
2016-08-31 13:12:46 +00:00
NSUInteger missedDiscussionsCount ;
2016-09-05 12:00:56 +00:00
NSUInteger missedHighlightCount ;
2016-08-31 13:12:46 +00:00
UILabel * missedDiscussionsBadgeLabel ;
2021-03-24 21:17:09 +00:00
UIView * missedDiscussionsDotView ;
2016-08-31 13:12:46 +00:00
2016-11-16 08:31:58 +00:00
// Potential encryption details view .
2021-10-28 15:41:16 +00:00
__weak EncryptionInfoView * encryptionInfoView ;
2017-07-14 14:41:25 +00:00
2017-02-14 17:21:46 +00:00
// The list of unknown devices that prevent outgoing messages from being sent
MXUsersDevicesMap < MXDeviceInfo * > * unknownDevices ;
2016-11-16 08:31:58 +00:00
2016-05-19 15:22:29 +00:00
// Observe kAppDelegateDidTapStatusBarNotification to handle tap on clock status bar .
2021-10-28 15:41:16 +00:00
__weak id kAppDelegateDidTapStatusBarNotificationObserver ;
2016-08-11 08:38:10 +00:00
// Observe kAppDelegateNetworkStatusDidChangeNotification to handle network status change .
2021-10-28 15:41:16 +00:00
__weak id kAppDelegateNetworkStatusDidChangeNotificationObserver ;
2018-08-21 16:25:56 +00:00
// Observers to manage MXSession state ( and sync errors )
2021-10-28 15:41:16 +00:00
__weak id kMXSessionStateDidChangeObserver ;
2018-08-21 16:25:56 +00:00
2016-08-08 16:40:22 +00:00
// Observers to manage ongoing conference call banner
2021-10-28 15:41:16 +00:00
__weak id kMXCallStateDidChangeObserver ;
__weak id kMXCallManagerConferenceStartedObserver ;
__weak id kMXCallManagerConferenceFinishedObserver ;
2017-08-09 15:31:15 +00:00
// Observers to manage widgets
2021-10-28 15:41:16 +00:00
__weak id kMXKWidgetManagerDidUpdateWidgetObserver ;
2016-08-31 13:12:46 +00:00
2017-04-18 13:55:51 +00:00
// Observer kMXRoomSummaryDidChangeNotification to keep updated the missed discussion count
2021-10-28 15:41:16 +00:00
__weak id mxRoomSummaryDidChangeObserver ;
2018-06-12 16:21:04 +00:00
// Observer for removing the re - request explanation / waiting dialog
2021-10-28 15:41:16 +00:00
__weak id mxEventDidDecryptNotificationObserver ;
2017-06-01 15:20:08 +00:00
// The table view cell in which the read marker is displayed ( nil by default ) .
MXKRoomBubbleTableViewCell * readMarkerTableViewCell ;
// Tell whether the view controller is appeared or not .
BOOL isAppeared ;
2017-06-22 08:51:40 +00:00
2021-03-18 10:02:42 +00:00
// Tell whether the room has a Jitsi call or not .
BOOL hasJitsiCall ;
2017-09-15 11:27:13 +00:00
// The right bar button items back up .
NSArray < UIBarButtonItem * > * rightBarButtonItems ;
2019-01-11 09:32:56 +00:00
// Observe kThemeServiceDidChangeThemeNotification to handle user interface theme change .
2021-10-28 15:41:16 +00:00
__weak id kThemeServiceDidChangeThemeNotificationObserver ;
2018-07-23 14:56:03 +00:00
2021-09-07 10:20:59 +00:00
// Observe URL preview updates to refresh cells .
2021-10-28 15:41:16 +00:00
__weak id URLPreviewDidUpdateNotificationObserver ;
2021-09-07 10:20:59 +00:00
2018-07-27 09:56:51 +00:00
// Listener for ` m . room . tombstone` event type
2021-10-28 15:41:16 +00:00
__weak id tombstoneEventNotificationsListener ;
2018-10-02 13:30:00 +00:00
// Homeserver notices
MXServerNotices * serverNotices ;
2020-07-14 12:39:04 +00:00
// Formatted body parser for events
FormattedBodyParser * formattedBodyParser ;
2020-11-11 21:49:46 +00:00
// Time to display notification content in the timeline
MXTaskProfile * notificationTaskProfile ;
2014-10-14 15:56:03 +00:00
}
2014-10-02 15:02:47 +00:00
2019-05-15 21:24:34 +00:00
@ property ( nonatomic , weak ) IBOutlet UIView * overlayContainerView ;
2021-04-23 15:36:51 +00:00
@ property ( nonatomic , strong ) RemoveJitsiWidgetView * removeJitsiWidgetView ;
2019-05-15 21:24:34 +00:00
2019-06-27 12:37:17 +00:00
@ property ( nonatomic , strong ) RoomContextualMenuViewController * roomContextualMenuViewController ;
2019-05-15 21:24:34 +00:00
@ property ( nonatomic , strong ) RoomContextualMenuPresenter * roomContextualMenuPresenter ;
2019-06-11 15:43:32 +00:00
@ property ( nonatomic , strong ) MXKErrorAlertPresentation * errorPresenter ;
2019-06-13 14:38:20 +00:00
@ property ( nonatomic , strong ) NSString * textMessageBeforeEditing ;
2019-07-04 17:24:15 +00:00
@ property ( nonatomic , strong ) EditHistoryCoordinatorBridgePresenter * editHistoryPresenter ;
2019-07-11 17:17:55 +00:00
@ property ( nonatomic , strong ) MXKDocumentPickerPresenter * documentPickerPresenter ;
2019-07-25 14:46:35 +00:00
@ property ( nonatomic , strong ) EmojiPickerCoordinatorBridgePresenter * emojiPickerCoordinatorBridgePresenter ;
2019-07-30 15:18:39 +00:00
@ property ( nonatomic , strong ) ReactionHistoryCoordinatorBridgePresenter * reactionHistoryCoordinatorBridgePresenter ;
2019-08-02 15:19:29 +00:00
@ property ( nonatomic , strong ) CameraPresenter * cameraPresenter ;
@ property ( nonatomic , strong ) MediaPickerCoordinatorBridgePresenter * mediaPickerPresenter ;
2019-12-03 18:58:51 +00:00
@ property ( nonatomic , strong ) RoomMessageURLParser * roomMessageURLParser ;
2020-09-14 16:44:30 +00:00
@ property ( nonatomic , strong ) RoomCreationModalCoordinatorBridgePresenter * roomCreationModalCoordinatorBridgePresenter ;
2020-09-18 15:57:40 +00:00
@ property ( nonatomic , strong ) RoomInfoCoordinatorBridgePresenter * roomInfoCoordinatorBridgePresenter ;
2021-01-14 10:38:17 +00:00
@ property ( nonatomic , strong ) CustomSizedPresentationController * customSizedPresentationController ;
2021-03-15 13:27:59 +00:00
@ property ( nonatomic , getter = isActivitiesViewExpanded ) BOOL activitiesViewExpanded ;
2021-03-20 20:31:17 +00:00
@ property ( nonatomic , getter = isScrollToBottomHidden ) BOOL scrollToBottomHidden ;
2021-08-16 13:44:44 +00:00
@ property ( nonatomic , getter = isMissedDiscussionsBadgeHidden ) BOOL missedDiscussionsBadgeHidden ;
2019-05-15 21:24:34 +00:00
2021-07-06 13:17:22 +00:00
@ property ( nonatomic , strong ) VoiceMessageController * voiceMessageController ;
2021-09-22 12:58:19 +00:00
@ property ( nonatomic , strong ) SpaceDetailPresenter * spaceDetailPresenter ;
2021-06-07 07:20:26 +00:00
2021-10-14 09:57:32 +00:00
@ property ( nonatomic , strong ) ShareManager * shareManager ;
2021-10-14 09:05:28 +00:00
2021-10-04 08:37:37 +00:00
@ property ( nonatomic , strong ) UserSuggestionCoordinatorBridge * userSuggestionCoordinator ;
@ property ( nonatomic , weak ) IBOutlet UIView * userSuggestionContainerView ;
2021-12-02 14:25:45 +00:00
@ property ( nonatomic ) AnalyticsScreenTimer * screenTimer ;
2014-10-02 15:02:47 +00:00
@ end
2014-10-03 17:26:39 +00:00
@ implementation RoomViewController
2016-06-10 15:15:42 +00:00
@ synthesize roomPreviewData ;
2014-10-02 15:02:47 +00:00
2016-02-10 09:59:48 +00:00
# pragma mark - Class methods
+ ( UINib * ) nib
{
return [ UINib nibWithNibName : NSStringFromClass ( self . class )
bundle : [ NSBundle bundleForClass : self . class ] ] ;
}
+ ( instancetype ) roomViewController
{
return [ [ [ self class ] alloc ] initWithNibName : NSStringFromClass ( self . class )
bundle : [ NSBundle bundleForClass : self . class ] ] ;
}
2021-09-07 15:43:49 +00:00
+ ( instancetype ) instantiate
{
UIStoryboard * storyboard = [ UIStoryboard storyboardWithName : @ "Main" bundle : [ NSBundle mainBundle ] ] ;
return [ storyboard instantiateViewControllerWithIdentifier : @ "RoomViewControllerStoryboardId" ] ;
}
2016-02-10 09:59:48 +00:00
# pragma mark -
2016-04-14 00:34:30 +00:00
- ( instancetype ) initWithNibName : ( nullable NSString * ) nibNameOrNil bundle : ( nullable NSBundle * ) nibBundleOrNil
{
self = [ super initWithNibName : nibNameOrNil bundle : nibBundleOrNil ] ;
if ( self )
{
// Disable auto join
self . autoJoinInvitedRoom = NO ;
2016-09-08 12:34:10 +00:00
// Disable auto scroll to bottom on keyboard presentation
self . scrollHistoryToTheBottomOnKeyboardPresentation = NO ;
2016-04-14 00:34:30 +00:00
}
return self ;
}
- ( nullable instancetype ) initWithCoder : ( NSCoder * ) aDecoder
{
self = [ super initWithCoder : aDecoder ] ;
if ( self )
{
// Disable auto join
self . autoJoinInvitedRoom = NO ;
2016-09-08 12:34:10 +00:00
// Disable auto scroll to bottom on keyboard presentation
self . scrollHistoryToTheBottomOnKeyboardPresentation = NO ;
2016-04-14 00:34:30 +00:00
}
return self ;
}
# pragma mark -
2017-01-03 13:40:23 +00:00
- ( void ) finalizeInit
2015-06-16 11:54:12 +00:00
{
2017-01-03 13:40:23 +00:00
[ super finalizeInit ] ;
2015-02-23 14:46:57 +00:00
2021-04-08 10:11:36 +00:00
self . resizeComposerAnimationDuration = kResizeComposerAnimationDuration ;
2021-04-07 07:32:57 +00:00
2017-01-03 13:40:23 +00:00
// Setup ` MXKViewControllerHandling` properties
2016-02-10 21:12:17 +00:00
self . enableBarTintColorStatusChange = NO ;
2017-01-03 13:40:23 +00:00
self . rageShakeManager = [ RageShakeManager sharedManager ] ;
2020-07-14 12:39:04 +00:00
formattedBodyParser = [ FormattedBodyParser new ] ;
2017-07-14 14:41:25 +00:00
2018-01-16 22:49:04 +00:00
_showMissedDiscussionsBadge = YES ;
2021-03-20 20:31:17 +00:00
_scrollToBottomHidden = YES ;
2018-01-16 22:49:04 +00:00
2017-02-14 10:01:38 +00:00
// Listen to the event sent state changes
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( eventDidChangeSentState : ) name : kMXEventDidChangeSentStateNotification object : nil ] ;
2019-06-27 10:25:01 +00:00
[ [ NSNotificationCenter defaultCenter ] addObserver : self selector : @ selector ( eventDidChangeIdentifier : ) name : kMXEventDidChangeIdentifierNotification object : nil ] ;
2020-12-08 21:01:46 +00:00
// Show / hide actions button in document preview according BuildSettings
self . allowActionsInDocumentPreview = BuildSettings . messageDetailsAllowShare ;
2021-06-07 07:20:26 +00:00
2021-06-23 14:52:08 +00:00
_voiceMessageController = [ [ VoiceMessageController alloc ] initWithThemeService : ThemeService . shared mediaServiceProvider : VoiceMessageMediaServiceProvider . sharedProvider ] ;
2021-06-07 07:20:26 +00:00
self . voiceMessageController . delegate = self ;
2021-12-02 14:25:45 +00:00
self . screenTimer = [ [ AnalyticsScreenTimer alloc ] initWithScreen : AnalyticsScreenRoom ] ;
2017-01-03 13:40:23 +00:00
}
- ( void ) viewDidLoad
{
[ super viewDidLoad ] ;
2016-02-10 21:12:17 +00:00
2015-12-07 12:57:21 +00:00
// Register first customized cell view classes used to render bubbles
2022-01-14 09:23:15 +00:00
[ [ RoomTimelineConfiguration shared ] . currentStyle . cellProvider registerCellsForTableView : self . bubblesTableView ] ;
2021-11-19 15:40:52 +00:00
2020-10-19 19:37:28 +00:00
[ self vc_removeBackTitle ] ;
2021-10-01 12:43:07 +00:00
// Display leftBarButtonItems or leftBarButtonItem to the right of the Back button
self . navigationItem . leftItemsSupplementBackButton = YES ;
2021-04-23 15:36:51 +00:00
[ self setupRemoveJitsiWidgetRemoveView ] ;
2015-08-18 08:04:30 +00:00
// Replace the default input toolbar view .
2015-12-07 12:57:21 +00:00
// Note : this operation will force the layout of subviews . That is why cell view classes must be registered before .
2018-08-27 13:32:37 +00:00
[ self updateRoomInputToolbarViewClassIfNeeded ] ;
2015-04-01 22:25:41 +00:00
2015-11-18 13:42:25 +00:00
// set extra area
2015-11-24 12:34:08 +00:00
[ self setRoomActivitiesViewClass : RoomActivitiesView . class ] ;
2015-11-18 13:42:25 +00:00
2016-06-13 15:28:17 +00:00
// Custom the attachmnet viewer
[ self setAttachmentsViewerClass : AttachmentsViewController . class ] ;
2017-08-14 18:01:59 +00:00
// Custom the event details view
[ self setEventDetailsViewClass : EventDetailsView . class ] ;
2018-01-16 22:49:04 +00:00
// Prepare missed dicussion badge ( if any )
self . showMissedDiscussionsBadge = _showMissedDiscussionsBadge ;
2016-08-31 13:12:46 +00:00
2016-04-18 13:22:57 +00:00
// Set up the room title view according to the data source ( if any )
[ self refreshRoomTitle ] ;
// Refresh tool bar if the room data source is set .
2015-11-18 14:59:04 +00:00
if ( self . roomDataSource )
{
2016-03-22 16:23:44 +00:00
[ self refreshRoomInputToolbar ] ;
}
2017-07-21 09:28:17 +00:00
2019-05-15 21:24:34 +00:00
self . roomContextualMenuPresenter = [ RoomContextualMenuPresenter new ] ;
2019-06-11 15:43:32 +00:00
self . errorPresenter = [ MXKErrorAlertPresentation new ] ;
2019-12-03 18:58:51 +00:00
self . roomMessageURLParser = [ RoomMessageURLParser new ] ;
2019-05-15 21:24:34 +00:00
2021-09-28 05:40:01 +00:00
self . jumpToLastUnreadLabel . text = [ VectorL10n roomJumpToFirstUnread ] ;
2020-07-10 12:47:37 +00:00
2021-10-28 15:41:16 +00:00
MXWeakify ( self ) ;
2017-07-21 09:28:17 +00:00
// Observe user interface theme change .
2019-01-11 09:32:56 +00:00
kThemeServiceDidChangeThemeNotificationObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kThemeServiceDidChangeThemeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2017-07-21 09:28:17 +00:00
2021-10-28 15:41:16 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2017-07-21 09:28:17 +00:00
[ self userInterfaceThemeDidChange ] ;
} ] ;
2021-10-04 08:37:37 +00:00
2017-07-21 09:28:17 +00:00
[ self userInterfaceThemeDidChange ] ;
2021-03-25 21:15:18 +00:00
2021-09-07 10:20:59 +00:00
// Observe URL preview updates .
2021-09-08 14:10:13 +00:00
[ self registerURLPreviewNotifications ] ;
2021-09-07 10:20:59 +00:00
2021-03-25 21:15:18 +00:00
[ self setupActions ] ;
2017-07-21 09:28:17 +00:00
}
- ( void ) userInterfaceThemeDidChange
{
2019-02-15 14:21:33 +00:00
// Consider the main navigation controller if the current view controller is embedded inside a split view controller .
UINavigationController * mainNavigationController = self . navigationController ;
if ( self . splitViewController . isCollapsed && self . splitViewController . viewControllers . count )
{
mainNavigationController = self . splitViewController . viewControllers . firstObject ;
}
2019-01-11 10:45:27 +00:00
[ ThemeService . shared . theme applyStyleOnNavigationBar : self . navigationController . navigationBar ] ;
2019-02-15 14:21:33 +00:00
if ( mainNavigationController )
{
[ ThemeService . shared . theme applyStyleOnNavigationBar : mainNavigationController . navigationBar ] ;
}
2021-03-15 13:27:59 +00:00
2019-02-15 14:21:33 +00:00
// Keep navigation bar transparent in some cases
2020-09-18 11:11:45 +00:00
if ( ! self . previewHeaderContainer . hidden )
2019-02-15 14:21:33 +00:00
{
self . navigationController . navigationBar . translucent = YES ;
mainNavigationController . navigationBar . translucent = YES ;
}
2020-08-27 09:29:39 +00:00
[ self . inputToolbarView customizeViewRendering ] ;
2019-01-11 10:45:27 +00:00
self . activityIndicator . backgroundColor = ThemeService . shared . theme . overlayBackgroundColor ;
2017-08-11 14:56:09 +00:00
2021-04-23 15:36:51 +00:00
[ self . removeJitsiWidgetView updateWithTheme : ThemeService . shared . theme ] ;
2017-08-11 14:56:09 +00:00
// Prepare jump to last unread banner
2021-04-26 22:37:41 +00:00
self . jumpToLastUnreadImageView . tintColor = ThemeService . shared . theme . tintColor ;
2020-07-10 12:47:37 +00:00
self . jumpToLastUnreadLabel . textColor = ThemeService . shared . theme . textPrimaryColor ;
2017-08-11 14:56:09 +00:00
2019-01-11 10:45:27 +00:00
self . previewHeaderContainer . backgroundColor = ThemeService . shared . theme . headerBackgroundColor ;
2017-08-11 14:56:09 +00:00
// Check the table view style to select its bg color .
2019-01-11 10:45:27 +00:00
self . bubblesTableView . backgroundColor = ( ( self . bubblesTableView . style = = UITableViewStylePlain ) ? ThemeService . shared . theme . backgroundColor : ThemeService . shared . theme . headerBackgroundColor ) ;
2019-02-18 11:53:13 +00:00
self . bubblesTableView . separatorColor = ThemeService . shared . theme . lineBreakColor ;
2017-08-11 14:56:09 +00:00
self . view . backgroundColor = self . bubblesTableView . backgroundColor ;
if ( self . bubblesTableView . dataSource )
{
[ self . bubblesTableView reloadData ] ;
}
2021-03-15 13:27:59 +00:00
2021-04-26 22:37:41 +00:00
[ self . scrollToBottomButton vc_addShadowWithColor : ThemeService . shared . theme . shadowColor
offset : CGSizeMake ( 0 , 4 )
radius : 6
opacity : 0.2 ] ;
2021-03-20 20:31:17 +00:00
2021-04-07 18:15:09 +00:00
self . inputBackgroundView . backgroundColor = [ ThemeService . shared . theme . backgroundColor colorWithAlphaComponent : 0.98 ] ;
2021-03-23 16:15:31 +00:00
2021-04-27 10:49:56 +00:00
if ( ThemeService . shared . isCurrentThemeDark )
2021-03-20 20:31:17 +00:00
{
[ self . scrollToBottomButton setImage : [ UIImage imageNamed : @ "scrolldown_dark" ] forState : UIControlStateNormal ] ;
2021-04-27 10:49:56 +00:00
2021-05-04 16:33:56 +00:00
self . jumpToLastUnreadBanner . backgroundColor = ThemeService . shared . theme . colors . navigation ;
2021-04-27 10:49:56 +00:00
[ self . jumpToLastUnreadBanner vc_removeShadow ] ;
2021-05-04 16:33:56 +00:00
self . resetReadMarkerButton . tintColor = ThemeService . shared . theme . colors . quarterlyContent ;
2021-03-20 20:31:17 +00:00
}
2021-04-27 10:49:56 +00:00
else
{
[ self . scrollToBottomButton setImage : [ UIImage imageNamed : @ "scrolldown" ] forState : UIControlStateNormal ] ;
2021-05-04 16:33:56 +00:00
self . jumpToLastUnreadBanner . backgroundColor = ThemeService . shared . theme . colors . background ;
2021-04-27 10:49:56 +00:00
[ self . jumpToLastUnreadBanner vc_addShadowWithColor : ThemeService . shared . theme . shadowColor
offset : CGSizeMake ( 0 , 4 )
2021-04-27 11:13:31 +00:00
radius : 8
opacity : 0.1 ] ;
2021-05-04 16:33:56 +00:00
self . resetReadMarkerButton . tintColor = ThemeService . shared . theme . colors . tertiaryContent ;
2021-03-20 20:31:17 +00:00
}
self . scrollToBottomBadgeLabel . badgeColor = ThemeService . shared . theme . tintColor ;
2020-04-08 10:58:12 +00:00
[ self setNeedsStatusBarAppearanceUpdate ] ;
2017-08-11 14:56:09 +00:00
}
- ( UIStatusBarStyle ) preferredStatusBarStyle
{
2019-01-11 10:45:27 +00:00
return ThemeService . shared . theme . statusBarStyle ;
2014-10-02 15:02:47 +00:00
}
2015-06-16 11:54:12 +00:00
- ( void ) didReceiveMemoryWarning
{
2014-10-02 15:02:47 +00:00
[ super didReceiveMemoryWarning ] ;
// Dispose of any resources that can be recreated .
}
2015-06-16 11:54:12 +00:00
- ( void ) viewWillAppear : ( BOOL ) animated
{
2014-10-13 16:53:33 +00:00
[ super viewWillAppear : animated ] ;
2021-03-15 13:27:59 +00:00
2016-04-18 13:22:57 +00:00
// Refresh the room title view
[ self refreshRoomTitle ] ;
2021-04-23 15:36:51 +00:00
// refresh remove Jitsi widget view
[ self refreshRemoveJitsiWidgetView ] ;
2016-04-18 13:22:57 +00:00
// Refresh tool bar if the room data source is set .
if ( self . roomDataSource )
{
[ self refreshRoomInputToolbar ] ;
}
2021-04-06 11:09:30 +00:00
// Reset typing notification in order to remove the allocated space
if ( [ self . roomDataSource isKindOfClass : RoomDataSource . class ] )
{
[ ( ( RoomDataSource * ) self . roomDataSource ) resetTypingNotification ] ;
}
2015-11-18 08:38:55 +00:00
[ self listenTypingNotifications ] ;
2016-08-09 10:47:52 +00:00
[ self listenCallNotifications ] ;
2017-08-09 15:31:15 +00:00
[ self listenWidgetNotifications ] ;
2018-07-27 09:56:51 +00:00
[ self listenTombstoneEventNotifications ] ;
2018-08-21 16:25:56 +00:00
[ self listenMXSessionStateChangeNotifications ] ;
2016-04-18 14:56:44 +00:00
2021-10-28 15:41:16 +00:00
MXWeakify ( self ) ;
2016-08-11 08:38:10 +00:00
// Observe kAppDelegateDidTapStatusBarNotification .
2016-05-19 15:22:29 +00:00
kAppDelegateDidTapStatusBarNotificationObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kAppDelegateDidTapStatusBarNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2021-10-28 15:41:16 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-09-01 09:44:15 +00:00
[ self setBubbleTableViewContentOffset : CGPointMake ( - self . bubblesTableView . adjustedContentInset . left , - self . bubblesTableView . adjustedContentInset . top ) animated : YES ] ;
2016-05-19 15:22:29 +00:00
} ] ;
2020-09-23 13:53:40 +00:00
if ( [ self . roomDataSource . roomId isEqualToString : [ LegacyAppDelegate theDelegate ] . lastNavigatedRoomIdFromPush ] )
{
[ self startActivityIndicator ] ;
[ self . roomDataSource reload ] ;
[ LegacyAppDelegate theDelegate ] . lastNavigatedRoomIdFromPush = nil ;
2020-11-11 21:49:46 +00:00
2021-12-02 14:25:45 +00:00
notificationTaskProfile = [ MXSDKOptions . sharedInstance . profiler startMeasuringTaskWithName : MXTaskProfileNameNotificationsOpenEvent ] ;
2020-09-23 13:53:40 +00:00
}
2014-10-13 16:53:33 +00:00
}
2015-06-16 11:54:12 +00:00
- ( void ) viewWillDisappear : ( BOOL ) animated
{
2014-10-13 16:53:33 +00:00
[ super viewWillDisappear : animated ] ;
2014-11-04 16:26:33 +00:00
// hide action
2016-05-03 16:29:54 +00:00
if ( currentAlert )
2015-08-19 09:05:47 +00:00
{
2017-07-14 14:41:25 +00:00
[ currentAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-05-03 16:29:54 +00:00
currentAlert = nil ;
2015-08-19 09:05:47 +00:00
}
2015-11-18 08:38:55 +00:00
[ self removeTypingNotificationsListener ] ;
2015-12-11 13:01:56 +00:00
if ( customizedRoomDataSource )
{
2016-04-06 07:41:23 +00:00
// Cancel potential selected event ( to leave edition mode )
if ( customizedRoomDataSource . selectedEventId )
{
[ self cancelEventSelection ] ;
}
2015-12-11 13:01:56 +00:00
}
2016-02-10 09:59:48 +00:00
2020-09-18 11:11:45 +00:00
// Hide preview header to restore navigation bar settings
2016-04-14 00:34:30 +00:00
[ self showPreviewHeader : NO ] ;
2016-05-19 15:22:29 +00:00
if ( kAppDelegateDidTapStatusBarNotificationObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : kAppDelegateDidTapStatusBarNotificationObserver ] ;
kAppDelegateDidTapStatusBarNotificationObserver = nil ;
}
2017-07-14 14:41:25 +00:00
2016-08-09 10:47:52 +00:00
[ self removeCallNotificationsListeners ] ;
2017-08-09 15:31:15 +00:00
[ self removeWidgetNotificationsListeners ] ;
2018-07-27 09:56:51 +00:00
[ self removeTombstoneEventNotificationsListener ] ;
2018-08-21 16:25:56 +00:00
[ self removeMXSessionStateChangeNotificationsListener ] ;
2021-03-15 13:27:59 +00:00
2017-06-01 15:20:08 +00:00
// Re - enable the read marker display , and disable its update .
self . roomDataSource . showReadMarker = YES ;
self . updateRoomReadMarker = NO ;
isAppeared = NO ;
2021-06-25 09:09:41 +00:00
2021-09-21 14:52:53 +00:00
[ VoiceMessageMediaServiceProvider . sharedProvider pauseAllServices ] ;
2014-10-13 16:53:33 +00:00
}
2015-06-16 11:54:12 +00:00
- ( void ) viewDidAppear : ( BOOL ) animated
{
2014-10-14 15:56:03 +00:00
[ super viewDidAppear : animated ] ;
2017-06-06 11:31:58 +00:00
isAppeared = YES ;
[ self checkReadMarkerVisibility ] ;
2015-06-16 11:54:12 +00:00
if ( self . roomDataSource )
{
2015-05-12 12:29:42 +00:00
// Set visible room id
2015-11-17 23:15:52 +00:00
[ AppDelegate theDelegate ] . visibleRoomId = self . roomDataSource . roomId ;
2016-08-11 08:38:10 +00:00
}
2021-10-28 15:41:16 +00:00
MXWeakify ( self ) ;
2016-08-11 08:38:10 +00:00
// Observe network reachability
kAppDelegateNetworkStatusDidChangeNotificationObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kAppDelegateNetworkStatusDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2016-04-01 14:35:55 +00:00
2021-10-28 15:41:16 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2016-04-01 14:35:55 +00:00
[ self refreshActivitiesViewDisplay ] ;
2016-08-11 08:38:10 +00:00
} ] ;
[ self refreshActivitiesViewDisplay ] ;
2017-06-01 15:20:08 +00:00
[ self refreshJumpToLastUnreadBannerDisplay ] ;
2016-08-31 13:12:46 +00:00
// Observe missed notifications
2017-04-18 13:55:51 +00:00
mxRoomSummaryDidChangeObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXRoomSummaryDidChangeNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2021-03-15 13:27:59 +00:00
2021-10-28 15:41:16 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2018-07-23 16:14:45 +00:00
MXRoomSummary * roomSummary = notif . object ;
2021-03-15 13:27:59 +00:00
2018-07-23 16:14:45 +00:00
if ( [ roomSummary . roomId isEqualToString : self . roomDataSource . roomId ] )
{
[ self refreshMissedDiscussionsCount : NO ] ;
}
2016-08-31 13:12:46 +00:00
} ] ;
[ self refreshMissedDiscussionsCount : YES ] ;
2020-04-17 19:38:52 +00:00
self . keyboardHeight = MAX ( self . keyboardHeight , 0 ) ;
2021-03-18 10:02:42 +00:00
if ( hasJitsiCall &&
2021-09-07 15:43:49 +00:00
! self . isRoomHavingAJitsiCall )
2021-03-18 10:02:42 +00:00
{
// the room had a Jitsi call before , but not now
hasJitsiCall = NO ;
[ self reloadBubblesTable : YES ] ;
}
2021-12-02 14:25:45 +00:00
// Screen tracking
[ self . screenTimer start ] ;
2014-10-14 15:56:03 +00:00
}
2015-06-16 11:54:12 +00:00
- ( void ) viewDidDisappear : ( BOOL ) animated
{
2014-12-09 09:39:56 +00:00
[ super viewDidDisappear : animated ] ;
2019-05-15 21:24:34 +00:00
// Hide contextual menu if needed
[ self hideContextualMenuAnimated : NO ] ;
2014-12-09 09:39:56 +00:00
// Reset visible room id
2016-08-11 08:38:10 +00:00
[ AppDelegate theDelegate ] . visibleRoomId = nil ;
if ( kAppDelegateNetworkStatusDidChangeNotificationObserver )
2016-04-01 14:35:55 +00:00
{
2016-08-11 08:38:10 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : kAppDelegateNetworkStatusDidChangeNotificationObserver ] ;
kAppDelegateNetworkStatusDidChangeNotificationObserver = nil ;
2016-04-01 14:35:55 +00:00
}
2016-08-31 13:12:46 +00:00
2017-04-18 13:55:51 +00:00
if ( mxRoomSummaryDidChangeObserver )
2016-08-31 13:12:46 +00:00
{
2017-04-18 13:55:51 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : mxRoomSummaryDidChangeObserver ] ;
mxRoomSummaryDidChangeObserver = nil ;
2016-08-31 13:12:46 +00:00
}
2021-03-15 13:27:59 +00:00
2018-06-12 16:21:04 +00:00
if ( mxEventDidDecryptNotificationObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : mxEventDidDecryptNotificationObserver ] ;
mxEventDidDecryptNotificationObserver = nil ;
}
2021-09-07 15:43:49 +00:00
if ( self . isRoomHavingAJitsiCall )
2021-03-18 10:02:42 +00:00
{
hasJitsiCall = YES ;
[ self reloadBubblesTable : YES ] ;
}
2021-12-02 14:25:45 +00:00
[ self . screenTimer stop ] ;
2014-12-09 09:39:56 +00:00
}
2015-08-26 17:04:30 +00:00
- ( void ) viewDidLayoutSubviews
{
2016-09-12 08:57:44 +00:00
[ super viewDidLayoutSubviews ] ;
2015-08-26 17:04:30 +00:00
UIEdgeInsets contentInset = self . bubblesTableView . contentInset ;
2021-04-01 20:59:55 +00:00
contentInset . bottom = self . view . safeAreaInsets . bottom ;
2015-08-26 17:04:30 +00:00
self . bubblesTableView . contentInset = contentInset ;
2016-04-18 14:56:44 +00:00
2016-11-16 08:31:58 +00:00
// Check here whether a subview has been added or removed
if ( encryptionInfoView )
{
2020-09-18 11:11:45 +00:00
if ( ! encryptionInfoView . superview )
2016-11-16 08:31:58 +00:00
{
// Reset
encryptionInfoView = nil ;
// Reload the full table to take into account a potential change on a device status .
[ self . bubblesTableView reloadData ] ;
}
}
if ( eventDetailsView )
{
2020-09-18 11:11:45 +00:00
if ( ! eventDetailsView . superview )
2016-11-16 08:31:58 +00:00
{
// Reset
eventDetailsView = nil ;
}
}
2021-03-15 13:27:59 +00:00
2016-06-10 15:15:42 +00:00
// Check whether the preview header is visible
2020-09-18 11:11:45 +00:00
if ( previewHeader )
2016-06-08 16:22:50 +00:00
{
2016-06-10 15:15:42 +00:00
if ( previewHeader . mainHeaderContainer . isHidden )
2016-06-08 16:22:50 +00:00
{
2016-06-10 15:15:42 +00:00
// Check here the main background height to display a correct navigation bar background .
CGRect frame = self . navigationController . navigationBar . frame ;
2016-06-08 16:22:50 +00:00
2016-06-10 15:15:42 +00:00
CGFloat mainHeaderBackgroundHeight = frame . size . height + ( frame . origin . y > 0 ? frame . origin . y : 0 ) ;
2016-06-08 16:22:50 +00:00
2016-06-10 15:15:42 +00:00
if ( previewHeader . mainHeaderBackgroundHeightConstraint . constant ! = mainHeaderBackgroundHeight )
{
previewHeader . mainHeaderBackgroundHeightConstraint . constant = mainHeaderBackgroundHeight ;
// Force the layout of previewHeader to update the position of ' bottomBorderView ' which
2021-03-15 13:36:37 +00:00
// is used to define the actual height of the preview container .
2016-06-10 15:15:42 +00:00
[ previewHeader layoutIfNeeded ] ;
}
2016-06-08 16:22:50 +00:00
}
2021-03-15 13:27:59 +00:00
2019-02-15 12:45:09 +00:00
self . edgesForExtendedLayout = UIRectEdgeAll ;
2021-03-15 13:27:59 +00:00
2016-06-10 15:15:42 +00:00
// Adjust the top constraint of the bubbles table
CGRect frame = previewHeader . bottomBorderView . frame ;
self . previewHeaderContainerHeightConstraint . constant = frame . origin . y + frame . size . height ;
2021-03-15 13:27:59 +00:00
2021-09-01 09:44:15 +00:00
self . bubblesTableViewTopConstraint . constant = self . previewHeaderContainerHeightConstraint . constant - self . bubblesTableView . adjustedContentInset . top ;
2017-06-01 15:20:08 +00:00
}
else
{
2019-02-15 12:45:09 +00:00
// In non expanded header mode , the navigation bar is opaque
// The table view must not display behind it
self . edgesForExtendedLayout = UIRectEdgeLeft | UIRectEdgeBottom | UIRectEdgeRight ;
2016-06-08 16:22:50 +00:00
}
2016-08-31 13:12:46 +00:00
2021-04-01 20:59:55 +00:00
// stay at the bottom if already was
if ( self . isBubblesTableScrollViewAtTheBottom )
{
[ self scrollBubblesTableViewToBottomAnimated : NO ] ;
2016-06-08 16:22:50 +00:00
}
2016-08-31 13:12:46 +00:00
[ self refreshMissedDiscussionsCount : YES ] ;
2015-08-26 17:04:30 +00:00
}
2016-02-10 14:08:51 +00:00
- ( void ) viewWillTransitionToSize : ( CGSize ) size withTransitionCoordinator : ( id < UIViewControllerTransitionCoordinator > ) coordinator
{
2021-03-24 21:17:09 +00:00
if ( [ self . titleView isKindOfClass : RoomTitleView . class ] )
{
RoomTitleView * roomTitleView = ( RoomTitleView * ) self . titleView ;
if ( UIInterfaceOrientationIsLandscape ( [ UIApplication sharedApplication ] . statusBarOrientation ) )
{
[ roomTitleView updateLayoutForOrientation : UIInterfaceOrientationPortrait ] ;
}
else
{
[ roomTitleView updateLayoutForOrientation : UIInterfaceOrientationLandscapeLeft ] ;
}
}
2016-04-26 07:49:48 +00:00
// Hide the expanded header or the preview in case of iPad and iPhone 6 plus .
// On these devices , the display mode of the splitviewcontroller may change during screen rotation .
// It may correspond to an overlay mode in portrait and a side - by - side mode in landscape .
// This display mode change involves a change at the navigation bar level .
// If we don ' t hide the header , the navigation bar is in a wrong state after rotation . FIXME : Find a way to keep visible the header on rotation .
2016-07-01 06:42:10 +00:00
if ( [ GBDeviceInfo deviceInfo ] . family = = GBDeviceFamilyiPad || [ GBDeviceInfo deviceInfo ] . displayInfo . display >= GBDeviceDisplay5p5Inch )
2016-04-14 00:34:30 +00:00
{
2017-01-24 12:35:06 +00:00
// Hide the preview header ( if any ) before rotating ( It will be restored by ` refreshRoomTitle` call if this is still a room preview ) .
[ self showPreviewHeader : NO ] ;
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) ( ( coordinator . transitionDuration + 0.5 ) * NSEC_PER _SEC ) ) , dispatch_get _main _queue ( ) , ^ {
2016-04-14 00:34:30 +00:00
2017-01-24 12:35:06 +00:00
// Let [ self refreshRoomTitle ] refresh this title view correctly
[ self refreshRoomTitle ] ;
} ) ;
2016-04-14 00:34:30 +00:00
}
2016-06-08 15:19:00 +00:00
else if ( previewHeader )
{
2016-06-08 16:22:50 +00:00
// Refresh here the preview header according to the coming screen orientation .
2016-06-08 15:19:00 +00:00
2016-06-08 16:22:50 +00:00
// Retrieve the affine transform indicating the amount of rotation being applied to the interface .
2020-08-25 16:48:09 +00:00
// This transform is the identity transform when no rotation is applied .
// Otherwise , it is a transform that applies a 90 degree , -90 degree , or 180 degree rotation .
2016-06-08 16:22:50 +00:00
CGAffineTransform transform = coordinator . targetTransform ;
2016-06-08 15:19:00 +00:00
2016-06-08 16:22:50 +00:00
// Consider here only the transform that applies a + / - 90 degree .
if ( transform . b * transform . c = = -1 )
{
UIInterfaceOrientation currentScreenOrientation = [ [ UIApplication sharedApplication ] statusBarOrientation ] ;
BOOL isLandscapeOriented = YES ;
2016-06-08 15:19:00 +00:00
2016-06-08 16:22:50 +00:00
switch ( currentScreenOrientation )
{
case UIInterfaceOrientationLandscapeRight :
case UIInterfaceOrientationLandscapeLeft :
{
// We leave here landscape orientation
isLandscapeOriented = NO ;
break ;
}
default :
break ;
}
[ self refreshPreviewHeader : isLandscapeOriented ] ;
}
2016-06-08 15:19:00 +00:00
}
2017-01-24 12:35:06 +00:00
else
{
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) ( ( coordinator . transitionDuration + 0.5 ) * NSEC_PER _SEC ) ) , dispatch_get _main _queue ( ) , ^ {
// Refresh the room title at the end of the transition to take into account the potential changes during the transition .
// For example the display of a preview header is ignored during transition .
[ self refreshRoomTitle ] ;
} ) ;
}
2016-02-10 14:08:51 +00:00
[ super viewWillTransitionToSize : size withTransitionCoordinator : coordinator ] ;
}
2019-09-19 15:21:03 +00:00
# pragma mark - Accessibility
// Handle scrolling when VoiceOver is on because it does not work well if we let the system do :
// VoiceOver loses the focus on the tableview
- ( BOOL ) accessibilityScroll : ( UIAccessibilityScrollDirection ) direction
{
BOOL canScroll = YES ;
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
// Scroll by one page
CGFloat tableViewHeight = self . bubblesTableView . frame . size . height ;
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
CGPoint offset = self . bubblesTableView . contentOffset ;
switch ( direction )
{
case UIAccessibilityScrollDirectionUp :
offset . y - = tableViewHeight ;
break ;
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
case UIAccessibilityScrollDirectionDown :
offset . y + = tableViewHeight ;
break ;
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
default :
break ;
}
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
if ( offset . y < 0 && ! [ self . roomDataSource . timeline canPaginate : MXTimelineDirectionBackwards ] )
{
// Can ' t paginate more . Let ' s stick on the first item
UIView * focusedView = [ self firstCellWithAccessibilityDataInCells : self . bubblesTableView . visibleCells . objectEnumerator ] ;
UIAccessibilityPostNotification ( UIAccessibilityLayoutChangedNotification , focusedView ) ;
canScroll = NO ;
}
else if ( offset . y > self . bubblesTableView . contentSize . height - tableViewHeight
&& ! [ self . roomDataSource . timeline canPaginate : MXTimelineDirectionForwards ] )
{
// Can ' t paginate more . Let ' s stick on the last item with accessibility
UIView * focusedView = [ self firstCellWithAccessibilityDataInCells : self . bubblesTableView . visibleCells . reverseObjectEnumerator ] ;
UIAccessibilityPostNotification ( UIAccessibilityLayoutChangedNotification , focusedView ) ;
canScroll = NO ;
}
else
{
// Disable VoiceOver while scrolling
self . bubblesTableView . accessibilityElementsHidden = YES ;
2021-03-15 13:27:59 +00:00
2019-10-28 17:55:55 +00:00
[ self setBubbleTableViewContentOffset : offset animated : NO ] ;
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
NSEnumerator < UITableViewCell * > * cells ;
if ( direction = = UIAccessibilityScrollDirectionUp )
{
cells = self . bubblesTableView . visibleCells . objectEnumerator ;
}
else
{
cells = self . bubblesTableView . visibleCells . reverseObjectEnumerator ;
}
UIView * cell = [ self firstCellWithAccessibilityDataInCells : cells ] ;
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
self . bubblesTableView . accessibilityElementsHidden = NO ;
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
// Force VoiceOver to focus on a visible item
UIAccessibilityPostNotification ( UIAccessibilityLayoutChangedNotification , cell ) ;
}
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
// If we cannot scroll , let VoiceOver indicates the border
return canScroll ;
}
- ( UIView * ) firstCellWithAccessibilityDataInCells : ( NSEnumerator < UITableViewCell * > * ) cells
{
UIView * view ;
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
for ( UITableViewCell * cell in cells )
{
if ( ! [ cell isKindOfClass : [ RoomEmptyBubbleCell class ] ] )
{
view = cell ;
break ;
}
}
2021-03-15 13:27:59 +00:00
2019-09-19 15:21:03 +00:00
return view ;
}
2015-05-27 14:51:46 +00:00
# pragma mark - Override MXKRoomViewController
2015-01-20 11:41:27 +00:00
2017-06-01 15:20:08 +00:00
- ( void ) onMatrixSessionChange
{
[ super onMatrixSessionChange ] ;
// Re - enable the read marker display , and disable its update .
self . roomDataSource . showReadMarker = YES ;
self . updateRoomReadMarker = NO ;
}
2020-11-11 21:49:46 +00:00
- ( void ) stopActivityIndicator
{
if ( notificationTaskProfile )
{
// Consider here we have displayed the message corresponding to the notification
[ MXSDKOptions . sharedInstance . profiler stopMeasuringTaskWithProfile : notificationTaskProfile ] ;
notificationTaskProfile = nil ;
}
[ super stopActivityIndicator ] ;
}
2015-06-16 11:54:12 +00:00
- ( void ) displayRoom : ( MXKRoomDataSource * ) dataSource
{
2016-06-10 15:15:42 +00:00
// Remove potential preview Data
if ( roomPreviewData )
{
roomPreviewData = nil ;
[ self removeMatrixSession : self . mainSession ] ;
}
2017-06-06 08:53:17 +00:00
// Enable the read marker display , and disable its update .
dataSource . showReadMarker = YES ;
self . updateRoomReadMarker = NO ;
2015-05-12 12:29:42 +00:00
[ super displayRoom : dataSource ] ;
2015-08-27 09:31:17 +00:00
2016-03-22 16:23:44 +00:00
customizedRoomDataSource = nil ;
2015-12-11 13:01:56 +00:00
2016-03-22 16:23:44 +00:00
if ( self . roomDataSource )
2015-12-11 13:01:56 +00:00
{
2018-10-02 13:30:00 +00:00
[ self listenToServerNotices ] ;
2021-03-15 13:27:59 +00:00
2017-06-09 07:40:22 +00:00
self . eventsAcknowledgementEnabled = YES ;
2016-03-22 16:23:44 +00:00
// Store ref on customized room data source
if ( [ dataSource isKindOfClass : RoomDataSource . class ] )
{
customizedRoomDataSource = ( RoomDataSource * ) dataSource ;
}
2021-04-21 09:47:28 +00:00
// Set room title view
[ self refreshRoomTitle ] ;
2015-12-11 13:01:56 +00:00
}
2016-03-22 16:23:44 +00:00
else
2016-03-11 13:45:06 +00:00
{
2016-03-22 16:23:44 +00:00
self . navigationItem . rightBarButtonItem . enabled = NO ;
2016-03-11 13:45:06 +00:00
}
2016-03-22 16:23:44 +00:00
[ self refreshRoomInputToolbar ] ;
2021-08-04 13:51:22 +00:00
[ VoiceMessageMediaServiceProvider . sharedProvider setCurrentRoomSummary : dataSource . room . summary ] ;
2021-09-23 07:48:15 +00:00
_voiceMessageController . roomId = dataSource . roomId ;
2021-10-04 08:37:37 +00:00
_userSuggestionCoordinator = [ [ UserSuggestionCoordinatorBridge alloc ] initWithMediaManager : self . roomDataSource . mxSession . mediaManager
room : dataSource . room ] ;
2021-10-05 11:30:31 +00:00
_userSuggestionCoordinator . delegate = self ;
2021-10-13 06:18:44 +00:00
[ self setupUserSuggestionView ] ;
2015-05-12 12:29:42 +00:00
}
2016-04-14 00:34:30 +00:00
- ( void ) onRoomDataSourceReady
{
// Handle here invitation
2018-07-17 16:00:56 +00:00
if ( self . roomDataSource . room . summary . membership = = MXMembershipInvite )
2016-04-14 00:34:30 +00:00
{
self . navigationItem . rightBarButtonItem . enabled = NO ;
// Show preview header
[ self showPreviewHeader : YES ] ;
}
else
{
[ super onRoomDataSourceReady ] ;
}
}
2015-06-16 11:54:12 +00:00
- ( void ) updateViewControllerAppearanceOnRoomDataSourceState
{
2015-04-16 17:08:29 +00:00
[ super updateViewControllerAppearanceOnRoomDataSourceState ] ;
2015-09-11 13:58:08 +00:00
2016-04-14 00:34:30 +00:00
if ( self . isRoomPreview )
{
self . navigationItem . rightBarButtonItem . enabled = NO ;
2016-06-10 15:15:42 +00:00
// Remove input tool bar if any
2016-04-20 16:50:03 +00:00
if ( self . inputToolbarView )
{
[ super setRoomInputToolbarViewClass : nil ] ;
}
2016-04-14 00:34:30 +00:00
2016-04-19 13:28:15 +00:00
if ( previewHeader )
{
previewHeader . mxRoom = self . roomDataSource . room ;
2016-06-08 15:19:00 +00:00
2016-06-10 15:15:42 +00:00
// Force the layout of subviews ( some constraints may have been updated )
2016-11-25 09:50:16 +00:00
[ self forceLayoutRefresh ] ;
2016-04-19 13:28:15 +00:00
}
2016-04-14 00:34:30 +00:00
}
else
{
[ self showPreviewHeader : NO ] ;
self . navigationItem . rightBarButtonItem . enabled = ( self . roomDataSource ! = nil ) ;
self . titleView . editable = NO ;
2016-04-20 16:50:03 +00:00
2016-04-28 17:22:50 +00:00
if ( self . roomDataSource )
2016-04-20 16:50:03 +00:00
{
2021-11-03 10:18:27 +00:00
// Update the input toolbar class and update the layout
[ self updateRoomInputToolbarViewClassIfNeeded ] ;
self . inputToolbarView . hidden = ( self . roomDataSource . state ! = MXKDataSourceStateReady ) ;
2016-04-20 16:50:03 +00:00
2021-11-03 10:18:27 +00:00
// Restore room activities view if none
2016-04-28 17:22:50 +00:00
if ( ! self . activitiesView )
{
// And the extra area
[ self setRoomActivitiesViewClass : RoomActivitiesView . class ] ;
}
2016-04-20 16:50:03 +00:00
}
}
}
2016-09-01 13:28:47 +00:00
- ( void ) leaveRoomOnEvent : ( MXEvent * ) event
{
2017-06-23 13:09:43 +00:00
// Force a simple title view initialised with the current room before leaving actually the room .
[ self setRoomTitleViewClass : SimpleRoomTitleView . class ] ;
self . titleView . editable = NO ;
self . titleView . mxRoom = self . roomDataSource . room ;
// Hide the potential read marker banner .
self . jumpToLastUnreadBannerContainer . hidden = YES ;
2016-09-01 13:28:47 +00:00
[ super leaveRoomOnEvent : event ] ;
2021-08-13 14:02:47 +00:00
2021-09-07 15:43:49 +00:00
if ( self . delegate )
{
[ self . delegate roomViewControllerDidLeaveRoom : self ] ;
}
else
{
[ [ AppDelegate theDelegate ] restoreInitialDisplay : nil ] ;
}
2016-09-01 13:28:47 +00:00
}
2018-02-21 15:10:38 +00:00
// Set the input toolbar according to the current display
2018-08-27 13:32:37 +00:00
- ( void ) updateRoomInputToolbarViewClassIfNeeded
2016-04-20 16:50:03 +00:00
{
2018-02-21 15:10:38 +00:00
Class roomInputToolbarViewClass = RoomInputToolbarView . class ;
2019-05-15 21:24:34 +00:00
BOOL shouldDismissContextualMenu = NO ;
2021-03-15 13:27:59 +00:00
2018-02-21 15:10:38 +00:00
// Check the user has enough power to post message
2018-07-20 09:28:02 +00:00
if ( self . roomDataSource . roomState )
2018-02-21 15:10:38 +00:00
{
2018-07-20 09:28:02 +00:00
MXRoomPowerLevels * powerLevels = self . roomDataSource . roomState . powerLevels ;
2018-02-21 15:10:38 +00:00
NSInteger userPowerLevel = [ powerLevels powerLevelOfUserWithUserID : self . mainSession . myUser . userId ] ;
2018-07-27 09:56:51 +00:00
2018-02-21 15:10:38 +00:00
BOOL canSend = ( userPowerLevel >= [ powerLevels minimumPowerLevelForSendingEventAsMessage : kMXEventTypeStringRoomMessage ] ) ;
2018-08-07 16:20:22 +00:00
BOOL isRoomObsolete = self . roomDataSource . roomState . isObsolete ;
2018-08-23 15:03:32 +00:00
BOOL isResourceLimitExceeded = [ self . roomDataSource . mxSession . syncError . errcode isEqualToString : kMXErrCodeStringResourceLimitExceeded ] ;
2018-07-27 09:56:51 +00:00
2018-08-23 15:03:32 +00:00
if ( isRoomObsolete || isResourceLimitExceeded )
2018-07-27 09:56:51 +00:00
{
roomInputToolbarViewClass = nil ;
2019-05-15 21:24:34 +00:00
shouldDismissContextualMenu = YES ;
2018-07-27 09:56:51 +00:00
}
else if ( ! canSend )
2018-02-21 15:10:38 +00:00
{
roomInputToolbarViewClass = DisabledRoomInputToolbarView . class ;
2019-05-15 21:24:34 +00:00
shouldDismissContextualMenu = YES ;
2018-02-21 15:10:38 +00:00
}
}
2021-03-15 13:27:59 +00:00
2016-04-20 16:50:03 +00:00
// Do not show toolbar in case of preview
if ( self . isRoomPreview )
{
roomInputToolbarViewClass = nil ;
2019-05-15 21:24:34 +00:00
shouldDismissContextualMenu = YES ;
}
if ( shouldDismissContextualMenu )
{
[ self hideContextualMenuAnimated : NO ] ;
2016-04-20 16:50:03 +00:00
}
2018-08-27 13:32:37 +00:00
// Change inputToolbarView class only if given class is different from current one
if ( ! self . inputToolbarView || ! [ self . inputToolbarView isMemberOfClass : roomInputToolbarViewClass ] )
{
[ super setRoomInputToolbarViewClass : roomInputToolbarViewClass ] ;
2021-06-07 07:20:26 +00:00
2021-07-28 15:40:03 +00:00
// The voice message toolbar cannot be set on DisabledInputToolbarView .
if ( [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] )
{
[ ( RoomInputToolbarView * ) self . inputToolbarView setVoiceMessageToolbarView : self . voiceMessageController . voiceMessageToolbarView ] ;
}
2021-06-07 07:20:26 +00:00
2018-08-27 13:32:37 +00:00
[ self updateInputToolBarViewHeight ] ;
2021-11-03 10:18:27 +00:00
[ self refreshRoomInputToolbar ] ;
2018-08-27 13:32:37 +00:00
}
2016-04-20 16:50:03 +00:00
}
2018-02-21 15:10:38 +00:00
// Get the height of the current room input toolbar
- ( CGFloat ) inputToolbarHeight
{
CGFloat height = 0 ;
2021-03-15 13:27:59 +00:00
2018-02-21 15:10:38 +00:00
if ( [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] )
{
2019-05-15 21:24:34 +00:00
height = ( ( RoomInputToolbarView * ) self . inputToolbarView ) . mainToolbarHeightConstraint . constant ;
2018-02-21 15:10:38 +00:00
}
else if ( [ self . inputToolbarView isKindOfClass : DisabledRoomInputToolbarView . class ] )
{
height = ( ( DisabledRoomInputToolbarView * ) self . inputToolbarView ) . mainToolbarMinHeightConstraint . constant ;
}
2021-03-15 13:27:59 +00:00
2018-02-21 15:10:38 +00:00
return height ;
}
2016-04-20 16:50:03 +00:00
- ( void ) setRoomActivitiesViewClass : ( Class ) roomActivitiesViewClass
{
2016-06-10 15:15:42 +00:00
// Do not show room activities in case of preview ( FIXME : show it when live events will be supported during peeking )
2016-04-20 16:50:03 +00:00
if ( self . isRoomPreview )
{
roomActivitiesViewClass = nil ;
2016-04-14 00:34:30 +00:00
}
2016-04-20 16:50:03 +00:00
[ super setRoomActivitiesViewClass : roomActivitiesViewClass ] ;
2021-03-15 13:27:59 +00:00
if ( ! self . isActivitiesViewExpanded )
{
self . roomActivitiesContainerHeightConstraint . constant = 0 ;
}
2015-01-22 10:09:14 +00:00
}
2015-06-16 11:54:12 +00:00
- ( BOOL ) isIRCStyleCommand : ( NSString * ) string
{
2015-05-27 14:51:46 +00:00
// Override the default behavior for ` / join` command in order to open automatically the joined room
2018-07-24 10:24:12 +00:00
if ( [ string hasPrefix : kMXKSlashCmdJoinRoom ] )
2015-06-16 11:54:12 +00:00
{
2015-05-27 14:51:46 +00:00
// Join a room
2016-07-01 15:06:20 +00:00
NSString * roomAlias ;
// Sanity check
2018-07-24 10:24:12 +00:00
if ( string . length > kMXKSlashCmdJoinRoom . length )
2016-07-01 15:06:20 +00:00
{
2018-07-24 10:24:12 +00:00
roomAlias = [ string substringFromIndex : kMXKSlashCmdJoinRoom . length + 1 ] ;
2016-07-01 15:06:20 +00:00
// Remove white space from both ends
roomAlias = [ roomAlias stringByTrimmingCharactersInSet : [ NSCharacterSet whitespaceCharacterSet ] ] ;
}
2015-05-27 14:51:46 +00:00
// Check
2015-06-16 11:54:12 +00:00
if ( roomAlias . length )
{
2019-07-08 09:15:44 +00:00
// TODO : / join command does not support via parameters yet
[ self . mainSession joinRoom : roomAlias viaServers : nil success : ^ ( MXRoom * room ) {
2021-09-07 15:43:49 +00:00
[ self showRoomWithId : room . roomId ] ;
2016-06-08 15:19:00 +00:00
} failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] Join roomAlias (%@) failed" , roomAlias ) ;
2016-06-08 15:19:00 +00:00
// Alert user
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2016-06-08 15:19:00 +00:00
} ] ;
2015-06-16 11:54:12 +00:00
}
else
{
2015-05-27 14:51:46 +00:00
// Display cmd usage in text input as placeholder
self . inputToolbarView . placeholder = @ "Usage: /join <room_alias>" ;
}
return YES ;
}
return [ super isIRCStyleCommand : string ] ;
}
2016-02-24 14:42:22 +00:00
- ( void ) setKeyboardHeight : ( CGFloat ) keyboardHeight
{
[ super setKeyboardHeight : keyboardHeight ] ;
2021-03-24 21:17:09 +00:00
self . inputToolbarView . maxHeight = round ( ( [ UIScreen mainScreen ] . bounds . size . height - keyboardHeight ) * 0.7 ) ;
2017-06-08 08:52:03 +00:00
// Make the activity indicator follow the keyboard
// At runtime , this creates a smooth animation
CGPoint activityIndicatorCenter = self . activityIndicator . center ;
activityIndicatorCenter . y = self . view . center . y - keyboardHeight / 2 ;
self . activityIndicator . center = activityIndicatorCenter ;
2016-02-24 14:42:22 +00:00
}
2016-11-16 08:31:58 +00:00
- ( void ) dismissTemporarySubViews
{
[ super dismissTemporarySubViews ] ;
if ( encryptionInfoView )
{
[ encryptionInfoView removeFromSuperview ] ;
encryptionInfoView = nil ;
}
}
2017-06-09 12:20:00 +00:00
- ( void ) setBubbleTableViewDisplayInTransition : ( BOOL ) bubbleTableViewDisplayInTransition
{
if ( self . isBubbleTableViewDisplayInTransition ! = bubbleTableViewDisplayInTransition )
{
[ super setBubbleTableViewDisplayInTransition : bubbleTableViewDisplayInTransition ] ;
2017-06-29 10:03:39 +00:00
// Refresh additional displays when the table is ready .
if ( ! bubbleTableViewDisplayInTransition && ! self . bubblesTableView . isHidden )
{
[ self refreshActivitiesViewDisplay ] ;
[ self checkReadMarkerVisibility ] ;
[ self refreshJumpToLastUnreadBannerDisplay ] ;
}
2017-06-09 12:20:00 +00:00
}
}
2018-07-23 14:56:03 +00:00
- ( void ) sendTextMessage : ( NSString * ) msgTxt
{
2019-04-30 15:07:43 +00:00
if ( self . inputToolBarSendMode = = RoomInputToolbarViewSendModeReply && customizedRoomDataSource . selectedEventId )
2018-07-23 14:56:03 +00:00
{
[ 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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[MXKRoomViewController] sendTextMessage failed." ) ;
2018-07-23 14:56:03 +00:00
} ] ;
}
2019-06-13 14:38:20 +00:00
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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[MXKRoomViewController] sendTextMessage failed." ) ;
2019-06-13 14:38:20 +00:00
} ] ;
}
2018-07-23 14:56:03 +00:00
else
{
// Let the datasource send it and manage the local echo
[ self . roomDataSource sendTextMessage : msgTxt success : nil failure : ^ ( NSError * error )
{
2021-03-15 13:27:59 +00:00
// Just log the error . The message will be displayed in red in the room history
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[MXKRoomViewController] sendTextMessage failed." ) ;
2021-03-15 13:27:59 +00:00
} ] ;
2018-07-23 14:56:03 +00:00
}
2021-03-08 22:12:52 +00:00
if ( customizedRoomDataSource . selectedEventId )
{
[ self cancelEventSelection ] ;
}
2018-07-23 14:56:03 +00:00
}
2020-04-08 13:07:24 +00:00
- ( void ) setRoomTitleViewClass : ( Class ) roomTitleViewClass
{
2021-03-15 13:27:59 +00:00
// Sanity check : accept only MXKRoomTitleView classes or sub - classes
NSParameterAssert ( [ roomTitleViewClass isSubclassOfClass : MXKRoomTitleView . class ] ) ;
MXKRoomTitleView * titleView = [ roomTitleViewClass roomTitleView ] ;
[ self setValue : titleView forKey : @ "titleView" ] ;
titleView . delegate = self ;
titleView . mxRoom = self . roomDataSource . room ;
self . navigationItem . leftBarButtonItem = [ [ UIBarButtonItem alloc ] initWithCustomView : titleView ] ;
2021-03-24 21:17:09 +00:00
if ( [ titleView isKindOfClass : RoomTitleView . class ] )
{
RoomTitleView * roomTitleView = ( RoomTitleView * ) self . titleView ;
missedDiscussionsBadgeLabel = roomTitleView . missedDiscussionsBadgeLabel ;
missedDiscussionsDotView = roomTitleView . dotView ;
[ roomTitleView updateLayoutForOrientation : [ UIApplication sharedApplication ] . statusBarOrientation ] ;
}
2021-03-15 13:27:59 +00:00
[ self updateViewControllerAppearanceOnRoomDataSourceState ] ;
2020-04-08 13:07:24 +00:00
[ self updateTitleViewEncryptionDecoration ] ;
}
2015-06-16 11:54:12 +00:00
- ( void ) destroy
{
2016-05-03 16:29:54 +00:00
if ( currentAlert )
2015-06-16 11:54:12 +00:00
{
2017-07-14 14:41:25 +00:00
[ currentAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-05-03 16:29:54 +00:00
currentAlert = nil ;
2015-03-31 16:33:37 +00:00
}
2015-12-11 13:01:56 +00:00
if ( customizedRoomDataSource )
{
customizedRoomDataSource . selectedEventId = nil ;
customizedRoomDataSource = nil ;
}
2016-06-27 13:34:17 +00:00
[ self removeTypingNotificationsListener ] ;
2019-01-11 09:32:56 +00:00
if ( kThemeServiceDidChangeThemeNotificationObserver )
2017-07-21 09:28:17 +00:00
{
2019-01-11 09:32:56 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : kThemeServiceDidChangeThemeNotificationObserver ] ;
kThemeServiceDidChangeThemeNotificationObserver = nil ;
2017-07-21 09:28:17 +00:00
}
2016-06-27 13:34:17 +00:00
if ( kAppDelegateDidTapStatusBarNotificationObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : kAppDelegateDidTapStatusBarNotificationObserver ] ;
kAppDelegateDidTapStatusBarNotificationObserver = nil ;
}
2016-08-11 08:38:10 +00:00
if ( kAppDelegateNetworkStatusDidChangeNotificationObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : kAppDelegateNetworkStatusDidChangeNotificationObserver ] ;
kAppDelegateNetworkStatusDidChangeNotificationObserver = nil ;
}
2017-04-18 13:55:51 +00:00
if ( mxRoomSummaryDidChangeObserver )
2016-08-31 13:12:46 +00:00
{
2017-04-18 13:55:51 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : mxRoomSummaryDidChangeObserver ] ;
mxRoomSummaryDidChangeObserver = nil ;
2016-08-31 13:12:46 +00:00
}
2018-06-12 16:21:04 +00:00
if ( mxEventDidDecryptNotificationObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : mxEventDidDecryptNotificationObserver ] ;
mxEventDidDecryptNotificationObserver = nil ;
}
2021-09-07 10:20:59 +00:00
if ( URLPreviewDidUpdateNotificationObserver )
{
2021-10-28 16:51:25 +00:00
[ NSNotificationCenter . defaultCenter removeObserver : URLPreviewDidUpdateNotificationObserver ] ;
2021-09-07 10:20:59 +00:00
}
2017-07-14 14:41:25 +00:00
2016-08-09 10:47:52 +00:00
[ self removeCallNotificationsListeners ] ;
2017-08-09 15:31:15 +00:00
[ self removeWidgetNotificationsListeners ] ;
2018-07-27 09:56:51 +00:00
[ self removeTombstoneEventNotificationsListener ] ;
2018-08-21 16:25:56 +00:00
[ self removeMXSessionStateChangeNotificationsListener ] ;
2018-10-02 13:30:00 +00:00
[ self removeServerNoticesListener ] ;
2021-03-15 13:27:59 +00:00
2020-09-18 11:11:45 +00:00
if ( previewHeader )
2016-04-14 00:34:30 +00:00
{
2016-06-10 15:15:42 +00:00
// Here [ destroy ] is called before [ viewWillDisappear : ]
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] destroyed whereas it is still visible" ) ;
2016-06-10 15:15:42 +00:00
2016-04-14 00:34:30 +00:00
[ previewHeader removeFromSuperview ] ;
previewHeader = nil ;
2016-06-10 15:15:42 +00:00
// Hide preview header container to ignore [ self showPreviewHeader : NO ] call ( if any ) .
self . previewHeaderContainer . hidden = YES ;
2016-04-14 00:34:30 +00:00
}
2016-06-10 15:15:42 +00:00
roomPreviewData = nil ;
2016-08-31 13:12:46 +00:00
missedDiscussionsBadgeLabel = nil ;
2017-07-14 14:41:25 +00:00
2017-02-14 10:01:38 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : self name : kMXEventDidChangeSentStateNotification object : nil ] ;
2019-06-27 10:25:01 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : self name : kMXEventDidChangeIdentifierNotification object : nil ] ;
2017-07-14 14:41:25 +00:00
2015-04-01 09:06:48 +00:00
[ super destroy ] ;
2015-03-31 16:33:37 +00:00
}
2021-03-20 20:31:17 +00:00
# pragma mark - Properties
2016-04-18 14:56:44 +00:00
2021-03-15 13:27:59 +00:00
- ( void ) setActivitiesViewExpanded : ( BOOL ) activitiesViewExpanded
2018-01-16 22:49:04 +00:00
{
2021-03-15 13:27:59 +00:00
if ( _activitiesViewExpanded ! = activitiesViewExpanded )
2018-01-16 22:49:04 +00:00
{
2021-03-15 13:27:59 +00:00
_activitiesViewExpanded = activitiesViewExpanded ;
2018-01-16 22:49:04 +00:00
2021-03-15 13:27:59 +00:00
self . roomActivitiesContainerHeightConstraint . constant = activitiesViewExpanded ? 53 : 0 ;
[ super roomInputToolbarView : self . inputToolbarView heightDidChanged : [ self inputToolbarHeight ] completion : nil ] ;
2018-01-16 22:49:04 +00:00
}
}
2021-03-20 20:31:17 +00:00
- ( void ) setScrollToBottomHidden : ( BOOL ) scrollToBottomHidden
{
if ( _scrollToBottomHidden ! = scrollToBottomHidden )
{
_scrollToBottomHidden = scrollToBottomHidden ;
}
2021-04-06 11:09:30 +00:00
if ( ! _scrollToBottomHidden && [ self . roomDataSource isKindOfClass : RoomDataSource . class ] )
{
RoomDataSource * roomDataSource = ( RoomDataSource * ) self . roomDataSource ;
if ( roomDataSource . currentTypingUsers && ! roomDataSource . currentTypingUsers . count )
{
[ roomDataSource resetTypingNotification ] ;
2021-04-28 14:00:19 +00:00
[ self . bubblesTableView reloadData ] ;
2021-04-06 11:09:30 +00:00
}
}
2021-03-20 20:31:17 +00:00
[ UIView animateWithDuration : .2 animations : ^ {
self . scrollToBottomBadgeLabel . alpha = ( scrollToBottomHidden || ! self . scrollToBottomBadgeLabel . text ) ? 0 : 1 ;
self . scrollToBottomButton . alpha = scrollToBottomHidden ? 0 : 1 ;
} ] ;
}
2021-08-16 13:44:44 +00:00
- ( void ) setMissedDiscussionsBadgeHidden : ( BOOL ) missedDiscussionsBadgeHidden {
_missedDiscussionsBadgeHidden = missedDiscussionsBadgeHidden ;
missedDiscussionsBadgeLabel . hidden = missedDiscussionsBadgeHidden ;
missedDiscussionsDotView . hidden = missedDiscussionsBadgeHidden ;
}
2016-03-22 16:23:44 +00:00
# pragma mark - Internals
2021-04-28 23:20:50 +00:00
- ( UIBarButtonItem * ) videoCallBarButtonItem
{
UIBarButtonItem * item = [ [ UIBarButtonItem alloc ] initWithImage : [ UIImage imageNamed : @ "video_call" ]
style : UIBarButtonItemStylePlain
target : self
action : @ selector ( onVideoCallPressed : ) ] ;
2021-09-28 05:40:01 +00:00
item . accessibilityLabel = [ VectorL10n roomAccessibilityVideoCall ] ;
2021-04-28 23:20:50 +00:00
return item ;
}
2021-04-23 15:36:51 +00:00
- ( void ) setupRemoveJitsiWidgetRemoveView
{
self . removeJitsiWidgetView = [ RemoveJitsiWidgetView instantiate ] ;
self . removeJitsiWidgetView . delegate = self ;
[ self . removeJitsiWidgetContainer vc_addSubViewMatchingParent : self . removeJitsiWidgetView ] ;
self . removeJitsiWidgetContainer . hidden = YES ;
[ self refreshRemoveJitsiWidgetView ] ;
}
2016-11-25 09:50:16 +00:00
- ( void ) forceLayoutRefresh
{
// Sanity check : check whether the table view data source is set .
if ( self . bubblesTableView . dataSource )
{
[ self . view layoutIfNeeded ] ;
}
}
2016-04-14 00:34:30 +00:00
- ( BOOL ) isRoomPreview
{
2016-06-10 15:15:42 +00:00
// Check first whether some preview data are defined .
if ( roomPreviewData )
2016-04-14 00:34:30 +00:00
{
return YES ;
}
2018-07-17 16:00:56 +00:00
if ( self . roomDataSource && self . roomDataSource . state = = MXKDataSourceStateReady && self . roomDataSource . room . summary . membership = = MXMembershipInvite )
2016-04-14 00:34:30 +00:00
{
return YES ;
}
return NO ;
}
2020-01-22 15:27:04 +00:00
- ( BOOL ) isEncryptionEnabled
{
return self . roomDataSource . room . summary . isEncrypted && self . mainSession . crypto ! = nil ;
}
2021-03-15 13:27:59 +00:00
- ( BOOL ) supportCallOption
{
2021-04-20 08:36:05 +00:00
BOOL callOptionAllowed = ( self . roomDataSource . room . isDirect && RiotSettings . shared . roomScreenAllowVoIPForDirectRoom ) || ( ! self . roomDataSource . room . isDirect && RiotSettings . shared . roomScreenAllowVoIPForNonDirectRoom ) ;
2021-04-19 18:39:07 +00:00
return callOptionAllowed && BuildSettings . allowVoIPUsage && self . roomDataSource . mxSession . callManager && self . roomDataSource . room . summary . membersCount . joined >= 2 ;
2021-03-15 13:27:59 +00:00
}
- ( BOOL ) isCallActive
{
MXCall * callInRoom = [ self . roomDataSource . mxSession . callManager callInRoom : self . roomDataSource . roomId ] ;
return ( callInRoom && callInRoom . state ! = MXCallStateEnded )
2021-04-02 13:12:21 +00:00
|| customizedRoomDataSource . jitsiWidget ;
2021-03-15 13:27:59 +00:00
}
2021-04-21 09:47:28 +00:00
/ * *
Returns a flag for the current user whether it ' s privileged to add / remove Jitsi widgets to this room .
* /
- ( BOOL ) canEditJitsiWidget
{
MXRoomPowerLevels * powerLevels = [ self . roomDataSource . roomState powerLevels ] ;
NSInteger requiredPower = [ powerLevels minimumPowerLevelForSendingEventAsStateEvent : kWidgetModularEventTypeString ] ;
NSInteger myPower = [ powerLevels powerLevelOfUserWithUserID : self . roomDataSource . mxSession . myUserId ] ;
return myPower >= requiredPower ;
2021-03-15 13:27:59 +00:00
}
2021-09-08 14:10:13 +00:00
- ( void ) registerURLPreviewNotifications
{
2021-10-28 15:41:16 +00:00
MXWeakify ( self ) ;
2021-09-08 14:10:13 +00:00
URLPreviewDidUpdateNotificationObserver = [ NSNotificationCenter . defaultCenter addObserverForName : URLPreviewDidUpdateNotification object : nil queue : NSOperationQueue . mainQueue usingBlock : ^ ( NSNotification * _Nonnull notification ) {
2021-10-28 15:41:16 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-09-08 14:10:13 +00:00
// Ensure this is the correct room
if ( ! [ ( NSString * ) notification . userInfo [ @ "roomId" ] isEqualToString : self . roomDataSource . roomId ] )
{
return ;
}
// Get the indexPath for the updated cell .
NSString * updatedEventId = notification . userInfo [ @ "eventId" ] ;
NSInteger updatedEventIndex = [ self . roomDataSource indexOfCellDataWithEventId : updatedEventId ] ;
NSIndexPath * updatedIndexPath = [ NSIndexPath indexPathForRow : updatedEventIndex inSection : 0 ] ;
// Store the content size and offset before reloading the cell
CGFloat originalContentSize = self . bubblesTableView . contentSize . height ;
CGPoint contentOffset = self . bubblesTableView . contentOffset ;
// Only update the content offset if the cell is visible or above the current visible cells .
BOOL shouldUpdateContentOffset = NO ;
NSIndexPath * lastVisibleIndexPath = [ self . bubblesTableView indexPathsForVisibleRows ] . lastObject ;
if ( lastVisibleIndexPath && updatedIndexPath . row < lastVisibleIndexPath . row )
{
shouldUpdateContentOffset = YES ;
}
// Note : Despite passing in the index path , this reloads the whole table .
[ self dataSource : self . roomDataSource didCellChange : updatedIndexPath ] ;
// Update the content offset to include any changes to the scroll view ' s height .
if ( shouldUpdateContentOffset )
{
CGFloat delta = self . bubblesTableView . contentSize . height - originalContentSize ;
contentOffset . y + = delta ;
self . bubblesTableView . contentOffset = contentOffset ;
}
} ] ;
}
2016-04-14 00:34:30 +00:00
- ( void ) refreshRoomTitle
{
2021-03-15 13:27:59 +00:00
NSMutableArray * rightBarButtonItems = nil ;
2017-06-22 08:51:40 +00:00
2016-04-14 00:34:30 +00:00
// Set the right room title view
if ( self . isRoomPreview )
{
[ self showPreviewHeader : YES ] ;
}
else if ( self . roomDataSource )
{
[ self showPreviewHeader : NO ] ;
if ( self . roomDataSource . isLive )
{
2021-03-15 13:27:59 +00:00
rightBarButtonItems = [ NSMutableArray new ] ;
2021-04-28 23:20:50 +00:00
BOOL hasCustomJoinButton = NO ;
2021-03-15 13:27:59 +00:00
if ( self . supportCallOption )
2017-09-15 11:27:13 +00:00
{
2021-04-28 09:33:54 +00:00
if ( self . roomDataSource . room . summary . membersCount . joined = = 2 && self . roomDataSource . room . isDirect )
2021-04-02 13:12:21 +00:00
{
2021-04-28 23:20:50 +00:00
// voice call button for Matrix call
UIBarButtonItem * itemVoice = [ [ UIBarButtonItem alloc ] initWithImage : [ UIImage imageNamed : @ "voice_call_hangon_icon" ]
style : UIBarButtonItemStylePlain
target : self
action : @ selector ( onVoiceCallPressed : ) ] ;
2021-09-28 05:40:01 +00:00
itemVoice . accessibilityLabel = [ VectorL10n roomAccessibilityCall ] ;
2021-04-28 23:20:50 +00:00
itemVoice . enabled = ! self . isCallActive ;
[ rightBarButtonItems addObject : itemVoice ] ;
// video call button for Matrix call
UIBarButtonItem * itemVideo = [ self videoCallBarButtonItem ] ;
itemVideo . enabled = ! self . isCallActive ;
[ rightBarButtonItems addObject : itemVideo ] ;
2021-04-21 09:47:28 +00:00
}
else
{
2021-04-28 23:20:50 +00:00
// video call button for Jitsi call
2021-04-21 09:47:28 +00:00
if ( self . isCallActive )
{
2021-09-07 15:43:49 +00:00
if ( self . isRoomHavingAJitsiCall )
2021-04-21 09:47:28 +00:00
{
2021-04-28 23:20:50 +00:00
// show a disabled call button
UIBarButtonItem * item = [ self videoCallBarButtonItem ] ;
2021-04-21 09:47:28 +00:00
item . enabled = NO ;
2021-04-28 23:20:50 +00:00
[ rightBarButtonItems addObject : item ] ;
2021-04-21 09:47:28 +00:00
}
else
{
// show Join button
2021-04-21 10:58:15 +00:00
CallTileActionButton * button = [ CallTileActionButton new ] ;
2021-04-28 23:21:59 +00:00
[ button setImage : [ UIImage imageNamed : @ "call_video_icon" ]
forState : UIControlStateNormal ] ;
2021-09-28 05:40:01 +00:00
[ button setTitle : [ VectorL10n roomJoinGroupCall ]
2021-04-28 23:21:59 +00:00
forState : UIControlStateNormal ] ;
2021-04-21 09:47:28 +00:00
[ button addTarget : self
action : @ selector ( onVideoCallPressed : )
forControlEvents : UIControlEventTouchUpInside ] ;
2021-04-21 10:58:15 +00:00
button . contentEdgeInsets = UIEdgeInsetsMake ( 4 , 12 , 4 , 12 ) ;
2021-04-28 23:20:50 +00:00
UIBarButtonItem * item = [ [ UIBarButtonItem alloc ] initWithCustomView : button ] ;
2021-09-28 05:40:01 +00:00
item . accessibilityLabel = [ VectorL10n roomAccessibilityVideoCall ] ;
2021-04-28 23:20:50 +00:00
[ rightBarButtonItems addObject : item ] ;
hasCustomJoinButton = YES ;
2021-04-21 09:47:28 +00:00
}
}
else
{
2021-04-28 23:20:50 +00:00
// show a video call button
// item will still be enabled , and when tapped an alert will be displayed to the user
UIBarButtonItem * item = [ self videoCallBarButtonItem ] ;
2021-04-21 09:47:28 +00:00
if ( ! self . canEditJitsiWidget )
{
item . image = [ [ UIImage imageNamed : @ "video_call" ] vc_withAlpha : 0.3 ] ;
}
2021-04-28 23:20:50 +00:00
[ rightBarButtonItems addObject : item ] ;
2021-04-21 09:47:28 +00:00
}
}
2017-09-15 11:27:13 +00:00
}
2021-03-15 13:27:59 +00:00
if ( [ self widgetsCount : NO ] )
2017-09-15 11:27:13 +00:00
{
2021-04-28 23:20:50 +00:00
UIBarButtonItem * item = [ [ UIBarButtonItem alloc ] initWithImage : [ UIImage imageNamed : @ "integrations_icon" ]
style : UIBarButtonItemStylePlain
target : self
action : @ selector ( onIntegrationsPressed : ) ] ;
2021-09-28 05:40:01 +00:00
item . accessibilityLabel = [ VectorL10n roomAccessibilityIntegrations ] ;
2021-04-28 23:20:50 +00:00
if ( hasCustomJoinButton )
{
item . imageInsets = UIEdgeInsetsMake ( 0 , -5 , 0 , -5 ) ;
item . landscapeImagePhoneInsets = UIEdgeInsetsMake ( 0 , -5 , 0 , -5 ) ;
}
2021-03-15 13:27:59 +00:00
[ rightBarButtonItems addObject : item ] ;
2017-09-15 11:27:13 +00:00
}
2021-03-15 13:27:59 +00:00
2016-04-22 22:56:21 +00:00
// Do not change title view class here if the expanded header is visible .
2020-09-18 11:11:45 +00:00
[ self setRoomTitleViewClass : RoomTitleView . class ] ;
( ( RoomTitleView * ) self . titleView ) . tapGestureDelegate = self ;
2016-04-14 00:34:30 +00:00
}
2021-03-15 13:27:59 +00:00
MXKImageView * userPictureView = ( ( RoomTitleView * ) self . titleView ) . pictureView ;
// Set user picture in input toolbar
if ( userPictureView )
2016-04-14 00:34:30 +00:00
{
2021-03-15 13:27:59 +00:00
[ self . roomDataSource . room . summary setRoomAvatarImageIn : userPictureView ] ;
2016-04-14 00:34:30 +00:00
}
2021-03-15 13:27:59 +00:00
[ self refreshMissedDiscussionsCount : YES ] ;
2016-04-14 00:34:30 +00:00
}
2021-03-15 13:27:59 +00:00
self . navigationItem . rightBarButtonItems = rightBarButtonItems ;
2016-04-14 00:34:30 +00:00
}
2016-03-22 16:23:44 +00:00
- ( void ) refreshRoomInputToolbar
{
2018-02-21 15:10:38 +00:00
MXKImageView * userPictureView ;
2021-03-15 13:27:59 +00:00
2016-03-22 16:23:44 +00:00
// Check whether the input toolbar is ready before updating it .
if ( self . inputToolbarView && [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] )
{
RoomInputToolbarView * roomInputToolbarView = ( RoomInputToolbarView * ) self . inputToolbarView ;
2020-01-22 15:27:04 +00:00
// Update encryption decoration if needed
[ self updateEncryptionDecorationForRoomInputToolbar : roomInputToolbarView ] ;
2016-03-22 16:23:44 +00:00
}
2018-02-21 15:10:38 +00:00
else if ( self . inputToolbarView && [ self . inputToolbarView isKindOfClass : DisabledRoomInputToolbarView . class ] )
{
DisabledRoomInputToolbarView * roomInputToolbarView = ( DisabledRoomInputToolbarView * ) self . inputToolbarView ;
2021-03-15 13:27:59 +00:00
2018-02-21 15:10:38 +00:00
// Get user picture view in input toolbar
userPictureView = roomInputToolbarView . pictureView ;
2021-03-15 13:27:59 +00:00
2018-02-21 15:10:38 +00:00
// For the moment , there is only one reason to use ` DisabledRoomInputToolbarView`
2021-09-28 05:40:01 +00:00
[ roomInputToolbarView setDisabledReason : [ VectorL10n roomDoNotHavePermissionToPost ] ] ;
2018-02-21 15:10:38 +00:00
}
2021-03-15 13:27:59 +00:00
2018-02-21 15:10:38 +00:00
// Set user picture in input toolbar
if ( userPictureView )
{
UIImage * preview = [ AvatarGenerator generateAvatarForMatrixItem : self . mainSession . myUser . userId withDisplayName : self . mainSession . myUser . displayname ] ;
2018-11-11 14:20:32 +00:00
// Suppose the avatar is stored unencrypted on the Matrix media repository .
2018-02-21 15:10:38 +00:00
userPictureView . enableInMemoryCache = YES ;
2018-11-11 14:20:32 +00:00
[ userPictureView setImageURI : self . mainSession . myUser . avatarUrl
withType : nil
andImageOrientation : UIImageOrientationUp
toFitViewSize : userPictureView . frame . size
withMethod : MXThumbnailingMethodCrop
previewImage : preview
mediaManager : self . mainSession . mediaManager ] ;
2018-02-21 15:10:38 +00:00
[ userPictureView . layer setCornerRadius : userPictureView . frame . size . width / 2 ] ;
userPictureView . clipsToBounds = YES ;
}
2016-03-22 16:23:44 +00:00
}
2021-03-24 21:17:09 +00:00
- ( void ) setInputToolBarSendMode : ( RoomInputToolbarViewSendMode ) sendMode forEventWithId : ( NSString * ) eventId
2018-07-23 14:56:03 +00:00
{
2018-08-06 12:08:28 +00:00
if ( self . inputToolbarView && [ self . inputToolbarView isKindOfClass : [ RoomInputToolbarView class ] ] )
{
RoomInputToolbarView * roomInputToolbarView = ( RoomInputToolbarView * ) self . inputToolbarView ;
2021-03-24 21:17:09 +00:00
if ( eventId )
{
MXEvent * event = [ self . roomDataSource eventWithEventId : eventId ] ;
MXRoomMember * roomMember = [ self . roomDataSource . roomState . members memberWithUserId : event . sender ] ;
2021-04-02 08:09:04 +00:00
if ( roomMember . displayname . length )
2021-03-24 21:17:09 +00:00
{
roomInputToolbarView . eventSenderDisplayName = roomMember . displayname ;
}
else
{
roomInputToolbarView . eventSenderDisplayName = event . sender ;
}
}
else
{
roomInputToolbarView . eventSenderDisplayName = nil ;
}
2019-04-30 15:07:43 +00:00
roomInputToolbarView . sendMode = sendMode ;
}
}
- ( RoomInputToolbarViewSendMode ) inputToolBarSendMode
{
RoomInputToolbarViewSendMode sendMode = RoomInputToolbarViewSendModeSend ;
if ( self . inputToolbarView && [ self . inputToolbarView isKindOfClass : [ RoomInputToolbarView class ] ] )
{
RoomInputToolbarView * roomInputToolbarView = ( RoomInputToolbarView * ) self . inputToolbarView ;
sendMode = roomInputToolbarView . sendMode ;
2018-08-06 12:08:28 +00:00
}
2021-03-15 13:27:59 +00:00
2019-04-30 15:07:43 +00:00
return sendMode ;
2018-07-23 14:56:03 +00:00
}
2016-04-20 07:52:49 +00:00
- ( void ) onSwipeGesture : ( UISwipeGestureRecognizer * ) swipeGestureRecognizer
{
UIView * view = swipeGestureRecognizer . view ;
2020-09-18 11:11:45 +00:00
if ( view = = self . activitiesView )
2016-09-05 08:40:46 +00:00
{
// Dismiss the keyboard when user swipes down on activities view .
[ self . inputToolbarView dismissKeyboard ] ;
}
2016-04-20 07:52:49 +00:00
}
2018-07-27 09:56:51 +00:00
- ( void ) updateInputToolBarViewHeight
{
// Update the inputToolBar height .
CGFloat height = [ self inputToolbarHeight ] ;
// Disable animation during the update
[ UIView setAnimationsEnabled : NO ] ;
[ self roomInputToolbarView : self . inputToolbarView heightDidChanged : height completion : nil ] ;
[ UIView setAnimationsEnabled : YES ] ;
}
2020-01-22 15:27:04 +00:00
- ( UIImage * ) roomEncryptionBadgeImage
{
UIImage * encryptionIcon ;
if ( self . isEncryptionEnabled )
{
RoomEncryptionTrustLevel roomEncryptionTrustLevel = ( ( RoomDataSource * ) self . roomDataSource ) . encryptionTrustLevel ;
2020-04-08 13:07:24 +00:00
encryptionIcon = [ EncryptionTrustLevelBadgeImageHelper roomBadgeImageFor : roomEncryptionTrustLevel ] ;
2020-01-22 15:27:04 +00:00
}
return encryptionIcon ;
}
- ( void ) updateInputToolbarEncryptionDecoration
{
if ( self . inputToolbarView && [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] )
{
RoomInputToolbarView * roomInputToolbarView = ( RoomInputToolbarView * ) self . inputToolbarView ;
[ self updateEncryptionDecorationForRoomInputToolbar : roomInputToolbarView ] ;
}
}
2020-04-08 13:07:24 +00:00
- ( void ) updateTitleViewEncryptionDecoration
{
if ( ! [ self . titleView isKindOfClass : [ RoomTitleView class ] ] )
{
return ;
}
RoomTitleView * roomTitleView = ( RoomTitleView * ) self . titleView ;
roomTitleView . badgeImageView . image = self . roomEncryptionBadgeImage ;
}
2020-01-22 15:27:04 +00:00
- ( void ) updateEncryptionDecorationForRoomInputToolbar : ( RoomInputToolbarView * ) roomInputToolbarView
{
roomInputToolbarView . isEncryptionEnabled = self . isEncryptionEnabled ;
}
2019-05-15 21:24:34 +00:00
- ( void ) handleLongPressFromCell : ( id < MXKCellRendering > ) cell withTappedEvent : ( MXEvent * ) event
{
if ( event && ! customizedRoomDataSource . selectedEventId )
{
2019-06-27 09:41:25 +00:00
[ self showContextualMenuForEvent : event fromSingleTapGesture : NO cell : cell animated : YES ] ;
2019-05-15 21:24:34 +00:00
}
}
2019-07-30 15:18:39 +00:00
- ( void ) showReactionHistoryForEventId : ( NSString * ) eventId animated : ( BOOL ) animated
{
if ( self . reactionHistoryCoordinatorBridgePresenter . isPresenting )
{
return ;
}
ReactionHistoryCoordinatorBridgePresenter * presenter = [ [ ReactionHistoryCoordinatorBridgePresenter alloc ] initWithSession : self . mainSession roomId : self . roomDataSource . roomId eventId : eventId ] ;
presenter . delegate = self ;
[ presenter presentFrom : self animated : animated ] ;
self . reactionHistoryCoordinatorBridgePresenter = presenter ;
}
2019-08-02 15:19:29 +00:00
- ( void ) showCameraControllerAnimated : ( BOOL ) animated
{
CameraPresenter * cameraPresenter = [ CameraPresenter new ] ;
cameraPresenter . delegate = self ;
[ cameraPresenter presentCameraFrom : self with : @ [ MXKUTI . image , MXKUTI . movie ] animated : YES ] ;
2021-03-15 13:27:59 +00:00
2019-08-02 15:19:29 +00:00
self . cameraPresenter = cameraPresenter ;
}
- ( void ) showMediaPickerAnimated : ( BOOL ) animated
{
MediaPickerCoordinatorBridgePresenter * mediaPickerPresenter = [ [ MediaPickerCoordinatorBridgePresenter alloc ] initWithSession : self . mainSession mediaUTIs : @ [ MXKUTI . image , MXKUTI . movie ] allowsMultipleSelection : YES ] ;
mediaPickerPresenter . delegate = self ;
UIView * sourceView ;
RoomInputToolbarView * roomInputToolbarView = [ self inputToolbarViewAsRoomInputToolbarView ] ;
if ( roomInputToolbarView )
{
sourceView = roomInputToolbarView . attachMediaButton ;
}
else
{
sourceView = self . inputToolbarView ;
}
2021-03-15 13:27:59 +00:00
2019-08-02 15:19:29 +00:00
[ mediaPickerPresenter presentFrom : self sourceView : sourceView sourceRect : sourceView . bounds animated : YES ] ;
self . mediaPickerPresenter = mediaPickerPresenter ;
}
2020-09-14 16:44:30 +00:00
- ( void ) showRoomCreationModalWithBubbleData : ( id < MXKRoomBubbleCellDataStoring > ) bubbleData
{
[ self . roomCreationModalCoordinatorBridgePresenter dismissWithAnimated : NO completion : nil ] ;
self . roomCreationModalCoordinatorBridgePresenter = [ [ RoomCreationModalCoordinatorBridgePresenter alloc ] initWithSession : self . mainSession bubbleData : bubbleData roomState : self . roomDataSource . roomState ] ;
self . roomCreationModalCoordinatorBridgePresenter . delegate = self ;
[ self . roomCreationModalCoordinatorBridgePresenter presentFrom : self animated : YES ] ;
}
2020-10-16 11:27:45 +00:00
- ( void ) showMemberDetails : ( MXRoomMember * ) member
{
if ( ! member )
{
return ;
}
RoomMemberDetailsViewController * memberViewController = [ RoomMemberDetailsViewController roomMemberDetailsViewController ] ;
// Set delegate to handle action on member ( start chat , mention )
memberViewController . delegate = self ;
memberViewController . enableMention = ( self . inputToolbarView ! = nil ) ;
memberViewController . enableVoipCall = NO ;
[ memberViewController displayRoomMember : member withMatrixRoom : self . roomDataSource . room ] ;
[ self . navigationController pushViewController : memberViewController animated : YES ] ;
}
2021-02-21 23:00:54 +00:00
- ( void ) showRoomAvatarChange
{
2021-02-23 14:52:24 +00:00
[ self showRoomInfoWithInitialSection : RoomInfoSectionChangeAvatar ] ;
2021-02-21 23:00:54 +00:00
}
- ( void ) showAddParticipants
{
2021-02-23 14:52:24 +00:00
[ self showRoomInfoWithInitialSection : RoomInfoSectionAddParticipants ] ;
2021-02-21 23:00:54 +00:00
}
- ( void ) showRoomTopicChange
{
2021-02-23 14:52:24 +00:00
[ self showRoomInfoWithInitialSection : RoomInfoSectionChangeTopic ] ;
2021-02-21 23:00:54 +00:00
}
2021-02-23 14:52:24 +00:00
- ( void ) showRoomInfo
2021-02-21 23:00:54 +00:00
{
2021-02-23 14:52:24 +00:00
[ self showRoomInfoWithInitialSection : RoomInfoSectionNone ] ;
}
- ( void ) showRoomInfoWithInitialSection : ( RoomInfoSection ) roomInfoSection
{
RoomInfoCoordinatorParameters * parameters = [ [ RoomInfoCoordinatorParameters alloc ] initWithSession : self . roomDataSource . mxSession room : self . roomDataSource . room initialSection : roomInfoSection ] ;
2021-02-21 23:00:54 +00:00
2021-02-23 14:52:24 +00:00
self . roomInfoCoordinatorBridgePresenter = [ [ RoomInfoCoordinatorBridgePresenter alloc ] initWithParameters : parameters ] ;
2021-02-21 23:00:54 +00:00
2021-02-23 14:52:24 +00:00
self . roomInfoCoordinatorBridgePresenter . delegate = self ;
2021-03-15 13:27:59 +00:00
[ self . roomInfoCoordinatorBridgePresenter pushFrom : self . navigationController animated : YES ] ;
2021-02-21 23:00:54 +00:00
}
2021-03-25 21:15:18 +00:00
- ( void ) setupActions {
2021-04-07 09:14:08 +00:00
if ( ! [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] ) {
return ;
}
2021-03-25 21:15:18 +00:00
RoomInputToolbarView * roomInputView = ( ( RoomInputToolbarView * ) self . inputToolbarView ) ;
2021-04-08 10:11:36 +00:00
MXWeakify ( self ) ;
2021-04-20 12:54:53 +00:00
NSMutableArray * actionItems = [ NSMutableArray new ] ;
if ( RiotSettings . shared . roomScreenAllowMediaLibraryAction )
{
[ actionItems addObject : [ [ RoomActionItem alloc ] initWithImage : [ UIImage imageNamed : @ "action_media_library" ] andAction : ^ {
2021-04-08 10:11:36 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
if ( [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] ) {
( ( RoomInputToolbarView * ) self . inputToolbarView ) . actionMenuOpened = NO ;
2021-03-25 21:15:18 +00:00
}
2021-04-08 10:11:36 +00:00
[ self showMediaPickerAnimated : YES ] ;
2021-04-20 12:54:53 +00:00
} ] ] ;
}
if ( RiotSettings . shared . roomScreenAllowStickerAction )
{
[ actionItems addObject : [ [ RoomActionItem alloc ] initWithImage : [ UIImage imageNamed : @ "action_sticker" ] andAction : ^ {
2021-04-08 10:11:36 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
if ( [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] ) {
( ( RoomInputToolbarView * ) self . inputToolbarView ) . actionMenuOpened = NO ;
2021-03-25 21:15:18 +00:00
}
2021-04-08 10:11:36 +00:00
[ self roomInputToolbarViewPresentStickerPicker ] ;
2021-04-20 12:54:53 +00:00
} ] ] ;
}
if ( RiotSettings . shared . roomScreenAllowFilesAction )
{
[ actionItems addObject : [ [ RoomActionItem alloc ] initWithImage : [ UIImage imageNamed : @ "action_file" ] andAction : ^ {
2021-04-08 10:11:36 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
if ( [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] ) {
( ( RoomInputToolbarView * ) self . inputToolbarView ) . actionMenuOpened = NO ;
2021-03-25 21:15:18 +00:00
}
2021-04-08 10:11:36 +00:00
[ self roomInputToolbarViewDidTapFileUpload ] ;
2021-04-20 12:54:53 +00:00
} ] ] ;
}
2022-01-18 12:17:58 +00:00
if ( BuildSettings . pollsEnabled )
2021-11-01 14:52:00 +00:00
{
[ actionItems addObject : [ [ RoomActionItem alloc ] initWithImage : [ UIImage imageNamed : @ "action_poll" ] andAction : ^ {
MXStrongifyAndReturnIfNil ( self ) ;
if ( [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] ) {
( ( RoomInputToolbarView * ) self . inputToolbarView ) . actionMenuOpened = NO ;
}
[ self . delegate roomViewControllerDidRequestPollCreationFormPresentation : self ] ;
} ] ] ;
}
2021-12-16 12:02:42 +00:00
if ( RiotSettings . shared . roomScreenAllowLocationAction )
{
[ actionItems addObject : [ [ RoomActionItem alloc ] initWithImage : [ UIImage imageNamed : @ "action_location" ] andAction : ^ {
MXStrongifyAndReturnIfNil ( self ) ;
if ( [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] ) {
( ( RoomInputToolbarView * ) self . inputToolbarView ) . actionMenuOpened = NO ;
}
[ self . delegate roomViewControllerDidRequestLocationSharingFormPresentation : self ] ;
} ] ] ;
}
2021-11-01 14:52:00 +00:00
if ( RiotSettings . shared . roomScreenAllowCameraAction )
{
[ actionItems addObject : [ [ RoomActionItem alloc ] initWithImage : [ UIImage imageNamed : @ "action_camera" ] andAction : ^ {
MXStrongifyAndReturnIfNil ( self ) ;
if ( [ self . inputToolbarView isKindOfClass : RoomInputToolbarView . class ] ) {
( ( RoomInputToolbarView * ) self . inputToolbarView ) . actionMenuOpened = NO ;
}
[ self showCameraControllerAnimated : YES ] ;
} ] ] ;
}
2021-04-20 12:54:53 +00:00
roomInputView . actionsBar . actionItems = actionItems ;
2021-03-25 21:15:18 +00:00
}
2021-10-31 18:53:57 +00:00
- ( NSString * ) textInputContextIdentifier
{
return self . roomDataSource . roomId ;
}
2021-03-25 21:15:18 +00:00
- ( void ) roomInputToolbarViewPresentStickerPicker
{
// Search for the sticker picker widget in the user account
Widget * widget = [ [ WidgetManager sharedManager ] userWidgets : self . roomDataSource . mxSession ofTypes : @ [ kWidgetTypeStickerPicker ] ] . firstObject ;
if ( widget )
{
// Display the widget
[ widget widgetUrl : ^ ( NSString * _Nonnull widgetUrl ) {
StickerPickerViewController * stickerPickerVC = [ [ StickerPickerViewController alloc ] initWithUrl : widgetUrl forWidget : widget ] ;
stickerPickerVC . roomDataSource = self . roomDataSource ;
[ self . navigationController pushViewController : stickerPickerVC animated : YES ] ;
} failure : ^ ( NSError * _Nonnull error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] Cannot display widget %@" , widget ) ;
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2021-03-25 21:15:18 +00:00
} ] ;
}
else
{
// The Sticker picker widget is not installed yet . Propose the user to install it
2021-04-08 10:11:36 +00:00
MXWeakify ( self ) ;
2021-09-28 05:40:01 +00:00
2021-03-25 21:15:18 +00:00
[ currentAlert dismissViewControllerAnimated : NO completion : nil ] ;
NSString * alertMessage = [ NSString stringWithFormat : @ "%@\n%@" ,
2021-09-28 05:40:01 +00:00
[ VectorL10n widgetStickerPickerNoStickerpacksAlert ] ,
[ VectorL10n widgetStickerPickerNoStickerpacksAlertAddNow ] ] ;
2021-11-01 15:05:02 +00:00
UIAlertController * installPrompt = [ UIAlertController alertControllerWithTitle : nil
message : alertMessage
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-03-25 21:15:18 +00:00
2021-11-01 15:05:02 +00:00
[ installPrompt addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n no ]
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action )
2021-03-25 21:15:18 +00:00
{
2021-04-08 10:11:36 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
self -> currentAlert = nil ;
2021-03-25 21:15:18 +00:00
} ] ] ;
2021-11-01 15:05:02 +00:00
[ installPrompt addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n yes ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action )
2021-03-25 21:15:18 +00:00
{
2021-04-08 10:11:36 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
self -> currentAlert = nil ;
// Show the sticker picker settings screen
IntegrationManagerViewController * modularVC = [ [ IntegrationManagerViewController alloc ]
initForMXSession : self . roomDataSource . mxSession
inRoom : self . roomDataSource . roomId
screen : [ IntegrationManagerViewController screenForWidget : kWidgetTypeStickerPicker ]
widgetId : nil ] ;
[ self presentViewController : modularVC animated : NO completion : nil ] ;
2021-03-25 21:15:18 +00:00
} ] ] ;
2021-11-01 15:05:02 +00:00
[ installPrompt mxk_setAccessibilityIdentifier : @ "RoomVCStickerPickerAlert" ] ;
[ self presentViewController : installPrompt animated : YES completion : nil ] ;
currentAlert = installPrompt ;
2021-03-25 21:15:18 +00:00
}
}
- ( void ) roomInputToolbarViewDidTapFileUpload
{
MXKDocumentPickerPresenter * documentPickerPresenter = [ MXKDocumentPickerPresenter new ] ;
documentPickerPresenter . delegate = self ;
NSArray < MXKUTI * > * allowedUTIs = @ [ MXKUTI . data ] ;
[ documentPickerPresenter presentDocumentPickerWith : allowedUTIs from : self animated : YES completion : nil ] ;
self . documentPickerPresenter = documentPickerPresenter ;
}
2021-08-16 16:48:26 +00:00
/ * *
Send a video asset via the room input toolbar prompting the user for the conversion preset to use
2021-08-23 10:16:32 +00:00
if the ` showMediaCompressionPrompt` setting has been enabled .
2021-08-16 16:48:26 +00:00
@ param videoAsset The video asset to send
@ param isPhotoLibraryAsset Whether the asset was picked from the user ' s photo library .
* /
- ( void ) sendVideoAsset : ( AVAsset * ) videoAsset isPhotoLibraryAsset : ( BOOL ) isPhotoLibraryAsset
{
RoomInputToolbarView * roomInputToolbarView = [ self inputToolbarViewAsRoomInputToolbarView ] ;
if ( ! roomInputToolbarView )
{
return ;
}
2021-08-23 10:16:32 +00:00
if ( RiotSettings . shared . showMediaCompressionPrompt )
2021-08-16 16:48:26 +00:00
{
// Show the video conversion prompt for the user to select what size video they would like to send .
UIAlertController * compressionPrompt = [ MXKTools videoConversionPromptForVideoAsset : videoAsset
withCompletion : ^ ( NSString * presetName ) {
// When the preset name is missing , the user cancelled .
if ( ! presetName )
{
return ;
}
// Set the chosen preset and send the video ( conversion takes place in the SDK ) .
[ MXSDKOptions sharedInstance ] . videoConversionPresetName = presetName ;
[ roomInputToolbarView sendSelectedVideoAsset : videoAsset isPhotoLibraryAsset : isPhotoLibraryAsset ] ;
} ] ;
2022-01-18 21:35:53 +00:00
compressionPrompt . popoverPresentationController . sourceView = roomInputToolbarView . attachMediaButton ;
compressionPrompt . popoverPresentationController . sourceRect = roomInputToolbarView . attachMediaButton . bounds ;
2021-08-16 16:48:26 +00:00
[ self presentViewController : compressionPrompt animated : YES completion : nil ] ;
}
else
{
// Otherwise default to 1080 p and send the video .
[ MXSDKOptions sharedInstance ] . videoConversionPresetName = AVAssetExportPreset1920x1080 ;
[ roomInputToolbarView sendSelectedVideoAsset : videoAsset isPhotoLibraryAsset : isPhotoLibraryAsset ] ;
}
}
2021-09-07 15:43:49 +00:00
- ( void ) showRoomWithId : ( NSString * ) roomId
{
if ( self . delegate )
{
[ self . delegate roomViewController : self showRoomWithId : roomId ] ;
}
else
{
[ [ AppDelegate theDelegate ] showRoom : roomId andEventId : nil withMatrixSession : self . roomDataSource . mxSession ] ;
}
}
- ( void ) leaveRoom
{
[ self startActivityIndicator ] ;
[ self . roomDataSource . room leave : ^ {
[ self stopActivityIndicator ] ;
// We remove the current view controller .
if ( self . delegate )
{
[ self . delegate roomViewControllerDidLeaveRoom : self ] ;
}
else
{
[ [ AppDelegate theDelegate ] restoreInitialDisplay : ^ { } ] ;
}
} failure : ^ ( NSError * error ) {
[ self stopActivityIndicator ] ;
MXLogDebug ( @ "[RoomVC] Failed to reject an invited room (%@) failed" , self . roomDataSource . room . roomId ) ;
} ] ;
}
- ( void ) roomPreviewDidTapCancelAction
{
// Decline this invitation = leave this page
if ( self . delegate )
{
[ self . delegate roomViewControllerPreviewDidTapCancel : self ] ;
}
else
{
[ [ AppDelegate theDelegate ] restoreInitialDisplay : ^ { } ] ;
}
}
- ( void ) startChatWithUserId : ( NSString * ) userId completion : ( void ( ^ ) ( void ) ) completion
{
if ( self . delegate )
{
[ self . delegate roomViewController : self startChatWithUserId : userId completion : completion ] ;
}
else
{
[ [ AppDelegate theDelegate ] createDirectChatWithUserId : userId completion : completion ] ;
}
}
- ( void ) showError : ( NSError * ) error
{
[ [ AppDelegate theDelegate ] showErrorAsAlert : error ] ;
}
- ( UIAlertController * ) showAlertWithTitle : ( NSString * ) title message : ( NSString * ) message
{
return [ [ AppDelegate theDelegate ] showAlertWithTitle : title message : message ] ;
}
2021-10-21 08:00:37 +00:00
- ( ScreenPresentationParameters * ) buildUniversalLinkPresentationParameters
2021-10-20 05:32:04 +00:00
{
2021-10-21 08:00:37 +00:00
return [ [ ScreenPresentationParameters alloc ] initWithRestoreInitialDisplay : NO stackAboveVisibleViews : BuildSettings . allowSplitViewDetailsScreenStacking sender : self sourceView : nil ] ;
2021-10-20 05:32:04 +00:00
}
2021-09-07 15:43:49 +00:00
- ( BOOL ) handleUniversalLinkURL : ( NSURL * ) universalLinkURL
{
2021-10-20 07:21:02 +00:00
UniversalLinkParameters * parameters = [ [ UniversalLinkParameters alloc ] initWithUniversalLinkURL : universalLinkURL presentationParameters : [ self buildUniversalLinkPresentationParameters ] ] ;
2021-10-12 17:12:08 +00:00
return [ self handleUniversalLinkWithParameters : parameters ] ;
2021-09-07 15:43:49 +00:00
}
2021-10-12 17:12:08 +00:00
2021-09-07 15:43:49 +00:00
- ( BOOL ) handleUniversalLinkFragment : ( NSString * ) fragment fromURL : ( NSURL * ) universalLinkURL
2021-10-12 17:12:08 +00:00
{
UniversalLinkParameters * parameters = [ [ UniversalLinkParameters alloc ] initWithFragment : fragment
2021-10-20 07:21:02 +00:00
universalLinkURL : universalLinkURL presentationParameters : [ self buildUniversalLinkPresentationParameters ] ] ;
2021-10-12 17:12:08 +00:00
return [ self handleUniversalLinkWithParameters : parameters ] ;
}
- ( BOOL ) handleUniversalLinkWithParameters : ( UniversalLinkParameters * ) parameters
2021-09-07 15:43:49 +00:00
{
if ( self . delegate )
{
2021-10-12 17:12:08 +00:00
return [ self . delegate roomViewController : self handleUniversalLinkWithParameters : parameters ] ;
2021-09-07 15:43:49 +00:00
}
else
{
2021-10-19 10:29:59 +00:00
return [ [ AppDelegate theDelegate ] handleUniversalLinkWithParameters : parameters ] ;
2021-09-07 15:43:49 +00:00
}
}
2021-10-13 06:18:44 +00:00
- ( void ) setupUserSuggestionView
{
if ( ! self . isViewLoaded ) {
MXLogError ( @ "Failed setting up user suggestions. View not loaded." ) ;
return ;
}
UIViewController * suggestionsViewController = self . userSuggestionCoordinator . toPresentable ;
2021-10-29 11:47:42 +00:00
if ( ! suggestionsViewController )
{
return ;
}
2021-10-13 06:18:44 +00:00
[ suggestionsViewController . view setTranslatesAutoresizingMaskIntoConstraints : NO ] ;
[ self addChildViewController : suggestionsViewController ] ;
[ self . userSuggestionContainerView addSubview : suggestionsViewController . view ] ;
[ NSLayoutConstraint activateConstraints : @ [ [ suggestionsViewController . view . topAnchor constraintEqualToAnchor : self . userSuggestionContainerView . topAnchor ] ,
[ suggestionsViewController . view . leadingAnchor constraintEqualToAnchor : self . userSuggestionContainerView . leadingAnchor ] ,
[ suggestionsViewController . view . trailingAnchor constraintEqualToAnchor : self . userSuggestionContainerView . trailingAnchor ] ,
[ suggestionsViewController . view . bottomAnchor constraintEqualToAnchor : self . userSuggestionContainerView . bottomAnchor ] , ] ] ;
[ suggestionsViewController didMoveToParentViewController : self ] ;
}
2021-09-07 15:43:49 +00:00
# pragma mark - Jitsi
- ( void ) showJitsiCallWithWidget : ( Widget * ) widget
{
[ [ AppDelegate theDelegate ] . callPresenter displayJitsiCallWithWidget : widget ] ;
}
- ( void ) endActiveJitsiCall
{
[ [ AppDelegate theDelegate ] . callPresenter endActiveJitsiCall ] ;
}
- ( BOOL ) isRoomHavingAJitsiCall
{
return [ self isRoomHavingAJitsiCallForWidgetId : self . roomDataSource . roomId ] ;
}
- ( BOOL ) isRoomHavingAJitsiCallForWidgetId : ( NSString * ) widgetId
{
return [ [ AppDelegate theDelegate ] . callPresenter . jitsiVC . widget . roomId isEqualToString : widgetId ] ;
}
2021-01-14 10:38:17 +00:00
# pragma mark - Dialpad
- ( void ) openDialpad
{
2021-01-14 15:20:00 +00:00
DialpadViewController * controller = [ DialpadViewController instantiateWithConfiguration : [ DialpadConfiguration default ] ] ;
2021-01-14 10:38:17 +00:00
controller . delegate = self ;
self . customSizedPresentationController = [ [ CustomSizedPresentationController alloc ] initWithPresentedViewController : controller presentingViewController : self ] ;
self . customSizedPresentationController . dismissOnBackgroundTap = NO ;
self . customSizedPresentationController . cornerRadius = 16 ;
controller . transitioningDelegate = self . customSizedPresentationController ;
[ self presentViewController : controller animated : YES completion : nil ] ;
}
# pragma mark - DialpadViewControllerDelegate
- ( void ) dialpadViewControllerDidTapCall : ( DialpadViewController * ) viewController withPhoneNumber : ( NSString * ) phoneNumber
{
if ( self . mainSession . callManager && phoneNumber . length > 0 )
{
[ self startActivityIndicator ] ;
[ viewController dismissViewControllerAnimated : YES completion : ^ {
MXWeakify ( self ) ;
[ self . mainSession . callManager placeCallAgainst : phoneNumber withVideo : NO success : ^ ( MXCall * _Nonnull call ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
self . customSizedPresentationController = nil ;
// do nothing extra here . UI will be handled automatically by the CallService .
} failure : ^ ( NSError * _Nullable error ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
} ] ;
} ] ;
}
}
- ( void ) dialpadViewControllerDidTapClose : ( DialpadViewController * ) viewController
{
[ viewController dismissViewControllerAnimated : YES completion : nil ] ;
self . customSizedPresentationController = nil ;
}
2016-04-14 00:34:30 +00:00
# pragma mark - Hide / Show preview header
- ( void ) showPreviewHeader : ( BOOL ) isVisible
{
2017-01-24 12:35:06 +00:00
if ( self . previewHeaderContainer && self . previewHeaderContainer . isHidden = = isVisible )
2016-04-14 00:34:30 +00:00
{
2017-01-24 12:35:06 +00:00
// Check conditions before making the preview room header visible .
// This operation is ignored if a screen rotation is in progress ,
// or if the view controller is not embedded inside a split view controller yet .
if ( isVisible && ( isSizeTransitionInProgress = = YES || ! self . splitViewController ) )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] Show preview header ignored" ) ;
2017-01-24 12:35:06 +00:00
return ;
}
2016-04-19 13:28:15 +00:00
if ( isVisible )
2016-04-14 00:34:30 +00:00
{
2021-10-29 08:50:24 +00:00
PreviewRoomTitleView * previewHeader = [ PreviewRoomTitleView roomTitleView ] ;
2016-04-14 00:34:30 +00:00
previewHeader . delegate = self ;
previewHeader . tapGestureDelegate = self ;
previewHeader . translatesAutoresizingMaskIntoConstraints = NO ;
[ self . previewHeaderContainer addSubview : previewHeader ] ;
2021-10-29 08:50:24 +00:00
self -> previewHeader = previewHeader ;
2016-04-14 00:34:30 +00:00
// Force preview header in full width
NSLayoutConstraint * leftConstraint = [ NSLayoutConstraint constraintWithItem : previewHeader
attribute : NSLayoutAttributeLeading
relatedBy : NSLayoutRelationEqual
toItem : self . previewHeaderContainer
attribute : NSLayoutAttributeLeading
multiplier : 1.0
constant : 0 ] ;
NSLayoutConstraint * rightConstraint = [ NSLayoutConstraint constraintWithItem : previewHeader
attribute : NSLayoutAttributeTrailing
relatedBy : NSLayoutRelationEqual
toItem : self . previewHeaderContainer
attribute : NSLayoutAttributeTrailing
multiplier : 1.0
constant : 0 ] ;
// Vertical constraints are required for iOS > 8
NSLayoutConstraint * topConstraint = [ NSLayoutConstraint constraintWithItem : previewHeader
attribute : NSLayoutAttributeTop
relatedBy : NSLayoutRelationEqual
toItem : self . previewHeaderContainer
attribute : NSLayoutAttributeTop
multiplier : 1.0
constant : 0 ] ;
NSLayoutConstraint * bottomConstraint = [ NSLayoutConstraint constraintWithItem : previewHeader
attribute : NSLayoutAttributeBottom
relatedBy : NSLayoutRelationEqual
toItem : self . previewHeaderContainer
attribute : NSLayoutAttributeBottom
multiplier : 1.0
constant : 0 ] ;
[ NSLayoutConstraint activateConstraints : @ [ leftConstraint , rightConstraint , topConstraint , bottomConstraint ] ] ;
2016-06-10 15:15:42 +00:00
if ( roomPreviewData )
2016-04-14 00:34:30 +00:00
{
2016-06-10 15:15:42 +00:00
previewHeader . roomPreviewData = roomPreviewData ;
2016-04-14 00:34:30 +00:00
}
2016-06-10 15:15:42 +00:00
else if ( self . roomDataSource )
2016-04-14 00:34:30 +00:00
{
2016-06-10 15:15:42 +00:00
previewHeader . mxRoom = self . roomDataSource . room ;
2016-04-14 00:34:30 +00:00
}
2016-04-19 13:28:15 +00:00
2016-06-08 15:19:00 +00:00
self . previewHeaderContainer . hidden = NO ;
2021-03-15 13:27:59 +00:00
2016-06-08 15:19:00 +00:00
// Finalize preview header display according to the screen orientation
2016-06-08 16:56:19 +00:00
[ self refreshPreviewHeader : UIInterfaceOrientationIsLandscape ( [ [ UIApplication sharedApplication ] statusBarOrientation ] ) ] ;
2016-04-19 13:28:15 +00:00
}
else
{
[ previewHeader removeFromSuperview ] ;
previewHeader = nil ;
2016-06-08 15:19:00 +00:00
self . previewHeaderContainer . hidden = YES ;
// Consider the main navigation controller if the current view controller is embedded inside a split view controller .
UINavigationController * mainNavigationController = self . navigationController ;
if ( self . splitViewController . isCollapsed && self . splitViewController . viewControllers . count )
{
mainNavigationController = self . splitViewController . viewControllers . firstObject ;
}
// Set a default title view class without handling tap gesture ( Let [ self refreshRoomTitle ] refresh this view correctly ) .
[ self setRoomTitleViewClass : RoomTitleView . class ] ;
2021-10-18 13:30:32 +00:00
2016-06-08 15:19:00 +00:00
// Remove the shadow image used to hide the bottom border of the navigation bar when the preview header is displayed
[ mainNavigationController . navigationBar setShadowImage : nil ] ;
[ mainNavigationController . navigationBar setBackgroundImage : nil forBarMetrics : UIBarMetricsDefault ] ;
[ UIView animateWithDuration : 0.3 delay : 0 options : UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
animations : ^ {
2021-03-15 13:27:59 +00:00
self . bubblesTableViewTopConstraint . constant = 0 ;
// Force to render the view
[ self forceLayoutRefresh ] ;
}
2016-06-08 15:19:00 +00:00
completion : ^ ( BOOL finished ) {
2021-03-15 13:27:59 +00:00
} ] ;
2016-04-14 00:34:30 +00:00
}
2016-06-08 15:19:00 +00:00
}
2021-03-15 13:27:59 +00:00
2020-04-21 13:27:27 +00:00
// Consider the main navigation controller if the current view controller is embedded inside a split view controller .
UINavigationController * mainNavigationController = self . navigationController ;
if ( self . splitViewController . isCollapsed && self . splitViewController . viewControllers . count )
{
mainNavigationController = self . splitViewController . viewControllers . firstObject ;
}
mainNavigationController . navigationBar . translucent = isVisible ;
self . navigationController . navigationBar . translucent = isVisible ;
2016-06-08 15:19:00 +00:00
}
2016-06-08 16:22:50 +00:00
- ( void ) refreshPreviewHeader : ( BOOL ) isLandscapeOriented
2016-06-08 15:19:00 +00:00
{
if ( previewHeader )
{
2018-03-12 14:56:41 +00:00
if ( isLandscapeOriented
2018-03-12 16:07:51 +00:00
&& [ GBDeviceInfo deviceInfo ] . family ! = GBDeviceFamilyiPad )
2016-04-14 00:34:30 +00:00
{
2016-06-08 16:22:50 +00:00
CGRect frame = self . navigationController . navigationBar . frame ;
previewHeader . mainHeaderContainer . hidden = YES ;
previewHeader . mainHeaderBackgroundHeightConstraint . constant = frame . size . height + ( frame . origin . y > 0 ? frame . origin . y : 0 ) ;
[ self setRoomTitleViewClass : RoomTitleView . class ] ;
// We don ' t want to handle tap gesture here
// Remove details icon
RoomTitleView * roomTitleView = ( RoomTitleView * ) self . titleView ;
2016-06-10 15:15:42 +00:00
// Set preview data to provide the room name
roomTitleView . roomPreviewData = roomPreviewData ;
2016-04-14 00:34:30 +00:00
}
2016-06-08 16:22:50 +00:00
else
2016-02-10 09:59:48 +00:00
{
2016-06-08 16:22:50 +00:00
previewHeader . mainHeaderContainer . hidden = NO ;
previewHeader . mainHeaderBackgroundHeightConstraint . constant = previewHeader . mainHeaderContainer . frame . size . height ;
2021-03-15 13:27:59 +00:00
2018-03-12 13:59:59 +00:00
if ( [ previewHeader isKindOfClass : PreviewRoomTitleView . class ] )
{
// In case of preview , update the header height so that we can
// display as much as possible the room topic in this header .
2018-03-12 16:07:51 +00:00
// Note : the header height is handled by the previewHeader . mainHeaderBackgroundHeightConstraint .
2018-03-12 13:59:59 +00:00
PreviewRoomTitleView * previewRoomTitleView = ( PreviewRoomTitleView * ) previewHeader ;
2021-03-15 13:27:59 +00:00
2018-03-12 13:59:59 +00:00
// Compute the height required to display all the room topic
CGSize sizeThatFitsTextView = [ previewRoomTitleView . roomTopic sizeThatFits : CGSizeMake ( previewRoomTitleView . roomTopic . frame . size . width , MAXFLOAT ) ] ;
2021-03-15 13:27:59 +00:00
2018-03-12 13:59:59 +00:00
// Increase the preview header height according to the room topic height
// but limit it in order to let room for room messages at the screen bottom .
// This free space depends on the device .
// On an iphone 5 screen , the room topic height cannot be more than 50 px .
// Then , on larger screen , we can allow it a bit more height but we
// apply a factor to give more priority to the display of more messages .
CGFloat screenHeight = [ [ UIScreen mainScreen ] bounds ] . size . height ;
CGFloat maxRoomTopicHeight = 50 + ( screenHeight - 568 ) / 3 ;
2021-03-15 13:27:59 +00:00
2018-03-12 13:59:59 +00:00
CGFloat additionalHeight = MIN ( maxRoomTopicHeight , sizeThatFitsTextView . height )
2021-03-15 13:27:59 +00:00
- previewRoomTitleView . roomTopic . frame . size . height ;
2018-03-12 13:59:59 +00:00
previewHeader . mainHeaderBackgroundHeightConstraint . constant + = additionalHeight ;
}
2021-03-15 13:27:59 +00:00
2016-06-08 16:22:50 +00:00
[ self setRoomTitleViewClass : RoomAvatarTitleView . class ] ;
// Note the avatar title view does not define tap gesture .
2017-10-20 17:22:37 +00:00
previewHeader . roomAvatar . alpha = 0.0 ;
2016-06-08 16:22:50 +00:00
// Set the avatar provided in preview data
if ( roomPreviewData . roomAvatarUrl )
2016-04-14 11:06:38 +00:00
{
2018-11-11 14:20:32 +00:00
previewHeader . roomAvatarURL = roomPreviewData . roomAvatarUrl ;
2016-04-14 11:06:38 +00:00
}
2016-06-08 16:22:50 +00:00
else if ( roomPreviewData . roomId && roomPreviewData . roomName )
2016-06-06 14:01:31 +00:00
{
2017-10-20 17:22:37 +00:00
previewHeader . roomAvatarPlaceholder = [ AvatarGenerator generateAvatarForMatrixItem : roomPreviewData . roomId withDisplayName : roomPreviewData . roomName ] ;
2016-06-06 14:01:31 +00:00
}
2016-06-08 16:22:50 +00:00
else
2016-06-06 14:01:31 +00:00
{
2018-12-03 16:14:32 +00:00
previewHeader . roomAvatarPlaceholder = [ MXKTools paintImage : [ UIImage imageNamed : @ "placeholder" ]
2019-01-11 10:45:27 +00:00
withColor : ThemeService . shared . theme . tintColor ] ;
2016-06-06 14:01:31 +00:00
}
2016-06-08 15:19:00 +00:00
}
2016-06-08 16:22:50 +00:00
// Force the layout of previewHeader to update the position of ' bottomBorderView ' which is used
// to define the actual height of the preview container .
[ previewHeader layoutIfNeeded ] ;
2016-06-10 15:15:42 +00:00
CGRect frame = previewHeader . bottomBorderView . frame ;
self . previewHeaderContainerHeightConstraint . constant = frame . origin . y + frame . size . height ;
2016-06-08 16:22:50 +00:00
// Consider the main navigation controller if the current view controller is embedded inside a split view controller .
UINavigationController * mainNavigationController = self . navigationController ;
if ( self . splitViewController . isCollapsed && self . splitViewController . viewControllers . count )
{
mainNavigationController = self . splitViewController . viewControllers . firstObject ;
}
// When the preview header is displayed , we hide the bottom border of the navigation bar ( the shadow image ) .
// The default shadow image is nil . When non - nil , this property represents a custom shadow image to show instead
// of the default . For a custom shadow image to be shown , a custom background image must also be set with the
// setBackgroundImage : forBarMetrics : method . If the default background image is used , then the default shadow
// image will be used regardless of the value of this property .
UIImage * shadowImage = [ [ UIImage alloc ] init ] ;
[ mainNavigationController . navigationBar setShadowImage : shadowImage ] ;
[ mainNavigationController . navigationBar setBackgroundImage : shadowImage forBarMetrics : UIBarMetricsDefault ] ;
[ UIView animateWithDuration : 0.3 delay : 0 options : UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
animations : ^ {
2021-03-15 13:27:59 +00:00
2021-09-01 09:44:15 +00:00
self . bubblesTableViewTopConstraint . constant = self . previewHeaderContainerHeightConstraint . constant - self . bubblesTableView . adjustedContentInset . top ;
2021-03-15 13:27:59 +00:00
previewHeader . roomAvatar . alpha = 1 ;
// Force to render the view
[ self forceLayoutRefresh ] ;
}
2016-06-08 16:22:50 +00:00
completion : ^ ( BOOL finished ) {
2021-03-15 13:27:59 +00:00
} ] ;
2016-02-10 09:59:48 +00:00
}
}
2016-04-14 00:34:30 +00:00
# pragma mark - Preview
2016-04-15 07:43:56 +00:00
- ( void ) displayRoomPreview : ( RoomPreviewData * ) previewData
2016-04-14 00:34:30 +00:00
{
2016-06-10 15:15:42 +00:00
// Release existing room data source or preview
[ self displayRoom : nil ] ;
2016-04-15 07:43:56 +00:00
if ( previewData )
2016-04-14 00:34:30 +00:00
{
2017-06-09 07:40:22 +00:00
self . eventsAcknowledgementEnabled = NO ;
2016-04-15 07:43:56 +00:00
[ self addMatrixSession : previewData . mxSession ] ;
roomPreviewData = previewData ;
2016-04-14 00:34:30 +00:00
[ self refreshRoomTitle ] ;
2016-06-10 15:15:42 +00:00
if ( roomPreviewData . roomDataSource )
{
[ super displayRoom : roomPreviewData . roomDataSource ] ;
}
2016-04-14 00:34:30 +00:00
}
}
2015-12-07 10:50:13 +00:00
# pragma mark - MXKDataSourceDelegate
- ( Class < MXKCellRendering > ) cellViewClassForCellData : ( MXKCellData * ) cellData
{
2022-01-14 09:23:15 +00:00
RoomTimelineCellIdentifier cellIdentifier = [ self cellIdentifierForCellData : cellData andRoomDataSource : customizedRoomDataSource ] ;
2015-12-07 10:50:13 +00:00
2022-01-14 09:23:15 +00:00
RoomTimelineConfiguration * timelineConfiguration = [ RoomTimelineConfiguration shared ] ;
return [ timelineConfiguration . currentStyle . cellProvider cellViewClassForCellIdentifier : cellIdentifier ] ;;
}
- ( RoomTimelineCellIdentifier ) cellIdentifierForCellData : ( MXKCellData * ) cellData andRoomDataSource : ( RoomDataSource * ) customizedRoomDataSource ;
{
2015-12-07 10:50:13 +00:00
// Sanity check
2021-11-19 15:40:52 +00:00
if ( ! [ cellData conformsToProtocol : @ protocol ( MXKRoomBubbleCellDataStoring ) ] )
2015-12-07 10:50:13 +00:00
{
2022-01-14 09:23:15 +00:00
return RoomTimelineCellIdentifierUnknown ;
2021-11-19 15:40:52 +00:00
}
2022-01-14 09:23:15 +00:00
BOOL showEncryptionBadge = NO ;
RoomTimelineCellIdentifier cellIdentifier ;
2020-02-06 17:46:32 +00:00
2021-11-19 15:40:52 +00:00
id < MXKRoomBubbleCellDataStoring > bubbleData = ( id < MXKRoomBubbleCellDataStoring > ) cellData ;
MXKRoomBubbleCellData * roomBubbleCellData ;
if ( [ bubbleData isKindOfClass : MXKRoomBubbleCellData . class ] )
{
roomBubbleCellData = ( MXKRoomBubbleCellData * ) bubbleData ;
showEncryptionBadge = roomBubbleCellData . containsBubbleComponentWithEncryptionBadge ;
}
// Select the suitable table view cell class , by considering first the empty bubble cell .
if ( bubbleData . hasNoDisplay )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierEmpty ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagRoomCreationIntro )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierRoomCreationIntro ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagRoomCreateWithPredecessor )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierRoomPredecessor ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagKeyVerificationRequestIncomingApproval )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = bubbleData . isPaginationFirstBubble ? RoomTimelineCellIdentifierKeyVerificationIncomingRequestApprovalWithPaginationTitle : RoomTimelineCellIdentifierKeyVerificationIncomingRequestApproval ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagKeyVerificationRequest )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = bubbleData . isPaginationFirstBubble ? RoomTimelineCellIdentifierKeyVerificationRequestStatusWithPaginationTitle : RoomTimelineCellIdentifierKeyVerificationRequestStatus ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagKeyVerificationConclusion )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = bubbleData . isPaginationFirstBubble ? RoomTimelineCellIdentifierKeyVerificationConclusionWithPaginationTitle : RoomTimelineCellIdentifierKeyVerificationConclusion ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagMembership )
{
if ( bubbleData . collapsed )
2020-02-06 17:46:32 +00:00
{
2021-11-19 15:40:52 +00:00
if ( bubbleData . nextCollapsableCellData )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = bubbleData . isPaginationFirstBubble ? RoomTimelineCellIdentifierMembershipCollapsedWithPaginationTitle : RoomTimelineCellIdentifierMembershipCollapsed ;
2021-11-19 15:40:52 +00:00
}
else
{
// Use a normal membership cell for a single membership event
2022-01-14 09:23:15 +00:00
cellIdentifier = bubbleData . isPaginationFirstBubble ? RoomTimelineCellIdentifierMembershipWithPaginationTitle : RoomTimelineCellIdentifierMembership ;
2021-11-19 15:40:52 +00:00
}
2020-02-06 17:46:32 +00:00
}
2021-11-19 15:40:52 +00:00
else if ( bubbleData . collapsedAttributedTextMessage )
2017-06-13 15:43:32 +00:00
{
2021-11-19 15:40:52 +00:00
// The cell ( and its series ) is not collapsed but this cell is the first
// of the series . So , use the cell with the "collapse" button .
2022-01-14 09:23:15 +00:00
cellIdentifier = bubbleData . isPaginationFirstBubble ? RoomTimelineCellIdentifierMembershipExpandedWithPaginationTitle : RoomTimelineCellIdentifierMembershipExpanded ;
2017-06-13 15:43:32 +00:00
}
2021-11-19 15:40:52 +00:00
else
2021-02-21 23:00:54 +00:00
{
2022-01-14 09:23:15 +00:00
cellIdentifier = bubbleData . isPaginationFirstBubble ? RoomTimelineCellIdentifierMembershipWithPaginationTitle : RoomTimelineCellIdentifierMembership ;
2021-02-21 23:00:54 +00:00
}
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagRoomCreateConfiguration )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = bubbleData . isPaginationFirstBubble ? RoomTimelineCellIdentifierRoomCreationCollapsedWithPaginationTitle : RoomTimelineCellIdentifierRoomCreationCollapsed ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagCall )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierDirectCallStatus ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagGroupCall )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierGroupCallStatus ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . attachment . type = = MXKAttachmentTypeVoiceMessage || bubbleData . attachment . type = = MXKAttachmentTypeAudio )
{
2022-01-25 13:53:53 +00:00
if ( bubbleData . isIncoming )
2018-07-27 16:53:51 +00:00
{
2022-01-25 13:53:53 +00:00
if ( bubbleData . isPaginationFirstBubble )
{
cellIdentifier = RoomTimelineCellIdentifierIncomingVoiceMessageWithPaginationTitle ;
}
else if ( bubbleData . shouldHideSenderInformation )
{
cellIdentifier = RoomTimelineCellIdentifierIncomingVoiceMessageWithoutSenderInfo ;
}
else
{
cellIdentifier = RoomTimelineCellIdentifierIncomingVoiceMessage ;
}
2019-12-20 09:43:07 +00:00
}
2021-11-19 15:40:52 +00:00
else
2019-12-20 09:43:07 +00:00
{
2022-01-25 13:53:53 +00:00
if ( bubbleData . isPaginationFirstBubble )
{
cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceMessageWithPaginationTitle ;
}
else if ( bubbleData . shouldHideSenderInformation )
{
cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceMessageWithoutSenderInfo ;
}
else
{
cellIdentifier = RoomTimelineCellIdentifierOutgoingVoiceMessage ;
}
2019-12-20 09:43:07 +00:00
}
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . tag = = RoomBubbleCellDataTagPoll )
{
if ( bubbleData . isPaginationFirstBubble )
2019-12-20 09:43:07 +00:00
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierPollWithPaginationTitle ;
2019-12-20 09:43:07 +00:00
}
2021-11-19 15:40:52 +00:00
else if ( bubbleData . shouldHideSenderInformation )
2017-07-03 15:57:10 +00:00
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierPollWithoutSenderInfo ;
2021-11-19 15:40:52 +00:00
}
else
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierPoll ;
2021-11-19 15:40:52 +00:00
}
}
2021-12-10 15:49:08 +00:00
else if ( bubbleData . tag = = RoomBubbleCellDataTagLocation )
{
2022-01-25 15:43:21 +00:00
if ( bubbleData . isIncoming )
2021-12-10 15:49:08 +00:00
{
2022-01-25 15:43:21 +00:00
if ( bubbleData . isPaginationFirstBubble )
{
cellIdentifier = RoomTimelineCellIdentifierIncomingLocationWithPaginationTitle ;
}
else if ( bubbleData . shouldHideSenderInformation )
{
cellIdentifier = RoomTimelineCellIdentifierIncomingLocationWithoutSenderInfo ;
}
else
{
cellIdentifier = RoomTimelineCellIdentifierIncomingLocation ;
}
2021-12-10 15:49:08 +00:00
}
else
{
2022-01-25 15:43:21 +00:00
if ( bubbleData . isPaginationFirstBubble )
{
cellIdentifier = RoomTimelineCellIdentifierOutgoingLocationWithPaginationTitle ;
}
else if ( bubbleData . shouldHideSenderInformation )
{
cellIdentifier = RoomTimelineCellIdentifierOutgoingLocationWithoutSenderInfo ;
}
else
{
cellIdentifier = RoomTimelineCellIdentifierOutgoingLocation ;
}
2021-12-10 15:49:08 +00:00
}
}
2021-11-19 15:40:52 +00:00
else if ( bubbleData . isIncoming )
{
if ( bubbleData . isAttachmentWithThumbnail )
{
// Check whether the provided celldata corresponds to a selected sticker
if ( customizedRoomDataSource . selectedEventId && ( bubbleData . attachment . type = = MXKAttachmentTypeSticker ) && [ bubbleData . attachment . eventId isEqualToString : customizedRoomDataSource . selectedEventId ] )
2017-07-05 10:29:54 +00:00
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierSelectedSticker ;
2017-07-05 10:29:54 +00:00
}
2021-11-19 15:40:52 +00:00
else if ( bubbleData . isPaginationFirstBubble )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingAttachmentEncryptedWithPaginationTitle : RoomTimelineCellIdentifierIncomingAttachmentWithPaginationTitle ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . shouldHideSenderInformation )
2017-07-07 15:47:58 +00:00
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingAttachmentEncryptedWithoutSenderInfo : RoomTimelineCellIdentifierIncomingAttachmentWithoutSenderInfo ;
2017-07-07 15:47:58 +00:00
}
else
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingAttachmentEncrypted : RoomTimelineCellIdentifierIncomingAttachment ;
2017-07-07 15:47:58 +00:00
}
2017-07-03 15:57:10 +00:00
}
2021-11-19 15:40:52 +00:00
else
2021-06-23 12:37:34 +00:00
{
2021-08-18 10:04:10 +00:00
if ( bubbleData . isPaginationFirstBubble )
{
2021-11-19 15:40:52 +00:00
if ( bubbleData . shouldHideSenderName )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingTextMessageEncryptedWithPaginationTitleWithoutSenderName : RoomTimelineCellIdentifierIncomingTextMessageWithPaginationTitleWithoutSenderName ;
2021-11-19 15:40:52 +00:00
}
else
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingTextMessageEncryptedWithPaginationTitle : RoomTimelineCellIdentifierIncomingTextMessageWithPaginationTitle ;
2021-11-19 15:40:52 +00:00
}
2021-08-18 10:04:10 +00:00
}
else if ( bubbleData . shouldHideSenderInformation )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingTextMessageEncryptedWithoutSenderInfo : RoomTimelineCellIdentifierIncomingTextMessageWithoutSenderInfo ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . shouldHideSenderName )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingTextMessageEncryptedWithoutSenderName : RoomTimelineCellIdentifierIncomingTextMessageWithoutSenderName ;
2021-08-18 10:04:10 +00:00
}
else
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierIncomingTextMessageEncrypted : RoomTimelineCellIdentifierIncomingTextMessage ;
2021-06-14 14:47:59 +00:00
}
}
2021-11-19 15:40:52 +00:00
}
else
{
// Handle here outgoing bubbles
if ( bubbleData . isAttachmentWithThumbnail )
2015-12-07 10:50:13 +00:00
{
2021-11-19 15:40:52 +00:00
// Check whether the provided celldata corresponds to a selected sticker
if ( customizedRoomDataSource . selectedEventId && ( bubbleData . attachment . type = = MXKAttachmentTypeSticker ) && [ bubbleData . attachment . eventId isEqualToString : customizedRoomDataSource . selectedEventId ] )
2015-12-08 09:10:59 +00:00
{
2022-01-14 09:23:15 +00:00
cellIdentifier = RoomTimelineCellIdentifierSelectedSticker ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . isPaginationFirstBubble )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingAttachmentEncryptedWithPaginationTitle : RoomTimelineCellIdentifierOutgoingAttachmentWithPaginationTitle ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . shouldHideSenderInformation )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingAttachmentEncryptedWithoutSenderInfo : RoomTimelineCellIdentifierOutgoingAttachmentWithoutSenderInfo ;
2015-12-08 09:10:59 +00:00
}
else
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingAttachmentEncrypted : RoomTimelineCellIdentifierOutgoingAttachment ;
2015-12-08 09:10:59 +00:00
}
2015-12-07 10:50:13 +00:00
}
else
{
2021-11-19 15:40:52 +00:00
if ( bubbleData . isPaginationFirstBubble )
2015-12-08 09:10:59 +00:00
{
2021-11-19 15:40:52 +00:00
if ( bubbleData . shouldHideSenderName )
2015-12-08 09:10:59 +00:00
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithPaginationTitleWithoutSenderName : RoomTimelineCellIdentifierOutgoingTextMessageWithPaginationTitleWithoutSenderName ;
2015-12-08 09:10:59 +00:00
}
else
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithPaginationTitle : RoomTimelineCellIdentifierOutgoingTextMessageWithPaginationTitle ;
2015-12-08 09:10:59 +00:00
}
}
2021-11-19 15:40:52 +00:00
else if ( bubbleData . shouldHideSenderInformation )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithoutSenderInfo : RoomTimelineCellIdentifierOutgoingTextMessageWithoutSenderInfo ;
2021-11-19 15:40:52 +00:00
}
else if ( bubbleData . shouldHideSenderName )
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingTextMessageEncryptedWithoutSenderName : RoomTimelineCellIdentifierOutgoingTextMessageWithoutSenderName ;
2021-11-19 15:40:52 +00:00
}
2015-12-08 09:10:59 +00:00
else
{
2022-01-14 09:23:15 +00:00
cellIdentifier = showEncryptionBadge ? RoomTimelineCellIdentifierOutgoingTextMessageEncrypted : RoomTimelineCellIdentifierOutgoingTextMessage ;
2015-12-08 09:10:59 +00:00
}
2015-12-07 10:50:13 +00:00
}
}
2022-01-14 09:23:15 +00:00
return cellIdentifier ;
2015-12-07 10:50:13 +00:00
}
2015-03-31 13:09:17 +00:00
# pragma mark - MXKDataSource delegate
2014-10-15 23:34:46 +00:00
2015-06-16 11:54:12 +00:00
- ( void ) dataSource : ( MXKDataSource * ) dataSource didRecognizeAction : ( NSString * ) actionIdentifier inCell : ( id < MXKCellRendering > ) cell userInfo : ( NSDictionary * ) userInfo
{
2015-12-11 13:01:56 +00:00
// Handle here user actions on bubbles for Vector app
if ( customizedRoomDataSource )
2015-06-16 11:54:12 +00:00
{
2019-05-22 14:37:06 +00:00
id < MXKRoomBubbleCellDataStoring > bubbleData ;
if ( [ cell isKindOfClass : [ MXKRoomBubbleTableViewCell class ] ] )
{
MXKRoomBubbleTableViewCell * roomBubbleTableViewCell = ( MXKRoomBubbleTableViewCell * ) cell ;
bubbleData = roomBubbleTableViewCell . bubbleData ;
}
2016-05-17 14:47:55 +00:00
if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellTapOnAvatarView ] )
2016-05-03 21:30:46 +00:00
{
2020-10-16 11:27:45 +00:00
MXRoomMember * member = [ self . roomDataSource . roomState . members memberWithUserId : userInfo [ kMXKRoomBubbleCellUserIdKey ] ] ;
[ self showMemberDetails : member ] ;
2016-05-03 21:30:46 +00:00
}
2016-05-17 14:47:55 +00:00
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellLongPressOnAvatarView ] )
{
// Add the member display name in text input
2018-07-20 09:28:02 +00:00
MXRoomMember * roomMember = [ self . roomDataSource . roomState . members memberWithUserId : userInfo [ kMXKRoomBubbleCellUserIdKey ] ] ;
2016-05-17 14:47:55 +00:00
if ( roomMember )
{
[ self mention : roomMember ] ;
}
}
2016-05-03 21:30:46 +00:00
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellTapOnMessageTextView ] || [ actionIdentifier isEqualToString : kMXKRoomBubbleCellTapOnContentView ] )
2015-12-11 13:01:56 +00:00
{
// Retrieve the tapped event
MXEvent * tappedEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
// Check whether a selection already exist or not
if ( customizedRoomDataSource . selectedEventId )
{
2015-12-18 14:20:57 +00:00
[ self cancelEventSelection ] ;
2015-12-11 13:01:56 +00:00
}
else if ( tappedEvent )
{
2019-02-04 14:49:49 +00:00
if ( tappedEvent . eventType = = MXEventTypeRoomCreate )
{
// Handle tap on RoomPredecessorBubbleCell
MXRoomCreateContent * createContent = [ MXRoomCreateContent modelFromJSON : tappedEvent . content ] ;
NSString * predecessorRoomId = createContent . roomPredecessorInfo . roomId ;
if ( predecessorRoomId )
{
// Show predecessor room
2021-09-07 15:43:49 +00:00
[ self showRoomWithId : predecessorRoomId ] ;
2019-02-04 14:49:49 +00:00
}
2020-09-14 11:24:41 +00:00
else
{
// Show contextual menu on single tap if bubble is not collapsed
if ( bubbleData . collapsed )
{
2020-09-14 16:44:30 +00:00
[ self showRoomCreationModalWithBubbleData : bubbleData ] ;
2020-09-14 11:24:41 +00:00
}
else
{
[ self showContextualMenuForEvent : tappedEvent fromSingleTapGesture : YES cell : cell animated : YES ] ;
}
}
2019-02-04 14:49:49 +00:00
}
2021-01-20 10:27:30 +00:00
else if ( bubbleData . tag = = RoomBubbleCellDataTagCall )
{
2021-01-21 11:27:32 +00:00
if ( [ bubbleData isKindOfClass : [ RoomBubbleCellData class ] ] )
{
// post notification ` RoomCallTileTapped`
2021-02-09 21:17:43 +00:00
[ [ NSNotificationCenter defaultCenter ] postNotificationName : RoomCallTileTappedNotification object : bubbleData ] ;
2021-03-16 21:49:07 +00:00
preventBubblesTableViewScroll = YES ;
[ self selectEventWithId : tappedEvent . eventId ] ;
2021-01-21 11:27:32 +00:00
}
2021-01-20 10:27:30 +00:00
}
2021-03-17 16:41:31 +00:00
else if ( bubbleData . tag = = RoomBubbleCellDataTagGroupCall )
{
if ( [ bubbleData isKindOfClass : [ RoomBubbleCellData class ] ] )
{
// post notification ` RoomGroupCallTileTapped`
[ [ NSNotificationCenter defaultCenter ] postNotificationName : RoomGroupCallTileTappedNotification object : bubbleData ] ;
preventBubblesTableViewScroll = YES ;
[ self selectEventWithId : tappedEvent . eventId ] ;
2021-01-21 11:27:32 +00:00
}
2021-01-20 10:27:30 +00:00
}
2019-02-04 14:49:49 +00:00
else
{
2019-05-22 14:37:06 +00:00
// Show contextual menu on single tap if bubble is not collapsed
if ( bubbleData . collapsed )
{
[ self selectEventWithId : tappedEvent . eventId ] ;
}
else
{
2021-12-20 15:33:59 +00:00
if ( tappedEvent . location ) {
[ _delegate roomViewController : self didRequestLocationPresentationForEvent : tappedEvent bubbleData : bubbleData ] ;
} else {
[ self showContextualMenuForEvent : tappedEvent fromSingleTapGesture : YES cell : cell animated : YES ] ;
}
2019-05-22 14:37:06 +00:00
}
2019-02-04 14:49:49 +00:00
}
2015-12-11 13:01:56 +00:00
}
}
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellTapOnOverlayContainer ] )
{
// Cancel the current event selection
2015-12-18 14:20:57 +00:00
[ self cancelEventSelection ] ;
}
2017-03-09 10:25:36 +00:00
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellRiotEditButtonPressed ] )
2015-12-18 14:20:57 +00:00
{
[ self dismissKeyboard ] ;
MXEvent * selectedEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
2017-07-14 14:41:25 +00:00
2015-12-18 14:20:57 +00:00
if ( selectedEvent )
2015-12-11 13:01:56 +00:00
{
2019-06-27 09:41:25 +00:00
[ self showContextualMenuForEvent : selectedEvent fromSingleTapGesture : YES cell : cell animated : YES ] ;
2016-09-02 07:59:27 +00:00
}
}
2020-01-14 19:23:36 +00:00
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellKeyVerificationIncomingRequestAcceptPressed ] )
{
NSString * eventId = userInfo [ kMXKRoomBubbleCellEventIdKey ] ;
RoomDataSource * roomDataSource = ( RoomDataSource * ) self . roomDataSource ;
[ roomDataSource acceptVerificationRequestForEventId : eventId success : ^ {
2021-03-15 13:27:59 +00:00
2020-01-14 19:23:36 +00:00
} failure : ^ ( NSError * error ) {
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2020-01-14 19:23:36 +00:00
} ] ;
}
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellKeyVerificationIncomingRequestDeclinePressed ] )
{
NSString * eventId = userInfo [ kMXKRoomBubbleCellEventIdKey ] ;
RoomDataSource * roomDataSource = ( RoomDataSource * ) self . roomDataSource ;
[ roomDataSource declineVerificationRequestForEventId : eventId success : ^ {
} failure : ^ ( NSError * error ) {
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2020-01-14 19:23:36 +00:00
} ] ;
}
2018-03-30 10:04:50 +00:00
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellTapOnAttachmentView ] )
2016-09-02 07:59:27 +00:00
{
2018-03-30 10:04:50 +00:00
if ( ( ( MXKRoomBubbleTableViewCell * ) cell ) . bubbleData . attachment . eventSentState = = MXEventSentStateFailed )
{
// Shortcut : when clicking on an unsent media , show the action sheet to resend it
2019-10-22 09:50:25 +00:00
NSString * eventId = ( ( MXKRoomBubbleTableViewCell * ) cell ) . bubbleData . attachment . eventId ;
MXEvent * selectedEvent = [ self . roomDataSource eventWithEventId : eventId ] ;
if ( selectedEvent )
{
[ self dataSource : dataSource didRecognizeAction : kMXKRoomBubbleCellRiotEditButtonPressed inCell : cell userInfo : @ { kMXKRoomBubbleCellEventKey : selectedEvent } ] ;
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomViewController] didRecognizeAction:inCell:userInfo tap on attachment with event state MXEventSentStateFailed. Selected event is nil for event id %@" , eventId ) ;
2019-10-22 09:50:25 +00:00
}
2018-03-30 10:04:50 +00:00
}
else if ( ( ( MXKRoomBubbleTableViewCell * ) cell ) . bubbleData . attachment . type = = MXKAttachmentTypeSticker )
{
// We don ' t open the attachments viewer when the user taps on a sticker .
// We consider this tap like a selection .
// Check whether a selection already exist or not
if ( customizedRoomDataSource . selectedEventId )
{
[ self cancelEventSelection ] ;
}
else
{
// Highlight this event in displayed message
2018-07-23 14:56:03 +00:00
[ self selectEventWithId : ( ( MXKRoomBubbleTableViewCell * ) cell ) . bubbleData . attachment . eventId ] ;
2018-03-30 10:04:50 +00:00
}
}
2018-04-11 09:33:35 +00:00
else
{
// Keep default implementation
[ super dataSource : dataSource didRecognizeAction : actionIdentifier inCell : cell userInfo : userInfo ] ;
}
2016-09-02 07:59:27 +00:00
}
2016-11-16 08:31:58 +00:00
else if ( [ actionIdentifier isEqualToString : kRoomEncryptedDataBubbleCellTapOnEncryptionIcon ] )
{
// Retrieve the tapped event
MXEvent * tappedEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
if ( tappedEvent )
{
[ self showEncryptionInformation : tappedEvent ] ;
}
}
2017-06-26 15:33:53 +00:00
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellTapOnReceiptsContainer ] )
{
MXKReceiptSendersContainer * container = userInfo [ kMXKRoomBubbleCellReceiptsContainerKey ] ;
2017-06-29 15:40:07 +00:00
[ ReadReceiptsViewController openInViewController : self fromContainer : container withSession : self . mainSession ] ;
2017-06-26 15:33:53 +00:00
}
2017-07-07 09:53:49 +00:00
else if ( [ actionIdentifier isEqualToString : kRoomMembershipExpandedBubbleCellTapOnCollapseButton ] )
{
2017-07-17 14:15:49 +00:00
// Reset the selection before collapsing
customizedRoomDataSource . selectedEventId = nil ;
2017-07-07 09:53:49 +00:00
[ self . roomDataSource collapseRoomBubble : ( ( MXKRoomBubbleTableViewCell * ) cell ) . bubbleData collapsed : YES ] ;
}
2019-05-15 21:24:34 +00:00
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellLongPressOnEvent ] )
{
MXEvent * tappedEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
2019-05-22 14:37:06 +00:00
if ( ! bubbleData . collapsed )
{
[ self handleLongPressFromCell : cell withTappedEvent : tappedEvent ] ;
}
2019-05-15 21:24:34 +00:00
}
2019-07-30 15:18:39 +00:00
else if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellLongPressOnReactionView ] )
{
NSString * tappedEventId = userInfo [ kMXKRoomBubbleCellEventIdKey ] ;
if ( tappedEventId )
{
[ self showReactionHistoryForEventId : tappedEventId animated : YES ] ;
}
}
2021-03-17 15:17:54 +00:00
else if ( [ actionIdentifier isEqualToString : RoomDirectCallStatusBubbleCell . callBackAction ] )
2021-01-20 10:27:13 +00:00
{
MXEvent * callInviteEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
MXCallInviteEventContent * eventContent = [ MXCallInviteEventContent modelFromJSON : callInviteEvent . content ] ;
2021-03-15 13:27:59 +00:00
[ self placeCallWithVideo2 : eventContent . isVideoCall ] ;
2021-01-20 10:27:13 +00:00
}
2021-03-17 15:17:54 +00:00
else if ( [ actionIdentifier isEqualToString : RoomDirectCallStatusBubbleCell . declineAction ] )
2021-03-12 01:04:23 +00:00
{
MXEvent * callInviteEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
MXCallInviteEventContent * eventContent = [ MXCallInviteEventContent modelFromJSON : callInviteEvent . content ] ;
MXCall * call = [ self . mainSession . callManager callWithCallId : eventContent . callId ] ;
[ call hangup ] ;
}
2021-03-17 15:17:54 +00:00
else if ( [ actionIdentifier isEqualToString : RoomDirectCallStatusBubbleCell . answerAction ] )
2021-03-12 01:04:23 +00:00
{
MXEvent * callInviteEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
MXCallInviteEventContent * eventContent = [ MXCallInviteEventContent modelFromJSON : callInviteEvent . content ] ;
MXCall * call = [ self . mainSession . callManager callWithCallId : eventContent . callId ] ;
[ call answer ] ;
}
2021-04-27 11:50:58 +00:00
else if ( [ actionIdentifier isEqualToString : RoomDirectCallStatusBubbleCell . endCallAction ] )
{
MXEvent * callInviteEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
MXCallInviteEventContent * eventContent = [ MXCallInviteEventContent modelFromJSON : callInviteEvent . content ] ;
MXCall * call = [ self . mainSession . callManager callWithCallId : eventContent . callId ] ;
[ call hangup ] ;
}
2021-03-17 16:43:51 +00:00
else if ( [ actionIdentifier isEqualToString : RoomGroupCallStatusBubbleCell . joinAction ] ||
[ actionIdentifier isEqualToString : RoomGroupCallStatusBubbleCell . answerAction ] )
{
MXWeakify ( self ) ;
// Check app permissions first
[ MXKTools checkAccessForCall : YES
2021-09-28 05:40:01 +00:00
manualChangeMessageForAudio : [ MatrixKitL10n microphoneAccessNotGrantedForCall : AppInfo . current . displayName ]
manualChangeMessageForVideo : [ MatrixKitL10n cameraAccessNotGrantedForCall : AppInfo . current . displayName ]
2021-03-17 16:43:51 +00:00
showPopUpInViewController : self completionHandler : ^ ( BOOL granted ) {
MXStrongifyAndReturnIfNil ( self ) ;
if ( granted )
{
// Present the Jitsi view controller
Widget * jitsiWidget = [ self -> customizedRoomDataSource jitsiWidget ] ;
if ( jitsiWidget )
{
2021-09-07 15:43:49 +00:00
[ self showJitsiCallWithWidget : jitsiWidget ] ;
2021-03-17 16:43:51 +00:00
}
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] didRecognizeAction:inCell:userInfo Warning: The application does not have the permission to join/answer the group call" ) ;
2021-03-17 16:43:51 +00:00
}
} ] ;
2021-03-18 10:32:34 +00:00
MXEvent * widgetEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
Widget * widget = [ [ Widget alloc ] initWithWidgetEvent : widgetEvent
inMatrixSession : customizedRoomDataSource . mxSession ] ;
[ [ JitsiService shared ] resetDeclineForWidgetWithId : widget . widgetId ] ;
}
2021-04-21 11:52:51 +00:00
else if ( [ actionIdentifier isEqualToString : RoomGroupCallStatusBubbleCell . leaveAction ] )
{
2021-09-07 15:43:49 +00:00
[ self endActiveJitsiCall ] ;
2021-04-21 11:52:51 +00:00
[ self reloadBubblesTable : YES ] ;
}
2021-03-18 10:32:34 +00:00
else if ( [ actionIdentifier isEqualToString : RoomGroupCallStatusBubbleCell . declineAction ] )
{
MXEvent * widgetEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
Widget * widget = [ [ Widget alloc ] initWithWidgetEvent : widgetEvent
inMatrixSession : customizedRoomDataSource . mxSession ] ;
[ [ JitsiService shared ] declineWidgetWithId : widget . widgetId ] ;
[ self reloadBubblesTable : YES ] ;
2021-03-17 16:43:51 +00:00
}
2021-02-21 23:00:54 +00:00
else if ( [ actionIdentifier isEqualToString : RoomCreationIntroCell . tapOnAvatarView ] )
{
[ self showRoomAvatarChange ] ;
}
else if ( [ actionIdentifier isEqualToString : RoomCreationIntroCell . tapOnAddParticipants ] )
{
[ self showAddParticipants ] ;
}
else if ( [ actionIdentifier isEqualToString : RoomCreationIntroCell . tapOnAddTopic ] )
{
[ self showRoomTopicChange ] ;
}
2016-09-02 07:59:27 +00:00
else
{
// Keep default implementation for other actions
[ super dataSource : dataSource didRecognizeAction : actionIdentifier inCell : cell userInfo : userInfo ] ;
}
}
else
{
// Keep default implementation for other actions
[ super dataSource : dataSource didRecognizeAction : actionIdentifier inCell : cell userInfo : userInfo ] ;
}
}
2019-06-27 08:23:20 +00:00
// Display the additiontal event actions menu
- ( void ) showAdditionalActionsMenuForEvent : ( MXEvent * ) selectedEvent inCell : ( id < MXKCellRendering > ) cell animated : ( BOOL ) animated
2016-09-02 07:59:27 +00:00
{
MXKRoomBubbleTableViewCell * roomBubbleTableViewCell = ( MXKRoomBubbleTableViewCell * ) cell ;
MXKAttachment * attachment = roomBubbleTableViewCell . bubbleData . attachment ;
2017-07-14 14:41:25 +00:00
2021-04-15 14:27:41 +00:00
BOOL isJitsiCallEvent = NO ;
switch ( selectedEvent . eventType ) {
case MXEventTypeCustom :
if ( [ selectedEvent . type isEqualToString : kWidgetMatrixEventTypeString ]
|| [ selectedEvent . type isEqualToString : kWidgetModularEventTypeString ] )
{
Widget * widget = [ [ Widget alloc ] initWithWidgetEvent : selectedEvent inMatrixSession : self . roomDataSource . mxSession ] ;
if ( [ widget . type isEqualToString : kWidgetTypeJitsiV1 ] ||
[ widget . type isEqualToString : kWidgetTypeJitsiV2 ] )
{
isJitsiCallEvent = YES ;
}
}
default :
break ;
}
2016-09-02 07:59:27 +00:00
if ( currentAlert )
{
2017-07-14 14:41:25 +00:00
[ currentAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-09-02 07:59:27 +00:00
currentAlert = nil ;
}
2017-07-14 14:41:25 +00:00
2021-10-28 07:16:22 +00:00
MXWeakify ( self ) ;
2021-11-01 15:05:02 +00:00
UIAlertController * actionsMenu = [ UIAlertController alertControllerWithTitle : nil message : nil preferredStyle : UIAlertControllerStyleActionSheet ] ;
2017-07-14 14:41:25 +00:00
2019-06-27 08:23:20 +00:00
// Add actions for a failed event
if ( selectedEvent . sentState = = MXEventSentStateFailed )
2016-09-02 07:59:27 +00:00
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n retry ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
// Let the datasource resend . It will manage local echo , etc .
[ self . roomDataSource resendEventWithEventId : selectedEvent . eventId success : nil failure : nil ] ;
2021-03-15 13:27:59 +00:00
} ] ] ;
2019-06-27 08:23:20 +00:00
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionDelete ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self . roomDataSource removeEventWithEventId : selectedEvent . eventId ] ;
2021-03-15 13:27:59 +00:00
} ] ] ;
2016-09-02 07:59:27 +00:00
}
2017-07-14 14:41:25 +00:00
2016-09-02 07:59:27 +00:00
// Add actions for text message
if ( ! attachment )
{
// Retrieved data related to the selected event
NSArray * components = roomBubbleTableViewCell . bubbleData . bubbleComponents ;
MXKRoomBubbleComponent * selectedComponent ;
for ( selectedComponent in components )
{
if ( [ selectedComponent . event . eventId isEqualToString : selectedEvent . eventId ] )
{
break ;
}
selectedComponent = nil ;
}
2017-07-14 14:41:25 +00:00
2021-03-15 13:27:59 +00:00
2019-06-27 08:23:20 +00:00
// Check status of the selected event
if ( selectedEvent . sentState = = MXEventSentStatePreparing ||
selectedEvent . sentState = = MXEventSentStateEncrypting ||
selectedEvent . sentState = = MXEventSentStateSending )
2016-09-02 07:59:27 +00:00
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionCancelSend ]
style : UIAlertActionStyleDefault
2021-11-17 17:36:44 +00:00
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
self -> currentAlert = nil ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
// Cancel and remove the outgoing message
[ self . roomDataSource . room cancelSendingOperation : selectedEvent . eventId ] ;
[ self . roomDataSource removeEventWithEventId : selectedEvent . eventId ] ;
[ self cancelEventSelection ] ;
2021-03-15 13:27:59 +00:00
} ] ] ;
2019-06-27 08:23:20 +00:00
}
2021-03-15 13:27:59 +00:00
2021-12-17 12:48:42 +00:00
if ( selectedEvent . sentState = = MXEventSentStateSent &&
selectedEvent . eventType ! = MXEventTypePollStart &&
2021-12-20 15:33:59 +00:00
! selectedEvent . location )
2021-11-19 15:40:52 +00:00
{
2021-11-17 17:36:44 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionForward ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-14 09:05:28 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-10-28 07:16:22 +00:00
[ self presentEventForwardingDialogForSelectedEvent : selectedEvent ] ;
} ] ] ;
}
2021-10-14 09:05:28 +00:00
2021-11-19 15:40:52 +00:00
if ( ! isJitsiCallEvent && selectedEvent . eventType ! = MXEventTypePollStart )
2021-04-15 14:27:41 +00:00
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionQuote ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
// 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 ] ;
2021-04-15 14:27:41 +00:00
} ] ] ;
}
2021-03-15 13:27:59 +00:00
2021-11-19 15:40:52 +00:00
if ( ! isJitsiCallEvent && BuildSettings . messageDetailsAllowShare && selectedEvent . eventType ! = MXEventTypePollStart )
2020-07-30 11:42:49 +00:00
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionShare ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self cancelEventSelection ] ;
NSArray * activityItems = @ [ selectedComponent . textMessage ] ;
UIActivityViewController * activityViewController = [ [ UIActivityViewController alloc ] initWithActivityItems : activityItems applicationActivities : nil ] ;
2020-07-30 11:42:49 +00:00
2021-10-28 07:16:22 +00:00
if ( activityViewController )
2020-07-30 11:42:49 +00:00
{
2021-10-28 07:16:22 +00:00
activityViewController . modalTransitionStyle = UIModalTransitionStyleCoverVertical ;
activityViewController . popoverPresentationController . sourceView = roomBubbleTableViewCell ;
activityViewController . popoverPresentationController . sourceRect = roomBubbleTableViewCell . bounds ;
2020-07-30 11:42:49 +00:00
2021-10-28 07:16:22 +00:00
[ self presentViewController : activityViewController animated : YES completion : nil ] ;
2020-07-30 11:42:49 +00:00
}
} ] ] ;
}
2019-06-27 08:23:20 +00:00
}
else // Add action for attachment
{
2021-10-28 07:16:22 +00:00
// Forwarding for already sent attachments
if ( selectedEvent . sentState = = MXEventSentStateSent && ( attachment . type = = MXKAttachmentTypeFile ||
attachment . type = = MXKAttachmentTypeImage ||
attachment . type = = MXKAttachmentTypeVideo ||
attachment . type = = MXKAttachmentTypeVoiceMessage ) ) {
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionForward ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self presentEventForwardingDialogForSelectedEvent : selectedEvent ] ;
2021-10-18 13:30:32 +00:00
} ] ] ;
}
2020-08-14 11:55:06 +00:00
if ( BuildSettings . messageDetailsAllowSave )
2016-09-02 07:59:27 +00:00
{
2020-08-14 11:06:22 +00:00
if ( attachment . type = = MXKAttachmentTypeImage || attachment . type = = MXKAttachmentTypeVideo )
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionSave ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2020-08-14 11:06:22 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
[ self startActivityIndicator ] ;
MXWeakify ( self ) ;
[ attachment save : ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
} failure : ^ ( NSError * error ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
2020-08-14 11:06:22 +00:00
2021-10-28 07:16:22 +00:00
// Alert user
[ self showError : error ] ;
} ] ;
2020-08-14 11:06:22 +00:00
2021-10-28 07:16:22 +00:00
// Start animation in case of download during attachment preparing
[ roomBubbleTableViewCell startProgressUI ] ;
2020-08-14 11:06:22 +00:00
} ] ] ;
}
2016-09-02 07:59:27 +00:00
}
2021-03-15 13:27:59 +00:00
2019-06-27 08:23:20 +00:00
// 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 ] )
2016-09-02 07:59:27 +00:00
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionCancelSend ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
// Get again the loader
MXMediaLoader * loader = [ MXMediaManager existingUploaderWithId : uploadId ] ;
if ( loader )
{
[ loader cancel ] ;
}
// Hide the progress animation
roomBubbleTableViewCell . progressView . hidden = YES ;
2020-07-30 11:42:49 +00:00
2021-10-28 07:16:22 +00:00
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 ] ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
2021-03-15 13:27:59 +00:00
} ] ] ;
}
}
if ( attachment . type ! = MXKAttachmentTypeSticker )
{
if ( BuildSettings . messageDetailsAllowShare )
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionShare ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
[ self startActivityIndicator ] ;
MXWeakify ( self ) ;
[ attachment prepareShare : ^ ( NSURL * fileURL ) {
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self stopActivityIndicator ] ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
self -> documentInteractionController = [ UIDocumentInteractionController interactionControllerWithURL : fileURL ] ;
[ self -> documentInteractionController setDelegate : self ] ;
self -> currentSharedAttachment = attachment ;
2021-10-14 09:05:28 +00:00
2021-10-28 07:16:22 +00:00
if ( ! [ self -> documentInteractionController presentOptionsMenuFromRect : self . view . frame inView : self . view animated : YES ] )
{
self -> documentInteractionController = nil ;
[ attachment onShareEnded ] ;
self -> currentSharedAttachment = nil ;
}
2020-07-30 11:42:49 +00:00
2021-10-28 07:16:22 +00:00
} failure : ^ ( NSError * error ) {
[ self showError : error ] ;
[ self stopActivityIndicator ] ;
} ] ;
2020-07-30 11:42:49 +00:00
2021-10-28 07:16:22 +00:00
// Start animation in case of download during attachment preparing
[ roomBubbleTableViewCell startProgressUI ] ;
2020-07-30 11:42:49 +00:00
} ] ] ;
}
2016-09-02 07:59:27 +00:00
}
}
2017-07-14 14:41:25 +00:00
2016-09-02 07:59:27 +00:00
// Check status of the selected event
2016-12-16 17:24:24 +00:00
if ( selectedEvent . sentState = = MXEventSentStateSent )
2016-09-02 07:59:27 +00:00
{
// Check whether download is in progress
2019-06-27 08:23:20 +00:00
if ( selectedEvent . isMediaAttachment )
2016-09-02 07:59:27 +00:00
{
2018-11-10 13:28:08 +00:00
NSString * downloadId = roomBubbleTableViewCell . bubbleData . attachment . downloadId ;
if ( [ MXMediaManager existingDownloaderWithIdentifier : downloadId ] )
2016-09-02 07:59:27 +00:00
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionCancelDownload ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self cancelEventSelection ] ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
// Get again the loader
MXMediaLoader * loader = [ MXMediaManager existingDownloaderWithIdentifier : downloadId ] ;
if ( loader )
2021-03-15 13:27:59 +00:00
{
2021-10-28 07:16:22 +00:00
[ loader cancel ] ;
2021-03-15 13:27:59 +00:00
}
2021-10-28 07:16:22 +00:00
// Hide the progress animation
roomBubbleTableViewCell . progressView . hidden = YES ;
2021-03-15 13:27:59 +00:00
} ] ] ;
2016-09-02 07:59:27 +00:00
}
}
2017-07-14 14:41:25 +00:00
2019-06-27 08:23:20 +00:00
// Do not allow to redact the event that enabled encryption ( m . room . encryption )
// because it breaks everything
if ( selectedEvent . eventType ! = MXEventTypeRoomEncryption )
2016-09-02 07:59:27 +00:00
{
2021-11-19 15:40:52 +00:00
NSString * title ;
if ( selectedEvent . eventType = = MXEventTypePollStart )
{
title = [ VectorL10n roomEventActionRemovePoll ] ;
}
else
{
title = [ VectorL10n roomEventActionRedact ] ;
}
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : title
2021-11-01 15:05:02 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self startActivityIndicator ] ;
MXWeakify ( self ) ;
[ self . roomDataSource . room redactEvent : selectedEvent . eventId reason : nil success : ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
} failure : ^ ( NSError * error ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
MXLogDebug ( @ "[RoomVC] Redact event (%@) failed" , selectedEvent . eventId ) ;
// Alert user
[ self showError : error ] ;
} ] ;
2021-03-15 13:27:59 +00:00
} ] ] ;
2016-09-02 07:59:27 +00:00
}
2021-03-15 13:27:59 +00:00
2021-11-19 15:40:52 +00:00
if ( selectedEvent . eventType = = MXEventTypePollStart && [ selectedEvent . sender isEqualToString : self . mainSession . myUser . userId ] ) {
if ( [ self . delegate roomViewController : self canEndPollWithEventIdentifier : selectedEvent . eventId ] ) {
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionEndPoll ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self . delegate roomViewController : self endPollWithEventIdentifier : selectedEvent . eventId ] ;
[ self hideContextualMenuAnimated : YES ] ;
} ] ] ;
}
}
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n cancel ]
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self hideContextualMenuAnimated : YES ] ;
} ] ] ;
2020-08-03 13:07:39 +00:00
if ( BuildSettings . messageDetailsAllowPermalink )
2020-07-30 11:40:04 +00:00
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionPermalink ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self cancelEventSelection ] ;
2020-07-30 11:40:04 +00:00
2021-10-28 07:16:22 +00:00
// Create a matrix . to permalink that is common to all matrix clients
NSString * permalink = [ MXTools permalinkToEvent : selectedEvent . eventId inRoom : selectedEvent . roomId ] ;
if ( permalink )
2020-07-30 11:40:04 +00:00
{
2021-10-28 07:16:22 +00:00
MXKPasteboardManager . shared . pasteboard . string = permalink ;
}
else
{
MXLogDebug ( @ "[RoomViewController] Contextual menu permalink action failed. Permalink is nil room id/event id: %@/%@" , selectedEvent . roomId , selectedEvent . eventId ) ;
2020-07-30 11:40:04 +00:00
}
} ] ] ;
}
2017-07-14 14:41:25 +00:00
2019-07-30 15:18:39 +00:00
// Add reaction history if event contains reactions
if ( roomBubbleTableViewCell . bubbleData . reactions [ selectedEvent . eventId ] . aggregatedReactionsWithNonZeroCount )
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionReactionHistory ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
[ self cancelEventSelection ] ;
// Show reaction history
[ self showReactionHistoryForEventId : selectedEvent . eventId animated : YES ] ;
} ] ] ;
2019-07-30 15:18:39 +00:00
}
2020-08-03 13:07:39 +00:00
if ( BuildSettings . messageDetailsAllowViewSource )
2018-08-08 14:14:56 +00:00
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionViewSource ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2020-07-30 11:37:24 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
2020-07-30 11:37:24 +00:00
2021-10-28 07:16:22 +00:00
// Display event details
[ self showEventDetails : selectedEvent ] ;
2020-07-30 11:37:24 +00:00
} ] ] ;
2021-03-15 13:27:59 +00:00
2020-07-30 11:37:24 +00:00
// Add "View Decrypted Source" for e2ee event we can decrypt
if ( selectedEvent . isEncrypted && selectedEvent . clearEvent )
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionViewDecryptedSource ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2020-07-30 11:37:24 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
2020-07-30 11:37:24 +00:00
2021-10-28 07:16:22 +00:00
// Display clear event details
[ self showEventDetails : selectedEvent . clearEvent ] ;
2020-07-30 11:37:24 +00:00
} ] ] ;
}
2018-08-08 14:14:56 +00:00
}
2017-07-14 14:41:25 +00:00
2021-04-29 15:03:34 +00:00
if ( ! [ selectedEvent . sender isEqualToString : self . mainSession . myUser . userId ] && RiotSettings . shared . roomContextualMenuShowReportContentOption )
2020-11-30 13:12:47 +00:00
{
2021-11-01 15:05:02 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionReport ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2020-07-30 15:12:17 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
// Prompt user to enter a description of the problem content .
2021-11-17 17:36:44 +00:00
UIAlertController * reportReasonAlert = [ UIAlertController alertControllerWithTitle : [ VectorL10n roomEventActionReportPromptReason ]
message : nil
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-10-28 07:16:22 +00:00
2021-11-17 17:36:44 +00:00
[ reportReasonAlert addTextFieldWithConfigurationHandler : ^ ( UITextField * textField ) {
2021-10-28 07:16:22 +00:00
textField . secureTextEntry = NO ;
textField . placeholder = nil ;
textField . keyboardType = UIKeyboardTypeDefault ;
} ] ;
MXWeakify ( self ) ;
2021-11-17 17:36:44 +00:00
[ reportReasonAlert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n ok ] style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2020-11-30 13:12:47 +00:00
2021-10-28 07:16:22 +00:00
NSString * text = [ self -> currentAlert textFields ] . firstObject . text ;
self -> currentAlert = nil ;
2020-11-30 13:12:47 +00:00
2021-10-28 07:16:22 +00:00
[ self startActivityIndicator ] ;
2020-11-30 13:12:47 +00:00
2021-10-28 07:16:22 +00:00
MXWeakify ( self ) ;
[ self . roomDataSource . room reportEvent : selectedEvent . eventId score : -100 reason : text success : ^ {
MXStrongifyAndReturnIfNil ( self ) ;
2020-07-30 11:38:36 +00:00
2021-10-28 07:16:22 +00:00
[ self stopActivityIndicator ] ;
// Prompt user to ignore content from this user
2021-11-17 17:36:44 +00:00
UIAlertController * ignoreUserAlert = [ UIAlertController alertControllerWithTitle : [ VectorL10n roomEventActionReportPromptIgnoreUser ]
message : nil
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-10-28 07:16:22 +00:00
MXWeakify ( self ) ;
2021-11-17 17:36:44 +00:00
[ ignoreUserAlert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n yes ] style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2020-11-30 13:12:47 +00:00
self -> currentAlert = nil ;
2020-07-30 11:38:36 +00:00
2020-11-30 13:12:47 +00:00
[ self startActivityIndicator ] ;
2020-07-30 15:12:17 +00:00
2021-10-28 07:16:22 +00:00
MXWeakify ( self ) ;
// Add the user to the blacklist : ignored users
[ self . mainSession ignoreUsers : @ [ selectedEvent . sender ] success : ^ {
MXStrongifyAndReturnIfNil ( self ) ;
2020-11-30 13:12:47 +00:00
[ self stopActivityIndicator ] ;
} failure : ^ ( NSError * error ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2020-11-30 13:12:47 +00:00
[ self stopActivityIndicator ] ;
2021-10-28 07:16:22 +00:00
MXLogDebug ( @ "[RoomVC] Ignore user (%@) failed" , selectedEvent . sender ) ;
2020-11-30 13:12:47 +00:00
// Alert user
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2020-11-30 13:12:47 +00:00
} ] ;
2021-10-28 07:16:22 +00:00
} ] ] ;
2020-11-30 13:12:47 +00:00
2021-11-17 17:36:44 +00:00
[ ignoreUserAlert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n no ] style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2020-11-30 13:12:47 +00:00
self -> currentAlert = nil ;
2021-10-28 07:16:22 +00:00
} ] ] ;
2020-11-30 13:12:47 +00:00
2021-11-17 17:36:44 +00:00
[ self presentViewController : ignoreUserAlert animated : YES completion : nil ] ;
self -> currentAlert = ignoreUserAlert ;
2021-10-28 07:16:22 +00:00
} failure : ^ ( NSError * error ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
MXLogDebug ( @ "[RoomVC] Report event (%@) failed" , selectedEvent . eventId ) ;
// Alert user
[ self showError : error ] ;
} ] ;
} ] ] ;
2020-11-30 13:12:47 +00:00
2021-11-17 17:36:44 +00:00
[ reportReasonAlert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n cancel ] style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
self -> currentAlert = nil ;
} ] ] ;
2021-11-17 17:36:44 +00:00
[ self presentViewController : reportReasonAlert animated : YES completion : nil ] ;
self -> currentAlert = reportReasonAlert ;
2020-11-30 13:12:47 +00:00
} ] ] ;
}
2021-03-15 13:27:59 +00:00
2021-04-15 14:27:41 +00:00
if ( ! isJitsiCallEvent && self . roomDataSource . room . summary . isEncrypted )
2016-11-16 08:31:58 +00:00
{
2021-11-19 15:40:52 +00:00
[ actionsMenu addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomEventActionViewEncryption ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-10-28 07:16:22 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
[ self cancelEventSelection ] ;
2021-03-15 13:27:59 +00:00
2021-10-28 07:16:22 +00:00
// Display encryption details
[ self showEncryptionInformation : selectedEvent ] ;
2021-03-15 13:27:59 +00:00
} ] ] ;
2015-12-11 13:01:56 +00:00
}
2021-03-15 13:27:59 +00:00
2021-11-19 15:40:52 +00:00
}
2016-09-02 07:59:27 +00:00
// Do not display empty action sheet
2021-11-01 15:05:02 +00:00
if ( actionsMenu . actions . count > 1 )
2016-09-02 07:59:27 +00:00
{
2019-07-25 14:46:35 +00:00
NSInteger bubbleComponentIndex = [ roomBubbleTableViewCell . bubbleData bubbleComponentIndexForEventId : selectedEvent . eventId ] ;
2019-06-27 08:23:20 +00:00
2019-07-25 14:46:35 +00:00
CGRect sourceRect = [ roomBubbleTableViewCell componentFrameInContentViewForIndex : bubbleComponentIndex ] ;
2019-06-27 08:23:20 +00:00
2021-11-01 15:05:02 +00:00
[ actionsMenu mxk_setAccessibilityIdentifier : @ "RoomVCEventMenuAlert" ] ;
[ actionsMenu popoverPresentationController ] . sourceView = roomBubbleTableViewCell ;
[ actionsMenu popoverPresentationController ] . sourceRect = sourceRect ;
[ self presentViewController : actionsMenu animated : animated completion : nil ] ;
currentAlert = actionsMenu ;
2015-03-31 13:09:17 +00:00
}
}
2021-10-28 07:16:22 +00:00
- ( void ) presentEventForwardingDialogForSelectedEvent : ( MXEvent * ) selectedEvent
{
ForwardingShareItemSender * shareItemSender = [ [ ForwardingShareItemSender alloc ] initWithEvent : selectedEvent ] ;
self . shareManager = [ [ ShareManager alloc ] initWithShareItemSender : shareItemSender
type : ShareManagerTypeForward ] ;
MXWeakify ( self ) ;
[ self . shareManager setCompletionCallback : ^ ( ShareManagerResult result ) {
MXStrongifyAndReturnIfNil ( self ) ;
if ( [ self . presentedViewController isEqual : self . shareManager . mainViewController ] )
{
[ self dismissViewControllerAnimated : YES completion : nil ] ;
}
self . shareManager = nil ;
} ] ;
[ self presentViewController : self . shareManager . mainViewController animated : YES completion : nil ] ;
}
2016-04-18 15:53:30 +00:00
- ( BOOL ) dataSource : ( MXKDataSource * ) dataSource shouldDoAction : ( NSString * ) actionIdentifier inCell : ( id < MXKCellRendering > ) cell userInfo : ( NSDictionary * ) userInfo defaultValue : ( BOOL ) defaultValue
{
BOOL shouldDoAction = defaultValue ;
2017-07-14 14:41:25 +00:00
2016-04-18 15:53:30 +00:00
if ( [ actionIdentifier isEqualToString : kMXKRoomBubbleCellShouldInteractWithURL ] )
{
// Try to catch universal link supported by the app
NSURL * url = userInfo [ kMXKRoomBubbleCellUrl ] ;
2019-03-04 17:29:13 +00:00
// Retrieve the type of interaction expected with the URL ( See UITextItemInteraction )
NSNumber * urlItemInteractionValue = userInfo [ kMXKRoomBubbleCellUrlItemInteraction ] ;
2016-09-29 15:50:20 +00:00
2019-12-03 18:58:51 +00:00
RoomMessageURLType roomMessageURLType = RoomMessageURLTypeUnknown ;
if ( url )
{
roomMessageURLType = [ self . roomMessageURLParser parseURL : url ] ;
}
2016-09-29 15:50:20 +00:00
// When a link refers to a room alias / id , a user id or an event id , the non - ASCII characters ( like ' # ' in room alias ) has been escaped
// to be able to convert it into a legal URL string .
2019-02-04 20:15:47 +00:00
NSString * absoluteURLString = [ url . absoluteString stringByRemovingPercentEncoding ] ;
2017-07-14 14:41:25 +00:00
2016-04-29 14:19:13 +00:00
// If the link can be open it by the app , let it do
if ( [ Tools isUniversalLink : url ] )
2016-04-18 15:53:30 +00:00
{
2016-04-29 14:19:13 +00:00
shouldDoAction = NO ;
2017-07-14 14:41:25 +00:00
2021-09-07 15:43:49 +00:00
[ self handleUniversalLinkURL : url ] ;
2016-04-18 15:53:30 +00:00
}
2016-08-26 14:20:20 +00:00
// Open a detail screen about the clicked user
2016-09-29 15:50:20 +00:00
else if ( [ MXTools isMatrixUserIdentifier : absoluteURLString ] )
2016-08-26 14:00:32 +00:00
{
shouldDoAction = NO ;
2017-07-14 14:41:25 +00:00
2016-09-29 15:50:20 +00:00
NSString * userId = absoluteURLString ;
2017-07-14 14:41:25 +00:00
2018-07-20 09:28:02 +00:00
MXRoomMember * member = [ self . roomDataSource . roomState . members memberWithUserId : userId ] ;
2016-08-26 14:00:32 +00:00
if ( member )
{
// Use the room member detail VC for room members
2020-10-16 11:27:45 +00:00
[ self showMemberDetails : member ] ;
2016-08-26 14:00:32 +00:00
}
else
{
// Use the contact detail VC for other users
MXUser * user = [ self . roomDataSource . room . mxSession userWithUserId : userId ] ;
if ( user )
{
selectedContact = [ [ MXKContact alloc ] initMatrixContactWithDisplayName : ( ( user . displayname . length > 0 ) ? user . displayname : user . userId ) andMatrixID : user . userId ] ;
}
else
{
selectedContact = [ [ MXKContact alloc ] initMatrixContactWithDisplayName : userId andMatrixID : userId ] ;
}
[ self performSegueWithIdentifier : @ "showContactDetails" sender : self ] ;
}
}
2016-08-26 14:16:49 +00:00
// Open the clicked room
2016-09-29 15:50:20 +00:00
else if ( [ MXTools isMatrixRoomIdentifier : absoluteURLString ] || [ MXTools isMatrixRoomAlias : absoluteURLString ] )
2016-08-26 14:16:49 +00:00
{
shouldDoAction = NO ;
2017-07-14 14:41:25 +00:00
2016-09-29 15:50:20 +00:00
NSString * roomIdOrAlias = absoluteURLString ;
2017-07-14 14:41:25 +00:00
2016-08-26 14:16:49 +00:00
// Open the room or preview it
2019-02-18 14:31:55 +00:00
NSString * fragment = [ NSString stringWithFormat : @ "/room/%@" , [ MXTools encodeURIComponent : roomIdOrAlias ] ] ;
2021-09-07 15:43:49 +00:00
[ self handleUniversalLinkFragment : fragment fromURL : url ] ;
2016-08-26 14:16:49 +00:00
}
2017-12-31 15:24:47 +00:00
// Preview the clicked group
else if ( [ MXTools isMatrixGroupIdentifier : absoluteURLString ] )
{
shouldDoAction = NO ;
// Open the group or preview it
2019-02-18 14:31:55 +00:00
NSString * fragment = [ NSString stringWithFormat : @ "/group/%@" , [ MXTools encodeURIComponent : absoluteURLString ] ] ;
2021-09-07 15:43:49 +00:00
[ self handleUniversalLinkFragment : fragment fromURL : url ] ;
2017-12-31 15:24:47 +00:00
}
2019-06-12 14:06:27 +00:00
else if ( [ absoluteURLString hasPrefix : EventFormatterOnReRequestKeysLinkAction ] )
2018-06-08 15:21:52 +00:00
{
2019-06-12 14:06:27 +00:00
NSArray < NSString * > * arguments = [ absoluteURLString componentsSeparatedByString : EventFormatterLinkActionSeparator ] ;
2018-06-08 15:21:52 +00:00
if ( arguments . count > 1 )
{
NSString * eventId = arguments [ 1 ] ;
MXEvent * event = [ self . roomDataSource eventWithEventId : eventId ] ;
2021-03-15 13:27:59 +00:00
2018-06-08 15:21:52 +00:00
if ( event )
{
2018-06-12 14:28:26 +00:00
[ self reRequestKeysAndShowExplanationAlert : event ] ;
2018-06-08 15:21:52 +00:00
}
}
}
2019-06-12 14:06:27 +00:00
else if ( [ absoluteURLString hasPrefix : EventFormatterEditedEventLinkAction ] )
{
NSArray < NSString * > * arguments = [ absoluteURLString componentsSeparatedByString : EventFormatterLinkActionSeparator ] ;
if ( arguments . count > 1 )
{
NSString * eventId = arguments [ 1 ] ;
2019-07-04 17:24:15 +00:00
[ self showEditHistoryForEventId : eventId animated : YES ] ;
2019-06-12 14:06:27 +00:00
}
shouldDoAction = NO ;
}
2019-03-04 17:29:13 +00:00
else if ( url && urlItemInteractionValue )
{
// Fallback case for external links
switch ( urlItemInteractionValue . integerValue ) {
2019-05-15 21:24:34 +00:00
case UITextItemInteractionInvokeDefaultAction :
2019-12-03 18:58:51 +00:00
{
switch ( roomMessageURLType ) {
case RoomMessageURLTypeAppleDataDetector :
// Keep the default OS behavior on single tap when UITextView data detector detect a known type .
shouldDoAction = YES ;
break ;
case RoomMessageURLTypeDummy :
// Do nothing for dummy links
shouldDoAction = NO ;
break ;
default :
2020-07-03 11:23:44 +00:00
{
MXEvent * tappedEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
NSString * format = tappedEvent . content [ @ "format" ] ;
NSString * formattedBody = tappedEvent . content [ @ "formatted_body" ] ;
// if an html formatted body exists
if ( [ format isEqualToString : kMXRoomMessageFormatHTML ] && formattedBody )
{
2020-07-14 12:39:04 +00:00
NSURL * visibleURL = [ formattedBodyParser getVisibleURLForURL : url inFormattedBody : formattedBody ] ;
2020-07-03 11:23:44 +00:00
if ( visibleURL && ! [ url isEqual : visibleURL ] )
{
// urls are different , show confirmation alert
2021-09-28 05:40:01 +00:00
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : [ VectorL10n externalLinkConfirmationTitle ] message : [ VectorL10n externalLinkConfirmationMessage : visibleURL . absoluteString : url . absoluteString ] preferredStyle : UIAlertControllerStyleAlert ] ;
2020-07-03 11:23:44 +00:00
2021-09-28 05:40:01 +00:00
UIAlertAction * continueAction = [ UIAlertAction actionWithTitle : [ VectorL10n continue ] style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * _Nonnull action ) {
2020-07-03 11:23:44 +00:00
// Try to open the link
[ [ UIApplication sharedApplication ] vc_open : url completionHandler : ^ ( BOOL success ) {
if ( ! success )
{
[ self showUnableToOpenLinkErrorAlert ] ;
}
} ] ;
} ] ;
2021-09-28 05:40:01 +00:00
UIAlertAction * cancelAction = [ UIAlertAction actionWithTitle : [ VectorL10n cancel ] style : UIAlertActionStyleCancel handler : nil ] ;
2020-07-03 11:23:44 +00:00
[ alert addAction : continueAction ] ;
[ alert addAction : cancelAction ] ;
2020-07-14 08:28:17 +00:00
[ self presentViewController : alert animated : YES completion : nil ] ;
2020-07-03 11:23:44 +00:00
return NO ;
}
}
2019-12-03 18:58:51 +00:00
// Try to open the link
[ [ UIApplication sharedApplication ] vc_open : url completionHandler : ^ ( BOOL success ) {
if ( ! success )
{
[ self showUnableToOpenLinkErrorAlert ] ;
}
} ] ;
shouldDoAction = NO ;
break ;
2020-07-03 11:23:44 +00:00
}
2021-03-15 13:27:59 +00:00
}
2019-03-04 17:29:13 +00:00
}
break ;
2019-05-15 21:24:34 +00:00
case UITextItemInteractionPresentActions :
{
2019-12-03 18:58:51 +00:00
// Retrieve the tapped event
MXEvent * tappedEvent = userInfo [ kMXKRoomBubbleCellEventKey ] ;
if ( tappedEvent )
{
// Long press on link , present room contextual menu .
[ self showContextualMenuForEvent : tappedEvent fromSingleTapGesture : NO cell : cell animated : YES ] ;
}
2019-05-15 21:24:34 +00:00
shouldDoAction = NO ;
}
2019-03-04 17:29:13 +00:00
break ;
2019-05-15 21:24:34 +00:00
case UITextItemInteractionPreview :
2019-03-04 17:29:13 +00:00
// Force touch on link , let MXKRoomBubbleTableViewCell UITextView use default peek and pop behavior .
break ;
default :
break ;
}
}
else
{
[ self showUnableToOpenLinkErrorAlert ] ;
}
2016-04-18 15:53:30 +00:00
}
2017-07-14 14:41:25 +00:00
2016-04-18 15:53:30 +00:00
return shouldDoAction ;
}
2018-07-23 14:56:03 +00:00
- ( void ) selectEventWithId : ( NSString * ) eventId
{
2019-06-13 14:38:20 +00:00
[ self selectEventWithId : eventId inputToolBarSendMode : RoomInputToolbarViewSendModeSend showTimestamp : YES ] ;
2019-05-15 21:24:34 +00:00
}
2019-04-30 15:07:43 +00:00
2019-06-13 14:38:20 +00:00
- ( void ) selectEventWithId : ( NSString * ) eventId inputToolBarSendMode : ( RoomInputToolbarViewSendMode ) inputToolBarSendMode showTimestamp : ( BOOL ) showTimestamp
2019-05-15 21:24:34 +00:00
{
2021-03-24 21:17:09 +00:00
[ self setInputToolBarSendMode : inputToolBarSendMode forEventWithId : eventId ] ;
2019-05-15 21:24:34 +00:00
customizedRoomDataSource . showBubbleDateTimeOnSelection = showTimestamp ;
2018-07-23 14:56:03 +00:00
customizedRoomDataSource . selectedEventId = eventId ;
2019-05-15 21:24:34 +00:00
// Force table refresh
[ self dataSource : self . roomDataSource didCellChange : nil ] ;
2018-07-23 14:56:03 +00:00
}
2015-12-18 14:20:57 +00:00
- ( void ) cancelEventSelection
{
2021-03-24 21:17:09 +00:00
[ self setInputToolBarSendMode : RoomInputToolbarViewSendModeSend forEventWithId : nil ] ;
2018-07-23 14:56:03 +00:00
2016-05-03 16:29:54 +00:00
if ( currentAlert )
2015-12-18 14:20:57 +00:00
{
2017-07-14 14:41:25 +00:00
[ currentAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-05-03 16:29:54 +00:00
currentAlert = nil ;
2015-12-18 14:20:57 +00:00
}
2017-07-14 14:41:25 +00:00
2019-05-15 21:24:34 +00:00
customizedRoomDataSource . showBubbleDateTimeOnSelection = YES ;
2015-12-18 14:20:57 +00:00
customizedRoomDataSource . selectedEventId = nil ;
2017-07-14 14:41:25 +00:00
2019-06-13 14:38:20 +00:00
[ self restoreTextMessageBeforeEditing ] ;
2016-03-09 17:29:39 +00:00
// Force table refresh
[ self dataSource : self . roomDataSource didCellChange : nil ] ;
2015-12-18 14:20:57 +00:00
}
2019-03-04 17:29:13 +00:00
- ( void ) showUnableToOpenLinkErrorAlert
{
2021-09-28 05:40:01 +00:00
[ self showAlertWithTitle : [ MatrixKitL10n error ]
message : [ VectorL10n roomMessageUnableOpenLinkErrorMessage ] ] ;
2019-03-04 17:29:13 +00:00
}
2019-06-13 14:38:20 +00:00
- ( void ) editEventContentWithId : ( NSString * ) eventId
{
MXEvent * event = [ self . roomDataSource eventWithEventId : eventId ] ;
RoomInputToolbarView * roomInputToolbarView = [ self inputToolbarViewAsRoomInputToolbarView ] ;
if ( roomInputToolbarView )
{
self . textMessageBeforeEditing = roomInputToolbarView . textMessage ;
2019-07-03 09:13:53 +00:00
roomInputToolbarView . textMessage = [ self . roomDataSource editableTextMessageForEvent : event ] ;
2019-06-13 14:38:20 +00:00
}
[ 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 ;
}
2020-01-22 15:27:04 +00:00
# pragma mark - RoomDataSourceDelegate
- ( void ) roomDataSource : ( RoomDataSource * ) roomDataSource didUpdateEncryptionTrustLevel : ( RoomEncryptionTrustLevel ) roomEncryptionTrustLevel
{
[ self updateInputToolbarEncryptionDecoration ] ;
2020-04-08 13:07:24 +00:00
[ self updateTitleViewEncryptionDecoration ] ;
2020-01-22 15:27:04 +00:00
}
2014-12-23 17:51:49 +00:00
# pragma mark - Segues
2015-06-16 11:54:12 +00:00
- ( void ) prepareForSegue : ( UIStoryboardSegue * ) segue sender : ( id ) sender
{
2015-06-19 22:45:27 +00:00
// Keep ref on destinationViewController
[ super prepareForSegue : segue sender : sender ] ;
2017-07-14 14:41:25 +00:00
2015-06-19 22:45:27 +00:00
id pushedViewController = [ segue destinationViewController ] ;
2017-07-14 14:41:25 +00:00
2020-10-15 11:18:13 +00:00
if ( [ [ segue identifier ] isEqualToString : @ "showRoomSearch" ] )
2015-12-31 09:12:45 +00:00
{
// Dismiss keyboard
[ self dismissKeyboard ] ;
2017-07-14 14:41:25 +00:00
2015-12-31 09:12:45 +00:00
RoomSearchViewController * roomSearchViewController = ( RoomSearchViewController * ) pushedViewController ;
2016-10-05 07:36:52 +00:00
// Add the current data source to be able to search messages .
roomSearchViewController . roomDataSource = self . roomDataSource ;
2015-12-31 09:12:45 +00:00
}
2016-08-26 14:00:32 +00:00
else if ( [ [ segue identifier ] isEqualToString : @ "showContactDetails" ] )
{
if ( selectedContact )
{
ContactDetailsViewController * contactDetailsViewController = segue . destinationViewController ;
2016-11-10 14:21:28 +00:00
contactDetailsViewController . enableVoipCall = NO ;
2016-08-26 14:00:32 +00:00
contactDetailsViewController . contact = selectedContact ;
2017-07-14 14:41:25 +00:00
2016-08-26 14:00:32 +00:00
selectedContact = nil ;
}
}
2017-02-14 17:21:46 +00:00
else if ( [ [ segue identifier ] isEqualToString : @ "showUnknownDevices" ] )
{
if ( unknownDevices )
{
UsersDevicesViewController * usersDevicesViewController = ( UsersDevicesViewController * ) segue . destinationViewController . childViewControllers . firstObject ;
2017-03-15 14:54:50 +00:00
[ usersDevicesViewController displayUsersDevices : unknownDevices andMatrixSession : self . roomDataSource . mxSession onComplete : nil ] ;
2017-07-14 14:41:25 +00:00
2017-02-14 17:21:46 +00:00
unknownDevices = nil ;
}
}
2017-07-14 14:41:25 +00:00
2015-11-20 16:07:45 +00:00
// Hide back button title
2015-11-26 15:47:24 +00:00
self . navigationItem . backBarButtonItem = [ [ UIBarButtonItem alloc ] initWithTitle : @ "" style : UIBarButtonItemStylePlain target : nil action : nil ] ;
2014-12-23 17:51:49 +00:00
}
2021-03-15 13:27:59 +00:00
# pragma mark - VoIP
2016-03-09 17:42:25 +00:00
2021-03-15 13:27:59 +00:00
- ( void ) placeCallWithVideo : ( BOOL ) video
2015-08-18 14:24:48 +00:00
{
2017-08-18 07:02:54 +00:00
__weak __typeof ( self ) weakSelf = self ;
2021-03-15 13:27:59 +00:00
2017-08-18 07:02:54 +00:00
// Check app permissions first
[ MXKTools checkAccessForCall : video
2021-09-28 05:40:01 +00:00
manualChangeMessageForAudio : [ MatrixKitL10n microphoneAccessNotGrantedForCall : AppInfo . current . displayName ]
manualChangeMessageForVideo : [ MatrixKitL10n cameraAccessNotGrantedForCall : AppInfo . current . displayName ]
2017-08-18 07:02:54 +00:00
showPopUpInViewController : self completionHandler : ^ ( BOOL granted ) {
2021-01-14 10:38:17 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
if ( granted )
{
if ( video )
{
2021-03-15 13:27:59 +00:00
[ self placeCallWithVideo2 : video ] ;
2021-01-14 10:38:17 +00:00
}
2021-02-12 12:44:41 +00:00
else if ( self . mainSession . callManager . supportsPSTN )
2021-01-14 10:38:17 +00:00
{
2021-03-15 13:27:59 +00:00
[ self showVoiceCallActionSheet ] ;
2021-01-14 10:38:17 +00:00
}
2021-02-12 12:44:41 +00:00
else
{
2021-03-15 13:27:59 +00:00
[ self placeCallWithVideo2 : NO ] ;
2021-02-12 12:44:41 +00:00
}
2021-01-14 10:38:17 +00:00
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "RoomViewController: Warning: The application does not have the permission to place the call" ) ;
2021-01-14 10:38:17 +00:00
}
}
} ] ;
}
2017-08-18 07:02:54 +00:00
2021-03-15 13:27:59 +00:00
- ( void ) showVoiceCallActionSheet
2021-01-14 10:38:17 +00:00
{
// Ask the user the kind of the call : voice or dialpad ?
2021-11-01 15:05:02 +00:00
UIAlertController * callActionSheet = [ UIAlertController alertControllerWithTitle : nil
message : nil
preferredStyle : UIAlertControllerStyleActionSheet ] ;
2021-03-15 13:27:59 +00:00
2021-01-14 10:38:17 +00:00
__weak typeof ( self ) weakSelf = self ;
2021-11-01 15:05:02 +00:00
[ callActionSheet addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomPlaceVoiceCall ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> currentAlert = nil ;
[ self placeCallWithVideo2 : NO ] ;
}
} ] ] ;
2021-01-14 10:38:17 +00:00
2021-11-01 15:05:02 +00:00
[ callActionSheet addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomOpenDialpad ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> currentAlert = nil ;
[ self openDialpad ] ;
}
} ] ] ;
2021-01-14 10:38:17 +00:00
2021-11-01 15:05:02 +00:00
[ callActionSheet addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n cancel ]
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> currentAlert = nil ;
}
} ] ] ;
2021-11-01 15:05:02 +00:00
[ callActionSheet popoverPresentationController ] . barButtonItem = self . navigationItem . rightBarButtonItems . firstObject ;
[ callActionSheet popoverPresentationController ] . permittedArrowDirections = UIPopoverArrowDirectionUp ;
[ self presentViewController : callActionSheet animated : YES completion : nil ] ;
currentAlert = callActionSheet ;
2017-08-18 07:02:54 +00:00
}
2021-03-15 13:27:59 +00:00
- ( void ) placeCallWithVideo2 : ( BOOL ) video
2017-08-18 07:02:54 +00:00
{
Widget * jitsiWidget = [ customizedRoomDataSource jitsiWidget ] ;
if ( jitsiWidget )
{
2021-04-21 09:47:28 +00:00
// If there is already a Jitsi call , join it
2021-09-07 15:43:49 +00:00
[ self showJitsiCallWithWidget : jitsiWidget ] ;
2017-08-11 15:02:45 +00:00
}
2021-04-21 09:47:28 +00:00
else
2017-08-17 16:59:02 +00:00
{
2021-04-21 09:47:28 +00:00
if ( self . roomDataSource . room . summary . membersCount . joined = = 2 && self . roomDataSource . room . isDirect )
{
// Matrix call
[ self . roomDataSource . room placeCallWithVideo : video success : nil failure : nil ] ;
2021-03-15 13:27:59 +00:00
}
2021-04-21 09:47:28 +00:00
else
{
// Jitsi call
if ( self . canEditJitsiWidget )
2021-03-15 13:27:59 +00:00
{
2021-04-21 09:47:28 +00:00
// User has right to add a Jitsi widget
// Create the Jitsi widget and open it directly
[ self startActivityIndicator ] ;
2021-03-15 13:27:59 +00:00
2021-04-21 09:47:28 +00:00
MXWeakify ( self ) ;
[ [ WidgetManager sharedManager ] createJitsiWidgetInRoom : self . roomDataSource . room
withVideo : video
success : ^ ( Widget * jitsiWidget )
{
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
2021-09-07 15:43:49 +00:00
[ self showJitsiCallWithWidget : jitsiWidget ] ;
2021-04-21 09:47:28 +00:00
}
failure : ^ ( NSError * error )
{
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
[ self showJitsiErrorAsAlert : error ] ;
} ] ;
2021-03-15 13:27:59 +00:00
}
2021-04-21 09:47:28 +00:00
else
2021-03-15 13:27:59 +00:00
{
2021-04-21 09:47:28 +00:00
// Insufficient privileges to add a Jitsi widget
MXWeakify ( self ) ;
[ currentAlert dismissViewControllerAnimated : NO completion : nil ] ;
2021-11-01 15:05:02 +00:00
UIAlertController * unprivilegedAlert = [ UIAlertController alertControllerWithTitle : [ VectorL10n roomNoPrivilegesToCreateGroupCall ]
message : nil
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-04-21 09:47:28 +00:00
2021-11-01 15:05:02 +00:00
[ unprivilegedAlert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n ok ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action )
2021-04-21 09:47:28 +00:00
{
MXStrongifyAndReturnIfNil ( self ) ;
self -> currentAlert = nil ;
} ] ] ;
2021-11-01 15:05:02 +00:00
[ unprivilegedAlert mxk_setAccessibilityIdentifier : @ "RoomVCCallAlert" ] ;
[ self presentViewController : unprivilegedAlert animated : YES completion : nil ] ;
currentAlert = unprivilegedAlert ;
2021-03-15 13:27:59 +00:00
}
2021-04-21 09:47:28 +00:00
}
2016-08-19 07:57:35 +00:00
}
2015-08-18 14:24:48 +00:00
}
2021-03-15 13:27:59 +00:00
- ( void ) hangupCall
2016-08-09 10:24:58 +00:00
{
MXCall * callInRoom = [ self . roomDataSource . mxSession . callManager callInRoom : self . roomDataSource . roomId ] ;
if ( callInRoom )
{
2016-08-09 10:47:52 +00:00
[ callInRoom hangup ] ;
2016-08-09 10:24:58 +00:00
}
2021-09-07 15:43:49 +00:00
else if ( self . isRoomHavingAJitsiCall )
2017-08-11 12:59:05 +00:00
{
2021-09-07 15:43:49 +00:00
[ self endActiveJitsiCall ] ;
2021-03-19 14:33:07 +00:00
[ self reloadBubblesTable : YES ] ;
2017-08-11 12:59:05 +00:00
}
2021-03-15 13:27:59 +00:00
2017-08-11 12:59:05 +00:00
[ self refreshActivitiesViewDisplay ] ;
[ self refreshRoomInputToolbar ] ;
2016-08-09 10:24:58 +00:00
}
2021-03-15 13:27:59 +00:00
# pragma mark - MXKRoomInputToolbarViewDelegate
- ( void ) roomInputToolbarView : ( MXKRoomInputToolbarView * ) toolbarView isTyping : ( BOOL ) typing
{
[ super roomInputToolbarView : toolbarView isTyping : typing ] ;
// Cancel potential selected event ( to leave edition mode )
NSString * selectedEventId = customizedRoomDataSource . selectedEventId ;
if ( typing && selectedEventId && ! [ self . roomDataSource canReplyToEventWithId : selectedEventId ] )
{
[ self cancelEventSelection ] ;
}
}
2016-01-13 09:06:18 +00:00
- ( void ) roomInputToolbarView : ( MXKRoomInputToolbarView * ) toolbarView heightDidChanged : ( CGFloat ) height completion : ( void ( ^ ) ( BOOL finished ) ) completion
{
if ( self . roomInputToolbarContainerHeightConstraint . constant ! = height )
{
// Hide temporarily the placeholder to prevent its distorsion during height animation
if ( ! savedInputToolbarPlaceholder )
{
savedInputToolbarPlaceholder = toolbarView . placeholder . length ? toolbarView . placeholder : @ "" ;
}
toolbarView . placeholder = nil ;
[ super roomInputToolbarView : toolbarView heightDidChanged : height completion : ^ ( BOOL finished ) {
if ( completion )
{
completion ( finished ) ;
}
2017-07-14 14:41:25 +00:00
2017-05-19 12:46:30 +00:00
// Consider here the saved placeholder only if no new placeholder has been defined during the height animation .
2016-01-13 09:06:18 +00:00
if ( ! toolbarView . placeholder )
{
// Restore the placeholder if any
2021-03-15 13:27:59 +00:00
toolbarView . placeholder = self -> savedInputToolbarPlaceholder . length ? self -> savedInputToolbarPlaceholder : nil ;
2016-01-13 09:06:18 +00:00
}
2021-03-15 13:27:59 +00:00
self -> savedInputToolbarPlaceholder = nil ;
2016-01-13 09:06:18 +00:00
} ] ;
}
}
2021-03-24 21:17:09 +00:00
- ( void ) roomInputToolbarViewDidTapCancel : ( MXKRoomInputToolbarView * ) toolbarView
{
[ self cancelEventSelection ] ;
}
2021-09-30 06:37:59 +00:00
2021-10-05 11:30:31 +00:00
- ( void ) roomInputToolbarViewDidChangeTextMessage : ( MXKRoomInputToolbarView * ) toolbarView
2021-09-30 06:37:59 +00:00
{
2021-10-05 11:30:31 +00:00
[ self . userSuggestionCoordinator processTextMessage : toolbarView . textMessage ] ;
2021-09-30 06:37:59 +00:00
}
2021-03-24 21:17:09 +00:00
2016-05-26 16:30:41 +00:00
# pragma mark - MXKRoomMemberDetailsViewControllerDelegate
- ( void ) roomMemberDetailsViewController : ( MXKRoomMemberDetailsViewController * ) roomMemberDetailsViewController startChatWithMemberId : ( NSString * ) matrixId completion : ( void ( ^ ) ( void ) ) completion
{
2021-09-07 15:43:49 +00:00
[ self startChatWithUserId : matrixId completion : completion ] ;
2016-05-26 16:30:41 +00:00
}
- ( void ) roomMemberDetailsViewController : ( MXKRoomMemberDetailsViewController * ) roomMemberDetailsViewController mention : ( MXRoomMember * ) member
{
[ self mention : member ] ;
}
2015-05-12 12:10:37 +00:00
# pragma mark - Action
2021-03-15 13:27:59 +00:00
- ( IBAction ) onVoiceCallPressed : ( id ) sender
2015-06-16 11:54:12 +00:00
{
2021-03-15 13:27:59 +00:00
if ( self . isCallActive )
2015-06-16 11:54:12 +00:00
{
2021-03-15 13:27:59 +00:00
[ self hangupCall ] ;
2015-05-12 12:10:37 +00:00
}
2021-03-15 13:27:59 +00:00
else
2017-09-15 11:27:13 +00:00
{
2021-03-15 13:27:59 +00:00
[ self placeCallWithVideo : NO ] ;
}
}
2017-09-27 13:21:38 +00:00
2021-03-15 13:27:59 +00:00
- ( IBAction ) onVideoCallPressed : ( id ) sender
{
[ self placeCallWithVideo : YES ] ;
}
2017-09-18 14:22:59 +00:00
2021-03-15 13:27:59 +00:00
- ( IBAction ) onIntegrationsPressed : ( id ) sender
{
WidgetPickerViewController * widgetPicker = [ [ WidgetPickerViewController alloc ] initForMXSession : self . roomDataSource . mxSession
inRoom : self . roomDataSource . roomId ] ;
[ widgetPicker showInViewController : self ] ;
}
2021-03-20 20:31:17 +00:00
- ( void ) scrollToBottomAction : ( id ) sender
{
[ self goBackToLive ] ;
}
2021-03-15 13:27:59 +00:00
- ( IBAction ) onButtonPressed : ( id ) sender
{
if ( sender = = self . jumpToLastUnreadButton )
2017-06-01 15:20:08 +00:00
{
2017-06-15 09:15:47 +00:00
// Dismiss potential keyboard .
[ self dismissKeyboard ] ;
2021-03-15 13:27:59 +00:00
2017-06-06 08:53:17 +00:00
// Jump to the last unread event by using a temporary room data source initialized with the last unread event id .
2018-07-24 14:30:48 +00:00
MXWeakify ( self ) ;
2018-07-20 09:28:02 +00:00
[ RoomDataSource loadRoomDataSourceWithRoomId : self . roomDataSource . roomId initialEventId : self . roomDataSource . room . accountData . readMarkerEventId andMatrixSession : self . mainSession onComplete : ^ ( id roomDataSource ) {
2018-07-24 14:30:48 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
[ roomDataSource finalizeInitialization ] ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
// Center the bubbles table content on the bottom of the read marker event in order to display correctly the read marker view .
self . centerBubblesTableViewContentOnTheInitialEventBottom = YES ;
[ self displayRoom : roomDataSource ] ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
// Give the data source ownership to the room view controller .
self . hasRoomDataSourceOwnership = YES ;
} ] ;
2017-06-01 15:20:08 +00:00
}
else if ( sender = = self . resetReadMarkerButton )
{
// Move the read marker to the current read receipt position .
[ self . roomDataSource . room forgetReadMarker ] ;
2017-06-13 15:43:32 +00:00
// Hide the banner
self . jumpToLastUnreadBannerContainer . hidden = YES ;
2017-06-01 15:20:08 +00:00
}
2015-05-12 12:10:37 +00:00
}
2016-04-19 13:28:15 +00:00
# pragma mark - UITableViewDelegate
2015-09-08 15:53:53 +00:00
2017-06-01 15:20:08 +00:00
- ( void ) tableView : ( UITableView * ) tableView willDisplayCell : ( UITableViewCell * ) cell forRowAtIndexPath : ( NSIndexPath * ) indexPath
{
2019-01-11 10:45:27 +00:00
cell . backgroundColor = ThemeService . shared . theme . backgroundColor ;
2017-08-11 14:56:09 +00:00
// Update the selected background view
2019-01-11 10:45:27 +00:00
if ( ThemeService . shared . theme . selectedBackgroundColor )
2017-08-11 14:56:09 +00:00
{
cell . selectedBackgroundView = [ [ UIView alloc ] init ] ;
2019-01-11 10:45:27 +00:00
cell . selectedBackgroundView . backgroundColor = ThemeService . shared . theme . selectedBackgroundColor ;
2017-08-11 14:56:09 +00:00
}
else
{
if ( tableView . style = = UITableViewStylePlain )
{
cell . selectedBackgroundView = nil ;
}
else
{
cell . selectedBackgroundView . backgroundColor = nil ;
}
}
2017-06-01 15:20:08 +00:00
if ( [ cell isKindOfClass : MXKRoomBubbleTableViewCell . class ] )
{
MXKRoomBubbleTableViewCell * roomBubbleTableViewCell = ( MXKRoomBubbleTableViewCell * ) cell ;
if ( roomBubbleTableViewCell . readMarkerView )
{
readMarkerTableViewCell = roomBubbleTableViewCell ;
[ self checkReadMarkerVisibility ] ;
}
}
}
- ( void ) tableView : ( UITableView * ) tableView didEndDisplayingCell : ( UITableViewCell * ) cell forRowAtIndexPath : ( NSIndexPath * ) indexPath
{
if ( cell = = readMarkerTableViewCell )
{
readMarkerTableViewCell = nil ;
}
[ super tableView : tableView didEndDisplayingCell : cell forRowAtIndexPath : indexPath ] ;
}
2015-09-08 15:53:53 +00:00
- ( void ) tableView : ( UITableView * ) tableView didSelectRowAtIndexPath : ( NSIndexPath * ) indexPath
{
[ super tableView : tableView didSelectRowAtIndexPath : indexPath ] ;
}
2016-04-20 12:09:14 +00:00
# pragma mark -
2017-06-01 15:20:08 +00:00
- ( void ) scrollViewDidScroll : ( UIScrollView * ) scrollView
{
[ super scrollViewDidScroll : scrollView ] ;
[ self checkReadMarkerVisibility ] ;
2017-06-19 08:32:22 +00:00
// Switch back to the live mode when the user scrolls to the bottom of the non live timeline .
2017-06-22 09:09:17 +00:00
if ( ! self . roomDataSource . isLive && ! [ self isRoomPreview ] )
2017-06-19 08:32:22 +00:00
{
2021-09-01 09:44:15 +00:00
CGFloat contentBottomPosY = self . bubblesTableView . contentOffset . y + self . bubblesTableView . frame . size . height - self . bubblesTableView . adjustedContentInset . bottom ;
2017-06-19 08:32:22 +00:00
if ( contentBottomPosY >= self . bubblesTableView . contentSize . height && ! [ self . roomDataSource . timeline canPaginate : MXTimelineDirectionForwards ] )
{
[ self goBackToLive ] ;
}
}
2017-06-01 15:20:08 +00:00
}
2016-04-20 12:09:14 +00:00
- ( void ) scrollViewWillBeginDragging : ( UIScrollView * ) scrollView
{
if ( [ MXKRoomViewController instancesRespondToSelector : @ selector ( scrollViewWillBeginDragging : ) ] )
{
[ super scrollViewWillBeginDragging : scrollView ] ;
}
}
- ( void ) scrollViewDidEndDragging : ( UIScrollView * ) scrollView willDecelerate : ( BOOL ) decelerate
{
if ( [ MXKRoomViewController instancesRespondToSelector : @ selector ( scrollViewDidEndDragging : willDecelerate : ) ] )
{
[ super scrollViewDidEndDragging : scrollView willDecelerate : decelerate ] ;
}
if ( decelerate = = NO )
{
// Handle swipe on expanded header
[ self onScrollViewDidEndScrolling : scrollView ] ;
2016-08-29 16:34:00 +00:00
[ self refreshActivitiesViewDisplay ] ;
2017-06-01 15:20:08 +00:00
[ self refreshJumpToLastUnreadBannerDisplay ] ;
2016-04-20 12:09:14 +00:00
}
else
{
// Dispatch async the expanded header handling in order to let the deceleration go first .
dispatch_async ( dispatch_get _main _queue ( ) , ^ {
2017-07-14 14:41:25 +00:00
2016-04-20 12:09:14 +00:00
// Handle swipe on expanded header
[ self onScrollViewDidEndScrolling : scrollView ] ;
} ) ;
}
}
2016-08-29 16:34:00 +00:00
- ( void ) scrollViewDidEndDecelerating : ( UIScrollView * ) scrollView
{
if ( [ MXKRoomViewController instancesRespondToSelector : @ selector ( scrollViewDidEndDecelerating : ) ] )
{
[ super scrollViewDidEndDecelerating : scrollView ] ;
}
[ self refreshActivitiesViewDisplay ] ;
2017-06-01 15:20:08 +00:00
[ self refreshJumpToLastUnreadBannerDisplay ] ;
2016-08-29 16:34:00 +00:00
}
- ( void ) scrollViewDidEndScrollingAnimation : ( UIScrollView * ) scrollView
{
if ( [ MXKRoomViewController instancesRespondToSelector : @ selector ( scrollViewDidEndScrollingAnimation : ) ] )
{
[ super scrollViewDidEndScrollingAnimation : scrollView ] ;
}
[ self refreshActivitiesViewDisplay ] ;
2017-06-01 15:20:08 +00:00
[ self refreshJumpToLastUnreadBannerDisplay ] ;
2016-08-29 16:34:00 +00:00
}
2016-04-20 12:09:14 +00:00
- ( void ) onScrollViewDidEndScrolling : ( UIScrollView * ) scrollView
{
2020-09-18 11:11:45 +00:00
2016-04-20 12:09:14 +00:00
}
2016-01-13 18:03:27 +00:00
# pragma mark - MXKRoomTitleViewDelegate
2015-11-20 13:12:37 +00:00
- ( BOOL ) roomTitleViewShouldBeginEditing : ( MXKRoomTitleView * ) titleView
{
2016-01-13 18:03:27 +00:00
// Disable room name edition
2015-11-20 13:12:37 +00:00
return NO ;
}
2016-02-10 09:59:48 +00:00
# pragma mark - RoomTitleViewTapGestureDelegate
- ( void ) roomTitleView : ( RoomTitleView * ) titleView recognizeTapGesture : ( UITapGestureRecognizer * ) tapGestureRecognizer
{
2016-08-22 09:54:26 +00:00
UIView * tappedView = tapGestureRecognizer . view ;
2016-02-10 09:59:48 +00:00
2016-08-22 09:54:26 +00:00
if ( tappedView = = titleView . titleMask )
2016-02-10 09:59:48 +00:00
{
2021-02-23 14:52:24 +00:00
[ self showRoomInfo ] ;
2017-06-20 09:32:54 +00:00
}
2016-08-22 09:54:26 +00:00
else if ( tappedView = = previewHeader . rightButton )
2016-04-14 00:34:30 +00:00
{
2016-07-12 20:40:35 +00:00
// ' Join ' button has been pressed
2021-09-15 08:58:38 +00:00
if ( ! roomPreviewData )
2016-04-14 00:34:30 +00:00
{
2021-09-15 08:58:38 +00:00
[ self joinRoom : ^ ( MXKRoomViewControllerJoinRoomResult result ) {
switch ( result )
{
case MXKRoomViewControllerJoinRoomResultSuccess :
[ self refreshRoomTitle ] ;
break ;
case MXKRoomViewControllerJoinRoomResultFailureRoomEmpty :
[ self declineRoomInvitation ] ;
break ;
default :
break ;
}
} ] ;
2016-06-10 15:15:42 +00:00
2021-09-15 08:58:38 +00:00
return ;
}
// Attempt to join the room ( keep reference on the potential eventId , the preview data will be removed automatically in case of success ) .
NSString * eventId = roomPreviewData . eventId ;
// We promote here join by room alias instead of room id when an alias is available .
NSString * roomIdOrAlias = roomPreviewData . roomId ;
if ( roomPreviewData . roomCanonicalAlias . length )
{
roomIdOrAlias = roomPreviewData . roomCanonicalAlias ;
}
else if ( roomPreviewData . roomAliases . count )
{
roomIdOrAlias = roomPreviewData . roomAliases . firstObject ;
}
// Note in case of simple link to a room the signUrl param is nil
[ self joinRoomWithRoomIdOrAlias : roomIdOrAlias viaServers : roomPreviewData . viaServers
andSignUrl : roomPreviewData . emailInvitation . signUrl
completion : ^ ( MXKRoomViewControllerJoinRoomResult result ) {
2020-02-10 16:54:52 +00:00
2021-09-15 08:58:38 +00:00
switch ( result )
2016-06-14 20:25:10 +00:00
{
2021-09-15 08:58:38 +00:00
case MXKRoomViewControllerJoinRoomResultSuccess :
2016-04-14 15:44:12 +00:00
{
2016-04-26 15:10:06 +00:00
// If an event was specified , replace the datasource by a non live datasource showing the event
if ( eventId )
{
2018-07-24 14:30:48 +00:00
MXWeakify ( self ) ;
2018-07-20 09:28:02 +00:00
[ RoomDataSource loadRoomDataSourceWithRoomId : self . roomDataSource . roomId initialEventId : eventId andMatrixSession : self . mainSession onComplete : ^ ( id roomDataSource ) {
2018-07-24 14:30:48 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
[ roomDataSource finalizeInitialization ] ;
( ( RoomDataSource * ) roomDataSource ) . markTimelineInitialEvent = YES ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
[ self displayRoom : roomDataSource ] ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
self . hasRoomDataSourceOwnership = YES ;
} ] ;
2016-04-26 15:10:06 +00:00
}
2016-06-10 15:15:42 +00:00
else
{
// Enable back the text input
[ self setRoomInputToolbarViewClass : RoomInputToolbarView . class ] ;
2018-07-27 09:56:51 +00:00
[ self updateInputToolBarViewHeight ] ;
2016-06-10 15:15:42 +00:00
// And the extra area
[ self setRoomActivitiesViewClass : RoomActivitiesView . class ] ;
[ self refreshRoomTitle ] ;
[ self refreshRoomInputToolbar ] ;
}
2021-09-15 08:58:38 +00:00
break ;
2016-04-14 15:44:12 +00:00
}
2021-09-15 08:58:38 +00:00
case MXKRoomViewControllerJoinRoomResultFailureRoomEmpty :
[ self declineRoomInvitation ] ;
break ;
default :
break ;
}
} ] ;
2016-04-14 00:34:30 +00:00
}
2016-08-22 09:54:26 +00:00
else if ( tappedView = = previewHeader . leftButton )
2016-04-14 00:34:30 +00:00
{
2021-09-15 08:58:38 +00:00
[ self declineRoomInvitation ] ;
}
}
- ( void ) declineRoomInvitation
{
// ' Decline ' button has been pressed
if ( roomPreviewData )
{
[ self roomPreviewDidTapCancelAction ] ;
}
else
{
[ self startActivityIndicator ] ;
[ self . roomDataSource . room leave : ^ {
[ self stopActivityIndicator ] ;
// We remove the current view controller .
// Pop to homes view controller
[ [ AppDelegate theDelegate ] restoreInitialDisplay : ^ { } ] ;
} failure : ^ ( NSError * error ) {
[ self stopActivityIndicator ] ;
MXLogDebug ( @ "[RoomVC] Failed to reject an invited room (%@) failed" , self . roomDataSource . room . roomId ) ;
} ] ;
2016-04-14 00:34:30 +00:00
}
2016-02-10 09:59:48 +00:00
}
2016-02-25 12:54:53 +00:00
# pragma mark - Typing management
2015-11-18 08:38:55 +00:00
- ( void ) removeTypingNotificationsListener
{
if ( self . roomDataSource )
{
// Remove the previous live listener
if ( typingNotifListener )
{
2018-07-19 07:47:34 +00:00
MXWeakify ( self ) ;
[ self . roomDataSource . room liveTimeline : ^ ( MXEventTimeline * liveTimeline ) {
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2018-07-19 07:47:34 +00:00
[ liveTimeline removeListener : self -> typingNotifListener ] ;
self -> typingNotifListener = nil ;
} ] ;
2015-11-18 08:38:55 +00:00
}
}
2016-06-27 13:34:17 +00:00
currentTypingUsers = nil ;
2015-11-18 08:38:55 +00:00
}
- ( void ) listenTypingNotifications
{
if ( self . roomDataSource )
{
// Add typing notification listener
2018-07-19 07:47:34 +00:00
MXWeakify ( self ) ;
2018-07-23 16:14:45 +00:00
self -> typingNotifListener = [ self . roomDataSource . room listenToEventsOfTypes : @ [ kMXEventTypeStringTypingNotification ] onEvent : ^ ( MXEvent * event , MXTimelineDirection direction , MXRoomState * roomState ) {
2018-07-19 07:47:34 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2018-07-23 16:14:45 +00:00
// Handle only live events
if ( direction = = MXTimelineDirectionForwards )
{
// Retrieve typing users list
NSMutableArray * typingUsers = [ NSMutableArray arrayWithArray : self . roomDataSource . room . typingUsers ] ;
// Remove typing info for the current user
NSUInteger index = [ typingUsers indexOfObject : self . mainSession . myUser . userId ] ;
if ( index ! = NSNotFound )
2015-11-25 15:53:52 +00:00
{
2018-07-23 16:14:45 +00:00
[ typingUsers removeObjectAtIndex : index ] ;
}
2021-03-15 13:27:59 +00:00
2018-07-23 16:14:45 +00:00
// Ignore this notification if both arrays are empty
if ( self -> currentTypingUsers . count || typingUsers . count )
{
self -> currentTypingUsers = typingUsers ;
[ self refreshActivitiesViewDisplay ] ;
2015-11-25 15:53:52 +00:00
}
2018-07-23 16:14:45 +00:00
}
2015-11-25 15:53:52 +00:00
} ] ;
2021-03-15 13:27:59 +00:00
2016-03-14 10:34:01 +00:00
// Retrieve the current typing users list
NSMutableArray * typingUsers = [ NSMutableArray arrayWithArray : self . roomDataSource . room . typingUsers ] ;
// Remove typing info for the current user
NSUInteger index = [ typingUsers indexOfObject : self . mainSession . myUser . userId ] ;
if ( index ! = NSNotFound )
{
[ typingUsers removeObjectAtIndex : index ] ;
}
currentTypingUsers = typingUsers ;
2016-02-25 12:54:53 +00:00
[ self refreshActivitiesViewDisplay ] ;
2015-11-18 08:38:55 +00:00
}
}
2016-02-25 12:54:53 +00:00
- ( void ) refreshTypingNotification
2015-11-18 08:38:55 +00:00
{
2021-03-22 18:55:41 +00:00
RoomDataSource * roomDataSource = ( RoomDataSource * ) self . roomDataSource ;
BOOL needsUpdate = currentTypingUsers . count ! = roomDataSource . currentTypingUsers . count ;
NSMutableArray * typingUsers = [ NSMutableArray new ] ;
for ( NSUInteger i = 0 ; i < currentTypingUsers . count ; i + + ) {
NSString * userId = currentTypingUsers [ i ] ;
MXRoomMember * member = [ self . roomDataSource . roomState . members memberWithUserId : userId ] ;
2021-04-07 10:26:51 +00:00
TypingUserInfo * userInfo ;
if ( member )
2015-12-03 10:38:37 +00:00
{
2021-04-07 10:26:51 +00:00
userInfo = [ [ TypingUserInfo alloc ] initWithMember : member ] ;
2015-12-03 10:38:37 +00:00
}
2021-04-07 10:26:51 +00:00
else
2016-02-25 12:54:53 +00:00
{
2021-04-07 10:26:51 +00:00
userInfo = [ [ TypingUserInfo alloc ] initWithUserId : userId ] ;
2016-02-25 12:54:53 +00:00
}
2021-04-07 10:26:51 +00:00
[ typingUsers addObject : userInfo ] ;
needsUpdate = needsUpdate || userInfo . userId ! = ( ( MXRoomMember * ) roomDataSource . currentTypingUsers [ i ] ) . userId ;
2021-03-22 18:55:41 +00:00
}
if ( needsUpdate )
2015-11-25 15:53:52 +00:00
{
2021-04-19 11:18:26 +00:00
// BOOL needsReload = roomDataSource . currentTypingUsers = = nil ;
// Quick fix for https : // github . com / vector - im / element - ios / issues / 4230
BOOL needsReload = YES ;
2021-03-22 18:55:41 +00:00
roomDataSource . currentTypingUsers = typingUsers ;
2021-03-29 20:26:03 +00:00
if ( needsReload )
2016-02-25 12:54:53 +00:00
{
2021-03-29 20:26:03 +00:00
[ self . bubblesTableView reloadData ] ;
2016-02-25 12:54:53 +00:00
}
else
{
2021-04-08 13:19:02 +00:00
NSInteger count = [ self . bubblesTableView numberOfRowsInSection : 0 ] ;
2021-03-29 20:26:03 +00:00
NSIndexPath * lastIndexPath = [ NSIndexPath indexPathForRow : count - 1 inSection : 0 ] ;
[ self . bubblesTableView reloadRowsAtIndexPaths : @ [ lastIndexPath ] withRowAnimation : UITableViewRowAnimationFade ] ;
2016-02-25 12:54:53 +00:00
}
2021-03-29 20:26:03 +00:00
if ( self . isScrollToBottomHidden
&& ! self . bubblesTableView . isDragging
&& ! self . bubblesTableView . isDecelerating )
2015-11-18 08:38:55 +00:00
{
2021-04-08 13:19:02 +00:00
NSInteger count = [ self . bubblesTableView numberOfRowsInSection : 0 ] ;
2021-03-22 18:55:41 +00:00
if ( count )
2016-02-25 12:54:53 +00:00
{
2021-03-22 18:55:41 +00:00
[ self scrollBubblesTableViewToBottomAnimated : YES ] ;
2016-02-25 12:54:53 +00:00
}
2015-11-18 08:38:55 +00:00
}
}
2016-02-25 12:54:53 +00:00
}
2016-08-09 10:47:52 +00:00
# pragma mark - Call notifications management
- ( void ) removeCallNotificationsListeners
{
if ( kMXCallStateDidChangeObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : kMXCallStateDidChangeObserver ] ;
kMXCallStateDidChangeObserver = nil ;
}
if ( kMXCallManagerConferenceStartedObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : kMXCallManagerConferenceStartedObserver ] ;
kMXCallManagerConferenceStartedObserver = nil ;
}
if ( kMXCallManagerConferenceFinishedObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : kMXCallManagerConferenceFinishedObserver ] ;
kMXCallManagerConferenceFinishedObserver = nil ;
}
}
- ( void ) listenCallNotifications
{
2021-10-28 15:41:16 +00:00
MXWeakify ( self ) ;
2016-08-09 10:47:52 +00:00
kMXCallStateDidChangeObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCallStateDidChange object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2017-07-14 14:41:25 +00:00
2021-10-28 15:41:16 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2016-08-09 10:47:52 +00:00
MXCall * call = notif . object ;
2021-10-28 15:41:16 +00:00
if ( [ call . room . roomId isEqualToString : self -> customizedRoomDataSource . roomId ] )
2016-08-09 10:47:52 +00:00
{
[ self refreshActivitiesViewDisplay ] ;
[ self refreshRoomInputToolbar ] ;
}
} ] ;
kMXCallManagerConferenceStartedObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCallManagerConferenceStarted object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2017-07-14 14:41:25 +00:00
2021-10-28 15:41:16 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2016-08-09 10:47:52 +00:00
NSString * roomId = notif . object ;
2021-10-28 15:41:16 +00:00
if ( [ roomId isEqualToString : self -> customizedRoomDataSource . roomId ] )
2016-08-09 10:47:52 +00:00
{
[ self refreshActivitiesViewDisplay ] ;
}
} ] ;
kMXCallManagerConferenceFinishedObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXCallManagerConferenceFinished object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2017-07-14 14:41:25 +00:00
2021-10-28 15:41:16 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2016-08-09 10:47:52 +00:00
NSString * roomId = notif . object ;
2021-10-28 15:41:16 +00:00
if ( [ roomId isEqualToString : self -> customizedRoomDataSource . roomId ] )
2016-08-09 10:47:52 +00:00
{
[ self refreshActivitiesViewDisplay ] ;
[ self refreshRoomInputToolbar ] ;
}
} ] ;
}
2018-10-02 13:30:00 +00:00
# pragma mark - Server notices management
- ( void ) removeServerNoticesListener
{
if ( serverNotices )
{
[ serverNotices close ] ;
serverNotices = nil ;
}
}
- ( void ) listenToServerNotices
{
if ( ! serverNotices )
{
serverNotices = [ [ MXServerNotices alloc ] initWithMatrixSession : self . roomDataSource . mxSession ] ;
serverNotices . delegate = self ;
}
}
- ( void ) serverNoticesDidChangeState : ( MXServerNotices * ) serverNotices
{
[ self refreshActivitiesViewDisplay ] ;
}
2017-08-09 15:31:15 +00:00
# pragma mark - Widget notifications management
- ( void ) removeWidgetNotificationsListeners
{
if ( kMXKWidgetManagerDidUpdateWidgetObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : kMXKWidgetManagerDidUpdateWidgetObserver ] ;
kMXKWidgetManagerDidUpdateWidgetObserver = nil ;
}
}
- ( void ) listenWidgetNotifications
{
2021-04-23 15:36:51 +00:00
MXWeakify ( self ) ;
2017-08-18 07:49:14 +00:00
kMXKWidgetManagerDidUpdateWidgetObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kWidgetManagerDidUpdateWidgetNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2021-03-15 13:27:59 +00:00
2021-04-23 15:36:51 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2017-08-09 15:31:15 +00:00
Widget * widget = notif . object ;
if ( widget . mxSession = = self . roomDataSource . mxSession
2021-04-23 15:36:51 +00:00
&& [ widget . roomId isEqualToString : self -> customizedRoomDataSource . roomId ] )
2017-08-09 15:31:15 +00:00
{
2021-04-23 15:36:51 +00:00
// Call button update
2017-09-27 13:40:21 +00:00
[ self refreshRoomTitle ] ;
2021-04-23 15:36:51 +00:00
// Remove Jitsi widget view update
[ self refreshRemoveJitsiWidgetView ] ;
2017-08-09 15:31:15 +00:00
}
} ] ;
}
2017-08-17 16:59:02 +00:00
- ( void ) showJitsiErrorAsAlert : ( NSError * ) error
{
// Customise the error for permission issues
if ( [ error . domain isEqualToString : WidgetManagerErrorDomain ] && error . code = = WidgetManagerErrorCodeNotEnoughPower )
{
error = [ NSError errorWithDomain : error . domain
code : error . code
userInfo : @ {
2021-09-28 05:40:01 +00:00
NSLocalizedDescriptionKey : [ VectorL10n roomConferenceCallNoPower ]
2021-03-15 13:27:59 +00:00
} ] ;
2017-08-17 16:59:02 +00:00
}
2021-03-15 13:27:59 +00:00
2017-08-17 16:59:02 +00:00
// Alert user
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2017-08-17 16:59:02 +00:00
}
2018-05-07 09:15:18 +00:00
- ( NSUInteger ) widgetsCount : ( BOOL ) includeUserWidgets
2017-09-27 13:21:38 +00:00
{
2020-04-24 07:04:49 +00:00
NSUInteger widgetsCount = [ [ WidgetManager sharedManager ] widgetsNotOfTypes : @ [ kWidgetTypeJitsiV1 , kWidgetTypeJitsiV2 ]
2018-07-20 09:28:02 +00:00
inRoom : self . roomDataSource . room
withRoomState : self . roomDataSource . roomState ] . count ;
2018-05-07 09:15:18 +00:00
if ( includeUserWidgets )
{
2018-05-24 17:01:19 +00:00
widgetsCount + = [ [ WidgetManager sharedManager ] userWidgets : self . roomDataSource . room . mxSession ] . count ;
2018-05-07 09:15:18 +00:00
}
2021-03-15 13:27:59 +00:00
2018-05-07 09:15:18 +00:00
return widgetsCount ;
2017-09-27 13:21:38 +00:00
}
2016-02-25 12:54:53 +00:00
# pragma mark - Unreachable Network Handling
- ( void ) refreshActivitiesViewDisplay
{
2016-08-29 16:34:00 +00:00
if ( [ self . activitiesView isKindOfClass : RoomActivitiesView . class ] )
2015-11-18 08:38:55 +00:00
{
2016-08-08 16:40:22 +00:00
RoomActivitiesView * roomActivitiesView = ( RoomActivitiesView * ) self . activitiesView ;
2021-03-15 13:27:59 +00:00
2016-09-05 08:40:46 +00:00
// Reset gesture recognizers
while ( roomActivitiesView . gestureRecognizers . count )
{
[ roomActivitiesView removeGestureRecognizer : roomActivitiesView . gestureRecognizers [ 0 ] ] ;
}
2021-03-15 13:27:59 +00:00
2018-08-21 16:25:56 +00:00
if ( [ self . roomDataSource . mxSession . syncError . errcode isEqualToString : kMXErrCodeStringResourceLimitExceeded ] )
{
2021-03-15 13:27:59 +00:00
self . activitiesViewExpanded = YES ;
2020-09-01 10:20:44 +00:00
[ roomActivitiesView showResourceLimitExceededError : self . roomDataSource . mxSession . syncError . userInfo onAdminContactTapped : ^ ( NSURL * adminContactURL ) {
[ [ UIApplication sharedApplication ] vc_open : adminContactURL completionHandler : ^ ( BOOL success ) {
2021-03-15 13:27:59 +00:00
if ( ! success )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] refreshActivitiesViewDisplay: adminContact(%@) cannot be opened" , adminContactURL ) ;
2021-03-15 13:27:59 +00:00
}
2020-09-01 10:20:44 +00:00
} ] ;
2018-08-21 16:25:56 +00:00
} ] ;
}
else if ( [ AppDelegate theDelegate ] . isOffline )
2016-02-25 12:54:53 +00:00
{
2021-03-15 13:27:59 +00:00
self . activitiesViewExpanded = YES ;
2021-09-28 05:40:01 +00:00
[ roomActivitiesView displayNetworkErrorNotification : [ VectorL10n roomOfflineNotification ] ] ;
2016-08-08 16:40:22 +00:00
}
2018-08-07 16:06:58 +00:00
else if ( customizedRoomDataSource . roomState . isObsolete )
2018-07-27 09:56:51 +00:00
{
2021-03-15 13:27:59 +00:00
self . activitiesViewExpanded = YES ;
2019-07-10 12:44:48 +00:00
MXWeakify ( self ) ;
2018-07-27 09:56:51 +00:00
[ roomActivitiesView displayRoomReplacementWithRoomLinkTappedHandler : ^ {
2019-07-10 12:44:48 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2019-07-10 12:44:48 +00:00
MXEvent * stoneTombEvent = [ self -> customizedRoomDataSource . roomState stateEventsWithType : kMXEventTypeStringRoomTombStone ] . lastObject ;
2021-03-15 13:27:59 +00:00
2019-07-10 12:44:48 +00:00
NSString * replacementRoomId = self -> customizedRoomDataSource . roomState . tombStoneContent . replacementRoomId ;
if ( [ self . roomDataSource . mxSession roomWithRoomId : replacementRoomId ] )
{
// Open the room if it is already joined
2021-09-07 15:43:49 +00:00
[ self showRoomWithId : replacementRoomId ] ;
2019-07-10 12:44:48 +00:00
}
else
{
// Else auto join it via the server that sent the event
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] Auto join an upgraded room: %@ -> %@. Sender: %@" , self -> customizedRoomDataSource . roomState . roomId ,
2019-07-10 12:44:48 +00:00
replacementRoomId , stoneTombEvent . sender ) ;
2021-03-15 13:27:59 +00:00
2019-07-10 12:44:48 +00:00
NSString * viaSenderServer = [ MXTools serverNameInMatrixIdentifier : stoneTombEvent . sender ] ;
2021-03-15 13:27:59 +00:00
2019-07-10 12:44:48 +00:00
if ( viaSenderServer )
{
[ self startActivityIndicator ] ;
[ self . roomDataSource . mxSession joinRoom : replacementRoomId viaServers : @ [ viaSenderServer ] success : ^ ( MXRoom * room ) {
[ self stopActivityIndicator ] ;
2021-09-07 15:43:49 +00:00
[ self showRoomWithId : replacementRoomId ] ;
2021-03-15 13:27:59 +00:00
2019-07-10 12:44:48 +00:00
} failure : ^ ( NSError * error ) {
[ self stopActivityIndicator ] ;
2021-03-15 13:27:59 +00:00
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] Failed to join an upgraded room. Error: %@" ,
2019-07-10 12:44:48 +00:00
error ) ;
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2019-07-10 12:44:48 +00:00
} ] ;
}
}
2018-07-27 09:56:51 +00:00
} ] ;
}
2016-02-25 12:54:53 +00:00
else if ( [ self checkUnsentMessages ] = = NO )
{
2017-06-06 08:53:17 +00:00
// Show "scroll to bottom" icon when the most recent message is not visible ,
// or when the timelime is not live ( this icon is used to go back to live ) .
2017-06-29 10:03:39 +00:00
// Note : we check if ` currentEventIdAtTableBottom` is set to know whether the table has been rendered at least once .
if ( ! self . roomDataSource . isLive || ( currentEventIdAtTableBottom && [ self isBubblesTableScrollViewAtTheBottom ] = = NO ) )
2016-08-29 16:34:00 +00:00
{
2021-04-21 09:50:37 +00:00
if ( self . roomDataSource . room )
{
// Retrieve the unread messages count
NSUInteger unreadCount = self . roomDataSource . room . summary . localUnreadEventCount ;
self . scrollToBottomBadgeLabel . text = unreadCount ? [ NSString stringWithFormat : @ "%lu" , unreadCount ] : nil ;
self . scrollToBottomHidden = NO ;
}
else
{
// will be here for left rooms
self . scrollToBottomBadgeLabel . text = nil ;
self . scrollToBottomHidden = YES ;
}
2016-08-29 16:34:00 +00:00
}
2018-10-02 13:30:00 +00:00
else if ( serverNotices . usageLimit && serverNotices . usageLimit . isServerNoticeUsageLimit )
{
2021-03-20 20:31:17 +00:00
self . scrollToBottomHidden = YES ;
2021-03-15 13:27:59 +00:00
self . activitiesViewExpanded = YES ;
[ roomActivitiesView showResourceUsageLimitNotice : serverNotices . usageLimit onAdminContactTapped : ^ ( NSURL * adminContactURL ) {
2020-09-01 10:20:44 +00:00
[ [ UIApplication sharedApplication ] vc_open : adminContactURL completionHandler : ^ ( BOOL success ) {
2021-03-15 13:27:59 +00:00
if ( ! success )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] refreshActivitiesViewDisplay: adminContact(%@) cannot be opened" , adminContactURL ) ;
2021-03-15 13:27:59 +00:00
}
2020-09-01 10:20:44 +00:00
} ] ;
2018-10-02 13:30:00 +00:00
} ] ;
}
2016-08-29 16:34:00 +00:00
else
{
2021-03-20 20:31:17 +00:00
self . scrollToBottomHidden = YES ;
2021-03-15 13:27:59 +00:00
self . activitiesViewExpanded = NO ;
2016-08-29 16:34:00 +00:00
[ self refreshTypingNotification ] ;
}
2016-02-25 12:54:53 +00:00
}
2016-09-05 08:40:46 +00:00
// Recognize swipe downward to dismiss keyboard if any
UISwipeGestureRecognizer * swipe = [ [ UISwipeGestureRecognizer alloc ] initWithTarget : self action : @ selector ( onSwipeGesture : ) ] ;
[ swipe setNumberOfTouchesRequired : 1 ] ;
[ swipe setDirection : UISwipeGestureRecognizerDirectionDown ] ;
[ roomActivitiesView addGestureRecognizer : swipe ] ;
2015-11-18 08:38:55 +00:00
}
2016-02-25 12:54:53 +00:00
}
2017-06-19 08:32:22 +00:00
- ( void ) goBackToLive
{
if ( self . roomDataSource . isLive )
{
// Enable the read marker display , and disable its update ( in order to not mark as read all the new messages by default ) .
self . roomDataSource . showReadMarker = YES ;
self . updateRoomReadMarker = NO ;
[ self scrollBubblesTableViewToBottomAnimated : YES ] ;
}
else
{
// Switch back to the room live timeline managed by MXKRoomDataSourceManager
MXKRoomDataSourceManager * roomDataSourceManager = [ MXKRoomDataSourceManager sharedManagerForMatrixSession : self . mainSession ] ;
2021-03-15 13:27:59 +00:00
2018-07-24 14:30:48 +00:00
MXWeakify ( self ) ;
2018-07-20 09:28:02 +00:00
[ roomDataSourceManager roomDataSourceForRoom : self . roomDataSource . roomId create : YES onComplete : ^ ( MXKRoomDataSource * roomDataSource ) {
2018-07-24 14:30:48 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
// Scroll to bottom the bubble history on the display refresh .
2018-07-24 14:30:48 +00:00
self -> shouldScrollToBottomOnTableRefresh = YES ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
[ self displayRoom : roomDataSource ] ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
// The room view controller do not have here the data source ownership .
self . hasRoomDataSourceOwnership = NO ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
[ self refreshActivitiesViewDisplay ] ;
[ self refreshJumpToLastUnreadBannerDisplay ] ;
2021-03-15 13:27:59 +00:00
2018-07-20 09:28:02 +00:00
if ( self . saveProgressTextInput )
{
// Restore the potential message partially typed before jump to last unread messages .
self . inputToolbarView . textMessage = roomDataSource . partialTextMessage ;
}
} ] ;
2017-06-19 08:32:22 +00:00
}
}
2016-08-31 13:12:46 +00:00
# pragma mark - Missed discussions handling
- ( void ) refreshMissedDiscussionsCount : ( BOOL ) force
{
2016-08-31 18:57:08 +00:00
// Ignore this action when no room is displayed
2021-08-16 13:44:44 +00:00
if ( ! self . showMissedDiscussionsBadge || ! self . roomDataSource || ! missedDiscussionsBadgeLabel
2021-03-24 21:17:09 +00:00
|| [ UIDevice currentDevice ] . userInterfaceIdiom ! = UIUserInterfaceIdiomPhone
|| ( [ [ UIScreen mainScreen ] nativeBounds ] . size . height > 2532 && UIInterfaceOrientationIsLandscape ( [ UIApplication sharedApplication ] . statusBarOrientation ) ) )
2021-03-15 13:27:59 +00:00
{
2021-08-16 13:44:44 +00:00
self . missedDiscussionsBadgeHidden = YES ;
2021-03-15 13:27:59 +00:00
return ;
}
2021-08-16 13:44:44 +00:00
self . missedDiscussionsBadgeHidden = NO ;
2016-09-05 12:00:56 +00:00
NSUInteger highlightCount = 0 ;
2017-04-18 13:55:51 +00:00
NSUInteger missedCount = [ [ AppDelegate theDelegate ] . masterTabBarController missedDiscussionsCount ] ;
// Compute the missed notifications count of the current room by considering its notification mode in Riot .
NSUInteger roomNotificationCount = self . roomDataSource . room . summary . notificationCount ;
if ( self . roomDataSource . room . isMentionsOnly )
{
// Only the highlighted missed messages must be considered here .
roomNotificationCount = self . roomDataSource . room . summary . highlightCount ;
}
// Remove the current room from the missed discussion counter .
if ( missedCount && roomNotificationCount )
2016-08-31 13:47:34 +00:00
{
2016-09-05 12:00:56 +00:00
missedCount - - ;
}
if ( missedCount )
{
// Compute the missed highlight count
2017-04-18 13:55:51 +00:00
highlightCount = [ [ AppDelegate theDelegate ] . masterTabBarController missedHighlightDiscussionsCount ] ;
if ( highlightCount && self . roomDataSource . room . summary . highlightCount )
2016-09-05 12:00:56 +00:00
{
// Remove the current room from the missed highlight counter
highlightCount - - ;
}
2016-08-31 13:47:34 +00:00
}
2016-09-05 12:00:56 +00:00
if ( force || missedDiscussionsCount ! = missedCount || missedHighlightCount ! = highlightCount )
2016-08-31 13:12:46 +00:00
{
2016-09-05 12:00:56 +00:00
missedDiscussionsCount = missedCount ;
missedHighlightCount = highlightCount ;
2016-08-31 13:12:46 +00:00
2016-09-05 12:00:56 +00:00
if ( missedCount )
2016-08-31 13:12:46 +00:00
{
2016-08-31 18:57:08 +00:00
// Refresh missed discussions count label
2016-09-05 12:00:56 +00:00
if ( missedCount > 99 )
2016-08-31 13:12:46 +00:00
{
missedDiscussionsBadgeLabel . text = @ "99+" ;
}
else
{
2016-09-05 12:00:56 +00:00
missedDiscussionsBadgeLabel . text = [ NSString stringWithFormat : @ "%tu" , missedCount ] ;
2016-08-31 13:12:46 +00:00
}
2021-03-24 21:17:09 +00:00
missedDiscussionsDotView . alpha = highlightCount = = 0 ? 0 : 1 ;
2016-08-31 18:57:08 +00:00
}
2021-03-15 13:27:59 +00:00
else
2016-08-31 18:57:08 +00:00
{
2021-03-15 13:27:59 +00:00
missedDiscussionsBadgeLabel . text = nil ;
2016-08-31 13:12:46 +00:00
}
}
}
2016-02-25 12:54:53 +00:00
# pragma mark - Unsent Messages Handling
- ( BOOL ) checkUnsentMessages
{
2021-09-22 08:41:22 +00:00
MXRoomSummarySentStatus sentStatus = MXRoomSummarySentStatusOk ;
2016-08-29 16:34:00 +00:00
if ( [ self . activitiesView isKindOfClass : RoomActivitiesView . class ] )
2015-11-18 08:38:55 +00:00
{
2021-09-22 08:41:22 +00:00
sentStatus = self . roomDataSource . room . summary . sentStatus ;
2021-03-15 13:27:59 +00:00
2021-09-22 08:41:22 +00:00
if ( sentStatus ! = MXRoomSummarySentStatusOk )
2016-02-25 12:54:53 +00:00
{
2021-09-22 08:41:22 +00:00
NSString * notification = sentStatus = = MXRoomSummarySentStatusSentFailedDueToUnknownDevices ?
2021-09-28 05:40:01 +00:00
[ VectorL10n roomUnsentMessagesUnknownDevicesNotification ] :
[ VectorL10n roomUnsentMessagesNotification ] ;
2017-07-14 14:41:25 +00:00
2021-11-01 15:05:02 +00:00
MXWeakify ( self ) ;
2016-03-07 12:56:52 +00:00
RoomActivitiesView * roomActivitiesView = ( RoomActivitiesView * ) self . activitiesView ;
2021-03-15 13:27:59 +00:00
self . activitiesViewExpanded = YES ;
2017-02-14 10:01:38 +00:00
[ roomActivitiesView displayUnsentMessagesNotification : notification withResendLink : ^ {
2016-02-25 12:54:53 +00:00
2016-03-07 12:56:52 +00:00
[ self resendAllUnsentMessages ] ;
2016-02-25 12:54:53 +00:00
2017-03-07 15:46:56 +00:00
} andCancelLink : ^ {
2017-07-14 14:41:25 +00:00
2017-03-07 15:46:56 +00:00
[ self cancelAllUnsentMessages ] ;
2017-07-14 14:41:25 +00:00
2016-03-07 12:56:52 +00:00
} andIconTapGesture : ^ {
2021-11-01 15:05:02 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2017-07-14 14:41:25 +00:00
2021-11-01 15:05:02 +00:00
if ( self -> currentAlert )
2016-02-25 12:54:53 +00:00
{
2021-11-01 15:05:02 +00:00
[ self -> currentAlert dismissViewControllerAnimated : NO completion : nil ] ;
2016-03-07 12:56:52 +00:00
}
2021-11-01 15:05:02 +00:00
MXWeakify ( self ) ;
UIAlertController * resendAlert = [ UIAlertController alertControllerWithTitle : nil message : nil preferredStyle : UIAlertControllerStyleActionSheet ] ;
2016-03-07 12:56:52 +00:00
2021-11-01 15:05:02 +00:00
[ resendAlert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomResendUnsentMessages ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
2021-11-01 15:05:02 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self resendAllUnsentMessages ] ;
self -> currentAlert = nil ;
2021-03-15 13:27:59 +00:00
} ] ] ;
2016-03-07 12:56:52 +00:00
2021-11-01 15:05:02 +00:00
[ resendAlert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n roomDeleteUnsentMessages ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
2021-11-01 15:05:02 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
[ self cancelAllUnsentMessages ] ;
self -> currentAlert = nil ;
2021-03-15 13:27:59 +00:00
} ] ] ;
2016-03-07 12:56:52 +00:00
2021-11-01 15:05:02 +00:00
[ resendAlert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n cancel ]
style : UIAlertActionStyleCancel
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
2021-11-01 15:05:02 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
self -> currentAlert = nil ;
2021-03-15 13:27:59 +00:00
} ] ] ;
2016-03-07 12:56:52 +00:00
2021-11-01 15:05:02 +00:00
[ resendAlert mxk_setAccessibilityIdentifier : @ "RoomVCUnsentMessagesMenuAlert" ] ;
[ resendAlert popoverPresentationController ] . sourceView = roomActivitiesView ;
[ resendAlert popoverPresentationController ] . sourceRect = roomActivitiesView . bounds ;
[ self presentViewController : resendAlert animated : YES completion : nil ] ;
self -> currentAlert = resendAlert ;
2016-02-25 12:54:53 +00:00
} ] ;
}
2015-11-18 08:38:55 +00:00
}
2021-09-22 08:41:22 +00:00
return sentStatus ! = MXRoomSummarySentStatusOk ;
2016-02-25 12:54:53 +00:00
}
2017-02-14 10:01:38 +00:00
- ( void ) eventDidChangeSentState : ( NSNotification * ) notif
{
// We are only interested by event that has just failed in their encryption
// because of unknown devices in the room
MXEvent * event = notif . object ;
if ( event . sentState = = MXEventSentStateFailed &&
[ event . roomId isEqualToString : self . roomDataSource . roomId ]
&& [ event . sentError . domain isEqualToString : MXEncryptingErrorDomain ]
&& event . sentError . code = = MXEncryptingErrorUnknownDeviceCode
2017-02-15 10:36:22 +00:00
&& ! unknownDevices ) // Show the alert once in case of resending several events
2017-02-14 10:01:38 +00:00
{
__weak __typeof ( self ) weakSelf = self ;
2017-07-14 14:41:25 +00:00
2017-02-14 10:01:38 +00:00
[ self dismissTemporarySubViews ] ;
2017-07-14 14:41:25 +00:00
2017-02-15 10:36:22 +00:00
// List all unknown devices
unknownDevices = [ [ MXUsersDevicesMap alloc ] init ] ;
2017-07-14 14:41:25 +00:00
2017-02-15 10:36:22 +00:00
NSArray < MXEvent * > * outgoingMsgs = self . roomDataSource . room . outgoingMessages ;
for ( MXEvent * event in outgoingMsgs )
{
if ( event . sentState = = MXEventSentStateFailed
&& [ event . sentError . domain isEqualToString : MXEncryptingErrorDomain ]
&& event . sentError . code = = MXEncryptingErrorUnknownDeviceCode )
{
MXUsersDevicesMap < MXDeviceInfo * > * eventUnknownDevices = event . sentError . userInfo [ MXEncryptingErrorUnknownDeviceDevicesKey ] ;
2017-07-14 14:41:25 +00:00
2017-02-15 10:36:22 +00:00
[ unknownDevices addEntriesFromMap : eventUnknownDevices ] ;
}
}
2017-07-14 14:41:25 +00:00
2021-11-01 15:05:02 +00:00
UIAlertController * unknownDevicesAlert = [ UIAlertController alertControllerWithTitle : [ VectorL10n unknownDevicesAlertTitle ]
message : [ VectorL10n unknownDevicesAlert ]
preferredStyle : UIAlertControllerStyleAlert ] ;
2017-07-14 14:41:25 +00:00
2021-11-01 15:05:02 +00:00
[ unknownDevicesAlert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n unknownDevicesVerify ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> currentAlert = nil ;
[ self performSegueWithIdentifier : @ "showUnknownDevices" sender : self ] ;
}
} ] ] ;
2017-07-14 14:41:25 +00:00
2021-11-01 15:05:02 +00:00
[ unknownDevicesAlert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n unknownDevicesSendAnyway ]
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> currentAlert = nil ;
// Acknowledge the existence of all devices
[ self startActivityIndicator ] ;
[ self . mainSession . crypto setDevicesKnown : self -> unknownDevices complete : ^ {
self -> unknownDevices = nil ;
[ self stopActivityIndicator ] ;
// And resend pending messages
[ self resendAllUnsentMessages ] ;
} ] ;
}
} ] ] ;
2017-07-14 14:41:25 +00:00
2021-11-01 15:05:02 +00:00
[ unknownDevicesAlert mxk_setAccessibilityIdentifier : @ "RoomVCUnknownDevicesAlert" ] ;
[ self presentViewController : unknownDevicesAlert animated : YES completion : nil ] ;
currentAlert = unknownDevicesAlert ;
2017-02-14 10:01:38 +00:00
}
}
2019-06-27 10:25:01 +00:00
- ( void ) eventDidChangeIdentifier : ( NSNotification * ) notif
{
MXEvent * event = notif . object ;
NSString * previousId = notif . userInfo [ kMXEventIdentifierKey ] ;
2021-03-15 13:27:59 +00:00
2019-06-27 10:25:01 +00:00
if ( [ customizedRoomDataSource . selectedEventId isEqualToString : previousId ] )
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] eventDidChangeIdentifier: Update selectedEventId" ) ;
2019-06-27 10:25:01 +00:00
customizedRoomDataSource . selectedEventId = event . eventId ;
}
}
2017-03-08 08:13:48 +00:00
2016-03-07 12:56:52 +00:00
- ( void ) resendAllUnsentMessages
{
// List unsent event ids
NSArray * outgoingMsgs = self . roomDataSource . room . outgoingMessages ;
NSMutableArray * failedEventIds = [ NSMutableArray arrayWithCapacity : outgoingMsgs . count ] ;
for ( MXEvent * event in outgoingMsgs )
{
2016-12-05 15:32:21 +00:00
if ( event . sentState = = MXEventSentStateFailed )
2016-03-07 12:56:52 +00:00
{
[ failedEventIds addObject : event . eventId ] ;
}
}
// Launch iterative operation
[ self resendFailedEvent : 0 inArray : failedEventIds ] ;
}
2016-02-25 12:54:53 +00:00
- ( void ) resendFailedEvent : ( NSUInteger ) index inArray : ( NSArray * ) failedEventIds
{
if ( index < failedEventIds . count )
2015-11-24 12:34:08 +00:00
{
2016-02-25 12:54:53 +00:00
NSString * failedEventId = failedEventIds [ index ] ;
NSUInteger nextIndex = index + 1 ;
// Let the datasource resend . It will manage local echo , etc .
[ self . roomDataSource resendEventWithEventId : failedEventId success : ^ ( NSString * eventId ) {
[ self resendFailedEvent : nextIndex inArray : failedEventIds ] ;
} failure : ^ ( NSError * error ) {
[ self resendFailedEvent : nextIndex inArray : failedEventIds ] ;
} ] ;
return ;
2015-11-24 12:34:08 +00:00
}
2016-02-25 12:54:53 +00:00
// Refresh activities view
[ self refreshActivitiesViewDisplay ] ;
2015-11-18 08:38:55 +00:00
}
2017-03-07 15:46:56 +00:00
- ( void ) cancelAllUnsentMessages
{
2021-11-01 15:05:02 +00:00
UIAlertController * cancelAlert = [ UIAlertController alertControllerWithTitle : [ VectorL10n roomUnsentMessagesCancelTitle ]
message : [ VectorL10n roomUnsentMessagesCancelMessage ]
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-03-15 13:27:59 +00:00
2021-03-02 20:56:50 +00:00
MXWeakify ( self ) ;
2021-11-01 15:05:02 +00:00
[ cancelAlert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n cancel ] style : UIAlertActionStyleCancel handler : ^ ( UIAlertAction * action ) {
2021-03-02 20:56:50 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
self -> currentAlert = nil ;
} ] ] ;
2021-11-01 15:05:02 +00:00
[ cancelAlert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n delete ] style : UIAlertActionStyleDestructive handler : ^ ( UIAlertAction * action ) {
2021-03-02 20:56:50 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
// Remove unsent event ids
for ( NSUInteger index = 0 ; index < self . roomDataSource . room . outgoingMessages . count ; )
2017-03-07 15:46:56 +00:00
{
2021-03-02 20:56:50 +00:00
MXEvent * event = self . roomDataSource . room . outgoingMessages [ index ] ;
if ( event . sentState = = MXEventSentStateFailed )
{
[ self . roomDataSource removeEventWithEventId : event . eventId ] ;
}
else
{
index + + ;
}
2017-03-07 15:46:56 +00:00
}
2021-03-15 13:27:59 +00:00
[ self refreshActivitiesViewDisplay ] ;
2021-11-01 15:05:02 +00:00
self -> currentAlert = nil ;
2021-03-02 20:56:50 +00:00
} ] ] ;
2021-03-15 13:27:59 +00:00
2021-11-01 15:05:02 +00:00
[ self presentViewController : cancelAlert animated : YES completion : nil ] ;
currentAlert = cancelAlert ;
2017-03-07 15:46:56 +00:00
}
2016-11-16 08:31:58 +00:00
# pragma mark - Encryption Information view
- ( void ) showEncryptionInformation : ( MXEvent * ) event
{
[ self dismissKeyboard ] ;
2017-07-14 14:41:25 +00:00
2016-11-16 08:31:58 +00:00
// Remove potential existing subviews
[ self dismissTemporarySubViews ] ;
2021-10-29 08:50:24 +00:00
EncryptionInfoView * encryptionInfoView = [ [ EncryptionInfoView alloc ] initWithEvent : event andMatrixSession : self . roomDataSource . mxSession ] ;
2016-11-16 08:31:58 +00:00
2016-11-28 15:26:59 +00:00
// Add shadow on added view
2016-11-16 08:31:58 +00:00
encryptionInfoView . layer . cornerRadius = 5 ;
encryptionInfoView . layer . shadowOffset = CGSizeMake ( 0 , 1 ) ;
encryptionInfoView . layer . shadowOpacity = 0.5 f ;
// Add the view and define edge constraints
[ self . view addSubview : encryptionInfoView ] ;
2021-10-29 08:50:24 +00:00
self -> encryptionInfoView = encryptionInfoView ;
2016-11-16 08:31:58 +00:00
[ self . view addConstraint : [ NSLayoutConstraint constraintWithItem : encryptionInfoView
attribute : NSLayoutAttributeTop
relatedBy : NSLayoutRelationEqual
toItem : self . topLayoutGuide
attribute : NSLayoutAttributeBottom
multiplier : 1.0 f
constant : 10.0 f ] ] ;
[ self . view addConstraint : [ NSLayoutConstraint constraintWithItem : encryptionInfoView
attribute : NSLayoutAttributeBottom
relatedBy : NSLayoutRelationEqual
toItem : self . bottomLayoutGuide
attribute : NSLayoutAttributeTop
multiplier : 1.0 f
constant : -10.0 f ] ] ;
[ self . view addConstraint : [ NSLayoutConstraint constraintWithItem : self . view
attribute : NSLayoutAttributeLeading
relatedBy : NSLayoutRelationEqual
toItem : encryptionInfoView
attribute : NSLayoutAttributeLeading
multiplier : 1.0 f
constant : -10.0 f ] ] ;
[ self . view addConstraint : [ NSLayoutConstraint constraintWithItem : self . view
attribute : NSLayoutAttributeTrailing
relatedBy : NSLayoutRelationEqual
toItem : encryptionInfoView
attribute : NSLayoutAttributeTrailing
multiplier : 1.0 f
constant : 10.0 f ] ] ;
[ self . view setNeedsUpdateConstraints ] ;
}
2017-06-01 15:20:08 +00:00
# pragma mark - Read marker handling
- ( void ) checkReadMarkerVisibility
{
2017-06-09 12:20:00 +00:00
if ( readMarkerTableViewCell && isAppeared && ! self . isBubbleTableViewDisplayInTransition )
2017-06-01 15:20:08 +00:00
{
// Check whether the read marker is visible
2021-09-01 09:44:15 +00:00
CGFloat contentTopPosY = self . bubblesTableView . contentOffset . y + self . bubblesTableView . adjustedContentInset . top ;
2017-06-07 11:07:44 +00:00
CGFloat readMarkerViewPosY = readMarkerTableViewCell . frame . origin . y + readMarkerTableViewCell . readMarkerView . frame . origin . y ;
if ( contentTopPosY <= readMarkerViewPosY )
2017-06-01 15:20:08 +00:00
{
2017-06-07 11:07:44 +00:00
// Compute the max vertical position visible according to contentOffset
2021-09-01 09:44:15 +00:00
CGFloat contentBottomPosY = self . bubblesTableView . contentOffset . y + self . bubblesTableView . frame . size . height - self . bubblesTableView . adjustedContentInset . bottom ;
2017-06-07 11:07:44 +00:00
if ( readMarkerViewPosY <= contentBottomPosY )
{
// Launch animation
[ self animateReadMarkerView ] ;
// Disable the read marker display when it has been rendered once .
self . roomDataSource . showReadMarker = NO ;
[ self refreshJumpToLastUnreadBannerDisplay ] ;
// Update the read marker position according the events acknowledgement in this view controller .
self . updateRoomReadMarker = YES ;
2017-06-09 12:20:00 +00:00
if ( self . roomDataSource . isLive )
{
// Move the read marker to the current read receipt position .
[ self . roomDataSource . room forgetReadMarker ] ;
}
2017-06-07 11:07:44 +00:00
}
2017-06-01 15:20:08 +00:00
}
}
}
- ( void ) animateReadMarkerView
{
// Check whether the cell with the read marker is known and if the marker is not animated yet .
if ( readMarkerTableViewCell && readMarkerTableViewCell . readMarkerView . isHidden )
{
RoomBubbleCellData * cellData = ( RoomBubbleCellData * ) readMarkerTableViewCell . bubbleData ;
// Do not display the marker if this is the last message .
if ( cellData . containsLastMessage && readMarkerTableViewCell . readMarkerView . tag = = cellData . mostRecentComponentIndex )
{
readMarkerTableViewCell . readMarkerView . hidden = YES ;
readMarkerTableViewCell = nil ;
}
else
{
readMarkerTableViewCell . readMarkerView . hidden = NO ;
// Animate the layout to hide the read marker
dispatch_after ( dispatch_time ( DISPATCH_TIME _NOW , ( int64_t ) ( 0.5 * NSEC_PER _SEC ) ) , dispatch_get _main _queue ( ) , ^ {
[ UIView animateWithDuration : 1.5 delay : 0 options : UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
animations : ^ {
2021-03-15 13:27:59 +00:00
readMarkerTableViewCell . readMarkerViewLeadingConstraint . constant = readMarkerTableViewCell . readMarkerViewTrailingConstraint . constant = readMarkerTableViewCell . bubbleOverlayContainer . frame . size . width / 2 ;
readMarkerTableViewCell . readMarkerView . alpha = 0 ;
// Force to render the view
[ readMarkerTableViewCell . bubbleOverlayContainer layoutIfNeeded ] ;
}
2017-06-01 15:20:08 +00:00
completion : ^ ( BOOL finished ) {
2021-03-15 13:27:59 +00:00
readMarkerTableViewCell . readMarkerView . hidden = YES ;
readMarkerTableViewCell . readMarkerView . alpha = 1 ;
readMarkerTableViewCell = nil ;
} ] ;
2017-06-01 15:20:08 +00:00
} ) ;
}
}
}
2021-04-23 15:36:51 +00:00
- ( void ) refreshRemoveJitsiWidgetView
{
if ( self . roomDataSource . isLive && ! self . roomDataSource . isPeeking )
{
Widget * jitsiWidget = [ customizedRoomDataSource jitsiWidget ] ;
if ( jitsiWidget && self . canEditJitsiWidget )
{
[ self . removeJitsiWidgetView reset ] ;
self . removeJitsiWidgetContainer . hidden = NO ;
self . removeJitsiWidgetView . delegate = self ;
}
else
{
self . removeJitsiWidgetContainer . hidden = YES ;
self . removeJitsiWidgetView . delegate = nil ;
}
}
else
{
[ self . removeJitsiWidgetView reset ] ;
self . removeJitsiWidgetContainer . hidden = YES ;
self . removeJitsiWidgetView . delegate = self ;
}
}
2017-06-01 15:20:08 +00:00
- ( void ) refreshJumpToLastUnreadBannerDisplay
{
2017-06-14 21:09:56 +00:00
// This banner is only displayed when the room timeline is in live ( and no peeking ) .
2017-06-06 08:53:17 +00:00
// Check whether the read marker exists and has not been rendered yet .
2017-06-14 21:09:56 +00:00
if ( self . roomDataSource . isLive && ! self . roomDataSource . isPeeking && self . roomDataSource . showReadMarker && self . roomDataSource . room . accountData . readMarkerEventId )
2017-06-01 15:20:08 +00:00
{
UITableViewCell * cell = [ self . bubblesTableView visibleCells ] . firstObject ;
if ( [ cell isKindOfClass : MXKRoomBubbleTableViewCell . class ] )
{
MXKRoomBubbleTableViewCell * roomBubbleTableViewCell = ( MXKRoomBubbleTableViewCell * ) cell ;
// Check whether the read marker is inside the first displayed cell .
if ( roomBubbleTableViewCell . readMarkerView )
{
2017-06-09 12:20:00 +00:00
// The read marker display is still enabled ( see roomDataSource . showReadMarker flag ) ,
// this means the read marker was not been visible yet .
// We show the banner if the marker is located in the top hidden part of the cell .
2021-09-01 09:44:15 +00:00
CGFloat contentTopPosY = self . bubblesTableView . contentOffset . y + self . bubblesTableView . adjustedContentInset . top ;
2017-06-09 12:20:00 +00:00
CGFloat readMarkerViewPosY = roomBubbleTableViewCell . frame . origin . y + roomBubbleTableViewCell . readMarkerView . frame . origin . y ;
self . jumpToLastUnreadBannerContainer . hidden = ( contentTopPosY < readMarkerViewPosY ) ;
2017-06-01 15:20:08 +00:00
}
else
{
2017-06-13 15:46:13 +00:00
// Check whether the read marker event is anterior to the first event displayed in the first rendered cell .
MXKRoomBubbleComponent * component = roomBubbleTableViewCell . bubbleData . bubbleComponents . firstObject ;
MXEvent * firstDisplayedEvent = component . event ;
2017-06-01 15:20:08 +00:00
MXEvent * currentReadMarkerEvent = [ self . roomDataSource . mxSession . store eventWithEventId : self . roomDataSource . room . accountData . readMarkerEventId inRoom : self . roomDataSource . roomId ] ;
2017-06-13 15:46:13 +00:00
if ( ! currentReadMarkerEvent || ( currentReadMarkerEvent . originServerTs < firstDisplayedEvent . originServerTs ) )
2017-06-01 15:20:08 +00:00
{
self . jumpToLastUnreadBannerContainer . hidden = NO ;
}
else
{
self . jumpToLastUnreadBannerContainer . hidden = YES ;
}
}
}
}
else
{
self . jumpToLastUnreadBannerContainer . hidden = YES ;
2017-06-08 08:30:29 +00:00
2017-06-14 21:09:56 +00:00
// Initialize the read marker if it does not exist yet , only in case of live timeline .
if ( ! self . roomDataSource . room . accountData . readMarkerEventId && self . roomDataSource . isLive && ! self . roomDataSource . isPeeking )
2017-06-08 08:30:29 +00:00
{
// Move the read marker to the current read receipt position by default .
[ self . roomDataSource . room forgetReadMarker ] ;
}
2017-06-01 15:20:08 +00:00
}
}
2017-06-20 09:32:54 +00:00
# pragma mark - ContactsTableViewControllerDelegate
- ( void ) contactsTableViewController : ( ContactsTableViewController * ) contactsTableViewController didSelectContact : ( MXKContact * ) contact
{
__weak typeof ( self ) weakSelf = self ;
if ( currentAlert )
{
2017-07-14 14:41:25 +00:00
[ currentAlert dismissViewControllerAnimated : NO completion : nil ] ;
2017-06-20 09:32:54 +00:00
currentAlert = nil ;
}
// Invite ?
2021-09-28 05:40:01 +00:00
NSString * promptMsg = [ VectorL10n roomParticipantsInvitePromptMsg : contact . displayName ] ;
2021-11-01 15:05:02 +00:00
UIAlertController * invitePrompt = [ UIAlertController alertControllerWithTitle : [ VectorL10n roomParticipantsInvitePromptTitle ]
message : promptMsg
preferredStyle : UIAlertControllerStyleAlert ] ;
2017-07-14 14:41:25 +00:00
2021-11-01 15:05:02 +00:00
[ invitePrompt addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n cancel ]
2018-01-19 02:43:28 +00:00
style : UIAlertActionStyleCancel
2017-07-14 14:41:25 +00:00
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
if ( weakSelf )
{
typeof ( self ) self = weakSelf ;
self -> currentAlert = nil ;
}
} ] ] ;
2017-07-14 14:41:25 +00:00
2021-11-01 15:05:02 +00:00
[ invitePrompt addAction : [ UIAlertAction actionWithTitle : [ VectorL10n invite ]
2017-07-14 14:41:25 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
// Sanity check
if ( ! weakSelf )
{
return ;
}
typeof ( self ) self = weakSelf ;
self -> currentAlert = nil ;
MXSession * session = self . roomDataSource . mxSession ;
NSString * roomId = self . roomDataSource . roomId ;
MXRoom * room = [ session roomWithRoomId : roomId ] ;
NSArray * identifiers = contact . matrixIdentifiers ;
NSString * participantId ;
if ( identifiers . count )
{
participantId = identifiers . firstObject ;
// Invite this user if a room is defined
[ room inviteUser : participantId success : ^ {
// Refresh display by removing the contacts picker
[ contactsTableViewController withdrawViewControllerAnimated : YES completion : nil ] ;
} failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] Invite %@ failed" , participantId ) ;
2021-03-15 13:27:59 +00:00
// Alert user
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2021-03-15 13:27:59 +00:00
} ] ;
}
else
{
if ( contact . emailAddresses . count )
{
// This is a local contact , consider the first email by default .
// TODO : Prompt the user to select the right email .
MXKEmail * email = contact . emailAddresses . firstObject ;
participantId = email . emailAddress ;
}
else
{
// This is the text filled by the user .
participantId = contact . displayName ;
}
// Is it an email or a Matrix user ID ?
if ( [ MXTools isEmailAddress : participantId ] )
{
[ room inviteUserByEmail : participantId success : ^ {
// Refresh display by removing the contacts picker
[ contactsTableViewController withdrawViewControllerAnimated : YES completion : nil ] ;
} failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] Invite be email %@ failed" , participantId ) ;
2021-03-15 13:27:59 +00:00
// Alert user
if ( [ error . domain isEqualToString : kMXRestClientErrorDomain ]
&& error . code = = MXRestClientErrorMissingIdentityServer )
{
2021-09-28 05:40:01 +00:00
[ self showAlertWithTitle : [ VectorL10n errorInvite3pidWithNoIdentityServer ] message : nil ] ;
2021-03-15 13:27:59 +00:00
}
else
{
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2021-03-15 13:27:59 +00:00
}
} ] ;
}
else // if ( [ MXTools isMatrixUserIdentifier : participantId ] )
{
[ room inviteUser : participantId success : ^ {
// Refresh display by removing the contacts picker
[ contactsTableViewController withdrawViewControllerAnimated : YES completion : nil ] ;
} failure : ^ ( NSError * error ) {
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomVC] Invite %@ failed" , participantId ) ;
2021-03-15 13:27:59 +00:00
// Alert user
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2021-03-15 13:27:59 +00:00
} ] ;
}
}
} ] ] ;
2017-07-14 14:41:25 +00:00
2021-11-01 15:05:02 +00:00
[ invitePrompt mxk_setAccessibilityIdentifier : @ "RoomVCInviteAlert" ] ;
[ self presentViewController : invitePrompt animated : YES completion : nil ] ;
currentAlert = invitePrompt ;
2017-06-20 09:32:54 +00:00
}
2018-06-08 15:21:52 +00:00
# pragma mark - Re - request encryption keys
2018-06-12 14:28:26 +00:00
- ( void ) reRequestKeysAndShowExplanationAlert : ( MXEvent * ) event
2018-06-08 15:21:52 +00:00
{
2018-06-12 14:28:26 +00:00
MXWeakify ( self ) ;
__block UIAlertController * alert ;
2021-02-02 10:31:21 +00:00
// Force device verification if session has cross - signing activated and device is not yet verified
if ( self . mainSession . crypto . crossSigning && self . mainSession . crypto . crossSigning . state = = MXCrossSigningStateCrossSigningExists )
{
[ self presentReviewUnverifiedSessionsAlert ] ;
return ;
}
2021-03-15 13:27:59 +00:00
2018-06-12 14:28:26 +00:00
// Make the re - request
[ self . mainSession . crypto reRequestRoomKeyForEvent : event ] ;
2021-03-15 13:27:59 +00:00
2018-06-12 14:28:26 +00:00
// Observe kMXEventDidDecryptNotification to remove automatically the dialog
// if the user has shared the keys from another device
2018-06-12 16:21:04 +00:00
mxEventDidDecryptNotificationObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXEventDidDecryptNotification object : nil queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
MXStrongifyAndReturnIfNil ( self ) ;
2021-03-15 13:27:59 +00:00
2018-06-12 14:28:26 +00:00
MXEvent * decryptedEvent = notif . object ;
2021-03-15 13:27:59 +00:00
2018-06-12 14:28:26 +00:00
if ( [ decryptedEvent . eventId isEqualToString : event . eventId ] )
{
2018-06-12 16:21:04 +00:00
[ [ NSNotificationCenter defaultCenter ] removeObserver : self -> mxEventDidDecryptNotificationObserver ] ;
self -> mxEventDidDecryptNotificationObserver = nil ;
2021-03-15 13:27:59 +00:00
2018-06-12 14:28:26 +00:00
if ( self -> currentAlert = = alert )
{
[ self -> currentAlert dismissViewControllerAnimated : YES completion : nil ] ;
self -> currentAlert = nil ;
}
}
} ] ;
2021-03-15 13:27:59 +00:00
2018-06-12 14:28:26 +00:00
// Show the explanation dialog
2021-09-22 11:10:16 +00:00
alert = [ UIAlertController alertControllerWithTitle : VectorL10n . rerequestKeysAlertTitle
2021-09-22 13:59:47 +00:00
message : [ VectorL10n e2eRoomKeyRequestMessage : AppInfo . current . displayName ]
2021-03-15 13:27:59 +00:00
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-09-28 05:40:01 +00:00
[ alert addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n ok ]
2021-03-15 13:27:59 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action )
{
MXStrongifyAndReturnIfNil ( self ) ;
[ [ NSNotificationCenter defaultCenter ] removeObserver : self -> mxEventDidDecryptNotificationObserver ] ;
self -> mxEventDidDecryptNotificationObserver = nil ;
self -> currentAlert = nil ;
} ] ] ;
2021-11-01 15:05:02 +00:00
[ self presentViewController : alert animated : YES completion : nil ] ;
currentAlert = alert ;
2018-06-08 15:21:52 +00:00
}
2021-02-02 10:31:21 +00:00
- ( void ) presentReviewUnverifiedSessionsAlert
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[MasterTabBarController] presentReviewUnverifiedSessionsAlertWithSession" ) ;
2021-03-15 13:27:59 +00:00
2021-02-02 10:31:21 +00:00
[ currentAlert dismissViewControllerAnimated : NO completion : nil ] ;
2021-03-15 13:27:59 +00:00
2021-09-28 05:40:01 +00:00
UIAlertController * alert = [ UIAlertController alertControllerWithTitle : [ VectorL10n keyVerificationSelfVerifyUnverifiedSessionsAlertTitle ]
message : [ VectorL10n keyVerificationSelfVerifyUnverifiedSessionsAlertMessage ]
2021-02-02 10:31:21 +00:00
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-03-15 13:27:59 +00:00
2021-09-28 05:40:01 +00:00
[ alert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n keyVerificationSelfVerifyUnverifiedSessionsAlertValidateAction ]
2021-02-02 10:31:21 +00:00
style : UIAlertActionStyleDefault
handler : ^ ( UIAlertAction * action ) {
2021-03-15 13:27:59 +00:00
[ self showSettingsSecurityScreen ] ;
} ] ] ;
2021-09-28 05:40:01 +00:00
[ alert addAction : [ UIAlertAction actionWithTitle : [ VectorL10n later ]
2021-02-02 10:31:21 +00:00
style : UIAlertActionStyleCancel
handler : nil ] ] ;
2021-03-15 13:27:59 +00:00
2021-02-02 10:31:21 +00:00
[ self presentViewController : alert animated : YES completion : nil ] ;
2021-03-15 13:27:59 +00:00
2021-02-02 10:31:21 +00:00
currentAlert = alert ;
}
- ( void ) showSettingsSecurityScreen
{
2021-09-07 15:43:49 +00:00
if ( self . delegate )
{
[ self . delegate roomViewController : self showCompleteSecurityForSession : self . mainSession ] ;
}
else
{
[ [ AppDelegate theDelegate ] presentCompleteSecurityForSession : self . mainSession ] ;
}
2021-02-02 10:31:21 +00:00
}
2018-07-27 09:56:51 +00:00
# pragma mark Tombstone event
- ( void ) listenTombstoneEventNotifications
{
// Room is already obsolete do not listen to tombstone event
2018-08-07 16:20:22 +00:00
if ( self . roomDataSource . roomState . isObsolete )
2018-07-27 09:56:51 +00:00
{
return ;
}
MXWeakify ( self ) ;
2018-08-07 16:20:22 +00:00
tombstoneEventNotificationsListener = [ self . roomDataSource . room listenToEventsOfTypes : @ [ kMXEventTypeStringRoomTombStone ] onEvent : ^ ( MXEvent * event , MXTimelineDirection direction , MXRoomState * roomState ) {
2018-07-27 09:56:51 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
// Update activitiesView with room replacement information
[ self refreshActivitiesViewDisplay ] ;
// Hide inputToolbarView
2018-08-27 13:32:37 +00:00
[ self updateRoomInputToolbarViewClassIfNeeded ] ;
2018-07-27 09:56:51 +00:00
} ] ;
}
- ( void ) removeTombstoneEventNotificationsListener
{
if ( self . roomDataSource )
{
// Remove the previous live listener
if ( tombstoneEventNotificationsListener )
{
2018-08-07 16:20:22 +00:00
[ self . roomDataSource . room removeListener : tombstoneEventNotificationsListener ] ;
2018-07-27 09:56:51 +00:00
tombstoneEventNotificationsListener = nil ;
}
}
}
2018-08-21 16:25:56 +00:00
# pragma mark MXSession state change
- ( void ) listenMXSessionStateChangeNotifications
{
2021-10-28 15:41:16 +00:00
MXWeakify ( self ) ;
2018-08-21 16:25:56 +00:00
kMXSessionStateDidChangeObserver = [ [ NSNotificationCenter defaultCenter ] addObserverForName : kMXSessionStateDidChangeNotification object : self . roomDataSource . mxSession queue : [ NSOperationQueue mainQueue ] usingBlock : ^ ( NSNotification * notif ) {
2021-03-15 13:27:59 +00:00
2021-10-28 15:41:16 +00:00
MXStrongifyAndReturnIfNil ( self ) ;
2018-08-21 16:25:56 +00:00
if ( self . roomDataSource . mxSession . state = = MXSessionStateSyncError
|| self . roomDataSource . mxSession . state = = MXSessionStateRunning )
{
[ self refreshActivitiesViewDisplay ] ;
2021-03-15 13:27:59 +00:00
2018-08-23 15:03:32 +00:00
// update inputToolbarView
2018-08-27 13:32:37 +00:00
[ self updateRoomInputToolbarViewClassIfNeeded ] ;
2018-08-21 16:25:56 +00:00
}
} ] ;
}
- ( void ) removeMXSessionStateChangeNotificationsListener
{
if ( kMXSessionStateDidChangeObserver )
{
[ [ NSNotificationCenter defaultCenter ] removeObserver : kMXSessionStateDidChangeObserver ] ;
kMXSessionStateDidChangeObserver = nil ;
}
}
2019-05-15 21:24:34 +00:00
# pragma mark - Contextual Menu
- ( NSArray < RoomContextualMenuItem * > * ) contextualMenuItemsForEvent : ( MXEvent * ) event andCell : ( id < MXKCellRendering > ) cell
{
2021-02-25 15:51:39 +00:00
if ( event . sentState = = MXEventSentStateFailed )
2020-10-13 22:01:29 +00:00
{
2021-02-25 15:51:39 +00:00
return @ [
[ self resendMenuItemWithEvent : event ] ,
[ self deleteMenuItemWithEvent : event ] ,
[ self editMenuItemWithEvent : event ] ,
[ self copyMenuItemWithEvent : event andCell : cell ]
] ;
2020-01-14 19:23:36 +00:00
}
2021-04-29 18:10:07 +00:00
BOOL showMoreOption = ( event . isState && RiotSettings . shared . roomContextualMenuShowMoreOptionForStates ) || ( ! event . isState && RiotSettings . shared . roomContextualMenuShowMoreOptionForMessages ) ;
2021-04-29 15:03:34 +00:00
if ( showMoreOption )
{
return @ [
[ self copyMenuItemWithEvent : event andCell : cell ] ,
[ self replyMenuItemWithEvent : event ] ,
[ self editMenuItemWithEvent : event ] ,
[ self moreMenuItemWithEvent : event andCell : cell ]
] ;
}
else
{
return @ [
[ self copyMenuItemWithEvent : event andCell : cell ] ,
[ self replyMenuItemWithEvent : event ] ,
[ self editMenuItemWithEvent : event ]
] ;
}
2019-05-15 21:24:34 +00:00
}
2019-06-27 09:41:25 +00:00
- ( void ) showContextualMenuForEvent : ( MXEvent * ) event fromSingleTapGesture : ( BOOL ) usedSingleTapGesture cell : ( id < MXKCellRendering > ) cell animated : ( BOOL ) animated
2019-05-15 21:24:34 +00:00
{
if ( self . roomContextualMenuPresenter . isPresenting )
{
return ;
}
2019-06-25 12:00:12 +00:00
NSString * selectedEventId = event . eventId ;
2019-05-15 21:24:34 +00:00
2019-06-25 12:00:12 +00:00
NSArray < RoomContextualMenuItem * > * contextualMenuItems = [ self contextualMenuItemsForEvent : event andCell : cell ] ;
ReactionsMenuViewModel * reactionsMenuViewModel ;
CGRect bubbleComponentFrameInOverlayView = CGRectNull ;
2019-05-22 16:48:50 +00:00
2019-07-09 11:51:14 +00:00
if ( [ cell isKindOfClass : MXKRoomBubbleTableViewCell . class ] && [ self . roomDataSource canReactToEventWithId : event . eventId ] )
2019-05-17 10:10:32 +00:00
{
2019-05-22 16:48:50 +00:00
MXKRoomBubbleTableViewCell * roomBubbleTableViewCell = ( MXKRoomBubbleTableViewCell * ) cell ;
MXKRoomBubbleCellData * bubbleCellData = roomBubbleTableViewCell . bubbleData ;
NSArray * bubbleComponents = bubbleCellData . bubbleComponents ;
2019-07-25 14:46:35 +00:00
NSInteger foundComponentIndex = [ bubbleCellData bubbleComponentIndexForEventId : event . eventId ] ;
2019-05-22 16:48:50 +00:00
CGRect bubbleComponentFrame ;
if ( bubbleComponents . count > 0 )
{
NSInteger selectedComponentIndex = foundComponentIndex ! = NSNotFound ? foundComponentIndex : 0 ;
2019-07-08 16:43:55 +00:00
bubbleComponentFrame = [ roomBubbleTableViewCell surroundingFrameInTableViewForComponentIndex : selectedComponentIndex ] ;
2019-05-22 16:48:50 +00:00
}
else
{
bubbleComponentFrame = roomBubbleTableViewCell . frame ;
}
2019-06-25 12:00:12 +00:00
bubbleComponentFrameInOverlayView = [ self . bubblesTableView convertRect : bubbleComponentFrame toView : self . overlayContainerView ] ;
2019-05-22 16:48:50 +00:00
2019-06-06 09:30:57 +00:00
NSString * roomId = self . roomDataSource . roomId ;
MXAggregations * aggregations = self . mainSession . aggregations ;
2019-06-25 12:00:12 +00:00
MXAggregatedReactions * aggregatedReactions = [ aggregations aggregatedReactionsOnEvent : selectedEventId inRoom : roomId ] ;
2019-06-06 09:30:57 +00:00
2019-06-25 12:00:12 +00:00
reactionsMenuViewModel = [ [ ReactionsMenuViewModel alloc ] initWithAggregatedReactions : aggregatedReactions eventId : selectedEventId ] ;
2019-06-06 09:30:57 +00:00
reactionsMenuViewModel . coordinatorDelegate = self ;
2019-05-17 10:10:32 +00:00
}
2019-06-25 12:00:12 +00:00
2019-06-27 12:37:17 +00:00
if ( ! self . roomContextualMenuViewController )
{
self . roomContextualMenuViewController = [ RoomContextualMenuViewController instantiate ] ;
self . roomContextualMenuViewController . delegate = self ;
}
[ self . roomContextualMenuViewController updateWithContextualMenuItems : contextualMenuItems reactionsMenuViewModel : reactionsMenuViewModel ] ;
2019-06-25 12:00:12 +00:00
[ self enableOverlayContainerUserInteractions : YES ] ;
2019-06-27 12:37:17 +00:00
[ self . roomContextualMenuPresenter presentWithRoomContextualMenuViewController : self . roomContextualMenuViewController
2019-06-25 12:00:12 +00:00
from : self
on : self . overlayContainerView
contentToReactFrame : bubbleComponentFrameInOverlayView
2019-06-27 09:41:25 +00:00
fromSingleTapGesture : usedSingleTapGesture
animated : animated
2019-06-25 12:00:12 +00:00
completion : ^ {
2021-03-15 13:27:59 +00:00
} ] ;
2019-06-27 09:41:25 +00:00
2019-10-28 17:55:55 +00:00
preventBubblesTableViewScroll = YES ;
2021-03-15 13:27:59 +00:00
[ self selectEventWithId : selectedEventId ] ;
2019-05-15 21:24:34 +00:00
}
- ( 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 ] ;
}
2019-10-29 08:19:37 +00:00
preventBubblesTableViewScroll = NO ;
2019-05-15 21:24:34 +00:00
[ self . roomContextualMenuPresenter hideContextualMenuWithAnimated : animated completion : ^ {
2019-05-24 09:33:44 +00:00
[ self enableOverlayContainerUserInteractions : NO ] ;
2019-05-15 21:24:34 +00:00
if ( completion )
{
completion ( ) ;
}
} ] ;
}
2019-05-24 09:33:44 +00:00
- ( void ) enableOverlayContainerUserInteractions : ( BOOL ) enableOverlayContainerUserInteractions
2019-05-15 21:24:34 +00:00
{
2019-05-24 09:33:44 +00:00
self . inputToolbarView . editable = ! enableOverlayContainerUserInteractions ;
self . bubblesTableView . scrollsToTop = ! enableOverlayContainerUserInteractions ;
self . overlayContainerView . userInteractionEnabled = enableOverlayContainerUserInteractions ;
2019-05-15 21:24:34 +00:00
}
2021-02-25 15:51:39 +00:00
- ( RoomContextualMenuItem * ) resendMenuItemWithEvent : ( MXEvent * ) event
{
MXWeakify ( self ) ;
RoomContextualMenuItem * resendMenuItem = [ [ RoomContextualMenuItem alloc ] initWithMenuAction : RoomContextualMenuActionResend ] ;
resendMenuItem . action = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self hideContextualMenuAnimated : YES cancelEventSelection : NO completion : nil ] ;
[ self cancelEventSelection ] ;
[ self . roomDataSource resendEventWithEventId : event . eventId success : nil failure : nil ] ;
} ;
2021-03-15 13:27:59 +00:00
2021-02-25 15:51:39 +00:00
return resendMenuItem ;
}
- ( RoomContextualMenuItem * ) deleteMenuItemWithEvent : ( MXEvent * ) event
{
MXWeakify ( self ) ;
RoomContextualMenuItem * deleteMenuItem = [ [ RoomContextualMenuItem alloc ] initWithMenuAction : RoomContextualMenuActionDelete ] ;
deleteMenuItem . action = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
MXWeakify ( self ) ;
[ self hideContextualMenuAnimated : YES cancelEventSelection : YES completion : ^ {
MXStrongifyAndReturnIfNil ( self ) ;
2021-11-01 15:05:02 +00:00
UIAlertController * deleteConfirmation = [ UIAlertController alertControllerWithTitle : [ VectorL10n roomEventActionDeleteConfirmationTitle ]
message : [ VectorL10n roomEventActionDeleteConfirmationMessage ]
preferredStyle : UIAlertControllerStyleAlert ] ;
2021-02-25 15:51:39 +00:00
2021-11-01 15:05:02 +00:00
[ deleteConfirmation addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n cancel ] style : UIAlertActionStyleDefault handler : ^ ( UIAlertAction * action ) {
2021-02-25 15:51:39 +00:00
} ] ] ;
2021-11-01 15:05:02 +00:00
[ deleteConfirmation addAction : [ UIAlertAction actionWithTitle : [ MatrixKitL10n delete ] style : UIAlertActionStyleDestructive handler : ^ ( UIAlertAction * action ) {
2021-02-25 15:51:39 +00:00
[ self . roomDataSource removeEventWithEventId : event . eventId ] ;
} ] ] ;
2021-11-01 15:05:02 +00:00
[ self presentViewController : deleteConfirmation animated : YES completion : nil ] ;
self -> currentAlert = deleteConfirmation ;
2021-02-25 15:51:39 +00:00
} ] ;
} ;
2021-03-15 13:27:59 +00:00
2021-02-25 15:51:39 +00:00
return deleteMenuItem ;
}
- ( RoomContextualMenuItem * ) editMenuItemWithEvent : ( MXEvent * ) event
{
MXWeakify ( self ) ;
RoomContextualMenuItem * editMenuItem = [ [ RoomContextualMenuItem alloc ] initWithMenuAction : RoomContextualMenuActionEdit ] ;
2022-01-12 06:44:53 +00:00
switch ( event . eventType ) {
case MXEventTypePollStart : {
editMenuItem . action = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self hideContextualMenuAnimated : YES cancelEventSelection : YES completion : nil ] ;
[ self . delegate roomViewController : self didRequestEditForPollWithStartEvent : event ] ;
} ;
editMenuItem . isEnabled = [ self . delegate roomViewController : self canEditPollWithEventIdentifier : event . eventId ] ;
break ;
}
default : {
editMenuItem . action = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self hideContextualMenuAnimated : YES cancelEventSelection : NO completion : nil ] ;
[ self editEventContentWithId : event . eventId ] ;
// And display the keyboard
[ self . inputToolbarView becomeFirstResponder ] ;
} ;
editMenuItem . isEnabled = [ self . roomDataSource canEditEventWithId : event . eventId ] ;
break ;
}
}
2021-02-25 15:51:39 +00:00
return editMenuItem ;
}
- ( RoomContextualMenuItem * ) copyMenuItemWithEvent : ( MXEvent * ) event andCell : ( id < MXKCellRendering > ) cell
{
MXKRoomBubbleTableViewCell * roomBubbleTableViewCell = ( MXKRoomBubbleTableViewCell * ) cell ;
MXKAttachment * attachment = roomBubbleTableViewCell . bubbleData . attachment ;
2021-03-15 13:27:59 +00:00
2021-02-25 15:51:39 +00:00
MXWeakify ( self ) ;
2021-11-19 15:40:52 +00:00
BOOL isCopyActionEnabled = ( event . eventType ! = MXEventTypePollStart && ( ! attachment || attachment . type ! = MXKAttachmentTypeSticker ) ) ;
2021-02-25 15:51:39 +00:00
if ( attachment && ! BuildSettings . messageDetailsAllowCopyMedia )
{
isCopyActionEnabled = NO ;
}
if ( isCopyActionEnabled )
{
switch ( event . eventType ) {
case MXEventTypeRoomMessage :
{
2021-12-13 08:04:30 +00:00
NSString * messageType = event . content [ kMXMessageTypeKey ] ;
2021-02-25 15:51:39 +00:00
if ( [ messageType isEqualToString : kMXMessageTypeKeyVerificationRequest ] )
{
isCopyActionEnabled = NO ;
}
break ;
}
case MXEventTypeKeyVerificationStart :
case MXEventTypeKeyVerificationAccept :
case MXEventTypeKeyVerificationKey :
case MXEventTypeKeyVerificationMac :
case MXEventTypeKeyVerificationDone :
case MXEventTypeKeyVerificationCancel :
isCopyActionEnabled = NO ;
break ;
2021-04-15 14:27:41 +00:00
case MXEventTypeCustom :
if ( [ event . type isEqualToString : kWidgetMatrixEventTypeString ]
|| [ event . type isEqualToString : kWidgetModularEventTypeString ] )
{
Widget * widget = [ [ Widget alloc ] initWithWidgetEvent : event inMatrixSession : self . roomDataSource . mxSession ] ;
if ( [ widget . type isEqualToString : kWidgetTypeJitsiV1 ] ||
[ widget . type isEqualToString : kWidgetTypeJitsiV2 ] )
{
isCopyActionEnabled = NO ;
}
}
2021-02-25 15:51:39 +00:00
default :
break ;
}
}
RoomContextualMenuItem * copyMenuItem = [ [ RoomContextualMenuItem alloc ] initWithMenuAction : RoomContextualMenuActionCopy ] ;
copyMenuItem . isEnabled = isCopyActionEnabled ;
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 ;
if ( textMessage )
{
MXKPasteboardManager . shared . pasteboard . string = textMessage ;
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[RoomViewController] Contextual menu copy failed. Text is nil for room id/event id: %@/%@" , selectedComponent . event . roomId , selectedComponent . event . eventId ) ;
2021-02-25 15:51:39 +00:00
}
[ 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
2021-09-07 15:43:49 +00:00
[ self showError : error ] ;
2021-02-25 15:51:39 +00:00
} ] ;
// Start animation in case of download during attachment preparing
[ roomBubbleTableViewCell startProgressUI ] ;
} ] ;
}
} ;
2021-03-15 13:27:59 +00:00
2021-02-25 15:51:39 +00:00
return copyMenuItem ;
}
- ( RoomContextualMenuItem * ) replyMenuItemWithEvent : ( MXEvent * ) event
{
MXWeakify ( self ) ;
RoomContextualMenuItem * replyMenuItem = [ [ RoomContextualMenuItem alloc ] initWithMenuAction : RoomContextualMenuActionReply ] ;
2021-07-12 09:18:49 +00:00
replyMenuItem . isEnabled = [ self . roomDataSource canReplyToEventWithId : event . eventId ] && ! self . voiceMessageController . isRecordingAudio ;
2021-02-25 15:51:39 +00:00
replyMenuItem . action = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self hideContextualMenuAnimated : YES cancelEventSelection : NO completion : nil ] ;
[ self selectEventWithId : event . eventId inputToolBarSendMode : RoomInputToolbarViewSendModeReply showTimestamp : NO ] ;
2021-03-15 13:27:59 +00:00
2021-02-25 15:51:39 +00:00
// And display the keyboard
[ self . inputToolbarView becomeFirstResponder ] ;
} ;
return replyMenuItem ;
}
- ( RoomContextualMenuItem * ) moreMenuItemWithEvent : ( MXEvent * ) event andCell : ( id < MXKCellRendering > ) cell
{
MXWeakify ( self ) ;
RoomContextualMenuItem * moreMenuItem = [ [ RoomContextualMenuItem alloc ] initWithMenuAction : RoomContextualMenuActionMore ] ;
moreMenuItem . action = ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self hideContextualMenuAnimated : YES completion : nil ] ;
[ self showAdditionalActionsMenuForEvent : event inCell : cell animated : YES ] ;
} ;
2021-03-15 13:27:59 +00:00
2021-02-25 15:51:39 +00:00
return moreMenuItem ;
}
2019-05-15 21:24:34 +00:00
# pragma mark - RoomContextualMenuViewControllerDelegate
- ( void ) roomContextualMenuViewControllerDidTapBackgroundOverlay : ( RoomContextualMenuViewController * ) viewController
{
[ self hideContextualMenuAnimated : YES ] ;
}
2019-06-06 09:30:57 +00:00
# pragma mark - ReactionsMenuViewModelCoordinatorDelegate
- ( void ) reactionsMenuViewModel : ( ReactionsMenuViewModel * ) viewModel didAddReaction : ( NSString * ) reaction forEventId : ( NSString * ) eventId
{
2019-06-11 15:43:32 +00:00
MXWeakify ( self ) ;
2019-06-25 12:00:12 +00:00
[ self hideContextualMenuAnimated : YES completion : ^ {
2019-06-06 09:30:57 +00:00
2019-06-25 12:00:12 +00:00
[ self . roomDataSource addReaction : reaction forEventId : eventId success : ^ {
} failure : ^ ( NSError * error ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self . errorPresenter presentErrorFromViewController : self forError : error animated : YES handler : nil ] ;
} ] ;
2019-06-06 09:30:57 +00:00
} ] ;
}
- ( void ) reactionsMenuViewModel : ( ReactionsMenuViewModel * ) viewModel didRemoveReaction : ( NSString * ) reaction forEventId : ( NSString * ) eventId
{
2019-06-11 15:43:32 +00:00
MXWeakify ( self ) ;
2019-06-25 12:00:12 +00:00
[ self hideContextualMenuAnimated : YES completion : ^ {
2019-06-06 09:30:57 +00:00
2019-06-25 12:00:12 +00:00
[ self . roomDataSource removeReaction : reaction forEventId : eventId success : ^ {
} failure : ^ ( NSError * error ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self . errorPresenter presentErrorFromViewController : self forError : error animated : YES handler : nil ] ;
} ] ;
2019-06-06 09:30:57 +00:00
} ] ;
}
2019-07-25 14:46:35 +00:00
- ( void ) reactionsMenuViewModelDidTapMoreReactions : ( ReactionsMenuViewModel * ) viewModel forEventId : ( NSString * ) eventId
{
[ self hideContextualMenuAnimated : YES ] ;
EmojiPickerCoordinatorBridgePresenter * emojiPickerCoordinatorBridgePresenter = [ [ EmojiPickerCoordinatorBridgePresenter alloc ] initWithSession : self . mainSession roomId : self . roomDataSource . roomId eventId : eventId ] ;
emojiPickerCoordinatorBridgePresenter . delegate = self ;
NSInteger cellRow = [ self . roomDataSource indexOfCellDataWithEventId : eventId ] ;
UIView * sourceView ;
CGRect sourceRect = CGRectNull ;
2019-07-25 17:16:15 +00:00
if ( cellRow >= 0 )
2019-07-25 14:46:35 +00:00
{
2021-03-15 13:27:59 +00:00
NSIndexPath * cellIndexPath = [ NSIndexPath indexPathForRow : cellRow inSection : 0 ] ;
2019-07-25 14:46:35 +00:00
UITableViewCell * cell = [ self . bubblesTableView cellForRowAtIndexPath : cellIndexPath ] ;
sourceView = cell ;
if ( [ cell isKindOfClass : [ MXKRoomBubbleTableViewCell class ] ] )
{
MXKRoomBubbleTableViewCell * roomBubbleTableViewCell = ( MXKRoomBubbleTableViewCell * ) cell ;
NSInteger bubbleComponentIndex = [ roomBubbleTableViewCell . bubbleData bubbleComponentIndexForEventId : eventId ] ;
sourceRect = [ roomBubbleTableViewCell componentFrameInContentViewForIndex : bubbleComponentIndex ] ;
}
}
[ emojiPickerCoordinatorBridgePresenter presentFrom : self sourceView : sourceView sourceRect : sourceRect animated : YES ] ;
self . emojiPickerCoordinatorBridgePresenter = emojiPickerCoordinatorBridgePresenter ;
}
2019-07-04 17:24:15 +00:00
# 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 ;
}
2019-07-11 17:17:55 +00:00
# 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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[MXKRoomViewController] sendImage failed." ) ;
2019-07-11 17:17:55 +00:00
} ] ;
}
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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[MXKRoomViewController] sendVideo failed." ) ;
2019-07-11 17:17:55 +00:00
} ] ;
}
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
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[MXKRoomViewController] sendFile failed." ) ;
2019-07-11 17:17:55 +00:00
} ] ;
}
else
{
2021-06-03 08:30:07 +00:00
MXLogDebug ( @ "[MXKRoomViewController] File upload using MIME type %@ is not supported." , mimeType ) ;
2019-07-11 17:17:55 +00:00
2021-09-28 05:40:01 +00:00
[ self showAlertWithTitle : [ VectorL10n fileUploadErrorTitle ]
message : [ VectorL10n fileUploadErrorUnsupportedFileTypeMessage ] ] ;
2019-07-11 17:17:55 +00:00
}
}
2019-07-25 14:46:35 +00:00
# pragma mark - EmojiPickerCoordinatorBridgePresenterDelegate
- ( void ) emojiPickerCoordinatorBridgePresenter : ( EmojiPickerCoordinatorBridgePresenter * ) coordinatorBridgePresenter didAddEmoji : ( NSString * ) emoji forEventId : ( NSString * ) eventId
{
MXWeakify ( self ) ;
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : ^ {
[ self . roomDataSource addReaction : emoji forEventId : eventId success : ^ {
} failure : ^ ( NSError * error ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self . errorPresenter presentErrorFromViewController : self forError : error animated : YES handler : nil ] ;
} ] ;
} ] ;
self . emojiPickerCoordinatorBridgePresenter = nil ;
}
- ( void ) emojiPickerCoordinatorBridgePresenter : ( EmojiPickerCoordinatorBridgePresenter * ) coordinatorBridgePresenter didRemoveEmoji : ( NSString * ) emoji forEventId : ( NSString * ) eventId
{
MXWeakify ( self ) ;
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : ^ {
[ self . roomDataSource removeReaction : emoji forEventId : eventId success : ^ {
} failure : ^ ( NSError * error ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self . errorPresenter presentErrorFromViewController : self forError : error animated : YES handler : nil ] ;
} ] ;
} ] ;
self . emojiPickerCoordinatorBridgePresenter = nil ;
}
- ( void ) emojiPickerCoordinatorBridgePresenterDidCancel : ( EmojiPickerCoordinatorBridgePresenter * ) coordinatorBridgePresenter
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : nil ] ;
self . emojiPickerCoordinatorBridgePresenter = nil ;
}
2019-07-30 15:18:39 +00:00
# pragma mark - ReactionHistoryCoordinatorBridgePresenterDelegate
- ( void ) reactionHistoryCoordinatorBridgePresenterDelegateDidClose : ( ReactionHistoryCoordinatorBridgePresenter * ) coordinatorBridgePresenter
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : ^ {
self . reactionHistoryCoordinatorBridgePresenter = nil ;
} ] ;
}
2019-08-02 15:19:29 +00:00
# pragma mark - CameraPresenterDelegate
- ( void ) cameraPresenterDidCancel : ( CameraPresenter * ) cameraPresenter
{
[ cameraPresenter dismissWithAnimated : YES completion : nil ] ;
self . cameraPresenter = nil ;
}
- ( void ) cameraPresenter : ( CameraPresenter * ) cameraPresenter didSelectImageData : ( NSData * ) imageData withUTI : ( MXKUTI * ) uti
{
[ cameraPresenter dismissWithAnimated : YES completion : nil ] ;
self . cameraPresenter = nil ;
RoomInputToolbarView * roomInputToolbarView = [ self inputToolbarViewAsRoomInputToolbarView ] ;
if ( roomInputToolbarView )
{
2021-09-07 13:46:02 +00:00
[ roomInputToolbarView sendSelectedImage : imageData
withMimeType : uti . mimeType
andCompressionMode : MediaCompressionHelper . defaultCompressionMode
isPhotoLibraryAsset : NO ] ;
2019-08-02 15:19:29 +00:00
}
}
- ( void ) cameraPresenter : ( CameraPresenter * ) cameraPresenter didSelectVideoAt : ( NSURL * ) url
{
[ cameraPresenter dismissWithAnimated : YES completion : nil ] ;
self . cameraPresenter = nil ;
2021-08-16 16:48:26 +00:00
AVURLAsset * selectedVideo = [ AVURLAsset assetWithURL : url ] ;
[ self sendVideoAsset : selectedVideo isPhotoLibraryAsset : NO ] ;
2019-08-02 15:19:29 +00:00
}
# pragma mark - MediaPickerCoordinatorBridgePresenterDelegate
- ( void ) mediaPickerCoordinatorBridgePresenterDidCancel : ( MediaPickerCoordinatorBridgePresenter * ) coordinatorBridgePresenter
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : nil ] ;
self . mediaPickerPresenter = nil ;
}
- ( void ) mediaPickerCoordinatorBridgePresenter : ( MediaPickerCoordinatorBridgePresenter * ) coordinatorBridgePresenter didSelectImageData : ( NSData * ) imageData withUTI : ( MXKUTI * ) uti
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : nil ] ;
self . mediaPickerPresenter = nil ;
RoomInputToolbarView * roomInputToolbarView = [ self inputToolbarViewAsRoomInputToolbarView ] ;
if ( roomInputToolbarView )
{
2021-09-07 13:46:02 +00:00
[ roomInputToolbarView sendSelectedImage : imageData
withMimeType : uti . mimeType
andCompressionMode : MediaCompressionHelper . defaultCompressionMode
isPhotoLibraryAsset : YES ] ;
2019-08-02 15:19:29 +00:00
}
}
2021-07-08 15:26:19 +00:00
- ( void ) mediaPickerCoordinatorBridgePresenter : ( MediaPickerCoordinatorBridgePresenter * ) coordinatorBridgePresenter didSelectVideo : ( AVAsset * ) videoAsset
2019-08-02 15:19:29 +00:00
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : nil ] ;
self . mediaPickerPresenter = nil ;
2021-08-16 16:48:26 +00:00
[ self sendVideoAsset : videoAsset isPhotoLibraryAsset : YES ] ;
2019-08-02 15:19:29 +00:00
}
- ( void ) mediaPickerCoordinatorBridgePresenter : ( MediaPickerCoordinatorBridgePresenter * ) coordinatorBridgePresenter didSelectAssets : ( NSArray < PHAsset * > * ) assets
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : nil ] ;
self . mediaPickerPresenter = nil ;
RoomInputToolbarView * roomInputToolbarView = [ self inputToolbarViewAsRoomInputToolbarView ] ;
if ( roomInputToolbarView )
{
2021-08-16 16:48:26 +00:00
// Set a 1080 p video conversion preset as compression mode only has an effect on the images .
[ MXSDKOptions sharedInstance ] . videoConversionPresetName = AVAssetExportPreset1920x1080 ;
2021-09-07 13:46:02 +00:00
[ roomInputToolbarView sendSelectedAssets : assets withCompressionMode : MediaCompressionHelper . defaultCompressionMode ] ;
2019-08-02 15:19:29 +00:00
}
}
2020-09-14 16:44:30 +00:00
# pragma mark - RoomCreationModalCoordinatorBridgePresenter
- ( void ) roomCreationModalCoordinatorBridgePresenterDelegateDidComplete : ( RoomCreationModalCoordinatorBridgePresenter * ) coordinatorBridgePresenter
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : nil ] ;
self . roomCreationModalCoordinatorBridgePresenter = nil ;
}
2020-09-18 15:57:40 +00:00
# pragma mark - RoomInfoCoordinatorBridgePresenterDelegate
- ( void ) roomInfoCoordinatorBridgePresenterDelegateDidComplete : ( RoomInfoCoordinatorBridgePresenter * ) coordinatorBridgePresenter
{
[ coordinatorBridgePresenter dismissWithAnimated : YES completion : nil ] ;
self . roomInfoCoordinatorBridgePresenter = nil ;
}
2021-07-21 13:19:06 +00:00
- ( void ) roomInfoCoordinatorBridgePresenter : ( RoomInfoCoordinatorBridgePresenter * ) coordinatorBridgePresenter didRequestMentionForMember : ( MXRoomMember * ) member
{
[ self mention : member ] ;
}
2021-08-13 14:02:47 +00:00
- ( void ) roomInfoCoordinatorBridgePresenterDelegateDidLeaveRoom : ( RoomInfoCoordinatorBridgePresenter * ) coordinatorBridgePresenter
{
2021-09-07 15:43:49 +00:00
if ( self . delegate )
{
[ self . delegate roomViewControllerDidLeaveRoom : self ] ;
}
else
{
[ [ AppDelegate theDelegate ] restoreInitialDisplay : nil ] ;
}
2021-08-13 14:02:47 +00:00
}
2021-04-23 15:36:51 +00:00
# pragma mark - RemoveJitsiWidgetViewDelegate
- ( void ) removeJitsiWidgetViewDidCompleteSliding : ( RemoveJitsiWidgetView * ) view
{
view . delegate = nil ;
Widget * jitsiWidget = [ customizedRoomDataSource jitsiWidget ] ;
[ self startActivityIndicator ] ;
// close the widget
MXWeakify ( self ) ;
[ [ WidgetManager sharedManager ] closeWidget : jitsiWidget . widgetId
inRoom : self . roomDataSource . room
success : ^ {
MXStrongifyAndReturnIfNil ( self ) ;
[ self stopActivityIndicator ] ;
// we can wait for kWidgetManagerDidUpdateWidgetNotification , but we want to be faster
self . removeJitsiWidgetContainer . hidden = YES ;
self . removeJitsiWidgetView . delegate = nil ;
// end active call if exists
2021-09-07 15:43:49 +00:00
if ( [ self isRoomHavingAJitsiCallForWidgetId : jitsiWidget . widgetId ] )
2021-04-23 15:36:51 +00:00
{
2021-09-07 15:43:49 +00:00
[ self endActiveJitsiCall ] ;
2021-04-23 15:36:51 +00:00
}
} failure : ^ ( NSError * error ) {
MXStrongifyAndReturnIfNil ( self ) ;
[ self showJitsiErrorAsAlert : error ] ;
[ self stopActivityIndicator ] ;
} ] ;
}
2021-06-07 07:20:26 +00:00
# pragma mark - VoiceMessageControllerDelegate
2021-06-14 14:47:59 +00:00
- ( void ) voiceMessageControllerDidRequestMicrophonePermission : ( VoiceMessageController * ) voiceMessageController
2021-06-08 07:04:44 +00:00
{
2021-09-28 05:40:01 +00:00
NSString * message = [ MatrixKitL10n microphoneAccessNotGrantedForVoiceMessage : AppInfo . current . displayName ] ;
2021-06-08 07:04:44 +00:00
[ MXKTools checkAccessForMediaType : AVMediaTypeAudio
manualChangeMessage : message
showPopUpInViewController : self completionHandler : ^ ( BOOL granted ) {
2021-06-14 14:47:59 +00:00
2021-06-08 07:04:44 +00:00
} ] ;
}
2021-07-13 13:03:20 +00:00
- ( void ) voiceMessageController : ( VoiceMessageController * ) voiceMessageController
didRequestSendForFileAtURL : ( NSURL * ) url
2021-07-16 09:49:28 +00:00
duration : ( NSUInteger ) duration
2021-07-13 13:03:20 +00:00
samples : ( NSArray < NSNumber * > * ) samples
completion : ( void ( ^ ) ( BOOL ) ) completion
2021-06-07 07:20:26 +00:00
{
2021-07-13 13:03:20 +00:00
[ self . roomDataSource sendVoiceMessage : url mimeType : nil duration : duration samples : samples success : ^ ( NSString * eventId ) {
2021-06-07 07:20:26 +00:00
MXLogDebug ( @ "Success with event id %@" , eventId ) ;
completion ( YES ) ;
} failure : ^ ( NSError * error ) {
MXLogError ( @ "Failed sending voice message" ) ;
completion ( NO ) ;
} ] ;
}
2021-09-22 12:58:19 +00:00
# pragma mark - SpaceDetailPresenterDelegate
- ( void ) spaceDetailPresenterDidComplete : ( SpaceDetailPresenter * ) presenter
{
self . spaceDetailPresenter = nil ;
}
- ( void ) spaceDetailPresenter : ( SpaceDetailPresenter * ) presenter didOpenSpaceWithId : ( NSString * ) spaceId
{
self . spaceDetailPresenter = nil ;
[ [ LegacyAppDelegate theDelegate ] openSpaceWithId : spaceId ] ;
}
- ( void ) spaceDetailPresenter : ( SpaceDetailPresenter * ) presenter didJoinSpaceWithId : ( NSString * ) spaceId
{
self . spaceDetailPresenter = nil ;
[ [ LegacyAppDelegate theDelegate ] openSpaceWithId : spaceId ] ;
}
2021-10-05 11:30:31 +00:00
# pragma mark - UserSuggestionCoordinatorBridgeDelegate
- ( void ) userSuggestionCoordinatorBridge : ( UserSuggestionCoordinatorBridge * ) coordinator
didRequestMentionForMember : ( MXRoomMember * ) member
textTrigger : ( NSString * ) textTrigger
{
if ( textTrigger . length ) {
NSString * textMessage = [ self . inputToolbarView textMessage ] ;
textMessage = [ textMessage stringByReplacingOccurrencesOfString : textTrigger
withString : @ ""
options : NSBackwardsSearch | NSAnchoredSearch
range : NSMakeRange ( 0 , textMessage . length ) ] ;
[ self . inputToolbarView setTextMessage : textMessage ] ;
}
[ self mention : member ] ;
}
2014-10-02 15:02:47 +00:00
@ end