Post-release bug fixes

This commit is contained in:
Ali 2020-03-31 15:33:14 +04:00
parent 6739a4a620
commit a453f2d86e
20 changed files with 502 additions and 251 deletions

View File

@ -2,7 +2,7 @@
include Utils.makefile
APP_VERSION="6.0"
APP_VERSION="6.0.1"
CORE_COUNT=$(shell sysctl -n hw.logicalcpu)
CORE_COUNT_MINUS_ONE=$(shell expr ${CORE_COUNT} \- 1)

View File

@ -5,7 +5,7 @@ set -e
BUILD_TELEGRAM_VERSION="1"
MACOS_VERSION="10.15"
XCODE_VERSION="11.4"
XCODE_VERSION="11.2"
GUEST_SHELL="bash"
VM_BASE_NAME="macos$(echo $MACOS_VERSION | sed -e 's/\.'/_/g)_Xcode$(echo $XCODE_VERSION | sed -e 's/\.'/_/g)"

View File

@ -30,9 +30,16 @@ private class AnimatedStickerNodeDisplayEvents: ASDisplayNode {
override func didExitHierarchy() {
super.didExitHierarchy()
if self.value {
self.value = false
self.updated?(false)
DispatchQueue.main.async { [weak self] in
guard let strongSelf = self else {
return
}
if !strongSelf.isInHierarchy {
if strongSelf.value {
strongSelf.value = false
strongSelf.updated?(false)
}
}
}
}
}

View File

@ -300,7 +300,7 @@ private final class ItemNode: ASDisplayNode {
width = badgeBackgroundFrame.maxX
}
return (width, shortTitleSize.width - self.shortTitleNode.insets.left - self.shortTitleNode.insets.right + 5.0)
return (width, shortTitleSize.width - self.shortTitleNode.insets.left - self.shortTitleNode.insets.right)
}
func updateArea(size: CGSize, sideInset: CGFloat, useShortTitle: Bool, transition: ContainedViewLayoutTransition) {

View File

@ -715,7 +715,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
} else {
isRemovedFromTotalUnreadCount = isRemovedFromTotalUnreadCountValue
}
peerPresence = peerPresenceValue
peerPresence = (peerPresenceValue as? TelegramUserPresence).flatMap { presence -> TelegramUserPresence in
TelegramUserPresence(status: presence.status, lastActivity: 0)
}
embeddedState = embeddedStateValue
summaryInfo = summaryInfoValue
inputActivities = inputActivitiesValue
@ -1180,7 +1182,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
switch item.content {
case let .peer(_, renderedPeer, _, _, presence, _ ,_ ,_, _, _, displayAsMessage, _):
if !displayAsMessage, let peer = renderedPeer.peer as? TelegramUser, let presence = presence as? TelegramUserPresence, !isServicePeer(peer) && !peer.flags.contains(.isSupport) && peer.id != item.context.account.peerId {
let relativeStatus = relativeUserPresenceStatus(presence, relativeTo: timestamp)
var updatedPresence = TelegramUserPresence(status: presence.status, lastActivity: 0)
let relativeStatus = relativeUserPresenceStatus(updatedPresence, relativeTo: timestamp)
if case .online = relativeStatus {
online = true
}
@ -1608,7 +1611,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.highlightedBackgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: layoutOffset - separatorHeight - topNegativeInset), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height + separatorHeight + topNegativeInset))
if let peerPresence = peerPresence as? TelegramUserPresence {
strongSelf.peerPresenceManager?.reset(presence: peerPresence)
strongSelf.peerPresenceManager?.reset(presence: TelegramUserPresence(status: peerPresence.status, lastActivity: 0))
}
strongSelf.updateLayout(size: layout.contentSize, leftInset: params.leftInset, rightInset: params.rightInset)
@ -1739,7 +1742,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
transition.updateFrame(node: self.badgeNode, frame: updatedBadgeFrame)
var mentionBadgeFrame = self.mentionBadgeNode.frame
if updatedBadgeFrame.width.isZero {
if updatedBadgeFrame.width.isZero || self.badgeNode.isHidden {
mentionBadgeFrame.origin.x = updatedBadgeFrame.minX - mentionBadgeFrame.width
} else {
mentionBadgeFrame.origin.x = updatedBadgeFrame.minX - 6.0 - mentionBadgeFrame.width

View File

@ -51,7 +51,7 @@ func chatListFilterItems(postbox: Postbox) -> Signal<(Int, [(ChatListFilter, Int
var result: [(ChatListFilter, Int, Bool)] = []
var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int, Bool, PeerGroupId?)] = [:]
var peerTagAndCount: [PeerId: (PeerSummaryCounterTags, Int, Bool, PeerGroupId?, Bool)] = [:]
var totalStates: [PeerGroupId: ChatListTotalUnreadState] = [:]
for entry in unreadCounts.entries {
@ -71,9 +71,9 @@ func chatListFilterItems(postbox: Postbox) -> Signal<(Int, [(ChatListFilter, Int
}
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings, case .muted = notificationSettings.muteState {
peerTagAndCount[peerId] = (tag, peerCount, false, peerView.groupId)
peerTagAndCount[peerId] = (tag, peerCount, false, peerView.groupId, true)
} else {
peerTagAndCount[peerId] = (tag, peerCount, true, peerView.groupId)
peerTagAndCount[peerId] = (tag, peerCount, true, peerView.groupId, false)
}
}
}
@ -147,8 +147,15 @@ func chatListFilterItems(postbox: Postbox) -> Signal<(Int, [(ChatListFilter, Int
}
}
for peerId in filter.data.includePeers.peers {
if let (tag, peerCount, hasUnmuted, groupId) = peerTagAndCount[peerId] {
if let groupId = groupId, !tags.contains(tag) {
if let (tag, peerCount, hasUnmuted, groupIdValue, isMuted) = peerTagAndCount[peerId], peerCount != 0, let groupId = groupIdValue {
var matches = true
if tags.contains(tag) {
if isMuted && filter.data.excludeMuted {
} else {
matches = false
}
}
if matches {
let matchesGroup: Bool
switch groupId {
case .root:
@ -170,8 +177,16 @@ func chatListFilterItems(postbox: Postbox) -> Signal<(Int, [(ChatListFilter, Int
}
}
for peerId in filter.data.excludePeers {
if let (tag, peerCount, _, groupId) = peerTagAndCount[peerId] {
if let groupId = groupId, tags.contains(tag) {
if let (tag, peerCount, _, groupIdValue, isMuted) = peerTagAndCount[peerId], peerCount != 0, let groupId = groupIdValue {
var matches = true
if tags.contains(tag) {
if isMuted && filter.data.excludeMuted {
} else {
matches = false
}
}
if matches {
let matchesGroup: Bool
switch groupId {
case .root:

View File

@ -512,8 +512,8 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
self.hapticFeedback.impact()
switch self.source {
case let .extracted(source):
if let contentAreaInScreenSpace = self.contentAreaInScreenSpace, let maybeContentNode = self.contentContainerNode.contentNode, case let .extracted(_, keepInPlace) = maybeContentNode {
case .extracted:
if let contentAreaInScreenSpace = self.contentAreaInScreenSpace, let maybeContentNode = self.contentContainerNode.contentNode, case .extracted = maybeContentNode {
var updatedContentAreaInScreenSpace = contentAreaInScreenSpace
updatedContentAreaInScreenSpace.origin.x = 0.0
updatedContentAreaInScreenSpace.size.width = self.bounds.width
@ -553,7 +553,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
self.effectView.effect = makeCustomZoomBlurEffect()
self.effectView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2 * animationDurationFactor)
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2 * animationDurationFactor * UIView.animationDurationFactor(), curve: .easeInOut, animations: { [weak self] in
self.propertyAnimator = UIViewPropertyAnimator(duration: 0.2 * animationDurationFactor * UIView.animationDurationFactor(), curve: .easeInOut, animations: {
})
}
@ -601,7 +601,7 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
self.actionsContainerNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: localSourceFrame.center.x - self.actionsContainerNode.position.x, y: localSourceFrame.center.y - self.actionsContainerNode.position.y)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
let contentContainerOffset = CGPoint(x: localContentSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localContentSourceFrame.center.y - self.contentContainerNode.frame.center.y)
let contentContainerOffset = CGPoint(x: localContentSourceFrame.center.x - self.contentContainerNode.frame.center.x - contentParentNode.contentRect.minX, y: localContentSourceFrame.center.y - self.contentContainerNode.frame.center.y - contentParentNode.contentRect.minY)
self.contentContainerNode.layer.animateSpring(from: NSValue(cgPoint: contentContainerOffset), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: springDuration, initialVelocity: 0.0, damping: springDamping, additive: true)
contentParentNode.applyAbsoluteOffsetSpring?(-contentContainerOffset.y, springDuration, springDamping)
}
@ -619,8 +619,6 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
})
if let originalProjectedContentViewFrame = self.originalProjectedContentViewFrame {
let actionsSideInset: CGFloat = (validLayout?.safeInsets.left ?? 0.0) + 11.0
let localSourceFrame = self.view.convert(CGRect(origin: CGPoint(x: originalProjectedContentViewFrame.1.minX, y: originalProjectedContentViewFrame.1.minY), size: CGSize(width: originalProjectedContentViewFrame.1.width, height: originalProjectedContentViewFrame.1.height)), to: self.scrollNode.view)
self.contentContainerNode.layer.animateSpring(from: min(localSourceFrame.width / self.contentContainerNode.frame.width, localSourceFrame.height / self.contentContainerNode.frame.height) as NSNumber, to: 1.0 as NSNumber, keyPath: "transform.scale", duration: springDuration, initialVelocity: 0.0, damping: springDamping)
@ -1121,13 +1119,13 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
let originalActionsY: CGFloat
if keepInPlace {
originalActionsY = originalProjectedContentViewFrame.1.minY - contentActionsSpacing - actionsSize.height
preferredActionsX = originalProjectedContentViewFrame.1.maxX - actionsSize.width
preferredActionsX = max(actionsSideInset, originalProjectedContentViewFrame.1.maxX - actionsSize.width)
} else {
originalActionsY = min(originalProjectedContentViewFrame.1.maxY + contentActionsSpacing, maximumActionsFrameOrigin)
preferredActionsX = originalProjectedContentViewFrame.1.minX
}
var originalActionsFrame = CGRect(origin: CGPoint(x: max(actionsSideInset, min(layout.size.width - actionsSize.width - actionsSideInset, preferredActionsX)), y: originalActionsY), size: actionsSize)
var originalContentX: CGFloat = originalProjectedContentViewFrame.1.minX
let originalContentX: CGFloat = originalProjectedContentViewFrame.1.minX
let originalContentY: CGFloat
if keepInPlace {
originalContentY = originalProjectedContentViewFrame.1.minY
@ -1152,15 +1150,26 @@ private final class ContextControllerNode: ViewControllerTracingNode, UIScrollVi
}
var overflowOffset: CGFloat
let contentContainerFrame: CGRect
var contentContainerFrame: CGRect
if keepInPlace {
overflowOffset = min(0.0, originalActionsFrame.minY - contentTopInset)
contentContainerFrame = originalContentFrame.offsetBy(dx: -contentParentNode.contentRect.minX, dy: -contentParentNode.contentRect.minY)
if keepInPlace && !overflowOffset.isZero {
let offsetDelta = contentParentNode.contentRect.height + 4.0
overflowOffset += offsetDelta
overflowOffset = min(0.0, overflowOffset)
originalActionsFrame.origin.x -= contentParentNode.contentRect.maxX - contentParentNode.contentRect.minX + 14.0
originalActionsFrame.origin.x = max(actionsSideInset, originalActionsFrame.origin.x)
//originalActionsFrame.origin.y += contentParentNode.contentRect.height
if originalActionsFrame.minX < contentContainerFrame.minX {
contentContainerFrame.origin.x = min(originalActionsFrame.maxX + 14.0, layout.size.width - actionsSideInset)
}
originalActionsFrame.origin.y += offsetDelta
if originalActionsFrame.maxY < originalContentFrame.maxY {
originalActionsFrame.origin.y += contentParentNode.contentRect.height
originalActionsFrame.origin.y = min(originalActionsFrame.origin.y, layout.size.height - originalActionsFrame.height - actionsBottomInset)
}
contentHeight -= offsetDelta
}
} else {

View File

@ -56,8 +56,8 @@ public final class ContextControllerSourceNode: ASDisplayNode {
targetContentRect = CGRect(origin: CGPoint(), size: targetNode.bounds.size)
}
let scaleSide = max(targetContentRect.width, targetContentRect.height)
let minScale: CGFloat = (scaleSide - 10.0) / scaleSide
let scaleSide = targetContentRect.width
let minScale: CGFloat = (scaleSide - 15.0) / scaleSide
let currentScale = 1.0 * (1.0 - progress) + minScale * progress
let originalCenterOffsetX: CGFloat = targetNode.bounds.width / 2.0 - targetContentRect.midX
@ -82,8 +82,6 @@ public final class ContextControllerSourceNode: ASDisplayNode {
targetNode.layer.sublayerTransform = sublayerTransform
targetNode.layer.animate(from: NSValue(caTransform3D: previousTransform), to: NSValue(caTransform3D: sublayerTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
//targetNode.layer.animateSpring(from: previousScale as NSNumber, to: currentScale as NSNumber, keyPath: "sublayerTransform.scale", duration: 0.5, delay: 0.0, initialVelocity: 0.0, damping: 90.0)
}
}
}

View File

@ -20,7 +20,7 @@ private class TimerTargetWrapper: NSObject {
}
}
private let beginDelay: Double = 0.1
private let beginDelay: Double = 0.12
private func cancelParentGestures(view: UIView) {
if let gestureRecognizers = view.gestureRecognizers {
@ -117,7 +117,7 @@ public final class ContextGesture: UIGestureRecognizer, UIGestureRecognizerDeleg
}
strongSelf.isValidated = true
if strongSelf.animator == nil {
strongSelf.animator = DisplayLinkAnimator(duration: 0.3, from: 0.0, to: 1.0, update: { value in
strongSelf.animator = DisplayLinkAnimator(duration: 0.2, from: 0.0, to: 1.0, update: { value in
guard let strongSelf = self else {
return
}

View File

@ -117,7 +117,7 @@ final class ChatBotInfoItemNode: ListViewItemNode {
break
case .ignore:
return .fail
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip:
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults:
return .waitForSingleTap
}
}

View File

@ -34,6 +34,7 @@ extension AnimatedStickerNode: GenericAnimatedStickerNode {
class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
private let contextSourceNode: ContextExtractedContentContainingNode
private let containerNode: ContextControllerSourceNode
let imageNode: TransformImageNode
private var animationNode: GenericAnimatedStickerNode?
private var didSetUpAnimationNode = false
@ -71,13 +72,54 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
required init() {
self.contextSourceNode = ContextExtractedContentContainingNode()
self.containerNode = ContextControllerSourceNode()
self.imageNode = TransformImageNode()
self.dateAndStatusNode = ChatMessageDateAndStatusNode()
super.init(layerBacked: false)
self.containerNode.shouldBegin = { [weak self] location in
guard let strongSelf = self else {
return false
}
if !strongSelf.imageNode.frame.contains(location) {
return false
}
if let action = strongSelf.gestureRecognized(gesture: .tap, location: location, recognizer: nil) {
if case .action = action {
return false
}
}
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) {
switch action {
case .action, .optionalAction:
return false
case .openContextMenu:
return true
}
}
return true
}
self.containerNode.activated = { [weak self] gesture, location in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) {
switch action {
case .action, .optionalAction:
break
case let .openContextMenu(tapMessage, selectAll, subFrame):
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture)
}
}
}
self.imageNode.displaysAsynchronously = false
self.addSubnode(self.contextSourceNode)
self.containerNode.addSubnode(self.contextSourceNode)
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
self.addSubnode(self.containerNode)
self.contextSourceNode.contentNode.addSubnode(self.imageNode)
self.contextSourceNode.contentNode.addSubnode(self.dateAndStatusNode)
}
@ -113,8 +155,17 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
return
}
//strongSelf.reactionRecognizer?.cancel()
if strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) {
recognizer.cancel()
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) {
switch action {
case let .action(f):
f()
recognizer.cancel()
case let .optionalAction(f):
f()
recognizer.cancel()
case .openContextMenu:
break
}
}
}
self.view.addGestureRecognizer(recognizer)
@ -655,6 +706,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let strongSelf = self {
strongSelf.appliedForwardInfo = (forwardSource, forwardAuthorSignature)
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
@ -677,6 +729,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
imageApply()
strongSelf.contextSourceNode.contentRect = strongSelf.imageNode.frame
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
if let updatedShareButtonNode = updatedShareButtonNode {
if updatedShareButtonNode !== strongSelf.shareButtonNode {
@ -855,48 +908,63 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
switch recognizer.state {
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
let _ = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil)
if let action = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil) {
if case .doubleTap = gesture {
self.containerNode.cancelGesture()
}
switch action {
case let .action(f):
f()
case let .optionalAction(f):
f()
case let .openContextMenu(tapMessage, selectAll, subFrame):
self.item?.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil)
}
} else if case .tap = gesture {
self.item?.controllerInteraction.clickThroughMessage()
}
}
default:
break
}
}
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> Bool {
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> InternalBubbleTapAction? {
switch gesture {
case .tap:
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
return .optionalAction({
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return true
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
})
}
return true
return nil
}
if let viaBotNode = self.viaBotNode, viaBotNode.frame.contains(location) {
@ -911,14 +979,15 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
}
if let botAddressName = botAddressName {
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
return .optionalAction({
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
})
}
return true
}
}
}
@ -928,8 +997,9 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
return true
return .optionalAction({
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
})
}
}
}
@ -937,9 +1007,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
if let item = self.item, self.imageNode.frame.contains(location) {
if let _ = self.telegramFile {
let _ = item.controllerInteraction.openMessage(item.message, .default)
return .optionalAction({
let _ = item.controllerInteraction.openMessage(item.message, .default)
})
} else if let _ = self.telegramDice {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_Dice, self, self.imageNode.frame.offsetBy(dx: 0.0, dy: self.imageNode.frame.height / 3.0))
return .optionalAction({
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_Dice, self, self.imageNode.frame.offsetBy(dx: 0.0, dy: self.imageNode.frame.height / 3.0))
})
} else if let _ = self.emojiFile {
if let animationNode = self.animationNode as? AnimatedStickerNode {
var startTime: Signal<Double, NoError>
@ -955,39 +1029,38 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
let beatingHearts: [UInt32] = [0x2764, 0x1F90E, 0x1F9E1, 0x1F49A, 0x1F49C, 0x1F49B, 0x1F5A4, 0x1F90D]
if let text = self.item?.message.text, let firstScalar = text.unicodeScalars.first, beatingHearts.contains(firstScalar.value) {
let _ = startTime.start(next: { [weak self] time in
guard let strongSelf = self else {
return
}
let heartbeatHaptic: HeartbeatHaptic
if let current = strongSelf.heartbeatHaptic {
heartbeatHaptic = current
} else {
heartbeatHaptic = HeartbeatHaptic()
heartbeatHaptic.enabled = true
strongSelf.heartbeatHaptic = heartbeatHaptic
}
if !heartbeatHaptic.active {
heartbeatHaptic.start(time: time)
}
return .optionalAction({
let _ = startTime.start(next: { [weak self] time in
guard let strongSelf = self else {
return
}
let heartbeatHaptic: HeartbeatHaptic
if let current = strongSelf.heartbeatHaptic {
heartbeatHaptic = current
} else {
heartbeatHaptic = HeartbeatHaptic()
heartbeatHaptic.enabled = true
strongSelf.heartbeatHaptic = heartbeatHaptic
}
if !heartbeatHaptic.active {
heartbeatHaptic.start(time: time)
}
})
})
}
}
}
return true
}
self.item?.controllerInteraction.clickThroughMessage()
return nil
case .longTap, .doubleTap:
if let item = self.item, self.imageNode.frame.contains(location) {
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, recognizer)
return false
return .openContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.imageNode.frame)
}
case .hold:
break
}
return true
return nil
}
@objc func shareButtonPressed() {

View File

@ -84,6 +84,7 @@ enum ChatMessageBubbleContentTapAction {
case tooltip(String, ASDisplayNode?, CGRect?)
case bankCard(String)
case ignore
case openPollResults(Data)
}
final class ChatMessageBubbleContentItem {

View File

@ -23,7 +23,7 @@ import GridMessageSelectionNode
import AppBundle
import Markdown
private enum InternalBubbleTapAction {
enum InternalBubbleTapAction {
case action(() -> Void)
case optionalAction(() -> Void)
case openContextMenu(tapMessage: Message, selectAll: Bool, subFrame: CGRect)
@ -418,7 +418,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
break
case .ignore:
return .fail
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip:
case .url, .peerMention, .textMention, .botCommand, .hashtag, .instantPage, .wallpaper, .theme, .call, .openMessage, .timecode, .bankCard, .tooltip, .openPollResults:
return .waitForSingleTap
}
}
@ -443,7 +443,6 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
f()
recognizer.cancel()
case .openContextMenu:
//recognizer.cancel()
break
}
}
@ -2309,6 +2308,9 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
if let action = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil) {
if case .doubleTap = gesture {
self.containerNode.cancelGesture()
}
switch action {
case let .action(f):
f()
@ -2546,6 +2548,12 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
let _ = item.controllerInteraction.displayMessageTooltip(item.message.id, text, node, rect)
})
}
case let .openPollResults(option):
if let item = self.item {
return .action({
item.controllerInteraction.openMessagePollResults(item.message.id, option)
})
}
}
}
return nil
@ -2608,6 +2616,8 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePrevewItemNode
})
case .tooltip:
break
case .openPollResults:
break
}
}
if let tapMessage = tapMessage {

View File

@ -21,6 +21,7 @@ private let inlineBotNameFont = nameFont
class ChatMessageInstantVideoItemNode: ChatMessageItemView {
private let contextSourceNode: ContextExtractedContentContainingNode
private let containerNode: ContextControllerSourceNode
private let interactiveVideoNode: ChatMessageInteractiveInstantVideoNode
private var selectionNode: ChatMessageSelectionNode?
@ -57,11 +58,52 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
required init() {
self.contextSourceNode = ContextExtractedContentContainingNode()
self.containerNode = ContextControllerSourceNode()
self.interactiveVideoNode = ChatMessageInteractiveInstantVideoNode()
super.init(layerBacked: false)
self.addSubnode(self.contextSourceNode)
self.containerNode.shouldBegin = { [weak self] location in
guard let strongSelf = self else {
return false
}
if !strongSelf.interactiveVideoNode.frame.contains(location) {
return false
}
if let action = strongSelf.gestureRecognized(gesture: .tap, location: location, recognizer: nil) {
if case .action = action {
return false
}
}
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) {
switch action {
case .action, .optionalAction:
return false
case .openContextMenu:
return true
}
}
return true
}
self.containerNode.activated = { [weak self] gesture, location in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) {
switch action {
case .action, .optionalAction:
break
case let .openContextMenu(tapMessage, selectAll, subFrame):
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture)
}
}
}
self.containerNode.addSubnode(self.contextSourceNode)
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
self.addSubnode(self.containerNode)
self.contextSourceNode.contentNode.addSubnode(self.interactiveVideoNode)
}
@ -219,7 +261,6 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
var updatedReplyBackgroundNode: ASImageNode?
var replyBackgroundImage: UIImage?
var replyMarkup: ReplyMarkupMessageAttribute?
var inlineBotNameString: String?
let availableWidth = max(60.0, params.width - params.leftInset - params.rightInset - videoLayout.contentSize.width - 20.0 - layoutConstants.bubble.edgeInset * 2.0 - avatarInset - layoutConstants.bubble.contentInsets.left)
@ -277,12 +318,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
if let replyAttribute = attribute as? ReplyMessageAttribute, let replyMessage = item.message.associatedMessages[replyAttribute.messageId] {
replyInfoApply = makeReplyInfoLayout(item.presentationData, item.presentationData.strings, item.context, .standalone, replyMessage, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude))
} else if let attribute = attribute as? InlineBotMessageAttribute {
if let peerId = attribute.peerId, let bot = item.message.peers[peerId] as? TelegramUser {
inlineBotNameString = bot.username
} else {
inlineBotNameString = attribute.title
}
} else if let _ = attribute as? InlineBotMessageAttribute {
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty {
replyMarkup = attribute
}
@ -389,6 +425,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _ in
if let strongSelf = self {
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.appliedItem = item
@ -410,6 +447,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
videoApply(videoLayoutData, transition)
strongSelf.contextSourceNode.contentRect = videoFrame
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
if let updatedShareButtonNode = updatedShareButtonNode {
if updatedShareButtonNode !== strongSelf.shareButtonNode {
@ -580,80 +618,20 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
switch recognizer.state {
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
switch gesture {
case .tap:
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
}
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
}
return
}
if let replyInfoNode = self.replyInfoNode, replyInfoNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
return
}
}
}
}
if let forwardInfoNode = self.forwardInfoNode, forwardInfoNode.frame.contains(location) {
if let item = self.item, let forwardInfo = item.message.forwardInfo {
if let sourceMessageId = forwardInfo.sourceMessageId {
if let channel = forwardInfo.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, forwardInfoNode, nil)
return
}
}
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
} else if let id = forwardInfo.source?.id ?? forwardInfo.author?.id {
item.controllerInteraction.openPeer(id, .chat(textInputState: nil, subject: nil), nil)
} else if let _ = forwardInfo.authorSignature {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
}
return
}
}
self.item?.controllerInteraction.clickThroughMessage()
case .longTap, .doubleTap:
if let item = self.item, let videoContentNode = self.interactiveVideoNode.videoContentNode(at: self.view.convert(location, to: self.interactiveVideoNode.view)) {
item.controllerInteraction.openMessageContextMenu(item.message, false, videoContentNode, videoContentNode.bounds, nil)
}
case .hold:
break
if let action = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil) {
if case .doubleTap = gesture {
self.containerNode.cancelGesture()
}
switch action {
case let .action(f):
f()
case let .optionalAction(f):
f()
case let .openContextMenu(tapMessage, selectAll, subFrame):
self.item?.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil)
}
} else if case .tap = gesture {
self.item?.controllerInteraction.clickThroughMessage()
}
}
default:
@ -661,6 +639,92 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView {
}
}
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> InternalBubbleTapAction? {
switch gesture {
case .tap:
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
}
}
return .optionalAction({
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
})
}
}
if let replyInfoNode = self.replyInfoNode, replyInfoNode.frame.contains(location) {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
return .optionalAction({
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
})
}
}
}
}
if let forwardInfoNode = self.forwardInfoNode, forwardInfoNode.frame.contains(location) {
if let item = self.item, let forwardInfo = item.message.forwardInfo {
if let sourceMessageId = forwardInfo.sourceMessageId {
if let channel = forwardInfo.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
return .optionalAction({
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, forwardInfoNode, nil)
})
}
}
return .optionalAction({
item.controllerInteraction.navigateToMessage(item.message.id, sourceMessageId)
})
} else if let id = forwardInfo.source?.id ?? forwardInfo.author?.id {
return .optionalAction({
item.controllerInteraction.openPeer(id, .chat(textInputState: nil, subject: nil), nil)
})
} else if let _ = forwardInfo.authorSignature {
return .optionalAction({
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, forwardInfoNode, nil)
})
}
}
}
return nil
case .longTap, .doubleTap:
if let item = self.item, let videoContentNode = self.interactiveVideoNode.videoContentNode(at: self.view.convert(location, to: self.interactiveVideoNode.view)) {
return .openContextMenu(tapMessage: item.message, selectAll: false, subFrame: videoContentNode.view.convert(videoContentNode.bounds, to: self.view))
}
case .hold:
break
}
return nil
}
@objc func shareButtonPressed() {
if let item = self.item {
if item.content.firstMessage.id.peerId == item.context.account.peerId {

View File

@ -137,13 +137,10 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
let makeDateAndStatusLayout = self.dateAndStatusNode.asyncLayout()
return { item, width, displaySize, statusDisplayType, automaticDownload in
var updatedTheme: ChatPresentationThemeData?
var secretVideoPlaceholderBackgroundImage: UIImage?
var updatedInfoBackgroundImage: UIImage?
var updatedMuteIconImage: UIImage?
if item.presentationData.theme != currentItem?.presentationData.theme {
updatedTheme = item.presentationData.theme
updatedInfoBackgroundImage = PresentationResourcesChat.chatInstantMessageInfoBackgroundImage(item.presentationData.theme.theme)
updatedMuteIconImage = PresentationResourcesChat.chatInstantMessageMuteIconImage(item.presentationData.theme.theme)
}
@ -382,13 +379,11 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
})
}
let mediaManager = item.context.sharedContext.mediaManager
let videoNode = UniversalVideoNode(postbox: item.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: ChatBubbleInstantVideoDecoration(diameter: displaySize.width + 2.0, backgroundImage: instantVideoBackgroundImage, tapped: {
let videoNode = UniversalVideoNode(postbox: item.context.account.postbox, audioSession: mediaManager.audioSession, manager: mediaManager.universalVideoManager, decoration: ChatBubbleInstantVideoDecoration(inset: 2.0, backgroundImage: instantVideoBackgroundImage, tapped: {
if let strongSelf = self {
if let item = strongSelf.item {
if strongSelf.infoBackgroundNode.alpha.isZero {
item.context.sharedContext.mediaManager.playlistControl(.playback(.togglePlayPause), type: .voice)
} else {
//let _ = item.controllerInteraction.openMessage(item.message)
}
}
}
@ -670,9 +665,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode {
self.item?.controllerInteraction.clickThroughMessage()
case .longTap, .doubleTap:
if let item = self.item, let videoNode = self.videoNode, videoNode.frame.contains(location) {
item.controllerInteraction.openMessageContextMenu(item.message, false, self, videoNode.frame, nil)
}
break
case .hold:
break
}

View File

@ -1489,8 +1489,7 @@ class ChatMessagePollBubbleContentNode: ChatMessageBubbleContentNode {
}
if hasNonZeroVoters {
if !isEstimating {
item.controllerInteraction.openMessagePollResults(item.message.id, option.opaqueIdentifier)
return .ignore
return .openPollResults(option.opaqueIdentifier)
}
return .openMessage
}

View File

@ -19,6 +19,7 @@ private let inlineBotNameFont = nameFont
class ChatMessageStickerItemNode: ChatMessageItemView {
private let contextSourceNode: ContextExtractedContentContainingNode
private let containerNode: ContextControllerSourceNode
let imageNode: TransformImageNode
var textNode: TextNode?
@ -45,13 +46,54 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
required init() {
self.contextSourceNode = ContextExtractedContentContainingNode()
self.containerNode = ContextControllerSourceNode()
self.imageNode = TransformImageNode()
self.dateAndStatusNode = ChatMessageDateAndStatusNode()
super.init(layerBacked: false)
self.containerNode.shouldBegin = { [weak self] location in
guard let strongSelf = self else {
return false
}
if !strongSelf.imageNode.frame.contains(location) {
return false
}
if let action = strongSelf.gestureRecognized(gesture: .tap, location: location, recognizer: nil) {
if case .action = action {
return false
}
}
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) {
switch action {
case .action, .optionalAction:
return false
case .openContextMenu:
return true
}
}
return true
}
self.containerNode.activated = { [weak self] gesture, location in
guard let strongSelf = self, let item = strongSelf.item else {
return
}
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: location, recognizer: nil) {
switch action {
case .action, .optionalAction:
break
case let .openContextMenu(tapMessage, selectAll, subFrame):
item.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, strongSelf, subFrame, gesture)
}
}
}
self.imageNode.displaysAsynchronously = false
self.addSubnode(self.contextSourceNode)
self.containerNode.addSubnode(self.contextSourceNode)
self.containerNode.targetNodeForActivationProgress = self.contextSourceNode.contentNode
self.addSubnode(self.containerNode)
self.contextSourceNode.contentNode.addSubnode(self.imageNode)
self.contextSourceNode.contentNode.addSubnode(self.dateAndStatusNode)
}
@ -87,8 +129,17 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
return
}
//strongSelf.reactionRecognizer?.cancel()
if strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) {
recognizer.cancel()
if let action = strongSelf.gestureRecognized(gesture: .longTap, location: point, recognizer: recognizer) {
switch action {
case let .action(f):
f()
recognizer.cancel()
case let .optionalAction(f):
f()
recognizer.cancel()
case .openContextMenu:
break
}
}
}
self.view.addGestureRecognizer(recognizer)
@ -409,9 +460,6 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
return (ListViewItemNodeLayout(contentSize: layoutSize, insets: layoutInsets), { [weak self] animation, _ in
if let strongSelf = self {
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
var transition: ContainedViewLayoutTransition = .immediate
if case let .System(duration) = animation {
transition = .animated(duration: duration, curve: .spring)
@ -421,7 +469,11 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
transition.updateFrame(node: strongSelf.imageNode, frame: updatedImageFrame)
imageApply()
strongSelf.containerNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.contextSourceNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.contextSourceNode.contentNode.frame = CGRect(origin: CGPoint(), size: layoutSize)
strongSelf.contextSourceNode.contentRect = strongSelf.imageNode.frame
strongSelf.containerNode.targetNodeForActivationProgressContentRect = strongSelf.contextSourceNode.contentRect
dateAndStatusApply(false)
@ -589,48 +641,65 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
switch recognizer.state {
case .ended:
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation {
let _ = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil)
if case .doubleTap = gesture {
self.containerNode.cancelGesture()
}
if let action = self.gestureRecognized(gesture: gesture, location: location, recognizer: nil) {
if case .doubleTap = gesture {
self.containerNode.cancelGesture()
}
switch action {
case let .action(f):
f()
case let .optionalAction(f):
f()
case let .openContextMenu(tapMessage, selectAll, subFrame):
self.item?.controllerInteraction.openMessageContextMenu(tapMessage, selectAll, self, subFrame, nil)
}
} else if case .tap = gesture {
self.item?.controllerInteraction.clickThroughMessage()
}
}
default:
break
}
}
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> Bool {
private func gestureRecognized(gesture: TapLongTapOrDoubleTapGesture, location: CGPoint, recognizer: TapLongTapOrDoubleTapGestureRecognizer?) -> InternalBubbleTapAction? {
switch gesture {
case .tap:
if let avatarNode = self.accessoryItemNode as? ChatMessageAvatarAccessoryItemNode, avatarNode.frame.contains(location) {
if let item = self.item, let author = item.content.firstMessage.author {
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
return .optionalAction({
var openPeerId = item.effectiveAuthorId ?? author.id
var navigate: ChatControllerInteractionNavigateToPeer
if item.content.firstMessage.id.peerId == item.context.account.peerId {
navigate = .chat(textInputState: nil, subject: nil)
} else {
navigate = .info
}
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
return true
for attribute in item.content.firstMessage.attributes {
if let attribute = attribute as? SourceReferenceMessageAttribute {
openPeerId = attribute.messageId.peerId
navigate = .chat(textInputState: nil, subject: .message(attribute.messageId))
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
if item.effectiveAuthorId?.namespace == Namespaces.Peer.Empty {
item.controllerInteraction.displayMessageTooltip(item.content.firstMessage.id, item.presentationData.strings.Conversation_ForwardAuthorHiddenTooltip, self, avatarNode.frame)
} else {
if let channel = item.content.firstMessage.forwardInfo?.author as? TelegramChannel, channel.username == nil {
if case .member = channel.participationStatus {
} else {
item.controllerInteraction.displayMessageTooltip(item.message.id, item.presentationData.strings.Conversation_PrivateChannelTooltip, self, avatarNode.frame)
}
}
item.controllerInteraction.openPeer(openPeerId, navigate, item.message)
}
})
}
return true
}
if let viaBotNode = self.viaBotNode, viaBotNode.frame.contains(location) {
@ -645,14 +714,15 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
}
if let botAddressName = botAddressName {
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
return .optionalAction({
item.controllerInteraction.updateInputState { textInputState in
return ChatTextInputState(inputText: NSAttributedString(string: "@" + botAddressName + " "))
}
item.controllerInteraction.updateInputMode { _ in
return .text
}
})
}
return true
}
}
}
@ -662,29 +732,29 @@ class ChatMessageStickerItemNode: ChatMessageItemView {
if let item = self.item {
for attribute in item.message.attributes {
if let attribute = attribute as? ReplyMessageAttribute {
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
return true
return .optionalAction({
item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId)
})
}
}
}
}
if let item = self.item, self.imageNode.frame.contains(location) {
let _ = item.controllerInteraction.openMessage(item.message, .default)
return true
return .optionalAction({
let _ = item.controllerInteraction.openMessage(item.message, .default)
})
}
self.item?.controllerInteraction.clickThroughMessage()
return nil
case .longTap, .doubleTap:
if let item = self.item, self.imageNode.frame.contains(location) {
item.controllerInteraction.openMessageContextMenu(item.message, false, self, self.imageNode.frame, recognizer)
return false
return .openContextMenu(tapMessage: item.message, selectAll: false, subFrame: self.imageNode.frame)
}
case .hold:
break
}
return true
return nil
}
@objc func shareButtonPressed() {

View File

@ -86,7 +86,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
if case let .chatSelection(_, selectedChats, additionalCategories) = mode {
placeholder = self.presentationData.strings.ChatListFilter_AddChatsTitle
let chatListNode = ChatListNode(context: context, groupId: .root, previewing: false, fillPreloadItems: false, mode: .peers(filter: [], isSelecting: true, additionalCategories: additionalCategories?.categories ?? []), theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
let chatListNode = ChatListNode(context: context, groupId: .root, previewing: false, fillPreloadItems: false, mode: .peers(filter: [.excludeSecretChats], isSelecting: true, additionalCategories: additionalCategories?.categories ?? []), theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, disableAnimations: true)
chatListNode.updateState { state in
var state = state
for peerId in selectedChats {
@ -171,12 +171,15 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
var searchGroups = false
var searchChannels = false
var globalSearch = false
if case let .peerSelection(peerSelection) = mode {
searchChatList = peerSelection.searchChatList
searchGroups = peerSelection.searchGroups
searchChannels = peerSelection.searchChannels
switch mode {
case .groupCreation, .channelCreation:
globalSearch = true
} else if case .chatSelection = mode {
case let .peerSelection(searchChatListValue, searchGroupsValue, searchChannelsValue):
searchChatList = searchChatListValue
searchGroups = searchGroupsValue
searchChannels = searchChannelsValue
globalSearch = true
case .chatSelection:
searchChatList = true
searchGroups = true
searchChannels = true

View File

@ -61,6 +61,8 @@ func textStringForForwardedMessage(_ message: Message, strings: PresentationStri
return ("", true)
case _ as TelegramMediaPoll:
return (strings.ForwardedPolls(1), true)
case _ as TelegramMediaDice:
return ("🎲", false)
default:
break
}

View File

@ -14,10 +14,12 @@ public final class ChatBubbleInstantVideoDecoration: UniversalVideoDecoration {
private let tapped: () -> Void
private var contentNode: (ASDisplayNode & UniversalVideoContentNode)?
private let inset: CGFloat
private var validLayoutSize: CGSize?
public init(diameter: CGFloat, backgroundImage: UIImage?, tapped: @escaping () -> Void) {
public init(inset: CGFloat, backgroundImage: UIImage?, tapped: @escaping () -> Void) {
self.inset = inset
self.tapped = tapped
let backgroundNode = ASImageNode()
@ -29,7 +31,6 @@ public final class ChatBubbleInstantVideoDecoration: UniversalVideoDecoration {
self.contentContainerNode = ASDisplayNode()
self.contentContainerNode.clipsToBounds = true
self.contentContainerNode.cornerRadius = (diameter - 3.0) / 2.0
let foregroundNode = ASDisplayNode()
self.foregroundNode = foregroundNode
@ -65,6 +66,9 @@ public final class ChatBubbleInstantVideoDecoration: UniversalVideoDecoration {
public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayoutSize = size
let diameter = size.width + inset
self.contentContainerNode.cornerRadius = (diameter - 3.0) / 2.0
if let backgroundNode = self.backgroundNode {
transition.updateFrame(node: backgroundNode, frame: CGRect(origin: CGPoint(), size: size))
}