mirror of
https://github.com/oxen-io/session-ios.git
synced 2023-12-13 21:30:14 +01:00
use action sheet to show options for deleting a message
This commit is contained in:
parent
33bd74338d
commit
8c897dcc3d
|
@ -2,82 +2,45 @@
|
||||||
extension ContextMenuVC {
|
extension ContextMenuVC {
|
||||||
|
|
||||||
struct Action {
|
struct Action {
|
||||||
let icon: UIImage?
|
let icon: UIImage
|
||||||
let title: String
|
let title: String
|
||||||
let tag: String
|
|
||||||
let work: () -> Void
|
let work: () -> Void
|
||||||
|
|
||||||
static func reply(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
static func reply(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
||||||
let title = "Reply"
|
let title = "Reply"
|
||||||
let tag = "reply"
|
return Action(icon: UIImage(named: "ic_reply")!, title: title) { delegate?.reply(viewItem) }
|
||||||
return Action(icon: UIImage(named: "ic_reply")!, title: title, tag: tag) { delegate?.reply(viewItem) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func copy(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
static func copy(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
||||||
let title = "Copy"
|
let title = "Copy"
|
||||||
let tag = "copy"
|
return Action(icon: UIImage(named: "ic_copy")!, title: title) { delegate?.copy(viewItem) }
|
||||||
return Action(icon: UIImage(named: "ic_copy")!, title: title, tag: tag) { delegate?.copy(viewItem) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func copySessionID(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
static func copySessionID(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
||||||
let title = "Copy Session ID"
|
let title = "Copy Session ID"
|
||||||
let tag = "copySessionID"
|
return Action(icon: UIImage(named: "ic_copy")!, title: title) { delegate?.copySessionID(viewItem) }
|
||||||
return Action(icon: UIImage(named: "ic_copy")!, title: title, tag: tag) { delegate?.copySessionID(viewItem) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func delete(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
static func delete(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
||||||
let title = "Delete"
|
let title = "Delete"
|
||||||
let tag = "delete"
|
return Action(icon: UIImage(named: "ic_trash")!, title: title) { delegate?.delete(viewItem) }
|
||||||
return Action(icon: UIImage(named: "ic_trash")!, title: title, tag: tag) { delegate?.delete(viewItem) }
|
|
||||||
}
|
|
||||||
|
|
||||||
static func deleteLocally(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
|
||||||
let title = "Delete for me"
|
|
||||||
let tag = "deleteforme"
|
|
||||||
return Action(icon: nil, title: title, tag: tag) { delegate?.deleteLocally(viewItem) }
|
|
||||||
}
|
|
||||||
|
|
||||||
static func deleteForEveryone(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
|
||||||
let tag = "deleteforeveryone"
|
|
||||||
var title = "Delete for everyone"
|
|
||||||
if !viewItem.isGroupThread {
|
|
||||||
title = "Delete for me and \(viewItem.interaction.thread.name())"
|
|
||||||
}
|
|
||||||
return Action(icon: nil, title: title, tag: tag) { delegate?.deleteForEveryone(viewItem) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func save(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
static func save(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
||||||
let title = "Save"
|
let title = "Save"
|
||||||
let tag = "save"
|
return Action(icon: UIImage(named: "ic_download")!, title: title) { delegate?.save(viewItem) }
|
||||||
return Action(icon: UIImage(named: "ic_download")!, title: title, tag: tag) { delegate?.save(viewItem) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func ban(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
static func ban(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
||||||
let title = "Ban User"
|
let title = "Ban User"
|
||||||
let tag = "banUser"
|
return Action(icon: UIImage(named: "ic_block")!, title: title) { delegate?.ban(viewItem) }
|
||||||
return Action(icon: UIImage(named: "ic_block")!, title: title, tag: tag) { delegate?.ban(viewItem) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static func banAndDeleteAllMessages(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
static func banAndDeleteAllMessages(_ viewItem: ConversationViewItem, _ delegate: ContextMenuActionDelegate?) -> Action {
|
||||||
let title = "Ban and Delete All"
|
let title = "Ban and Delete All"
|
||||||
let tag = "banAndDeleteAll"
|
return Action(icon: UIImage(named: "ic_block")!, title: title) { delegate?.banAndDeleteAllMessages(viewItem) }
|
||||||
return Action(icon: UIImage(named: "ic_block")!, title: title, tag: tag) { delegate?.banAndDeleteAllMessages(viewItem) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func deleteActions(for viewItem: ConversationViewItem, delegate: ContextMenuActionDelegate?) -> [Action] {
|
|
||||||
switch viewItem.interaction.interactionType() {
|
|
||||||
case .outgoingMessage:
|
|
||||||
if let message = viewItem.interaction as? TSMessage, let _ = message.serverHash {
|
|
||||||
return [Action.deleteForEveryone(viewItem, delegate), Action.deleteLocally(viewItem, delegate)]
|
|
||||||
}
|
|
||||||
return [Action.deleteLocally(viewItem, delegate)]
|
|
||||||
case .incomingMessage:
|
|
||||||
return [Action.deleteLocally(viewItem, delegate)]
|
|
||||||
default: return [] // Should never occur
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static func actions(for viewItem: ConversationViewItem, delegate: ContextMenuActionDelegate?) -> [Action] {
|
static func actions(for viewItem: ConversationViewItem, delegate: ContextMenuActionDelegate?) -> [Action] {
|
||||||
func isReplyingAllowed() -> Bool {
|
func isReplyingAllowed() -> Bool {
|
||||||
|
@ -119,14 +82,12 @@ extension ContextMenuVC {
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Delegate
|
// MARK: Delegate
|
||||||
protocol ContextMenuActionDelegate : class {
|
protocol ContextMenuActionDelegate : AnyObject {
|
||||||
|
|
||||||
func reply(_ viewItem: ConversationViewItem)
|
func reply(_ viewItem: ConversationViewItem)
|
||||||
func copy(_ viewItem: ConversationViewItem)
|
func copy(_ viewItem: ConversationViewItem)
|
||||||
func copySessionID(_ viewItem: ConversationViewItem)
|
func copySessionID(_ viewItem: ConversationViewItem)
|
||||||
func delete(_ viewItem: ConversationViewItem)
|
func delete(_ viewItem: ConversationViewItem)
|
||||||
func deleteLocally(_ viewItem: ConversationViewItem)
|
|
||||||
func deleteForEveryone(_ viewItem: ConversationViewItem)
|
|
||||||
func save(_ viewItem: ConversationViewItem)
|
func save(_ viewItem: ConversationViewItem)
|
||||||
func ban(_ viewItem: ConversationViewItem)
|
func ban(_ viewItem: ConversationViewItem)
|
||||||
func banAndDeleteAllMessages(_ viewItem: ConversationViewItem)
|
func banAndDeleteAllMessages(_ viewItem: ConversationViewItem)
|
||||||
|
|
|
@ -26,25 +26,20 @@ extension ContextMenuVC {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func setUpViewHierarchy() {
|
private func setUpViewHierarchy() {
|
||||||
var subviews: [UIView] = []
|
|
||||||
// Icon
|
// Icon
|
||||||
if let icon = action.icon {
|
let iconSize = ActionView.iconSize
|
||||||
let iconSize = ActionView.iconSize
|
let iconImageView = UIImageView(image: action.icon.resizedImage(to: CGSize(width: iconSize, height: iconSize))!.withTint(Colors.text))
|
||||||
let iconImageView = UIImageView(image: icon.resizedImage(to: CGSize(width: iconSize, height: iconSize))!.withTint(Colors.text))
|
let iconImageViewSize = ActionView.iconImageViewSize
|
||||||
let iconImageViewSize = ActionView.iconImageViewSize
|
iconImageView.set(.width, to: iconImageViewSize)
|
||||||
iconImageView.set(.width, to: iconImageViewSize)
|
iconImageView.set(.height, to: iconImageViewSize)
|
||||||
iconImageView.set(.height, to: iconImageViewSize)
|
iconImageView.contentMode = .center
|
||||||
iconImageView.contentMode = .center
|
|
||||||
subviews.append(iconImageView)
|
|
||||||
}
|
|
||||||
// Title
|
// Title
|
||||||
let titleLabel = UILabel()
|
let titleLabel = UILabel()
|
||||||
titleLabel.text = action.title
|
titleLabel.text = action.title
|
||||||
titleLabel.textColor = Colors.text
|
titleLabel.textColor = Colors.text
|
||||||
titleLabel.font = .systemFont(ofSize: Values.mediumFontSize)
|
titleLabel.font = .systemFont(ofSize: Values.mediumFontSize)
|
||||||
subviews.append(titleLabel)
|
|
||||||
// Stack view
|
// Stack view
|
||||||
let stackView = UIStackView(arrangedSubviews: subviews)
|
let stackView = UIStackView(arrangedSubviews: [ iconImageView, titleLabel ])
|
||||||
stackView.axis = .horizontal
|
stackView.axis = .horizontal
|
||||||
stackView.spacing = Values.smallSpacing
|
stackView.spacing = Values.smallSpacing
|
||||||
stackView.alignment = .center
|
stackView.alignment = .center
|
||||||
|
@ -61,7 +56,6 @@ extension ContextMenuVC {
|
||||||
// MARK: Interaction
|
// MARK: Interaction
|
||||||
@objc private func handleTap() {
|
@objc private func handleTap() {
|
||||||
action.work()
|
action.work()
|
||||||
guard action.tag != "delete" else { return }
|
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,20 @@ final class ContextMenuVC : UIViewController {
|
||||||
} else {
|
} else {
|
||||||
timestampLabel.pin(.left, to: .right, of: snapshot, withInset: Values.smallSpacing)
|
timestampLabel.pin(.left, to: .right, of: snapshot, withInset: Values.smallSpacing)
|
||||||
}
|
}
|
||||||
let menuHeight = self.updateMenu()
|
// Menu
|
||||||
|
let menuBackgroundView = UIView()
|
||||||
|
menuBackgroundView.backgroundColor = Colors.receivedMessageBackground
|
||||||
|
menuBackgroundView.layer.cornerRadius = ContextMenuVC.menuCornerRadius
|
||||||
|
menuBackgroundView.layer.masksToBounds = true
|
||||||
|
menuView.addSubview(menuBackgroundView)
|
||||||
|
menuBackgroundView.pin(to: menuView)
|
||||||
|
let actionViews = ContextMenuVC.actions(for: viewItem, delegate: delegate).map { ActionView(for: $0, dismiss: snDismiss) }
|
||||||
|
let menuStackView = UIStackView(arrangedSubviews: actionViews)
|
||||||
|
menuStackView.axis = .vertical
|
||||||
|
menuView.addSubview(menuStackView)
|
||||||
|
menuStackView.pin(to: menuView)
|
||||||
|
view.addSubview(menuView)
|
||||||
|
let menuHeight = CGFloat(actionViews.count) * ContextMenuVC.actionViewHeight
|
||||||
let spacing = Values.smallSpacing
|
let spacing = Values.smallSpacing
|
||||||
let margin = max(UIApplication.shared.keyWindow!.safeAreaInsets.bottom, Values.mediumSpacing)
|
let margin = max(UIApplication.shared.keyWindow!.safeAreaInsets.bottom, Values.mediumSpacing)
|
||||||
if frame.maxY + spacing + menuHeight > UIScreen.main.bounds.height - margin {
|
if frame.maxY + spacing + menuHeight > UIScreen.main.bounds.height - margin {
|
||||||
|
@ -106,25 +119,6 @@ final class ContextMenuVC : UIViewController {
|
||||||
super.viewDidLayoutSubviews()
|
super.viewDidLayoutSubviews()
|
||||||
menuView.layer.shadowPath = UIBezierPath(roundedRect: menuView.bounds, cornerRadius: ContextMenuVC.menuCornerRadius).cgPath
|
menuView.layer.shadowPath = UIBezierPath(roundedRect: menuView.bounds, cornerRadius: ContextMenuVC.menuCornerRadius).cgPath
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateMenu(forDelete: Bool = false) -> CGFloat {
|
|
||||||
// Menu: return the menuHeight
|
|
||||||
menuView.subviews.forEach({ $0.removeFromSuperview() })
|
|
||||||
let menuBackgroundView = UIView()
|
|
||||||
menuBackgroundView.backgroundColor = Colors.receivedMessageBackground
|
|
||||||
menuBackgroundView.layer.cornerRadius = ContextMenuVC.menuCornerRadius
|
|
||||||
menuBackgroundView.layer.masksToBounds = true
|
|
||||||
menuView.addSubview(menuBackgroundView)
|
|
||||||
menuBackgroundView.pin(to: menuView)
|
|
||||||
let actions = forDelete ? ContextMenuVC.deleteActions(for: viewItem, delegate: delegate) : ContextMenuVC.actions(for: viewItem, delegate: delegate)
|
|
||||||
let actionViews = actions.map { ActionView(for: $0, dismiss: snDismiss) }
|
|
||||||
let menuStackView = UIStackView(arrangedSubviews: actionViews)
|
|
||||||
menuStackView.axis = .vertical
|
|
||||||
menuView.addSubview(menuStackView)
|
|
||||||
menuStackView.pin(to: menuView)
|
|
||||||
view.addSubview(menuView)
|
|
||||||
return CGFloat(actionViews.count) * ContextMenuVC.actionViewHeight
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: Interaction
|
// MARK: Interaction
|
||||||
@objc private func handleTap() {
|
@objc private func handleTap() {
|
||||||
|
|
|
@ -546,7 +546,22 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc
|
||||||
}
|
}
|
||||||
|
|
||||||
func delete(_ viewItem: ConversationViewItem) {
|
func delete(_ viewItem: ConversationViewItem) {
|
||||||
let _ = self.contextMenuVC?.updateMenu(forDelete: true)
|
let alertVC = UIAlertController.init(title: nil, message: nil, preferredStyle: .actionSheet)
|
||||||
|
let deleteLocallyAction = UIAlertAction.init(title: "Delete just for me", style: .destructive) { _ in
|
||||||
|
self.deleteLocally(viewItem)
|
||||||
|
}
|
||||||
|
var title = "Delete for everyone"
|
||||||
|
if !viewItem.isGroupThread {
|
||||||
|
title = "Delete for me and \(viewItem.interaction.thread.name())"
|
||||||
|
}
|
||||||
|
let deleteRemotelyAction = UIAlertAction.init(title: title, style: .destructive) { _ in
|
||||||
|
self.deleteForEveryone(viewItem)
|
||||||
|
}
|
||||||
|
let cancelAction = UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil)
|
||||||
|
alertVC.addAction(deleteLocallyAction)
|
||||||
|
alertVC.addAction(deleteRemotelyAction)
|
||||||
|
alertVC.addAction(cancelAction)
|
||||||
|
self.navigationController?.presentAlert(alertVC)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func buildUsendRequest(_ viewItem: ConversationViewItem) -> UnsendRequest? {
|
private func buildUsendRequest(_ viewItem: ConversationViewItem) -> UnsendRequest? {
|
||||||
|
|
Loading…
Reference in a new issue