From 681b5c9d3aa69629d9ccd5a023a9e104b12a3054 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Tue, 28 Jan 2025 19:59:32 +0400 Subject: [PATCH] Various improvements --- .../AvatarNode/Sources/AvatarNode.swift | 28 ++++++++++++ .../Sources/BrowserBookmarksScreen.swift | 1 + .../Sources/Node/ChatListNoticeItem.swift | 12 ++++-- .../Sources/ContactsPeerItem.swift | 5 +++ .../ContextUI/Sources/ContextController.swift | 13 +++++- .../Sources/ItemListPeerItem.swift | 5 +++ .../Sources/ChatMessageBubbleItemNode.swift | 43 +++++++++++-------- .../ChatRecentActionsControllerNode.swift | 4 +- .../ChatSendAudioMessageContextPreview.swift | 1 + .../Sources/ChatControllerInteraction.swift | 3 ++ .../Sources/MediaEditorComposer.swift | 5 +-- .../Sources/PeerInfoScreen.swift | 15 +++++++ .../Sources/PeerListItemComponent.swift | 5 +++ .../Resources/Animations/anim_camera.json | 1 + .../Chat/ChatControllerLoadDisplayNode.swift | 1 + .../TelegramUI/Sources/ChatController.swift | 5 +++ .../OverlayAudioPlayerControllerNode.swift | 1 + .../Sources/SharedAccountContext.swift | 6 ++- 18 files changed, 123 insertions(+), 31 deletions(-) create mode 100644 submodules/TelegramUI/Resources/Animations/anim_camera.json diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index ef4f04dd57..a82f3f78f9 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -492,6 +492,30 @@ public final class AvatarNode: ASDisplayNode { } } + public func playCameraAnimation() { + let animationBackgroundNode = ASImageNode() + animationBackgroundNode.isUserInteractionEnabled = false + animationBackgroundNode.frame = self.imageNode.frame + animationBackgroundNode.image = generateGradientFilledCircleImage(diameter: self.imageNode.frame.width, colors: AvatarNode.repostColors.map { $0.cgColor } as NSArray) + self.addSubnode(animationBackgroundNode) + + let animationNode = AnimationNode(animation: "anim_camera", colors: [:], scale: 0.082) + animationNode.isUserInteractionEnabled = false + self.addSubnode(animationNode) + + if var size = animationNode.preferredSize() { + size = CGSize(width: ceil(size.width), height: ceil(size.height)) + animationNode.frame = CGRect(x: floor((self.bounds.width - size.width) / 2.0) + 1.0, y: floor((self.bounds.height - size.height) / 2.0), width: size.width, height: size.height) + Queue.mainQueue().after(0.15, { + animationNode.play() + animationNode.completion = { [weak animationNode, weak animationBackgroundNode] in + animationNode?.removeFromSupernode() + animationBackgroundNode?.removeFromSupernode() + } + }) + } + } + public func setPeer( accountPeerId: EnginePeer.Id, postbox: Postbox, @@ -1151,6 +1175,10 @@ public final class AvatarNode: ASDisplayNode { self.contentNode.playRepostAnimation() } + public func playCameraAnimation() { + self.contentNode.playCameraAnimation () + } + public func setPeer( accountPeerId: EnginePeer.Id, postbox: Postbox, diff --git a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift index c763da34db..dc7b089167 100644 --- a/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift +++ b/submodules/BrowserUI/Sources/BrowserBookmarksScreen.swift @@ -163,6 +163,7 @@ public final class BrowserBookmarksScreen: ViewController { }, playMessageEffect: { _ in }, editMessageFactCheck: { _ in }, sendGift: { _ in + }, openUniqueGift: { _ in }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { diff --git a/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift b/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift index b50d94405c..e1a1f44eba 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListNoticeItem.swift @@ -137,10 +137,6 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { self.zPosition = 1.0 } - override func didLoad() { - super.didLoad() - } - @objc private func closePressed() { guard let item = self.item else { return @@ -500,6 +496,14 @@ final class ChatListNoticeItemNode: ItemListRevealOptionsItemNode { } } + override public func selected() { + super.selected() + + if case .setupPhoto = self.item?.notice { + self.avatarNode?.playCameraAnimation() + } + } + @objc private func okButtonPressed() { self.item?.action(.buttonChoice(isPositive: true)) } diff --git a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift index 60b5b64dbb..65f3572cfb 100644 --- a/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift +++ b/submodules/ContactsPeerItem/Sources/ContactsPeerItem.swift @@ -791,6 +791,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { let premiumConfiguration = PremiumConfiguration.with(appConfiguration: item.context.currentAppConfiguration.with { $0 }) var credibilityIcon: EmojiStatusComponent.Content? + var credibilityParticleColor: UIColor? var verifiedIcon: EmojiStatusComponent.Content? switch item.peer { case let .peer(peer, _): @@ -801,6 +802,9 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) } else if let emojiStatus = peer.emojiStatus { credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) + if let color = emojiStatus.color { + credibilityParticleColor = UIColor(rgb: UInt32(bitPattern: color)) + } } else if peer.isPremium && !premiumConfiguration.isPremiumDisabled { credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor) } @@ -1498,6 +1502,7 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode { animationCache: animationCache, animationRenderer: animationRenderer, content: credibilityIcon, + particleColor: credibilityParticleColor, isVisibleForAnimations: strongSelf.visibilityStatus, action: nil, emojiFileUpdated: nil diff --git a/submodules/ContextUI/Sources/ContextController.swift b/submodules/ContextUI/Sources/ContextController.swift index fd8b12acdd..5e1c2fe0aa 100644 --- a/submodules/ContextUI/Sources/ContextController.swift +++ b/submodules/ContextUI/Sources/ContextController.swift @@ -141,6 +141,7 @@ public final class ContextMenuActionItem { public let textIcon: (PresentationTheme) -> UIImage? public let textLinkAction: () -> Void public let action: ((Action) -> Void)? + public let longPressAction: ((Action) -> Void)? convenience public init( id: AnyHashable? = nil, @@ -160,7 +161,8 @@ public final class ContextMenuActionItem { iconAnimation: IconAnimation? = nil, textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil }, textLinkAction: @escaping () -> Void = {}, - action: ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)? + action: ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)?, + longPressAction: ((ContextControllerProtocol?, @escaping (ContextMenuActionResult) -> Void) -> Void)? = nil ) { self.init( id: id, @@ -184,6 +186,11 @@ public final class ContextMenuActionItem { return { impl in action(impl.controller, impl.dismissWithResult) } + }, + longPressAction: longPressAction.flatMap { longPressAction in + return { impl in + longPressAction(impl.controller, impl.dismissWithResult) + } } ) } @@ -206,7 +213,8 @@ public final class ContextMenuActionItem { iconAnimation: IconAnimation? = nil, textIcon: @escaping (PresentationTheme) -> UIImage? = { _ in return nil }, textLinkAction: @escaping () -> Void = {}, - action: ((Action) -> Void)? + action: ((Action) -> Void)?, + longPressAction: ((Action) -> Void)? = nil ) { self.id = id self.text = text @@ -226,6 +234,7 @@ public final class ContextMenuActionItem { self.textIcon = textIcon self.textLinkAction = textLinkAction self.action = action + self.longPressAction = longPressAction } } diff --git a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift index 6256672ee8..b59c798b7e 100644 --- a/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift +++ b/submodules/ItemListPeerItem/Sources/ItemListPeerItem.swift @@ -922,6 +922,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo var updatedLabelBadgeImage: UIImage? var credibilityIcon: EmojiStatusComponent.Content? + var credibilityParticleColor: UIColor? var verifiedIcon: EmojiStatusComponent.Content? if case .threatSelfAsSaved = item.aliasHandling, item.peer.id == item.context.accountPeerId { @@ -932,6 +933,9 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo credibilityIcon = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) } else if let emojiStatus = item.peer.emojiStatus { credibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2)) + if let color = emojiStatus.color { + credibilityParticleColor = UIColor(rgb: UInt32(bitPattern: color)) + } } else if item.peer.isPremium && !item.context.isPremiumDisabled { credibilityIcon = .premium(color: item.presentationData.theme.list.itemAccentColor) } @@ -1504,6 +1508,7 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo animationCache: animationCache, animationRenderer: animationRenderer, content: credibilityIcon, + particleColor: credibilityParticleColor, isVisibleForAnimations: strongSelf.visibilityStatus, action: nil, emojiFileUpdated: nil diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index 4d05707766..0b1d54f9c4 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -2100,7 +2100,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI bottomNodeMergeStatus = .Both } - var currentCredibilityIcon: EmojiStatusComponent.Content? + var currentCredibilityIcon: (EmojiStatusComponent.Content, UIColor?)? var initialDisplayHeader = true if hidesHeaders || item.message.adAttribute != nil { @@ -2163,18 +2163,18 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if case let .peer(peerId) = item.chatLocation, let authorPeerId = item.message.author?.id, authorPeerId == peerId { if effectiveAuthor is TelegramChannel, let emojiStatus = effectiveAuthor.emojiStatus { - currentCredibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor, themeColor: color.withMultipliedAlpha(0.4), loopMode: .count(2)) + currentCredibilityIcon = (.animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor, themeColor: color.withMultipliedAlpha(0.4), loopMode: .count(2)), nil) } } else if effectiveAuthor.isScam { - currentCredibilityIcon = .text(color: incoming ? item.presentationData.theme.theme.chat.message.incoming.scamColor : item.presentationData.theme.theme.chat.message.outgoing.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()) + currentCredibilityIcon = (.text(color: incoming ? item.presentationData.theme.theme.chat.message.incoming.scamColor : item.presentationData.theme.theme.chat.message.outgoing.scamColor, string: item.presentationData.strings.Message_ScamAccount.uppercased()), nil) } else if effectiveAuthor.isFake { - currentCredibilityIcon = .text(color: incoming ? item.presentationData.theme.theme.chat.message.incoming.scamColor : item.presentationData.theme.theme.chat.message.outgoing.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()) + currentCredibilityIcon = (.text(color: incoming ? item.presentationData.theme.theme.chat.message.incoming.scamColor : item.presentationData.theme.theme.chat.message.outgoing.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased()), nil) } else if let emojiStatus = effectiveAuthor.emojiStatus { - currentCredibilityIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor, themeColor: color.withMultipliedAlpha(0.4), loopMode: .count(2)) + currentCredibilityIcon = (.animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: incoming ? item.presentationData.theme.theme.chat.message.incoming.mediaPlaceholderColor : item.presentationData.theme.theme.chat.message.outgoing.mediaPlaceholderColor, themeColor: color.withMultipliedAlpha(0.4), loopMode: .count(2)), emojiStatus.color.flatMap { UIColor(rgb: UInt32(bitPattern: $0)) }) } else if effectiveAuthor.isVerified { - currentCredibilityIcon = .verified(fillColor: item.presentationData.theme.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) + currentCredibilityIcon = (.verified(fillColor: item.presentationData.theme.theme.list.itemCheckColors.fillColor, foregroundColor: item.presentationData.theme.theme.list.itemCheckColors.foregroundColor, sizeType: .compact), nil) } else if effectiveAuthor.isPremium { - currentCredibilityIcon = .premium(color: color.withMultipliedAlpha(0.4)) + currentCredibilityIcon = (.premium(color: color.withMultipliedAlpha(0.4)), nil) } } if let rawAuthorNameColor = authorNameColor { @@ -2414,7 +2414,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI } var credibilityIconWidth: CGFloat = 0.0 - if let currentCredibilityIcon = currentCredibilityIcon { + if let (currentCredibilityIcon, _) = currentCredibilityIcon { credibilityIconWidth += 4.0 switch currentCredibilityIcon { case let .text(_, string): @@ -3223,7 +3223,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI nameNodeOriginY: CGFloat, authorNameColor: UIColor?, layoutConstants: ChatMessageItemLayoutConstants, - currentCredibilityIcon: EmojiStatusComponent.Content?, + currentCredibilityIcon: (EmojiStatusComponent.Content, UIColor?)?, adminNodeSizeApply: (CGSize, () -> TextNode?), boostNodeSizeApply: (CGSize, () -> TextNode?), contentUpperRightCorner: CGPoint, @@ -3428,7 +3428,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI nameHighlightNode.image = generateFilledRoundedRectImage(size: CGSize(width: 8.0, height: 8.0), cornerRadius: 4.0, color: nameColor.withAlphaComponent(0.1))?.stretchableImage(withLeftCapWidth: 4, topCapHeight: 4) } - if let currentCredibilityIcon = currentCredibilityIcon { + if let (currentCredibilityIcon, currentParticleColor) = currentCredibilityIcon { let credibilityIconView: ComponentHostView if let current = strongSelf.credibilityIconView { credibilityIconView = current @@ -3448,6 +3448,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI animationCache: item.context.animationCache, animationRenderer: item.context.animationRenderer, content: currentCredibilityIcon, + particleColor: currentParticleColor, isVisibleForAnimations: strongSelf.visibilityStatus, action: nil ) @@ -5767,16 +5768,20 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI @objc private func credibilityButtonPressed() { if let item = self.item, let credibilityIconView = self.credibilityIconView, let iconContent = self.credibilityIconContent, let peer = item.message.author { - var emojiFileId: Int64? - switch iconContent { - case let .animation(content, _, _, _, _): - emojiFileId = content.fileId.id - case .premium: - break - default: - return + if case let .starGift(_, _, _, slug, _, _, _, _, _) = peer.emojiStatus?.content { + item.controllerInteraction.openUniqueGift(slug) + } else { + var emojiFileId: Int64? + switch iconContent { + case let .animation(content, _, _, _, _): + emojiFileId = content.fileId.id + case .premium: + break + default: + return + } + item.controllerInteraction.openPremiumStatusInfo(peer.id, credibilityIconView, emojiFileId, peer.nameColor ?? .blue) } - item.controllerInteraction.openPremiumStatusInfo(peer.id, credibilityIconView, emojiFileId, peer.nameColor ?? .blue) } } diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift index ba5c035ffe..78cb47fa98 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsControllerNode.swift @@ -632,8 +632,8 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode { }, playMessageEffect: { _ in }, editMessageFactCheck: { _ in }, sendGift: { _ in - }, requestMessageUpdate: { _, _ in - + }, openUniqueGift: { _ in + }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { }, scrollToMessageId: { _ in diff --git a/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift b/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift index 57af149086..0efb1b00c0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift +++ b/submodules/TelegramUI/Components/Chat/ChatSendAudioMessageContextPreview/Sources/ChatSendAudioMessageContextPreview.swift @@ -489,6 +489,7 @@ public final class ChatSendGroupMediaMessageContextPreview: UIView, ChatSendMess }, playMessageEffect: { _ in }, editMessageFactCheck: { _ in }, sendGift: { _ in + }, openUniqueGift: { _ in }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { diff --git a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift index b073853d4d..b81d5360fd 100644 --- a/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift +++ b/submodules/TelegramUI/Components/ChatControllerInteraction/Sources/ChatControllerInteraction.swift @@ -268,6 +268,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol public let playMessageEffect: (Message) -> Void public let editMessageFactCheck: (MessageId) -> Void public let sendGift: (EnginePeer.Id) -> Void + public let openUniqueGift: (String) -> Void public let requestMessageUpdate: (MessageId, Bool) -> Void public let cancelInteractiveKeyboardGestures: () -> Void @@ -402,6 +403,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol playMessageEffect: @escaping (Message) -> Void, editMessageFactCheck: @escaping (MessageId) -> Void, sendGift: @escaping (EnginePeer.Id) -> Void, + openUniqueGift: @escaping (String) -> Void, requestMessageUpdate: @escaping (MessageId, Bool) -> Void, cancelInteractiveKeyboardGestures: @escaping () -> Void, dismissTextInput: @escaping () -> Void, @@ -515,6 +517,7 @@ public final class ChatControllerInteraction: ChatControllerInteractionProtocol self.playMessageEffect = playMessageEffect self.editMessageFactCheck = editMessageFactCheck self.sendGift = sendGift + self.openUniqueGift = openUniqueGift self.requestMessageUpdate = requestMessageUpdate self.cancelInteractiveKeyboardGestures = cancelInteractiveKeyboardGestures diff --git a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift index 3991c44358..be27ec04bf 100644 --- a/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift +++ b/submodules/TelegramUI/Components/MediaEditor/Sources/MediaEditorComposer.swift @@ -229,7 +229,7 @@ public func makeEditorImageComposition(context: CIContext, postbox: Postbox, inp } else if values.isAvatar { maskImage = rectangleMaskImage(size: CGSize(width: 1080.0, height: 1080.0)) } else if let outputDimensions { - maskImage = rectangleMaskImage(size: outputDimensions.aspectFitted(CGSize(width: 1080.0, height: 1080.0))) + maskImage = rectangleMaskImage(size: outputDimensions.aspectFitted(CGSize(width: 1080.0, height: 1920.0))) } if let drawing = values.drawing, let image = CIImage(image: drawing, options: [.colorSpace: colorSpace]) { @@ -308,8 +308,7 @@ private func makeEditorImageFrameComposition(context: CIContext, inputImage: CII let scaledSize = CGSize(width: minSize, height: minSize) resultImage = resultImage.transformed(by: CGAffineTransform(translationX: -(dimensions.width - scaledSize.width) / 2.0, y: -(dimensions.height - scaledSize.height) / 2.0)).cropped(to: CGRect(origin: .zero, size: scaledSize)) } else if values.isCover, let outputDimensions { - let minSize = min(dimensions.width, dimensions.height) - let scaledSize = outputDimensions.aspectFitted(CGSize(width: minSize, height: minSize)) + let scaledSize = outputDimensions.aspectFitted(dimensions) resultImage = resultImage.transformed(by: CGAffineTransform(translationX: -(dimensions.width - scaledSize.width) / 2.0, y: -(dimensions.height - scaledSize.height) / 2.0)).cropped(to: CGRect(origin: .zero, size: scaledSize)) } else if values.isStory { resultImage = resultImage.cropped(to: CGRect(origin: .zero, size: dimensions)) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index f765a2a9d4..1e0a50c6a4 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -3680,6 +3680,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro }, playMessageEffect: { _ in }, editMessageFactCheck: { _ in }, sendGift: { _ in + }, openUniqueGift: { _ in }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { @@ -10970,20 +10971,30 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro giftsContext?.updateFilter(updatedFilter) } + let switchToFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in + giftsContext?.updateFilter(value) + } + items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unlimited, icon: { theme in return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in toggleFilter(.unlimited) + }, longPressAction: { _, f in + switchToFilter(.unlimited) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in return filter.contains(.limited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in toggleFilter(.limited) + }, longPressAction: { _, f in + switchToFilter(.limited) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in toggleFilter(.unique) + }, longPressAction: { _, f in + switchToFilter(.unique) }))) if channel.hasPermission(.sendSomething) { @@ -10993,11 +11004,15 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in toggleFilter(.displayed) + }, longPressAction: { _, f in + switchToFilter(.displayed) }))) items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Hidden, icon: { theme in return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil }, action: { _, f in toggleFilter(.hidden) + }, longPressAction: { _, f in + switchToFilter(.hidden) }))) } diff --git a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift index 947d0afe4c..6c7f7effb4 100644 --- a/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift +++ b/submodules/TelegramUI/Components/Stories/PeerListItemComponent/Sources/PeerListItemComponent.swift @@ -835,6 +835,7 @@ public final class PeerListItemComponent: Component { let avatarFrame = CGRect(origin: CGPoint(x: avatarLeftInset, y: floorToScreenPixels((height - verticalInset * 2.0 - avatarSize) / 2.0)), size: CGSize(width: avatarSize, height: avatarSize)) var statusIcon: EmojiStatusComponent.Content? + var particleColor: UIColor? if let peer = component.peer { if peer.isScam { statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_ScamAccount.uppercased()) @@ -842,6 +843,9 @@ public final class PeerListItemComponent: Component { statusIcon = .text(color: component.theme.chat.message.incoming.scamColor, string: component.strings.Message_FakeAccount.uppercased()) } else if let emojiStatus = peer.emojiStatus { statusIcon = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 20.0, height: 20.0), placeholderColor: component.theme.list.mediaPlaceholderColor, themeColor: component.theme.list.itemAccentColor, loopMode: .count(2)) + if let color = emojiStatus.color { + particleColor = UIColor(rgb: UInt32(bitPattern: color)) + } } else if peer.isVerified { statusIcon = .verified(fillColor: component.theme.list.itemCheckColors.fillColor, foregroundColor: component.theme.list.itemCheckColors.foregroundColor, sizeType: .compact) } else if peer.isPremium { @@ -1086,6 +1090,7 @@ public final class PeerListItemComponent: Component { animationCache: animationCache, animationRenderer: animationRenderer, content: statusIcon, + particleColor: particleColor, isVisibleForAnimations: true, action: nil, emojiFileUpdated: nil diff --git a/submodules/TelegramUI/Resources/Animations/anim_camera.json b/submodules/TelegramUI/Resources/Animations/anim_camera.json new file mode 100644 index 0000000000..6c796df16a --- /dev/null +++ b/submodules/TelegramUI/Resources/Animations/anim_camera.json @@ -0,0 +1 @@ +{"tgs":1,"v":"5.5.2","fr":60,"ip":0,"op":46,"w":512,"h":512,"nm":"Camera 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"Splash","parent":3,"sr":1,"ks":{"p":{"a":0,"k":[116.176,-62.405,0]},"a":{"a":0,"k":[-122.379,-77.172,0]},"s":{"a":0,"k":[-100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-165.731,-18.652],[-126.56,-42.433]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":18},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-69.274,-137.29],[-83.995,-87.983]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":18},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-136.006,-137.186],[-108.247,-84.401]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":18},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 3","bm":0,"hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":0,"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-180.537,-86.011],[-128.641,-65.685]],"c":false}},"nm":"Path 1","hd":false},{"ty":"st","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":18},"lc":2,"lj":1,"ml":4,"bm":0,"nm":"Stroke 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 4","bm":0,"hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":3,"s":[100]},{"t":12,"s":[0]}]},"e":{"a":1,"k":[{"i":{"x":[0.3],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":0,"s":[100]},{"t":10,"s":[0]}]},"o":{"a":0,"k":0},"m":1,"nm":"Trim Paths 1","hd":false}],"ip":2,"op":13,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Camera 1","sr":1,"ks":{"r":{"a":1,"k":[{"i":{"x":[0.2],"y":[1]},"o":{"x":[0.5],"y":[0]},"t":7,"s":[0]},{"i":{"x":[0.25],"y":[1]},"o":{"x":[0.4],"y":[0]},"t":15,"s":[1]},{"i":{"x":[0.281],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":23,"s":[-3]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.3],"y":[0]},"t":31,"s":[2]},{"i":{"x":[0.7],"y":[1]},"o":{"x":[0.167],"y":[0]},"t":37,"s":[-1.5]},{"t":42,"s":[0]}]},"p":{"a":1,"k":[{"i":{"x":0,"y":1},"o":{"x":0.3,"y":0},"t":2,"s":[247.272,261,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":7,"s":[247.272,284,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.25,"y":1},"o":{"x":0.4,"y":0},"t":18,"s":[247.272,206.84,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":27,"s":[247.272,294.349,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.3,"y":0},"t":34,"s":[247.272,253,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.7,"y":1},"o":{"x":0.167,"y":0},"t":40,"s":[247.272,266.25,0],"to":[0,0,0],"ti":[0,0,0]},{"t":45,"s":[247.272,261,0]}]},"a":{"a":0,"k":[-8.728,5,0]},"s":{"a":1,"k":[{"i":{"x":[0,0,0],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":2,"s":[100,100,100]},{"i":{"x":[0.2,0.2,0.2],"y":[1,1,1]},"o":{"x":[0.5,0.5,0.5],"y":[0,0,0]},"t":7,"s":[107,95.79,100]},{"i":{"x":[0.25,0.25,0.25],"y":[1,1,1]},"o":{"x":[0.4,0.4,0.4],"y":[0,0,0]},"t":18,"s":[100,104,100]},{"i":{"x":[0.4,0.4,0.4],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":27,"s":[105,96.845,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.3,0.3,0.3],"y":[0,0,0]},"t":34,"s":[98,102,100]},{"i":{"x":[0.7,0.7,0.7],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":40,"s":[101,99,100]},{"t":45,"s":[100,100,100]}]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.19,"y":1},"o":{"x":0.483,"y":0},"t":0,"s":[{"i":[[9.528,0],[7.544,4.529],[4.296,8.285],[0,8.324],[-3.741,7.028],[-2.417,2.808],[-4.165,2.655],[-9.922,0],[-8.538,-6.707],[-2.326,-2.835],[-1.755,-3.672],[0,-7.746],[3.866,-7.117],[2.603,-2.905],[4.252,-2.581]],"o":[[-9.44,0],[-7.973,-4.786],[-3.584,-6.913],[0,-8.517],[1.755,-3.297],[3.209,-3.729],[7.79,-4.965],[11.7,0],[2.885,2.266],[2.555,3.113],[3.131,6.551],[0,8.668],[-1.876,3.454],[-3.302,3.685],[-7.591,4.608]],"v":[[-8.728,64.083],[-34.544,56.95],[-53.348,36.943],[-58.957,13.855],[-53.096,-9.712],[-46.813,-18.895],[-35.691,-28.531],[-8.728,-36.374],[22.275,-25.666],[30.117,-17.99],[36.615,-7.779],[41.5,13.855],[35.439,37.795],[28.69,47.363],[17.299,56.822]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":7,"s":[{"i":[[9.639,0],[7.632,3.566],[4.345,6.523],[0,6.554],[-3.784,5.533],[-2.445,2.211],[-4.214,2.09],[-10.038,0],[-8.637,-5.281],[-2.354,-2.232],[-1.776,-2.891],[0,-6.098],[3.911,-5.603],[2.633,-2.287],[4.301,-2.032]],"o":[[-9.55,0],[-8.066,-3.768],[-3.626,-5.442],[0,-6.706],[1.775,-2.596],[3.247,-2.936],[7.88,-3.909],[11.836,0],[2.919,1.784],[2.585,2.451],[3.168,5.158],[0,6.824],[-1.898,2.719],[-3.34,2.901],[-7.679,3.628]],"v":[[-9.205,54.86],[-35.32,49.244],[-54.343,33.492],[-60.017,15.315],[-54.088,-3.24],[-47.732,-10.47],[-36.48,-18.056],[-9.205,-24.23],[22.159,-15.8],[30.092,-9.757],[36.666,-1.717],[41.608,15.315],[35.476,34.163],[28.649,41.696],[17.126,49.143]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":18,"s":[{"i":[[8.966,0],[7.099,4.49],[4.042,8.213],[0,8.252],[-3.52,6.967],[-2.274,2.784],[-3.919,2.632],[-9.337,0],[-8.034,-6.649],[-2.189,-2.81],[-1.652,-3.64],[0,-7.679],[3.637,-7.055],[2.449,-2.879],[4.001,-2.558]],"o":[[-8.883,0],[-7.502,-4.745],[-3.373,-6.853],[0,-8.443],[1.651,-3.268],[3.02,-3.696],[7.33,-4.922],[11.009,0],[2.715,2.247],[2.404,3.086],[2.946,6.495],[0,8.593],[-1.765,3.424],[-3.107,3.653],[-7.142,4.568]],"v":[[-8.251,53.376],[-32.542,46.305],[-50.236,26.471],[-55.514,3.583],[-49.999,-19.78],[-44.086,-28.883],[-33.621,-38.435],[-8.251,-46.21],[20.922,-35.595],[28.302,-27.985],[34.416,-17.863],[39.013,3.583],[33.309,27.315],[26.959,36.8],[16.24,46.178]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":27,"s":[{"i":[[9.781,0],[7.744,4.005],[4.409,7.325],[0,7.36],[-3.84,6.214],[-2.481,2.483],[-4.275,2.348],[-10.185,0],[-8.764,-5.931],[-2.388,-2.506],[-1.802,-3.247],[0,-6.849],[3.968,-6.293],[2.672,-2.568],[4.364,-2.282]],"o":[[-9.69,0],[-8.184,-4.232],[-3.679,-6.112],[0,-7.531],[1.801,-2.915],[3.294,-3.297],[7.996,-4.391],[12.009,0],[2.961,2.004],[2.623,2.753],[3.214,5.793],[0,7.665],[-1.926,3.054],[-3.389,3.258],[-7.791,4.074]],"v":[[-8.696,57.793],[-35.195,51.486],[-54.497,33.795],[-60.254,13.38],[-54.238,-7.458],[-47.789,-15.578],[-36.372,-24.098],[-8.696,-31.033],[23.128,-21.565],[31.177,-14.778],[37.848,-5.749],[42.862,13.38],[36.64,34.549],[29.713,43.009],[18.02,51.373]],"c":true}]},{"t":34,"s":[{"i":[[9.528,0],[7.544,4.529],[4.296,8.285],[0,8.324],[-3.741,7.028],[-2.417,2.808],[-4.165,2.655],[-9.922,0],[-8.538,-6.707],[-2.326,-2.835],[-1.755,-3.672],[0,-7.746],[3.866,-7.117],[2.603,-2.905],[4.252,-2.581]],"o":[[-9.44,0],[-7.973,-4.786],[-3.584,-6.913],[0,-8.517],[1.755,-3.297],[3.209,-3.729],[7.79,-4.965],[11.7,0],[2.885,2.266],[2.555,3.113],[3.131,6.551],[0,8.668],[-1.876,3.454],[-3.302,3.685],[-7.591,4.608]],"v":[[-8.728,64.083],[-34.544,56.95],[-53.348,36.943],[-58.957,13.855],[-53.096,-9.712],[-46.813,-18.895],[-35.691,-28.531],[-8.728,-36.374],[22.275,-25.666],[30.117,-17.99],[36.615,-7.779],[41.5,13.855],[35.439,37.795],[28.69,47.363],[17.299,56.822]],"c":true}]}]},"nm":"Path 1","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.19,"y":1},"o":{"x":0.483,"y":0},"t":0,"s":[{"i":[[9.528,0],[7.544,4.529],[4.296,8.285],[0,8.324],[-3.741,7.028],[-2.417,2.808],[-4.165,2.655],[-9.922,0],[-8.538,-6.707],[-2.326,-2.835],[-1.755,-3.672],[0,-7.746],[3.866,-7.117],[2.603,-2.905],[4.252,-2.581]],"o":[[-9.44,0],[-7.973,-4.786],[-3.584,-6.913],[0,-8.517],[1.755,-3.297],[3.209,-3.729],[7.79,-4.965],[11.7,0],[2.885,2.266],[2.555,3.113],[3.131,6.551],[0,8.668],[-1.876,3.454],[-3.302,3.685],[-7.591,4.608]],"v":[[-8.728,64.083],[-34.544,56.95],[-53.348,36.943],[-58.957,13.855],[-53.096,-9.712],[-46.813,-18.895],[-35.691,-28.531],[-8.728,-36.374],[22.275,-25.666],[30.117,-17.99],[36.615,-7.779],[41.5,13.855],[35.439,37.795],[28.69,47.363],[17.299,56.822]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":7,"s":[{"i":[[9.639,0],[7.632,3.566],[4.345,6.523],[0,6.554],[-3.784,5.533],[-2.445,2.211],[-4.214,2.09],[-10.038,0],[-8.637,-5.281],[-2.354,-2.232],[-1.776,-2.891],[0,-6.098],[3.911,-5.603],[2.633,-2.287],[4.301,-2.032]],"o":[[-9.55,0],[-8.066,-3.768],[-3.626,-5.442],[0,-6.706],[1.775,-2.596],[3.247,-2.936],[7.88,-3.909],[11.836,0],[2.919,1.784],[2.585,2.451],[3.168,5.158],[0,6.824],[-1.898,2.719],[-3.34,2.901],[-7.679,3.628]],"v":[[-9.205,54.86],[-35.32,49.244],[-54.343,33.492],[-60.017,15.315],[-54.088,-3.24],[-47.732,-10.47],[-36.48,-18.056],[-9.205,-24.23],[22.159,-15.8],[30.092,-9.757],[36.666,-1.717],[41.608,15.315],[35.476,34.163],[28.649,41.696],[17.126,49.143]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":18,"s":[{"i":[[8.966,0],[7.099,4.49],[4.042,8.213],[0,8.252],[-3.52,6.967],[-2.274,2.784],[-3.919,2.632],[-9.337,0],[-8.034,-6.649],[-2.189,-2.81],[-1.652,-3.64],[0,-7.679],[3.637,-7.055],[2.449,-2.879],[4.001,-2.558]],"o":[[-8.883,0],[-7.502,-4.745],[-3.373,-6.853],[0,-8.443],[1.651,-3.268],[3.02,-3.696],[7.33,-4.922],[11.009,0],[2.715,2.247],[2.404,3.086],[2.946,6.495],[0,8.593],[-1.765,3.424],[-3.107,3.653],[-7.142,4.568]],"v":[[-8.251,53.376],[-32.542,46.305],[-50.236,26.471],[-55.514,3.583],[-49.999,-19.78],[-44.086,-28.883],[-33.621,-38.435],[-8.251,-46.21],[20.922,-35.595],[28.302,-27.985],[34.416,-17.863],[39.013,3.583],[33.309,27.315],[26.959,36.8],[16.24,46.178]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":27,"s":[{"i":[[9.781,0],[7.744,4.005],[4.409,7.325],[0,7.36],[-3.84,6.214],[-2.481,2.483],[-4.275,2.348],[-10.185,0],[-8.764,-5.931],[-2.388,-2.506],[-1.802,-3.247],[0,-6.849],[3.968,-6.293],[2.672,-2.568],[4.364,-2.282]],"o":[[-9.69,0],[-8.184,-4.232],[-3.679,-6.112],[0,-7.531],[1.801,-2.915],[3.294,-3.297],[7.996,-4.391],[12.009,0],[2.961,2.004],[2.623,2.753],[3.214,5.793],[0,7.665],[-1.926,3.054],[-3.389,3.258],[-7.791,4.074]],"v":[[-8.696,57.793],[-35.195,51.486],[-54.497,33.795],[-60.254,13.38],[-54.238,-7.458],[-47.789,-15.578],[-36.372,-24.098],[-8.696,-31.033],[23.128,-21.565],[31.177,-14.778],[37.848,-5.749],[42.862,13.38],[36.64,34.549],[29.713,43.009],[18.02,51.373]],"c":true}]},{"t":34,"s":[{"i":[[9.528,0],[7.544,4.529],[4.296,8.285],[0,8.324],[-3.741,7.028],[-2.417,2.808],[-4.165,2.655],[-9.922,0],[-8.538,-6.707],[-2.326,-2.835],[-1.755,-3.672],[0,-7.746],[3.866,-7.117],[2.603,-2.905],[4.252,-2.581]],"o":[[-9.44,0],[-7.973,-4.786],[-3.584,-6.913],[0,-8.517],[1.755,-3.297],[3.209,-3.729],[7.79,-4.965],[11.7,0],[2.885,2.266],[2.555,3.113],[3.131,6.551],[0,8.668],[-1.876,3.454],[-3.302,3.685],[-7.591,4.608]],"v":[[-8.728,64.083],[-34.544,56.95],[-53.348,36.943],[-58.957,13.855],[-53.096,-9.712],[-46.813,-18.895],[-35.691,-28.531],[-8.728,-36.374],[22.275,-25.666],[30.117,-17.99],[36.615,-7.779],[41.5,13.855],[35.439,37.795],[28.69,47.363],[17.299,56.822]],"c":true}]}]},"nm":"Path 4","hd":false},{"ty":"tr","p":{"a":0,"k":[-8.728,13.855]},"a":{"a":0,"k":[-8.728,13.855]},"s":{"a":0,"k":[63,63]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ty":"tr","p":{"a":0,"k":[-9.728,13.605]},"a":{"a":0,"k":[-8.728,13.855]},"s":{"a":0,"k":[107,107]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false},{"ind":1,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.19,"y":1},"o":{"x":0.483,"y":0},"t":0,"s":[{"i":[[12.482,0],[0,0],[4.138,4.13],[0,0],[4.042,0],[0,0],[2.421,-2.975],[0,0],[5.553,0.092],[0,0],[-0.736,-14.94],[0,0],[-15.469,-0.428],[-0.754,-0.35],[0.375,16.014],[0,0]],"o":[[0,0],[-5.077,0.13],[0,0],[-2.414,-3.052],[0,0],[-3.981,0],[0,0],[-4.755,5.116],[0,0],[-12.482,0],[0,0],[0.028,13.948],[0,0],[16.855,0.273],[-0.425,0.053],[0,-11.749]],"v":[[87.546,-72.409],[55.577,-72.38],[41.112,-77.005],[28.836,-89.625],[16.699,-94.462],[-36.718,-94.45],[-48.938,-89.755],[-59.62,-78.616],[-74.178,-72.467],[-106.055,-72.712],[-133.764,-44.81],[-133.903,72.552],[-106.281,99.553],[83.645,99.977],[114,71.986],[113.966,-46.797]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":7,"s":[{"i":[[12.885,0],[0,0],[0.781,0.44],[0,0],[4.173,0],[0,0],[2.499,-1.37],[0,0],[1.288,0],[0,0],[0,-10.534],[0,0],[-12.885,0],[-0.778,-0.314],[-0.164,10.807],[0,0]],"o":[[0,0],[-1.308,0],[0,0],[-2.492,-1.405],[0,0],[-4.11,0],[0,0],[-0.783,0.429],[0,0],[-12.885,0],[0,0],[0,10.534],[0,0],[17.042,-0.538],[-0.438,0.048],[0,-10.534]],"v":[[88.167,-54.035],[49.465,-53.999],[46.146,-54.699],[34.247,-61.406],[23.655,-63.639],[-40.39,-63.639],[-50.874,-61.466],[-63.256,-54.68],[-66.542,-53.999],[-104.811,-53.787],[-128.141,-30.537],[-128.088,70.922],[-100.579,94.088],[81.258,94.084],[111.631,68.701],[111.414,-29.741]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":18,"s":[{"i":[[11.741,0],[0,0],[0.712,1.036],[0,0],[3.802,0],[0,0],[2.277,-3.225],[0,0],[1.173,0],[0,0],[0,-12.735],[0,0],[-11.741,0],[-0.709,-0.379],[-0.15,13.064],[0,0]],"o":[[0,0],[-1.191,0],[0,0],[-2.271,-3.308],[0,0],[-3.745,0],[0,0],[-0.714,1.01],[0,0],[-11.741,0],[0,0],[0,12.735],[0,0],[15.529,-0.65],[-0.4,0.058],[0,-12.735]],"v":[[78.926,-79.476],[45.296,-79.582],[42.271,-81.23],[31.43,-97.021],[21.778,-102.278],[-36.579,-102.278],[-46.133,-97.162],[-57.415,-81.185],[-60.409,-79.582],[-95.584,-78.754],[-116.842,-55.696],[-116.842,80.721],[-95.584,103.779],[75.587,103.354],[100.259,79.506],[100.184,-56.418]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":27,"s":[{"i":[[12.812,0],[0,0],[0.777,0.907],[0,0],[4.149,0],[0,0],[2.485,-2.822],[0,0],[1.281,0],[0,0],[0,-11.143],[0,0],[-12.812,0],[-0.774,-0.332],[-0.163,11.431],[0,0]],"o":[[0,0],[-1.3,0],[0,0],[-2.478,-2.894],[0,0],[-4.087,0],[0,0],[-0.779,0.884],[0,0],[-12.812,0],[0,0],[0,11.143],[0,0],[16.947,-0.569],[-0.436,0.051],[0,-11.143]],"v":[[94,-64.91],[48.996,-64.947],[45.695,-66.389],[33.864,-81.155],[23.331,-85.755],[-40.354,-85.755],[-50.779,-81.278],[-63.092,-66.35],[-66.359,-64.947],[-113.964,-65.041],[-137.162,-44.865],[-137.162,74.501],[-113.964,94.677],[90.356,95.068],[117.28,74.201],[117.199,-44.734]],"c":true}]},{"t":34,"s":[{"i":[[12.482,0],[0,0],[4.138,4.13],[0,0],[4.042,0],[0,0],[2.421,-2.975],[0,0],[5.553,0.092],[0,0],[-0.736,-14.94],[0,0],[-15.469,-0.428],[-0.754,-0.35],[0.375,16.014],[0,0]],"o":[[0,0],[-5.077,0.13],[0,0],[-2.414,-3.052],[0,0],[-3.981,0],[0,0],[-4.755,5.116],[0,0],[-12.482,0],[0,0],[0.028,13.948],[0,0],[16.855,0.273],[-0.425,0.053],[0,-11.749]],"v":[[87.546,-72.409],[55.577,-72.38],[41.112,-77.005],[28.836,-89.625],[16.699,-94.462],[-36.718,-94.45],[-48.938,-89.755],[-59.62,-78.616],[-74.178,-72.467],[-106.055,-72.712],[-133.764,-44.81],[-133.903,72.552],[-106.281,99.553],[83.645,99.977],[114,71.986],[113.966,-46.797]],"c":true}]}]},"nm":"Path 2","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"a":1,"k":[{"i":{"x":0.19,"y":1},"o":{"x":0.483,"y":0},"t":0,"s":[{"i":[[-7.111,0],[0,-7.111],[7.111,0],[0,7.111]],"o":[[7.111,0],[0,7.111],[-7.111,0],[0,-7.111]],"v":[[73,-38.75],[85.875,-25.875],[73,-13],[60.125,-25.875]],"c":true}]},{"i":{"x":0.2,"y":1},"o":{"x":0.5,"y":0},"t":7,"s":[{"i":[[-12.959,0],[0,-10.579],[12.959,0],[0,10.579]],"o":[[12.959,0],[0,10.579],[-12.959,0],[0,-10.579]],"v":[[73,-36.758],[96.464,-17.602],[73,1.554],[49.536,-17.602]],"c":true}]},{"i":{"x":0.5,"y":1},"o":{"x":0.8,"y":0},"t":18,"s":[{"i":[[-6.429,0],[0,-7.945],[6.429,0],[0,7.945]],"o":[[6.429,0],[0,7.945],[-6.429,0],[0,-7.945]],"v":[[72.652,-48.202],[84.294,-33.815],[72.652,-19.429],[61.011,-33.815]],"c":true}]},{"i":{"x":0.4,"y":1},"o":{"x":0.3,"y":0},"t":27,"s":[{"i":[[-7.701,0],[0,-6.078],[7.701,0],[0,6.078]],"o":[[7.701,0],[0,6.078],[-7.701,0],[0,-6.078]],"v":[[73,-36.88],[86.943,-25.875],[73,-14.87],[59.057,-25.875]],"c":true}]},{"t":34,"s":[{"i":[[-7.111,0],[0,-7.111],[7.111,0],[0,7.111]],"o":[[7.111,0],[0,7.111],[-7.111,0],[0,-7.111]],"v":[[73,-38.75],[85.875,-25.875],[73,-13],[60.125,-25.875]],"c":true}]}]},"nm":"Path 3","hd":false},{"ty":"tr","p":{"a":0,"k":[73.75,-28.625]},"a":{"a":0,"k":[73,-25.875]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 2","bm":0,"hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1]},"o":{"a":0,"k":100},"r":1,"bm":0,"nm":"Fill 1","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0]},"a":{"a":0,"k":[0,0]},"s":{"a":0,"k":[100,100]},"r":{"a":0,"k":0},"o":{"a":0,"k":100},"sk":{"a":0,"k":0},"sa":{"a":0,"k":0},"nm":"Transform"}],"nm":"Group 1","bm":0,"hd":false}],"ip":0,"op":180,"st":0,"bm":0}]} \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 291ed90410..dddca01936 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -4333,6 +4333,7 @@ extension ChatControllerImpl { guard let self, let peerId = self.chatLocation.peerId else { return } + if peerId.namespace == Namespaces.Peer.CloudUser { self.presentAttachmentMenu(subject: .gift) Queue.mainQueue().after(0.5) { diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 402877a551..b30f9e8e71 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4585,6 +4585,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let controller = self.context.sharedContext.makeGiftOptionsController(context: context, peerId: peerId, premiumOptions: premiumOptions, hasBirthday: hasBirthday, completion: nil) self.push(controller) }) + }, openUniqueGift: { [weak self] slug in + guard let self else { + return + } + self.openUrl("https://t.me/nft/\(slug)", concealed: false) }, requestMessageUpdate: { [weak self] id, scroll in if let self { self.chatDisplayNode.historyNode.requestMessageUpdate(id, andScrollToItem: scroll) diff --git a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift index 29951f6d65..7334debc24 100644 --- a/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift +++ b/submodules/TelegramUI/Sources/OverlayAudioPlayerControllerNode.swift @@ -181,6 +181,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, ASGestu }, playMessageEffect: { _ in }, editMessageFactCheck: { _ in }, sendGift: { _ in + }, openUniqueGift: { _ in }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 5cc2644511..45da671fb3 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1937,6 +1937,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { }, playMessageEffect: { _ in }, editMessageFactCheck: { _ in }, sendGift: { _ in + }, openUniqueGift: { _ in }, requestMessageUpdate: { _, _ in }, cancelInteractiveKeyboardGestures: { }, dismissTextInput: { @@ -3256,15 +3257,18 @@ private func peerInfoControllerImpl(context: AccountContext, updatedPresentation } else if let _ = peer as? TelegramChannel { var forumTopicThread: ChatReplyThreadMessage? var switchToRecommendedChannels = false + var switchToGifts = false switch mode { case let .forumTopic(thread): forumTopicThread = thread case .recommendedChannels: switchToRecommendedChannels = true + case .gifts: + switchToGifts = true default: break } - return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], forumTopicThread: forumTopicThread, switchToRecommendedChannels: switchToRecommendedChannels) + return PeerInfoScreenImpl(context: context, updatedPresentationData: updatedPresentationData, peerId: peer.id, avatarInitiallyExpanded: avatarInitiallyExpanded, isOpenedFromChat: isOpenedFromChat, nearbyPeerDistance: nil, reactionSourceMessageId: nil, callMessages: [], forumTopicThread: forumTopicThread, switchToRecommendedChannels: switchToRecommendedChannels, switchToGifts: switchToGifts) } else if peer is TelegramUser { var nearbyPeerDistance: Int32? var reactionSourceMessageId: MessageId?