mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
Split out generic action sheet components
Keep the message specific components separte, so we could re-use the MenuActionsViewController.
This commit is contained in:
parent
093a5eaa68
commit
82fdd5b883
6 changed files with 283 additions and 273 deletions
|
@ -419,7 +419,8 @@
|
|||
4C20B2B720CA0034001BAC90 /* ThreadViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4542DF51208B82E9007B4E76 /* ThreadViewModel.swift */; };
|
||||
4C20B2B920CA10DE001BAC90 /* ConversationSearchViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */; };
|
||||
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */; };
|
||||
4CB5F26720F6E1E2004D1B42 /* MessageActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MessageActionsViewController.swift */; };
|
||||
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */; };
|
||||
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CB5F26820F7D060004D1B42 /* MessageActions.swift */; };
|
||||
4CC0B59C20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */; };
|
||||
70377AAB1918450100CAF501 /* MobileCoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 70377AAA1918450100CAF501 /* MobileCoreServices.framework */; };
|
||||
768A1A2B17FC9CD300E00ED8 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 768A1A2A17FC9CD300E00ED8 /* libz.dylib */; };
|
||||
|
@ -1083,8 +1084,9 @@
|
|||
4C13C9F520E57BA30089A98B /* ColorPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorPickerViewController.swift; sourceTree = "<group>"; };
|
||||
4C20B2B820CA10DE001BAC90 /* ConversationSearchViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationSearchViewController.swift; sourceTree = "<group>"; };
|
||||
4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissableTextField.swift; sourceTree = "<group>"; };
|
||||
4CB5F26820F7D060004D1B42 /* MessageActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActions.swift; sourceTree = "<group>"; };
|
||||
4CC0B59B20EC5F2E00CF6EE0 /* ConversationConfigurationSyncOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConversationConfigurationSyncOperation.swift; sourceTree = "<group>"; };
|
||||
4CFF4C0920F55BBA005DA313 /* MessageActionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageActionsViewController.swift; sourceTree = "<group>"; };
|
||||
4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuActionsViewController.swift; sourceTree = "<group>"; };
|
||||
69349DE607F5BA6036C9AC60 /* Pods-SignalShareExtension.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SignalShareExtension.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SignalShareExtension/Pods-SignalShareExtension.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
70377AAA1918450100CAF501 /* MobileCoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MobileCoreServices.framework; path = System/Library/Frameworks/MobileCoreServices.framework; sourceTree = SDKROOT; };
|
||||
748A5CAEDD7C919FC64C6807 /* Pods_SignalTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SignalTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
|
@ -1691,7 +1693,7 @@
|
|||
452EC6DE205E9E30000E787C /* MediaGalleryViewController.swift */,
|
||||
45F32C1D205718B000A300D5 /* MediaPageViewController.swift */,
|
||||
454A84032059C787008B8C75 /* MediaTileViewController.swift */,
|
||||
4CFF4C0920F55BBA005DA313 /* MessageActionsViewController.swift */,
|
||||
4CFF4C0920F55BBA005DA313 /* MenuActionsViewController.swift */,
|
||||
34CA1C261F7156F300E51C51 /* MessageDetailViewController.swift */,
|
||||
34B3F84F1E8DF1700035BE1A /* NewContactThreadViewController.h */,
|
||||
34B3F8501E8DF1700035BE1A /* NewContactThreadViewController.m */,
|
||||
|
@ -1989,6 +1991,7 @@
|
|||
45DF5DF11DDB843F00C936C7 /* CompareSafetyNumbersActivity.swift */,
|
||||
458E38351D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.h */,
|
||||
458E38361D668EBF0094BD24 /* OWSDeviceProvisioningURLParser.m */,
|
||||
4CB5F26820F7D060004D1B42 /* MessageActions.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
|
@ -3264,7 +3267,7 @@
|
|||
340FC8CD20518C77007AEB0F /* OWSBackupJob.m in Sources */,
|
||||
34D1F0AE1F867BFC0066283D /* OWSMessageCell.m in Sources */,
|
||||
4C4AEC4520EC343B0020E72B /* DismissableTextField.swift in Sources */,
|
||||
4CB5F26720F6E1E2004D1B42 /* MessageActionsViewController.swift in Sources */,
|
||||
4CB5F26720F6E1E2004D1B42 /* MenuActionsViewController.swift in Sources */,
|
||||
451686AB1F520CDA00AC3D4B /* MultiDeviceProfileKeyUpdateJob.swift in Sources */,
|
||||
45D231771DC7E8F10034FA89 /* SessionResetJob.swift in Sources */,
|
||||
340FC8A9204DAC8D007AEB0F /* NotificationSettingsOptionsViewController.m in Sources */,
|
||||
|
@ -3350,6 +3353,7 @@
|
|||
76EB054018170B33006006FC /* AppDelegate.m in Sources */,
|
||||
34D1F0831F8678AA0066283D /* ConversationInputTextView.m in Sources */,
|
||||
340FC8B6204DAC8D007AEB0F /* OWSQRCodeScanningViewController.m in Sources */,
|
||||
4CB5F26920F7D060004D1B42 /* MessageActions.swift in Sources */,
|
||||
340FC8B5204DAC8D007AEB0F /* AboutTableViewController.m in Sources */,
|
||||
34BECE2B1F74C12700D7438D /* DebugUIStress.m in Sources */,
|
||||
340FC8B9204DAC8D007AEB0F /* UpdateGroupViewController.m in Sources */,
|
||||
|
|
131
Signal/src/Models/MessageActions.swift
Normal file
131
Signal/src/Models/MessageActions.swift
Normal file
|
@ -0,0 +1,131 @@
|
|||
//
|
||||
// Copyright (c) 2018 Open Whisper Systems. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
@objc
|
||||
protocol MessageActionsDelegate: class {
|
||||
func messageActionsShowDetailsForItem(_ conversationViewItem: ConversationViewItem)
|
||||
func messageActionsReplyToItem(_ conversationViewItem: ConversationViewItem)
|
||||
}
|
||||
|
||||
struct MessageActionBuilder {
|
||||
static func reply(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
|
||||
return MenuAction(image: #imageLiteral(resourceName: "ic_reply"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_REPLY", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { [weak delegate] (_) in
|
||||
delegate?.messageActionsReplyToItem(conversationViewItem)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
static func copyText(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
|
||||
return MenuAction(image: #imageLiteral(resourceName: "ic_copy"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_COPY_TEXT", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { (_) in
|
||||
conversationViewItem.copyTextAction()
|
||||
})
|
||||
}
|
||||
|
||||
static func showDetails(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
|
||||
return MenuAction(image: #imageLiteral(resourceName: "ic_info"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_DETAILS", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { [weak delegate] (_) in
|
||||
delegate?.messageActionsShowDetailsForItem(conversationViewItem)
|
||||
})
|
||||
}
|
||||
|
||||
static func deleteMessage(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
|
||||
return MenuAction(image: #imageLiteral(resourceName: "ic_trash"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE", comment: "Action sheet button title"),
|
||||
subtitle: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE_SUBTITLE", comment: "Action sheet button subtitle"),
|
||||
block: { (_) in
|
||||
conversationViewItem.deleteAction()
|
||||
})
|
||||
}
|
||||
|
||||
static func copyMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
|
||||
return MenuAction(image: #imageLiteral(resourceName: "ic_copy"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_COPY_MEDIA", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { (_) in
|
||||
conversationViewItem.copyMediaAction()
|
||||
})
|
||||
}
|
||||
|
||||
static func saveMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MenuAction {
|
||||
return MenuAction(image: #imageLiteral(resourceName: "ic_download"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_SAVE_MEDIA", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { (_) in
|
||||
conversationViewItem.saveMediaAction()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension ConversationViewItem {
|
||||
|
||||
@objc
|
||||
func textActions(delegate: MessageActionsDelegate) -> [MenuAction] {
|
||||
var actions: [MenuAction] = []
|
||||
|
||||
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(replyAction)
|
||||
|
||||
if self.hasBodyTextActionContent {
|
||||
let copyTextAction = MessageActionBuilder.copyText(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(copyTextAction)
|
||||
}
|
||||
|
||||
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(deleteAction)
|
||||
|
||||
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(showDetailsAction)
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
@objc
|
||||
func mediaActions(delegate: MessageActionsDelegate) -> [MenuAction] {
|
||||
var actions: [MenuAction] = []
|
||||
|
||||
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(replyAction)
|
||||
|
||||
if self.hasMediaActionContent {
|
||||
let copyMediaAction = MessageActionBuilder.copyMedia(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(copyMediaAction)
|
||||
let saveMediaAction = MessageActionBuilder.saveMedia(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(saveMediaAction)
|
||||
}
|
||||
|
||||
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(deleteAction)
|
||||
|
||||
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(showDetailsAction)
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
@objc
|
||||
func quotedMessageActions(delegate: MessageActionsDelegate) -> [MenuAction] {
|
||||
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
|
||||
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
||||
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
|
||||
|
||||
return [replyAction, deleteAction, showDetailsAction]
|
||||
}
|
||||
|
||||
@objc
|
||||
func infoMessageActions(delegate: MessageActionsDelegate) -> [MenuAction] {
|
||||
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
||||
|
||||
return [deleteAction]
|
||||
}
|
||||
}
|
|
@ -132,6 +132,7 @@ typedef enum : NSUInteger {
|
|||
ConversationViewCellDelegate,
|
||||
ConversationInputTextViewDelegate,
|
||||
MessageActionsDelegate,
|
||||
MenuActionsViewControllerDelegate,
|
||||
OWSMessageBubbleViewDelegate,
|
||||
UICollectionViewDelegate,
|
||||
UICollectionViewDataSource,
|
||||
|
@ -1981,13 +1982,6 @@ typedef enum : NSUInteger {
|
|||
|
||||
#pragma mark - MessageActionsDelegate
|
||||
|
||||
- (void)messageActionsDidHide:(MessageActionsViewController *)messageActionsViewController
|
||||
{
|
||||
[[OWSWindowManager sharedManager] hideMessageActionsWindow:messageActionsViewController];
|
||||
|
||||
[self updateShouldObserveDBModifications];
|
||||
}
|
||||
|
||||
- (void)messageActionsShowDetailsForItem:(ConversationViewItem *)conversationViewItem
|
||||
{
|
||||
[self showDetailViewForViewItem:conversationViewItem];
|
||||
|
@ -1998,7 +1992,16 @@ typedef enum : NSUInteger {
|
|||
[self populateReplyForViewItem:conversationViewItem];
|
||||
}
|
||||
|
||||
- (void)messageActions:(MessageActionsViewController *)messageActionsViewController
|
||||
#pragma mark - MenuActionsViewControllerDelegate
|
||||
|
||||
- (void)menuActionsDidHide:(MenuActionsViewController *)menuActionsViewController
|
||||
{
|
||||
[[OWSWindowManager sharedManager] hideMenuActionsWindow];
|
||||
|
||||
[self updateShouldObserveDBModifications];
|
||||
}
|
||||
|
||||
- (void)menuActions:(MenuActionsViewController *)menuActionsViewController
|
||||
isPresentingWithVerticalFocusChange:(CGFloat)verticalChange
|
||||
{
|
||||
UIEdgeInsets oldInset = self.collectionView.contentInset;
|
||||
|
@ -2026,7 +2029,7 @@ typedef enum : NSUInteger {
|
|||
self.collectionView.contentInset = newInset;
|
||||
}
|
||||
|
||||
- (void)messageActions:(MessageActionsViewController *)messageActionsViewController
|
||||
- (void)menuActions:(MenuActionsViewController *)menuActionsViewController
|
||||
isDismissingWithVerticalFocusChange:(CGFloat)verticalChange
|
||||
{
|
||||
UIEdgeInsets oldInset = self.collectionView.contentInset;
|
||||
|
@ -2058,36 +2061,36 @@ typedef enum : NSUInteger {
|
|||
|
||||
- (void)conversationCell:(ConversationViewCell *)cell didLongpressMediaViewItem:(ConversationViewItem *)viewItem
|
||||
{
|
||||
NSArray<MessageAction *> *messageActions = [viewItem mediaActionsWithDelegate:self];
|
||||
NSArray<MenuAction *> *messageActions = [viewItem mediaActionsWithDelegate:self];
|
||||
[self presentMessageActions:messageActions withFocusedCell:cell];
|
||||
}
|
||||
|
||||
- (void)conversationCell:(ConversationViewCell *)cell didLongpressTextViewItem:(ConversationViewItem *)viewItem
|
||||
{
|
||||
NSArray<MessageAction *> *messageActions = [viewItem textActionsWithDelegate:self];
|
||||
NSArray<MenuAction *> *messageActions = [viewItem textActionsWithDelegate:self];
|
||||
[self presentMessageActions:messageActions withFocusedCell:cell];
|
||||
}
|
||||
|
||||
- (void)conversationCell:(ConversationViewCell *)cell didLongpressQuoteViewItem:(ConversationViewItem *)viewItem
|
||||
{
|
||||
NSArray<MessageAction *> *messageActions = [viewItem quotedMessageActionsWithDelegate:self];
|
||||
NSArray<MenuAction *> *messageActions = [viewItem quotedMessageActionsWithDelegate:self];
|
||||
[self presentMessageActions:messageActions withFocusedCell:cell];
|
||||
}
|
||||
|
||||
- (void)conversationCell:(ConversationViewCell *)cell didLongpressSystemMessageViewItem:(ConversationViewItem *)viewItem
|
||||
{
|
||||
NSArray<MessageAction *> *messageActions = [viewItem infoMessageActionsWithDelegate:self];
|
||||
NSArray<MenuAction *> *messageActions = [viewItem infoMessageActionsWithDelegate:self];
|
||||
[self presentMessageActions:messageActions withFocusedCell:cell];
|
||||
}
|
||||
|
||||
- (void)presentMessageActions:(NSArray<MessageAction *> *)messageActions withFocusedCell:(ConversationViewCell *)cell
|
||||
- (void)presentMessageActions:(NSArray<MenuAction *> *)messageActions withFocusedCell:(ConversationViewCell *)cell
|
||||
{
|
||||
MessageActionsViewController *messageActionsViewController =
|
||||
[[MessageActionsViewController alloc] initWithFocusedView:cell actions:messageActions];
|
||||
MenuActionsViewController *menuActionsViewController =
|
||||
[[MenuActionsViewController alloc] initWithFocusedView:cell actions:messageActions];
|
||||
|
||||
messageActionsViewController.delegate = self;
|
||||
menuActionsViewController.delegate = self;
|
||||
|
||||
[[OWSWindowManager sharedManager] showMessageActionsWindow:messageActionsViewController];
|
||||
[[OWSWindowManager sharedManager] showMenuActionsWindow:menuActionsViewController];
|
||||
|
||||
[self updateShouldObserveDBModifications];
|
||||
}
|
||||
|
@ -4550,7 +4553,7 @@ typedef enum : NSUInteger {
|
|||
return;
|
||||
}
|
||||
|
||||
if (OWSWindowManager.sharedManager.isPresentingMessageActions) {
|
||||
if (OWSWindowManager.sharedManager.isPresentingMenuActions) {
|
||||
self.shouldObserveDBModifications = NO;
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -5,148 +5,41 @@
|
|||
import Foundation
|
||||
|
||||
@objc
|
||||
protocol MessageActionsDelegate: class {
|
||||
func messageActionsDidHide(_ messageActionsViewController: MessageActionsViewController)
|
||||
func messageActionsShowDetailsForItem(_ conversationViewItem: ConversationViewItem)
|
||||
func messageActionsReplyToItem(_ conversationViewItem: ConversationViewItem)
|
||||
func messageActions(_ messageActionsViewController: MessageActionsViewController, isPresentingWithVerticalFocusChange: CGFloat)
|
||||
func messageActions(_ messageActionsViewController: MessageActionsViewController, isDismissingWithVerticalFocusChange: CGFloat)
|
||||
}
|
||||
public class MenuAction: NSObject {
|
||||
let block: (MenuAction) -> Void
|
||||
let image: UIImage
|
||||
let title: String
|
||||
let subtitle: String?
|
||||
|
||||
struct MessageActionBuilder {
|
||||
static func reply(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
||||
return MessageAction(image: #imageLiteral(resourceName: "ic_reply"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_REPLY", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { [weak delegate] (_) in
|
||||
delegate?.messageActionsReplyToItem(conversationViewItem)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
static func copyText(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
||||
return MessageAction(image: #imageLiteral(resourceName: "ic_copy"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_COPY_TEXT", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { (_) in
|
||||
conversationViewItem.copyTextAction()
|
||||
})
|
||||
}
|
||||
|
||||
static func showDetails(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
||||
return MessageAction(image: #imageLiteral(resourceName: "ic_info"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_DETAILS", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { [weak delegate] (_) in
|
||||
delegate?.messageActionsShowDetailsForItem(conversationViewItem)
|
||||
})
|
||||
}
|
||||
|
||||
static func deleteMessage(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
||||
return MessageAction(image: #imageLiteral(resourceName: "ic_trash"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE", comment: "Action sheet button title"),
|
||||
subtitle: NSLocalizedString("MESSAGE_ACTION_DELETE_MESSAGE_SUBTITLE", comment: "Action sheet button subtitle"),
|
||||
block: { (_) in
|
||||
conversationViewItem.deleteAction()
|
||||
})
|
||||
}
|
||||
|
||||
static func copyMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
||||
return MessageAction(image: #imageLiteral(resourceName: "ic_copy"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_COPY_MEDIA", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { (_) in
|
||||
conversationViewItem.copyMediaAction()
|
||||
})
|
||||
}
|
||||
|
||||
static func saveMedia(conversationViewItem: ConversationViewItem, delegate: MessageActionsDelegate) -> MessageAction {
|
||||
return MessageAction(image: #imageLiteral(resourceName: "ic_download"),
|
||||
title: NSLocalizedString("MESSAGE_ACTION_SAVE_MEDIA", comment: "Action sheet button title"),
|
||||
subtitle: nil,
|
||||
block: { (_) in
|
||||
conversationViewItem.saveMediaAction()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
extension ConversationViewItem {
|
||||
|
||||
@objc
|
||||
func textActions(delegate: MessageActionsDelegate) -> [MessageAction] {
|
||||
var actions: [MessageAction] = []
|
||||
|
||||
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(replyAction)
|
||||
|
||||
if self.hasBodyTextActionContent {
|
||||
let copyTextAction = MessageActionBuilder.copyText(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(copyTextAction)
|
||||
}
|
||||
|
||||
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(deleteAction)
|
||||
|
||||
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(showDetailsAction)
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
@objc
|
||||
func mediaActions(delegate: MessageActionsDelegate) -> [MessageAction] {
|
||||
var actions: [MessageAction] = []
|
||||
|
||||
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(replyAction)
|
||||
|
||||
if self.hasMediaActionContent {
|
||||
let copyMediaAction = MessageActionBuilder.copyMedia(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(copyMediaAction)
|
||||
let saveMediaAction = MessageActionBuilder.saveMedia(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(saveMediaAction)
|
||||
}
|
||||
|
||||
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(deleteAction)
|
||||
|
||||
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
|
||||
actions.append(showDetailsAction)
|
||||
|
||||
return actions
|
||||
}
|
||||
|
||||
@objc
|
||||
func quotedMessageActions(delegate: MessageActionsDelegate) -> [MessageAction] {
|
||||
let replyAction = MessageActionBuilder.reply(conversationViewItem: self, delegate: delegate)
|
||||
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
||||
let showDetailsAction = MessageActionBuilder.showDetails(conversationViewItem: self, delegate: delegate)
|
||||
|
||||
return [replyAction, deleteAction, showDetailsAction]
|
||||
}
|
||||
|
||||
@objc
|
||||
func infoMessageActions(delegate: MessageActionsDelegate) -> [MessageAction] {
|
||||
let deleteAction = MessageActionBuilder.deleteMessage(conversationViewItem: self, delegate: delegate)
|
||||
|
||||
return [deleteAction]
|
||||
public init(image: UIImage, title: String, subtitle: String?, block: @escaping (MenuAction) -> Void) {
|
||||
self.image = image
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.block = block
|
||||
}
|
||||
}
|
||||
|
||||
@objc
|
||||
class MessageActionsViewController: UIViewController, MessageActionSheetDelegate {
|
||||
protocol MenuActionsViewControllerDelegate: class {
|
||||
func menuActionsDidHide(_ menuActionsViewController: MenuActionsViewController)
|
||||
func menuActions(_ menuActionsViewController: MenuActionsViewController, isPresentingWithVerticalFocusChange: CGFloat)
|
||||
func menuActions(_ menuActionsViewController: MenuActionsViewController, isDismissingWithVerticalFocusChange: CGFloat)
|
||||
}
|
||||
|
||||
@objc
|
||||
class MenuActionsViewController: UIViewController, MenuActionSheetDelegate {
|
||||
|
||||
@objc
|
||||
weak var delegate: MessageActionsDelegate?
|
||||
weak var delegate: MenuActionsViewControllerDelegate?
|
||||
|
||||
private let focusedView: UIView
|
||||
private let actionSheetView: MessageActionSheetView
|
||||
private let actionSheetView: MenuActionSheetView
|
||||
|
||||
@objc
|
||||
required init(focusedView: UIView, actions: [MessageAction]) {
|
||||
required init(focusedView: UIView, actions: [MenuAction]) {
|
||||
self.focusedView = focusedView
|
||||
|
||||
self.actionSheetView = MessageActionSheetView(actions: actions)
|
||||
self.actionSheetView = MenuActionSheetView(actions: actions)
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
actionSheetView.delegate = self
|
||||
|
@ -206,8 +99,6 @@ class MessageActionsViewController: UIViewController, MessageActionSheetDelegate
|
|||
}
|
||||
|
||||
private func animatePresentation() {
|
||||
// TODO first time only?
|
||||
|
||||
guard let actionSheetViewVerticalConstraint = self.actionSheetViewVerticalConstraint else {
|
||||
owsFail("\(self.logTag) in \(#function) actionSheetViewVerticalConstraint was unexpectedly nil")
|
||||
return
|
||||
|
@ -255,27 +146,27 @@ class MessageActionsViewController: UIViewController, MessageActionSheetDelegate
|
|||
|
||||
let offset = -overlap
|
||||
self.presentationFocusOffset = offset
|
||||
self.delegate?.messageActions(self, isPresentingWithVerticalFocusChange: offset)
|
||||
self.delegate?.menuActions(self, isPresentingWithVerticalFocusChange: offset)
|
||||
},
|
||||
completion: nil)
|
||||
}
|
||||
|
||||
private func animateDismiss(action: MessageAction?) {
|
||||
private func animateDismiss(action: MenuAction?) {
|
||||
guard let actionSheetViewVerticalConstraint = self.actionSheetViewVerticalConstraint else {
|
||||
owsFail("\(self.logTag) in \(#function) actionSheetVerticalConstraint was unexpectedly nil")
|
||||
self.delegate?.messageActionsDidHide(self)
|
||||
self.delegate?.menuActionsDidHide(self)
|
||||
return
|
||||
}
|
||||
|
||||
guard let snapshotView = self.snapshotView else {
|
||||
owsFail("\(self.logTag) in \(#function) snapshotView was unexpectedly nil")
|
||||
self.delegate?.messageActionsDidHide(self)
|
||||
self.delegate?.menuActionsDidHide(self)
|
||||
return
|
||||
}
|
||||
|
||||
guard let presentationFocusOffset = self.presentationFocusOffset else {
|
||||
owsFail("\(self.logTag) in \(#function) presentationFocusOffset was unexpectedly nil")
|
||||
self.delegate?.messageActionsDidHide(self)
|
||||
self.delegate?.menuActionsDidHide(self)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -293,11 +184,11 @@ class MessageActionsViewController: UIViewController, MessageActionSheetDelegate
|
|||
snapshotView.frame.origin.y -= presentationFocusOffset
|
||||
// this helps when focused view is above navbars, etc.
|
||||
snapshotView.alpha = 0
|
||||
self.delegate?.messageActions(self, isDismissingWithVerticalFocusChange: presentationFocusOffset)
|
||||
self.delegate?.menuActions(self, isDismissingWithVerticalFocusChange: presentationFocusOffset)
|
||||
},
|
||||
completion: { _ in
|
||||
self.view.isHidden = true
|
||||
self.delegate?.messageActionsDidHide(self)
|
||||
self.delegate?.menuActionsDidHide(self)
|
||||
if let action = action {
|
||||
action.block(action)
|
||||
}
|
||||
|
@ -316,43 +207,92 @@ class MessageActionsViewController: UIViewController, MessageActionSheetDelegate
|
|||
animateDismiss(action: nil)
|
||||
}
|
||||
|
||||
// MARK: MessageActionSheetDelegate
|
||||
// MARK: MenuActionSheetDelegate
|
||||
|
||||
func actionSheet(_ actionSheet: MessageActionSheetView, didSelectAction action: MessageAction) {
|
||||
func actionSheet(_ actionSheet: MenuActionSheetView, didSelectAction action: MenuAction) {
|
||||
animateDismiss(action: action)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: ActionView
|
||||
protocol MenuActionSheetDelegate: class {
|
||||
func actionSheet(_ actionSheet: MenuActionSheetView, didSelectAction action: MenuAction)
|
||||
}
|
||||
|
||||
@objc
|
||||
public class MessageAction: NSObject {
|
||||
let block: (MessageAction) -> Void
|
||||
let image: UIImage
|
||||
let title: String
|
||||
let subtitle: String?
|
||||
class MenuActionSheetView: UIView, MenuActionViewDelegate {
|
||||
|
||||
public init(image: UIImage, title: String, subtitle: String?, block: @escaping (MessageAction) -> Void) {
|
||||
self.image = image
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.block = block
|
||||
private let actionStackView: UIStackView
|
||||
private var actions: [MenuAction]
|
||||
weak var delegate: MenuActionSheetDelegate?
|
||||
|
||||
override var bounds: CGRect {
|
||||
didSet {
|
||||
updateMask()
|
||||
}
|
||||
}
|
||||
|
||||
convenience init(actions: [MenuAction]) {
|
||||
self.init(frame: CGRect.zero)
|
||||
actions.forEach { self.addAction($0) }
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
actionStackView = UIStackView()
|
||||
actionStackView.axis = .vertical
|
||||
actionStackView.spacing = CGHairlineWidth()
|
||||
|
||||
actions = []
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
backgroundColor = UIColor.ows_light10
|
||||
addSubview(actionStackView)
|
||||
actionStackView.autoPinToSuperviewEdges()
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
// Prevent panning from percolating to the superview, which would
|
||||
// cause us to dismiss
|
||||
let panGestureSink = UIPanGestureRecognizer(target: nil, action: nil)
|
||||
self.addGestureRecognizer(panGestureSink)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("not implemented")
|
||||
}
|
||||
|
||||
public func addAction(_ action: MenuAction) {
|
||||
let actionView = MenuActionView(action: action)
|
||||
actionView.delegate = self
|
||||
actions.append(action)
|
||||
self.actionStackView.addArrangedSubview(actionView)
|
||||
}
|
||||
|
||||
// MARK: MenuActionViewDelegate
|
||||
|
||||
func actionView(_ actionView: MenuActionView, didSelectAction action: MenuAction) {
|
||||
self.delegate?.actionSheet(self, didSelectAction: action)
|
||||
}
|
||||
|
||||
// MARK:
|
||||
|
||||
private func updateMask() {
|
||||
let cornerRadius: CGFloat = 16
|
||||
let path: UIBezierPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
|
||||
let mask = CAShapeLayer()
|
||||
mask.path = path.cgPath
|
||||
self.layer.mask = mask
|
||||
}
|
||||
}
|
||||
|
||||
protocol MessageActionSheetDelegate: class {
|
||||
func actionSheet(_ actionSheet: MessageActionSheetView, didSelectAction action: MessageAction)
|
||||
protocol MenuActionViewDelegate: class {
|
||||
func actionView(_ actionView: MenuActionView, didSelectAction action: MenuAction)
|
||||
}
|
||||
|
||||
protocol MessageActionViewDelegate: class {
|
||||
func actionView(_ actionView: MessageActionView, didSelectAction action: MessageAction)
|
||||
}
|
||||
class MenuActionView: UIButton {
|
||||
public weak var delegate: MenuActionViewDelegate?
|
||||
private let action: MenuAction
|
||||
|
||||
class MessageActionView: UIButton {
|
||||
public weak var delegate: MessageActionViewDelegate?
|
||||
private let action: MessageAction
|
||||
|
||||
required init(action: MessageAction) {
|
||||
required init(action: MenuAction) {
|
||||
self.action = action
|
||||
|
||||
super.init(frame: CGRect.zero)
|
||||
|
@ -413,69 +353,3 @@ class MessageActionView: UIButton {
|
|||
fatalError("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
class MessageActionSheetView: UIView, MessageActionViewDelegate {
|
||||
|
||||
private let actionStackView: UIStackView
|
||||
private var actions: [MessageAction]
|
||||
weak var delegate: MessageActionSheetDelegate?
|
||||
|
||||
override var bounds: CGRect {
|
||||
didSet {
|
||||
updateMask()
|
||||
}
|
||||
}
|
||||
|
||||
convenience init(actions: [MessageAction]) {
|
||||
self.init(frame: CGRect.zero)
|
||||
actions.forEach { self.addAction($0) }
|
||||
}
|
||||
|
||||
override init(frame: CGRect) {
|
||||
actionStackView = UIStackView()
|
||||
actionStackView.axis = .vertical
|
||||
actionStackView.spacing = CGHairlineWidth()
|
||||
|
||||
actions = []
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
backgroundColor = UIColor.ows_light10
|
||||
addSubview(actionStackView)
|
||||
actionStackView.autoPinToSuperviewEdges()
|
||||
|
||||
self.clipsToBounds = true
|
||||
|
||||
// Prevent panning from percolating to the superview, which would
|
||||
// cause us to dismiss
|
||||
let panGestureSink = UIPanGestureRecognizer(target: nil, action: nil)
|
||||
self.addGestureRecognizer(panGestureSink)
|
||||
}
|
||||
|
||||
required init?(coder aDecoder: NSCoder) {
|
||||
fatalError("not implemented")
|
||||
}
|
||||
|
||||
public func addAction(_ action: MessageAction) {
|
||||
let actionView = MessageActionView(action: action)
|
||||
actionView.delegate = self
|
||||
actions.append(action)
|
||||
self.actionStackView.addArrangedSubview(actionView)
|
||||
}
|
||||
|
||||
// MARK: MessageActionViewDelegate
|
||||
|
||||
func actionView(_ actionView: MessageActionView, didSelectAction action: MessageAction) {
|
||||
self.delegate?.actionSheet(self, didSelectAction: action)
|
||||
}
|
||||
|
||||
// MARK:
|
||||
|
||||
private func updateMask() {
|
||||
let cornerRadius: CGFloat = 16
|
||||
let path: UIBezierPath = UIBezierPath(roundedRect: bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: cornerRadius, height: cornerRadius))
|
||||
let mask = CAShapeLayer()
|
||||
mask.path = path.cgPath
|
||||
self.layer.mask = mask
|
||||
}
|
||||
}
|
|
@ -29,10 +29,10 @@ extern const UIWindowLevel UIWindowLevel_Background;
|
|||
|
||||
#pragma mark - Message Actions
|
||||
|
||||
@property (nonatomic, readonly) BOOL isPresentingMessageActions;
|
||||
@property (nonatomic, readonly) BOOL isPresentingMenuActions;
|
||||
|
||||
- (void)showMessageActionsWindow:(UIViewController *)messageActionsViewController;
|
||||
- (void)hideMessageActionsWindow:(UIViewController *)messageActionsViewController;
|
||||
- (void)showMenuActionsWindow:(UIViewController *)menuActionsViewController;
|
||||
- (void)hideMenuActionsWindow;
|
||||
|
||||
#pragma mark - Calls
|
||||
|
||||
|
|
|
@ -101,8 +101,8 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
@property (nonatomic) UINavigationController *callNavigationController;
|
||||
|
||||
// UIWindowLevel_MessageActions
|
||||
@property (nonatomic) UIWindow *messageActionsWindow;
|
||||
@property (nonatomic, nullable) UIViewController *messageActionsViewController;
|
||||
@property (nonatomic) UIWindow *menuActionsWindow;
|
||||
@property (nonatomic, nullable) UIViewController *menuActionsViewController;
|
||||
|
||||
// UIWindowLevel_Background if inactive,
|
||||
// UIWindowLevel_ScreenBlocking() if active.
|
||||
|
@ -157,7 +157,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
|
||||
self.returnToCallWindow = [self createReturnToCallWindow:rootWindow];
|
||||
self.callViewWindow = [self createCallViewWindow:rootWindow];
|
||||
self.messageActionsWindow = [self createMessageActionsWindowWithRoowWindow:rootWindow];
|
||||
self.menuActionsWindow = [self createMenuActionsWindowWithRoowWindow:rootWindow];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(didChangeStatusBarFrame:)
|
||||
|
@ -200,7 +200,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
return window;
|
||||
}
|
||||
|
||||
- (UIWindow *)createMessageActionsWindowWithRoowWindow:(UIWindow *)rootWindow
|
||||
- (UIWindow *)createMenuActionsWindowWithRoowWindow:(UIWindow *)rootWindow
|
||||
{
|
||||
UIWindow *window;
|
||||
if (@available(iOS 11, *)) {
|
||||
|
@ -262,25 +262,23 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
|
||||
#pragma mark - Message Actions
|
||||
|
||||
- (BOOL)isPresentingMessageActions
|
||||
- (BOOL)isPresentingMenuActions
|
||||
{
|
||||
return self.messageActionsViewController != nil;
|
||||
return self.menuActionsViewController != nil;
|
||||
}
|
||||
|
||||
- (void)showMessageActionsWindow:(UIViewController *)messageActionsViewController
|
||||
- (void)showMenuActionsWindow:(UIViewController *)menuActionsViewController
|
||||
{
|
||||
self.messageActionsViewController = messageActionsViewController;
|
||||
self.messageActionsWindow.rootViewController = messageActionsViewController;
|
||||
self.menuActionsViewController = menuActionsViewController;
|
||||
self.menuActionsWindow.rootViewController = menuActionsViewController;
|
||||
|
||||
[self ensureWindowState];
|
||||
}
|
||||
|
||||
- (void)hideMessageActionsWindow:(UIViewController *)messageActionsViewController
|
||||
- (void)hideMenuActionsWindow
|
||||
{
|
||||
OWSAssert(self.messageActionsViewController == messageActionsViewController);
|
||||
|
||||
self.messageActionsWindow.rootViewController = nil;
|
||||
self.messageActionsViewController = nil;
|
||||
self.menuActionsWindow.rootViewController = nil;
|
||||
self.menuActionsViewController = nil;
|
||||
|
||||
[self ensureWindowState];
|
||||
}
|
||||
|
@ -404,7 +402,7 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
[self ensureCallViewWindowHidden];
|
||||
[self ensureMessageActionsWindowHidden];
|
||||
[self ensureScreenBlockWindowHidden];
|
||||
} else if (self.messageActionsViewController) {
|
||||
} else if (self.menuActionsViewController) {
|
||||
// Show Message Actions
|
||||
|
||||
[self ensureRootWindowShown];
|
||||
|
@ -503,23 +501,23 @@ const UIWindowLevel UIWindowLevel_MessageActions(void)
|
|||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
if (self.messageActionsWindow.hidden) {
|
||||
if (self.menuActionsWindow.hidden) {
|
||||
DDLogInfo(@"%@ showing message actions window.", self.logTag);
|
||||
}
|
||||
|
||||
// Do not make key, we want the keyboard to stay popped.
|
||||
self.messageActionsWindow.hidden = NO;
|
||||
self.menuActionsWindow.hidden = NO;
|
||||
}
|
||||
|
||||
- (void)ensureMessageActionsWindowHidden
|
||||
{
|
||||
OWSAssertIsOnMainThread();
|
||||
|
||||
if (!self.messageActionsWindow.hidden) {
|
||||
if (!self.menuActionsWindow.hidden) {
|
||||
DDLogInfo(@"%@ hiding message actions window.", self.logTag);
|
||||
}
|
||||
|
||||
self.messageActionsWindow.hidden = YES;
|
||||
self.menuActionsWindow.hidden = YES;
|
||||
}
|
||||
|
||||
- (void)ensureScreenBlockWindowShown
|
||||
|
|
Loading…
Reference in a new issue