From e450862adfb92a9126031e796f3ca89bc4a8f67f Mon Sep 17 00:00:00 2001 From: Ali <> Date: Mon, 22 Aug 2022 02:20:05 +0300 Subject: [PATCH] Reaction improvements --- .../Sources/AccountContext.swift | 1 + ...tControllerExtractedPresentationNode.swift | 103 ++++++++++++++---- .../Sources/ReactionContextNode.swift | 64 +++++++++-- .../TelegramEngine/Peers/ReportPeer.swift | 22 ++++ .../Peers/TelegramEnginePeers.swift | 6 +- .../TelegramUI/Sources/ChatBotInfoItem.swift | 2 +- .../Sources/ChatButtonKeyboardInputNode.swift | 4 +- .../TelegramUI/Sources/ChatController.swift | 21 ++-- .../Sources/ChatControllerInteraction.swift | 4 +- .../ChatInterfaceStateContextMenus.swift | 4 +- .../ChatMessageAnimatedStickerItemNode.swift | 2 +- .../Sources/ChatMessageBubbleItemNode.swift | 6 +- .../Sources/ChatMessageDateHeader.swift | 4 +- .../ChatMessageInstantVideoItemNode.swift | 2 +- .../Sources/ChatMessageItemView.swift | 2 +- .../ChatMessageWebpageBubbleContentNode.swift | 2 +- .../ChatPinnedMessageTitlePanelNode.swift | 2 +- .../ChatRecentActionsControllerNode.swift | 2 +- .../Sources/DrawingStickersScreen.swift | 2 +- .../OverlayAudioPlayerControllerNode.swift | 2 +- .../PeerInfoGroupsInCommonPaneNode.swift | 2 +- .../Sources/PeerInfo/PeerInfoScreen.swift | 85 ++++++++++----- .../Sources/SharedAccountContext.swift | 13 ++- .../Sources/TelegramRootController.swift | 2 +- 24 files changed, 264 insertions(+), 95 deletions(-) diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 9821fd95de..bf2c51d228 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -467,6 +467,7 @@ public enum PeerInfoControllerMode { case calls(messages: [Message]) case nearbyPeer(distance: Int32) case group(PeerId) + case reaction(MessageId) } public enum ContactListActionItemInlineIconPosition { diff --git a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift index 859d8b0dad..4e56f650c0 100644 --- a/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift +++ b/submodules/ContextUI/Sources/ContextControllerExtractedPresentationNode.swift @@ -179,7 +179,8 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo private let dismissTapNode: ASDisplayNode private let dismissAccessibilityArea: AccessibilityAreaNode private let clippingNode: ASDisplayNode - private let scrollNode: ASScrollNode + private let scroller: UIScrollView + private let scrollNode: ASDisplayNode private var reactionContextNode: ReactionContextNode? private var reactionContextNodeIsAnimatingOut: Bool = false @@ -192,6 +193,14 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo private var strings: PresentationStrings? + private enum OverscrollMode { + case unrestricted + case topOnly + case disabled + } + + private var overscrollMode: OverscrollMode = .unrestricted + init( getController: @escaping () -> ContextControllerProtocol?, requestUpdate: @escaping (ContainedViewLayoutTransition) -> Void, @@ -215,13 +224,17 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo self.clippingNode = ASDisplayNode() self.clippingNode.clipsToBounds = true - self.scrollNode = ASScrollNode() - self.scrollNode.canCancelAllTouchesInViews = true - self.scrollNode.view.delaysContentTouches = false - self.scrollNode.view.showsVerticalScrollIndicator = false + self.scroller = UIScrollView() + self.scroller.canCancelContentTouches = true + self.scroller.delaysContentTouches = false + self.scroller.showsVerticalScrollIndicator = false if #available(iOS 11.0, *) { - self.scrollNode.view.contentInsetAdjustmentBehavior = .never + self.scroller.contentInsetAdjustmentBehavior = .never } + self.scroller.alwaysBounceVertical = true + + self.scrollNode = ASDisplayNode() + self.scrollNode.view.addGestureRecognizer(self.scroller.panGestureRecognizer) self.contentRectDebugNode = ASDisplayNode() self.contentRectDebugNode.isUserInteractionEnabled = false @@ -244,11 +257,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo self.scrollNode.addSubnode(self.dismissAccessibilityArea) self.scrollNode.addSubnode(self.actionsStackNode) - /*#if DEBUG - self.scrollNode.addSubnode(self.contentRectDebugNode) - #endif*/ - - self.scrollNode.view.delegate = self + self.scroller.delegate = self self.dismissTapNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dismissTapGesture(_:)))) @@ -292,10 +301,65 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo } } + func scrollViewWillBeginDragging(_ scrollView: UIScrollView) { + if let reactionContextNode = self.reactionContextNode, (reactionContextNode.isExpanded || !reactionContextNode.canBeExpanded) { + self.overscrollMode = .disabled + self.scroller.alwaysBounceVertical = false + } else { + if scrollView.contentSize.height > scrollView.bounds.height { + self.overscrollMode = .unrestricted + self.scroller.alwaysBounceVertical = true + } else { + if self.reactionContextNode != nil { + self.overscrollMode = .topOnly + self.scroller.alwaysBounceVertical = true + } else { + self.overscrollMode = .disabled + self.scroller.alwaysBounceVertical = false + } + } + } + } + func scrollViewDidScroll(_ scrollView: UIScrollView) { + var adjustedBounds = scrollView.bounds + var topOverscroll: CGFloat = 0.0 + switch self.overscrollMode { + case .unrestricted: + if adjustedBounds.origin.y < 0.0 { + topOverscroll = -adjustedBounds.origin.y + } + case .disabled: + break + case .topOnly: + if scrollView.contentSize.height <= scrollView.bounds.height { + if adjustedBounds.origin.y > 0.0 { + adjustedBounds.origin.y = 0.0 + } else { + topOverscroll = -adjustedBounds.origin.y + } + } else { + if adjustedBounds.origin.y < 0.0 { + topOverscroll = -adjustedBounds.origin.y + } else if adjustedBounds.origin.y + adjustedBounds.height > scrollView.contentSize.height { + adjustedBounds.origin.y = scrollView.contentSize.height - adjustedBounds.height + } + } + } + self.scrollNode.bounds = adjustedBounds + if let reactionContextNode = self.reactionContextNode { - let isIntersectingContent = scrollView.contentOffset.y >= 10.0 + let isIntersectingContent = adjustedBounds.minY >= 10.0 reactionContextNode.updateIsIntersectingContent(isIntersectingContent: isIntersectingContent, transition: .animated(duration: 0.25, curve: .easeInOut)) + + if !reactionContextNode.isExpanded && reactionContextNode.canBeExpanded { + if topOverscroll > 60.0 && self.scroller.isDragging { + self.scroller.panGestureRecognizer.state = .cancelled + reactionContextNode.expand() + } else { + reactionContextNode.updateExtension(distance: topOverscroll) + } + } } } @@ -341,7 +405,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo } private func getCurrentScrollingState() -> CGFloat { - return self.scrollNode.view.contentOffset.y + return self.scrollNode.bounds.minY } private func getActionsStackPositionLock() -> CGFloat? { @@ -410,6 +474,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo transition.updateFrame(node: self.clippingNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true) if self.scrollNode.frame != CGRect(origin: CGPoint(), size: layout.size) { transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true) + transition.updateFrame(view: self.scroller, frame: CGRect(origin: CGPoint(), size: layout.size), beginWithCurrentState: true) } if let current = self.contentNode { @@ -684,16 +749,16 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo } let contentSize = CGSize(width: layout.size.width, height: contentHeight) - if self.scrollNode.view.contentSize != contentSize { - let previousContentOffset = self.scrollNode.view.contentOffset - self.scrollNode.view.contentSize = contentSize + if self.scroller.contentSize != contentSize { + let previousContentOffset = self.scroller.contentOffset + self.scroller.contentSize = contentSize if let storedScrollingState = self.actionsStackNode.storedScrollingState { self.actionsStackNode.clearStoredScrollingState() - self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: storedScrollingState) + self.scroller.contentOffset = CGPoint(x: 0.0, y: storedScrollingState) } if case .none = stateTransition, transition.isAnimated { - let contentOffset = self.scrollNode.view.contentOffset + let contentOffset = self.scroller.contentOffset transition.animateOffsetAdditive(layer: self.scrollNode.layer, offset: previousContentOffset.y - contentOffset.y) } } @@ -718,7 +783,7 @@ final class ContextControllerExtractedPresentationNode: ASDisplayNode, ContextCo let duration: Double = 0.42 let springDamping: CGFloat = 104.0 - self.scrollNode.view.contentOffset = CGPoint(x: 0.0, y: defaultScrollY) + self.scroller.contentOffset = CGPoint(x: 0.0, y: defaultScrollY) self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index 2ed1bca108..1031daf415 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -112,7 +112,7 @@ private final class ExpandItemView: UIView { self.tintView.layer.cornerRadius = size.width / 2.0 if let image = self.arrowView.image { - transition.updateFrame(view: self.arrowView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - image.size.width) / 2.0), y: floorToScreenPixels((size.height - image.size.height) / 2.0)), size: image.size)) + transition.updateFrame(view: self.arrowView, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - image.size.width) / 2.0), y: floorToScreenPixels(size.height - size.width + (size.width - image.size.height) / 2.0)), size: image.size)) } } } @@ -168,6 +168,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { public private(set) var currentContentHeight: CGFloat = 46.0 public private(set) var isExpanded: Bool = false + public private(set) var canBeExpanded: Bool = false + + private var animateFromExtensionDistance: CGFloat = 0.0 + private var extensionDistance: CGFloat = 0.0 private var emojiContent: EmojiPagerContentComponent? private var emojiContentDisposable: Disposable? @@ -252,6 +256,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.contentContainer.view.addSubview(expandItemView) self.contentTintContainer.view.addSubview(expandItemView.tintView) + + self.canBeExpanded = true } else { self.expandItemView = nil } @@ -289,6 +295,16 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.backgroundNode.updateIsIntersectingContent(isIntersectingContent: isIntersectingContent, transition: transition) } + public func updateExtension(distance: CGFloat) { + if self.extensionDistance != distance { + self.extensionDistance = distance + + if let (size, insets, anchorRect) = self.validLayout { + self.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil) + } + } + } + private func calculateBackgroundFrame(containerSize: CGSize, insets: UIEdgeInsets, anchorRect: CGRect, contentSize: CGSize) -> (backgroundFrame: CGRect, visualBackgroundFrame: CGRect, isLeftAligned: Bool, cloudSourcePoint: CGFloat) { var contentSize = contentSize contentSize.width = max(46.0, contentSize.width) @@ -325,7 +341,8 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { cloudSourcePoint = max(rect.minX + 46.0 / 2.0, anchorRect.minX) } - let visualRect = rect + var visualRect = rect + visualRect.size.height += self.extensionDistance return (rect, visualRect, isLeftAligned, cloudSourcePoint) } @@ -478,7 +495,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } if let expandItemView = self.expandItemView { - let baseNextFrame = CGRect(origin: CGPoint(x: nextX + 3.0, y: containerHeight - contentHeight + floor((contentHeight - 30.0) / 2.0) + (self.isExpanded ? 46.0 : 0.0)), size: CGSize(width: 30.0, height: 30.0)) + let baseNextFrame = CGRect(origin: CGPoint(x: nextX + 3.0, y: containerHeight - contentHeight + floor((contentHeight - 30.0) / 2.0) + (self.isExpanded ? 46.0 : 0.0)), size: CGSize(width: 30.0, height: 30.0 + self.extensionDistance)) transition.updateFrame(view: expandItemView, frame: baseNextFrame) transition.updateFrame(view: expandItemView.tintView, frame: baseNextFrame) @@ -488,10 +505,10 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { if let currentMaskFrame = currentMaskFrame { let transition = maskTransition ?? transition - transition.updateFrame(node: self.leftBackgroundMaskNode, frame: CGRect(x: -1000.0 + currentMaskFrame.minX, y: 0.0, width: 1000.0, height: self.currentContentHeight)) - transition.updateFrame(node: self.rightBackgroundMaskNode, frame: CGRect(x: currentMaskFrame.maxX, y: 0.0, width: 1000.0, height: self.currentContentHeight)) + transition.updateFrame(node: self.leftBackgroundMaskNode, frame: CGRect(x: -1000.0 + currentMaskFrame.minX, y: 0.0, width: 1000.0, height: self.currentContentHeight + self.extensionDistance)) + transition.updateFrame(node: self.rightBackgroundMaskNode, frame: CGRect(x: currentMaskFrame.maxX, y: 0.0, width: 1000.0, height: self.currentContentHeight + self.extensionDistance)) } else { - self.leftBackgroundMaskNode.frame = CGRect(x: 0.0, y: 0.0, width: 1000.0, height: self.currentContentHeight) + self.leftBackgroundMaskNode.frame = CGRect(x: 0.0, y: 0.0, width: 1000.0, height: self.currentContentHeight + self.extensionDistance) self.rightBackgroundMaskNode.frame = CGRect(origin: .zero, size: .zero) } @@ -569,12 +586,15 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { self.isLeftAligned = isLeftAligned self.visibleItemCount = itemCount + var scrollFrame = CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? 46.0 : 0.0), size: actualBackgroundFrame.size) + scrollFrame.origin.y += floorToScreenPixels(self.extensionDistance / 2.0) + transition.updateFrame(node: self.contentContainer, frame: visualBackgroundFrame, beginWithCurrentState: true) transition.updateFrame(node: self.contentTintContainer, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: visualBackgroundFrame.size), beginWithCurrentState: true) transition.updateFrame(view: self.contentContainerMask, frame: CGRect(origin: CGPoint(), size: visualBackgroundFrame.size), beginWithCurrentState: true) - transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(x: 0.0, y: self.isExpanded ? 46.0 : 0.0), size: actualBackgroundFrame.size), beginWithCurrentState: true) + transition.updateFrame(node: self.scrollNode, frame: scrollFrame, beginWithCurrentState: true) transition.updateFrame(node: self.previewingItemContainer, frame: visualBackgroundFrame, beginWithCurrentState: true) - self.scrollNode.view.contentSize = CGSize(width: completeContentWidth, height: visualBackgroundFrame.size.height) + self.scrollNode.view.contentSize = CGSize(width: completeContentWidth, height: scrollFrame.size.height) self.updateScrolling(transition: transition) @@ -607,9 +627,23 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { guard let strongSelf = self else { return } + strongSelf.emojiContent = emojiContent - if let (size, insets, anchorRect) = strongSelf.validLayout { - strongSelf.updateLayout(size: size, insets: insets, anchorRect: anchorRect, isAnimatingOut: false, transition: .immediate, animateInFromAnchorRect: nil, animateOutToAnchorRect: nil) + + if let reactionSelectionComponentHost = strongSelf.reactionSelectionComponentHost, let componentView = reactionSelectionComponentHost.view { + let _ = reactionSelectionComponentHost.update( + transition: .immediate, + component: AnyComponent(EmojiStatusSelectionComponent( + theme: strongSelf.presentationData.theme, + strings: strongSelf.presentationData.strings, + deviceMetrics: DeviceMetrics.iPhone13, + emojiContent: emojiContent, + backgroundColor: .clear, + separatorColor: strongSelf.presentationData.theme.list.itemPlainSeparatorColor.withMultipliedAlpha(0.5) + )), + environment: {}, + containerSize: CGSize(width: componentView.bounds.width, height: 300.0) + ) } }) } @@ -754,7 +788,7 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { componentTransition.setFrame(view: componentView, frame: CGRect(origin: componentFrame.origin, size: CGSize(width: componentFrame.width, height: componentFrame.height))) if animateIn { - transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -46.0)) + transition.animatePositionAdditive(layer: componentView.layer, offset: CGPoint(x: 0.0, y: -46.0 + floorToScreenPixels(self.animateFromExtensionDistance / 2.0))) } } } @@ -1250,6 +1284,14 @@ public final class ReactionContextNode: ASDisplayNode, UIScrollViewDelegate { } } + public func expand() { + self.animateFromExtensionDistance = self.extensionDistance + self.extensionDistance = 0.0 + self.currentContentHeight = 300.0 + self.isExpanded = true + self.isExpandedUpdated(.animated(duration: 0.4, curve: .spring)) + } + public func highlightGestureMoved(location: CGPoint, hover: Bool) { let highlightedReaction = self.previewReaction(at: location)?.reaction if self.highlightedReaction != highlightedReaction { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ReportPeer.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ReportPeer.swift index 0777db7d45..b8a0a66f18 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/ReportPeer.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/ReportPeer.swift @@ -170,6 +170,28 @@ func _internal_reportPeerMessages(account: Account, messageIds: [MessageId], rea } |> switchToLatest } +func _internal_reportPeerReaction(account: Account, authorId: PeerId, messageId: MessageId) -> Signal { + return account.postbox.transaction { transaction -> (Api.InputPeer, Api.InputUser)? in + guard let peer = transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) else { + return nil + } + guard let author = transaction.getPeer(authorId).flatMap(apiInputUser) else { + return nil + } + return (peer, author) + } + |> mapToSignal { inputData -> Signal in + guard let (inputPeer, inputUser) = inputData else { + return .complete() + } + return account.network.request(Api.functions.messages.reportReaction(peer: inputPeer, id: messageId.id, userId: inputUser)) + |> `catch` { _ -> Signal in + return .single(.boolFalse) + } + |> ignoreValues + } +} + func _internal_dismissPeerStatusOptions(account: Account, peerId: PeerId) -> Signal { return account.postbox.transaction { transaction -> Signal in transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift index 741e3446da..cb5e667345 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Peers/TelegramEnginePeers.swift @@ -160,7 +160,11 @@ public extension TelegramEngine { } public func reportPeerMessages(messageIds: [MessageId], reason: ReportReason, message: String) -> Signal { - return _internal_reportPeerMessages(account: account, messageIds: messageIds, reason: reason, message: message) + return _internal_reportPeerMessages(account: self.account, messageIds: messageIds, reason: reason, message: message) + } + + public func reportPeerReaction(authorId: PeerId, messageId: MessageId) -> Signal { + return _internal_reportPeerReaction(account: self.account, authorId: authorId, messageId: messageId) } public func dismissPeerStatusOptions(peerId: PeerId) -> Signal { diff --git a/submodules/TelegramUI/Sources/ChatBotInfoItem.swift b/submodules/TelegramUI/Sources/ChatBotInfoItem.swift index 67c3eb0d1a..e380b8dcd3 100644 --- a/submodules/TelegramUI/Sources/ChatBotInfoItem.swift +++ b/submodules/TelegramUI/Sources/ChatBotInfoItem.swift @@ -408,7 +408,7 @@ final class ChatBotInfoItemNode: ListViewItemNode { case let .url(url, concealed): self.item?.controllerInteraction.openUrl(url, concealed, nil, nil) case let .peerMention(peerId, _): - self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil) + self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil) case let .textMention(name): self.item?.controllerInteraction.openPeerMention(name) case let .botCommand(command): diff --git a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift index 5e1898e644..753e2f949b 100644 --- a/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatButtonKeyboardInputNode.swift @@ -247,7 +247,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode { peerId = message.id.peerId } if let botPeer = botPeer, let addressName = botPeer.addressName { - self.controllerInteraction.openPeer(peerId, .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil, peekData: nil), nil, nil) + self.controllerInteraction.openPeer(peerId, .chat(textInputState: ChatTextInputState(inputText: NSAttributedString(string: "@\(addressName) \(query)")), subject: nil, peekData: nil), nil, false, nil) } } case .payment: @@ -259,7 +259,7 @@ final class ChatButtonKeyboardInputNode: ChatInputNode { case let .setupPoll(isQuiz): self.controllerInteraction.openPollCreation(isQuiz) case let .openUserProfile(peerId): - self.controllerInteraction.openPeer(peerId, .info, nil, nil) + self.controllerInteraction.openPeer(peerId, .info, nil, false, nil) case let .openWebView(url, simple): self.controllerInteraction.openWebView(markupButton.title, url, simple, false) } diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 4f40c690bd..191c4968ce 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -881,7 +881,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }, openPeer: { [weak self] peerId in if let strongSelf = self { - strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, nil) + strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, false, nil) } }, openHashtag: { [weak self] peerName, hashtag in if let strongSelf = self { @@ -948,8 +948,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } }) }))) - }, openPeer: { [weak self] id, navigation, fromMessage, _ in - self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage) + }, openPeer: { [weak self] id, navigation, fromMessage, isReaction, _ in + self?.openPeer(peerId: id, navigation: navigation, fromMessage: fromMessage, fromReactionMessageId: isReaction ? fromMessage?.id : nil) }, openPeerMention: { [weak self] name in self?.openPeerMention(name) }, openMessageContextMenu: { [weak self] message, selectAll, node, frame, anyRecognizer, location in @@ -1437,7 +1437,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G return } - strongSelf.openPeer(peerId: id, navigation: .default, fromMessage: MessageReference(message)) + strongSelf.openPeer(peerId: id, navigation: .default, fromMessage: MessageReference(message), fromReactionMessageId: message.id) }) }))) @@ -1552,7 +1552,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G ), file: items.first?.file, action: action) - return .single(tip) |> delay(1.0, queue: .mainQueue()) + return .single(tip) } else { return .complete() } @@ -12707,7 +12707,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .mention(peerId, mention): switch action { case .tap: - strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, nil) + strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, false, nil) case .longTap: strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil) } @@ -12795,7 +12795,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .mention(peerId, mention): switch action { case .tap: - strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, nil) + strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, false, nil) case .longTap: strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil) } @@ -12904,7 +12904,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G case let .mention(peerId, mention): switch action { case .tap: - strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, nil) + strongSelf.controllerInteraction?.openPeer(peerId, .default, nil, false, nil) case .longTap: strongSelf.controllerInteraction?.longTap(.peerMention(peerId, mention), nil) } @@ -14887,7 +14887,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) } - private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: MessageReference?, expandAvatar: Bool = false) { + private func openPeer(peerId: PeerId?, navigation: ChatControllerInteractionNavigateToPeer, fromMessage: MessageReference?, fromReactionMessageId: MessageId? = nil, expandAvatar: Bool = false) { let _ = self.presentVoiceMessageDiscardAlert(action: { if case let .peer(currentPeerId) = self.chatLocation, peerId == currentPeerId { switch navigation { @@ -14936,6 +14936,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G if let _ = fromMessage, let chatPeerId = chatPeerId { mode = .group(chatPeerId) } + if let fromReactionMessageId = fromReactionMessageId { + mode = .reaction(fromReactionMessageId) + } var expandAvatar = expandAvatar if peer.smallProfileImage == nil { expandAvatar = false diff --git a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift index b84ff770ef..eaa2413067 100644 --- a/submodules/TelegramUI/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Sources/ChatControllerInteraction.swift @@ -59,7 +59,7 @@ struct UnreadMessageRangeKey: Hashable { public final class ChatControllerInteraction { let openMessage: (Message, ChatControllerInteractionOpenMessageMode) -> Bool - let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Peer?) -> Void + let openPeer: (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Bool, Peer?) -> Void let openPeerMention: (String) -> Void let openMessageContextMenu: (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?, CGPoint?) -> Void let updateMessageReaction: (Message, ChatControllerInteractionReaction) -> Void @@ -164,7 +164,7 @@ public final class ChatControllerInteraction { init( openMessage: @escaping (Message, ChatControllerInteractionOpenMessageMode) -> Bool, - openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Peer?) -> Void, + openPeer: @escaping (PeerId?, ChatControllerInteractionNavigateToPeer, MessageReference?, Bool, Peer?) -> Void, openPeerMention: @escaping (String) -> Void, openMessageContextMenu: @escaping (Message, Bool, ASDisplayNode, CGRect, UIGestureRecognizer?, CGPoint?) -> Void, openMessageReactionContextMenu: @escaping (Message, ContextExtractedContentContainingView, ContextGesture?, MessageReaction.Reaction) -> Void, diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 59d683228b..e0e079d971 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1584,7 +1584,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState actions.insert(.custom(ChatReadReportContextItem(context: context, message: message, stats: readStats, action: { c, f, stats, customReactionEmojiPacks, firstCustomEmojiReaction in if reactionCount == 0, let stats = stats, stats.peers.count == 1 { c.dismiss(completion: { - controllerInteraction.openPeer(stats.peers[0].id, .default, nil, nil) + controllerInteraction.openPeer(stats.peers[0].id, .default, nil, false, nil) }) } else if (stats != nil && !stats!.peers.isEmpty) || reactionCount != 0 { var tip: ContextController.Tip? @@ -1625,7 +1625,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState }, openPeer: { [weak c] id in c?.dismiss(completion: { - controllerInteraction.openPeer(id, .default, nil, nil) + controllerInteraction.openPeer(id, .default, MessageReference(message), true, nil) }) } )), tip: tip))) diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index 4da4fc9b83..7fc44dc54d 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1941,7 +1941,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId) } else if let peer = forwardInfo.source ?? forwardInfo.author { - item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil) + item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil) } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) } diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 768a16c21b..a7bbf79ded 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -3221,7 +3221,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode }) } else { return .optionalAction({ - item.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, item.message.peers[peerId]) + item.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, item.message.peers[peerId]) }) } } @@ -3253,7 +3253,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode } item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId) } else if let peer = forwardInfo.source ?? forwardInfo.author { - item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil) + item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil) } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) } @@ -3292,7 +3292,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode }) case let .peerMention(peerId, _): return .action({ - self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil) + self.item?.controllerInteraction.openPeer(peerId, .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil) }) case let .textMention(name): return .action({ diff --git a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift index 939444c7a0..9c59e4d874 100644 --- a/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift +++ b/submodules/TelegramUI/Sources/ChatMessageDateHeader.swift @@ -612,9 +612,9 @@ final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode { self.controllerInteraction.displayMessageTooltip(id, self.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, self.avatarNode.frame) } else { if let channel = self.peer as? TelegramChannel, case .broadcast = channel.info { - self.controllerInteraction.openPeer(self.peerId, .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, nil) + self.controllerInteraction.openPeer(self.peerId, .chat(textInputState: nil, subject: nil, peekData: nil), self.messageReference, false, nil) } else { - self.controllerInteraction.openPeer(self.peerId, .info, self.messageReference, nil) + self.controllerInteraction.openPeer(self.peerId, .info, self.messageReference, false, nil) } } } diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index c10623445e..632c3f2152 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -918,7 +918,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId) } else if let peer = forwardInfo.source ?? forwardInfo.author { - item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, nil) + item.controllerInteraction.openPeer(peer.id, peer is TelegramUser ? .info : .chat(textInputState: nil, subject: nil, peekData: nil), nil, false, nil) } else if let _ = forwardInfo.authorSignature { item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil) } diff --git a/submodules/TelegramUI/Sources/ChatMessageItemView.swift b/submodules/TelegramUI/Sources/ChatMessageItemView.swift index 7f48d56237..91ec438ad8 100644 --- a/submodules/TelegramUI/Sources/ChatMessageItemView.swift +++ b/submodules/TelegramUI/Sources/ChatMessageItemView.swift @@ -847,7 +847,7 @@ public class ChatMessageItemView: ListViewItemNode, ChatMessageItemNodeProtocol case .setupPoll: break case let .openUserProfile(peerId): - item.controllerInteraction.openPeer(peerId, .info, nil, nil) + item.controllerInteraction.openPeer(peerId, .info, nil, false, nil) case let .openWebView(url, simple): item.controllerInteraction.openWebView(button.title, url, simple, false) } diff --git a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift index 4f527c518d..a8f8915fe1 100644 --- a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -76,7 +76,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { } navigationData = .chat(textInputState: nil, subject: subject, peekData: nil) } - item.controllerInteraction.openPeer(id, navigationData, nil, nil) + item.controllerInteraction.openPeer(id, navigationData, nil, false, nil) case let .join(_, joinHash): item.controllerInteraction.openJoinLink(joinHash) } diff --git a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift index cb4bcc5e94..62bd25fd6e 100644 --- a/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatPinnedMessageTitlePanelNode.swift @@ -851,7 +851,7 @@ final class ChatPinnedMessageTitlePanelNode: ChatTitleAccessoryPanelNode { case .setupPoll: break case let .openUserProfile(peerId): - controllerInteraction.openPeer(peerId, .info, nil, nil) + controllerInteraction.openPeer(peerId, .info, nil, false, nil) case let .openWebView(url, simple): controllerInteraction.openWebView(button.title, url, simple, false) } diff --git a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift index 26672d5613..eb66759d35 100644 --- a/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatRecentActionsControllerNode.swift @@ -248,7 +248,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, gallerySource: gallerySource)) } return false - }, openPeer: { [weak self] peerId, _, message, peer in + }, openPeer: { [weak self] peerId, _, message, _, peer in if let peerId = peerId, peerId != context.account.peerId { self?.openPeer(peerId: peerId, peer: peer) } diff --git a/submodules/TelegramUI/Sources/DrawingStickersScreen.swift b/submodules/TelegramUI/Sources/DrawingStickersScreen.swift index ecfa7ff302..53c2385a20 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 96d9135a80..deabae37bc 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -67,7 +67,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu } else { return false } - }, openPeer: { _, _, _, _ in + }, openPeer: { _, _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in diff --git a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift index 5b8f1fbdf2..306880c43c 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/Panes/PeerInfoGroupsInCommonPaneNode.swift @@ -186,7 +186,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode { } } let transaction = preparedTransition(from: self.currentEntries, to: entries, context: self.context, presentationData: presentationData, openPeer: { [weak self] peer in - self?.chatControllerInteraction.openPeer(peer.id, .default, nil, nil) + self?.chatControllerInteraction.openPeer(peer.id, .default, nil, false, nil) }, openPeerContextAction: { [weak self] peer, node, gesture in self?.openPeerContextAction(peer, node, gesture) }) diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index fc6eab38f0..0b4cf3e348 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -449,6 +449,12 @@ private enum PeerInfoSettingsSection { case emojiStatus } +private enum PeerInfoReportType { + case `default` + case user + case reaction(MessageId) +} + private final class PeerInfoInteraction { let openChat: () -> Void let openUsername: (String) -> Void @@ -459,7 +465,7 @@ private final class PeerInfoInteraction { let requestDeleteContact: () -> Void let openAddContact: () -> Void let updateBlocked: (Bool) -> Void - let openReport: (Bool) -> Void + let openReport: (PeerInfoReportType) -> Void let openShareBot: () -> Void let openAddBotToGroup: () -> Void let performBotCommand: (PeerInfoBotCommand) -> Void @@ -503,7 +509,7 @@ private final class PeerInfoInteraction { openChat: @escaping () -> Void, openAddContact: @escaping () -> Void, updateBlocked: @escaping (Bool) -> Void, - openReport: @escaping (Bool) -> Void, + openReport: @escaping (PeerInfoReportType) -> Void, openShareBot: @escaping () -> Void, openAddBotToGroup: @escaping () -> Void, performBotCommand: @escaping (PeerInfoBotCommand) -> Void, @@ -900,7 +906,7 @@ private func settingsEditingItems(data: PeerInfoScreenData?, state: PeerInfoStat return result } -private func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, callMessages: [Message]) -> [(AnyHashable, [PeerInfoScreenItem])] { +private func infoItems(data: PeerInfoScreenData?, context: AccountContext, presentationData: PresentationData, interaction: PeerInfoInteraction, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message]) -> [(AnyHashable, [PeerInfoScreenItem])] { guard let data = data else { return [] } @@ -965,13 +971,21 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese })) } } - if let _ = nearbyPeerDistance { + if let reactionSourceMessageId = reactionSourceMessageId { items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 3, text: presentationData.strings.UserInfo_SendMessage, action: { interaction.openChat() })) items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 4, text: presentationData.strings.ReportPeer_Report, color: .destructive, action: { - interaction.openReport(true) + interaction.openReport(.reaction(reactionSourceMessageId)) + })) + } else if let _ = nearbyPeerDistance { + items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 3, text: presentationData.strings.UserInfo_SendMessage, action: { + interaction.openChat() + })) + + items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 4, text: presentationData.strings.ReportPeer_Report, color: .destructive, action: { + interaction.openReport(.user) })) } else { if !data.isContact { @@ -1010,7 +1024,7 @@ private func infoItems(data: PeerInfoScreenData?, context: AccountContext, prese if user.botInfo != nil, !user.isVerified { items[.peerInfo]!.append(PeerInfoScreenActionItem(id: 6, text: presentationData.strings.ReportPeer_Report, action: { - interaction.openReport(false) + interaction.openReport(.default) })) } @@ -1717,6 +1731,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate highlightedButton: nil ) private let nearbyPeerDistance: Int32? + private let reactionSourceMessageId: MessageId? private var dataDisposable: Disposable? private let activeActionDisposable = MetaDisposable() @@ -1757,7 +1772,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } private var didSetReady = false - init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?) { + init(controller: PeerInfoScreenImpl, context: AccountContext, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool, hintGroupInCommon: PeerId?, requestsContext: PeerInvitationImportersContext?) { self.controller = controller self.context = context self.peerId = peerId @@ -1765,6 +1780,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate self.videoCallsEnabled = true self.presentationData = controller.presentationData self.nearbyPeerDistance = nearbyPeerDistance + self.reactionSourceMessageId = reactionSourceMessageId self.callMessages = callMessages self.isSettings = isSettings self.isMediaOnly = context.account.peerId == peerId && !isSettings @@ -1808,8 +1824,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate updateBlocked: { [weak self] block in self?.updateBlocked(block: block) }, - openReport: { [weak self] user in - self?.openReport(user: user, contextController: nil, backAction: nil) + openReport: { [weak self] type in + self?.openReport(type: type, contextController: nil, backAction: nil) }, openShareBot: { [weak self] in self?.openShareBot() @@ -1914,7 +1930,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate return false } return strongSelf.openMessage(id: message.id) - }, openPeer: { [weak self] id, navigation, _, _ in + }, openPeer: { [weak self] id, navigation, _, _, _ in if let id = id { self?.openPeer(peerId: id, navigation: navigation) } @@ -2443,7 +2459,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate let items: [ContextMenuItem] = [ .action(ContextMenuActionItem(text: presentationData.strings.Conversation_LinkDialogOpen, icon: { _ in nil }, action: { _, f in f(.dismissWithoutContent) - self?.chatInterfaceInteraction.openPeer(peer.id, .default, nil, nil) + self?.chatInterfaceInteraction.openPeer(peer.id, .default, nil, false, nil) })) ] let contextController = ContextController(account: strongSelf.context.account, presentationData: presentationData, source: .controller(ContextControllerContentSourceImpl(controller: chatController, sourceNode: node)), items: .single(ContextController.Items(content: .list(items))), gesture: gesture) @@ -4296,7 +4312,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate items.append(.action(ContextMenuActionItem(text: presentationData.strings.ReportPeer_Report, icon: { theme in generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Report"), color: theme.contextMenu.primaryColor) }, action: { [weak self] c, f in - self?.openReport(user: false, contextController: c, backAction: { c in + self?.openReport(type: .default, contextController: c, backAction: { c in if let mainItemsImpl = mainItemsImpl { c.setItems(mainItemsImpl() |> map { ContextController.Items(content: .list($0)) }, minHeight: nil) } @@ -5354,26 +5370,37 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate }) } - private func openReport(user: Bool, contextController: ContextControllerProtocol?, backAction: ((ContextControllerProtocol) -> Void)?) { + private func openReport(type: PeerInfoReportType, contextController: ContextControllerProtocol?, backAction: ((ContextControllerProtocol) -> Void)?) { guard let controller = self.controller else { return } self.view.endEditing(true) - let options: [PeerReportOption] - if user { - options = [.spam, .fake, .violence, .pornography, .childAbuse] - } else { - options = [.spam, .fake, .violence, .pornography, .childAbuse, .copyright, .other] - } - - presentPeerReportOptions(context: self.context, parent: controller, contextController: contextController, backAction: backAction, subject: .peer(self.peerId), options: options, passthrough: true, completion: { [weak self] reason, _ in - if let reason = reason { - DispatchQueue.main.async { - self?.openChatForReporting(reason) + switch type { + case let .reaction(sourceMessageId): + let _ = (self.context.engine.peers.reportPeerReaction(authorId: self.peerId, messageId: sourceMessageId) + |> deliverOnMainQueue).start(completed: { [weak self] in + guard let strongSelf = self else { + return } + strongSelf.controller?.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .emoji(name: "PoliceCar", text: strongSelf.presentationData.strings.Report_Succeed), elevatedLayout: false, action: { _ in return false }), in: .current) + }) + default: + let options: [PeerReportOption] + if case .user = type { + options = [.spam, .fake, .violence, .pornography, .childAbuse] + } else { + options = [.spam, .fake, .violence, .pornography, .childAbuse, .copyright, .other] } - }) + + presentPeerReportOptions(context: self.context, parent: controller, contextController: contextController, backAction: backAction, subject: .peer(self.peerId), options: options, passthrough: true, completion: { [weak self] reason, _ in + if let reason = reason { + DispatchQueue.main.async { + self?.openChatForReporting(reason) + } + } + }) + } } private func openEncryptionKey() { @@ -7258,7 +7285,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate insets.left += sectionInset insets.right += sectionInset - let items = self.isSettings ? settingsItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isExpanded: self.headerNode.isAvatarExpanded) : infoItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, nearbyPeerDistance: self.nearbyPeerDistance, callMessages: self.callMessages) + let items = self.isSettings ? settingsItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, isExpanded: self.headerNode.isAvatarExpanded) : infoItems(data: self.data, context: self.context, presentationData: self.presentationData, interaction: self.interaction, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages) contentHeight += headerHeight if !(self.isSettings && self.state.isEditing) { @@ -7873,6 +7900,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc private let avatarInitiallyExpanded: Bool private let isOpenedFromChat: Bool private let nearbyPeerDistance: Int32? + private let reactionSourceMessageId: MessageId? private let callMessages: [Message] private let isSettings: Bool private let hintGroupInCommon: PeerId? @@ -7911,13 +7939,14 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc private var validLayout: (layout: ContainerViewLayout, navigationHeight: CGFloat)? - public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, callMessages: [Message], isSettings: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil) { + public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)?, peerId: PeerId, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, nearbyPeerDistance: Int32?, reactionSourceMessageId: MessageId?, callMessages: [Message], isSettings: Bool = false, hintGroupInCommon: PeerId? = nil, requestsContext: PeerInvitationImportersContext? = nil) { self.context = context self.updatedPresentationData = updatedPresentationData self.peerId = peerId self.avatarInitiallyExpanded = avatarInitiallyExpanded self.isOpenedFromChat = isOpenedFromChat self.nearbyPeerDistance = nearbyPeerDistance + self.reactionSourceMessageId = reactionSourceMessageId self.callMessages = callMessages self.isSettings = isSettings self.hintGroupInCommon = hintGroupInCommon @@ -8204,7 +8233,7 @@ public final class PeerInfoScreenImpl: ViewController, PeerInfoScreen, KeyShortc } override public func loadDisplayNode() { - self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, callMessages: self.callMessages, isSettings: self.isSettings, hintGroupInCommon: self.hintGroupInCommon, requestsContext: requestsContext) + self.displayNode = PeerInfoScreenNode(controller: self, context: self.context, peerId: self.peerId, avatarInitiallyExpanded: self.avatarInitiallyExpanded, isOpenedFromChat: self.isOpenedFromChat, nearbyPeerDistance: self.nearbyPeerDistance, reactionSourceMessageId: self.reactionSourceMessageId, callMessages: self.callMessages, isSettings: self.isSettings, hintGroupInCommon: self.hintGroupInCommon, requestsContext: requestsContext) self.controllerNode.accountsAndPeers.set(self.accountsAndPeers.get() |> map { $0.1 }) self.controllerNode.activeSessionsContextAndCount.set(self.activeSessionsContextAndCount.get()) self.cachedDataPromise.set(self.controllerNode.cachedDataPromise.get()) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index b5c891a183..97dfa1c9e3 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 @@ -1514,11 +1514,12 @@ public final class SharedAccountContextImpl: SharedAccountContext { private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? { if let _ = peer as? TelegramGroup { - return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: []) + return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: []) } else if let _ = peer as? TelegramChannel { - return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: []) + return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: []) } else if peer is TelegramUser { var nearbyPeerDistance: Int32? + var reactionSourceMessageId: MessageId? var callMessages: [Message] = [] var hintGroupInCommon: PeerId? switch mode { @@ -1530,10 +1531,12 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation break case let .group(id): hintGroupInCommon = id + case let .reaction(messageId): + reactionSourceMessageId = messageId } - return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, callMessages: callMessages, hintGroupInCommon: hintGroupInCommon) + return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nearbyPeerDistance, reactionSourceMessageId: reactionSourceMessageId, callMessages: callMessages, hintGroupInCommon: hintGroupInCommon) } else if peer is TelegramSecretChat { - return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, callMessages: []) + return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: []) } return nil } diff --git a/submodules/TelegramUI/Sources/TelegramRootController.swift b/submodules/TelegramUI/Sources/TelegramRootController.swift index ab664bfbc6..6a6e66e61b 100644 --- a/submodules/TelegramUI/Sources/TelegramRootController.swift +++ b/submodules/TelegramUI/Sources/TelegramRootController.swift @@ -124,7 +124,7 @@ public final class TelegramRootController: NavigationController { sharedContext.switchingData = (nil, nil, nil) } - let accountSettingsController = PeerInfoScreenImpl(context: self.context, updatedPresentationData: nil, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, callMessages: [], isSettings: true) + let accountSettingsController = PeerInfoScreenImpl(context: self.context, updatedPresentationData: nil, peerId: self.context.account.peerId, avatarInitiallyExpanded: false, isOpenedFromChat: false, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], isSettings: true) accountSettingsController.tabBarItemDebugTapAction = { [weak self] in guard let strongSelf = self else { return