|
@ -3,6 +3,7 @@ Changes in 0.8.6 (2019-xx-xx)
|
|||
|
||||
Improvements:
|
||||
* RoomVC: When replying, use a "Reply" button instead of "Send".
|
||||
* RoomVC: New message actions (#2394).
|
||||
|
||||
Changes in 0.8.5 (2019-xx-xx)
|
||||
===============================================
|
||||
|
|
|
@ -189,6 +189,7 @@
|
|||
B1798302211B13B3001FD722 /* OnBoardingManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1798301211B13B3001FD722 /* OnBoardingManager.swift */; };
|
||||
B19EFA3921F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */; };
|
||||
B19EFA3B21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */; };
|
||||
B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1A5B33D227ADF2A004CBA85 /* UIImage.swift */; };
|
||||
B1B5571820EE6C4D00210D55 /* CountryPickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */; };
|
||||
B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */; };
|
||||
B1B5571A20EE6C4D00210D55 /* SettingsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5567E20EE6C4C00210D55 /* SettingsViewController.m */; };
|
||||
|
@ -426,6 +427,17 @@
|
|||
B1B5599320EFC5E400210D55 /* DecryptionFailure.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5598D20EFC5E400210D55 /* DecryptionFailure.m */; };
|
||||
B1B5599420EFC5E400210D55 /* DecryptionFailureTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = B1B5599120EFC5E400210D55 /* DecryptionFailureTracker.m */; };
|
||||
B1B9194C2118984300FE25B5 /* RoomPredecessorBubbleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1B9194A2118984300FE25B5 /* RoomPredecessorBubbleCell.xib */; };
|
||||
B1C562CA2289C2690037F12A /* UIGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */; };
|
||||
B1C562CC228AB3510037F12A /* UIStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562CB228AB3510037F12A /* UIStackView.swift */; };
|
||||
B1C562D9228C0B760037F12A /* RoomContextualMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */; };
|
||||
B1C562DB228C0BB00037F12A /* RoomContextualMenuAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562DA228C0BB00037F12A /* RoomContextualMenuAction.swift */; };
|
||||
B1C562E1228C7C8C0037F12A /* RoomContextualMenuToolbarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562DC228C7C890037F12A /* RoomContextualMenuToolbarView.swift */; };
|
||||
B1C562E2228C7C8D0037F12A /* RoomContextualMenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562DD228C7C8A0037F12A /* RoomContextualMenuViewController.swift */; };
|
||||
B1C562E3228C7C8D0037F12A /* RoomContextualMenuPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562DE228C7C8B0037F12A /* RoomContextualMenuPresenter.swift */; };
|
||||
B1C562E4228C7C8D0037F12A /* RoomContextualMenuToolbarView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1C562DF228C7C8C0037F12A /* RoomContextualMenuToolbarView.xib */; };
|
||||
B1C562E5228C7C8D0037F12A /* RoomContextualMenuViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B1C562E0228C7C8C0037F12A /* RoomContextualMenuViewController.storyboard */; };
|
||||
B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1C562E6228C7CF10037F12A /* ContextualMenuItemView.swift */; };
|
||||
B1C562E9228C7CF20037F12A /* ContextualMenuItemView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B1C562E7228C7CF20037F12A /* ContextualMenuItemView.xib */; };
|
||||
B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2621EF6913000D1D89 /* UIViewController.swift */; };
|
||||
B1CA3A2921EF692B000D1D89 /* UIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CA3A2821EF692B000D1D89 /* UIView.swift */; };
|
||||
B1CE9EFD22148703000FAE6A /* SignOutAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */; };
|
||||
|
@ -760,6 +772,7 @@
|
|||
B1798301211B13B3001FD722 /* OnBoardingManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnBoardingManager.swift; sourceTree = "<group>"; };
|
||||
B19EFA3821F8BB2C00FC070E /* KeyBackupRecoverCoordinatorType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinatorType.swift; sourceTree = "<group>"; };
|
||||
B19EFA3A21F8BB4100FC070E /* KeyBackupRecoverCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyBackupRecoverCoordinator.swift; sourceTree = "<group>"; };
|
||||
B1A5B33D227ADF2A004CBA85 /* UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImage.swift; sourceTree = "<group>"; };
|
||||
B1B5567920EE6C4C00210D55 /* CountryPickerViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CountryPickerViewController.h; sourceTree = "<group>"; };
|
||||
B1B5567A20EE6C4C00210D55 /* CountryPickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CountryPickerViewController.m; sourceTree = "<group>"; };
|
||||
B1B5567C20EE6C4C00210D55 /* LanguagePickerViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LanguagePickerViewController.m; sourceTree = "<group>"; };
|
||||
|
@ -1133,6 +1146,17 @@
|
|||
B1B5599020EFC5E400210D55 /* Analytics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Analytics.h; sourceTree = "<group>"; };
|
||||
B1B5599120EFC5E400210D55 /* DecryptionFailureTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DecryptionFailureTracker.m; sourceTree = "<group>"; };
|
||||
B1B9194A2118984300FE25B5 /* RoomPredecessorBubbleCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = RoomPredecessorBubbleCell.xib; sourceTree = "<group>"; };
|
||||
B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizer.swift; sourceTree = "<group>"; };
|
||||
B1C562CB228AB3510037F12A /* UIStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackView.swift; sourceTree = "<group>"; };
|
||||
B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuItem.swift; sourceTree = "<group>"; };
|
||||
B1C562DA228C0BB00037F12A /* RoomContextualMenuAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuAction.swift; sourceTree = "<group>"; };
|
||||
B1C562DC228C7C890037F12A /* RoomContextualMenuToolbarView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuToolbarView.swift; sourceTree = "<group>"; };
|
||||
B1C562DD228C7C8A0037F12A /* RoomContextualMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuViewController.swift; sourceTree = "<group>"; };
|
||||
B1C562DE228C7C8B0037F12A /* RoomContextualMenuPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomContextualMenuPresenter.swift; sourceTree = "<group>"; };
|
||||
B1C562DF228C7C8C0037F12A /* RoomContextualMenuToolbarView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RoomContextualMenuToolbarView.xib; sourceTree = "<group>"; };
|
||||
B1C562E0228C7C8C0037F12A /* RoomContextualMenuViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = RoomContextualMenuViewController.storyboard; sourceTree = "<group>"; };
|
||||
B1C562E6228C7CF10037F12A /* ContextualMenuItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContextualMenuItemView.swift; sourceTree = "<group>"; };
|
||||
B1C562E7228C7CF20037F12A /* ContextualMenuItemView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContextualMenuItemView.xib; sourceTree = "<group>"; };
|
||||
B1CA3A2621EF6913000D1D89 /* UIViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIViewController.swift; sourceTree = "<group>"; };
|
||||
B1CA3A2821EF692B000D1D89 /* UIView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIView.swift; sourceTree = "<group>"; };
|
||||
B1CE9EFC22148703000FAE6A /* SignOutAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignOutAlertPresenter.swift; sourceTree = "<group>"; };
|
||||
|
@ -1997,6 +2021,7 @@
|
|||
B1B556A120EE6C4C00210D55 /* Files */,
|
||||
B1B556A420EE6C4C00210D55 /* Members */,
|
||||
B1B5569020EE6C4C00210D55 /* Settings */,
|
||||
B1C562D7228C0B4C0037F12A /* ContextualMenu */,
|
||||
);
|
||||
path = Room;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2977,6 +3002,22 @@
|
|||
path = Analytics;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1C562D7228C0B4C0037F12A /* ContextualMenu */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B1C562DA228C0BB00037F12A /* RoomContextualMenuAction.swift */,
|
||||
B1C562D8228C0B760037F12A /* RoomContextualMenuItem.swift */,
|
||||
B1C562DE228C7C8B0037F12A /* RoomContextualMenuPresenter.swift */,
|
||||
B1C562DD228C7C8A0037F12A /* RoomContextualMenuViewController.swift */,
|
||||
B1C562E0228C7C8C0037F12A /* RoomContextualMenuViewController.storyboard */,
|
||||
B1C562E6228C7CF10037F12A /* ContextualMenuItemView.swift */,
|
||||
B1C562E7228C7CF20037F12A /* ContextualMenuItemView.xib */,
|
||||
B1C562DC228C7C890037F12A /* RoomContextualMenuToolbarView.swift */,
|
||||
B1C562DF228C7C8C0037F12A /* RoomContextualMenuToolbarView.xib */,
|
||||
);
|
||||
path = ContextualMenu;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B1CE9EFB22148681000FAE6A /* SignOut */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -3131,6 +3172,9 @@
|
|||
B109D6F0222D8C400061B6D9 /* UIApplication.swift */,
|
||||
B1DB4F05223015080065DBFA /* Character.swift */,
|
||||
B1DB4F0A223131600065DBFA /* String.swift */,
|
||||
B1A5B33D227ADF2A004CBA85 /* UIImage.swift */,
|
||||
B1C562C92289C2690037F12A /* UIGestureRecognizer.swift */,
|
||||
B1C562CB228AB3510037F12A /* UIStackView.swift */,
|
||||
);
|
||||
path = Categories;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3426,6 +3470,7 @@
|
|||
B1B558EA20EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
B1B558CD20EF768F00210D55 /* RoomOutgoingEncryptedAttachmentWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
B1B9194C2118984300FE25B5 /* RoomPredecessorBubbleCell.xib in Resources */,
|
||||
B1C562E9228C7CF20037F12A /* ContextualMenuItemView.xib in Resources */,
|
||||
B1B5572120EE6C4D00210D55 /* ContactsTableViewController.xib in Resources */,
|
||||
B1B5593A20EF7BAC00210D55 /* TableViewCellWithLabelAndLargeTextView.xib in Resources */,
|
||||
B1B558D820EF768F00210D55 /* RoomIncomingEncryptedAttachmentWithPaginationTitleBubbleCell.xib in Resources */,
|
||||
|
@ -3496,6 +3541,7 @@
|
|||
B1B557D720EF5EA900210D55 /* RoomActivitiesView.xib in Resources */,
|
||||
B1098BF821ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.storyboard in Resources */,
|
||||
F083BDF31E7009ED00A9B29C /* Images.xcassets in Resources */,
|
||||
B1C562E4228C7C8D0037F12A /* RoomContextualMenuToolbarView.xib in Resources */,
|
||||
B1B5590720EF768F00210D55 /* RoomOutgoingTextMsgWithPaginationTitleWithoutSenderNameBubbleCell.xib in Resources */,
|
||||
B169329920F39E6300746532 /* LaunchScreen.storyboard in Resources */,
|
||||
B1B5595320EF9A8700210D55 /* RecentTableViewCell.xib in Resources */,
|
||||
|
@ -3520,6 +3566,7 @@
|
|||
B1B558C020EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.xib in Resources */,
|
||||
B1B5572420EE6C4D00210D55 /* RoomViewController.xib in Resources */,
|
||||
B169331520F3CAFC00746532 /* PublicRoomTableViewCell.xib in Resources */,
|
||||
B1C562E5228C7C8D0037F12A /* RoomContextualMenuViewController.storyboard in Resources */,
|
||||
3232ABA2225730E100AD6A5C /* DeviceVerificationStartViewController.storyboard in Resources */,
|
||||
3284A35120A07C210044F922 /* postMessageAPI.js in Resources */,
|
||||
B1B557A220EF58AD00210D55 /* ContactTableViewCell.xib in Resources */,
|
||||
|
@ -3808,6 +3855,7 @@
|
|||
B1B5572F20EE6C4D00210D55 /* ReadReceiptsViewController.m in Sources */,
|
||||
B1B558CB20EF768F00210D55 /* RoomIncomingEncryptedTextMsgWithoutSenderInfoBubbleCell.m in Sources */,
|
||||
B169330B20F3CA3A00746532 /* Contact.m in Sources */,
|
||||
B1A5B33E227ADF2A004CBA85 /* UIImage.swift in Sources */,
|
||||
B1D4752A21EE52B10067973F /* KeyBackupSetupIntroViewController.swift in Sources */,
|
||||
B1B5599220EFC5E400210D55 /* Analytics.m in Sources */,
|
||||
B14F143422144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyViewAction.swift in Sources */,
|
||||
|
@ -3852,6 +3900,7 @@
|
|||
32891D712264DF7B00C82226 /* DeviceVerificationVerifiedViewController.swift in Sources */,
|
||||
F083BDEF1E7009ED00A9B29C /* UINavigationController+Riot.m in Sources */,
|
||||
B1B5581F20EF625800210D55 /* SimpleRoomTitleView.m in Sources */,
|
||||
B1C562E2228C7C8D0037F12A /* RoomContextualMenuViewController.swift in Sources */,
|
||||
B169330020F3C97D00746532 /* RoomDataSource.m in Sources */,
|
||||
B1B558ED20EF768F00210D55 /* RoomIncomingTextMsgWithoutSenderNameBubbleCell.m in Sources */,
|
||||
B1B5571920EE6C4D00210D55 /* LanguagePickerViewController.m in Sources */,
|
||||
|
@ -3891,6 +3940,7 @@
|
|||
3232ABBA2257BE6500AD6A5C /* DeviceVerificationVerifyViewModel.swift in Sources */,
|
||||
B1098C1021ED07E4000DDA48 /* Presentable.swift in Sources */,
|
||||
B1B558E020EF768F00210D55 /* RoomOutgoingTextMsgBubbleCell.m in Sources */,
|
||||
B1C562E3228C7C8D0037F12A /* RoomContextualMenuPresenter.swift in Sources */,
|
||||
B1B5593C20EF7BAC00210D55 /* TableViewCellWithCheckBoxes.m in Sources */,
|
||||
32891D6B2264CBA300C82226 /* SimpleScreenTemplateViewController.swift in Sources */,
|
||||
B1CA3A2721EF6914000D1D89 /* UIViewController.swift in Sources */,
|
||||
|
@ -3936,6 +3986,8 @@
|
|||
B1B5572020EE6C4D00210D55 /* ContactsTableViewController.m in Sources */,
|
||||
B1B5581920EF625800210D55 /* RoomTitleView.m in Sources */,
|
||||
B1098BE321ECE09F000DDA48 /* RiotDefaults.swift in Sources */,
|
||||
B1C562CA2289C2690037F12A /* UIGestureRecognizer.swift in Sources */,
|
||||
B1C562CC228AB3510037F12A /* UIStackView.swift in Sources */,
|
||||
B1B557BE20EF5B4500210D55 /* RoomInputToolbarView.m in Sources */,
|
||||
B1B5573B20EE6C4D00210D55 /* FavouritesViewController.m in Sources */,
|
||||
B1B5579920EF575B00210D55 /* AuthInputsView.m in Sources */,
|
||||
|
@ -3977,11 +4029,14 @@
|
|||
324A2054225FC571004FE8B0 /* DeviceVerificationIncomingCoordinatorType.swift in Sources */,
|
||||
3232ABB92257BE6500AD6A5C /* DeviceVerificationVerifyViewController.swift in Sources */,
|
||||
B139C21F21FE5D6600BB68EC /* KeyBackupRecoverFromPassphraseViewAction.swift in Sources */,
|
||||
B1C562DB228C0BB00037F12A /* RoomContextualMenuAction.swift in Sources */,
|
||||
B1B5574720EE6C4D00210D55 /* UsersDevicesViewController.m in Sources */,
|
||||
B1098BFF21ECFE65000DDA48 /* PasswordStrengthView.swift in Sources */,
|
||||
B1B558D220EF768F00210D55 /* RoomEncryptedDataBubbleCell.m in Sources */,
|
||||
B1B558FA20EF768F00210D55 /* RoomMembershipBubbleCell.m in Sources */,
|
||||
3232ABA1225730E100AD6A5C /* DeviceVerificationCoordinatorType.swift in Sources */,
|
||||
B1C562D9228C0B760037F12A /* RoomContextualMenuItem.swift in Sources */,
|
||||
B1C562E1228C7C8C0037F12A /* RoomContextualMenuToolbarView.swift in Sources */,
|
||||
B1B557BF20EF5B4500210D55 /* DisabledRoomInputToolbarView.m in Sources */,
|
||||
B1B5578620EF564900210D55 /* GroupTableViewCellWithSwitch.m in Sources */,
|
||||
B1098BE821ECFE52000DDA48 /* Coordinator.swift in Sources */,
|
||||
|
@ -4036,6 +4091,7 @@
|
|||
B1098C0021ECFE65000DDA48 /* KeyBackupSetupPassphraseViewController.swift in Sources */,
|
||||
B1B5591020EF782800210D55 /* TableViewCellWithPhoneNumberTextField.m in Sources */,
|
||||
B1DB4F06223015080065DBFA /* Character.swift in Sources */,
|
||||
B1C562E8228C7CF20037F12A /* ContextualMenuItemView.swift in Sources */,
|
||||
B14F143022144F6500FA0595 /* KeyBackupRecoverFromRecoveryKeyCoordinatorType.swift in Sources */,
|
||||
B1E5368921FB1E20001F3AFF /* UIButton.swift in Sources */,
|
||||
);
|
||||
|
|
|
@ -2555,8 +2555,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
|
|||
// Get modular widget events in rooms histories
|
||||
[[MXKAppSettings standardAppSettings] addSupportedEventTypes:@[kWidgetMatrixEventTypeString, kWidgetModularEventTypeString]];
|
||||
|
||||
// Disable long press on event in bubble cells
|
||||
[MXKRoomBubbleTableViewCell disableLongPressGestureOnEvent:YES];
|
||||
// Enable long press on event in bubble cells
|
||||
[MXKRoomBubbleTableViewCell disableLongPressGestureOnEvent:NO];
|
||||
|
||||
// Set first RoomDataSource class used in Vector
|
||||
[MXKRoomDataSourceManager registerRoomDataSourceClass:RoomDataSource.class];
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
23
Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_copy.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_copy.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_copy@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_copy@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 506 B |
After Width: | Height: | Size: 968 B |
After Width: | Height: | Size: 1.4 KiB |
23
Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_edit.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_edit.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_edit@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_edit@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 871 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.6 KiB |
23
Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_more.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_more.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_more@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_more@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 366 B |
After Width: | Height: | Size: 658 B |
After Width: | Height: | Size: 567 B |
23
Riot/Assets/Images.xcassets/Room/ContextMenu/room_context_menu_reply.imageset/Contents.json
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_reply.png",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_reply@2x.png",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"filename" : "room_context_menu_reply@3x.png",
|
||||
"scale" : "3x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
After Width: | Height: | Size: 476 B |
After Width: | Height: | Size: 920 B |
After Width: | Height: | Size: 1.4 KiB |
|
@ -278,6 +278,8 @@
|
|||
"room_event_action_cancel_send" = "Cancel Send";
|
||||
"room_event_action_cancel_download" = "Cancel Download";
|
||||
"room_event_action_view_encryption" = "Encryption Information";
|
||||
"room_event_action_reply" = "Reply";
|
||||
"room_event_action_edit" = "Edit";
|
||||
"room_warning_about_encryption" = "End-to-end encryption is in beta and may not be reliable.\n\nYou should not yet trust it to secure data.\n\nDevices will not yet be able to decrypt history from before they joined the room.\n\nEncrypted messages will not be visible on clients that do not yet implement encryption.";
|
||||
"room_event_failed_to_send" = "Failed to send";
|
||||
"room_action_send_photo_or_video" = "Send photo or video";
|
||||
|
|
|
@ -51,6 +51,15 @@ extern NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer;
|
|||
*/
|
||||
- (void)selectComponent:(NSUInteger)componentIndex;
|
||||
|
||||
/**
|
||||
Highlight a component in receiver and show or not edit button.
|
||||
|
||||
@param componentIndex index of the component in bubble message data
|
||||
@param showEditButton true to show edit button
|
||||
@param showTimestamp true to show timestamp label
|
||||
*/
|
||||
- (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton showTimestamp:(BOOL)showTimestamp;
|
||||
|
||||
/**
|
||||
Mark a component in receiver.
|
||||
|
||||
|
|
|
@ -137,11 +137,19 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT
|
|||
}
|
||||
|
||||
- (void)selectComponent:(NSUInteger)componentIndex
|
||||
{
|
||||
[self selectComponent:componentIndex showEditButton:YES showTimestamp:YES];
|
||||
}
|
||||
|
||||
- (void)selectComponent:(NSUInteger)componentIndex showEditButton:(BOOL)showEditButton showTimestamp:(BOOL)showTimestamp
|
||||
{
|
||||
if (componentIndex < bubbleData.bubbleComponents.count)
|
||||
{
|
||||
// Add time label
|
||||
[self addTimestampLabelForComponent:componentIndex];
|
||||
if (showTimestamp)
|
||||
{
|
||||
// Add time label
|
||||
[self addTimestampLabelForComponent:componentIndex];
|
||||
}
|
||||
|
||||
// Blur timestamp labels which are not related to the selected component (if any)
|
||||
for (UIView* view in self.bubbleInfoContainer.subviews)
|
||||
|
@ -164,8 +172,11 @@ NSString *const kMXKRoomBubbleCellTapOnReceiptsContainer = @"kMXKRoomBubbleCellT
|
|||
}
|
||||
}
|
||||
|
||||
// Add the edit button
|
||||
[self addEditButtonForComponent:componentIndex completion:nil];
|
||||
if (showEditButton)
|
||||
{
|
||||
// Add the edit button
|
||||
[self addEditButtonForComponent:componentIndex completion:nil];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
28
Riot/Categories/UIGestureRecognizer.swift
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIGestureRecognizer {
|
||||
|
||||
func vc_isTouchingInside(view: UIView? = nil) -> Bool {
|
||||
guard let view = view ?? self.view else {
|
||||
return false
|
||||
}
|
||||
let touchedLocation = self.location(in: view)
|
||||
return view.bounds.contains(touchedLocation)
|
||||
}
|
||||
}
|
51
Riot/Categories/UIImage.swift
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
extension UIImage {
|
||||
|
||||
class func vc_image(from color: UIColor, size: CGSize = CGSize(width: 1, height: 1)) -> UIImage? {
|
||||
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
|
||||
UIGraphicsBeginImageContext(rect.size)
|
||||
let context = UIGraphicsGetCurrentContext()
|
||||
|
||||
context?.setFillColor(color.cgColor)
|
||||
context?.fill(rect)
|
||||
|
||||
var image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
UIGraphicsBeginImageContext(size)
|
||||
image?.draw(in: rect)
|
||||
image = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
func vc_tintedImage(usingColor tintColor: UIColor) -> UIImage? {
|
||||
UIGraphicsBeginImageContextWithOptions(self.size, false, self.scale)
|
||||
let drawRect = CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height)
|
||||
|
||||
self.draw(in: drawRect)
|
||||
tintColor.set()
|
||||
UIRectFillUsingBlendMode(drawRect, .sourceAtop)
|
||||
let tintedImage: UIImage? = UIGraphicsGetImageFromCurrentImageContext()
|
||||
UIGraphicsEndImageContext()
|
||||
return tintedImage
|
||||
}
|
||||
}
|
28
Riot/Categories/UIStackView.swift
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
extension UIStackView {
|
||||
|
||||
func vc_removeAllSubviews() {
|
||||
let subviews = self.arrangedSubviews
|
||||
for subview in subviews {
|
||||
self.removeArrangedSubview(subview)
|
||||
subview.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -75,6 +75,10 @@ internal enum Asset {
|
|||
internal static let scrolldown = ImageAsset(name: "scrolldown")
|
||||
internal static let scrollup = ImageAsset(name: "scrollup")
|
||||
internal static let typing = ImageAsset(name: "typing")
|
||||
internal static let roomContextMenuCopy = ImageAsset(name: "room_context_menu_copy")
|
||||
internal static let roomContextMenuEdit = ImageAsset(name: "room_context_menu_edit")
|
||||
internal static let roomContextMenuMore = ImageAsset(name: "room_context_menu_more")
|
||||
internal static let roomContextMenuReply = ImageAsset(name: "room_context_menu_reply")
|
||||
internal static let uploadIcon = ImageAsset(name: "upload_icon")
|
||||
internal static let voiceCallIcon = ImageAsset(name: "voice_call_icon")
|
||||
internal static let addParticipant = ImageAsset(name: "add_participant")
|
||||
|
|
|
@ -72,6 +72,11 @@ internal enum StoryboardScene {
|
|||
|
||||
internal static let initialScene = InitialSceneType<Riot.KeyBackupSetupSuccessFromRecoveryKeyViewController>(storyboard: KeyBackupSetupSuccessFromRecoveryKeyViewController.self)
|
||||
}
|
||||
internal enum RoomContextualMenuViewController: StoryboardType {
|
||||
internal static let storyboardName = "RoomContextualMenuViewController"
|
||||
|
||||
internal static let initialScene = InitialSceneType<Riot.RoomContextualMenuViewController>(storyboard: RoomContextualMenuViewController.self)
|
||||
}
|
||||
internal enum SimpleScreenTemplateViewController: StoryboardType {
|
||||
internal static let storyboardName = "SimpleScreenTemplateViewController"
|
||||
|
||||
|
|
|
@ -1698,6 +1698,10 @@ internal enum VectorL10n {
|
|||
internal static var roomEventActionDelete: String {
|
||||
return VectorL10n.tr("Vector", "room_event_action_delete")
|
||||
}
|
||||
/// Edit
|
||||
internal static var roomEventActionEdit: String {
|
||||
return VectorL10n.tr("Vector", "room_event_action_edit")
|
||||
}
|
||||
/// Reason for kicking this user
|
||||
internal static var roomEventActionKickPromptReason: String {
|
||||
return VectorL10n.tr("Vector", "room_event_action_kick_prompt_reason")
|
||||
|
@ -1718,6 +1722,10 @@ internal enum VectorL10n {
|
|||
internal static var roomEventActionRedact: String {
|
||||
return VectorL10n.tr("Vector", "room_event_action_redact")
|
||||
}
|
||||
/// Reply
|
||||
internal static var roomEventActionReply: String {
|
||||
return VectorL10n.tr("Vector", "room_event_action_reply")
|
||||
}
|
||||
/// Report content
|
||||
internal static var roomEventActionReport: String {
|
||||
return VectorL10n.tr("Vector", "room_event_action_report")
|
||||
|
|
|
@ -16,6 +16,6 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
protocol Themable: class {
|
||||
@objc protocol Themable: class {
|
||||
func update(theme: Theme)
|
||||
}
|
||||
|
|
|
@ -35,6 +35,10 @@ typedef NS_ENUM(NSInteger, RoomBubbleCellDataTag)
|
|||
*/
|
||||
@property(nonatomic) BOOL containsLastMessage;
|
||||
|
||||
/**
|
||||
Indicate true to display the timestamp of the selected component.
|
||||
*/
|
||||
@property(nonatomic) BOOL showTimestampForSelectedComponent;
|
||||
|
||||
/**
|
||||
The event id of the current selected event inside the bubble. Default is nil.
|
||||
|
|
|
@ -199,7 +199,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
|
|||
}
|
||||
|
||||
// Check whether the timestamp is displayed for this component, and check whether a vertical whitespace is required
|
||||
if ((selectedComponentIndex == index || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName))
|
||||
if (((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName))
|
||||
{
|
||||
currentAttributedTextMsg = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]];
|
||||
[currentAttributedTextMsg appendAttributedString:componentString];
|
||||
|
@ -238,7 +238,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
|
|||
}
|
||||
|
||||
// Check whether the timestamp is displayed
|
||||
if (selectedComponentIndex == index || lastMessageIndex == index)
|
||||
if ((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index)
|
||||
{
|
||||
[currentAttributedTextMsg appendAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]];
|
||||
}
|
||||
|
@ -294,7 +294,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
|
|||
NSInteger lastMessageIndex = self.containsLastMessage ? self.mostRecentComponentIndex : NSNotFound;
|
||||
|
||||
// Check whether the timestamp is displayed for this first component, and check whether a vertical whitespace is required
|
||||
if ((selectedComponentIndex == index || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName))
|
||||
if (((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index) && (self.shouldHideSenderInformation || self.shouldHideSenderName))
|
||||
{
|
||||
attributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]];
|
||||
[attributedString appendAttributedString:component.attributedTextMessage];
|
||||
|
@ -322,7 +322,7 @@ static NSAttributedString *readReceiptVerticalWhitespace = nil;
|
|||
{
|
||||
// Prepare its attributed string by considering potential vertical margin required to display timestamp.
|
||||
NSAttributedString *componentString;
|
||||
if (selectedComponentIndex == index || lastMessageIndex == index)
|
||||
if ((selectedComponentIndex == index && self.showTimestampForSelectedComponent) || lastMessageIndex == index)
|
||||
{
|
||||
NSMutableAttributedString *componentAttributedString = [[NSMutableAttributedString alloc] initWithAttributedString:[RoomBubbleCellData timestampVerticalWhitespace]];
|
||||
[componentAttributedString appendAttributedString:component.attributedTextMessage];
|
||||
|
|
172
Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.swift
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
final class ContextualMenuItemView: UIView, NibOwnerLoadable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum ColorAlpha {
|
||||
static let normal: CGFloat = 1.0
|
||||
static let highlighted: CGFloat = 0.3
|
||||
}
|
||||
|
||||
private enum ViewAlpha {
|
||||
static let normal: CGFloat = 1.0
|
||||
static let disabled: CGFloat = 0.5
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var imageView: UIImageView!
|
||||
@IBOutlet private weak var titleLabel: UILabel!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var originalImage: UIImage?
|
||||
|
||||
private var isHighlighted: Bool = false {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var titleColor: UIColor = .black {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
var imageColor: UIColor = .black {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
var isEnabled: Bool = true {
|
||||
didSet {
|
||||
self.updateView()
|
||||
}
|
||||
}
|
||||
|
||||
var action: (() -> Void)?
|
||||
|
||||
// MARK: Setup
|
||||
|
||||
private func commonInit() {
|
||||
self.setupGestureRecognizer()
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: CGRect.zero)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.loadNibContent()
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.loadNibContent()
|
||||
self.commonInit()
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func fill(title: String, image: UIImage?) {
|
||||
self.originalImage = image?.withRenderingMode(.alwaysTemplate)
|
||||
self.titleLabel.text = title
|
||||
self.updateView()
|
||||
}
|
||||
|
||||
func fill(menuItem: RoomContextualMenuItem) {
|
||||
self.fill(title: menuItem.title, image: menuItem.image)
|
||||
self.action = menuItem.action
|
||||
self.isEnabled = menuItem.isEnabled
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupGestureRecognizer() {
|
||||
let gestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(buttonAction(_:)))
|
||||
gestureRecognizer.minimumPressDuration = 0
|
||||
self.addGestureRecognizer(gestureRecognizer)
|
||||
}
|
||||
|
||||
private func updateView() {
|
||||
|
||||
let viewAlpha = self.isEnabled ? ViewAlpha.normal : ViewAlpha.disabled
|
||||
let colorAlpha = self.isHighlighted ? ColorAlpha.highlighted : ColorAlpha.normal
|
||||
|
||||
self.updateTitleAndImageAlpha(viewAlpha)
|
||||
self.imageView.tintColor = self.imageColor
|
||||
self.updateTitleAndImageColorAlpha(colorAlpha)
|
||||
}
|
||||
|
||||
private func updateTitleAndImageAlpha(_ alpha: CGFloat) {
|
||||
self.imageView.alpha = alpha
|
||||
self.titleLabel.alpha = alpha
|
||||
}
|
||||
|
||||
private func updateTitleAndImageColorAlpha(_ alpha: CGFloat) {
|
||||
let titleColor: UIColor
|
||||
let image: UIImage?
|
||||
|
||||
if alpha < 1.0 {
|
||||
titleColor = self.titleColor.withAlphaComponent(alpha)
|
||||
image = self.originalImage?.vc_tintedImage(usingColor: self.imageColor.withAlphaComponent(alpha))
|
||||
} else {
|
||||
titleColor = self.titleColor
|
||||
image = self.originalImage
|
||||
}
|
||||
|
||||
self.titleLabel.textColor = titleColor
|
||||
self.imageView.image = image
|
||||
}
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
@objc private func buttonAction(_ sender: UILongPressGestureRecognizer) {
|
||||
guard self.isEnabled else {
|
||||
return
|
||||
}
|
||||
|
||||
let isBackgroundViewTouched = sender.vc_isTouchingInside()
|
||||
|
||||
switch sender.state {
|
||||
case .began, .changed:
|
||||
self.isHighlighted = isBackgroundViewTouched
|
||||
case .ended:
|
||||
self.isHighlighted = false
|
||||
|
||||
if isBackgroundViewTouched {
|
||||
self.action?()
|
||||
}
|
||||
case .cancelled:
|
||||
self.isHighlighted = false
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
55
Riot/Modules/Room/ContextualMenu/ContextualMenuItemView.xib
Normal file
|
@ -0,0 +1,55 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="ContextualMenuItemView" customModule="Riot" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="imageView" destination="RcD-qR-Hvt" id="NnI-K5-aTj"/>
|
||||
<outlet property="titleLabel" destination="Wap-UK-AxI" id="AlN-i0-4IN"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="Eav-pf-73a">
|
||||
<rect key="frame" x="0.0" y="0.0" width="64" height="69"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="icon-copy" translatesAutoresizingMaskIntoConstraints="NO" id="RcD-qR-Hvt">
|
||||
<rect key="frame" x="21.5" y="8" width="21" height="29"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" secondItem="RcD-qR-Hvt" secondAttribute="height" multiplier="21:29" id="V50-97-yzO"/>
|
||||
<constraint firstAttribute="height" constant="29" id="tBN-Ex-KO4"/>
|
||||
</constraints>
|
||||
</imageView>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Copy" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Wap-UK-AxI">
|
||||
<rect key="frame" x="5" y="39" width="54" height="25"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<color key="textColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="RcD-qR-Hvt" firstAttribute="centerX" secondItem="Eav-pf-73a" secondAttribute="centerX" id="6sB-0G-LfN"/>
|
||||
<constraint firstItem="Wap-UK-AxI" firstAttribute="leading" secondItem="Eav-pf-73a" secondAttribute="leading" constant="5" id="989-CC-w7E"/>
|
||||
<constraint firstItem="RcD-qR-Hvt" firstAttribute="top" secondItem="Eav-pf-73a" secondAttribute="top" constant="8" id="hos-Pl-Og0"/>
|
||||
<constraint firstItem="Wap-UK-AxI" firstAttribute="top" secondItem="RcD-qR-Hvt" secondAttribute="bottom" constant="2" id="iQ8-gd-onK"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Wap-UK-AxI" secondAttribute="trailing" constant="5" id="tjm-F0-G8m"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Wap-UK-AxI" secondAttribute="bottom" constant="5" id="y2C-S8-hdF"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<point key="canvasLocation" x="-121.73913043478262" y="-31.138392857142854"/>
|
||||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="icon-copy" width="24" height="24"/>
|
||||
</resources>
|
||||
</document>
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc enum RoomContextualMenuAction: Int {
|
||||
case copy
|
||||
case reply
|
||||
case edit
|
||||
case more
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
var title: String {
|
||||
let title: String
|
||||
|
||||
switch self {
|
||||
case .copy:
|
||||
title = VectorL10n.roomEventActionCopy
|
||||
case .reply:
|
||||
title = VectorL10n.roomEventActionReply
|
||||
case .edit:
|
||||
title = VectorL10n.roomEventActionEdit
|
||||
case .more:
|
||||
title = VectorL10n.roomEventActionMore
|
||||
}
|
||||
|
||||
return title
|
||||
}
|
||||
|
||||
var image: UIImage? {
|
||||
let image: UIImage?
|
||||
|
||||
switch self {
|
||||
case .copy:
|
||||
image = Asset.Images.roomContextMenuCopy.image
|
||||
case .reply:
|
||||
image = Asset.Images.roomContextMenuReply.image
|
||||
case .edit:
|
||||
image = Asset.Images.roomContextMenuEdit.image
|
||||
case .more:
|
||||
image = Asset.Images.roomContextMenuMore.image
|
||||
default:
|
||||
image = nil
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers
|
||||
final class RoomContextualMenuItem: NSObject {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
let title: String
|
||||
let image: UIImage?
|
||||
|
||||
var isEnabled: Bool = true
|
||||
var action: (() -> Void)?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
init(menuAction: RoomContextualMenuAction) {
|
||||
self.title = menuAction.title
|
||||
self.image = menuAction.image
|
||||
super.init()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
|
||||
@objcMembers
|
||||
final class RoomContextualMenuPresenter: NSObject {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let animationDuration: TimeInterval = 0.3
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private weak var roomContextualMenuViewController: RoomContextualMenuViewController?
|
||||
|
||||
// MARK: Public
|
||||
|
||||
var isPresenting: Bool {
|
||||
return self.roomContextualMenuViewController != nil
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func present(roomContextualMenuViewController: RoomContextualMenuViewController,
|
||||
from viewController: UIViewController,
|
||||
on view: UIView,
|
||||
animated: Bool,
|
||||
completion: (() -> Void)?) {
|
||||
guard self.roomContextualMenuViewController == nil else {
|
||||
return
|
||||
}
|
||||
|
||||
roomContextualMenuViewController.view.alpha = 0
|
||||
|
||||
viewController.vc_addChildViewController(viewController: roomContextualMenuViewController, onView: view)
|
||||
|
||||
self.roomContextualMenuViewController = roomContextualMenuViewController
|
||||
|
||||
roomContextualMenuViewController.hideMenuToolbar()
|
||||
roomContextualMenuViewController.view.layoutIfNeeded()
|
||||
|
||||
let animationInstructions: (() -> Void) = {
|
||||
roomContextualMenuViewController.showMenuToolbar()
|
||||
roomContextualMenuViewController.view.alpha = 1
|
||||
roomContextualMenuViewController.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
if animated {
|
||||
UIView.animate(withDuration: Constants.animationDuration, animations: {
|
||||
animationInstructions()
|
||||
}, completion: { completed in
|
||||
completion?()
|
||||
})
|
||||
} else {
|
||||
animationInstructions()
|
||||
completion?()
|
||||
}
|
||||
}
|
||||
|
||||
func hideContextualMenu(animated: Bool, completion: (() -> Void)?) {
|
||||
guard let roomContextualMenuViewController = self.roomContextualMenuViewController else {
|
||||
return
|
||||
}
|
||||
|
||||
let animationInstructions: (() -> Void) = {
|
||||
roomContextualMenuViewController.hideMenuToolbar()
|
||||
roomContextualMenuViewController.view.alpha = 0
|
||||
roomContextualMenuViewController.view.layoutIfNeeded()
|
||||
}
|
||||
|
||||
let animationCompletionInstructions: (() -> Void) = {
|
||||
roomContextualMenuViewController.vc_removeFromParent()
|
||||
completion?()
|
||||
}
|
||||
|
||||
if animated {
|
||||
UIView.animate(withDuration: Constants.animationDuration, animations: {
|
||||
animationInstructions()
|
||||
}, completion: { completed in
|
||||
animationCompletionInstructions()
|
||||
})
|
||||
} else {
|
||||
animationInstructions()
|
||||
animationCompletionInstructions()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
import Reusable
|
||||
|
||||
final class RoomContextualMenuToolbarView: MXKRoomInputToolbarView, NibOwnerLoadable, Themable {
|
||||
|
||||
// MARK: - Constants
|
||||
|
||||
private enum Constants {
|
||||
static let menuItemMinWidth: CGFloat = 50.0
|
||||
static let menuItemMaxWidth: CGFloat = 80.0
|
||||
}
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var menuItemsStackView: UIStackView!
|
||||
@IBOutlet private weak var separatorView: UIView!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme?
|
||||
private var menuItemViews: [ContextualMenuItemView] = []
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
@objc func update(theme: Theme) {
|
||||
self.theme = theme
|
||||
self.backgroundColor = theme.backgroundColor
|
||||
self.tintColor = theme.tintColor
|
||||
self.separatorView.backgroundColor = theme.lineBreakColor
|
||||
|
||||
for menuItemView in self.menuItemViews {
|
||||
menuItemView.titleColor = theme.textPrimaryColor
|
||||
menuItemView.imageColor = theme.tintColor
|
||||
}
|
||||
}
|
||||
|
||||
@objc func fill(contextualMenuItems: [RoomContextualMenuItem]) {
|
||||
self.menuItemsStackView.vc_removeAllSubviews()
|
||||
self.menuItemViews.removeAll()
|
||||
|
||||
for menuItem in contextualMenuItems {
|
||||
let menuItemView = ContextualMenuItemView()
|
||||
menuItemView.fill(menuItem: menuItem)
|
||||
|
||||
if let theme = theme {
|
||||
menuItemView.titleColor = theme.textPrimaryColor
|
||||
menuItemView.imageColor = theme.tintColor
|
||||
}
|
||||
|
||||
self.add(menuItemView: menuItemView)
|
||||
}
|
||||
|
||||
self.layoutIfNeeded()
|
||||
}
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
private func commonInit() {
|
||||
}
|
||||
|
||||
convenience init() {
|
||||
self.init(frame: CGRect.zero)
|
||||
self.loadNibContent()
|
||||
commonInit()
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
super.init(coder: aDecoder)
|
||||
self.loadNibContent()
|
||||
commonInit()
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
self.loadNibContent()
|
||||
commonInit()
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func awakeFromNib() {
|
||||
super.awakeFromNib()
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func add(menuItemView: ContextualMenuItemView) {
|
||||
let menuItemContentView = UIView()
|
||||
menuItemContentView.backgroundColor = .clear
|
||||
|
||||
self.add(menuItemView: menuItemView, on: menuItemContentView)
|
||||
|
||||
self.menuItemsStackView.addArrangedSubview(menuItemContentView)
|
||||
|
||||
let widthConstraint = menuItemContentView.widthAnchor.constraint(equalTo: self.menuItemsStackView.widthAnchor)
|
||||
widthConstraint.priority = .defaultLow
|
||||
widthConstraint.isActive = true
|
||||
|
||||
self.menuItemViews.append(menuItemView)
|
||||
}
|
||||
|
||||
private func add(menuItemView: ContextualMenuItemView, on contentView: UIView) {
|
||||
contentView.translatesAutoresizingMaskIntoConstraints = false
|
||||
menuItemView.translatesAutoresizingMaskIntoConstraints = false
|
||||
|
||||
contentView.addSubview(menuItemView)
|
||||
|
||||
menuItemView.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
|
||||
menuItemView.centerYAnchor.constraint(equalTo: contentView.centerYAnchor).isActive = true
|
||||
|
||||
let widthConstraint = menuItemView.widthAnchor.constraint(equalToConstant: 0.0)
|
||||
widthConstraint.priority = .defaultLow
|
||||
widthConstraint.isActive = true
|
||||
|
||||
let minWidthConstraint = menuItemView.widthAnchor.constraint(greaterThanOrEqualToConstant: Constants.menuItemMinWidth)
|
||||
minWidthConstraint.priority = .required
|
||||
minWidthConstraint.isActive = true
|
||||
|
||||
let maxWidthConstraint = menuItemView.widthAnchor.constraint(lessThanOrEqualToConstant: Constants.menuItemMaxWidth)
|
||||
maxWidthConstraint.priority = .required
|
||||
maxWidthConstraint.isActive = true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="RoomContextualMenuToolbarView" customModule="Riot" customModuleProvider="target">
|
||||
<connections>
|
||||
<outlet property="menuItemsStackView" destination="ayT-FO-8xC" id="v7N-rd-lEb"/>
|
||||
<outlet property="separatorView" destination="dzn-dX-0at" id="KVI-n6-pkG"/>
|
||||
</connections>
|
||||
</placeholder>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
|
||||
<view contentMode="scaleToFill" id="gik-f6-38I">
|
||||
<rect key="frame" x="0.0" y="0.0" width="470" height="63"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="dzn-dX-0at" userLabel="Separator View">
|
||||
<rect key="frame" x="10" y="0.0" width="450" height="1"/>
|
||||
<color key="backgroundColor" red="0.93725490199999995" green="0.93725490199999995" blue="0.95686274510000002" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="1" id="Ktg-wG-Xuk"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="ayT-FO-8xC">
|
||||
<rect key="frame" x="0.0" y="1" width="470" height="62"/>
|
||||
</stackView>
|
||||
<toolbar opaque="NO" clearsContextBeforeDrawing="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="kgj-Hq-tEB">
|
||||
<rect key="frame" x="0.0" y="1" width="414" height="52"/>
|
||||
<items>
|
||||
<barButtonItem title="Copy" id="P1Q-AZ-Qem" userLabel="Copy"/>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="bzo-GF-Clo"/>
|
||||
<barButtonItem title="Reply" id="pW0-ss-bsI"/>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="bbf-N0-Zis"/>
|
||||
<barButtonItem title="Edit" id="X3Q-SB-6e1"/>
|
||||
<barButtonItem style="plain" systemItem="flexibleSpace" id="2D4-jO-fyW"/>
|
||||
<barButtonItem title="More" id="skA-D0-EBc"/>
|
||||
</items>
|
||||
</toolbar>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstItem="dzn-dX-0at" firstAttribute="leading" secondItem="gik-f6-38I" secondAttribute="leading" constant="10" id="0qV-0P-IyR"/>
|
||||
<constraint firstItem="kgj-Hq-tEB" firstAttribute="leading" secondItem="gik-f6-38I" secondAttribute="leading" id="14D-JP-KDd"/>
|
||||
<constraint firstAttribute="bottom" secondItem="ayT-FO-8xC" secondAttribute="bottom" id="CtJ-Y4-5vr"/>
|
||||
<constraint firstAttribute="bottom" secondItem="kgj-Hq-tEB" secondAttribute="bottom" constant="1" id="RpB-Ka-WmR"/>
|
||||
<constraint firstItem="kgj-Hq-tEB" firstAttribute="top" secondItem="dzn-dX-0at" secondAttribute="bottom" id="SSI-IW-4eG"/>
|
||||
<constraint firstItem="ayT-FO-8xC" firstAttribute="top" secondItem="dzn-dX-0at" secondAttribute="bottom" id="Stq-dM-Psw"/>
|
||||
<constraint firstAttribute="trailing" secondItem="ayT-FO-8xC" secondAttribute="trailing" id="cPG-PI-jhM"/>
|
||||
<constraint firstItem="dzn-dX-0at" firstAttribute="top" secondItem="gik-f6-38I" secondAttribute="top" id="dFk-HG-k1l"/>
|
||||
<constraint firstAttribute="trailing" secondItem="dzn-dX-0at" secondAttribute="trailing" constant="10" id="uxF-0x-HFI"/>
|
||||
<constraint firstItem="ayT-FO-8xC" firstAttribute="leading" secondItem="gik-f6-38I" secondAttribute="leading" id="yDp-d9-du7"/>
|
||||
<constraint firstAttribute="trailing" secondItem="kgj-Hq-tEB" secondAttribute="trailing" id="yrf-JI-gvp"/>
|
||||
</constraints>
|
||||
<nil key="simulatedTopBarMetrics"/>
|
||||
<nil key="simulatedBottomBarMetrics"/>
|
||||
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
|
||||
<variation key="default">
|
||||
<mask key="subviews">
|
||||
<exclude reference="kgj-Hq-tEB"/>
|
||||
</mask>
|
||||
</variation>
|
||||
<point key="canvasLocation" x="-1457.9710144927537" y="-643.19196428571422"/>
|
||||
</view>
|
||||
</objects>
|
||||
</document>
|
|
@ -0,0 +1,57 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="2eW-Ga-w3t">
|
||||
<device id="retina6_1" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--Room Contextual Menu View Controller-->
|
||||
<scene sceneID="I8V-hb-Jea">
|
||||
<objects>
|
||||
<viewController id="2eW-Ga-w3t" customClass="RoomContextualMenuViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="X0o-r8-auN">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Szx-Dr-Ndt">
|
||||
<rect key="frame" x="0.0" y="0.0" width="414" height="793"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="0GC-JU-rI3" customClass="RoomContextualMenuToolbarView" customModule="Riot" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="793" width="414" height="69"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="69" id="ynL-KP-iB4"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="Szx-Dr-Ndt" secondAttribute="trailing" id="2eB-6O-P3h"/>
|
||||
<constraint firstItem="Szx-Dr-Ndt" firstAttribute="leading" secondItem="X0o-r8-auN" secondAttribute="leading" id="4qK-G6-nr9"/>
|
||||
<constraint firstItem="Szx-Dr-Ndt" firstAttribute="top" secondItem="X0o-r8-auN" secondAttribute="top" id="GVa-P9-DcG"/>
|
||||
<constraint firstItem="0GC-JU-rI3" firstAttribute="leading" secondItem="X0o-r8-auN" secondAttribute="leading" id="TZJ-nm-Ppz"/>
|
||||
<constraint firstItem="0GC-JU-rI3" firstAttribute="top" secondItem="Szx-Dr-Ndt" secondAttribute="bottom" id="Wyl-wh-kh4"/>
|
||||
<constraint firstAttribute="trailing" secondItem="0GC-JU-rI3" secondAttribute="trailing" id="lzM-FD-x89"/>
|
||||
<constraint firstItem="225-y0-Elg" firstAttribute="bottom" secondItem="0GC-JU-rI3" secondAttribute="bottom" id="s4i-80-0iu"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="225-y0-Elg"/>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="backgroundOverlayView" destination="Szx-Dr-Ndt" id="Whj-e5-bas"/>
|
||||
<outlet property="menuToolbarView" destination="0GC-JU-rI3" id="j0z-I8-Pcr"/>
|
||||
<outlet property="menuToolbarViewBottomConstraint" destination="s4i-80-0iu" id="E5w-5m-m5O"/>
|
||||
<outlet property="menuToolbarViewHeightConstraint" destination="ynL-KP-iB4" id="Zeb-b0-Yil"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="8NV-wl-Hp0" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53.623188405797109" y="135.9375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
Copyright 2019 New Vector Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import UIKit
|
||||
|
||||
@objc protocol RoomContextualMenuViewControllerDelegate: class {
|
||||
func roomContextualMenuViewControllerDidTapBackgroundOverlay(_ viewController: RoomContextualMenuViewController)
|
||||
}
|
||||
|
||||
@objcMembers
|
||||
final class RoomContextualMenuViewController: UIViewController, Themable {
|
||||
|
||||
// MARK: - Properties
|
||||
|
||||
// MARK: Outlets
|
||||
|
||||
@IBOutlet private weak var backgroundOverlayView: UIView!
|
||||
@IBOutlet private weak var menuToolbarView: RoomContextualMenuToolbarView!
|
||||
|
||||
@IBOutlet private weak var menuToolbarViewHeightConstraint: NSLayoutConstraint!
|
||||
@IBOutlet private weak var menuToolbarViewBottomConstraint: NSLayoutConstraint!
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private var theme: Theme!
|
||||
private var contextualMenuItems: [RoomContextualMenuItem] = []
|
||||
|
||||
private var hiddenToolbarViewBottomConstant: CGFloat {
|
||||
let bottomSafeAreaHeight: CGFloat
|
||||
|
||||
if #available(iOS 11.0, *) {
|
||||
bottomSafeAreaHeight = self.view.safeAreaInsets.bottom
|
||||
} else {
|
||||
bottomSafeAreaHeight = self.bottomLayoutGuide.length
|
||||
}
|
||||
|
||||
return -(self.menuToolbarViewHeightConstraint.constant + bottomSafeAreaHeight)
|
||||
}
|
||||
|
||||
// MARK: Public
|
||||
|
||||
weak var delegate: RoomContextualMenuViewControllerDelegate?
|
||||
|
||||
// MARK: - Setup
|
||||
|
||||
class func instantiate(with contextualMenuItems: [RoomContextualMenuItem]) -> RoomContextualMenuViewController {
|
||||
let viewController = StoryboardScene.RoomContextualMenuViewController.initialScene.instantiate()
|
||||
viewController.theme = ThemeService.shared().theme
|
||||
viewController.contextualMenuItems = contextualMenuItems
|
||||
return viewController
|
||||
}
|
||||
|
||||
// MARK: - Life cycle
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
|
||||
// Do any additional setup after loading the view.
|
||||
|
||||
self.backgroundOverlayView.isUserInteractionEnabled = true
|
||||
self.menuToolbarView.fill(contextualMenuItems: self.contextualMenuItems)
|
||||
self.setupBackgroundOverlayTapGestureRecognizer()
|
||||
|
||||
self.registerThemeServiceDidChangeThemeNotification()
|
||||
self.update(theme: self.theme)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
func showMenuToolbar() {
|
||||
self.menuToolbarViewBottomConstraint.constant = 0
|
||||
}
|
||||
|
||||
func hideMenuToolbar() {
|
||||
self.menuToolbarViewBottomConstraint.constant = self.hiddenToolbarViewBottomConstant
|
||||
}
|
||||
|
||||
func update(theme: Theme) {
|
||||
self.menuToolbarView.update(theme: theme)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func setupBackgroundOverlayTapGestureRecognizer() {
|
||||
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(gestureRecognizer:)))
|
||||
self.backgroundOverlayView.addGestureRecognizer(tapGestureRecognizer)
|
||||
}
|
||||
|
||||
@objc private func handleTap(gestureRecognizer: UIGestureRecognizer) {
|
||||
self.delegate?.roomContextualMenuViewControllerDidTapBackgroundOverlay(self)
|
||||
}
|
||||
|
||||
private func registerThemeServiceDidChangeThemeNotification() {
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(themeDidChange), name: .themeServiceDidChangeTheme, object: nil)
|
||||
}
|
||||
|
||||
@objc private func themeDidChange() {
|
||||
self.update(theme: ThemeService.shared().theme)
|
||||
}
|
||||
}
|
|
@ -34,6 +34,11 @@
|
|||
*/
|
||||
@property(nonatomic) BOOL markTimelineInitialEvent;
|
||||
|
||||
/**
|
||||
Tell whether timestamp should be displayed on event selection. Default is YES.
|
||||
*/
|
||||
@property(nonatomic) BOOL showBubbleDateTimeOnSelection;
|
||||
|
||||
/**
|
||||
Check if there is an active jitsi widget in the room and return it.
|
||||
|
||||
|
|
|
@ -58,6 +58,8 @@
|
|||
|
||||
self.markTimelineInitialEvent = NO;
|
||||
|
||||
self.showBubbleDateTimeOnSelection = YES;
|
||||
|
||||
// Observe user interface theme change.
|
||||
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
|
@ -445,7 +447,7 @@
|
|||
NSInteger selectedComponentIndex = cellData.selectedComponentIndex;
|
||||
if (selectedComponentIndex != NSNotFound)
|
||||
{
|
||||
[bubbleCell selectComponent:cellData.selectedComponentIndex];
|
||||
[bubbleCell selectComponent:cellData.selectedComponentIndex showEditButton:NO showTimestamp:cellData.showTimestampForSelectedComponent];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -492,11 +494,14 @@
|
|||
{
|
||||
RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:_selectedEventId];
|
||||
cellData.selectedEventId = nil;
|
||||
cellData.showTimestampForSelectedComponent = NO;
|
||||
}
|
||||
|
||||
if (selectedEventId.length)
|
||||
{
|
||||
RoomBubbleCellData *cellData = [self cellDataOfEventWithEventId:selectedEventId];
|
||||
|
||||
cellData.showTimestampForSelectedComponent = self.showBubbleDateTimeOnSelection;
|
||||
|
||||
if (cellData.collapsed && cellData.nextCollapsableCellData)
|
||||
{
|
||||
|
|
|
@ -123,7 +123,7 @@
|
|||
|
||||
#import "Riot-Swift.h"
|
||||
|
||||
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate>
|
||||
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate, RoomContextualMenuViewControllerDelegate>
|
||||
{
|
||||
// The expanded header
|
||||
ExpandedRoomTitleView *expandedHeader;
|
||||
|
@ -213,6 +213,10 @@
|
|||
MXServerNotices *serverNotices;
|
||||
}
|
||||
|
||||
@property (nonatomic, weak) IBOutlet UIView *overlayContainerView;
|
||||
|
||||
@property (nonatomic, strong) RoomContextualMenuPresenter *roomContextualMenuPresenter;
|
||||
|
||||
@end
|
||||
|
||||
@implementation RoomViewController
|
||||
|
@ -404,6 +408,8 @@
|
|||
[self refreshRoomInputToolbar];
|
||||
}
|
||||
|
||||
self.roomContextualMenuPresenter = [RoomContextualMenuPresenter new];
|
||||
|
||||
// Observe user interface theme change.
|
||||
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
|
||||
|
||||
|
@ -589,6 +595,9 @@
|
|||
{
|
||||
[super viewDidDisappear:animated];
|
||||
|
||||
// Hide contextual menu if needed
|
||||
[self hideContextualMenuAnimated:NO];
|
||||
|
||||
// Reset visible room id
|
||||
[AppDelegate theDelegate].visibleRoomId = nil;
|
||||
|
||||
|
@ -936,6 +945,8 @@
|
|||
- (void)updateRoomInputToolbarViewClassIfNeeded
|
||||
{
|
||||
Class roomInputToolbarViewClass = RoomInputToolbarView.class;
|
||||
|
||||
BOOL shouldDismissContextualMenu = NO;
|
||||
|
||||
// Check the user has enough power to post message
|
||||
if (self.roomDataSource.roomState)
|
||||
|
@ -950,10 +961,12 @@
|
|||
if (isRoomObsolete || isResourceLimitExceeded)
|
||||
{
|
||||
roomInputToolbarViewClass = nil;
|
||||
shouldDismissContextualMenu = YES;
|
||||
}
|
||||
else if (!canSend)
|
||||
{
|
||||
roomInputToolbarViewClass = DisabledRoomInputToolbarView.class;
|
||||
shouldDismissContextualMenu = YES;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -961,6 +974,12 @@
|
|||
if (self.isRoomPreview)
|
||||
{
|
||||
roomInputToolbarViewClass = nil;
|
||||
shouldDismissContextualMenu = YES;
|
||||
}
|
||||
|
||||
if (shouldDismissContextualMenu)
|
||||
{
|
||||
[self hideContextualMenuAnimated:NO];
|
||||
}
|
||||
|
||||
// Change inputToolbarView class only if given class is different from current one
|
||||
|
@ -978,7 +997,7 @@
|
|||
|
||||
if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class])
|
||||
{
|
||||
height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant;
|
||||
height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarHeightConstraint.constant;
|
||||
}
|
||||
else if ([self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class])
|
||||
{
|
||||
|
@ -1485,6 +1504,14 @@
|
|||
[UIView setAnimationsEnabled:YES];
|
||||
}
|
||||
|
||||
- (void)handleLongPressFromCell:(id<MXKCellRendering>)cell withTappedEvent:(MXEvent*)event
|
||||
{
|
||||
if (event && !customizedRoomDataSource.selectedEventId)
|
||||
{
|
||||
[self showContextualMenuForEvent:event cell:cell animated:YES];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Hide/Show expanded header
|
||||
|
||||
- (void)showExpandedHeader:(BOOL)isVisible
|
||||
|
@ -1552,6 +1579,9 @@
|
|||
mainNavigationController.navigationBar.translucent = isVisible;
|
||||
self.navigationController.navigationBar.translucent = isVisible;
|
||||
|
||||
// Hide contextual menu if needed
|
||||
[self hideContextualMenuAnimated:YES];
|
||||
|
||||
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
|
||||
animations:^{
|
||||
|
||||
|
@ -2030,9 +2060,6 @@
|
|||
[self selectEventWithId:tappedEvent.eventId];
|
||||
}
|
||||
}
|
||||
|
||||
// Force table refresh
|
||||
[self dataSource:self.roomDataSource didCellChange:nil];
|
||||
}
|
||||
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnOverlayContainer])
|
||||
{
|
||||
|
@ -2073,9 +2100,6 @@
|
|||
// Highlight this event in displayed message
|
||||
[self selectEventWithId:((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.eventId];
|
||||
}
|
||||
|
||||
// Force table refresh
|
||||
[self dataSource:self.roomDataSource didCellChange:nil];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2105,6 +2129,11 @@
|
|||
|
||||
[self.roomDataSource collapseRoomBubble:((MXKRoomBubbleTableViewCell*)cell).bubbleData collapsed:YES];
|
||||
}
|
||||
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnEvent])
|
||||
{
|
||||
MXEvent *tappedEvent = userInfo[kMXKRoomBubbleCellEventKey];
|
||||
[self handleLongPressFromCell:cell withTappedEvent:tappedEvent];
|
||||
}
|
||||
else
|
||||
{
|
||||
// Keep default implementation for other actions
|
||||
|
@ -2213,24 +2242,6 @@
|
|||
}]];
|
||||
}
|
||||
}
|
||||
|
||||
if (level == 0)
|
||||
{
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_copy", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
[self cancelEventSelection];
|
||||
|
||||
[[UIPasteboard generalPasteboard] setString:selectedComponent.textMessage];
|
||||
}
|
||||
|
||||
}]];
|
||||
}
|
||||
|
||||
if (level == 0)
|
||||
{
|
||||
|
@ -2323,42 +2334,6 @@
|
|||
}]];
|
||||
}
|
||||
|
||||
if (attachment.type != MXKAttachmentTypeSticker)
|
||||
{
|
||||
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_copy", @"Vector", nil)
|
||||
style:UIAlertActionStyleDefault
|
||||
handler:^(UIAlertAction * action) {
|
||||
|
||||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
|
||||
[self cancelEventSelection];
|
||||
|
||||
[self startActivityIndicator];
|
||||
|
||||
[attachment copy:^{
|
||||
|
||||
__strong __typeof(weakSelf)self = weakSelf;
|
||||
[self stopActivityIndicator];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
__strong __typeof(weakSelf)self = weakSelf;
|
||||
[self stopActivityIndicator];
|
||||
|
||||
//Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
|
||||
}];
|
||||
|
||||
// Start animation in case of download during attachment preparing
|
||||
[roomBubbleTableViewCell startProgressUI];
|
||||
}
|
||||
|
||||
}]];
|
||||
}
|
||||
|
||||
// Check status of the selected event
|
||||
if (selectedEvent.sentState == MXEventSentStatePreparing ||
|
||||
selectedEvent.sentState == MXEventSentStateEncrypting ||
|
||||
|
@ -2733,7 +2708,7 @@
|
|||
if (weakSelf)
|
||||
{
|
||||
typeof(self) self = weakSelf;
|
||||
[self cancelEventSelection];
|
||||
[self hideContextualMenuAnimated:YES];
|
||||
}
|
||||
|
||||
}]];
|
||||
|
@ -2843,10 +2818,8 @@
|
|||
else if (url && urlItemInteractionValue)
|
||||
{
|
||||
// Fallback case for external links
|
||||
|
||||
// TODO: Use UITextItemInteraction enum when minimum deployement target will be iOS 10
|
||||
switch (urlItemInteractionValue.integerValue) {
|
||||
case 0: //UITextItemInteractionInvokeDefaultAction
|
||||
case UITextItemInteractionInvokeDefaultAction:
|
||||
{
|
||||
[[UIApplication sharedApplication] vc_open:url completionHandler:^(BOOL success) {
|
||||
if (!success)
|
||||
|
@ -2857,10 +2830,13 @@
|
|||
shouldDoAction = NO;
|
||||
}
|
||||
break;
|
||||
case 1: //UITextItemInteractionPresentActions
|
||||
// Long press on link, let MXKRoomBubbleTableViewCell UITextView present the default contextual menu.
|
||||
case UITextItemInteractionPresentActions:
|
||||
{
|
||||
// Long press on link, present room contextual menu.
|
||||
shouldDoAction = NO;
|
||||
}
|
||||
break;
|
||||
case 2: //UITextItemInteractionPreview
|
||||
case UITextItemInteractionPreview:
|
||||
// Force touch on link, let MXKRoomBubbleTableViewCell UITextView use default peek and pop behavior.
|
||||
break;
|
||||
default:
|
||||
|
@ -2878,11 +2854,18 @@
|
|||
|
||||
- (void)selectEventWithId:(NSString*)eventId
|
||||
{
|
||||
BOOL shouldEnableReplyMode = [self.roomDataSource canReplyToEventWithId:eventId];
|
||||
|
||||
[self setInputToolBarSendMode: shouldEnableReplyMode ? RoomInputToolbarViewSendModeReply : RoomInputToolbarViewSendModeSend];
|
||||
[self selectEventWithId:eventId enableReplyMode:NO showTimestamp:YES];
|
||||
}
|
||||
|
||||
- (void)selectEventWithId:(NSString*)eventId enableReplyMode:(BOOL)enableReplyMode showTimestamp:(BOOL)showTimestamp
|
||||
{
|
||||
[self setInputToolBarSendMode: enableReplyMode ? RoomInputToolbarViewSendModeReply : RoomInputToolbarViewSendModeSend];
|
||||
|
||||
customizedRoomDataSource.showBubbleDateTimeOnSelection = showTimestamp;
|
||||
customizedRoomDataSource.selectedEventId = eventId;
|
||||
|
||||
// Force table refresh
|
||||
[self dataSource:self.roomDataSource didCellChange:nil];
|
||||
}
|
||||
|
||||
- (void)cancelEventSelection
|
||||
|
@ -2895,6 +2878,7 @@
|
|||
currentAlert = nil;
|
||||
}
|
||||
|
||||
customizedRoomDataSource.showBubbleDateTimeOnSelection = YES;
|
||||
customizedRoomDataSource.selectedEventId = nil;
|
||||
|
||||
// Force table refresh
|
||||
|
@ -4976,5 +4960,169 @@
|
|||
}
|
||||
}
|
||||
|
||||
#pragma mark - Contextual Menu
|
||||
|
||||
- (NSArray<RoomContextualMenuItem*>*)contextualMenuItemsForEvent:(MXEvent*)event andCell:(id<MXKCellRendering>)cell
|
||||
{
|
||||
NSString *eventId = event.eventId;
|
||||
MXKRoomBubbleTableViewCell *roomBubbleTableViewCell = (MXKRoomBubbleTableViewCell *)cell;
|
||||
MXKAttachment *attachment = roomBubbleTableViewCell.bubbleData.attachment;
|
||||
|
||||
MXWeakify(self);
|
||||
|
||||
// Copy action
|
||||
|
||||
RoomContextualMenuItem *copyMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionCopy];
|
||||
copyMenuItem.isEnabled = !attachment || attachment.type != MXKAttachmentTypeSticker;
|
||||
copyMenuItem.action = ^{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
if (!attachment)
|
||||
{
|
||||
NSArray *components = roomBubbleTableViewCell.bubbleData.bubbleComponents;
|
||||
MXKRoomBubbleComponent *selectedComponent;
|
||||
for (selectedComponent in components)
|
||||
{
|
||||
if ([selectedComponent.event.eventId isEqualToString:event.eventId])
|
||||
{
|
||||
break;
|
||||
}
|
||||
selectedComponent = nil;
|
||||
}
|
||||
NSString *textMessage = selectedComponent.textMessage;
|
||||
|
||||
[UIPasteboard generalPasteboard].string = textMessage;
|
||||
|
||||
[self hideContextualMenuAnimated:YES];
|
||||
}
|
||||
else if (attachment.type != MXKAttachmentTypeSticker)
|
||||
{
|
||||
[self hideContextualMenuAnimated:YES completion:^{
|
||||
[self startActivityIndicator];
|
||||
|
||||
[attachment copy:^{
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
} failure:^(NSError *error) {
|
||||
|
||||
[self stopActivityIndicator];
|
||||
|
||||
//Alert user
|
||||
[[AppDelegate theDelegate] showErrorAsAlert:error];
|
||||
}];
|
||||
|
||||
// Start animation in case of download during attachment preparing
|
||||
[roomBubbleTableViewCell startProgressUI];
|
||||
}];
|
||||
}
|
||||
};
|
||||
|
||||
// Reply action
|
||||
|
||||
RoomContextualMenuItem *replyMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionReply];
|
||||
replyMenuItem.isEnabled = [self.roomDataSource canReplyToEventWithId:eventId];
|
||||
replyMenuItem.action = ^{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
|
||||
[self hideContextualMenuAnimated:YES cancelEventSelection:NO completion:nil];
|
||||
[self selectEventWithId:eventId enableReplyMode:YES showTimestamp:NO];
|
||||
};
|
||||
|
||||
// Edit action
|
||||
|
||||
RoomContextualMenuItem *editMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionEdit];
|
||||
// TODO: Handle edit action
|
||||
editMenuItem.isEnabled = NO;
|
||||
|
||||
// More action
|
||||
|
||||
RoomContextualMenuItem *moreMenuItem = [[RoomContextualMenuItem alloc] initWithMenuAction:RoomContextualMenuActionMore];
|
||||
moreMenuItem.action = ^{
|
||||
MXStrongifyAndReturnIfNil(self);
|
||||
[self showEditButtonAlertMenuForEvent:event inCell:cell level:0];
|
||||
};
|
||||
|
||||
// Actions list
|
||||
|
||||
NSArray<RoomContextualMenuItem*> *actionItems = @[
|
||||
copyMenuItem,
|
||||
replyMenuItem,
|
||||
editMenuItem,
|
||||
moreMenuItem
|
||||
];
|
||||
|
||||
return actionItems;
|
||||
}
|
||||
|
||||
- (void)showContextualMenuForEvent:(MXEvent*)event cell:(id<MXKCellRendering>)cell animated:(BOOL)animated
|
||||
{
|
||||
if (self.roomContextualMenuPresenter.isPresenting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
[self selectEventWithId:event.eventId enableReplyMode:NO showTimestamp:NO];
|
||||
|
||||
NSArray<RoomContextualMenuItem*>* contextualMenuItems = [self contextualMenuItemsForEvent:event andCell:cell];
|
||||
|
||||
RoomContextualMenuViewController *roomContextualMenuViewController = [RoomContextualMenuViewController instantiateWith:contextualMenuItems];
|
||||
roomContextualMenuViewController.delegate = self;
|
||||
|
||||
[self.roomContextualMenuPresenter presentWithRoomContextualMenuViewController:roomContextualMenuViewController
|
||||
from:self
|
||||
on:self.overlayContainerView
|
||||
animated:YES
|
||||
completion:^{
|
||||
[self contextualMenuAnimationCompletionAfterBeingShown:YES];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)hideContextualMenuAnimated:(BOOL)animated
|
||||
{
|
||||
[self hideContextualMenuAnimated:animated completion:nil];
|
||||
}
|
||||
|
||||
- (void)hideContextualMenuAnimated:(BOOL)animated completion:(void(^)(void))completion
|
||||
{
|
||||
[self hideContextualMenuAnimated:animated cancelEventSelection:YES completion:completion];
|
||||
}
|
||||
|
||||
- (void)hideContextualMenuAnimated:(BOOL)animated cancelEventSelection:(BOOL)cancelEventSelection completion:(void(^)(void))completion
|
||||
{
|
||||
if (!self.roomContextualMenuPresenter.isPresenting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (cancelEventSelection)
|
||||
{
|
||||
[self cancelEventSelection];
|
||||
}
|
||||
|
||||
[self.roomContextualMenuPresenter hideContextualMenuWithAnimated:animated completion:^{
|
||||
[self contextualMenuAnimationCompletionAfterBeingShown:NO];
|
||||
|
||||
if (completion)
|
||||
{
|
||||
completion();
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)contextualMenuAnimationCompletionAfterBeingShown:(BOOL)isShown
|
||||
{
|
||||
self.inputToolbarView.editable = !isShown;
|
||||
self.bubblesTableView.scrollsToTop = !isShown;
|
||||
self.overlayContainerView.userInteractionEnabled = isShown;
|
||||
}
|
||||
|
||||
#pragma mark - RoomContextualMenuViewControllerDelegate
|
||||
|
||||
- (void)roomContextualMenuViewControllerDidTapBackgroundOverlay:(RoomContextualMenuViewController *)viewController
|
||||
{
|
||||
[self hideContextualMenuAnimated:YES];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES">
|
||||
<device id="retina4_7" orientation="portrait">
|
||||
<adaptation id="fullscreen"/>
|
||||
</device>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14460.20"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
|
@ -21,6 +21,7 @@
|
|||
<outlet property="jumpToLastUnreadBannerSeparatorView" destination="knN-q1-QkJ" id="hHJ-c8-QfN"/>
|
||||
<outlet property="jumpToLastUnreadButton" destination="ISb-UT-u0O" id="fs0-sQ-lRe"/>
|
||||
<outlet property="jumpToLastUnreadLabel" destination="S1q-B4-Df3" id="McV-gv-bUa"/>
|
||||
<outlet property="overlayContainerView" destination="gt1-EO-UVY" id="5q6-pW-UyZ"/>
|
||||
<outlet property="previewHeaderContainer" destination="54r-18-K1g" id="Klt-RV-V1E"/>
|
||||
<outlet property="previewHeaderContainerHeightConstraint" destination="goj-GZ-IkD" id="GbA-T9-kiL"/>
|
||||
<outlet property="resetReadMarkerButton" destination="c4g-BY-xOo" id="KuR-hH-rz1"/>
|
||||
|
@ -130,7 +131,7 @@
|
|||
<constraint firstItem="ISb-UT-u0O" firstAttribute="centerY" secondItem="Vlz-UJ-Jz8" secondAttribute="centerY" id="w7t-WC-VjP"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XX4-n6-hCm" userLabel="Activities Container">
|
||||
<view clipsSubviews="YES" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="XX4-n6-hCm" userLabel="Activities Container">
|
||||
<rect key="frame" x="0.0" y="606" width="375" height="20"/>
|
||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="RoomVCActivitiesContainer"/>
|
||||
|
@ -146,10 +147,15 @@
|
|||
<constraint firstAttribute="height" constant="41" id="5eD-Fm-RDb"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<view userInteractionEnabled="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gt1-EO-UVY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</view>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<accessibility key="accessibilityConfiguration" identifier="RoomVCView"/>
|
||||
<constraints>
|
||||
<constraint firstItem="gt1-EO-UVY" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="0Gc-VV-1ao"/>
|
||||
<constraint firstAttribute="trailing" secondItem="BGD-sd-SQR" secondAttribute="trailing" id="0la-ok-MBr"/>
|
||||
<constraint firstItem="S6r-bo-jxw" firstAttribute="width" secondItem="BGD-sd-SQR" secondAttribute="width" id="3Mr-fA-bfF"/>
|
||||
<constraint firstItem="nLd-BP-JAE" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="4Q7-hr-rqi"/>
|
||||
|
@ -160,12 +166,15 @@
|
|||
<constraint firstItem="XX4-n6-hCm" firstAttribute="bottom" secondItem="nLd-BP-JAE" secondAttribute="top" id="QO8-nF-xys"/>
|
||||
<constraint firstItem="XX4-n6-hCm" firstAttribute="width" secondItem="iN0-l3-epB" secondAttribute="width" id="WhE-lH-ZtR"/>
|
||||
<constraint firstItem="BGD-sd-SQR" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="X14-4s-uGM"/>
|
||||
<constraint firstItem="gt1-EO-UVY" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="XGH-fF-BKB"/>
|
||||
<constraint firstAttribute="trailing" secondItem="nLd-BP-JAE" secondAttribute="trailing" id="YAu-gd-ItG"/>
|
||||
<constraint firstItem="S6r-bo-jxw" firstAttribute="centerX" secondItem="BGD-sd-SQR" secondAttribute="centerX" id="a2s-5o-q2d"/>
|
||||
<constraint firstItem="gt1-EO-UVY" firstAttribute="bottom" secondItem="nLd-BP-JAE" secondAttribute="bottom" id="acJ-g8-R7x"/>
|
||||
<constraint firstItem="XX4-n6-hCm" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="csl-KT-4s9"/>
|
||||
<constraint firstItem="54r-18-K1g" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="ghf-co-a4t"/>
|
||||
<constraint firstItem="BGD-sd-SQR" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="haP-Kv-OLI"/>
|
||||
<constraint firstAttribute="bottom" secondItem="nLd-BP-JAE" secondAttribute="bottom" id="kQ6-Cg-FMi"/>
|
||||
<constraint firstAttribute="trailing" secondItem="gt1-EO-UVY" secondAttribute="trailing" id="qmd-M4-phm"/>
|
||||
<constraint firstItem="uK2-9a-rZj" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" id="u8r-eN-1g8"/>
|
||||
<constraint firstItem="S6r-bo-jxw" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="xYa-gT-4x0"/>
|
||||
<constraint firstItem="uK2-9a-rZj" firstAttribute="top" secondItem="iN0-l3-epB" secondAttribute="top" id="y6b-JK-CF5"/>
|
||||
|
@ -174,7 +183,7 @@
|
|||
</view>
|
||||
</objects>
|
||||
<resources>
|
||||
<image name="cancel" width="32" height="32"/>
|
||||
<image name="jump_to_unread" width="48" height="48"/>
|
||||
<image name="cancel" width="20" height="20"/>
|
||||
<image name="jump_to_unread" width="30" height="30"/>
|
||||
</resources>
|
||||
</document>
|
||||
|
|