Signal-iOS/Signal/src/ViewControllers/ConversationView/ConversationViewController+MessageActions.swift
2021-06-18 09:37:43 -03:00

138 lines
6.1 KiB
Swift

//
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
//
import Foundation
extension ConversationViewController {
func presentMessageActions(_ messageActions: [MessageAction],
withFocusedCell cell: UICollectionViewCell,
itemViewModel: CVItemViewModelImpl) {
guard let window = view.window,
let navigationController = navigationController else {
owsFailDebug("Missing window or navigationController.")
return
}
let messageActionsViewController = MessageActionsViewController(itemViewModel: itemViewModel,
focusedView: cell,
actions: messageActions)
messageActionsViewController.delegate = self
self.messageActionsViewController = messageActionsViewController
setupMessageActionsState(forCell: cell)
messageActionsViewController.present(on: window,
prepareConstraints: {
// In order to ensure the bottom bar remains above the keyboard, we pin it
// to our bottom bar which follows the inputAccessoryView
messageActionsViewController.bottomBar.autoPinEdge(.bottom,
to: .bottom,
of: self.bottomBar)
// We only want the message actions to show up over the detail view, in
// the case where we are expanded. So match its edges to our nav controller.
messageActionsViewController.view.autoPinEdges(toEdgesOf: navigationController.view)
},
animateAlongside: {
self.bottomBar.alpha = 0
},
completion: nil)
}
func updateMessageActionsState(forCell cell: UIView) {
// While presenting message actions, cache the original content offset.
// This allows us to restore the user to their original scroll position
// when they dismiss the menu.
self.messageActionsOriginalContentOffset = self.collectionView.contentOffset
self.messageActionsOriginalFocusY = self.view.convert(cell.frame.origin, from: self.collectionView).y
}
func setupMessageActionsState(forCell cell: UIView) {
guard let navigationController = navigationController else {
owsFailDebug("Missing navigationController.")
return
}
updateMessageActionsState(forCell: cell)
// While the menu actions are presented, temporarily use extra content
// inset padding so that interactions near the top or bottom of the
// collection view can be scrolled anywhere within the viewport.
// This allows us to keep the message position constant even when
// messages dissappear above / below the focused message to the point
// that we have less than one screen worth of content.
let navControllerSize = navigationController.view.frame.size
self.messageActionsExtraContentInsetPadding = max(navControllerSize.width, navControllerSize.height)
var contentInset = self.collectionView.contentInset
contentInset.top += self.messageActionsExtraContentInsetPadding
contentInset.bottom += self.messageActionsExtraContentInsetPadding
self.collectionView.contentInset = contentInset
}
func clearMessageActionsState() {
self.bottomBar.alpha = 1
var contentInset = self.collectionView.contentInset
contentInset.top -= self.messageActionsExtraContentInsetPadding
contentInset.bottom -= self.messageActionsExtraContentInsetPadding
self.collectionView.contentInset = contentInset
// TODO: This isn't safe. We should capture a token that represents scroll state.
self.collectionView.contentOffset = self.messageActionsOriginalContentOffset
self.messageActionsOriginalContentOffset = .zero
self.messageActionsExtraContentInsetPadding = 0
self.messageActionsViewController = nil
}
@objc
public var isPresentingMessageActions: Bool {
self.messageActionsViewController != nil
}
@objc
public func dismissMessageActions(animated: Bool) {
dismissMessageActions(animated: animated, completion: nil)
}
public typealias MessageActionsCompletion = () -> Void
@objc
public func dismissMessageActions(animated: Bool, completion: MessageActionsCompletion?) {
Logger.verbose("")
guard let messageActionsViewController = messageActionsViewController else {
return
}
if animated {
messageActionsViewController.dismiss(animateAlongside: {
self.bottomBar.alpha = 1
}, completion: {
self.clearMessageActionsState()
completion?()
})
} else {
messageActionsViewController.dismissWithoutAnimating()
clearMessageActionsState()
completion?()
}
}
func dismissMessageActionsIfNecessary() {
if shouldDismissMessageActions {
dismissMessageActions(animated: true)
}
}
var shouldDismissMessageActions: Bool {
guard let messageActionsViewController = messageActionsViewController else {
return false
}
let messageActionInteractionId = messageActionsViewController.focusedInteraction.uniqueId
// Check whether there is still a view item for this interaction.
return self.indexPath(forInteractionUniqueId: messageActionInteractionId) == nil
}
}