From beedb2ce7e823425d3554e2c3e394ff1b59b1d37 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 17 Mar 2023 22:46:28 +0400 Subject: [PATCH] Add forwarded icon in chat list --- .../Sources/Node/ChatListItem.swift | 43 +++++ .../Resources/PresentationResourceKey.swift | 2 + .../PresentationResourcesChatList.swift | 6 + .../ForwardedIcon.imageset/Contents.json | 12 ++ .../ForwardedIcon.imageset/forwarded.pdf | 154 ++++++++++++++++++ 5 files changed, 217 insertions(+) create mode 100644 submodules/TelegramUI/Images.xcassets/Chat List/ForwardedIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat List/ForwardedIcon.imageset/forwarded.pdf diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 023a8a2991..38605a929f 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -890,6 +890,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { private var compoundTextButtonNode: HighlightTrackingButtonNode? let measureNode: TextNode private var currentItemHeight: CGFloat? + let forwardedIconNode: ASImageNode let textNode: TextNodeWithEntities var dustNode: InvisibleInkDustNode? let inputActivitiesNode: ChatListInputActivitiesNode @@ -1160,6 +1161,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { self.mentionBadgeNode = ChatListBadgeNode() self.onlineNode = PeerOnlineMarkerNode() + self.forwardedIconNode = ASImageNode() + self.forwardedIconNode.isLayerBacked = true + self.forwardedIconNode.displaysAsynchronously = false + self.forwardedIconNode.displayWithoutProcessing = true + self.pinnedIconNode = ASImageNode() self.pinnedIconNode.isLayerBacked = true self.pinnedIconNode.displaysAsynchronously = false @@ -1651,6 +1657,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { var currentMutedIconImage: UIImage? var currentCredibilityIconContent: EmojiStatusComponent.Content? var currentSecretIconImage: UIImage? + var currentForwardedIcon: UIImage? var selectableControlSizeAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)? var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? @@ -1766,10 +1773,13 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { let contentImageSide: CGFloat = max(10.0, min(20.0, floor(item.presentationData.fontSize.baseDisplaySize * 18.0 / 17.0))) let contentImageSize = CGSize(width: contentImageSide, height: contentImageSide) let contentImageSpacing: CGFloat = 2.0 + let forwardedIconSpacing: CGFloat = 6.0 let contentImageTrailingSpace: CGFloat = 5.0 var contentImageSpecs: [(message: EngineMessage, media: EngineMedia, size: CGSize)] = [] var forumThread: (id: Int64, title: String, iconId: Int64?, iconColor: Int32, isUnread: Bool)? + var displayForwardedIcon = false + switch contentData { case let .chat(itemPeer, _, _, _, text, spoilers, customEmojiRanges): var isUser = false @@ -1928,6 +1938,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { attributedText = composedString + if let forwardInfo = message.forwardInfo, !forwardInfo.flags.contains(.isImported) { + displayForwardedIcon = true + } + var displayMediaPreviews = true if message._asMessage().containsSecretMedia { displayMediaPreviews = false @@ -2009,6 +2023,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { attributedText = textString } + if displayForwardedIcon { + currentForwardedIcon = PresentationResourcesChatList.forwardedIcon(item.presentationData.theme) + } + + if let currentForwardedIcon { + textLeftCutout += currentForwardedIcon.size.width + if !contentImageSpecs.isEmpty { + textLeftCutout += forwardedIconSpacing + } else { + textLeftCutout += contentImageTrailingSpace + } + } + for i in 0 ..< contentImageSpecs.count { if i != 0 { textLeftCutout += contentImageSpacing @@ -3161,6 +3188,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { strongSelf.authorNode.alpha = 0.0 strongSelf.compoundHighlightingNode?.alpha = 0.0 strongSelf.dustNode?.alpha = 0.0 + strongSelf.forwardedIconNode.alpha = 0.0 if animated || animateContent { strongSelf.inputActivitiesNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) @@ -3168,6 +3196,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { strongSelf.authorNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15) strongSelf.compoundHighlightingNode?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15) strongSelf.dustNode?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15) + strongSelf.forwardedIconNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15) } } } else { @@ -3177,6 +3206,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { strongSelf.authorNode.alpha = 1.0 strongSelf.compoundHighlightingNode?.alpha = 1.0 strongSelf.dustNode?.alpha = 1.0 + strongSelf.forwardedIconNode.alpha = 1.0 if animated || animateContent { strongSelf.inputActivitiesNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { value in if let strongSelf = self, value { @@ -3187,6 +3217,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { strongSelf.authorNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) strongSelf.compoundHighlightingNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) strongSelf.dustNode?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) + strongSelf.forwardedIconNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) } else { strongSelf.inputActivitiesNode.removeFromSupernode() } @@ -3203,6 +3234,18 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { inputActivitiesApply?() var mediaPreviewOffset = textNodeFrame.origin.offsetBy(dx: 1.0, dy: floor((measureLayout.size.height - contentImageSize.height) / 2.0)) + + if let currentForwardedIcon = currentForwardedIcon { + strongSelf.forwardedIconNode.image = currentForwardedIcon + if strongSelf.forwardedIconNode.supernode == nil { + strongSelf.mainContentContainerNode.addSubnode(strongSelf.forwardedIconNode) + } + transition.updateFrame(node: strongSelf.forwardedIconNode, frame: CGRect(origin: CGPoint(x: mediaPreviewOffset.x, y: mediaPreviewOffset.y + 3.0), size: currentForwardedIcon.size)) + mediaPreviewOffset.x += currentForwardedIcon.size.width + forwardedIconSpacing + } else if strongSelf.forwardedIconNode.supernode != nil { + strongSelf.forwardedIconNode.removeFromSupernode() + } + var validMediaIds: [EngineMedia.Id] = [] for (message, media, mediaSize) in contentImageSpecs { var mediaId = media.id diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index c52a686a77..7bf1a7998d 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -106,6 +106,8 @@ public enum PresentationResourceKey: Int32 { case chatListRecentStatusVoiceChatPinnedIcon case chatListRecentStatusVoiceChatPanelIcon + case chatListForwardedIcon + case chatListGeneralTopicIcon case chatListGeneralTopicSmallIcon diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift index c6235cba73..8f80673a7e 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChatList.swift @@ -247,6 +247,12 @@ public struct PresentationResourcesChatList { }) } + public static func forwardedIcon(_ theme: PresentationTheme) -> UIImage? { + return theme.image(PresentationResourceKey.chatListForwardedIcon.rawValue, { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat List/ForwardedIcon"), color: theme.chatList.muteIconColor) + }) + } + public static func verifiedIcon(_ theme: PresentationTheme) -> UIImage? { return theme.image(PresentationResourceKey.chatListVerifiedIcon.rawValue, { theme in if let backgroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconBackground"), let foregroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconForeground") { diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ForwardedIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat List/ForwardedIcon.imageset/Contents.json new file mode 100644 index 0000000000..04c466c9d1 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/ForwardedIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "forwarded.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat List/ForwardedIcon.imageset/forwarded.pdf b/submodules/TelegramUI/Images.xcassets/Chat List/ForwardedIcon.imageset/forwarded.pdf new file mode 100644 index 0000000000..8e34379240 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat List/ForwardedIcon.imageset/forwarded.pdf @@ -0,0 +1,154 @@ +%PDF-1.7 + +1 0 obj + << /Type /XObject + /Length 2 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 14.000000 14.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 2.376520 cm +0.000000 0.000000 0.000000 scn +7.214178 7.143479 m +7.214178 8.355392 l +7.214178 9.166631 7.214178 9.572250 7.376865 9.767084 c +7.518138 9.936272 7.730846 10.029038 7.950955 10.017458 c +8.204431 10.004122 8.501691 9.728144 9.096210 9.176188 c +13.115910 5.444274 l +13.423076 5.159099 13.576659 5.016511 13.633628 4.849649 c +13.683687 4.703024 13.683687 4.543934 13.633628 4.397310 c +13.576659 4.230447 13.423076 4.087859 13.115908 3.802683 c +9.096210 0.070769 l +8.501690 -0.481186 8.204431 -0.757164 7.950955 -0.770499 c +7.730846 -0.782080 7.518138 -0.689313 7.376865 -0.520126 c +7.214178 -0.325293 7.214178 0.080327 7.214178 0.891567 c +7.214178 2.103479 l +4.071625 2.103479 2.169120 0.875460 1.113997 -0.180696 c +0.644118 -0.651034 0.409179 -0.886204 0.299532 -0.890471 c +0.197423 -0.894444 0.118586 -0.855472 0.059738 -0.771932 c +-0.003455 -0.682224 0.035404 -0.392442 0.113122 0.187123 c +0.460188 2.775306 1.841749 7.143479 7.214178 7.143479 c +h +f* +n +Q + +endstream +endobj + +2 0 obj + 1042 +endobj + +3 0 obj + << /Type /XObject + /Length 4 0 R + /Group << /Type /Group + /S /Transparency + >> + /Subtype /Form + /Resources << >> + /BBox [ 0.000000 0.000000 14.000000 14.000000 ] + >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm +0.000000 0.000000 0.000000 scn +0.000000 14.000000 m +14.000000 14.000000 l +14.000000 0.000000 l +0.000000 0.000000 l +0.000000 14.000000 l +h +f +n +Q + +endstream +endobj + +4 0 obj + 232 +endobj + +5 0 obj + << /XObject << /X1 1 0 R >> + /ExtGState << /E1 << /SMask << /Type /Mask + /G 3 0 R + /S /Alpha + >> + /Type /ExtGState + >> >> + >> +endobj + +6 0 obj + << /Length 7 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +/E1 gs +/X1 Do +Q + +endstream +endobj + +7 0 obj + 46 +endobj + +8 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 14.000000 14.000000 ] + /Resources 5 0 R + /Contents 6 0 R + /Parent 9 0 R + >> +endobj + +9 0 obj + << /Kids [ 8 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +10 0 obj + << /Pages 9 0 R + /Type /Catalog + >> +endobj + +xref +0 11 +0000000000 65535 f +0000000010 00000 n +0000001300 00000 n +0000001323 00000 n +0000001803 00000 n +0000001825 00000 n +0000002123 00000 n +0000002225 00000 n +0000002246 00000 n +0000002419 00000 n +0000002493 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 10 0 R + /Size 11 +>> +startxref +2553 +%%EOF \ No newline at end of file