mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-28 19:05:49 +00:00
iPad trackpad support improvements
This commit is contained in:
parent
689a5e0427
commit
f1ccd3cf33
@ -1885,6 +1885,8 @@ plist_fragment(
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
""".format(
|
||||
telegram_bundle_id = telegram_bundle_id,
|
||||
)
|
||||
|
||||
@ -73,9 +73,9 @@ private struct ChatListSearchRecentPeersEntry: Comparable, Identifiable {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
|
||||
func item(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool) -> ListViewItem {
|
||||
return HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: mode, context: context, peer: self.peer, presence: self.presence, unreadBadge: self.unreadBadge, action: peerSelected, contextAction: { peer, node, gesture in
|
||||
peerContextAction(peer, node, gesture)
|
||||
func item(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool) -> ListViewItem {
|
||||
return HorizontalPeerItem(theme: self.theme, strings: self.strings, mode: mode, context: context, peer: self.peer, presence: self.presence, unreadBadge: self.unreadBadge, action: peerSelected, contextAction: { peer, node, gesture, location in
|
||||
peerContextAction(peer, node, gesture, location)
|
||||
}, isPeerSelected: isPeerSelected, customWidth: self.itemCustomWidth)
|
||||
}
|
||||
}
|
||||
@ -88,7 +88,7 @@ private struct ChatListSearchRecentNodeTransition {
|
||||
let animated: Bool
|
||||
}
|
||||
|
||||
private func preparedRecentPeersTransition(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, share: Bool = false, from fromEntries: [ChatListSearchRecentPeersEntry], to toEntries: [ChatListSearchRecentPeersEntry], firstTime: Bool, animated: Bool) -> ChatListSearchRecentNodeTransition {
|
||||
private func preparedRecentPeersTransition(context: AccountContext, mode: HorizontalPeerItemMode, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, share: Bool = false, from fromEntries: [ChatListSearchRecentPeersEntry], to toEntries: [ChatListSearchRecentPeersEntry], firstTime: Bool, animated: Bool) -> ChatListSearchRecentNodeTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
@ -107,7 +107,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode {
|
||||
private let share: Bool
|
||||
|
||||
private let peerSelected: (EnginePeer) -> Void
|
||||
private let peerContextAction: (EnginePeer, ASDisplayNode, ContextGesture?) -> Void
|
||||
private let peerContextAction: (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void
|
||||
private let isPeerSelected: (EnginePeer.Id) -> Bool
|
||||
|
||||
private let disposable = MetaDisposable()
|
||||
@ -122,7 +122,7 @@ public final class ChatListSearchRecentPeersNode: ASDisplayNode {
|
||||
return self.ready.get()
|
||||
}
|
||||
|
||||
public init(context: AccountContext, theme: PresentationTheme, mode: HorizontalPeerItemMode, strings: PresentationStrings, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, share: Bool = false) {
|
||||
public init(context: AccountContext, theme: PresentationTheme, mode: HorizontalPeerItemMode, strings: PresentationStrings, peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, share: Bool = false) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.themeAndStringsPromise = Promise((self.theme, self.strings))
|
||||
|
||||
@ -1119,7 +1119,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self?.toolbarActionSelected(action: action)
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.containerNode.activateChatPreview = { [weak self] item, node, gesture in
|
||||
self.chatListDisplayNode.containerNode.activateChatPreview = { [weak self] item, node, gesture, location in
|
||||
guard let strongSelf = self else {
|
||||
gesture?.cancel()
|
||||
return
|
||||
@ -1141,14 +1141,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatListController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: archiveContextMenuItems(context: strongSelf.context, groupId: groupId._asGroup(), chatListController: strongSelf) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
case let .peer(_, peer, _, _, _, _, _, _, _, promoInfo, _, _, _):
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
|
||||
chatController.canReadHistory.set(false)
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)), items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
let source: ContextContentSource
|
||||
if let location = location {
|
||||
source = .location(ChatListContextLocationContentSource(controller: strongSelf, location: location))
|
||||
} else {
|
||||
let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(id: peer.peerId), subject: nil, botStart: nil, mode: .standard(previewing: true))
|
||||
chatController.canReadHistory.set(false)
|
||||
source = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController))
|
||||
}
|
||||
|
||||
let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.peerId, promoInfo: promoInfo, source: .chatList(filter: strongSelf.chatListDisplayNode.containerNode.currentItemNode.chatListFilter), chatListController: strongSelf, joined: joined) |> map { ContextController.Items(content: .list($0)) }, gesture: gesture)
|
||||
strongSelf.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.peerContextAction = { [weak self] peer, source, node, gesture in
|
||||
self.chatListDisplayNode.peerContextAction = { [weak self] peer, source, node, gesture, location in
|
||||
guard let strongSelf = self else {
|
||||
gesture?.cancel()
|
||||
return
|
||||
@ -3415,3 +3422,17 @@ private final class ChatListHeaderBarContextExtractedContentSource: ContextExtra
|
||||
return ContextControllerPutBackViewInfo(contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChatListContextLocationContentSource: ContextLocationContentSource {
|
||||
private let controller: ViewController
|
||||
private let location: CGPoint
|
||||
|
||||
init(controller: ViewController, location: CGPoint) {
|
||||
self.controller = controller
|
||||
self.location = location
|
||||
}
|
||||
|
||||
func transitionInfo() -> ContextControllerLocationViewInfo? {
|
||||
return ContextControllerLocationViewInfo(location: self.location, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +181,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
||||
let timestamp1: Int32 = 100000
|
||||
let peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
let interaction = ChatListNodeInteraction(context: context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in })
|
||||
|
||||
@ -529,8 +529,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
itemNode.listNode.contentScrollingEnded = { [weak self] listView in
|
||||
return self?.contentScrollingEnded?(listView) ?? false
|
||||
}
|
||||
itemNode.listNode.activateChatPreview = { [weak self] item, sourceNode, gesture in
|
||||
self?.activateChatPreview?(item, sourceNode, gesture)
|
||||
itemNode.listNode.activateChatPreview = { [weak self] item, sourceNode, gesture, location in
|
||||
self?.activateChatPreview?(item, sourceNode, gesture, location)
|
||||
}
|
||||
itemNode.listNode.addedVisibleChatsWithPeerIds = { [weak self] ids in
|
||||
self?.addedVisibleChatsWithPeerIds?(ids)
|
||||
@ -577,7 +577,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)?
|
||||
var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
|
||||
var contentScrollingEnded: ((ListView) -> Bool)?
|
||||
var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
|
||||
var didBeginSelectingChats: (() -> Void)?
|
||||
var displayFilterLimit: (() -> Void)?
|
||||
@ -1088,7 +1088,7 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
var requestOpenRecentPeerOptions: ((EnginePeer) -> Void)?
|
||||
var requestOpenMessageFromSearch: ((EnginePeer, EngineMessage.Id, Bool) -> Void)?
|
||||
var requestAddContact: ((String) -> Void)?
|
||||
var peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
var peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
var dismissSelfIfCompletedPresentation: (() -> Void)?
|
||||
var isEmptyUpdated: ((Bool) -> Void)?
|
||||
var emptyListAction: (() -> Void)?
|
||||
|
||||
@ -15,11 +15,11 @@ class ChatListRecentPeersListItem: ListViewItem {
|
||||
let context: AccountContext
|
||||
let peers: [EnginePeer]
|
||||
let peerSelected: (EnginePeer) -> Void
|
||||
let peerContextAction: (EnginePeer, ASDisplayNode, ContextGesture?) -> Void
|
||||
let peerContextAction: (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void
|
||||
|
||||
let header: ListViewItemHeader?
|
||||
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, context: AccountContext, peers: [EnginePeer], peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?) -> Void) {
|
||||
init(theme: PresentationTheme, strings: PresentationStrings, context: AccountContext, peers: [EnginePeer], peerSelected: @escaping (EnginePeer) -> Void, peerContextAction: @escaping (EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.context = context
|
||||
@ -122,8 +122,8 @@ class ChatListRecentPeersListItemNode: ListViewItemNode {
|
||||
} else {
|
||||
peersNode = ChatListSearchRecentPeersNode(context: item.context, theme: item.theme, mode: .list(compact: false), strings: item.strings, peerSelected: { peer in
|
||||
self?.item?.peerSelected(peer)
|
||||
}, peerContextAction: { peer, node, gesture in
|
||||
self?.item?.peerContextAction(peer, node, gesture)
|
||||
}, peerContextAction: { peer, node, gesture, location in
|
||||
self?.item?.peerContextAction(peer, node, gesture, location)
|
||||
}, isPeerSelected: { _ in
|
||||
return false
|
||||
})
|
||||
|
||||
@ -49,12 +49,12 @@ final class ChatListSearchInteraction {
|
||||
let toggleMessageSelection: (EngineMessage.Id, Bool) -> Void
|
||||
let messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)
|
||||
let mediaMessageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void)
|
||||
let peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
let peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
let present: (ViewController, Any?) -> Void
|
||||
let dismissInput: () -> Void
|
||||
let getSelectedMessageIds: () -> Set<EngineMessage.Id>?
|
||||
|
||||
init(openPeer: @escaping (EnginePeer, EnginePeer?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer) -> Void, openMessage: @escaping (EnginePeer, EngineMessage.Id, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (EngineMessage.Id, Bool) -> Void, messageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void), mediaMessageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set<EngineMessage.Id>?) {
|
||||
init(openPeer: @escaping (EnginePeer, EnginePeer?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer) -> Void, openMessage: @escaping (EnginePeer, EngineMessage.Id, Bool) -> Void, openUrl: @escaping (String) -> Void, clearRecentSearch: @escaping () -> Void, addContact: @escaping (String) -> Void, toggleMessageSelection: @escaping (EngineMessage.Id, Bool) -> Void, messageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void), mediaMessageContextAction: @escaping ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?) -> Void), peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, dismissInput: @escaping () -> Void, getSelectedMessageIds: @escaping () -> Set<EngineMessage.Id>?) {
|
||||
self.openPeer = openPeer
|
||||
self.openDisabledPeer = openDisabledPeer
|
||||
self.openMessage = openMessage
|
||||
@ -129,7 +129,7 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
|
||||
private var validLayout: (ContainerViewLayout, CGFloat)?
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, groupId: EngineChatList.Group, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, filter: ChatListNodePeersFilter, groupId: EngineChatList.Group, displaySearchFilters: Bool, hasDownloads: Bool, initialFilter: ChatListSearchFilter = .chats, openPeer originalOpenPeer: @escaping (EnginePeer, EnginePeer?, Bool) -> Void, openDisabledPeer: @escaping (EnginePeer) -> Void, openRecentPeerOptions: @escaping (EnginePeer) -> Void, openMessage originalOpenMessage: @escaping (EnginePeer, EngineMessage.Id, Bool) -> Void, addContact: ((String) -> Void)?, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, present: @escaping (ViewController, Any?) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, navigationController: NavigationController?) {
|
||||
self.context = context
|
||||
self.peersFilter = filter
|
||||
self.groupId = groupId
|
||||
@ -225,8 +225,8 @@ public final class ChatListSearchContainerNode: SearchDisplayControllerContentNo
|
||||
self?.messageContextAction(message, node: node, rect: rect, gesture: gesture, paneKey: paneKey, downloadResource: downloadResource)
|
||||
}, mediaMessageContextAction: { [weak self] message, node, rect, gesture in
|
||||
self?.mediaMessageContextAction(message, node: node, rect: rect, gesture: gesture)
|
||||
}, peerContextAction: { peer, source, node, gesture in
|
||||
peerContextAction?(peer, source, node, gesture)
|
||||
}, peerContextAction: { peer, source, node, gesture, location in
|
||||
peerContextAction?(peer, source, node, gesture, location)
|
||||
}, present: { c, a in
|
||||
present(c, a)
|
||||
}, dismissInput: { [weak self] in
|
||||
|
||||
@ -87,14 +87,14 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void) -> ListViewItem {
|
||||
func item(context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void) -> ListViewItem {
|
||||
switch self {
|
||||
case let .topPeers(peers, theme, strings):
|
||||
return ChatListRecentPeersListItem(theme: theme, strings: strings, context: context, peers: peers, peerSelected: { peer in
|
||||
peerSelected(peer)
|
||||
}, peerContextAction: { peer, node, gesture in
|
||||
}, peerContextAction: { peer, node, gesture, location in
|
||||
if let peerContextAction = peerContextAction {
|
||||
peerContextAction(peer, .recentPeers, node, gesture)
|
||||
peerContextAction(peer, .recentPeers, node, gesture, location)
|
||||
} else {
|
||||
gesture?.cancel()
|
||||
}
|
||||
@ -204,9 +204,9 @@ private enum ChatListRecentEntry: Comparable, Identifiable {
|
||||
disabledPeerSelected(EnginePeer(chatPeer))
|
||||
}
|
||||
}, deletePeer: deletePeer, contextAction: peerContextAction.flatMap { peerContextAction in
|
||||
return { node, gesture in
|
||||
return { node, gesture, location in
|
||||
if let chatPeer = peer.peer.peers[peer.peer.peerId] {
|
||||
peerContextAction(EnginePeer(chatPeer), .recentSearch, node, gesture)
|
||||
peerContextAction(EnginePeer(chatPeer), .recentSearch, node, gesture, location)
|
||||
} else {
|
||||
gesture?.cancel()
|
||||
}
|
||||
@ -418,7 +418,7 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
public func item(context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ListViewItem {
|
||||
public func item(context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ListViewItem {
|
||||
switch self {
|
||||
case let .recentlySearchedPeer(peer, associatedPeer, unreadBadge, _, theme, strings, nameSortOrder, nameDisplayOrder):
|
||||
let primaryPeer: EnginePeer
|
||||
@ -488,9 +488,9 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
interaction.peerSelected(peer, nil, nil)
|
||||
}
|
||||
}, contextAction: peerContextAction.flatMap { peerContextAction in
|
||||
return { node, gesture in
|
||||
return { node, gesture, location in
|
||||
if let chatPeer = chatPeer, chatPeer.id.namespace != Namespaces.Peer.SecretChat {
|
||||
peerContextAction(chatPeer, .search(nil), node, gesture)
|
||||
peerContextAction(chatPeer, .search(nil), node, gesture, location)
|
||||
} else {
|
||||
gesture?.cancel()
|
||||
}
|
||||
@ -575,9 +575,9 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
interaction.peerSelected(peer, nil, nil)
|
||||
}
|
||||
}, contextAction: peerContextAction.flatMap { peerContextAction in
|
||||
return { node, gesture in
|
||||
return { node, gesture, location in
|
||||
if let chatPeer = chatPeer, chatPeer.id.namespace != Namespaces.Peer.SecretChat {
|
||||
peerContextAction(chatPeer, .search(nil), node, gesture)
|
||||
peerContextAction(chatPeer, .search(nil), node, gesture, location)
|
||||
} else {
|
||||
gesture?.cancel()
|
||||
}
|
||||
@ -638,8 +638,8 @@ public enum ChatListSearchEntry: Comparable, Identifiable {
|
||||
return ContactsPeerItem(presentationData: ItemListPresentationData(presentationData), sortOrder: nameSortOrder, displayOrder: nameDisplayOrder, context: context, peerMode: .generalSearch, peer: .peer(peer: EnginePeer(peer.peer), chatPeer: EnginePeer(peer.peer)), status: .addressName(suffixString), badge: badge, enabled: enabled, selection: .none, editing: ContactsPeerItemEditing(editable: false, editing: false, revealed: false), index: nil, header: header, action: { _ in
|
||||
interaction.peerSelected(EnginePeer(peer.peer), nil, nil)
|
||||
}, contextAction: peerContextAction.flatMap { peerContextAction in
|
||||
return { node, gesture in
|
||||
peerContextAction(EnginePeer(peer.peer), .search(nil), node, gesture)
|
||||
return { node, gesture, location in
|
||||
peerContextAction(EnginePeer(peer.peer), .search(nil), node, gesture, location)
|
||||
}
|
||||
})
|
||||
case let .message(message, peer, readState, presentationData, _, selected, displayCustomHeader, orderingKey, _, _, allPaused):
|
||||
@ -710,7 +710,7 @@ public struct ChatListSearchContainerTransition {
|
||||
}
|
||||
}
|
||||
|
||||
private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void) -> ChatListSearchContainerRecentTransition {
|
||||
private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [ChatListRecentEntry], to toEntries: [ChatListRecentEntry], context: AccountContext, presentationData: ChatListPresentationData, filter: ChatListNodePeersFilter, peerSelected: @escaping (EnginePeer) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, clearRecentlySearchedPeers: @escaping () -> Void, deletePeer: @escaping (EnginePeer.Id) -> Void) -> ChatListSearchContainerRecentTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
@ -720,7 +720,7 @@ private func chatListSearchContainerPreparedRecentTransition(from fromEntries: [
|
||||
return ChatListSearchContainerRecentTransition(deletions: deletions, insertions: insertions, updates: updates)
|
||||
}
|
||||
|
||||
public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, isEmpty: Bool, isLoading: Bool, animated: Bool, context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ChatListSearchContainerTransition {
|
||||
public func chatListSearchContainerPreparedTransition(from fromEntries: [ChatListSearchEntry], to toEntries: [ChatListSearchEntry], displayingResults: Bool, isEmpty: Bool, isLoading: Bool, animated: Bool, context: AccountContext, presentationData: PresentationData, enableHeaders: Bool, filter: ChatListNodePeersFilter, key: ChatListSearchPaneKey, tagMask: EngineMessage.Tags?, interaction: ChatListNodeInteraction, listInteraction: ListMessageItemInteraction, peerContextAction: ((EnginePeer, ChatListSearchContextActionSource, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, toggleExpandLocalResults: @escaping () -> Void, toggleExpandGlobalResults: @escaping () -> Void, searchPeer: @escaping (EnginePeer) -> Void, searchQuery: String?, searchOptions: ChatListSearchOptions?, messageContextAction: ((EngineMessage, ASDisplayNode?, CGRect?, UIGestureRecognizer?, ChatListSearchPaneKey, (id: String, size: Int64, isFirstInList: Bool)?) -> Void)?, openClearRecentlyDownloaded: @escaping () -> Void, toggleAllPaused: @escaping () -> Void) -> ChatListSearchContainerTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
@ -1720,7 +1720,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
}, togglePeerMarkedUnread: { _, _ in
|
||||
}, toggleArchivedFolderHiddenByDefault: {
|
||||
}, hidePsa: { _ in
|
||||
}, activateChatPreview: { item, node, gesture in
|
||||
}, activateChatPreview: { item, node, gesture, location in
|
||||
guard let peerContextAction = interaction.peerContextAction else {
|
||||
gesture?.cancel()
|
||||
return
|
||||
@ -1728,7 +1728,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
switch item.content {
|
||||
case let .peer(messages, peer, _, _, _, _, _, _, _, _, _, _, _):
|
||||
if let peer = peer.peer, let message = messages.first {
|
||||
peerContextAction(peer, .search(message.id), node, gesture)
|
||||
peerContextAction(peer, .search(message.id), node, gesture, location)
|
||||
}
|
||||
case .groupReference:
|
||||
gesture?.cancel()
|
||||
@ -1879,8 +1879,8 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
|
||||
let animated = (previousSelectedMessageIds == nil) != (strongSelf.selectedMessages == nil)
|
||||
let firstTime = previousEntries == nil
|
||||
var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags?.0 != nil, isEmpty: !isSearching && (entriesAndFlags?.0.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture in
|
||||
interaction.peerContextAction?(message, node, rect, gesture)
|
||||
var transition = chatListSearchContainerPreparedTransition(from: previousEntries ?? [], to: newEntries, displayingResults: entriesAndFlags?.0 != nil, isEmpty: !isSearching && (entriesAndFlags?.0.isEmpty ?? false), isLoading: isSearching, animated: animated, context: context, presentationData: strongSelf.presentationData, enableHeaders: true, filter: peersFilter, key: strongSelf.key, tagMask: tagMask, interaction: chatListInteraction, listInteraction: listInteraction, peerContextAction: { message, node, rect, gesture, location in
|
||||
interaction.peerContextAction?(message, node, rect, gesture, location)
|
||||
}, toggleExpandLocalResults: {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -2042,9 +2042,9 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
||||
self?.recentListNode.clearHighlightAnimated(true)
|
||||
}, disabledPeerSelected: { peer in
|
||||
interaction.openDisabledPeer(peer)
|
||||
}, peerContextAction: { peer, source, node, gesture in
|
||||
}, peerContextAction: { peer, source, node, gesture, location in
|
||||
if let peerContextAction = interaction.peerContextAction {
|
||||
peerContextAction(peer, source, node, gesture)
|
||||
peerContextAction(peer, source, node, gesture, location)
|
||||
} else {
|
||||
gesture?.cancel()
|
||||
}
|
||||
@ -2925,7 +2925,7 @@ private final class ChatListSearchShimmerNode: ASDisplayNode {
|
||||
var peers: [EnginePeer.Id: EnginePeer] = [:]
|
||||
peers[peer1.id] = peer1
|
||||
let interaction = ChatListNodeInteraction(context: context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in }, activateChatPreview: { _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in })
|
||||
|
||||
|
||||
@ -163,7 +163,7 @@ public class ChatListItem: ListViewItem, ChatListSearchItemNeighbour {
|
||||
self.interaction.groupSelected(groupId)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static func mergeType(item: ChatListItem, previousItem: ListViewItem?, nextItem: ListViewItem?) -> (first: Bool, last: Bool, firstWithHeader: Bool, nextIsPinned: Bool) {
|
||||
var first = false
|
||||
var last = false
|
||||
@ -734,7 +734,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
guard let strongSelf = self, let item = strongSelf.item else {
|
||||
return
|
||||
}
|
||||
item.interaction.activateChatPreview(item, strongSelf.contextContainer, gesture)
|
||||
item.interaction.activateChatPreview(item, strongSelf.contextContainer, gesture, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -743,6 +743,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.playbackStartDisposable.dispose()
|
||||
}
|
||||
|
||||
override func secondaryAction(at point: CGPoint) {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
item.interaction.activateChatPreview(item, self.contextContainer, nil, point)
|
||||
}
|
||||
|
||||
func setupItem(item: ChatListItem, synchronousLoads: Bool) {
|
||||
let previousItem = self.item
|
||||
self.item = item
|
||||
|
||||
@ -72,7 +72,7 @@ public final class ChatListNodeInteraction {
|
||||
let togglePeerMarkedUnread: (EnginePeer.Id, Bool) -> Void
|
||||
let toggleArchivedFolderHiddenByDefault: () -> Void
|
||||
let hidePsa: (EnginePeer.Id) -> Void
|
||||
let activateChatPreview: (ChatListItem, ASDisplayNode, ContextGesture?) -> Void
|
||||
let activateChatPreview: (ChatListItem, ASDisplayNode, ContextGesture?, CGPoint?) -> Void
|
||||
let present: (ViewController) -> Void
|
||||
|
||||
public var searchTextHighightState: String?
|
||||
@ -81,7 +81,7 @@ public final class ChatListNodeInteraction {
|
||||
let animationCache: AnimationCache
|
||||
let animationRenderer: MultiAnimationRenderer
|
||||
|
||||
public init(context: AccountContext, activateSearch: @escaping () -> Void, peerSelected: @escaping (EnginePeer, EnginePeer?, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, togglePeerSelected: @escaping (EnginePeer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (EnginePeer, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (EngineChatList.Group) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, setItemPinned: @escaping (EngineChatList.PinnedItem.Id, Bool) -> Void, setPeerMuted: @escaping (EnginePeer.Id, Bool) -> Void, deletePeer: @escaping (EnginePeer.Id, Bool) -> Void, updatePeerGrouping: @escaping (EnginePeer.Id, Bool) -> Void, togglePeerMarkedUnread: @escaping (EnginePeer.Id, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (EnginePeer.Id) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?) -> Void, present: @escaping (ViewController) -> Void) {
|
||||
public init(context: AccountContext, activateSearch: @escaping () -> Void, peerSelected: @escaping (EnginePeer, EnginePeer?, ChatListNodeEntryPromoInfo?) -> Void, disabledPeerSelected: @escaping (EnginePeer) -> Void, togglePeerSelected: @escaping (EnginePeer) -> Void, togglePeersSelection: @escaping ([PeerEntry], Bool) -> Void, additionalCategorySelected: @escaping (Int) -> Void, messageSelected: @escaping (EnginePeer, EngineMessage, ChatListNodeEntryPromoInfo?) -> Void, groupSelected: @escaping (EngineChatList.Group) -> Void, addContact: @escaping (String) -> Void, setPeerIdWithRevealedOptions: @escaping (EnginePeer.Id?, EnginePeer.Id?) -> Void, setItemPinned: @escaping (EngineChatList.PinnedItem.Id, Bool) -> Void, setPeerMuted: @escaping (EnginePeer.Id, Bool) -> Void, deletePeer: @escaping (EnginePeer.Id, Bool) -> Void, updatePeerGrouping: @escaping (EnginePeer.Id, Bool) -> Void, togglePeerMarkedUnread: @escaping (EnginePeer.Id, Bool) -> Void, toggleArchivedFolderHiddenByDefault: @escaping () -> Void, hidePsa: @escaping (EnginePeer.Id) -> Void, activateChatPreview: @escaping (ChatListItem, ASDisplayNode, ContextGesture?, CGPoint?) -> Void, present: @escaping (ViewController) -> Void) {
|
||||
self.activateSearch = activateSearch
|
||||
self.peerSelected = peerSelected
|
||||
self.disabledPeerSelected = disabledPeerSelected
|
||||
@ -631,7 +631,7 @@ public final class ChatListNode: ListView {
|
||||
public var push: ((ViewController) -> Void)?
|
||||
public var toggleArchivedFolderHiddenByDefault: (() -> Void)?
|
||||
public var hidePsa: ((EnginePeer.Id) -> Void)?
|
||||
public var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
public var activateChatPreview: ((ChatListItem, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
|
||||
private var theme: PresentationTheme
|
||||
|
||||
@ -917,12 +917,12 @@ public final class ChatListNode: ListView {
|
||||
self?.toggleArchivedFolderHiddenByDefault?()
|
||||
}, hidePsa: { [weak self] id in
|
||||
self?.hidePsa?(id)
|
||||
}, activateChatPreview: { [weak self] item, node, gesture in
|
||||
}, activateChatPreview: { [weak self] item, node, gesture, location in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let activateChatPreview = strongSelf.activateChatPreview {
|
||||
activateChatPreview(item, node, gesture)
|
||||
activateChatPreview(item, node, gesture, location)
|
||||
} else {
|
||||
gesture?.cancel()
|
||||
}
|
||||
|
||||
@ -99,11 +99,11 @@ private final class ContactListNodeInteraction {
|
||||
fileprivate let authorize: () -> Void
|
||||
fileprivate let suppressWarning: () -> Void
|
||||
fileprivate let openPeer: (ContactListPeer, ContactListAction) -> Void
|
||||
fileprivate let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
fileprivate let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
|
||||
let itemHighlighting = ContactItemHighlighting()
|
||||
|
||||
init(activateSearch: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer, ContactListAction) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?) {
|
||||
init(activateSearch: @escaping () -> Void, authorize: @escaping () -> Void, suppressWarning: @escaping () -> Void, openPeer: @escaping (ContactListPeer, ContactListAction) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) {
|
||||
self.activateSearch = activateSearch
|
||||
self.authorize = authorize
|
||||
self.suppressWarning = suppressWarning
|
||||
@ -211,13 +211,13 @@ private enum ContactListNodeEntry: Comparable, Identifiable {
|
||||
if isSearch {
|
||||
status = .none
|
||||
}
|
||||
var itemContextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
var itemContextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
if isContextActionEnabled, let contextAction = interaction.contextAction {
|
||||
itemContextAction = { node, gesture in
|
||||
itemContextAction = { node, gesture, location in
|
||||
switch itemPeer {
|
||||
case let .peer(peer, _):
|
||||
if let peer = peer {
|
||||
contextAction(peer, node, gesture)
|
||||
contextAction(peer, node, gesture, location)
|
||||
}
|
||||
case .deviceContact:
|
||||
break
|
||||
@ -866,7 +866,7 @@ public final class ContactListNode: ASDisplayNode {
|
||||
public var openPeer: ((ContactListPeer, ContactListAction) -> Void)?
|
||||
public var openPrivacyPolicy: (() -> Void)?
|
||||
public var suppressPermissionWarning: (() -> Void)?
|
||||
private let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
private let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
|
||||
private let previousEntries = Atomic<[ContactListNodeEntry]?>(value: nil)
|
||||
private let disposable = MetaDisposable()
|
||||
@ -880,7 +880,7 @@ public final class ContactListNode: ASDisplayNode {
|
||||
|
||||
public var multipleSelection = false
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, presentation: Signal<ContactListPresentation, NoError>, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, displayCallIcons: Bool = false, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)? = nil, isSearch: Bool = false, multipleSelection: Bool = false) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, presentation: Signal<ContactListPresentation, NoError>, filters: [ContactListFilter] = [.excludeSelf], selectionState: ContactListNodeGroupSelectionState? = nil, displayPermissionPlaceholder: Bool = true, displaySortOptions: Bool = false, displayCallIcons: Bool = false, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil, isSearch: Bool = false, multipleSelection: Bool = false) {
|
||||
self.context = context
|
||||
self.filters = filters
|
||||
self.displayPermissionPlaceholder = displayPermissionPlaceholder
|
||||
|
||||
@ -88,10 +88,10 @@ final class ContactsControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
var contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
var contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
|
||||
self.contactListNode = ContactListNode(context: context, presentation: presentation, displaySortOptions: true, contextAction: { peer, node, gesture in
|
||||
contextAction?(peer, node, gesture)
|
||||
self.contactListNode = ContactListNode(context: context, presentation: presentation, displaySortOptions: true, contextAction: { peer, node, gesture, location in
|
||||
contextAction?(peer, node, gesture, location)
|
||||
})
|
||||
|
||||
super.init()
|
||||
@ -130,8 +130,8 @@ final class ContactsControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
contextAction = { [weak self] peer, node, gesture in
|
||||
self?.contextAction(peer: peer, node: node, gesture: gesture)
|
||||
contextAction = { [weak self] peer, node, gesture, location in
|
||||
self?.contextAction(peer: peer, node: node, gesture: gesture, location: location)
|
||||
}
|
||||
}
|
||||
|
||||
@ -170,7 +170,7 @@ final class ContactsControllerNode: ASDisplayNode {
|
||||
self.contactListNode.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
}
|
||||
|
||||
private func contextAction(peer: EnginePeer, node: ASDisplayNode, gesture: ContextGesture?) {
|
||||
private func contextAction(peer: EnginePeer, node: ASDisplayNode?, gesture: ContextGesture?, location: CGPoint?) {
|
||||
guard let contactsController = self.controller else {
|
||||
return
|
||||
}
|
||||
@ -193,8 +193,8 @@ final class ContactsControllerNode: ASDisplayNode {
|
||||
if let requestOpenPeerFromSearch = self?.requestOpenPeerFromSearch {
|
||||
requestOpenPeerFromSearch(peer)
|
||||
}
|
||||
}, contextAction: { [weak self] peer, node, gesture in
|
||||
self?.contextAction(peer: peer, node: node, gesture: gesture)
|
||||
}, contextAction: { [weak self] peer, node, gesture, location in
|
||||
self?.contextAction(peer: peer, node: node, gesture: gesture, location: location)
|
||||
}), cancel: { [weak self] in
|
||||
if let requestDeactivateSearch = self?.requestDeactivateSearch {
|
||||
requestDeactivateSearch()
|
||||
|
||||
@ -121,7 +121,7 @@ private enum ContactListSearchEntry: Comparable, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
func item(context: AccountContext, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?) -> ListViewItem {
|
||||
func item(context: AccountContext, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) -> ListViewItem {
|
||||
switch self {
|
||||
case let .addContact(theme, strings, phoneNumber):
|
||||
return ContactsAddItem(theme: theme, strings: strings, phoneNumber: phoneNumber, header: ChatListSearchItemHeader(type: .phoneNumber, theme: theme, strings: strings, actionTitle: nil, action: nil), action: {
|
||||
@ -162,8 +162,8 @@ private enum ContactListSearchEntry: Comparable, Identifiable {
|
||||
openPeer(peer)
|
||||
}, contextAction: contextAction.flatMap { contextAction in
|
||||
return nativePeer.flatMap { nativePeer in
|
||||
return { node, gesture in
|
||||
contextAction(nativePeer, node, gesture)
|
||||
return { node, gesture, location in
|
||||
contextAction(nativePeer, node, gesture, location)
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -180,7 +180,7 @@ struct ContactListSearchContainerTransition {
|
||||
let query: String
|
||||
}
|
||||
|
||||
private func contactListSearchContainerPreparedRecentTransition(from fromEntries: [ContactListSearchEntry], to toEntries: [ContactListSearchEntry], isSearching: Bool, emptyResults: Bool, query: String, context: AccountContext, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?) -> ContactListSearchContainerTransition {
|
||||
private func contactListSearchContainerPreparedRecentTransition(from fromEntries: [ContactListSearchEntry], to toEntries: [ContactListSearchEntry], isSearching: Bool, emptyResults: Bool, query: String, context: AccountContext, presentationData: PresentationData, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, timeFormat: PresentationDateTimeFormat, addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) -> ContactListSearchContainerTransition {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries)
|
||||
|
||||
let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) }
|
||||
@ -206,7 +206,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
private let context: AccountContext
|
||||
private let addContact: ((String) -> Void)?
|
||||
private let openPeer: (ContactListPeer) -> Void
|
||||
private let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
private let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
|
||||
private let dimNode: ASDisplayNode
|
||||
public let listNode: ListView
|
||||
@ -229,7 +229,7 @@ public final class ContactsSearchContainerNode: SearchDisplayControllerContentNo
|
||||
return true
|
||||
}
|
||||
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, onlyWriteable: Bool, categories: ContactsSearchCategories, filters: [ContactListFilter] = [.excludeSelf], addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?) {
|
||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, onlyWriteable: Bool, categories: ContactsSearchCategories, filters: [ContactListFilter] = [.excludeSelf], addContact: ((String) -> Void)?, openPeer: @escaping (ContactListPeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?) {
|
||||
self.context = context
|
||||
self.addContact = addContact
|
||||
self.openPeer = openPeer
|
||||
|
||||
@ -148,7 +148,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
|
||||
let setPeerIdWithRevealedOptions: ((EnginePeer.Id?, EnginePeer.Id?) -> Void)?
|
||||
let deletePeer: ((EnginePeer.Id) -> Void)?
|
||||
let itemHighlighting: ContactItemHighlighting?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
let contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
let arrowAction: (() -> Void)?
|
||||
|
||||
public let selectable: Bool
|
||||
@ -157,7 +157,7 @@ public class ContactsPeerItem: ItemListItem, ListViewItemWithHeader {
|
||||
|
||||
public let header: ListViewItemHeader?
|
||||
|
||||
public init(presentationData: ItemListPresentationData, style: ItemListStyle = .plain, sectionId: ItemListSectionId = 0, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, context: AccountContext, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, selectionPosition: ContactsPeerItemSelectionPosition = .right, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], additionalActions: [ContactsPeerItemAction] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: SortIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, disabledAction: ((ContactsPeerItemPeer) -> Void)? = nil, setPeerIdWithRevealedOptions: ((EnginePeer.Id?, EnginePeer.Id?) -> Void)? = nil, deletePeer: ((EnginePeer.Id) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil, contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? = nil, arrowAction: (() -> Void)? = nil) {
|
||||
public init(presentationData: ItemListPresentationData, style: ItemListStyle = .plain, sectionId: ItemListSectionId = 0, sortOrder: PresentationPersonNameOrder, displayOrder: PresentationPersonNameOrder, context: AccountContext, peerMode: ContactsPeerItemPeerMode, peer: ContactsPeerItemPeer, status: ContactsPeerItemStatus, badge: ContactsPeerItemBadge? = nil, enabled: Bool, selection: ContactsPeerItemSelection, selectionPosition: ContactsPeerItemSelectionPosition = .right, editing: ContactsPeerItemEditing, options: [ItemListPeerItemRevealOption] = [], additionalActions: [ContactsPeerItemAction] = [], actionIcon: ContactsPeerItemActionIcon = .none, index: SortIndex?, header: ListViewItemHeader?, action: @escaping (ContactsPeerItemPeer) -> Void, disabledAction: ((ContactsPeerItemPeer) -> Void)? = nil, setPeerIdWithRevealedOptions: ((EnginePeer.Id?, EnginePeer.Id?) -> Void)? = nil, deletePeer: ((EnginePeer.Id) -> Void)? = nil, itemHighlighting: ContactItemHighlighting? = nil, contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? = nil, arrowAction: (() -> Void)? = nil) {
|
||||
self.presentationData = presentationData
|
||||
self.style = style
|
||||
self.sectionId = sectionId
|
||||
@ -427,7 +427,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
gesture.cancel()
|
||||
return
|
||||
}
|
||||
contextAction(strongSelf.containerNode, gesture)
|
||||
contextAction(strongSelf.containerNode, gesture, nil)
|
||||
}
|
||||
|
||||
self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in
|
||||
@ -453,6 +453,13 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
}
|
||||
|
||||
public override func secondaryAction(at point: CGPoint) {
|
||||
guard let item = self.item, let contextAction = item.contextAction else {
|
||||
return
|
||||
}
|
||||
contextAction(self.containerNode, nil, point)
|
||||
}
|
||||
|
||||
override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) {
|
||||
if let (item, _, _, _, _, _) = self.layoutParams {
|
||||
let (first, last, firstWithHeader) = ContactsPeerItem.mergeType(item: item, previousItem: previousItem, nextItem: nextItem)
|
||||
|
||||
@ -193,7 +193,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
fileprivate var dismissedForCancel: (() -> Void)?
|
||||
private let getController: () -> ContextControllerProtocol?
|
||||
private weak var gesture: ContextGesture?
|
||||
|
||||
|
||||
private var didSetItemsReady = false
|
||||
let itemsReady = Promise<Bool>()
|
||||
let contentReady = Promise<Bool>()
|
||||
@ -371,7 +371,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if strongSelf.didMoveFromInitialGesturePoint {
|
||||
if let presentationNode = strongSelf.presentationNode {
|
||||
let presentationPoint = strongSelf.view.convert(localPoint, to: presentationNode.view)
|
||||
presentationNode.highlightGestureMoved(location: presentationPoint)
|
||||
presentationNode.highlightGestureMoved(location: presentationPoint, hover: false)
|
||||
} else {
|
||||
let actionPoint = strongSelf.view.convert(localPoint, to: strongSelf.actionsContainerNode.view)
|
||||
let actionNode = strongSelf.actionsContainerNode.actionNode(at: actionPoint)
|
||||
@ -445,7 +445,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
if strongSelf.didMoveFromInitialGesturePoint {
|
||||
if let presentationNode = strongSelf.presentationNode {
|
||||
let presentationPoint = strongSelf.view.convert(localPoint, to: presentationNode.view)
|
||||
presentationNode.highlightGestureMoved(location: presentationPoint)
|
||||
presentationNode.highlightGestureMoved(location: presentationPoint, hover: false)
|
||||
} else {
|
||||
let actionPoint = strongSelf.view.convert(localPoint, to: strongSelf.actionsContainerNode.view)
|
||||
var actionNode = strongSelf.actionsContainerNode.actionNode(at: actionPoint)
|
||||
@ -504,7 +504,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
|
||||
switch source {
|
||||
case .reference, .extracted:
|
||||
case .location, .reference, .extracted:
|
||||
self.contentReady.set(.single(true))
|
||||
case let .controller(source):
|
||||
self.contentReady.set(source.controller.ready.get())
|
||||
@ -538,6 +538,10 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
super.didLoad()
|
||||
|
||||
self.dismissNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTapped)))
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
self.view.addGestureRecognizer(UIHoverGestureRecognizer(target: self, action: #selector(self.hoverGesture(_:))))
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dimNodeTapped() {
|
||||
@ -548,8 +552,93 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
self.beginDismiss(.default)
|
||||
}
|
||||
|
||||
@available(iOS 13.0, *)
|
||||
@objc private func hoverGesture(_ gestureRecognizer: UIHoverGestureRecognizer) {
|
||||
guard self.didCompleteAnimationIn else {
|
||||
return
|
||||
}
|
||||
|
||||
let localPoint = gestureRecognizer.location(in: self.view)
|
||||
|
||||
switch gestureRecognizer.state {
|
||||
case .changed:
|
||||
if let presentationNode = self.presentationNode {
|
||||
let presentationPoint = self.view.convert(localPoint, to: presentationNode.view)
|
||||
presentationNode.highlightGestureMoved(location: presentationPoint, hover: true)
|
||||
} else {
|
||||
let actionPoint = self.view.convert(localPoint, to: self.actionsContainerNode.view)
|
||||
let actionNode = self.actionsContainerNode.actionNode(at: actionPoint)
|
||||
if self.highlightedActionNode !== actionNode {
|
||||
self.highlightedActionNode?.setIsHighlighted(false)
|
||||
self.highlightedActionNode = actionNode
|
||||
if let actionNode = actionNode {
|
||||
actionNode.setIsHighlighted(true)
|
||||
}
|
||||
}
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
let reactionPoint = self.view.convert(localPoint, to: reactionContextNode.view)
|
||||
let highlightedReaction = reactionContextNode.reaction(at: reactionPoint)?.reaction
|
||||
if self.highlightedReaction?.rawValue != highlightedReaction?.rawValue {
|
||||
self.highlightedReaction = highlightedReaction
|
||||
self.hapticFeedback.tap()
|
||||
}
|
||||
}
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
if let presentationNode = self.presentationNode {
|
||||
presentationNode.highlightGestureMoved(location: CGPoint(x: -1, y: -1), hover: true)
|
||||
} else {
|
||||
if let highlightedActionNode = self.highlightedActionNode {
|
||||
self.highlightedActionNode = nil
|
||||
highlightedActionNode.setIsHighlighted(false)
|
||||
}
|
||||
|
||||
if let _ = self.reactionContextNode {
|
||||
self.highlightedReaction = nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func initializeContent() {
|
||||
switch self.source {
|
||||
case let .location(source):
|
||||
let presentationNode = ContextControllerExtractedPresentationNode(
|
||||
getController: { [weak self] in
|
||||
return self?.getController()
|
||||
},
|
||||
requestUpdate: { [weak self] transition in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if let validLayout = strongSelf.validLayout {
|
||||
strongSelf.updateLayout(
|
||||
layout: validLayout,
|
||||
transition: transition,
|
||||
previousActionsContainerNode: nil
|
||||
)
|
||||
}
|
||||
},
|
||||
requestDismiss: { [weak self] result in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.dismissedForCancel?()
|
||||
strongSelf.beginDismiss(result)
|
||||
},
|
||||
requestAnimateOut: { [weak self] result, completion in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.animateOut(result: result, completion: completion)
|
||||
},
|
||||
source: .location(source)
|
||||
)
|
||||
self.presentationNode = presentationNode
|
||||
self.addSubnode(presentationNode)
|
||||
case let .reference(source):
|
||||
if let controller = self.getController() as? ContextController, controller.workaroundUseLegacyImplementation {
|
||||
let transitionInfo = source.transitionInfo()
|
||||
@ -701,7 +790,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
|
||||
switch self.source {
|
||||
case .reference:
|
||||
case .location, .reference:
|
||||
break
|
||||
case .extracted:
|
||||
if let contentAreaInScreenSpace = self.contentAreaInScreenSpace, let maybeContentNode = self.contentContainerNode.contentNode, case .extracted = maybeContentNode {
|
||||
@ -900,6 +989,53 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
var result = initialResult
|
||||
|
||||
switch self.source {
|
||||
case let .location(source):
|
||||
let transitionInfo = source.transitionInfo()
|
||||
if transitionInfo == nil {
|
||||
result = .dismissWithoutContent
|
||||
}
|
||||
|
||||
switch result {
|
||||
case let .custom(value):
|
||||
switch value {
|
||||
case let .animated(duration, curve):
|
||||
transitionDuration = duration
|
||||
transitionCurve = curve
|
||||
default:
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
self.isUserInteractionEnabled = false
|
||||
self.isAnimatingOut = true
|
||||
|
||||
self.scrollNode.view.setContentOffset(self.scrollNode.view.contentOffset, animated: false)
|
||||
|
||||
if !self.dimNode.isHidden {
|
||||
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
} else {
|
||||
self.withoutBlurDimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: transitionDuration * animationDurationFactor, removeOnCompletion: false)
|
||||
}
|
||||
|
||||
self.actionsContainerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15 * animationDurationFactor, removeOnCompletion: false, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.actionsContainerNode.layer.animateScale(from: 1.0, to: 0.1, duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false)
|
||||
|
||||
let animateOutToItem: Bool
|
||||
switch result {
|
||||
case .default, .custom:
|
||||
animateOutToItem = true
|
||||
case .dismissWithoutContent:
|
||||
animateOutToItem = false
|
||||
}
|
||||
|
||||
if animateOutToItem, let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
|
||||
let localSourceFrame = self.view.convert(originalProjectedContentViewFrame.1, to: self.scrollNode.view)
|
||||
self.actionsContainerNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y), duration: transitionDuration * animationDurationFactor, timingFunction: transitionCurve.timingFunction, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
case let .reference(source):
|
||||
guard let maybeContentNode = self.contentContainerNode.contentNode, case let .reference(referenceView) = maybeContentNode else {
|
||||
return
|
||||
@ -2071,6 +2207,30 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextControllerLocationViewInfo {
|
||||
public let location: CGPoint
|
||||
public let contentAreaInScreenSpace: CGRect
|
||||
public let insets: UIEdgeInsets
|
||||
|
||||
public init(location: CGPoint, contentAreaInScreenSpace: CGRect, insets: UIEdgeInsets = UIEdgeInsets()) {
|
||||
self.location = location
|
||||
self.contentAreaInScreenSpace = contentAreaInScreenSpace
|
||||
self.insets = insets
|
||||
}
|
||||
}
|
||||
|
||||
public protocol ContextLocationContentSource: AnyObject {
|
||||
var shouldBeDismissed: Signal<Bool, NoError> { get }
|
||||
|
||||
func transitionInfo() -> ContextControllerLocationViewInfo?
|
||||
}
|
||||
|
||||
public extension ContextLocationContentSource {
|
||||
var shouldBeDismissed: Signal<Bool, NoError> {
|
||||
return .single(false)
|
||||
}
|
||||
}
|
||||
|
||||
public final class ContextControllerReferenceViewInfo {
|
||||
public let referenceView: UIView
|
||||
public let contentAreaInScreenSpace: CGRect
|
||||
@ -2171,6 +2331,7 @@ public protocol ContextControllerContentSource: AnyObject {
|
||||
}
|
||||
|
||||
public enum ContextContentSource {
|
||||
case location(ContextLocationContentSource)
|
||||
case reference(ContextReferenceContentSource)
|
||||
case extracted(ContextExtractedContentSource)
|
||||
case controller(ContextControllerContentSource)
|
||||
@ -2297,6 +2458,18 @@ public final class ContextController: ViewController, StandalonePresentableContr
|
||||
super.init(navigationBarPresentationData: nil)
|
||||
|
||||
switch source {
|
||||
case let .location(locationSource):
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
self.shouldBeDismissedDisposable = (locationSource.shouldBeDismissed
|
||||
|> filter { $0 }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.dismiss(result: .default, completion: {})
|
||||
})
|
||||
case let .reference(referenceSource):
|
||||
self.statusBar.statusBarStyle = .Ignore
|
||||
|
||||
|
||||
@ -111,6 +111,7 @@ private extension ContextControllerTakeViewInfo.ContainingItem {
|
||||
|
||||
final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextControllerPresentationNode, UIScrollViewDelegate {
|
||||
enum ContentSource {
|
||||
case location(ContextLocationContentSource)
|
||||
case reference(ContextReferenceContentSource)
|
||||
case extracted(ContextExtractedContentSource)
|
||||
}
|
||||
@ -298,11 +299,11 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
}
|
||||
|
||||
func highlightGestureMoved(location: CGPoint) {
|
||||
func highlightGestureMoved(location: CGPoint, hover: Bool) {
|
||||
self.actionsStackNode.highlightGestureMoved(location: self.view.convert(location, to: self.actionsStackNode.view))
|
||||
|
||||
if let reactionContextNode = self.reactionContextNode {
|
||||
reactionContextNode.highlightGestureMoved(location: self.view.convert(location, to: reactionContextNode.view))
|
||||
reactionContextNode.highlightGestureMoved(location: self.view.convert(location, to: reactionContextNode.view), hover: hover)
|
||||
}
|
||||
}
|
||||
|
||||
@ -337,7 +338,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
|
||||
private func getActionsStackPositionLock() -> CGFloat? {
|
||||
switch self.source {
|
||||
case .reference:
|
||||
case .location, .reference:
|
||||
return nil
|
||||
case .extracted:
|
||||
return self.actionsStackNode.view.convert(CGPoint(), to: self.view).y
|
||||
@ -366,7 +367,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
}
|
||||
|
||||
switch self.source {
|
||||
case .reference:
|
||||
case .location, .reference:
|
||||
self.backgroundNode.updateColor(
|
||||
color: .clear,
|
||||
enableBlur: false,
|
||||
@ -396,7 +397,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
contentNode = current
|
||||
} else {
|
||||
switch self.source {
|
||||
case .reference:
|
||||
case .location, .reference:
|
||||
contentNode = nil
|
||||
case let .extracted(source):
|
||||
guard let takeInfo = source.takeView() else {
|
||||
@ -453,6 +454,13 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
var contentRect: CGRect
|
||||
|
||||
switch self.source {
|
||||
case let .location(location):
|
||||
if let transitionInfo = location.transitionInfo() {
|
||||
contentRect = CGRect(origin: transitionInfo.location, size: CGSize(width: 1.0, height: 1.0))
|
||||
contentParentGlobalFrame = CGRect(origin: CGPoint(x: 0.0, y: contentRect.minX), size: CGSize(width: layout.size.width, height: contentRect.height))
|
||||
} else {
|
||||
return
|
||||
}
|
||||
case let .reference(reference):
|
||||
if let transitionInfo = reference.transitionInfo() {
|
||||
contentRect = convertFrame(transitionInfo.referenceView.bounds, from: transitionInfo.referenceView, to: self.view).insetBy(dx: -2.0, dy: 0.0)
|
||||
@ -478,7 +486,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
let keepInPlace: Bool
|
||||
let centerActionsHorizontally: Bool
|
||||
switch self.source {
|
||||
case .reference:
|
||||
case .location, .reference:
|
||||
keepInPlace = true
|
||||
centerActionsHorizontally = false
|
||||
case let .extracted(source):
|
||||
@ -505,7 +513,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
|
||||
let actionsStackPresentation: ContextControllerActionsStackNode.Presentation
|
||||
switch self.source {
|
||||
case .reference:
|
||||
case .location, .reference:
|
||||
actionsStackPresentation = .inline
|
||||
case .extracted:
|
||||
actionsStackPresentation = .modal
|
||||
@ -578,11 +586,13 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
actionsFrame.origin.x = actionsEdgeInset
|
||||
}
|
||||
} else {
|
||||
if contentRect.midX < layout.size.width / 2.0 {
|
||||
if case .location = self.source {
|
||||
actionsFrame.origin.x = contentParentGlobalFrame.minX + contentRect.minX + actionsSideInset - 4.0
|
||||
} else if contentRect.midX < layout.size.width / 2.0 {
|
||||
actionsFrame.origin.x = contentParentGlobalFrame.minX + contentRect.minX + actionsSideInset - 4.0
|
||||
} else {
|
||||
switch self.source {
|
||||
case .reference:
|
||||
case .location, .reference:
|
||||
actionsFrame.origin.x = floor(contentParentGlobalFrame.minX + contentRect.midX - actionsFrame.width / 2.0)
|
||||
if actionsFrame.maxX > layout.size.width - actionsEdgeInset {
|
||||
actionsFrame.origin.x = layout.size.width - actionsEdgeInset - actionsFrame.width
|
||||
@ -794,6 +804,15 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo
|
||||
let currentContentScreenFrame: CGRect
|
||||
|
||||
switch self.source {
|
||||
case let .location(location):
|
||||
if let putBackInfo = location.transitionInfo() {
|
||||
self.clippingNode.layer.animateFrame(from: CGRect(origin: CGPoint(), size: layout.size), to: CGRect(origin: CGPoint(x: 0.0, y: putBackInfo.contentAreaInScreenSpace.minY), size: CGSize(width: layout.size.width, height: putBackInfo.contentAreaInScreenSpace.height)), duration: duration, timingFunction: timingFunction, removeOnCompletion: false)
|
||||
self.clippingNode.layer.animateBoundsOriginYAdditive(from: 0.0, to: putBackInfo.contentAreaInScreenSpace.minY, duration: duration, timingFunction: timingFunction, removeOnCompletion: false)
|
||||
|
||||
currentContentScreenFrame = CGRect(origin: putBackInfo.location, size: CGSize(width: 1.0, height: 1.0))
|
||||
} else {
|
||||
return
|
||||
}
|
||||
case let .reference(source):
|
||||
if let putBackInfo = source.transitionInfo() {
|
||||
self.clippingNode.layer.animateFrame(from: CGRect(origin: CGPoint(), size: layout.size), to: CGRect(origin: CGPoint(x: 0.0, y: putBackInfo.contentAreaInScreenSpace.minY), size: CGSize(width: layout.size.width, height: putBackInfo.contentAreaInScreenSpace.height)), duration: duration, timingFunction: timingFunction, removeOnCompletion: false)
|
||||
|
||||
@ -28,7 +28,7 @@ protocol ContextControllerPresentationNode: ASDisplayNode {
|
||||
func animateOutToReaction(value: String, targetView: UIView, hideNode: Bool, animateTargetContainer: UIView?, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, completion: @escaping () -> Void)
|
||||
func cancelReactionAnimation()
|
||||
|
||||
func highlightGestureMoved(location: CGPoint)
|
||||
func highlightGestureMoved(location: CGPoint, hover: Bool)
|
||||
func highlightGestureFinished(performAction: Bool)
|
||||
|
||||
func addRelativeContentOffset(_ offset: CGPoint, transition: ContainedViewLayoutTransition)
|
||||
|
||||
@ -4623,38 +4623,61 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
|
||||
override open func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
|
||||
let isSecondary: Bool
|
||||
if #available(iOS 13.4, *) {
|
||||
isSecondary = event?.buttonMask == .secondary
|
||||
} else {
|
||||
isSecondary = false
|
||||
}
|
||||
|
||||
if let selectionTouchLocation = self.selectionTouchLocation {
|
||||
let index = self.itemIndexAtPoint(selectionTouchLocation)
|
||||
if index != self.highlightedItemIndex {
|
||||
self.clearHighlightAnimated(false)
|
||||
}
|
||||
|
||||
if let index = index {
|
||||
if self.items[index].selectable {
|
||||
self.highlightedItemIndex = index
|
||||
for itemNode in self.itemNodes {
|
||||
if itemNode.index == index {
|
||||
if itemNode.canBeSelected {
|
||||
if !itemNode.isLayerBacked {
|
||||
self.reorderItemNodeToFront(itemNode)
|
||||
for (_, headerNode) in self.itemHeaderNodes {
|
||||
self.reorderHeaderNodeToFront(headerNode)
|
||||
}
|
||||
}
|
||||
let itemNodeFrame = itemNode.frame
|
||||
itemNode.setHighlighted(true, at: selectionTouchLocation.offsetBy(dx: -itemNodeFrame.minX, dy: -itemNodeFrame.minY), animated: false)
|
||||
} else {
|
||||
self.highlightedItemIndex = nil
|
||||
itemNode.tapped()
|
||||
if isSecondary {
|
||||
if let index = index {
|
||||
if self.items[index].selectable {
|
||||
self.highlightedItemIndex = index
|
||||
for itemNode in self.itemNodes {
|
||||
if itemNode.index == index {
|
||||
itemNode.secondaryAction(at: selectionTouchLocation)
|
||||
self.items[index].performSecondaryAction(listView: self)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if index != self.highlightedItemIndex {
|
||||
self.clearHighlightAnimated(false)
|
||||
}
|
||||
|
||||
if let index = index {
|
||||
if self.items[index].selectable {
|
||||
self.highlightedItemIndex = index
|
||||
for itemNode in self.itemNodes {
|
||||
if itemNode.index == index {
|
||||
if itemNode.canBeSelected {
|
||||
if !itemNode.isLayerBacked {
|
||||
self.reorderItemNodeToFront(itemNode)
|
||||
for (_, headerNode) in self.itemHeaderNodes {
|
||||
self.reorderHeaderNodeToFront(headerNode)
|
||||
}
|
||||
}
|
||||
let itemNodeFrame = itemNode.frame
|
||||
itemNode.setHighlighted(true, at: selectionTouchLocation.offsetBy(dx: -itemNodeFrame.minX, dy: -itemNodeFrame.minY), animated: false)
|
||||
} else {
|
||||
self.highlightedItemIndex = nil
|
||||
itemNode.tapped()
|
||||
}
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let highlightedItemIndex = self.highlightedItemIndex {
|
||||
|
||||
if !isSecondary, let highlightedItemIndex = self.highlightedItemIndex {
|
||||
for itemNode in self.itemNodes {
|
||||
if itemNode.index == highlightedItemIndex {
|
||||
itemNode.selected()
|
||||
|
||||
@ -96,4 +96,7 @@ public extension ListViewItem {
|
||||
|
||||
func selected(listView: ListView) {
|
||||
}
|
||||
|
||||
func performSecondaryAction(listView: ListView) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,6 +586,9 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
|
||||
open func selected() {
|
||||
}
|
||||
|
||||
open func secondaryAction(at point: CGPoint) {
|
||||
}
|
||||
|
||||
open func isReorderable(at point: CGPoint) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -60,6 +60,7 @@ public enum TapLongTapOrDoubleTapGesture {
|
||||
case tap
|
||||
case doubleTap
|
||||
case longTap
|
||||
case secondaryTap
|
||||
case hold
|
||||
}
|
||||
|
||||
@ -81,6 +82,8 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer,
|
||||
|
||||
public var tapActionAtPoint: ((CGPoint) -> TapLongTapOrDoubleTapGestureRecognizerAction)?
|
||||
public var longTap: ((CGPoint, TapLongTapOrDoubleTapGestureRecognizer) -> Void)?
|
||||
public var secondaryTap: ((CGPoint, TapLongTapOrDoubleTapGestureRecognizer) -> Void)?
|
||||
|
||||
private var recognizedLongTap: Bool = false
|
||||
public var externalUpdated: ((UIView?, CGPoint) -> Void)?
|
||||
public var externalEnded: (((UIView?, CGPoint)?) -> Void)?
|
||||
@ -178,10 +181,17 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer,
|
||||
|
||||
self.touchCount += touches.count
|
||||
|
||||
let isSecondary: Bool
|
||||
if #available(iOS 13.4, *) {
|
||||
isSecondary = event.buttonMask == .secondary
|
||||
} else {
|
||||
isSecondary = false
|
||||
}
|
||||
|
||||
if let touch = touches.first {
|
||||
let touchLocation = touch.location(in: self.view)
|
||||
|
||||
if self.highlightPoint != touchLocation {
|
||||
if !isSecondary, self.highlightPoint != touchLocation {
|
||||
self.highlightPoint = touchLocation
|
||||
self.highlight?(touchLocation)
|
||||
}
|
||||
@ -261,6 +271,13 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer,
|
||||
|
||||
self.touchCount -= touches.count
|
||||
|
||||
let isSecondary: Bool
|
||||
if #available(iOS 13.4, *) {
|
||||
isSecondary = event.buttonMask == .secondary
|
||||
} else {
|
||||
isSecondary = false
|
||||
}
|
||||
|
||||
if self.highlightPoint != nil {
|
||||
self.highlightPoint = nil
|
||||
self.highlight?(nil)
|
||||
@ -281,32 +298,37 @@ public final class TapLongTapOrDoubleTapGestureRecognizer: UIGestureRecognizer,
|
||||
}
|
||||
|
||||
if self.tapCount == 1 {
|
||||
var tapAction: TapLongTapOrDoubleTapGestureRecognizerAction = .waitForDoubleTap
|
||||
if let tapActionAtPoint = self.tapActionAtPoint, let (touchLocation, _) = self.touchLocationAndTimestamp {
|
||||
tapAction = tapActionAtPoint(touchLocation)
|
||||
}
|
||||
|
||||
switch tapAction {
|
||||
case .waitForSingleTap, .keepWithSingleTap:
|
||||
if let (touchLocation, _) = self.touchLocationAndTimestamp {
|
||||
self.lastRecognizedGestureAndLocation = (.tap, touchLocation)
|
||||
}
|
||||
self.state = .ended
|
||||
case .waitForDoubleTap:
|
||||
self.state = .began
|
||||
let timer = Timer(timeInterval: 0.16, target: TapLongTapOrDoubleTapGestureRecognizerTimerTarget(target: self), selector: #selector(TapLongTapOrDoubleTapGestureRecognizerTimerTarget.tapEvent), userInfo: nil, repeats: false)
|
||||
self.timer = timer
|
||||
RunLoop.main.add(timer, forMode: .common)
|
||||
case let .waitForHold(_, acceptTap):
|
||||
if let (touchLocation, _) = self.touchLocationAndTimestamp, acceptTap {
|
||||
if self.state != .began {
|
||||
if isSecondary, let (touchLocation, _) = self.touchLocationAndTimestamp {
|
||||
self.secondaryTap?(touchLocation, self)
|
||||
self.state = .ended
|
||||
} else {
|
||||
var tapAction: TapLongTapOrDoubleTapGestureRecognizerAction = .waitForDoubleTap
|
||||
if let tapActionAtPoint = self.tapActionAtPoint, let (touchLocation, _) = self.touchLocationAndTimestamp {
|
||||
tapAction = tapActionAtPoint(touchLocation)
|
||||
}
|
||||
|
||||
switch tapAction {
|
||||
case .waitForSingleTap, .keepWithSingleTap:
|
||||
if let (touchLocation, _) = self.touchLocationAndTimestamp {
|
||||
self.lastRecognizedGestureAndLocation = (.tap, touchLocation)
|
||||
self.state = .began
|
||||
}
|
||||
}
|
||||
self.state = .ended
|
||||
case .fail:
|
||||
self.state = .failed
|
||||
self.state = .ended
|
||||
case .waitForDoubleTap:
|
||||
self.state = .began
|
||||
let timer = Timer(timeInterval: 0.16, target: TapLongTapOrDoubleTapGestureRecognizerTimerTarget(target: self), selector: #selector(TapLongTapOrDoubleTapGestureRecognizerTimerTarget.tapEvent), userInfo: nil, repeats: false)
|
||||
self.timer = timer
|
||||
RunLoop.main.add(timer, forMode: .common)
|
||||
case let .waitForHold(_, acceptTap):
|
||||
if let (touchLocation, _) = self.touchLocationAndTimestamp, acceptTap {
|
||||
if self.state != .began {
|
||||
self.lastRecognizedGestureAndLocation = (.tap, touchLocation)
|
||||
self.state = .began
|
||||
}
|
||||
}
|
||||
self.state = .ended
|
||||
case .fail:
|
||||
self.state = .failed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +72,7 @@ public final class HashtagSearchController: TelegramBaseController {
|
||||
}, togglePeerMarkedUnread: { _, _ in
|
||||
}, toggleArchivedFolderHiddenByDefault: {
|
||||
}, hidePsa: { _ in
|
||||
}, activateChatPreview: { _, _, gesture in
|
||||
}, activateChatPreview: { _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
})
|
||||
|
||||
@ -25,13 +25,13 @@ public final class HorizontalPeerItem: ListViewItem {
|
||||
let context: AccountContext
|
||||
public let peer: EnginePeer
|
||||
let action: (EnginePeer) -> Void
|
||||
let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
let contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
let isPeerSelected: (EnginePeer.Id) -> Bool
|
||||
let customWidth: CGFloat?
|
||||
let presence: EnginePeer.Presence?
|
||||
let unreadBadge: (Int32, Bool)?
|
||||
|
||||
public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, context: AccountContext, peer: EnginePeer, presence: EnginePeer.Presence?, unreadBadge: (Int32, Bool)?, action: @escaping (EnginePeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, customWidth: CGFloat?) {
|
||||
public init(theme: PresentationTheme, strings: PresentationStrings, mode: HorizontalPeerItemMode, context: AccountContext, peer: EnginePeer, presence: EnginePeer.Presence?, unreadBadge: (Int32, Bool)?, action: @escaping (EnginePeer) -> Void, contextAction: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?, isPeerSelected: @escaping (EnginePeer.Id) -> Bool, customWidth: CGFloat?) {
|
||||
self.theme = theme
|
||||
self.strings = strings
|
||||
self.mode = mode
|
||||
@ -191,9 +191,9 @@ public final class HorizontalPeerItemNode: ListViewItemNode {
|
||||
strongSelf.peerNode.updateSelection(selected: item.isPeerSelected(item.peer.id), animated: false)
|
||||
|
||||
if let contextAction = item.contextAction {
|
||||
strongSelf.peerNode.contextAction = { [weak item] node, gesture in
|
||||
strongSelf.peerNode.contextAction = { [weak item] node, gesture, location in
|
||||
if let item = item {
|
||||
contextAction(item.peer, node, gesture)
|
||||
contextAction(item.peer, node, gesture, location)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -13,6 +13,10 @@ public final class ItemListRevealOptionsGestureRecognizer: UIPanGestureRecognize
|
||||
override public init(target: Any?, action: Selector?) {
|
||||
super.init(target: target, action: action)
|
||||
|
||||
if #available(iOS 13.4, *) {
|
||||
self.allowedScrollTypesMask = .continuous
|
||||
}
|
||||
|
||||
self.maximumNumberOfTouches = 1
|
||||
}
|
||||
|
||||
@ -161,9 +165,13 @@ open class ItemListRevealOptionsItemNode: ListViewItemNode, UIGestureRecognizerD
|
||||
override open func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if let recognizer = self.recognizer, gestureRecognizer == self.tapRecognizer {
|
||||
return abs(self.revealOffset) > 0.0 && !recognizer.validatedGesture
|
||||
} else {
|
||||
return true
|
||||
} else if let recognizer = self.recognizer, gestureRecognizer == self.recognizer, recognizer.numberOfTouches == 0 {
|
||||
let translation = recognizer.velocity(in: recognizer.view)
|
||||
if abs(translation.y) > 4.0 && abs(translation.y) > abs(translation.x) * 2.5 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
open func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
|
||||
@ -826,7 +826,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
|
||||
item.interaction.openUrl(url, false, true, nil)
|
||||
}
|
||||
}
|
||||
case .hold, .doubleTap:
|
||||
case .hold, .doubleTap, .secondaryTap:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,6 +85,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
private var longPressTimer: SwiftSignalKit.Timer?
|
||||
|
||||
private var highlightedReaction: ReactionItem.Reaction?
|
||||
private var highlightedByHover = false
|
||||
private var didTriggerExpandedReaction: Bool = false
|
||||
private var continuousHaptic: Any?
|
||||
private var validLayout: (CGSize, UIEdgeInsets, CGRect)?
|
||||
@ -467,7 +468,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
size: visualBackgroundFrame.size,
|
||||
cloudSourcePoint: cloudSourcePoint - visualBackgroundFrame.minX,
|
||||
isLeftAligned: isLeftAligned,
|
||||
isMinimized: self.highlightedReaction != nil,
|
||||
isMinimized: self.highlightedReaction != nil && !self.highlightedByHover,
|
||||
transition: transition
|
||||
)
|
||||
|
||||
@ -846,14 +847,18 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public func highlightGestureMoved(location: CGPoint) {
|
||||
public func highlightGestureMoved(location: CGPoint, hover: Bool) {
|
||||
let highlightedReaction = self.previewReaction(at: location)?.reaction
|
||||
if self.highlightedReaction != highlightedReaction {
|
||||
self.highlightedReaction = highlightedReaction
|
||||
if self.hapticFeedback == nil {
|
||||
self.hapticFeedback = HapticFeedback()
|
||||
self.highlightedByHover = hover && highlightedReaction != nil
|
||||
|
||||
if !hover {
|
||||
if self.hapticFeedback == nil {
|
||||
self.hapticFeedback = HapticFeedback()
|
||||
}
|
||||
self.hapticFeedback?.tap()
|
||||
}
|
||||
self.hapticFeedback?.tap()
|
||||
|
||||
if let (size, insets, anchorRect) = self.validLayout {
|
||||
self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, transition: .animated(duration: 0.18, curve: .easeInOut), animateInFromAnchorRect: nil, animateOutToAnchorRect: nil, animateReactionHighlight: true)
|
||||
|
||||
@ -74,7 +74,7 @@ public final class SelectablePeerNode: ASDisplayNode {
|
||||
private let textNode: ASTextNode
|
||||
|
||||
public var toggleSelection: (() -> Void)?
|
||||
public var contextAction: ((ASDisplayNode, ContextGesture?) -> Void)? {
|
||||
public var contextAction: ((ASDisplayNode, ContextGesture?, CGPoint?) -> Void)? {
|
||||
didSet {
|
||||
self.contextContainer.isGestureEnabled = self.contextAction != nil
|
||||
}
|
||||
@ -133,7 +133,7 @@ public final class SelectablePeerNode: ASDisplayNode {
|
||||
gesture.cancel()
|
||||
return
|
||||
}
|
||||
contextAction(strongSelf.contextContainer, gesture)
|
||||
contextAction(strongSelf.contextContainer, gesture, nil)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -212,7 +212,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
|
||||
|
||||
let interaction = ChatListNodeInteraction(context: self.context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
|
||||
}, activateChatPreview: { _, _, gesture in
|
||||
}, activateChatPreview: { _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in })
|
||||
|
||||
|
||||
@ -832,7 +832,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
|
||||
|
||||
let interaction = ChatListNodeInteraction(context: self.context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
|
||||
}, activateChatPreview: { _, _, gesture in
|
||||
}, activateChatPreview: { _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
})
|
||||
|
||||
@ -356,7 +356,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
||||
|
||||
let interaction = ChatListNodeInteraction(context: self.context, activateSearch: {}, peerSelected: { _, _, _ in }, disabledPeerSelected: { _ in }, togglePeerSelected: { _ in }, togglePeersSelection: { _, _ in }, additionalCategorySelected: { _ in
|
||||
}, messageSelected: { _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, deletePeer: { _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, hidePsa: { _ in
|
||||
}, activateChatPreview: { _, _, gesture in
|
||||
}, activateChatPreview: { _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in
|
||||
})
|
||||
|
||||
@ -62,7 +62,7 @@ final class ShareControllerRecentPeersGridItemNode: GridItemNode {
|
||||
} else {
|
||||
peersNode = ChatListSearchRecentPeersNode(context: context, theme: theme, mode: .actionSheet, strings: strings, peerSelected: { [weak self] peer in
|
||||
self?.controllerInteraction?.togglePeer(EngineRenderedPeer(peer: peer), true)
|
||||
}, peerContextAction: { _, _, gesture in gesture?.cancel() }, isPeerSelected: { [weak self] peerId in
|
||||
}, peerContextAction: { _, _, gesture, _ in gesture?.cancel() }, isPeerSelected: { [weak self] peerId in
|
||||
return self?.controllerInteraction?.selectedPeerIds.contains(peerId) ?? false
|
||||
}, share: true)
|
||||
self.peersNode = peersNode
|
||||
|
||||
@ -952,7 +952,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage)
|
||||
}, openPeerMention: { [weak self] name in
|
||||
self?.openPeerMention(name)
|
||||
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, anyRecognizer in
|
||||
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, anyRecognizer, location in
|
||||
guard let strongSelf = self, strongSelf.isNodeLoaded else {
|
||||
return
|
||||
}
|
||||
@ -1225,7 +1225,14 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
}
|
||||
}
|
||||
|
||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, engine: strongSelf.context.engine, message: message, selectAll: selectAll)), items: actionsSignal, recognizer: recognizer, gesture: gesture)
|
||||
let source: ContextContentSource
|
||||
if let location = location {
|
||||
source = .location(ChatMessageContextLocationContentSource(controller: strongSelf, location: node.view.convert(node.bounds, to: nil).origin.offsetBy(dx: location.x, dy: location.y)))
|
||||
} else {
|
||||
source = .extracted(ChatMessageContextExtractedContentSource(chatNode: strongSelf.chatDisplayNode, engine: strongSelf.context.engine, message: message, selectAll: selectAll))
|
||||
}
|
||||
|
||||
let controller = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: source, items: actionsSignal, recognizer: recognizer, gesture: gesture)
|
||||
controller.immediateItemsTransitionAnimation = disableTransitionAnimations
|
||||
controller.getOverlayViews = { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
|
||||
@ -61,7 +61,7 @@ public final class ChatControllerInteraction {
|
||||
let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool
|
||||
let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Peer?) -> Void
|
||||
let openPeerMention: (String) -> Void
|
||||
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void
|
||||
let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?, CGPoint?) -> Void
|
||||
let updateMessageReaction: (Message, ChatControllerInteractionReaction) -> Void
|
||||
let openMessageReactionContextMenu: (Message, ContextExtractedContentContainingView, ContextGesture?, String) -> Void
|
||||
let activateMessagePinch: (PinchSourceContainerNode) -> Void
|
||||
@ -166,7 +166,7 @@ public final class ChatControllerInteraction {
|
||||
openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool,
|
||||
openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Peer?) -> Void,
|
||||
openPeerMention: @escaping (String) -> Void,
|
||||
openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?) -> Void,
|
||||
openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?, CGPoint?) -> Void,
|
||||
openMessageReactionContextMenu: @escaping (Message, ContextExtractedContentContainingView, ContextGesture?, String) -> Void,
|
||||
updateMessageReaction: @escaping (Message, ChatControllerInteractionReaction) -> Void,
|
||||
activateMessagePinch: @escaping (PinchSourceContainerNode) -> Void,
|
||||
|
||||
@ -197,7 +197,7 @@ extension ListMessageItemInteraction {
|
||||
self.init(openMessage: { message, mode -> Bool in
|
||||
return controllerInteraction.openMessage(message, mode)
|
||||
}, openMessageContextMenu: { message, bool, node, rect, gesture in
|
||||
controllerInteraction.openMessageContextMenu(message, bool, node, rect, gesture)
|
||||
controllerInteraction.openMessageContextMenu(message, bool, node, rect, gesture, nil)
|
||||
}, toggleMessagesSelection: { messageId, selected in
|
||||
controllerInteraction.toggleMessagesSelection(messageId, selected)
|
||||
}, openUrl: { url, param1, param2, message in
|
||||
|
||||
@ -267,7 +267,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
case .action, .optionalAction:
|
||||
break
|
||||
case let .openContextMenu(tapMessage, selectAll, subFrame):
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture)
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -750,7 +750,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
case .options:
|
||||
if let item = self.item {
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1512,7 +1512,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
if canAddMessageReactions(message: item.message) {
|
||||
item.controllerInteraction.updateMessageReaction(item.message, .default)
|
||||
} else {
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil, nil)
|
||||
}
|
||||
}
|
||||
} else if case .tap = gesture {
|
||||
@ -1999,7 +1999,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case .longTap, .doubleTap:
|
||||
case .longTap, .doubleTap, .secondaryTap:
|
||||
if let item = self.item, self.imageNode.frame.contains(location) {
|
||||
return .openContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.imageNode.frame)
|
||||
}
|
||||
@ -2441,7 +2441,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil, nil)
|
||||
}
|
||||
|
||||
override func targetReactionView(value: String) -> UIView? {
|
||||
|
||||
@ -602,7 +602,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
case .action, .optionalAction:
|
||||
break
|
||||
case let .openContextMenu(tapMessage, selectAll, subFrame):
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture)
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -945,6 +945,20 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
}
|
||||
}
|
||||
recognizer.secondaryTap = { [weak self] point, recognizer in
|
||||
guard let strongSelf = self, let item = strongSelf.item else {
|
||||
return
|
||||
}
|
||||
|
||||
if let action = strongSelf.gestureRecognized(gesture: .secondaryTap, location: point, recognizer: recognizer) {
|
||||
switch action {
|
||||
case .action, .optionalAction:
|
||||
break
|
||||
case let .openContextMenu(tapMessage, selectAll, subFrame):
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, nil, point)
|
||||
}
|
||||
}
|
||||
}
|
||||
recognizer.highlight = { [weak self] point in
|
||||
if let strongSelf = self {
|
||||
for contentNode in strongSelf.contentNodes {
|
||||
@ -3051,7 +3065,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
}
|
||||
}
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, subFrame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, subFrame, nil, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3128,7 +3142,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
if canAddMessageReactions(message: tapMessage) {
|
||||
item.controllerInteraction.updateMessageReaction(tapMessage, .default)
|
||||
} else {
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil, nil)
|
||||
}
|
||||
}
|
||||
} else if case .tap = gesture {
|
||||
@ -3365,7 +3379,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case .longTap, .doubleTap:
|
||||
case .longTap, .doubleTap, .secondaryTap:
|
||||
if let item = self.item, self.backgroundNode.frame.contains(location) {
|
||||
let message = item.message
|
||||
|
||||
@ -3957,7 +3971,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
return
|
||||
}
|
||||
let subFrame = self.backgroundNode.frame
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, true, self, subFrame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, true, self, subFrame, nil, nil)
|
||||
}
|
||||
|
||||
override func targetReactionView(value: String) -> UIView? {
|
||||
|
||||
@ -5,6 +5,21 @@ import ContextUI
|
||||
import Postbox
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
final class ChatMessageContextLocationContentSource: ContextLocationContentSource {
|
||||
private let controller: ViewController
|
||||
private let location: CGPoint
|
||||
|
||||
init(controller: ViewController, location: CGPoint) {
|
||||
self.controller = controller
|
||||
self.location = location
|
||||
}
|
||||
|
||||
func transitionInfo() -> ContextControllerLocationViewInfo? {
|
||||
return ContextControllerLocationViewInfo(location: self.location, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class ChatMessageContextExtractedContentSource: ContextExtractedContentSource {
|
||||
let keepInPlace: Bool = false
|
||||
|
||||
@ -127,7 +127,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
break
|
||||
case let .openContextMenu(tapMessage, selectAll, subFrame):
|
||||
strongSelf.recognizer?.cancel()
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture)
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture, nil)
|
||||
if (strongSelf.appliedCurrentlyPlaying ?? false) && strongSelf.interactiveVideoNode.isPlaying {
|
||||
strongSelf.wasPlaying = true
|
||||
strongSelf.interactiveVideoNode.pause()
|
||||
@ -241,7 +241,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
}
|
||||
case .options:
|
||||
if let item = self.item {
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.interactiveVideoNode.frame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.interactiveVideoNode.frame, nil, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -932,7 +932,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
}
|
||||
}
|
||||
return nil
|
||||
case .longTap, .doubleTap:
|
||||
case .longTap, .doubleTap, .secondaryTap:
|
||||
if let item = self.item, self.interactiveVideoNode.frame.contains(location) {
|
||||
return .openContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.interactiveVideoNode.frame)
|
||||
}
|
||||
@ -1308,7 +1308,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.interactiveVideoNode.frame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.interactiveVideoNode.frame, nil, nil)
|
||||
}
|
||||
|
||||
private var absoluteRect: (CGRect, CGSize)?
|
||||
|
||||
@ -787,7 +787,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
|
||||
}
|
||||
|
||||
self.item?.controllerInteraction.clickThroughMessage()
|
||||
case .longTap, .doubleTap:
|
||||
case .longTap, .doubleTap, .secondaryTap:
|
||||
break
|
||||
case .hold:
|
||||
break
|
||||
|
||||
@ -141,7 +141,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
case .action, .optionalAction:
|
||||
break
|
||||
case let .openContextMenu(tapMessage, selectAll, subFrame):
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture)
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -322,7 +322,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
case .options:
|
||||
if let item = self.item {
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil, nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1105,7 +1105,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
if canAddMessageReactions(message: item.message) {
|
||||
item.controllerInteraction.updateMessageReaction(tapMessage, .default)
|
||||
} else {
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil, nil)
|
||||
}
|
||||
}
|
||||
} else if case .tap = gesture {
|
||||
@ -1165,7 +1165,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
}
|
||||
|
||||
return nil
|
||||
case .longTap, .doubleTap:
|
||||
case .longTap, .doubleTap, .secondaryTap:
|
||||
if let item = self.item, self.imageNode.frame.contains(location) {
|
||||
return .openContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.imageNode.frame)
|
||||
}
|
||||
@ -1610,7 +1610,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
|
||||
guard let item = self.item else {
|
||||
return
|
||||
}
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil)
|
||||
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, nil, nil)
|
||||
}
|
||||
|
||||
override func targetReactionView(value: String) -> UIView? {
|
||||
|
||||
@ -254,8 +254,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}
|
||||
}, openPeerMention: { [weak self] name in
|
||||
self?.openPeerMention(name)
|
||||
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, _ in
|
||||
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame)
|
||||
}, openMessageContextMenu: { [weak self] message, selectAll, node, frame, _, location in
|
||||
self?.openMessageContextMenu(message: message, selectAll: selectAll, node: node, frame: frame, location: location)
|
||||
}, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
}, updateMessageReaction: { _, _ in
|
||||
}, activateMessagePinch: { _ in
|
||||
@ -795,7 +795,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
||||
}))
|
||||
}
|
||||
|
||||
private func openMessageContextMenu(message: Message, selectAll: Bool, node: ASDisplayNode, frame: CGRect) {
|
||||
private func openMessageContextMenu(message: Message, selectAll: Bool, node: ASDisplayNode, frame: CGRect, location: CGPoint?) {
|
||||
var actions: [ContextMenuAction] = []
|
||||
if !message.text.isEmpty {
|
||||
actions.append(ContextMenuAction(content: .text(title: self.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: self.presentationData.strings.Conversation_ContextMenuCopy), action: {
|
||||
|
||||
@ -225,7 +225,7 @@ class ChatSearchResultsControllerNode: ViewControllerTracingNode, UIScrollViewDe
|
||||
}, togglePeerMarkedUnread: { _, _ in
|
||||
}, toggleArchivedFolderHiddenByDefault: {
|
||||
}, hidePsa: { _ in
|
||||
}, activateChatPreview: { [weak self] item, node, gesture in
|
||||
}, activateChatPreview: { [weak self] item, node, gesture, _ in
|
||||
guard let strongSelf = self else {
|
||||
gesture?.cancel()
|
||||
return
|
||||
|
||||
@ -59,9 +59,9 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
self.displayDeviceContacts = displayDeviceContacts
|
||||
self.displayCallIcons = displayCallIcons
|
||||
|
||||
var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?) -> Void)?
|
||||
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false)), displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture in
|
||||
contextActionImpl?(peer, node, gesture)
|
||||
var contextActionImpl: ((EnginePeer, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
self.contactListNode = ContactListNode(context: context, updatedPresentationData: (presentationData, self.presentationDataPromise.get()), presentation: .single(.natural(options: options, includeChatList: false)), displayCallIcons: displayCallIcons, contextAction: multipleSelection ? { peer, node, gesture, _ in
|
||||
contextActionImpl?(peer, node, gesture, nil)
|
||||
} : nil, multipleSelection: multipleSelection)
|
||||
|
||||
self.dimNode = ASDisplayNode()
|
||||
@ -105,7 +105,7 @@ final class ContactSelectionControllerNode: ASDisplayNode {
|
||||
self?.requestMultipleAction?(false, nil)
|
||||
}
|
||||
|
||||
contextActionImpl = { [weak self] peer, node, gesture in
|
||||
contextActionImpl = { [weak self] peer, node, gesture, _ in
|
||||
if let strongSelf = self, (strongSelf.selectionState?.selectedPeerIndices.isEmpty ?? true) {
|
||||
strongSelf.contactListNode.updateSelectionState { state in
|
||||
let peerId = ContactListPeerId.peer(peer.id)
|
||||
|
||||
@ -109,7 +109,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
||||
var selectStickerImpl: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
||||
|
||||
self.controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
return false }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
}, updateMessageReaction: { _, _ in }, activateMessagePinch: { _ in
|
||||
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, _, _, _, node, rect, _ in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||
|
||||
@ -69,7 +69,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
||||
}
|
||||
}, openPeer: { _, _, _, _ in
|
||||
}, openPeerMention: { _ in
|
||||
}, openMessageContextMenu: { _, _, _, _, _ in
|
||||
}, openMessageContextMenu: { _, _, _, _, _, _ in
|
||||
}, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
}, updateMessageReaction: { _, _ in
|
||||
}, activateMessagePinch: { _ in
|
||||
|
||||
@ -1690,7 +1690,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
return chatControllerInteraction.openMessage(message, mode)
|
||||
},
|
||||
openMessageContextMenu: { message, bool, node, rect, gesture in
|
||||
chatControllerInteraction.openMessageContextMenu(message, bool, node, rect, gesture)
|
||||
chatControllerInteraction.openMessageContextMenu(message, bool, node, rect, gesture, nil)
|
||||
},
|
||||
toggleMessagesSelection: { messageId, selected in
|
||||
chatControllerInteraction.toggleMessagesSelection(messageId, selected)
|
||||
|
||||
@ -1878,7 +1878,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
||||
self?.openPeer(peerId: id, navigation: navigation)
|
||||
}
|
||||
}, openPeerMention: { _ in
|
||||
}, openMessageContextMenu: { [weak self] message, _, node, frame, anyRecognizer in
|
||||
}, openMessageContextMenu: { [weak self] message, _, node, frame, anyRecognizer, _ in
|
||||
guard let strongSelf = self, let node = node as? ContextExtractedContentContainingNode else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1274,7 +1274,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
||||
let controllerInteraction: ChatControllerInteraction
|
||||
|
||||
controllerInteraction = ChatControllerInteraction(openMessage: { _, _ in
|
||||
return false }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
return false }, openPeer: { _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
||||
}, updateMessageReaction: { _, _ in }, activateMessagePinch: { _ in
|
||||
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||
}, tapMessage: { message in
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user