Add forwarded icon in chat list

This commit is contained in:
Ilya Laktyushin
2023-03-17 22:46:28 +04:00
parent d4d0d5ff2d
commit beedb2ce7e
5 changed files with 217 additions and 0 deletions

View File

@@ -890,6 +890,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
private var compoundTextButtonNode: HighlightTrackingButtonNode? private var compoundTextButtonNode: HighlightTrackingButtonNode?
let measureNode: TextNode let measureNode: TextNode
private var currentItemHeight: CGFloat? private var currentItemHeight: CGFloat?
let forwardedIconNode: ASImageNode
let textNode: TextNodeWithEntities let textNode: TextNodeWithEntities
var dustNode: InvisibleInkDustNode? var dustNode: InvisibleInkDustNode?
let inputActivitiesNode: ChatListInputActivitiesNode let inputActivitiesNode: ChatListInputActivitiesNode
@@ -1160,6 +1161,11 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
self.mentionBadgeNode = ChatListBadgeNode() self.mentionBadgeNode = ChatListBadgeNode()
self.onlineNode = PeerOnlineMarkerNode() self.onlineNode = PeerOnlineMarkerNode()
self.forwardedIconNode = ASImageNode()
self.forwardedIconNode.isLayerBacked = true
self.forwardedIconNode.displaysAsynchronously = false
self.forwardedIconNode.displayWithoutProcessing = true
self.pinnedIconNode = ASImageNode() self.pinnedIconNode = ASImageNode()
self.pinnedIconNode.isLayerBacked = true self.pinnedIconNode.isLayerBacked = true
self.pinnedIconNode.displaysAsynchronously = false self.pinnedIconNode.displaysAsynchronously = false
@@ -1651,6 +1657,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
var currentMutedIconImage: UIImage? var currentMutedIconImage: UIImage?
var currentCredibilityIconContent: EmojiStatusComponent.Content? var currentCredibilityIconContent: EmojiStatusComponent.Content?
var currentSecretIconImage: UIImage? var currentSecretIconImage: UIImage?
var currentForwardedIcon: UIImage?
var selectableControlSizeAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)? var selectableControlSizeAndApply: (CGFloat, (CGSize, Bool) -> ItemListSelectableControlNode)?
var reorderControlSizeAndApply: (CGFloat, (CGFloat, Bool, ContainedViewLayoutTransition) -> ItemListEditableReorderControlNode)? 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 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 contentImageSize = CGSize(width: contentImageSide, height: contentImageSide)
let contentImageSpacing: CGFloat = 2.0 let contentImageSpacing: CGFloat = 2.0
let forwardedIconSpacing: CGFloat = 6.0
let contentImageTrailingSpace: CGFloat = 5.0 let contentImageTrailingSpace: CGFloat = 5.0
var contentImageSpecs: [(message: EngineMessage, media: EngineMedia, size: CGSize)] = [] var contentImageSpecs: [(message: EngineMessage, media: EngineMedia, size: CGSize)] = []
var forumThread: (id: Int64, title: String, iconId: Int64?, iconColor: Int32, isUnread: Bool)? var forumThread: (id: Int64, title: String, iconId: Int64?, iconColor: Int32, isUnread: Bool)?
var displayForwardedIcon = false
switch contentData { switch contentData {
case let .chat(itemPeer, _, _, _, text, spoilers, customEmojiRanges): case let .chat(itemPeer, _, _, _, text, spoilers, customEmojiRanges):
var isUser = false var isUser = false
@@ -1928,6 +1938,10 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
attributedText = composedString attributedText = composedString
if let forwardInfo = message.forwardInfo, !forwardInfo.flags.contains(.isImported) {
displayForwardedIcon = true
}
var displayMediaPreviews = true var displayMediaPreviews = true
if message._asMessage().containsSecretMedia { if message._asMessage().containsSecretMedia {
displayMediaPreviews = false displayMediaPreviews = false
@@ -2009,6 +2023,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
attributedText = textString 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 { for i in 0 ..< contentImageSpecs.count {
if i != 0 { if i != 0 {
textLeftCutout += contentImageSpacing textLeftCutout += contentImageSpacing
@@ -3161,6 +3188,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.authorNode.alpha = 0.0 strongSelf.authorNode.alpha = 0.0
strongSelf.compoundHighlightingNode?.alpha = 0.0 strongSelf.compoundHighlightingNode?.alpha = 0.0
strongSelf.dustNode?.alpha = 0.0 strongSelf.dustNode?.alpha = 0.0
strongSelf.forwardedIconNode.alpha = 0.0
if animated || animateContent { if animated || animateContent {
strongSelf.inputActivitiesNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15) 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.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.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.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 { } else {
@@ -3177,6 +3206,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
strongSelf.authorNode.alpha = 1.0 strongSelf.authorNode.alpha = 1.0
strongSelf.compoundHighlightingNode?.alpha = 1.0 strongSelf.compoundHighlightingNode?.alpha = 1.0
strongSelf.dustNode?.alpha = 1.0 strongSelf.dustNode?.alpha = 1.0
strongSelf.forwardedIconNode.alpha = 1.0
if animated || animateContent { if animated || animateContent {
strongSelf.inputActivitiesNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { value in strongSelf.inputActivitiesNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, completion: { value in
if let strongSelf = self, value { 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.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.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.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 { } else {
strongSelf.inputActivitiesNode.removeFromSupernode() strongSelf.inputActivitiesNode.removeFromSupernode()
} }
@@ -3203,6 +3234,18 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
inputActivitiesApply?() inputActivitiesApply?()
var mediaPreviewOffset = textNodeFrame.origin.offsetBy(dx: 1.0, dy: floor((measureLayout.size.height - contentImageSize.height) / 2.0)) 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] = [] var validMediaIds: [EngineMedia.Id] = []
for (message, media, mediaSize) in contentImageSpecs { for (message, media, mediaSize) in contentImageSpecs {
var mediaId = media.id var mediaId = media.id

View File

@@ -106,6 +106,8 @@ public enum PresentationResourceKey: Int32 {
case chatListRecentStatusVoiceChatPinnedIcon case chatListRecentStatusVoiceChatPinnedIcon
case chatListRecentStatusVoiceChatPanelIcon case chatListRecentStatusVoiceChatPanelIcon
case chatListForwardedIcon
case chatListGeneralTopicIcon case chatListGeneralTopicIcon
case chatListGeneralTopicSmallIcon case chatListGeneralTopicSmallIcon

View File

@@ -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? { public static func verifiedIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.chatListVerifiedIcon.rawValue, { theme in return theme.image(PresentationResourceKey.chatListVerifiedIcon.rawValue, { theme in
if let backgroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconBackground"), let foregroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconForeground") { if let backgroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconBackground"), let foregroundImage = UIImage(bundleImageName: "Chat List/PeerVerifiedIconForeground") {

View File

@@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "forwarded.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -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