From 0619f73819f6515dacc81d815c17a74cb02599a5 Mon Sep 17 00:00:00 2001 From: Isaac <> Date: Sat, 18 Oct 2025 02:00:35 +0800 Subject: [PATCH] Updates --- .../Sources/CallStatusBarNode.swift | 2 +- .../Sources/PresentationGroupCall.swift | 2 +- .../TelegramEngine/Calls/GroupCalls.swift | 46 ++++++++++++------- .../Sources/ChatTextInputPanelComponent.swift | 7 +++ .../Sources/ChatTextInputPanelNode.swift | 33 +++++++------ .../Sources/MessageInputPanelComponent.swift | 5 ++ .../StoryContentLiveChatComponent.swift | 36 +++++++++++---- .../Sources/StoryItemContentComponent.swift | 18 +++++--- .../StoryItemSetContainerComponent.swift | 2 +- 9 files changed, 101 insertions(+), 50 deletions(-) diff --git a/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift b/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift index 9646f8c7a3..e40dafcf8a 100644 --- a/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift +++ b/submodules/TelegramCallsUI/Sources/CallStatusBarNode.swift @@ -218,7 +218,7 @@ public class CallStatusBarNodeImpl: CallStatusBarNode { private var reactionItems: [ReactionItem]? private var messagesState: GroupCallMessagesContext.State? private let messagesStateDisposable = MetaDisposable() - private var currentMessageId: Int64? + private var currentMessageId: GroupCallMessagesContext.Message.Id? private let hierarchyTrackingNode: HierarchyTrackingNode private var isCurrentlyInHierarchy = true diff --git a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift index 028bfe5803..4226c26374 100644 --- a/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift +++ b/submodules/TelegramCallsUI/Sources/PresentationGroupCall.swift @@ -4042,7 +4042,7 @@ public final class PresentationGroupCallImpl: PresentationGroupCall { } } - public func deleteMessage(id: Int64) { + public func deleteMessage(id: GroupCallMessagesContext.Message.Id) { if let messagesContext = self.messagesContext { messagesContext.deleteMessage(id: id) } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift index be7dd051ab..a2cacc309f 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Calls/GroupCalls.swift @@ -3674,7 +3674,22 @@ private func deserializeGroupCallMessage(data: Data) -> (randomId: Int64, text: public final class GroupCallMessagesContext { public final class Message: Equatable { - public let id: Int64 + public struct Id: Hashable { + public enum Space { + case local + case remote + } + + public var space: Space + public var id: Int64 + + public init(space: Space, id: Int64) { + self.space = space + self.id = id + } + } + + public let id: Id public let author: EnginePeer? public let text: String public let entities: [MessageTextEntity] @@ -3682,7 +3697,7 @@ public final class GroupCallMessagesContext { public let lifetime: Int32 public let paidStars: Int64? - public init(id: Int64, author: EnginePeer?, text: String, entities: [MessageTextEntity], date: Int32, lifetime: Int32, paidStars: Int64?) { + public init(id: Id, author: EnginePeer?, text: String, entities: [MessageTextEntity], date: Int32, lifetime: Int32, paidStars: Int64?) { self.id = id self.author = author self.text = text @@ -3692,7 +3707,7 @@ public final class GroupCallMessagesContext { self.paidStars = paidStars } - public func withId(_ id: Int64) -> Message { + public func withId(_ id: Id) -> Message { return Message( id: id, author: self.author, @@ -3793,9 +3808,7 @@ public final class GroupCallMessagesContext { } switch update.update { case let .newPlaintextMessage(authorId, messageId, text, entities, timestamp, paidMessageStars): - if authorId != self.account.peerId { - addedMessages.append((authorId, messageId, text, entities, timestamp, paidMessageStars)) - } + addedMessages.append((authorId, messageId, text, entities, timestamp, paidMessageStars)) case let .newOpaqueMessage(authorId, data): if authorId != self.account.peerId { addedOpaqueMessages.append((authorId, data)) @@ -3833,7 +3846,7 @@ public final class GroupCallMessagesContext { continue } messages.append(Message( - id: randomId, + id: Message.Id(space: .remote, id: randomId), author: transaction.getPeer(addedOpaqueMessage.authorId).flatMap(EnginePeer.init), text: text, entities: entities, @@ -3856,7 +3869,7 @@ public final class GroupCallMessagesContext { } let message = Message( - id: Int64(addedMessage.messageId), + id: Message.Id(space: .remote, id: Int64(addedMessage.messageId)), author: transaction.getPeer(addedMessage.authorId).flatMap(EnginePeer.init), text: addedMessage.text, entities: addedMessage.entities, @@ -3874,7 +3887,7 @@ public final class GroupCallMessagesContext { return } for message in messages { - self.processedIds.insert(message.id) + self.processedIds.insert(message.id.id) } var state = self.state var existingIds = Set(state.messages.map(\.id)) @@ -3963,7 +3976,7 @@ public final class GroupCallMessagesContext { var state = self.state let message = Message( - id: randomId, + id: Message.Id(space: .local, id: randomId), author: fromPeer.flatMap(EnginePeer.init), text: text, entities: entities, @@ -4029,12 +4042,13 @@ public final class GroupCallMessagesContext { for update in updates.allUpdates { if case let .updateMessageID(id, randomIdValue) = update { if randomIdValue == randomId { + self.processedIds.insert(Int64(id)) var state = self.state - if let index = state.messages.firstIndex(where: { $0.id == randomId }) { - state.messages[index] = state.messages[index].withId(Int64(id)) + if let index = state.messages.firstIndex(where: { $0.id == Message.Id(space: .local, id: randomId) }) { + state.messages[index] = state.messages[index].withId(Message.Id(space: .remote, id: Int64(id))) } - if let index = state.pinnedMessages.firstIndex(where: { $0.id == randomId }) { - state.pinnedMessages[index] = state.pinnedMessages[index].withId(Int64(id)) + if let index = state.pinnedMessages.firstIndex(where: { $0.id == Message.Id(space: .local, id: randomId) }) { + state.pinnedMessages[index] = state.pinnedMessages[index].withId(Message.Id(space: .remote, id: Int64(id))) } self.state = state break @@ -4048,7 +4062,7 @@ public final class GroupCallMessagesContext { }) } - func deleteMessage(id: Int64) { + func deleteMessage(id: Message.Id) { var updatedState: State? if let index = self.state.messages.firstIndex(where: { $0.id == id }) { if updatedState == nil { @@ -4091,7 +4105,7 @@ public final class GroupCallMessagesContext { } } - public func deleteMessage(id: Int64) { + public func deleteMessage(id: Message.Id) { self.impl.with { impl in impl.deleteMessage(id: id) } diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelComponent.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelComponent.swift index b975c01992..10010331e4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelComponent.swift @@ -234,6 +234,13 @@ public final class ChatTextInputPanelComponent: Component { textView.deleteBackward() } + public func activateInput() { + guard let panelNode = self.panelNode else { + return + } + panelNode.ensureFocused() + } + public func updateState(transition: ComponentTransition) { self.state?.updated(transition: transition) } diff --git a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift index aed0c20108..43c31cfe39 100644 --- a/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatTextInputPanelNode/Sources/ChatTextInputPanelNode.swift @@ -1783,20 +1783,6 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg peerUpdated = true } - if let customLeftAction = self.customLeftAction { - switch customLeftAction { - case let .toggleExpanded(_, isExpanded): - var iconTransform = CATransform3DIdentity - iconTransform = CATransform3DTranslate(iconTransform, 0.0, 1.0, 0.0) - if isExpanded || "".isEmpty { - iconTransform = CATransform3DRotate(iconTransform, CGFloat.pi, 0.0, 0.0, 1.0) - } - transition.updateTransform(layer: self.attachmentButtonIcon.layer, transform: iconTransform) - } - } else { - self.attachmentButtonIcon.layer.transform = CATransform3DIdentity - } - if peerUpdated || previousState?.chatLocation != interfaceState.chatLocation || previousState?.interfaceState.silentPosting != interfaceState.interfaceState.silentPosting || themeUpdated || !self.initializedPlaceholder || previousState?.keyboardButtonsMessage?.id != interfaceState.keyboardButtonsMessage?.id || previousState?.keyboardButtonsMessage?.visibleReplyMarkupPlaceholder != interfaceState.keyboardButtonsMessage?.visibleReplyMarkupPlaceholder || dismissedButtonMessageUpdated || replyMessageUpdated || (previousState?.interfaceState.editMessage == nil) != (interfaceState.interfaceState.editMessage == nil) || previousState?.forumTopicData != interfaceState.forumTopicData || previousState?.replyMessage?.id != interfaceState.replyMessage?.id || previousState?.sendPaidMessageStars != interfaceState.sendPaidMessageStars { self.initializedPlaceholder = true @@ -1923,6 +1909,20 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg } } + if let customLeftAction = self.customLeftAction { + switch customLeftAction { + case let .toggleExpanded(_, isExpanded): + var iconTransform = CATransform3DIdentity + iconTransform = CATransform3DTranslate(iconTransform, 0.0, 1.0, 0.0) + if isExpanded { + iconTransform = CATransform3DRotate(iconTransform, CGFloat.pi, 0.0, 0.0, 1.0) + } + transition.updateTransform(layer: self.attachmentButtonIcon.layer, transform: iconTransform) + } + } else { + self.attachmentButtonIcon.layer.transform = CATransform3DIdentity + } + var textFieldMinHeight: CGFloat = 33.0 if let presentationInterfaceState = self.presentationInterfaceState { textFieldMinHeight = calclulateTextFieldMinHeight(presentationInterfaceState, metrics: metrics) @@ -2868,7 +2868,10 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg transition.updateFrame(node: self.attachmentButtonDisabledNode, frame: self.attachmentButtonBackground.frame) if let image = self.attachmentButtonIcon.image { - transition.updateFrame(view: self.attachmentButtonIcon, frame: CGRect(origin: CGPoint(x: floor((attachmentButtonFrame.width - image.size.width) * 0.5), y: floor((attachmentButtonFrame.height - image.size.height) * 0.5)), size: image.size)) + let attachmentButtonIconFrame = CGRect(origin: CGPoint(x: floor((attachmentButtonFrame.width - image.size.width) * 0.5), y: floor((attachmentButtonFrame.height - image.size.height) * 0.5)), size: image.size) + let transition = ComponentTransition(transition) + transition.setPosition(view: self.attachmentButtonIcon, position: attachmentButtonIconFrame.center) + transition.setBounds(view: self.attachmentButtonIcon, bounds: CGRect(origin: CGPoint(), size: attachmentButtonIconFrame.size)) } if let context = self.context, let interfaceState = self.presentationInterfaceState, let editMessageState = interfaceState.editMessageState, let updatedMediaReference = editMessageState.mediaReference { diff --git a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift index 1b8bc3d897..ac3a08ea20 100644 --- a/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift +++ b/submodules/TelegramUI/Components/MessageInputPanelComponent/Sources/MessageInputPanelComponent.swift @@ -694,6 +694,11 @@ public final class MessageInputPanelComponent: Component { } public func activateInput() { + if let inputPanelView = self.inputPanel?.view as? ChatTextInputPanelComponent.View { + inputPanelView.activateInput() + return + } + if let textFieldView = self.textField.view as? TextFieldComponent.View { textFieldView.activateInput() } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift index 385bbc5bc8..1dce5cd63c 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryContentLiveChatComponent.swift @@ -117,7 +117,7 @@ private final class MessageItemComponent: Component { self.containerNode.isGestureEnabled = component.contextGesture != nil - let insets = UIEdgeInsets(top: 8.0, left: 8.0, bottom: 8.0, right: 8.0) + let insets = UIEdgeInsets(top: 9.0, left: 20.0, bottom: 9.0, right: 20.0) let avatarSize: CGFloat = 24.0 let avatarSpacing: CGFloat = 6.0 @@ -175,7 +175,8 @@ private final class MessageItemComponent: Component { textView.bounds = CGRect(origin: CGPoint(), size: textFrame.size) } - let backgroundFrame = CGRect(origin: CGPoint(x: 6.0, y: 2.0), size: CGSize(width: textFrame.maxX + 8.0 - 6.0, height: textFrame.maxY + 3.0)) + let backgroundOrigin = CGPoint(x: avatarFrame.minX - 2.0, y: avatarFrame.minY - 2.0) + let backgroundFrame = CGRect(origin: backgroundOrigin, size: CGSize(width: textFrame.maxX + 8.0 - backgroundOrigin.x, height: max(avatarFrame.maxY + 2.0, textFrame.maxY + 5.0) - backgroundOrigin.y)) if let paidStars = component.message.paidStars { let backgroundView: UIImageView @@ -185,7 +186,7 @@ private final class MessageItemComponent: Component { backgroundView = UIImageView() self.backgroundView = backgroundView self.extractedContainerNode.contentNode.view.insertSubview(backgroundView, at: 0) - backgroundView.image = generateStretchableFilledCircleImage(diameter: 28.0, color: .white)?.withRenderingMode(.alwaysTemplate) + backgroundView.image = generateStretchableFilledCircleImage(diameter: avatarSize + 2.0 * 2.0, color: .white)?.withRenderingMode(.alwaysTemplate) } transition.setFrame(view: backgroundView, frame: backgroundFrame) backgroundView.tintColor = getStarAmountColorMapping(value: paidStars) @@ -219,7 +220,7 @@ private final class MessageItemComponent: Component { self.extractedContainerNode.frame = CGRect(origin: CGPoint(), size: size) self.extractedContainerNode.contentNode.frame = CGRect(origin: CGPoint(), size: size) - self.extractedContainerNode.contentRect = backgroundFrame + self.extractedContainerNode.contentRect = backgroundFrame.insetBy(dx: -4.0, dy: 0.0) self.containerNode.frame = CGRect(origin: CGPoint(), size: size) return size @@ -548,7 +549,7 @@ private final class PinnedBarComponent: Component { let itemHeight: CGFloat = 32.0 - let insets = UIEdgeInsets(top: 16.0, left: 16.0, bottom: 16.0, right: 16.0) + let insets = UIEdgeInsets(top: 13.0, left: 20.0, bottom: 13.0, right: 20.0) let size = CGSize(width: availableSize.width, height: insets.top + itemHeight + insets.bottom) @@ -564,7 +565,7 @@ private final class PinnedBarComponent: Component { } } - let listInsets = UIEdgeInsets(top: 0.0, left: 8.0, bottom: 0.0, right: 8.0) + let listInsets = UIEdgeInsets(top: 0.0, left: insets.left, bottom: 0.0, right: insets.right) let listFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: size.width, height: size.height)) let _ = self.list.update( transition: transition, @@ -665,6 +666,8 @@ final class StoryContentLiveChatComponent: Component { private var messagesState: GroupCallMessagesContext.State? private var stateDisposable: Disposable? + private var currentListIsEmpty: Bool = true + public var isChatEmpty: Bool { guard let messagesState = self.messagesState else { return true @@ -761,7 +764,7 @@ final class StoryContentLiveChatComponent: Component { self.state?.updated(transition: .spring(duration: 0.4)) } - private func openMessageContextMenu(id: Int64, gesture: ContextGesture, sourceNode: ContextExtractedContentContainingNode) { + private func openMessageContextMenu(id: GroupCallMessagesContext.Message.Id, gesture: ContextGesture, sourceNode: ContextExtractedContentContainingNode) { Task { @MainActor [weak self] in guard let self else { return @@ -844,6 +847,8 @@ final class StoryContentLiveChatComponent: Component { defer { self.isUpdating = false } + + let alphaTransition: ComponentTransition = transition.animation.isImmediate ? .immediate : .easeInOut(duration: 0.2) if self.component?.call !== component.call { self.stateDisposable?.dispose() @@ -868,6 +873,8 @@ final class StoryContentLiveChatComponent: Component { self.component = component self.state = state + let previousListIsEmpty = self.currentListIsEmpty + var listItems: [AnyComponentWithIdentity] = [] var topMessageByPeerId: [EnginePeer.Id: GroupCallMessagesContext.Message] = [:] if let messagesState = self.messagesState { @@ -908,6 +915,8 @@ final class StoryContentLiveChatComponent: Component { return lhs.date > rhs.date }) + self.currentListIsEmpty = listItems.isEmpty + let pinnedBarSize = self.pinnedBar.update( transition: transition, component: AnyComponent(PinnedBarComponent( @@ -938,13 +947,19 @@ final class StoryContentLiveChatComponent: Component { transition.setAlpha(view: pinnedBarView, alpha: topMessages.isEmpty ? 0.0 : 1.0) } - var listInsets = UIEdgeInsets(top: component.insets.bottom + 16.0, left: component.insets.right, bottom: component.insets.top + 8.0, right: component.insets.left) + var listInsets = UIEdgeInsets(top: component.insets.bottom + 8.0, left: component.insets.right, bottom: component.insets.top + 8.0, right: component.insets.left) if !topMessages.isEmpty { listInsets.top = availableSize.height - pinnedBarFrame.minY } - listInsets.top += 4.0 + listInsets.top += 1.0 + + var listTransition = transition + if previousListIsEmpty != self.currentListIsEmpty { + listTransition = listTransition.withAnimation(.none) + } + let _ = self.list.update( - transition: transition, + transition: listTransition, component: AnyComponent(AsyncListComponent( externalState: self.listState, items: listItems, @@ -963,6 +978,7 @@ final class StoryContentLiveChatComponent: Component { } transition.setPosition(view: listView, position: listFrame.offsetBy(dx: 0.0, dy: self.isChatExpanded ? 0.0 : listFrame.height).center) transition.setBounds(view: listView, bounds: CGRect(origin: CGPoint(), size: listFrame.size)) + alphaTransition.setAlpha(view: listView, alpha: listItems.isEmpty ? 0.0 : 1.0) } transition.setFrame(view: self.listContainer, frame: CGRect(origin: CGPoint(), size: availableSize)) diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift index fea21ce7b7..c2011f550b 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemContentComponent.swift @@ -110,6 +110,7 @@ final class StoryItemContentComponent: Component { private var component: StoryItemContentComponent? private weak var state: EmptyComponentState? private var environment: StoryContentItem.Environment? + private var isUpdating: Bool = false private var unsupportedText: ComponentView? private var unsupportedButton: ComponentView? @@ -180,7 +181,7 @@ final class StoryItemContentComponent: Component { guard let self else { return } - self.state?.updated(transition: .immediate) + self.state?.updated(transition: .immediate, isLocal: true) } } @@ -298,8 +299,8 @@ final class StoryItemContentComponent: Component { } } videoNode.canAttachContent = true - if update { - self.state?.updated(transition: .immediate) + if update && !self.isUpdating { + self.state?.updated(transition: .immediate, isLocal: true) } } } @@ -546,7 +547,7 @@ final class StoryItemContentComponent: Component { if !self.contentLoaded { self.contentLoaded = true - self.state?.updated(transition: .immediate) + self.state?.updated(transition: .immediate, isLocal: true) } } } @@ -612,6 +613,11 @@ final class StoryItemContentComponent: Component { } func update(component: StoryItemContentComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: ComponentTransition) -> CGSize { + self.isUpdating = true + defer { + self.isUpdating = false + } + let previousItem = self.component?.item self.component = component @@ -735,7 +741,7 @@ final class StoryItemContentComponent: Component { } if !self.contentLoaded { self.contentLoaded = true - self.state?.updated(transition: .immediate) + self.state?.updated(transition: .immediate, isLocal: true) } }) } @@ -900,7 +906,7 @@ final class StoryItemContentComponent: Component { } self.contentLoaded = true if applyState { - self.state?.updated(transition: .immediate) + self.state?.updated(transition: .immediate, isLocal: true) } } self.imageView.update( diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift index 62b5dbcf1e..67166f8fee 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerComponent.swift @@ -3791,7 +3791,7 @@ public final class StoryItemSetContainerComponent: Component { let contentFrame = CGRect(origin: CGPoint(x: 0.0, y: component.containerInsets.top - (contentSize.height - contentVisualHeight) * 0.5 - contentBottomInsetOverflow), size: contentSize) var contentInsets = UIEdgeInsets(top: 54.0, left: 0.0, bottom: 0.0, right: 0.0) if let inputPanelFrameValue { - contentInsets.bottom = max(0.0, contentFrame.maxY - (inputPanelFrameValue.minY + 8.0)) + contentInsets.bottom = max(0.0, contentFrame.maxY - inputPanelFrameValue.minY - 2.0) } transition.setFrame(view: self.viewListsContainer, frame: CGRect(origin: CGPoint(x: contentFrame.minX, y: 0.0), size: CGSize(width: contentSize.width, height: availableSize.height)))