diff --git a/Telegram/BUILD b/Telegram/BUILD
index fa7bea15d5..03a0109fe6 100644
--- a/Telegram/BUILD
+++ b/Telegram/BUILD
@@ -1885,6 +1885,8 @@ plist_fragment(
CADisableMinimumFrameDurationOnPhone
+ UIApplicationSupportsIndirectInputEvents
+
""".format(
telegram_bundle_id = telegram_bundle_id,
)
diff --git a/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift b/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift
index e6e160558c..61c54f4826 100644
--- a/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift
+++ b/submodules/ChatListSearchRecentPeersNode/Sources/ChatListSearchRecentPeersNode.swift
@@ -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))
diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift
index 02ed9d2d8d..c5de228318 100644
--- a/submodules/ChatListUI/Sources/ChatListController.swift
+++ b/submodules/ChatListUI/Sources/ChatListController.swift
@@ -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)
+ }
+}
diff --git a/submodules/ChatListUI/Sources/ChatListControllerNode.swift b/submodules/ChatListUI/Sources/ChatListControllerNode.swift
index ae0d5b8e8f..7a64838eb2 100644
--- a/submodules/ChatListUI/Sources/ChatListControllerNode.swift
+++ b/submodules/ChatListUI/Sources/ChatListControllerNode.swift
@@ -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)?
diff --git a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift
index fd1bc9b262..400be7eb22 100644
--- a/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift
+++ b/submodules/ChatListUI/Sources/ChatListRecentPeersListItem.swift
@@ -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
})
diff --git a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift
index 1c68ea26ba..420d380b1f 100644
--- a/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift
+++ b/submodules/ChatListUI/Sources/ChatListSearchContainerNode.swift
@@ -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?
- 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?) {
+ 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?) {
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)? = 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)? = 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
diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift
index 206e4f3eb1..e7abed7b33 100644
--- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift
+++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift
@@ -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 })
diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift
index 564b0569a5..5f27925810 100644
--- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift
+++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift
@@ -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
diff --git a/submodules/ChatListUI/Sources/Node/ChatListNode.swift b/submodules/ChatListUI/Sources/Node/ChatListNode.swift
index ba7c393edf..658e20eb15 100644
--- a/submodules/ChatListUI/Sources/Node/ChatListNode.swift
+++ b/submodules/ChatListUI/Sources/Node/ChatListNode.swift
@@ -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()
}
diff --git a/submodules/ContactListUI/Sources/ContactListNode.swift b/submodules/ContactListUI/Sources/ContactListNode.swift
index 6efb4701dc..279b65aeec 100644
--- a/submodules/ContactListUI/Sources/ContactListNode.swift
+++ b/submodules/ContactListUI/Sources/ContactListNode.swift
@@ -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)? = nil, presentation: Signal, 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)? = nil, presentation: Signal, 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
diff --git a/submodules/ContactListUI/Sources/ContactsControllerNode.swift b/submodules/ContactListUI/Sources/ContactsControllerNode.swift
index 109565d468..95800a0b31 100644
--- a/submodules/ContactListUI/Sources/ContactsControllerNode.swift
+++ b/submodules/ContactListUI/Sources/ContactsControllerNode.swift
@@ -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()
diff --git a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift
index 5336adfb33..c01bd6b402 100644
--- a/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift
+++ b/submodules/ContactListUI/Sources/ContactsSearchContainerNode.swift
@@ -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)? = 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)? = 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
diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift
index ea842498af..050eb5789e 100644
--- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift
+++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift
@@ -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)
diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift
index 5e655079d0..98d5d21b73 100644
--- a/submodules/ContextUI/Sources/ContextController.swift
+++ b/submodules/ContextUI/Sources/ContextController.swift
@@ -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()
let contentReady = Promise()
@@ -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 { get }
+
+ func transitionInfo() -> ContextControllerLocationViewInfo?
+}
+
+public extension ContextLocationContentSource {
+ var shouldBeDismissed: Signal {
+ 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
diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift
index ee33474803..66d0b94623 100644
--- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift
+++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift
@@ -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)
diff --git a/submodules/ContextUI/Sources/ContextControllerPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerPresentationNode.swift
index 54eb0fec80..95a0af80d9 100644
--- a/submodules/ContextUI/Sources/ContextControllerPresentationNode.swift
+++ b/submodules/ContextUI/Sources/ContextControllerPresentationNode.swift
@@ -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)
diff --git a/submodules/Display/Source/ListView.swift b/submodules/Display/Source/ListView.swift
index b482bc67ad..278de23218 100644
--- a/submodules/Display/Source/ListView.swift
+++ b/submodules/Display/Source/ListView.swift
@@ -4623,38 +4623,61 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
override open func touchesEnded(_ touches: Set, 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()
diff --git a/submodules/Display/Source/ListViewItem.swift b/submodules/Display/Source/ListViewItem.swift
index f99829ea00..4a20af1620 100644
--- a/submodules/Display/Source/ListViewItem.swift
+++ b/submodules/Display/Source/ListViewItem.swift
@@ -96,4 +96,7 @@ public extension ListViewItem {
func selected(listView: ListView) {
}
+
+ func performSecondaryAction(listView: ListView) {
+ }
}
diff --git a/submodules/Display/Source/ListViewItemNode.swift b/submodules/Display/Source/ListViewItemNode.swift
index 54080485d9..ffee957d65 100644
--- a/submodules/Display/Source/ListViewItemNode.swift
+++ b/submodules/Display/Source/ListViewItemNode.swift
@@ -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
}
diff --git a/submodules/Display/Source/TapLongTapOrDoubleTapGestureRecognizer.swift b/submodules/Display/Source/TapLongTapOrDoubleTapGestureRecognizer.swift
index 42e6e2beb2..a6e37daf86 100644
--- a/submodules/Display/Source/TapLongTapOrDoubleTapGestureRecognizer.swift
+++ b/submodules/Display/Source/TapLongTapOrDoubleTapGestureRecognizer.swift
@@ -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
+ }
}
}
}
diff --git a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift
index 1fc0704580..79582574cd 100644
--- a/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift
+++ b/submodules/HashtagSearchUI/Sources/HashtagSearchController.swift
@@ -72,7 +72,7 @@ public final class HashtagSearchController: TelegramBaseController {
}, togglePeerMarkedUnread: { _, _ in
}, toggleArchivedFolderHiddenByDefault: {
}, hidePsa: { _ in
- }, activateChatPreview: { _, _, gesture in
+ }, activateChatPreview: { _, _, gesture, _ in
gesture?.cancel()
}, present: { _ in
})
diff --git a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift
index 5051bbe78f..b35103ec43 100644
--- a/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift
+++ b/submodules/HorizontalPeerItem/Sources/HorizontalPeerItem.swift
@@ -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 {
diff --git a/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift b/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift
index e4e46d604c..1f3c40f659 100644
--- a/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift
+++ b/submodules/ItemListUI/Sources/Items/ItemListEditableItem.swift
@@ -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 {
diff --git a/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift b/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift
index 131e2b51fb..422af3781e 100644
--- a/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift
+++ b/submodules/ListMessageItem/Sources/ListMessageSnippetItemNode.swift
@@ -826,7 +826,7 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
item.interaction.openUrl(url, false, true, nil)
}
}
- case .hold, .doubleTap:
+ case .hold, .doubleTap, .secondaryTap:
break
}
}
diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift
index af13929f5a..508b871d78 100644
--- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift
+++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift
@@ -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)
diff --git a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift
index c590dfa764..318e21e449 100644
--- a/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift
+++ b/submodules/SelectablePeerNode/Sources/SelectablePeerNode.swift
@@ -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)
}
}
diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift
index 0e7ceca482..fb04805e2a 100644
--- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift
+++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift
@@ -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 })
diff --git a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift
index 309f10eed5..3bd404d4b4 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemeAccentColorControllerNode.swift
@@ -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
})
diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
index b543c852c4..5923c11d0e 100644
--- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
+++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift
@@ -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
})
diff --git a/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift b/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift
index f96f74110e..f1cf0a0bb8 100644
--- a/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift
+++ b/submodules/ShareController/Sources/ShareControllerRecentPeersGridItem.swift
@@ -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
diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift
index ee1194d0b2..a7dfbfd8c7 100644
--- a/submodules/TelegramUI/Sources/ChatController.swift
+++ b/submodules/TelegramUI/Sources/ChatController.swift
@@ -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 {
diff --git a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift
index 75332b03d1..8c383a695e 100644
--- a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift
+++ b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift
@@ -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,
diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift
index 90c1257da4..63d9a64d76 100644
--- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift
+++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift
@@ -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
diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift
index 7673f8f510..58a6c6db69 100644
--- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift
@@ -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? {
diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
index c632945b36..817ea279ce 100644
--- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift
@@ -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? {
diff --git a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift
index 61feec2d12..319ca3f95e 100644
--- a/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageContextControllerContentSource.swift
@@ -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
diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift
index ea5cbd4cf7..6766f2010a 100644
--- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift
@@ -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)?
diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift
index 2e50baecd8..799220b1cd 100644
--- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift
@@ -787,7 +787,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
}
self.item?.controllerInteraction.clickThroughMessage()
- case .longTap, .doubleTap:
+ case .longTap, .doubleTap, .secondaryTap:
break
case .hold:
break
diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift
index 7724544ea2..abc5f2b60d 100644
--- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift
+++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift
@@ -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? {
diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift
index 5fa9774f2f..26672d5613 100644
--- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift
+++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift
@@ -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: {
diff --git a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift
index e9a78f14e5..159cea8ccd 100644
--- a/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift
+++ b/submodules/TelegramUI/Sources/ChatSearchResultsContollerNode.swift
@@ -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
diff --git a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift
index 1c7310f7a9..b0c1c61aa2 100644
--- a/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift
+++ b/submodules/TelegramUI/Sources/ContactSelectionControllerNode.swift
@@ -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)
diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift
index 97ea2d7c5e..ecfa7ff302 100644
--- a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift
+++ b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift
@@ -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
diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift
index 928371d929..96d9135a80 100644
--- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift
+++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift
@@ -69,7 +69,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
}
}, openPeer: { _, _, _, _ in
}, openPeerMention: { _ in
- }, openMessageContextMenu: { _, _, _, _, _ in
+ }, openMessageContextMenu: { _, _, _, _, _, _ in
}, openMessageReactionContextMenu: { _, _, _, _ in
}, updateMessageReaction: { _, _ in
}, activateMessagePinch: { _ in
diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift
index c8ca92ec23..2d175e3795 100644
--- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift
+++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoVisualMediaPaneNode.swift
@@ -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)
diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
index a6674e796c..9a18862f15 100644
--- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
+++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift
@@ -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
}
diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift
index 00ae018e33..d9342e5139 100644
--- a/submodules/TelegramUI/Sources/SharedAccountContext.swift
+++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift
@@ -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