Merge pull request #2424 from vector-im/riot_2394

New message actions
This commit is contained in:
SBiOSoftWhare 2019-05-16 14:08:18 +02:00 committed by GitHub
commit 5c12f86fa8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
45 changed files with 1374 additions and 89 deletions

View file

@ -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)
===============================================

View file

@ -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 */,
);

View file

@ -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];

View file

@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View 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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 968 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View 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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 871 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View 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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 658 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

View 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"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 476 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 920 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -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";

View file

@ -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.

View file

@ -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];
}
}
}

View 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)
}
}

View 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
}
}

View 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()
}
}
}

View file

@ -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")

View file

@ -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"

View file

@ -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")

View file

@ -16,6 +16,6 @@
import Foundation
protocol Themable: class {
@objc protocol Themable: class {
func update(theme: Theme)
}

View file

@ -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.

View file

@ -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];

View 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
}
}
}

View 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>

View file

@ -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
}
}

View file

@ -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()
}
}

View file

@ -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()
}
}
}

View file

@ -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
}
}

View file

@ -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>

View file

@ -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>

View file

@ -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)
}
}

View file

@ -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.

View file

@ -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)
{

View file

@ -123,7 +123,7 @@
#import "Riot-Swift.h"
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate>
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, RoomTitleViewTapGestureDelegate, RoomParticipantsViewControllerDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate, RoomContextualMenuViewControllerDelegate>
{
// The expanded header
ExpandedRoomTitleView *expandedHeader;
@ -213,6 +213,10 @@
MXServerNotices *serverNotices;
}
@property (nonatomic, weak) IBOutlet UIView *overlayContainerView;
@property (nonatomic, strong) RoomContextualMenuPresenter *roomContextualMenuPresenter;
@end
@implementation RoomViewController
@ -404,6 +408,8 @@
[self refreshRoomInputToolbar];
}
self.roomContextualMenuPresenter = [RoomContextualMenuPresenter new];
// Observe user interface theme change.
kThemeServiceDidChangeThemeNotificationObserver = [[NSNotificationCenter defaultCenter] addObserverForName:kThemeServiceDidChangeThemeNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *notif) {
@ -589,6 +595,9 @@
{
[super viewDidDisappear:animated];
// Hide contextual menu if needed
[self hideContextualMenuAnimated:NO];
// Reset visible room id
[AppDelegate theDelegate].visibleRoomId = nil;
@ -936,6 +945,8 @@
- (void)updateRoomInputToolbarViewClassIfNeeded
{
Class roomInputToolbarViewClass = RoomInputToolbarView.class;
BOOL shouldDismissContextualMenu = NO;
// Check the user has enough power to post message
if (self.roomDataSource.roomState)
@ -950,10 +961,12 @@
if (isRoomObsolete || isResourceLimitExceeded)
{
roomInputToolbarViewClass = nil;
shouldDismissContextualMenu = YES;
}
else if (!canSend)
{
roomInputToolbarViewClass = DisabledRoomInputToolbarView.class;
shouldDismissContextualMenu = YES;
}
}
@ -961,6 +974,12 @@
if (self.isRoomPreview)
{
roomInputToolbarViewClass = nil;
shouldDismissContextualMenu = YES;
}
if (shouldDismissContextualMenu)
{
[self hideContextualMenuAnimated:NO];
}
// Change inputToolbarView class only if given class is different from current one
@ -978,7 +997,7 @@
if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class])
{
height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarMinHeightConstraint.constant;
height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarHeightConstraint.constant;
}
else if ([self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class])
{
@ -1485,6 +1504,14 @@
[UIView setAnimationsEnabled:YES];
}
- (void)handleLongPressFromCell:(id<MXKCellRendering>)cell withTappedEvent:(MXEvent*)event
{
if (event && !customizedRoomDataSource.selectedEventId)
{
[self showContextualMenuForEvent:event cell:cell animated:YES];
}
}
#pragma mark - Hide/Show expanded header
- (void)showExpandedHeader:(BOOL)isVisible
@ -1552,6 +1579,9 @@
mainNavigationController.navigationBar.translucent = isVisible;
self.navigationController.navigationBar.translucent = isVisible;
// Hide contextual menu if needed
[self hideContextualMenuAnimated:YES];
[UIView animateWithDuration:0.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn
animations:^{
@ -2030,9 +2060,6 @@
[self selectEventWithId:tappedEvent.eventId];
}
}
// Force table refresh
[self dataSource:self.roomDataSource didCellChange:nil];
}
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellTapOnOverlayContainer])
{
@ -2073,9 +2100,6 @@
// Highlight this event in displayed message
[self selectEventWithId:((MXKRoomBubbleTableViewCell*)cell).bubbleData.attachment.eventId];
}
// Force table refresh
[self dataSource:self.roomDataSource didCellChange:nil];
}
else
{
@ -2105,6 +2129,11 @@
[self.roomDataSource collapseRoomBubble:((MXKRoomBubbleTableViewCell*)cell).bubbleData collapsed:YES];
}
else if ([actionIdentifier isEqualToString:kMXKRoomBubbleCellLongPressOnEvent])
{
MXEvent *tappedEvent = userInfo[kMXKRoomBubbleCellEventKey];
[self handleLongPressFromCell:cell withTappedEvent:tappedEvent];
}
else
{
// Keep default implementation for other actions
@ -2213,24 +2242,6 @@
}]];
}
}
if (level == 0)
{
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_copy", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self cancelEventSelection];
[[UIPasteboard generalPasteboard] setString:selectedComponent.textMessage];
}
}]];
}
if (level == 0)
{
@ -2323,42 +2334,6 @@
}]];
}
if (attachment.type != MXKAttachmentTypeSticker)
{
[currentAlert addAction:[UIAlertAction actionWithTitle:NSLocalizedStringFromTable(@"room_event_action_copy", @"Vector", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
if (weakSelf)
{
typeof(self) self = weakSelf;
[self cancelEventSelection];
[self startActivityIndicator];
[attachment copy:^{
__strong __typeof(weakSelf)self = weakSelf;
[self stopActivityIndicator];
} failure:^(NSError *error) {
__strong __typeof(weakSelf)self = weakSelf;
[self stopActivityIndicator];
//Alert user
[[AppDelegate theDelegate] showErrorAsAlert:error];
}];
// Start animation in case of download during attachment preparing
[roomBubbleTableViewCell startProgressUI];
}
}]];
}
// Check status of the selected event
if (selectedEvent.sentState == MXEventSentStatePreparing ||
selectedEvent.sentState == MXEventSentStateEncrypting ||
@ -2733,7 +2708,7 @@
if (weakSelf)
{
typeof(self) self = weakSelf;
[self cancelEventSelection];
[self hideContextualMenuAnimated:YES];
}
}]];
@ -2843,10 +2818,8 @@
else if (url && urlItemInteractionValue)
{
// Fallback case for external links
// TODO: Use UITextItemInteraction enum when minimum deployement target will be iOS 10
switch (urlItemInteractionValue.integerValue) {
case 0: //UITextItemInteractionInvokeDefaultAction
case UITextItemInteractionInvokeDefaultAction:
{
[[UIApplication sharedApplication] vc_open:url completionHandler:^(BOOL success) {
if (!success)
@ -2857,10 +2830,13 @@
shouldDoAction = NO;
}
break;
case 1: //UITextItemInteractionPresentActions
// Long press on link, let MXKRoomBubbleTableViewCell UITextView present the default contextual menu.
case UITextItemInteractionPresentActions:
{
// Long press on link, present room contextual menu.
shouldDoAction = NO;
}
break;
case 2: //UITextItemInteractionPreview
case UITextItemInteractionPreview:
// Force touch on link, let MXKRoomBubbleTableViewCell UITextView use default peek and pop behavior.
break;
default:
@ -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

View file

@ -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>