diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 0efdfa61e6..c3b5082d6d 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -848,13 +848,20 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController return } - var subject: ChatControllerSubject? - if case let .search(messageId) = source, let id = messageId { - subject = .message(id: id, highlight: false) + let contextContentSource: ContextContentSource + if peer.id.namespace == Namespaces.Peer.SecretChat, let node = node.subnodes?.first as? ContextExtractedContentContainingNode { + contextContentSource = .extracted(ChatListHeaderBarContextExtractedContentSource(controller: strongSelf, sourceNode: node, keepInPlace: false)) + } else { + var subject: ChatControllerSubject? + if case let .search(messageId) = source, let id = messageId { + subject = .message(id: id, highlight: false) + } + let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.id), subject: subject, botStart: nil, mode: .standard(previewing: true)) + chatController.canReadHistory.set(false) + contextContentSource = .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node, navigationController: strongSelf.navigationController as? NavigationController)) } - let chatController = strongSelf.context.sharedContext.makeChatController(context: strongSelf.context, chatLocation: .peer(peer.id), subject: subject, 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.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false), reactionItems: [], gesture: gesture) + + let contextController = ContextController(account: strongSelf.context.account, presentationData: strongSelf.presentationData, source: contextContentSource, items: chatContextMenuItems(context: strongSelf.context, peerId: peer.id, promoInfo: nil, source: .search(source), chatListController: strongSelf, joined: false), reactionItems: [], gesture: gesture) strongSelf.presentInGlobalOverlay(contextController) } diff --git a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift index e0b5e68a3b..6a994b166e 100644 --- a/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift +++ b/submodules/ChatListUI/Sources/ChatListSearchListPaneNode.swift @@ -206,7 +206,7 @@ private enum ChatListRecentEntry: Comparable, Identifiable { } }, deletePeer: deletePeer, contextAction: peerContextAction.flatMap { peerContextAction in return { node, gesture in - if let chatPeer = peer.peer.peers[peer.peer.peerId], chatPeer.id.namespace != Namespaces.Peer.SecretChat { + if let chatPeer = peer.peer.peers[peer.peer.peerId] { peerContextAction(chatPeer, .recentSearch, node, gesture) } else { gesture?.cancel() diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index 8f9dc6203b..90d59c9932 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -310,7 +310,15 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { private let separatorNode: ASDisplayNode private let highlightedBackgroundNode: ASDisplayNode + private let extractedBackgroundImageNode: ASImageNode + private let containerNode: ContextControllerSourceNode + private let contextSourceNode: ContextExtractedContentContainingNode + + private var extractedRect: CGRect? + private var nonExtractedRect: CGRect? + + private let offsetContainerNode: ASDisplayNode private let avatarNode: AvatarNode private let titleNode: TextNode @@ -356,8 +364,15 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { self.highlightedBackgroundNode = ASDisplayNode() self.highlightedBackgroundNode.isLayerBacked = true + self.extractedBackgroundImageNode = ASImageNode() + self.extractedBackgroundImageNode.displaysAsynchronously = false + self.extractedBackgroundImageNode.alpha = 0.0 + + self.contextSourceNode = ContextExtractedContentContainingNode() self.containerNode = ContextControllerSourceNode() + self.offsetContainerNode = ASDisplayNode() + self.avatarNode = AvatarNode(font: avatarFont) self.avatarNode.isLayerBacked = !smartInvertColorsEnabled() @@ -369,12 +384,20 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { self.isAccessibilityElement = true self.addSubnode(self.backgroundNode) + self.addSubnode(self.topSeparatorNode) self.addSubnode(self.separatorNode) + + self.containerNode.addSubnode(self.contextSourceNode) + self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode self.addSubnode(self.containerNode) - self.containerNode.addSubnode(self.avatarNode) - self.containerNode.addSubnode(self.titleNode) - self.containerNode.addSubnode(self.statusNode) + + self.contextSourceNode.contentNode.addSubnode(self.extractedBackgroundImageNode) + self.contextSourceNode.contentNode.addSubnode(self.offsetContainerNode) + + self.offsetContainerNode.addSubnode(self.avatarNode) + self.offsetContainerNode.addSubnode(self.titleNode) + self.offsetContainerNode.addSubnode(self.statusNode) self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in if let strongSelf = self, let layoutParams = strongSelf.layoutParams { @@ -390,6 +413,28 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { } contextAction(strongSelf.containerNode, gesture) } + + self.contextSourceNode.willUpdateIsExtractedToContextPreview = { [weak self] isExtracted, transition in + guard let strongSelf = self, let item = strongSelf.item else { + return + } + + if isExtracted { + strongSelf.extractedBackgroundImageNode.image = generateStretchableFilledCircleImage(diameter: 28.0, color: item.presentationData.theme.list.plainBackgroundColor) + } + + if let extractedRect = strongSelf.extractedRect, let nonExtractedRect = strongSelf.nonExtractedRect { + let rect = isExtracted ? extractedRect : nonExtractedRect + transition.updateFrame(node: strongSelf.extractedBackgroundImageNode, frame: rect) + } + + transition.updateSublayerTransformOffset(layer: strongSelf.offsetContainerNode.layer, offset: CGPoint(x: isExtracted ? 12.0 : 0.0, y: 0.0)) + transition.updateAlpha(node: strongSelf.extractedBackgroundImageNode, alpha: isExtracted ? 1.0 : 0.0, completion: { _ in + if !isExtracted { + self?.extractedBackgroundImageNode.image = nil + } + }) + } } override public func layoutForParams(_ params: ListViewItemLayoutParams, item: ListViewItem, previousItem: ListViewItem?, nextItem: ListViewItem?) { @@ -741,8 +786,23 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { strongSelf.accessibilityValue = statusAttributedString?.string strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: nodeLayout.contentSize) + strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: nodeLayout.contentSize) + strongSelf.offsetContainerNode.frame = CGRect(origin: CGPoint(), size: nodeLayout.contentSize) + strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: nodeLayout.contentSize) strongSelf.containerNode.isGestureEnabled = item.contextAction != nil + let nonExtractedRect = CGRect(origin: CGPoint(), size: CGSize(width: nodeLayout.contentSize.width - 16.0, height: nodeLayout.contentSize.height)) + let extractedRect = CGRect(origin: CGPoint(), size: nodeLayout.contentSize).insetBy(dx: 16.0 + params.leftInset, dy: 0.0) + strongSelf.extractedRect = extractedRect + strongSelf.nonExtractedRect = nonExtractedRect + + if strongSelf.contextSourceNode.isExtractedToContextPreview { + strongSelf.extractedBackgroundImageNode.frame = extractedRect + } else { + strongSelf.extractedBackgroundImageNode.frame = nonExtractedRect + } + strongSelf.contextSourceNode.contentRect = extractedRect + switch item.peer { case let .peer(peer, _): if let peer = peer { @@ -827,7 +887,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { verificationIconNode.displayWithoutProcessing = true verificationIconNode.displaysAsynchronously = false strongSelf.verificationIconNode = verificationIconNode - strongSelf.containerNode.addSubnode(verificationIconNode) + strongSelf.offsetContainerNode.addSubnode(verificationIconNode) } if let verificationIconNode = strongSelf.verificationIconNode { verificationIconNode.image = verificationIconImage @@ -846,7 +906,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { let actionButtonNode = HighlightableButtonNode() actionButtonNode.isUserInteractionEnabled = action.action != nil actionButtonNode.addTarget(strongSelf, action: #selector(strongSelf.actionButtonPressed(_:)), forControlEvents: .touchUpInside) - strongSelf.containerNode.addSubnode(actionButtonNode) + strongSelf.offsetContainerNode.addSubnode(actionButtonNode) actionButtonNodes.append(actionButtonNode) } @@ -877,7 +937,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { let arrowButtonNode = HighlightableButtonNode() arrowButtonNode.addTarget(self, action: #selector(strongSelf.arrowButtonPressed), forControlEvents: .touchUpInside) strongSelf.arrowButtonNode = arrowButtonNode - strongSelf.containerNode.addSubnode(arrowButtonNode) + strongSelf.offsetContainerNode.addSubnode(arrowButtonNode) } if let arrowButtonNode = strongSelf.arrowButtonNode { arrowButtonNode.setImage(arrowButtonImage, for: .normal) @@ -901,7 +961,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { badgeBackgroundNode.isLayerBacked = true badgeBackgroundNode.displaysAsynchronously = false badgeBackgroundNode.displayWithoutProcessing = true - strongSelf.containerNode.addSubnode(badgeBackgroundNode) + strongSelf.offsetContainerNode.addSubnode(badgeBackgroundNode) strongSelf.badgeBackgroundNode = badgeBackgroundNode badgeTransition = .immediate } @@ -920,7 +980,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { let badgeTextNode = badgeTextApply() if badgeTextNode !== strongSelf.badgeTextNode { strongSelf.badgeTextNode?.removeFromSupernode() - strongSelf.containerNode.addSubnode(badgeTextNode) + strongSelf.offsetContainerNode.addSubnode(badgeTextNode) strongSelf.badgeTextNode = badgeTextNode }