From 6b78a35695e3e6aeb122d33e4350879129af84bf Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Sat, 25 Apr 2026 19:53:39 +0200 Subject: [PATCH] Various fixes --- .../Sources/ChatTextInputPanelNode.swift | 17 ++--- .../Sources/ChatScheduleTimeScreen.swift | 16 +++-- ...rollerOpenMessageReactionContextMenu.swift | 62 +++++++++++-------- .../ChatInterfaceStateContextMenus.swift | 53 ++++++++++------ 4 files changed, 90 insertions(+), 58 deletions(-) diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index ee6f187026..a40c0fe8dd 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -3103,17 +3103,18 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg self.textPlaceholderNode.view.setMonochromaticEffect(tintColor: placeholderColor) self.textInputNode?.textView.accessibilityHint = currentPlaceholder + + if transition.isAnimated, let snapshotView = self.textPlaceholderNode.view.snapshotView(afterScreenUpdates: false) { + snapshotView.frame = self.textPlaceholderNode.frame + self.textPlaceholderNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.textPlaceholderNode.view) + snapshotView.layer.animateAlpha(from: snapshotView.alpha, to: 0.0, duration: 0.22, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + self.textPlaceholderNode.layer.animateAlpha(from: 0.0, to: self.textPlaceholderNode.alpha, duration: 0.18) + } let placeholderSize = self.textPlaceholderNode.updateLayout(CGSize(width: textPlaceholderMaxWidth, height: CGFloat.greatestFiniteMagnitude)) - if transition.isAnimated, let snapshotLayer = self.textPlaceholderNode.layer.snapshotContentTree() { - self.textPlaceholderNode.supernode?.layer.insertSublayer(snapshotLayer, above: self.textPlaceholderNode.layer) - snapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.22, removeOnCompletion: false, completion: { [weak snapshotLayer] _ in - snapshotLayer?.removeFromSuperlayer() - }) - self.textPlaceholderNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.18) - } - textPlaceholderSize = placeholderSize } else { textPlaceholderSize = self.textPlaceholderNode.updateLayout(CGSize(width: textPlaceholderMaxWidth, height: CGFloat.greatestFiniteMagnitude)) diff --git a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift index d4adae0648..a8843ec543 100644 --- a/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift +++ b/submodules/TelegramUI/Components/ChatScheduleTimeController/Sources/ChatScheduleTimeScreen.swift @@ -185,11 +185,19 @@ private final class ChatScheduleTimeSheetContentComponent: Component { //TODO:localize let text: String - if case .user = peer { - if isSilentPosting { - text = "\(peer.compactDisplayTitle) will receive a silent notification" + if case let .user(user) = peer { + if user.id == component.context.account.peerId { + if isSilentPosting { + text = "You will receive a silent notification" + } else { + text = "You will be notified" + } } else { - text = "\(peer.compactDisplayTitle) will be notified" + if isSilentPosting { + text = "\(peer.compactDisplayTitle) will receive a silent notification" + } else { + text = "\(peer.compactDisplayTitle) will be notified" + } } } else if isChannel { if isSilentPosting { diff --git a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift index 4e0f3d3898..3f53bb2c28 100644 --- a/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift +++ b/submodules/TelegramUI/Sources/ChatControllerOpenMessageReactionContextMenu.swift @@ -188,9 +188,41 @@ extension ChatControllerImpl { } var dismissController: ((@escaping () -> Void) -> Void)? + let canDeleteReactions = canDeleteOtherUserMessages(message: message) + let canViewReactions = canViewMessageReactionList(message: message) + let deleteReaction: ((EnginePeer, MessageReaction.Reaction) -> Void)? + if canDeleteReactions && canViewReactions { + deleteReaction = { [weak self] peer, _ in + dismissController?({ [weak self] in + guard let self, self.chatLocation.peerId?.namespace == Namespaces.Peer.CloudChannel else { + return + } + let _ = (self.context.sharedContext.chatAvailableMessageActions( + engine: self.context.engine, + accountPeerId: self.context.account.peerId, + messageIds: Set([message.id]), + keepUpdated: false + ) + |> deliverOnMainQueue).startStandalone(next: { [weak self] actions in + guard let self, !actions.options.isEmpty else { + return + } + self.presentBanMessageOptions( + accountPeerId: self.context.account.peerId, + author: peer._asPeer(), + messageIds: Set([message.id]), + options: actions.options, + reaction: true + ) + }) + }) + } + } else { + deleteReaction = nil + } var items: ContextController.Items - if canViewMessageReactionList(message: message) { + if canViewReactions { items = ContextController.Items(content: .custom(ReactionListContextMenuContent( context: self.context, displayReadTimestamps: true, @@ -210,31 +242,7 @@ extension ChatControllerImpl { self.openPeer(peer: peer, navigation: .default, fromMessage: MessageReference(message), fromReactionMessageId: hasReaction ? message.id : nil) }) }, - deleteReaction: { [weak self] peer, _ in - dismissController?({ [weak self] in - guard let self, self.chatLocation.peerId?.namespace == Namespaces.Peer.CloudChannel else { - return - } - let _ = (self.context.sharedContext.chatAvailableMessageActions( - engine: self.context.engine, - accountPeerId: self.context.account.peerId, - messageIds: Set([message.id]), - keepUpdated: false - ) - |> deliverOnMainQueue).startStandalone(next: { [weak self] actions in - guard let self, !actions.options.isEmpty else { - return - } - self.presentBanMessageOptions( - accountPeerId: self.context.account.peerId, - author: peer._asPeer(), - messageIds: Set([message.id]), - options: actions.options, - reaction: true - ) - }) - }) - } + deleteReaction: deleteReaction ))) } else { items = ContextController.Items(content: .list([])) @@ -325,7 +333,7 @@ extension ChatControllerImpl { let presentationContext = self.controllerInteraction?.presentationContext let premiumConfiguration = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }) - if "".isEmpty { + if canDeleteReactions && canViewReactions { items.tip = .deleteReaction } else if !packReferences.isEmpty && !premiumConfiguration.isPremiumDisabled { items.tip = .animatedEmoji(text: nil, arguments: nil, file: nil, action: nil) diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index 3b64e0803d..a881957c9a 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -52,6 +52,13 @@ func canEditMessage(context: AccountContext, limitsConfiguration: EngineConfigur return canEditMessage(accountPeerId: context.account.peerId, limitsConfiguration: limitsConfiguration, message: message) } +func canDeleteOtherUserMessages(message: Message) -> Bool { + guard let channel = message.peers[message.id.peerId] as? TelegramChannel else { + return false + } + return channel.hasPermission(.deleteAllMessages) +} + private func canEditMessage(accountPeerId: PeerId, limitsConfiguration: EngineConfiguration.Limits, message: Message, reschedule: Bool = false) -> Bool { var hasEditRights = false var unlimitedInterval = reschedule @@ -2173,24 +2180,9 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState displayReadTimestamps = true } - c.pushItems(items: .single(ContextController.Items(content: .custom(ReactionListContextMenuContent( - context: context, - displayReadTimestamps: displayReadTimestamps, - availableReactions: availableReactions, - animationCache: controllerInteraction.presentationContext.animationCache, - animationRenderer: controllerInteraction.presentationContext.animationRenderer, - message: EngineMessage(message), - reaction: nil, - readStats: stats, - back: { [weak c] in - c?.popItems() - }, - openPeer: { [weak c] peer, hasReaction in - c?.dismiss(completion: { - controllerInteraction.openPeer(peer, .default, MessageReference(message), hasReaction ? .reaction : .default) - }) - }, - deleteReaction: { [weak c] peer, _ in + let deleteReaction: ((EnginePeer, MessageReaction.Reaction) -> Void)? + if canDeleteOtherUserMessages(message: message) { + deleteReaction = { [weak c] peer, _ in c?.dismiss(completion: { guard let chatController = interfaceInteraction.chatController() as? ChatControllerImpl, chatController.chatLocation.peerId?.namespace == Namespaces.Peer.CloudChannel else { return @@ -2209,11 +2201,34 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState accountPeerId: context.account.peerId, author: peer._asPeer(), messageIds: Set([message.id]), - options: actions.options + options: actions.options, + reaction: true ) }) }) } + } else { + deleteReaction = nil + } + + c.pushItems(items: .single(ContextController.Items(content: .custom(ReactionListContextMenuContent( + context: context, + displayReadTimestamps: displayReadTimestamps, + availableReactions: availableReactions, + animationCache: controllerInteraction.presentationContext.animationCache, + animationRenderer: controllerInteraction.presentationContext.animationRenderer, + message: EngineMessage(message), + reaction: nil, + readStats: stats, + back: { [weak c] in + c?.popItems() + }, + openPeer: { [weak c] peer, hasReaction in + c?.dismiss(completion: { + controllerInteraction.openPeer(peer, .default, MessageReference(message), hasReaction ? .reaction : .default) + }) + }, + deleteReaction: deleteReaction )), tip: tip))) } else { f(.default)