From 13c6cf58c8c85a31ce5d42a11131157393ae1971 Mon Sep 17 00:00:00 2001 From: Ali <> Date: Tue, 20 Jun 2023 16:58:52 +0300 Subject: [PATCH] Stories --- submodules/TelegramApi/Sources/Api31.swift | 15 +++ .../State/AccountStateManagementUtils.swift | 2 + .../Sources/State/AccountViewTracker.swift | 15 ++- .../SyncCore_TelegramMediaWebpage.swift | 10 +- .../TelegramEngine/Messages/Stories.swift | 2 + .../Resources/PresentationResourceKey.swift | 8 ++ .../Resources/PresentationResourcesChat.swift | 35 +++++++ submodules/TelegramUI/BUILD | 1 + .../Chat/ChatMessageForwardInfoNode/BUILD | 23 +++++ .../Sources/ChatMessageForwardInfoNode.swift | 60 +++++++++-- .../ExpiredStoryIcon.imageset/Contents.json | 12 +++ .../ExpiredStoryIcon.imageset/timer.pdf | 95 ++++++++++++++++++ .../Contents.json | 12 +++ .../Slice 1.png | Bin 0 -> 6637 bytes .../TelegramUI/Sources/ChatController.swift | 4 + .../Sources/ChatHistoryListNode.swift | 14 ++- .../ChatMessageAnimatedStickerItemNode.swift | 3 +- .../Sources/ChatMessageBubbleItemNode.swift | 27 ++++- .../ChatMessageInstantVideoItemNode.swift | 3 +- ...atMessageInteractiveInstantVideoNode.swift | 3 +- .../Sources/ChatMessageReplyInfoNode.swift | 51 +++++++++- .../Sources/ChatMessageStickerItemNode.swift | 3 +- .../ChatMessageWebpageBubbleContentNode.swift | 6 +- .../TelegramUI/Sources/OpenChatMessage.swift | 4 +- 24 files changed, 372 insertions(+), 36 deletions(-) create mode 100644 submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/BUILD rename submodules/TelegramUI/{ => Components/Chat/ChatMessageForwardInfoNode}/Sources/ChatMessageForwardInfoNode.swift (84%) create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryIcon.imageset/timer.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryPlaceholder.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryPlaceholder.imageset/Slice 1.png diff --git a/submodules/TelegramApi/Sources/Api31.swift b/submodules/TelegramApi/Sources/Api31.swift index fb3a5fc70c..7f56233329 100644 --- a/submodules/TelegramApi/Sources/Api31.swift +++ b/submodules/TelegramApi/Sources/Api31.swift @@ -8696,6 +8696,21 @@ public extension Api.functions.stories { }) } } +public extension Api.functions.stories { + static func toggleAllStoriesHidden(hidden: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(2082822084) + hidden.serialize(buffer, true) + return (FunctionDescription(name: "stories.toggleAllStoriesHidden", parameters: [("hidden", String(describing: hidden))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } +} public extension Api.functions.stories { static func togglePinned(id: [Int32], pinned: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index add0ffac0c..a713c032f9 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -2123,6 +2123,8 @@ func resolveStories(postbox: Postbox, source: FetchMessageHistoryHoleSource, if let entry = CodableEntry(updatedItem) { updated = entry } + } else { + updated = CodableEntry(data: Data()) } if current != updated { transaction.setStory(id: StoryId(peerId: peerId, id: id), value: updated ?? CodableEntry(data: Data())) diff --git a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift index f0afb02650..600d8624eb 100644 --- a/submodules/TelegramCore/Sources/State/AccountViewTracker.swift +++ b/submodules/TelegramCore/Sources/State/AccountViewTracker.swift @@ -1236,7 +1236,14 @@ public final class AccountViewTracker { let timestamp = Int32(CFAbsoluteTimeGetCurrent()) for messageId in messageIds { let messageTimestamp = self.refreshStoriesForMessageIdsAndTimestamps[messageId] - if messageTimestamp == nil { + var refresh = false + if let messageTimestamp = messageTimestamp { + refresh = messageTimestamp < timestamp - 60 + } else { + refresh = true + } + + if refresh { self.refreshStoriesForMessageIdsAndTimestamps[messageId] = timestamp addedMessageIds.append(messageId) } @@ -1258,6 +1265,12 @@ public final class AccountViewTracker { result.insert(story.storyId) } } + + for attribute in message.attributes { + if let attribute = attribute as? ReplyStoryAttribute { + result.insert(attribute.storyId) + } + } } } return result diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaWebpage.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaWebpage.swift index 8387c277fc..f56add72b1 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaWebpage.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_TelegramMediaWebpage.swift @@ -308,7 +308,15 @@ public final class TelegramMediaWebpage: Media, Equatable { public var id: MediaId? { return self.webpageId } - public let peerIds: [PeerId] = [] + public var peerIds: [PeerId] { + var result: [PeerId] = [] + for storyId in self.storyIds { + if !result.contains(storyId.peerId) { + result.append(storyId.peerId) + } + } + return result + } public var storyIds: [StoryId] { if case let .Loaded(content) = self.content, let story = content.story { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index fc684ab374..bd0a6ac903 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -1707,6 +1707,8 @@ func _internal_refreshStories(account: Account, peerId: PeerId, ids: [Int32]) -> if let entry = CodableEntry(updatedItem) { updated = entry } + } else { + updated = CodableEntry(data: Data()) } if current != updated { transaction.setStory(id: StoryId(peerId: peerId, id: id), value: updated ?? CodableEntry(data: Data())) diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift index 0b8c7195d3..36619e64be 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourceKey.swift @@ -297,6 +297,12 @@ public enum PresentationResourceKey: Int32 { case uploadToneIcon } +public enum ChatExpiredStoryIndicatorType: Hashable { + case incoming + case outgoing + case free +} + public enum PresentationResourceParameterKey: Hashable { case chatOutgoingFullCheck(CGFloat) case chatOutgoingPartialCheck(CGFloat) @@ -333,4 +339,6 @@ public enum PresentationResourceParameterKey: Hashable { case chatInputMediaPanelGridDismissImage(color: UInt32) case statusAutoremoveIcon(isActive: Bool) + + case chatExpiredStoryIndicatorIcon(type: ChatExpiredStoryIndicatorType) } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift index e0478fd6bd..d70b7e17e5 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesChat.swift @@ -1246,4 +1246,39 @@ public struct PresentationResourcesChat { return generateTintedImage(image: UIImage(bundleImageName: "Chat List/GeneralTopicIcon"), color: theme.chat.message.mediaOverlayControlColors.foregroundColor) }) } + + public static func chatExpiredStoryIndicatorIcon(_ theme: PresentationTheme, type: ChatExpiredStoryIndicatorType) -> UIImage? { + return theme.image(PresentationResourceParameterKey.chatExpiredStoryIndicatorIcon(type: type), { theme in + return generateImage(CGSize(width: 34.0, height: 34.0), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + + context.addPath(UIBezierPath(roundedRect: CGRect(origin: CGPoint(), size: size), cornerRadius: 6.0).cgPath) + context.clip() + + let color: UIColor + switch type { + case .incoming: + color = theme.chat.message.incoming.mediaPlaceholderColor + case .outgoing: + color = theme.chat.message.outgoing.mediaPlaceholderColor + case .free: + color = theme.chat.message.freeform.withWallpaper.fill[0] + } + + context.setFillColor(color.cgColor) + context.fill(CGRect(origin: CGPoint(), size: size)) + + if let image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Message/ExpiredStoryIcon"), color: .white), let icon = UIImage(bundleImageName: "Chat/Message/ExpiredStoryPlaceholder") { + UIGraphicsPushContext(context) + + icon.draw(in: CGRect(origin: CGPoint(), size: size)) + + //image.draw(at: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), blendMode: .destinationOut, alpha: 1.0) + image.draw(at: CGPoint(x: floor((size.width - image.size.width) * 0.5), y: floor((size.height - image.size.height) * 0.5)), blendMode: .normal, alpha: 1.0) + + UIGraphicsPopContext() + } + }) + }) + } } diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index c944fc7b3b..bcf14c354e 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -375,6 +375,7 @@ swift_library( "//submodules/TelegramUI/Components/MoreHeaderButton", "//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent", "//submodules/TelegramUI/Components/Stories/StorySetIndicatorComponent", + "//submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode", "//submodules/Utils/VolumeButtons", ] + select({ "@build_bazel_rules_apple//apple:ios_armv7": [], diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/BUILD new file mode 100644 index 0000000000..7f304fb3d2 --- /dev/null +++ b/submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/BUILD @@ -0,0 +1,23 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "ChatMessageForwardInfoNode", + module_name = "ChatMessageForwardInfoNode", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/AsyncDisplayKit", + "//submodules/Display", + "//submodules/Postbox", + "//submodules/TelegramCore", + "//submodules/TelegramPresentationData", + "//submodules/LocalizedPeerData", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/TelegramUI/Sources/ChatMessageForwardInfoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/Sources/ChatMessageForwardInfoNode.swift similarity index 84% rename from submodules/TelegramUI/Sources/ChatMessageForwardInfoNode.swift rename to submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/Sources/ChatMessageForwardInfoNode.swift index fb8d39d8b5..3ad6b0a5fd 100644 --- a/submodules/TelegramUI/Sources/ChatMessageForwardInfoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageForwardInfoNode/Sources/ChatMessageForwardInfoNode.swift @@ -7,7 +7,7 @@ import TelegramCore import TelegramPresentationData import LocalizedPeerData -enum ChatMessageForwardInfoType: Equatable { +public enum ChatMessageForwardInfoType: Equatable { case bubble(incoming: Bool) case standalone } @@ -57,18 +57,27 @@ private final class InfoButtonNode: HighlightableButtonNode { } } -class ChatMessageForwardInfoNode: ASDisplayNode { +public class ChatMessageForwardInfoNode: ASDisplayNode { + public struct StoryData: Equatable { + public var isExpired: Bool + + public init(isExpired: Bool) { + self.isExpired = isExpired + } + } + private var textNode: TextNode? private var credibilityIconNode: ASImageNode? private var infoNode: InfoButtonNode? + private var expiredStoryIconView: UIImageView? - var openPsa: ((String, ASDisplayNode) -> Void)? + public var openPsa: ((String, ASDisplayNode) -> Void)? - override init() { + override public init() { super.init() } - func hasAction(at point: CGPoint) -> Bool { + public func hasAction(at point: CGPoint) -> Bool { if let infoNode = self.infoNode, infoNode.frame.contains(point) { return true } else { @@ -76,7 +85,7 @@ class ChatMessageForwardInfoNode: ASDisplayNode { } } - func updatePsaButtonDisplay(isVisible: Bool, animated: Bool) { + public func updatePsaButtonDisplay(isVisible: Bool, animated: Bool) { if let infoNode = self.infoNode { if isVisible != !infoNode.iconNode.alpha.isZero { let transition: ContainedViewLayoutTransition @@ -91,10 +100,10 @@ class ChatMessageForwardInfoNode: ASDisplayNode { } } - class func asyncLayout(_ maybeNode: ChatMessageForwardInfoNode?) -> (_ presentationData: ChatPresentationData, _ strings: PresentationStrings, _ type: ChatMessageForwardInfoType, _ peer: Peer?, _ authorName: String?, _ psaType: String?, _ isStory: Bool, _ constrainedSize: CGSize) -> (CGSize, (CGFloat) -> ChatMessageForwardInfoNode) { + public static func asyncLayout(_ maybeNode: ChatMessageForwardInfoNode?) -> (_ presentationData: ChatPresentationData, _ strings: PresentationStrings, _ type: ChatMessageForwardInfoType, _ peer: Peer?, _ authorName: String?, _ psaType: String?, _ storyData: StoryData?, _ constrainedSize: CGSize) -> (CGSize, (CGFloat) -> ChatMessageForwardInfoNode) { let textNodeLayout = TextNode.asyncLayout(maybeNode?.textNode) - return { presentationData, strings, type, peer, authorName, psaType, isStory, constrainedSize in + return { presentationData, strings, type, peer, authorName, psaType, storyData, constrainedSize in let fontSize = floor(presentationData.fontSize.baseDisplaySize * 13.0 / 17.0) let prefixFont = Font.regular(fontSize) let peerFont = Font.medium(fontSize) @@ -149,7 +158,7 @@ class ChatMessageForwardInfoNode: ASDisplayNode { } else { titleColor = incoming ? presentationData.theme.theme.chat.message.incoming.accentTextColor : presentationData.theme.theme.chat.message.outgoing.accentTextColor - if isStory { + if let _ = storyData { completeSourceString = strings.Message_ForwardedStoryShort(peerString) } else { completeSourceString = strings.Message_ForwardedMessageShort(peerString) @@ -234,6 +243,11 @@ class ChatMessageForwardInfoNode: ASDisplayNode { if hasPsaInfo { infoWidth += 32.0 } + var leftOffset: CGFloat = 0.0 + if let storyData, storyData.isExpired { + leftOffset += 34.0 + 6.0 + } + infoWidth += leftOffset let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: string, backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: constrainedSize.width - credibilityIconWidth - infoWidth, height: constrainedSize.height), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) @@ -251,7 +265,33 @@ class ChatMessageForwardInfoNode: ASDisplayNode { node.textNode = textNode node.addSubnode(textNode) } - textNode.frame = CGRect(origin: CGPoint(), size: textLayout.size) + textNode.frame = CGRect(origin: CGPoint(x: leftOffset, y: 0.0), size: textLayout.size) + + if let storyData, storyData.isExpired { + let expiredStoryIconView: UIImageView + if let current = node.expiredStoryIconView { + expiredStoryIconView = current + } else { + expiredStoryIconView = UIImageView() + node.expiredStoryIconView = expiredStoryIconView + node.view.addSubview(expiredStoryIconView) + } + + let imageType: ChatExpiredStoryIndicatorType + switch type { + case .standalone: + imageType = .free + case let .bubble(incoming): + imageType = incoming ? .incoming : .outgoing + } + + expiredStoryIconView.image = PresentationResourcesChat.chatExpiredStoryIndicatorIcon(presentationData.theme.theme, type: imageType) + if let image = expiredStoryIconView.image { + expiredStoryIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: image.size) + } + } else if let expiredStoryIconView = node.expiredStoryIconView { + expiredStoryIconView.removeFromSuperview() + } if let credibilityIconImage = currentCredibilityIconImage { let credibilityIconNode: ASImageNode diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryIcon.imageset/Contents.json new file mode 100644 index 0000000000..a5c36ef619 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "timer.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryIcon.imageset/timer.pdf b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryIcon.imageset/timer.pdf new file mode 100644 index 0000000000..4767c66cdd --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryIcon.imageset/timer.pdf @@ -0,0 +1,95 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 11.386475 7.000000 cm +0.000000 0.000000 0.000000 scn +3.109776 20.000000 m +1.179763 20.000000 -0.283886 18.259834 0.046805 16.358360 c +0.156231 15.729159 0.419039 15.136623 0.811980 14.633168 c +3.413476 11.300000 l +3.439425 11.257834 l +3.914118 10.486457 3.914118 9.513542 3.439425 8.742166 c +3.413476 8.700000 l +0.811979 5.366831 l +0.419039 4.863377 0.156231 4.270840 0.046805 3.641638 c +-0.283886 1.740166 1.179765 0.000000 3.109779 0.000000 c +8.117176 0.000000 l +10.047190 0.000000 11.510839 1.740166 11.180147 3.641638 c +11.070721 4.270841 10.807913 4.863377 10.414972 5.366832 c +7.813476 8.700000 l +7.787528 8.742166 l +7.312835 9.513542 7.312835 10.486458 7.787528 11.257834 c +7.813476 11.300000 l +10.414973 14.633169 l +10.807914 15.136623 11.070721 15.729160 11.180147 16.358362 c +11.510839 18.259834 10.047188 20.000000 8.117173 20.000000 c +3.109776 20.000000 l +h +7.428961 3.624302 m +6.183511 4.350814 4.643443 4.350814 3.397992 3.624302 c +1.891497 2.745512 l +1.538821 2.539783 1.684751 2.000000 2.093044 2.000000 c +8.733909 2.000000 l +9.142202 2.000000 9.288133 2.539783 8.935456 2.745512 c +7.428961 3.624302 l +h +f* +n +Q + +endstream +endobj + +3 0 obj + 1194 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 34.000000 34.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000001284 00000 n +0000001307 00000 n +0000001480 00000 n +0000001554 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +1613 +%%EOF \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryPlaceholder.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryPlaceholder.imageset/Contents.json new file mode 100644 index 0000000000..037cefa85b --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryPlaceholder.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Slice 1.png", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryPlaceholder.imageset/Slice 1.png b/submodules/TelegramUI/Images.xcassets/Chat/Message/ExpiredStoryPlaceholder.imageset/Slice 1.png new file mode 100644 index 0000000000000000000000000000000000000000..7546315a2c3d064f2a9798349378c9126346cc44 GIT binary patch literal 6637 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 01c31e3170..408c7df734 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -4508,6 +4508,10 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G guard let self else { return } + if let story = message.associatedStories[storyId], story.data.isEmpty { + return + } + let storyContent = SingleStoryContentContextImpl(context: self.context, storyId: storyId) let _ = (storyContent.state |> take(1) diff --git a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift index 2560d71d23..5c9c5808ab 100644 --- a/submodules/TelegramUI/Sources/ChatHistoryListNode.swift +++ b/submodules/TelegramUI/Sources/ChatHistoryListNode.swift @@ -2093,6 +2093,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if message.stableId != ChatHistoryListNode.fixedAdMessageStableId { visibleAdOpaqueIds.append(attribute.opaqueId) } + } else if let _ = attribute as? ReplyStoryAttribute { + storiesRequiredValidation = true } } @@ -2132,14 +2134,10 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode { if invoice.version != TelegramMediaInvoice.lastVersion { contentRequiredValidation = true } - } else if let story = media as? TelegramMediaStory { - if message.associatedStories[story.storyId] == nil { - storiesRequiredValidation = true - } - } else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, let story = content.story { - if message.associatedStories[story.storyId] == nil { - storiesRequiredValidation = true - } + } else if let _ = media as? TelegramMediaStory { + storiesRequiredValidation = true + } else if let webpage = media as? TelegramMediaWebpage, case let .Loaded(content) = webpage.content, let _ = content.story { + storiesRequiredValidation = true } } if contentRequiredValidation { diff --git a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift index cc904d33eb..157d82f2c3 100644 --- a/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -28,6 +28,7 @@ import AppBundle import ChatPresentationInterfaceState import TextNodeWithEntities import ChatControllerInteraction +import ChatMessageForwardInfoNode private let nameFont = Font.medium(14.0) private let inlineBotPrefixFont = Font.regular(14.0) @@ -1348,7 +1349,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { } } let availableWidth = max(60.0, availableContentWidth + 6.0) - forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, false, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) + forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, nil, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) } if replyInfoApply != nil || viaBotApply != nil || forwardInfoSizeApply != nil { diff --git a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift index 06472006c5..6dab7fc27d 100644 --- a/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageBubbleItemNode.swift @@ -29,6 +29,7 @@ import MultiAnimationRenderer import ComponentFlow import EmojiStatusComponent import ChatControllerInteraction +import ChatMessageForwardInfoNode enum InternalBubbleTapAction { case action(() -> Void) @@ -127,11 +128,14 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([ messageWithCaptionToAdd = (message, itemAttributes) } result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default))) - } else if let _ = media as? TelegramMediaStory { + } else if let story = media as? TelegramMediaStory { if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported), message.text.isEmpty { messageWithCaptionToAdd = (message, itemAttributes) } - result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default))) + if let storyItem = message.associatedStories[story.storyId], storyItem.data.isEmpty { + } else { + result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default))) + } } else if let file = media as? TelegramMediaFile { let isVideo = file.isVideo || (file.isAnimated && file.dimensions != nil) if isVideo { @@ -1126,7 +1130,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode authorNameLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode), adminBadgeLayout: (TextNodeLayoutArguments) -> (TextNodeLayout, () -> TextNode), threadInfoLayout: (ChatMessageThreadInfoNode.Arguments) -> (CGSize, (Bool) -> ChatMessageThreadInfoNode), - forwardInfoLayout: (ChatPresentationData, PresentationStrings, ChatMessageForwardInfoType, Peer?, String?, String?, Bool, CGSize) -> (CGSize, (CGFloat) -> ChatMessageForwardInfoNode), + forwardInfoLayout: (ChatPresentationData, PresentationStrings, ChatMessageForwardInfoType, Peer?, String?, String?, ChatMessageForwardInfoNode.StoryData?, CGSize) -> (CGSize, (CGFloat) -> ChatMessageForwardInfoNode), replyInfoLayout: (ChatMessageReplyInfoNode.Arguments) -> (CGSize, (Bool) -> ChatMessageReplyInfoNode), actionButtonsLayout: (AccountContext, ChatPresentationThemeData, PresentationChatBubbleCorners, PresentationStrings, WallpaperBackgroundNode?, ReplyMarkupMessageAttribute, Message, CGFloat) -> (minWidth: CGFloat, layout: (CGFloat) -> (CGSize, (ListViewItemUpdateAnimation) -> ChatMessageActionButtonsNode)), reactionButtonsLayout: (ChatMessageReactionButtonsNode.Arguments) -> (minWidth: CGFloat, layout: (CGFloat) -> (size: CGSize, apply: (ListViewItemUpdateAnimation) -> ChatMessageReactionButtonsNode)), @@ -2012,7 +2016,7 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode forwardAuthorSignature = forwardInfo.authorSignature } } - let sizeAndApply = forwardInfoLayout(item.presentationData, item.presentationData.strings, .bubble(incoming: incoming), forwardSource, forwardAuthorSignature, forwardPsaType, false, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude)) + let sizeAndApply = forwardInfoLayout(item.presentationData, item.presentationData.strings, .bubble(incoming: incoming), forwardSource, forwardAuthorSignature, forwardPsaType, nil, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude)) forwardInfoSizeApply = (sizeAndApply.0, { width in sizeAndApply.1(width) }) forwardInfoOriginY = headerSize.height @@ -2026,12 +2030,25 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode forwardSource = firstMessage.peers[storyMedia.storyId.peerId] - let sizeAndApply = forwardInfoLayout(item.presentationData, item.presentationData.strings, .bubble(incoming: incoming), forwardSource, nil, nil, true, CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude)) + var isExpired: Bool = false + if let storyItem = firstMessage.associatedStories[storyMedia.storyId], storyItem.data.isEmpty { + isExpired = true + } + + let sizeAndApply = forwardInfoLayout(item.presentationData, item.presentationData.strings, .bubble(incoming: incoming), forwardSource, nil, nil, ChatMessageForwardInfoNode.StoryData(isExpired: isExpired), CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right, height: CGFloat.greatestFiniteMagnitude)) forwardInfoSizeApply = (sizeAndApply.0, { width in sizeAndApply.1(width) }) + if isExpired { + headerSize.height += 6.0 + } + forwardInfoOriginY = headerSize.height headerSize.width = max(headerSize.width, forwardInfoSizeApply.0.width + bubbleWidthInsets) headerSize.height += forwardInfoSizeApply.0.height + + if isExpired { + headerSize.height += 16.0 + } } var hasReply = replyMessage != nil || replyStory != nil diff --git a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift index 93de93541a..7917a3d28f 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInstantVideoItemNode.swift @@ -13,6 +13,7 @@ import LocalizedPeerData import ContextUI import Markdown import ChatControllerInteraction +import ChatMessageForwardInfoNode private let nameFont = Font.medium(14.0) @@ -528,7 +529,7 @@ class ChatMessageInstantVideoItemNode: ChatMessageItemView, UIGestureRecognizerD } } let availableWidth = max(60.0, availableContentWidth - normalDisplaySize.width + 6.0) - forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, false, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) + forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, nil, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) } if replyInfoApply != nil || viaBotApply != nil || forwardInfoSizeApply != nil { diff --git a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift index 88280f5780..c6f3efe3db 100644 --- a/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -19,6 +19,7 @@ import UndoUI import TelegramNotices import Markdown import TextFormat +import ChatMessageForwardInfoNode struct ChatMessageInstantVideoItemLayoutResult { let contentSize: CGSize @@ -399,7 +400,7 @@ class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { } } let availableWidth = max(60.0, availableContentWidth - 210.0 + 6.0) - forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, false, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) + forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, nil, CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude)) } var notConsumed = false diff --git a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift index 7feec6b71c..ea927fb248 100644 --- a/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageReplyInfoNode.swift @@ -77,6 +77,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { private var dustNode: InvisibleInkDustNode? private var imageNode: TransformImageNode? private var previousMediaReference: AnyMediaReference? + private var expiredStoryIconView: UIImageView? override init() { self.contentNode = ASDisplayNode() @@ -111,6 +112,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { let textString: NSAttributedString let isMedia: Bool let isText: Bool + var isExpiredStory: Bool = false if let message = arguments.message { let author = message.effectiveAuthor @@ -135,9 +137,14 @@ class ChatMessageReplyInfoNode: ASDisplayNode { titleString = arguments.strings.User_DeletedAccount } //TODO:localize - textString = NSAttributedString(string: "Story") isMedia = true isText = false + if let storyItem = arguments.parentMessage.associatedStories[story], storyItem.data.isEmpty { + isExpiredStory = true + textString = NSAttributedString(string: "Expired story") + } else { + textString = NSAttributedString(string: "Story") + } } else { titleString = " " textString = NSAttributedString(string: " ") @@ -276,6 +283,8 @@ class ChatMessageReplyInfoNode: ASDisplayNode { imageDimensions = representation.dimensions.cgSize } } + } else if storyItem.data.isEmpty { + imageDimensions = CGSize(width: 34.0, height: 34.0) } } @@ -293,7 +302,12 @@ class ChatMessageReplyInfoNode: ASDisplayNode { let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: titleString, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: textInsets)) let (textLayout, textApply) = textNodeLayout(TextNodeLayoutArguments(attributedString: messageText, backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: contrainedTextSize, alignment: .natural, cutout: nil, insets: textInsets)) - let imageSide = titleLayout.size.height + textLayout.size.height - 16.0 + let imageSide: CGFloat + if isExpiredStory { + imageSide = 38.0 + } else { + imageSide = titleLayout.size.height + textLayout.size.height - 16.0 + } var applyImage: (() -> TransformImageNode)? if let imageDimensions = imageDimensions { @@ -306,7 +320,9 @@ class ChatMessageReplyInfoNode: ASDisplayNode { imageSize.width += 2.0 imageSize.height += 2.0 } - applyImage = imageNodeLayout(TransformImageArguments(corners: ImageCorners(radius: radius), imageSize: imageSize, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets(), emptyColor: placeholderColor)) + if !isExpiredStory { + applyImage = imageNodeLayout(TransformImageArguments(corners: ImageCorners(radius: radius), imageSize: imageSize, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets(), emptyColor: placeholderColor)) + } } var mediaUpdated = false @@ -341,6 +357,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { } } } + let _ = isExpiredStory let size = CGSize(width: max(titleLayout.size.width - textInsets.left - textInsets.right, textLayout.size.width - textInsets.left - textInsets.right) + leftInset, height: titleLayout.size.height + textLayout.size.height - 2 * (textInsets.top + textInsets.bottom) + 2 * spacing) @@ -380,7 +397,7 @@ class ChatMessageReplyInfoNode: ASDisplayNode { if let applyImage = applyImage { let imageNode = applyImage() if node.imageNode == nil { - imageNode.isLayerBacked = false//!smartInvertColorsEnabled() + imageNode.isLayerBacked = false node.addSubnode(imageNode) node.imageNode = imageNode } @@ -397,6 +414,32 @@ class ChatMessageReplyInfoNode: ASDisplayNode { node.imageNode?.captureProtected = message.isCopyProtected() } + if isExpiredStory { + let expiredStoryIconView: UIImageView + if let current = node.expiredStoryIconView { + expiredStoryIconView = current + } else { + expiredStoryIconView = UIImageView() + node.expiredStoryIconView = expiredStoryIconView + node.view.addSubview(expiredStoryIconView) + } + + let imageType: ChatExpiredStoryIndicatorType + switch arguments.type { + case .standalone: + imageType = .free + case let .bubble(incoming): + imageType = incoming ? .incoming : .outgoing + } + + expiredStoryIconView.image = PresentationResourcesChat.chatExpiredStoryIndicatorIcon(arguments.presentationData.theme.theme, type: imageType) + if let image = expiredStoryIconView.image { + expiredStoryIconView.frame = CGRect(origin: CGPoint(x: 8.0, y: 3.0), size: image.size) + } + } else if let expiredStoryIconView = node.expiredStoryIconView { + expiredStoryIconView.removeFromSuperview() + } + titleNode.frame = CGRect(origin: CGPoint(x: leftInset - textInsets.left - 2.0, y: spacing - textInsets.top + 1.0), size: titleLayout.size) let textFrame = CGRect(origin: CGPoint(x: leftInset - textInsets.left - 2.0, y: titleNode.frame.maxY - textInsets.bottom + spacing - textInsets.top - 2.0), size: textLayout.size) diff --git a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift index da73561f62..de5493b9f2 100644 --- a/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageStickerItemNode.swift @@ -14,6 +14,7 @@ import Markdown import ShimmerEffect import WallpaperBackgroundNode import ChatControllerInteraction +import ChatMessageForwardInfoNode private let nameFont = Font.medium(14.0) private let inlineBotPrefixFont = Font.regular(14.0) @@ -744,7 +745,7 @@ class ChatMessageStickerItemNode: ChatMessageItemView { } } let availableForwardWidth = max(60.0, availableWidth + 6.0) - forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, false, CGSize(width: availableForwardWidth, height: CGFloat.greatestFiniteMagnitude)) + forwardInfoSizeApply = makeForwardInfoLayout(item.presentationData, item.presentationData.strings, .standalone, forwardSource, forwardAuthorSignature, forwardPsaType, nil, CGSize(width: availableForwardWidth, height: CGFloat.greatestFiniteMagnitude)) } var needsReplyBackground = false diff --git a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift index d16746bc55..b1a02b4f41 100644 --- a/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift +++ b/submodules/TelegramUI/Sources/ChatMessageWebpageBubbleContentNode.swift @@ -221,7 +221,7 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { mediaAndFlags = (image, flags) } } else if let story = mainMedia as? TelegramMediaStory { - mediaAndFlags = (story, [.preferMediaBeforeText]) + mediaAndFlags = (story, []) } else if let type = webpage.type { if type == "telegram_background" { var colors: [UInt32] = [] @@ -330,6 +330,10 @@ final class ChatMessageWebpageBubbleContentNode: ChatMessageBubbleContentNode { case "telegram_chatlist": actionTitle = item.presentationData.strings.Conversation_OpenChatFolder case "telegram_story": + if let story = webpage.story, let peer = item.message.peers[story.storyId.peerId] { + title = EnginePeer(peer).displayTitle(strings: item.presentationData.strings, displayOrder: item.presentationData.nameDisplayOrder) + subtitle = nil + } actionTitle = "OPEN STORY" default: break diff --git a/submodules/TelegramUI/Sources/OpenChatMessage.swift b/submodules/TelegramUI/Sources/OpenChatMessage.swift index 9c362b03b9..29953348fe 100644 --- a/submodules/TelegramUI/Sources/OpenChatMessage.swift +++ b/submodules/TelegramUI/Sources/OpenChatMessage.swift @@ -92,9 +92,9 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool { let scale = toScale.interpolate(to: fromScale, amount: state.progress) transition.setTransform(view: view, transform: CATransform3DMakeScale(scale, scale, 1.0)) }, - insertCloneTransitionView: nil/*{ view in + insertCloneTransitionView: { view in params.addToTransitionSurface(view) - }*/ + } ), destinationRect: selectedTransitionNode.1, destinationCornerRadius: 0.0,