diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 4272b583a6..9268a2b745 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -14686,3 +14686,7 @@ Sorry for the inconvenience."; "Gift.Unique.CollectibleBy" = "collectible %1$@ by %2$@"; "SendInviteLink.TextCallsRestrictedSendOneInviteLink" = "**%@** restricts calling them. You can send them an invite link to call instead."; + +"Chat.ReplyPanel.ReplyToTodoItem" = "Reply to Checklist Item"; + +"Chat.Todo.ReplyToItem" = "Reply to Item"; diff --git a/submodules/AccountContext/Sources/Premium.swift b/submodules/AccountContext/Sources/Premium.swift index ed2b980d59..997b692306 100644 --- a/submodules/AccountContext/Sources/Premium.swift +++ b/submodules/AccountContext/Sources/Premium.swift @@ -44,6 +44,7 @@ public enum PremiumIntroSource { case messageEffects case todo case auth(String) + case premiumGift(TelegramMediaFile) } public enum PremiumGiftSource: Equatable { diff --git a/submodules/AttachmentUI/Sources/AttachmentPanel.swift b/submodules/AttachmentUI/Sources/AttachmentPanel.swift index fe9de52114..a59105ca76 100644 --- a/submodules/AttachmentUI/Sources/AttachmentPanel.swift +++ b/submodules/AttachmentUI/Sources/AttachmentPanel.swift @@ -941,7 +941,7 @@ final class AttachmentPanel: ASDisplayNode, ASScrollViewDelegate { self.mainButtonNode.addTarget(self, action: #selector(self.mainButtonPressed), forControlEvents: .touchUpInside) self.secondaryButtonNode.addTarget(self, action: #selector(self.secondaryButtonPressed), forControlEvents: .touchUpInside) - self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in + self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _, _ in }, setupEditMessage: { _, _ in }, beginMessageSelection: { _, _ in }, cancelMessageSelection: { _ in diff --git a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift index 225c91f1c5..c8afd40bea 100644 --- a/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift +++ b/submodules/ChatInterfaceState/Sources/ChatInterfaceState.swift @@ -481,16 +481,19 @@ public final class ChatInterfaceState: Codable, Equatable { public struct ReplyMessageSubject: Codable, Equatable { public var messageId: EngineMessage.Id public var quote: EngineMessageReplyQuote? + public var todoItemId: Int32? - public init(messageId: EngineMessage.Id, quote: EngineMessageReplyQuote?) { + public init(messageId: EngineMessage.Id, quote: EngineMessageReplyQuote?, todoItemId: Int32?) { self.messageId = messageId self.quote = quote + self.todoItemId = todoItemId } public var subjectModel: EngineMessageReplySubject { return EngineMessageReplySubject( messageId: self.messageId, - quote: self.quote + quote: self.quote, + todoItemId: self.todoItemId ) } } @@ -545,7 +548,8 @@ public final class ChatInterfaceState: Codable, Equatable { var result = self.withUpdatedComposeInputState(ChatTextInputState(inputText: chatInputStateStringWithAppliedEntities(state?.text ?? "", entities: state?.entities ?? []))).withUpdatedReplyMessageSubject((state?.replySubject).flatMap { return ReplyMessageSubject( messageId: $0.messageId, - quote: $0.quote + quote: $0.quote, + todoItemId: $0.todoItemId ) }) if let timestamp = state?.timestamp { @@ -629,7 +633,7 @@ public final class ChatInterfaceState: Codable, Equatable { let replyMessageIdNamespace: Int32? = try? container.decodeIfPresent(Int32.self, forKey: "r.n") let replyMessageIdId: Int32? = try? container.decodeIfPresent(Int32.self, forKey: "r.i") if let replyMessageIdPeerId = replyMessageIdPeerId, let replyMessageIdNamespace = replyMessageIdNamespace, let replyMessageIdId = replyMessageIdId { - self.replyMessageSubject = ReplyMessageSubject(messageId: EngineMessage.Id(peerId: EnginePeer.Id(replyMessageIdPeerId), namespace: replyMessageIdNamespace, id: replyMessageIdId), quote: nil) + self.replyMessageSubject = ReplyMessageSubject(messageId: EngineMessage.Id(peerId: EnginePeer.Id(replyMessageIdPeerId), namespace: replyMessageIdNamespace, id: replyMessageIdId), quote: nil, todoItemId: nil) } else { self.replyMessageSubject = nil } diff --git a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift index bc7dbc2d85..394b9e6fc9 100644 --- a/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift +++ b/submodules/ChatPresentationInterfaceState/Sources/ChatPanelInterfaceInteraction.swift @@ -69,7 +69,7 @@ public final class ChatPanelInterfaceInteraction { case editPrice } - public let setupReplyMessage: (MessageId?, @escaping (ContainedViewLayoutTransition, @escaping () -> Void) -> Void) -> Void + public let setupReplyMessage: (MessageId?, Int32?, @escaping (ContainedViewLayoutTransition, @escaping () -> Void) -> Void) -> Void public let setupEditMessage: (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void public let beginMessageSelection: ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void public let cancelMessageSelection: (ContainedViewLayoutTransition) -> Void @@ -192,7 +192,7 @@ public final class ChatPanelInterfaceInteraction { public let statuses: ChatPanelInterfaceInteractionStatuses? public init( - setupReplyMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition, @escaping () -> Void) -> Void) -> Void, + setupReplyMessage: @escaping (MessageId?, Int32?, @escaping (ContainedViewLayoutTransition, @escaping () -> Void) -> Void) -> Void, setupEditMessage: @escaping (MessageId?, @escaping (ContainedViewLayoutTransition) -> Void) -> Void, beginMessageSelection: @escaping ([MessageId], @escaping (ContainedViewLayoutTransition) -> Void) -> Void, cancelMessageSelection: @escaping (ContainedViewLayoutTransition) -> Void, @@ -443,7 +443,7 @@ public final class ChatPanelInterfaceInteraction { updateInputModeAndDismissedButtonKeyboardMessageId: @escaping ((ChatPresentationInterfaceState) -> (ChatInputMode, MessageId?)) -> Void, openLinkEditing: @escaping () -> Void ) { - self.init(setupReplyMessage: { _, _ in + self.init(setupReplyMessage: { _, _, _ in }, setupEditMessage: { _, _ in }, beginMessageSelection: { _, _ in }, cancelMessageSelection: { _ in diff --git a/submodules/ComponentFlow/Source/Base/Transition.swift b/submodules/ComponentFlow/Source/Base/Transition.swift index d196265547..d1fc259ff4 100644 --- a/submodules/ComponentFlow/Source/Base/Transition.swift +++ b/submodules/ComponentFlow/Source/Base/Transition.swift @@ -1059,6 +1059,34 @@ public struct ComponentTransition { } } + public func setShapeLayerStrokeColor(layer: CAShapeLayer, color: UIColor, completion: ((Bool) -> Void)? = nil) { + if let current = layer.strokeColor, current == color.cgColor { + completion?(true) + return + } + + switch self.animation { + case .none: + layer.strokeColor = color.cgColor + completion?(true) + case let .curve(duration, curve): + let previousColor: CGColor = layer.strokeColor ?? UIColor.clear.cgColor + layer.strokeColor = color.cgColor + + layer.animate( + from: previousColor, + to: color.cgColor, + keyPath: "strokeColor", + duration: duration, + delay: 0.0, + curve: curve, + removeOnCompletion: true, + additive: false, + completion: completion + ) + } + } + public func setShapeLayerStrokeStart(layer: CAShapeLayer, strokeStart: CGFloat, completion: ((Bool) -> Void)? = nil) { switch self.animation { case .none: diff --git a/submodules/ComposePollUI/Sources/ComposePollScreen.swift b/submodules/ComposePollUI/Sources/ComposePollScreen.swift index 5e11feb4dd..fbd309fcf7 100644 --- a/submodules/ComposePollUI/Sources/ComposePollScreen.swift +++ b/submodules/ComposePollUI/Sources/ComposePollScreen.swift @@ -288,7 +288,7 @@ final class ComposePollScreenComponent: Component { self.reorderingItem = nil for (itemId, itemView) in self.pollOptionsSectionContainer.itemViews { if itemId == reorderingItem.id, let view = itemView.contents.view { - let viewFrame = view.convert(view.bounds, to: self) + let viewFrame = view.convert(view.bounds, to: self.scrollView) let transition = ComponentTransition.spring(duration: 0.3) transition.setPosition(view: reorderingItem.snapshotView, position: viewFrame.center) transition.setAlpha(view: reorderingItem.backgroundView, alpha: 0.0, completion: { _ in @@ -311,13 +311,14 @@ final class ComposePollScreenComponent: Component { snapshotView.center = targetPosition + let localPoint = self.pollOptionsSectionContainer.convert(targetPosition, from: self.scrollView) for (itemId, itemView) in self.pollOptionsSectionContainer.itemViews { if itemId == id { continue } if let view = itemView.contents.view { - let viewFrame = view.convert(view.bounds, to: self) - if viewFrame.contains(targetPosition) { + let viewFrame = view.convert(view.bounds, to: self.pollOptionsSectionContainer) + if viewFrame.contains(localPoint) { if let targetIndex = self.pollOptions.firstIndex(where: { AnyHashable($0.id) == itemId }), let reorderingItem = self.pollOptions.first(where: { AnyHashable($0.id) == id }) { self.reorderIfPossible(item: reorderingItem, toIndex: targetIndex) } diff --git a/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift index 89f0abff51..3565513a21 100644 --- a/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift +++ b/submodules/DrawingUI/Sources/DrawingStickerEntityView.swift @@ -1346,86 +1346,6 @@ private final class StickerVideoDecoration: UniversalVideoDecoration { } } -private extension UIBezierPath { - static func smoothCurve( - through points: [CGPoint], - length: CGFloat - ) -> UIBezierPath { - let angle = (CGFloat.pi * 2) / CGFloat(points.count) - let smoothness: CGFloat = ((4 / 3) * tan(angle / 4)) / sin(angle / 2) / 2 - - var smoothPoints = [SmoothPoint]() - for index in (0 ..< points.count) { - let prevIndex = index - 1 - let prev = points[prevIndex >= 0 ? prevIndex : points.count + prevIndex] - let curr = points[index] - let next = points[(index + 1) % points.count] - - let angle: CGFloat = { - let dx = next.x - prev.x - let dy = -next.y + prev.y - let angle = atan2(dy, dx) - if angle < 0 { - return abs(angle) - } else { - return 2 * .pi - angle - } - }() - - smoothPoints.append( - SmoothPoint( - point: curr, - inAngle: angle + .pi, - inLength: smoothness * distance(from: curr, to: prev), - outAngle: angle, - outLength: smoothness * distance(from: curr, to: next) - ) - ) - } - - let resultPath = UIBezierPath() - resultPath.move(to: smoothPoints[0].point) - for index in (0 ..< smoothPoints.count) { - let curr = smoothPoints[index] - let next = smoothPoints[(index + 1) % points.count] - let currSmoothOut = curr.smoothOut() - let nextSmoothIn = next.smoothIn() - resultPath.addCurve(to: next.point, controlPoint1: currSmoothOut, controlPoint2: nextSmoothIn) - } - resultPath.close() - return resultPath - } - - static private func distance(from fromPoint: CGPoint, to toPoint: CGPoint) -> CGFloat { - return sqrt((fromPoint.x - toPoint.x) * (fromPoint.x - toPoint.x) + (fromPoint.y - toPoint.y) * (fromPoint.y - toPoint.y)) - } - - struct SmoothPoint { - let point: CGPoint - - let inAngle: CGFloat - let inLength: CGFloat - - let outAngle: CGFloat - let outLength: CGFloat - - func smoothIn() -> CGPoint { - return smooth(angle: inAngle, length: inLength) - } - - func smoothOut() -> CGPoint { - return smooth(angle: outAngle, length: outLength) - } - - private func smooth(angle: CGFloat, length: CGFloat) -> CGPoint { - return CGPoint( - x: point.x + length * cos(angle), - y: point.y + length * sin(angle) - ) - } - } -} - extension UIImageView { func setDrawingAnimatedImage(data: Data) { DispatchQueue.global().async { diff --git a/submodules/GalleryUI/BUILD b/submodules/GalleryUI/BUILD index 814b1d9c52..9e84ea0a51 100644 --- a/submodules/GalleryUI/BUILD +++ b/submodules/GalleryUI/BUILD @@ -60,7 +60,8 @@ swift_library( "//submodules/TelegramUI/Components/AnimatedTextComponent", "//submodules/TelegramUI/Components/LottieComponent", "//submodules/Components/MultilineTextComponent", - "//submodules/Components/BundleIconComponent", + "//submodules/Components/MultilineTextWithEntitiesComponent", + "//submodules/Components/BundleIconComponent", "//submodules/Components/BalancedTextComponent", "//submodules/Components/ComponentDisplayAdapters", "//submodules/ComponentFlow", diff --git a/submodules/GalleryUI/Sources/Items/VideoAdComponent.swift b/submodules/GalleryUI/Sources/Items/VideoAdComponent.swift index d563e9ae42..ea84451943 100644 --- a/submodules/GalleryUI/Sources/Items/VideoAdComponent.swift +++ b/submodules/GalleryUI/Sources/Items/VideoAdComponent.swift @@ -4,6 +4,7 @@ import Display import SwiftSignalKit import ComponentFlow import MultilineTextComponent +import MultilineTextWithEntitiesComponent import Postbox import TelegramCore import TelegramPresentationData @@ -12,6 +13,7 @@ import PlainButtonComponent import AvatarNode import AccountContext import PhotoResources +import TextFormat final class VideoAdComponent: Component { let context: AccountContext @@ -138,12 +140,23 @@ final class VideoAdComponent: Component { environment: {}, containerSize: CGSize(width: availableSize.width - leftInset - rightInset, height: availableSize.height) ) + + let textColor = UIColor.white + + var entities: [MessageTextEntity] = [] + if let attribute = component.message.attributes.first(where: { $0 is TextEntitiesMessageAttribute }) as? TextEntitiesMessageAttribute { + entities = attribute.entities + } + let attributedText = stringWithAppliedEntities(component.message.text, entities: entities, baseColor: textColor, linkColor: textColor, baseFont: Font.regular(14.0), linkFont: Font.regular(14.0), boldFont: Font.semibold(14.0), italicFont: Font.italic(14.0), boldItalicFont: Font.semiboldItalic(14.0), fixedFont: Font.monospace(14.0), blockQuoteFont: Font.regular(14.0), message: nil) let textSize = self.text.update( transition: .immediate, component: AnyComponent( - MultilineTextComponent( - text: .plain(NSAttributedString(string: component.message.text, font: Font.regular(14.0), textColor: .white)), - maximumNumberOfLines: 0 + MultilineTextWithEntitiesComponent( + context: component.context, + animationCache: component.context.animationCache, + animationRenderer: component.context.animationRenderer, + placeholderColor: UIColor.white.withAlphaComponent(0.2), + text: .plain(attributedText) ) ), environment: {}, diff --git a/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift b/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift index 59dd08228d..2cddf85876 100644 --- a/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift +++ b/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift @@ -20,6 +20,7 @@ class EmojiHeaderComponent: Component { let placeholderColor: UIColor let accentColor: UIColor let fileId: Int64 + let file: TelegramMediaFile? let isVisible: Bool let hasIdleAnimations: Bool @@ -30,6 +31,7 @@ class EmojiHeaderComponent: Component { placeholderColor: UIColor, accentColor: UIColor, fileId: Int64, + file: TelegramMediaFile? = nil, isVisible: Bool, hasIdleAnimations: Bool ) { @@ -39,6 +41,7 @@ class EmojiHeaderComponent: Component { self.placeholderColor = placeholderColor self.accentColor = accentColor self.fileId = fileId + self.file = file self.isVisible = isVisible self.hasIdleAnimations = hasIdleAnimations } @@ -156,7 +159,7 @@ class EmojiHeaderComponent: Component { animationCache: component.animationCache, animationRenderer: component.animationRenderer, content: .animation( - content: .customEmoji(fileId: component.fileId), + content: component.file.flatMap { .file(file: $0) } ?? .customEmoji(fileId: component.fileId), size: CGSize(width: 100.0, height: 100.0), placeholderColor: component.placeholderColor, themeColor: component.accentColor, @@ -170,6 +173,10 @@ class EmojiHeaderComponent: Component { ) self.statusView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - size.width) / 2.0), y: 63.0), size: size) + if let _ = component.file { + self.statusView.isHidden = false + } + return availableSize } } diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index 85599ec2aa..9f5316aa71 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -315,6 +315,12 @@ public enum PremiumSource: Equatable { } else { return false } + case let .premiumGift(lhsFile): + if case let .premiumGift(rhsFile) = rhs, lhsFile.id == rhsFile.id { + return true + } else { + return false + } } } @@ -364,6 +370,7 @@ public enum PremiumSource: Equatable { case messageEffects case todo case auth(String) + case premiumGift(TelegramMediaFile) var identifier: String? { switch self { @@ -461,6 +468,8 @@ public enum PremiumSource: Equatable { return "todo" case .auth: return "auth" + case .premiumGift: + return "premium_gift" } } } @@ -1817,7 +1826,10 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { var link = "" let textString: String - if case .emojiStatus = context.component.source { + if case .premiumGift = context.component.source { + //TODO:localize + textString = "Subscribe to **Telegram Premium** to send up to **5** of these gifts and unlock access to multiple additional features." + } else if case .emojiStatus = context.component.source { textString = strings.Premium_EmojiStatusText.replacingOccurrences(of: "#", with: "# ") } else if case .giftTerms = context.component.source { textString = strings.Premium_PersonalDescription @@ -3354,6 +3366,22 @@ private final class PremiumIntroScreenComponent: CombinedComponent { availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0), transition: context.transition ) + } else if case let .premiumGift(file) = context.component.source, case let .accountContext(accountContext) = context.component.screenContext { + header = emoji.update( + component: EmojiHeaderComponent( + context: accountContext, + animationCache: accountContext.animationCache, + animationRenderer: accountContext.animationRenderer, + placeholderColor: environment.theme.list.mediaPlaceholderColor, + accentColor: environment.theme.list.itemAccentColor, + fileId: file.fileId.id, + file: file, + isVisible: starIsVisible, + hasIdleAnimations: state.hasIdleAnimations + ), + availableSize: CGSize(width: min(414.0, context.availableSize.width), height: 220.0), + transition: context.transition + ) } else { header = star.update( component: PremiumStarComponent( @@ -3389,7 +3417,10 @@ private final class PremiumIntroScreenComponent: CombinedComponent { ) let titleString: String - if case .business = context.component.mode { + if case .premiumGift = context.component.source { + //TODO:localize + titleString = "Premium Gift" + } else if case .business = context.component.mode { titleString = environment.strings.Business_Title } else if case .emojiStatus = context.component.source { titleString = environment.strings.Premium_Title diff --git a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift index ea5f892175..3649da5e39 100644 --- a/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift +++ b/submodules/QrCodeUI/Sources/QrCodeScanScreen.swift @@ -43,41 +43,6 @@ private func parseAuthTransferUrl(_ url: URL) -> Data? { return nil } -private func generateFrameImage() -> UIImage? { - return generateImage(CGSize(width: 64.0, height: 64.0), contextGenerator: { size, context in - let bounds = CGRect(origin: CGPoint(), size: size) - context.clear(bounds) - context.setStrokeColor(UIColor.white.cgColor) - context.setLineWidth(4.0) - context.setLineCap(.round) - - let path = CGMutablePath() - path.move(to: CGPoint(x: 2.0, y: 2.0 + 26.0)) - path.addArc(tangent1End: CGPoint(x: 2.0, y: 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: 2.0), radius: 6.0) - path.addLine(to: CGPoint(x: 2.0 + 26.0, y: 2.0)) - context.addPath(path) - context.strokePath() - - path.move(to: CGPoint(x: size.width - 2.0, y: 2.0 + 26.0)) - path.addArc(tangent1End: CGPoint(x: size.width - 2.0, y: 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: 2.0), radius: 6.0) - path.addLine(to: CGPoint(x: size.width - 2.0 - 26.0, y: 2.0)) - context.addPath(path) - context.strokePath() - - path.move(to: CGPoint(x: 2.0, y: size.height - 2.0 - 26.0)) - path.addArc(tangent1End: CGPoint(x: 2.0, y: size.height - 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0), radius: 6.0) - path.addLine(to: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0)) - context.addPath(path) - context.strokePath() - - path.move(to: CGPoint(x: size.width - 2.0, y: size.height - 2.0 - 26.0)) - path.addArc(tangent1End: CGPoint(x: size.width - 2.0, y: size.height - 2.0), tangent2End: CGPoint(x: 2.0 + 26.0, y: size.height - 2.0), radius: 6.0) - path.addLine(to: CGPoint(x: size.width - 2.0 - 26.0, y: size.height - 2.0)) - context.addPath(path) - context.strokePath() - })?.stretchableImage(withLeftCapWidth: 32, topCapHeight: 32) -} - public final class QrCodeScanScreen: ViewController { public enum Subject { case authTransfer(activeSessionsContext: ActiveSessionsContext) diff --git a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift index ff0d42bcf8..d491b0a930 100644 --- a/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift +++ b/submodules/SettingsUI/Sources/BubbleSettings/BubbleSettingsController.swift @@ -172,7 +172,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, ASScrollViewDel let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) - let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" diff --git a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift index 7ccaa2c03e..86d484ae40 100644 --- a/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift +++ b/submodules/SettingsUI/Sources/Text Size/TextSizeSelectionController.swift @@ -450,7 +450,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, ASScrollView let message1 = Message(stableId: 4, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 4), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66003, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message1], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) - let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message2 = Message(stableId: 3, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 3), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66002, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(self.context.sharedContext.makeChatMessagePreviewItem(context: self.context, messages: [message2], theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, fontSize: self.presentationData.chatFontSize, chatBubbleCorners: self.presentationData.chatBubbleCorners, dateTimeFormat: self.presentationData.dateTimeFormat, nameOrder: self.presentationData.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: self.chatBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) let waveformBase64 = "DAAOAAkACQAGAAwADwAMABAADQAPABsAGAALAA0AGAAfABoAHgATABgAGQAYABQADAAVABEAHwANAA0ACQAWABkACQAOAAwACQAfAAAAGQAVAAAAEwATAAAACAAfAAAAHAAAABwAHwAAABcAGQAAABQADgAAABQAHwAAAB8AHwAAAAwADwAAAB8AEwAAABoAFwAAAB8AFAAAAAAAHwAAAAAAHgAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAHwAAAAAAAAA=" diff --git a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift index e1456944e4..12bfd759ad 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemePreviewControllerNode.swift @@ -614,7 +614,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate { messages[message4.id] = message4 sampleMessages.append(message4) - let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) messages[message5.id] = message5 sampleMessages.append(message5) @@ -625,7 +625,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, ASScrollViewDelegate { let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message6) - let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message7) let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) diff --git a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift index 0bf7aa6ffe..e464e8e82d 100644 --- a/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift +++ b/submodules/SettingsUI/Sources/Themes/ThemeSettingsChatPreviewItem.swift @@ -167,7 +167,7 @@ class ThemeSettingsChatPreviewItemNode: ListViewItemNode { messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: text, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) } - let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil, verificationIconFileId: nil) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: messageItem.outgoing ? otherPeerId : peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: messageItem.outgoing ? TelegramUser(id: otherPeerId, accessHash: nil, firstName: "", lastName: "", username: nil, phone: nil, photo: [], botInfo: nil, restrictionInfo: nil, flags: [], emojiStatus: nil, usernames: [], storiesHidden: nil, nameColor: nil, backgroundEmojiId: nil, profileColor: nil, profileBackgroundEmojiId: nil, subscriberCount: nil, verificationIconFileId: nil) : nil, text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)] : [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) } diff --git a/submodules/ShareController/Sources/ShareController.swift b/submodules/ShareController/Sources/ShareController.swift index 466f63e0c1..d8d79b653e 100644 --- a/submodules/ShareController/Sources/ShareController.swift +++ b/submodules/ShareController/Sources/ShareController.swift @@ -1997,10 +1997,10 @@ public final class ShareController: ViewController { var messages: [EnqueueMessage] = [] if !text.isEmpty { messages.append(.message(text: url + "\n\n" + text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { - EngineMessageReplySubject(messageId: $0, quote: nil) + EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) } else { - messages.append(.message(text: url, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: url, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) } messages = transformMessages(messages, showNames: showNames, silently: silently, sendPaidMessageStars: requiresStars[peerId]) shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) @@ -2033,9 +2033,9 @@ public final class ShareController: ViewController { var messages: [EnqueueMessage] = [] if !text.isEmpty { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) } - messages.append(.message(text: string, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: string, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) messages = transformMessages(messages, showNames: showNames, silently: silently, sendPaidMessageStars: requiresStars[peerId]) shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } @@ -2067,12 +2067,12 @@ public final class ShareController: ViewController { var messages: [EnqueueMessage] = [] if !text.isEmpty { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) } let attributedText = NSMutableAttributedString(string: string, attributes: [ChatTextInputAttributes.italic: true as NSNumber]) attributedText.append(NSAttributedString(string: "\n\n\(url)")) let entities = generateChatInputTextEntities(attributedText) - messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: attributedText.string, attributes: [TextEntitiesMessageAttribute(entities: entities)], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) messages = transformMessages(messages, showNames: showNames, silently: silently, sendPaidMessageStars: requiresStars[peerId]) shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } @@ -2103,7 +2103,7 @@ public final class ShareController: ViewController { } var messages: [EnqueueMessage] = [] - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: .standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: Int64.random(in: Int64.min ... Int64.max)), representations: representations.map({ $0.representation }), immediateThumbnailData: nil, reference: nil, partialReference: nil, flags: [])), threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) messages = transformMessages(messages, showNames: showNames, silently: silently, sendPaidMessageStars: requiresStars[peerId]) shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } @@ -2187,7 +2187,7 @@ public final class ShareController: ViewController { var messages: [EnqueueMessage] = [] if !text.isEmpty && !sendTextAsCaption { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) } var attributes: [MessageAttribute] = [] if let startAtTimestamp = mediaParameters?.startAtTimestamp, let startAtTimestampNode = strongSelf.controllerNode.startAtTimestampNode, startAtTimestampNode.value { @@ -2196,7 +2196,7 @@ public final class ShareController: ViewController { if let forwardSourceMessageId { messages.append(.forward(source: forwardSourceMessageId, threadId: threadId, grouping: .auto, attributes: attributes, correlationId: nil)) } else { - messages.append(.message(text: sendTextAsCaption ? text : "", attributes: attributes, inlineStickers: [:], mediaReference: mediaReference, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: sendTextAsCaption ? text : "", attributes: attributes, inlineStickers: [:], mediaReference: mediaReference, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) } messages = transformMessages(messages, showNames: showNames, silently: silently, sendPaidMessageStars: requiresStars[peerId]) shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) @@ -2229,9 +2229,9 @@ public final class ShareController: ViewController { var messages: [EnqueueMessage] = [] if !text.isEmpty { - messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) } - messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) + messages.append(.message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: media), threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])) messages = transformMessages(messages, showNames: showNames, silently: silently, sendPaidMessageStars: requiresStars[peerId]) shareSignals.append(enqueueMessages(account: currentContext.context.account, peerId: peerId, messages: messages)) } @@ -2265,7 +2265,7 @@ public final class ShareController: ViewController { let correlationId = Int64.random(in: Int64.min ... Int64.max) correlationIds.append(correlationId) - messagesToEnqueue.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: [])) + messagesToEnqueue.append(.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: threadId, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: correlationId, bubbleUpEmojiOrStickersets: [])) } for message in messages { for media in message.media { diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 1c5bc65eff..b837fa1af1 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -953,6 +953,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[520210263] = { return Api.StarGiftAttributeId.parse_starGiftAttributeIdBackdrop($0) } dict[1219145276] = { return Api.StarGiftAttributeId.parse_starGiftAttributeIdModel($0) } dict[1242965043] = { return Api.StarGiftAttributeId.parse_starGiftAttributeIdPattern($0) } + dict[-1653926992] = { return Api.StarGiftCollection.parse_starGiftCollection($0) } dict[-586389774] = { return Api.StarRefProgram.parse_starRefProgram($0) } dict[-1145654109] = { return Api.StarsAmount.parse_starsAmount($0) } dict[1957618656] = { return Api.StarsAmount.parse_starsTonAmount($0) } @@ -1431,6 +1432,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1803939105] = { return Api.payments.ResaleStarGifts.parse_resaleStarGifts($0) } dict[-74456004] = { return Api.payments.SavedInfo.parse_savedInfo($0) } dict[-1779201615] = { return Api.payments.SavedStarGifts.parse_savedStarGifts($0) } + dict[-1977011469] = { return Api.payments.StarGiftCollections.parse_starGiftCollections($0) } + dict[-1598402793] = { return Api.payments.StarGiftCollections.parse_starGiftCollectionsNotModified($0) } dict[377215243] = { return Api.payments.StarGiftUpgradePreview.parse_starGiftUpgradePreview($0) } dict[-2069218660] = { return Api.payments.StarGiftWithdrawalUrl.parse_starGiftWithdrawalUrl($0) } dict[785918357] = { return Api.payments.StarGifts.parse_starGifts($0) } @@ -2141,6 +2144,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.StarGiftAttributeId: _1.serialize(buffer, boxed) + case let _1 as Api.StarGiftCollection: + _1.serialize(buffer, boxed) case let _1 as Api.StarRefProgram: _1.serialize(buffer, boxed) case let _1 as Api.StarsAmount: @@ -2545,6 +2550,8 @@ public extension Api { _1.serialize(buffer, boxed) case let _1 as Api.payments.SavedStarGifts: _1.serialize(buffer, boxed) + case let _1 as Api.payments.StarGiftCollections: + _1.serialize(buffer, boxed) case let _1 as Api.payments.StarGiftUpgradePreview: _1.serialize(buffer, boxed) case let _1 as Api.payments.StarGiftWithdrawalUrl: diff --git a/submodules/TelegramApi/Sources/Api25.swift b/submodules/TelegramApi/Sources/Api25.swift index f686bdef05..342ffa7fa5 100644 --- a/submodules/TelegramApi/Sources/Api25.swift +++ b/submodules/TelegramApi/Sources/Api25.swift @@ -1,3 +1,61 @@ +public extension Api { + enum StarGiftCollection: TypeConstructorDescription { + case starGiftCollection(flags: Int32, collectionId: Int32, title: String, icon: Api.Document?, giftsCount: Int32, hash: Int64) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .starGiftCollection(let flags, let collectionId, let title, let icon, let giftsCount, let hash): + if boxed { + buffer.appendInt32(-1653926992) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt32(collectionId, buffer: buffer, boxed: false) + serializeString(title, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {icon!.serialize(buffer, true)} + serializeInt32(giftsCount, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .starGiftCollection(let flags, let collectionId, let title, let icon, let giftsCount, let hash): + return ("starGiftCollection", [("flags", flags as Any), ("collectionId", collectionId as Any), ("title", title as Any), ("icon", icon as Any), ("giftsCount", giftsCount as Any), ("hash", hash as Any)]) + } + } + + public static func parse_starGiftCollection(_ reader: BufferReader) -> StarGiftCollection? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int32? + _2 = reader.readInt32() + var _3: String? + _3 = parseString(reader) + var _4: Api.Document? + if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.Document + } } + var _5: Int32? + _5 = reader.readInt32() + var _6: Int64? + _6 = reader.readInt64() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.StarGiftCollection.starGiftCollection(flags: _1!, collectionId: _2!, title: _3!, icon: _4, giftsCount: _5!, hash: _6!) + } + else { + return nil + } + } + + } +} public extension Api { enum StarRefProgram: TypeConstructorDescription { case starRefProgram(flags: Int32, botId: Int64, commissionPermille: Int32, durationMonths: Int32?, endDate: Int32?, dailyRevenuePerUser: Api.StarsAmount?) @@ -1242,49 +1300,3 @@ public extension Api { } } -public extension Api { - enum StickerPack: TypeConstructorDescription { - case stickerPack(emoticon: String, documents: [Int64]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .stickerPack(let emoticon, let documents): - if boxed { - buffer.appendInt32(313694676) - } - serializeString(emoticon, buffer: buffer, boxed: false) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(documents.count)) - for item in documents { - serializeInt64(item, buffer: buffer, boxed: false) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .stickerPack(let emoticon, let documents): - return ("stickerPack", [("emoticon", emoticon as Any), ("documents", documents as Any)]) - } - } - - public static func parse_stickerPack(_ reader: BufferReader) -> StickerPack? { - var _1: String? - _1 = parseString(reader) - var _2: [Int64]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api26.swift b/submodules/TelegramApi/Sources/Api26.swift index 89231e7546..dff9d15574 100644 --- a/submodules/TelegramApi/Sources/Api26.swift +++ b/submodules/TelegramApi/Sources/Api26.swift @@ -1,3 +1,49 @@ +public extension Api { + enum StickerPack: TypeConstructorDescription { + case stickerPack(emoticon: String, documents: [Int64]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .stickerPack(let emoticon, let documents): + if boxed { + buffer.appendInt32(313694676) + } + serializeString(emoticon, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(documents.count)) + for item in documents { + serializeInt64(item, buffer: buffer, boxed: false) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .stickerPack(let emoticon, let documents): + return ("stickerPack", [("emoticon", emoticon as Any), ("documents", documents as Any)]) + } + } + + public static func parse_stickerPack(_ reader: BufferReader) -> StickerPack? { + var _1: String? + _1 = parseString(reader) + var _2: [Int64]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.StickerPack.stickerPack(emoticon: _1!, documents: _2!) + } + else { + return nil + } + } + + } +} public extension Api { enum StickerSet: TypeConstructorDescription { case stickerSet(flags: Int32, installedDate: Int32?, id: Int64, accessHash: Int64, title: String, shortName: String, thumbs: [Api.PhotoSize]?, thumbDcId: Int32?, thumbVersion: Int32?, thumbDocumentId: Int64?, count: Int32, hash: Int32) diff --git a/submodules/TelegramApi/Sources/Api36.swift b/submodules/TelegramApi/Sources/Api36.swift index b25d7db3c7..64ef1503e5 100644 --- a/submodules/TelegramApi/Sources/Api36.swift +++ b/submodules/TelegramApi/Sources/Api36.swift @@ -218,6 +218,60 @@ public extension Api.payments { } } +public extension Api.payments { + enum StarGiftCollections: TypeConstructorDescription { + case starGiftCollections(collections: [Api.StarGiftCollection]) + case starGiftCollectionsNotModified + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .starGiftCollections(let collections): + if boxed { + buffer.appendInt32(-1977011469) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(collections.count)) + for item in collections { + item.serialize(buffer, true) + } + break + case .starGiftCollectionsNotModified: + if boxed { + buffer.appendInt32(-1598402793) + } + + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .starGiftCollections(let collections): + return ("starGiftCollections", [("collections", collections as Any)]) + case .starGiftCollectionsNotModified: + return ("starGiftCollectionsNotModified", []) + } + } + + public static func parse_starGiftCollections(_ reader: BufferReader) -> StarGiftCollections? { + var _1: [Api.StarGiftCollection]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StarGiftCollection.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.payments.StarGiftCollections.starGiftCollections(collections: _1!) + } + else { + return nil + } + } + public static func parse_starGiftCollectionsNotModified(_ reader: BufferReader) -> StarGiftCollections? { + return Api.payments.StarGiftCollections.starGiftCollectionsNotModified + } + + } +} public extension Api.payments { enum StarGiftUpgradePreview: TypeConstructorDescription { case starGiftUpgradePreview(sampleAttributes: [Api.StarGiftAttribute]) @@ -1758,197 +1812,3 @@ public extension Api.stats { } } -public extension Api.stats { - enum MegagroupStats: TypeConstructorDescription { - case megagroupStats(period: Api.StatsDateRangeDays, members: Api.StatsAbsValueAndPrev, messages: Api.StatsAbsValueAndPrev, viewers: Api.StatsAbsValueAndPrev, posters: Api.StatsAbsValueAndPrev, growthGraph: Api.StatsGraph, membersGraph: Api.StatsGraph, newMembersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, messagesGraph: Api.StatsGraph, actionsGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, weekdaysGraph: Api.StatsGraph, topPosters: [Api.StatsGroupTopPoster], topAdmins: [Api.StatsGroupTopAdmin], topInviters: [Api.StatsGroupTopInviter], users: [Api.User]) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): - if boxed { - buffer.appendInt32(-276825834) - } - period.serialize(buffer, true) - members.serialize(buffer, true) - messages.serialize(buffer, true) - viewers.serialize(buffer, true) - posters.serialize(buffer, true) - growthGraph.serialize(buffer, true) - membersGraph.serialize(buffer, true) - newMembersBySourceGraph.serialize(buffer, true) - languagesGraph.serialize(buffer, true) - messagesGraph.serialize(buffer, true) - actionsGraph.serialize(buffer, true) - topHoursGraph.serialize(buffer, true) - weekdaysGraph.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topPosters.count)) - for item in topPosters { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topAdmins.count)) - for item in topAdmins { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(topInviters.count)) - for item in topInviters { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(users.count)) - for item in users { - item.serialize(buffer, true) - } - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): - return ("megagroupStats", [("period", period as Any), ("members", members as Any), ("messages", messages as Any), ("viewers", viewers as Any), ("posters", posters as Any), ("growthGraph", growthGraph as Any), ("membersGraph", membersGraph as Any), ("newMembersBySourceGraph", newMembersBySourceGraph as Any), ("languagesGraph", languagesGraph as Any), ("messagesGraph", messagesGraph as Any), ("actionsGraph", actionsGraph as Any), ("topHoursGraph", topHoursGraph as Any), ("weekdaysGraph", weekdaysGraph as Any), ("topPosters", topPosters as Any), ("topAdmins", topAdmins as Any), ("topInviters", topInviters as Any), ("users", users as Any)]) - } - } - - public static func parse_megagroupStats(_ reader: BufferReader) -> MegagroupStats? { - var _1: Api.StatsDateRangeDays? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StatsDateRangeDays - } - var _2: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _3: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _4: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _5: Api.StatsAbsValueAndPrev? - if let signature = reader.readInt32() { - _5 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev - } - var _6: Api.StatsGraph? - if let signature = reader.readInt32() { - _6 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _7: Api.StatsGraph? - if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _8: Api.StatsGraph? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _9: Api.StatsGraph? - if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _10: Api.StatsGraph? - if let signature = reader.readInt32() { - _10 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _11: Api.StatsGraph? - if let signature = reader.readInt32() { - _11 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _12: Api.StatsGraph? - if let signature = reader.readInt32() { - _12 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _13: Api.StatsGraph? - if let signature = reader.readInt32() { - _13 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _14: [Api.StatsGroupTopPoster]? - if let _ = reader.readInt32() { - _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopPoster.self) - } - var _15: [Api.StatsGroupTopAdmin]? - if let _ = reader.readInt32() { - _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopAdmin.self) - } - var _16: [Api.StatsGroupTopInviter]? - if let _ = reader.readInt32() { - _16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopInviter.self) - } - var _17: [Api.User]? - if let _ = reader.readInt32() { - _17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = _10 != nil - let _c11 = _11 != nil - let _c12 = _12 != nil - let _c13 = _13 != nil - let _c14 = _14 != nil - let _c15 = _15 != nil - let _c16 = _16 != nil - let _c17 = _17 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { - return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, weekdaysGraph: _13!, topPosters: _14!, topAdmins: _15!, topInviters: _16!, users: _17!) - } - else { - return nil - } - } - - } -} -public extension Api.stats { - enum MessageStats: TypeConstructorDescription { - case messageStats(viewsGraph: Api.StatsGraph, reactionsByEmotionGraph: Api.StatsGraph) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .messageStats(let viewsGraph, let reactionsByEmotionGraph): - if boxed { - buffer.appendInt32(2145983508) - } - viewsGraph.serialize(buffer, true) - reactionsByEmotionGraph.serialize(buffer, true) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .messageStats(let viewsGraph, let reactionsByEmotionGraph): - return ("messageStats", [("viewsGraph", viewsGraph as Any), ("reactionsByEmotionGraph", reactionsByEmotionGraph as Any)]) - } - } - - public static func parse_messageStats(_ reader: BufferReader) -> MessageStats? { - var _1: Api.StatsGraph? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - var _2: Api.StatsGraph? - if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.StatsGraph - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.stats.MessageStats.messageStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api37.swift b/submodules/TelegramApi/Sources/Api37.swift index 7e36ea42bf..62bcb3429a 100644 --- a/submodules/TelegramApi/Sources/Api37.swift +++ b/submodules/TelegramApi/Sources/Api37.swift @@ -1,3 +1,197 @@ +public extension Api.stats { + enum MegagroupStats: TypeConstructorDescription { + case megagroupStats(period: Api.StatsDateRangeDays, members: Api.StatsAbsValueAndPrev, messages: Api.StatsAbsValueAndPrev, viewers: Api.StatsAbsValueAndPrev, posters: Api.StatsAbsValueAndPrev, growthGraph: Api.StatsGraph, membersGraph: Api.StatsGraph, newMembersBySourceGraph: Api.StatsGraph, languagesGraph: Api.StatsGraph, messagesGraph: Api.StatsGraph, actionsGraph: Api.StatsGraph, topHoursGraph: Api.StatsGraph, weekdaysGraph: Api.StatsGraph, topPosters: [Api.StatsGroupTopPoster], topAdmins: [Api.StatsGroupTopAdmin], topInviters: [Api.StatsGroupTopInviter], users: [Api.User]) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): + if boxed { + buffer.appendInt32(-276825834) + } + period.serialize(buffer, true) + members.serialize(buffer, true) + messages.serialize(buffer, true) + viewers.serialize(buffer, true) + posters.serialize(buffer, true) + growthGraph.serialize(buffer, true) + membersGraph.serialize(buffer, true) + newMembersBySourceGraph.serialize(buffer, true) + languagesGraph.serialize(buffer, true) + messagesGraph.serialize(buffer, true) + actionsGraph.serialize(buffer, true) + topHoursGraph.serialize(buffer, true) + weekdaysGraph.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topPosters.count)) + for item in topPosters { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topAdmins.count)) + for item in topAdmins { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(topInviters.count)) + for item in topInviters { + item.serialize(buffer, true) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(users.count)) + for item in users { + item.serialize(buffer, true) + } + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .megagroupStats(let period, let members, let messages, let viewers, let posters, let growthGraph, let membersGraph, let newMembersBySourceGraph, let languagesGraph, let messagesGraph, let actionsGraph, let topHoursGraph, let weekdaysGraph, let topPosters, let topAdmins, let topInviters, let users): + return ("megagroupStats", [("period", period as Any), ("members", members as Any), ("messages", messages as Any), ("viewers", viewers as Any), ("posters", posters as Any), ("growthGraph", growthGraph as Any), ("membersGraph", membersGraph as Any), ("newMembersBySourceGraph", newMembersBySourceGraph as Any), ("languagesGraph", languagesGraph as Any), ("messagesGraph", messagesGraph as Any), ("actionsGraph", actionsGraph as Any), ("topHoursGraph", topHoursGraph as Any), ("weekdaysGraph", weekdaysGraph as Any), ("topPosters", topPosters as Any), ("topAdmins", topAdmins as Any), ("topInviters", topInviters as Any), ("users", users as Any)]) + } + } + + public static func parse_megagroupStats(_ reader: BufferReader) -> MegagroupStats? { + var _1: Api.StatsDateRangeDays? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StatsDateRangeDays + } + var _2: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _3: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _4: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _4 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _5: Api.StatsAbsValueAndPrev? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.StatsAbsValueAndPrev + } + var _6: Api.StatsGraph? + if let signature = reader.readInt32() { + _6 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _7: Api.StatsGraph? + if let signature = reader.readInt32() { + _7 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _8: Api.StatsGraph? + if let signature = reader.readInt32() { + _8 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _9: Api.StatsGraph? + if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _10: Api.StatsGraph? + if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _11: Api.StatsGraph? + if let signature = reader.readInt32() { + _11 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _12: Api.StatsGraph? + if let signature = reader.readInt32() { + _12 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _13: Api.StatsGraph? + if let signature = reader.readInt32() { + _13 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _14: [Api.StatsGroupTopPoster]? + if let _ = reader.readInt32() { + _14 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopPoster.self) + } + var _15: [Api.StatsGroupTopAdmin]? + if let _ = reader.readInt32() { + _15 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopAdmin.self) + } + var _16: [Api.StatsGroupTopInviter]? + if let _ = reader.readInt32() { + _16 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StatsGroupTopInviter.self) + } + var _17: [Api.User]? + if let _ = reader.readInt32() { + _17 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = _10 != nil + let _c11 = _11 != nil + let _c12 = _12 != nil + let _c13 = _13 != nil + let _c14 = _14 != nil + let _c15 = _15 != nil + let _c16 = _16 != nil + let _c17 = _17 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 { + return Api.stats.MegagroupStats.megagroupStats(period: _1!, members: _2!, messages: _3!, viewers: _4!, posters: _5!, growthGraph: _6!, membersGraph: _7!, newMembersBySourceGraph: _8!, languagesGraph: _9!, messagesGraph: _10!, actionsGraph: _11!, topHoursGraph: _12!, weekdaysGraph: _13!, topPosters: _14!, topAdmins: _15!, topInviters: _16!, users: _17!) + } + else { + return nil + } + } + + } +} +public extension Api.stats { + enum MessageStats: TypeConstructorDescription { + case messageStats(viewsGraph: Api.StatsGraph, reactionsByEmotionGraph: Api.StatsGraph) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .messageStats(let viewsGraph, let reactionsByEmotionGraph): + if boxed { + buffer.appendInt32(2145983508) + } + viewsGraph.serialize(buffer, true) + reactionsByEmotionGraph.serialize(buffer, true) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .messageStats(let viewsGraph, let reactionsByEmotionGraph): + return ("messageStats", [("viewsGraph", viewsGraph as Any), ("reactionsByEmotionGraph", reactionsByEmotionGraph as Any)]) + } + } + + public static func parse_messageStats(_ reader: BufferReader) -> MessageStats? { + var _1: Api.StatsGraph? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + var _2: Api.StatsGraph? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.StatsGraph + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.stats.MessageStats.messageStats(viewsGraph: _1!, reactionsByEmotionGraph: _2!) + } + else { + return nil + } + } + + } +} public extension Api.stats { enum PublicForwards: TypeConstructorDescription { case publicForwards(flags: Int32, count: Int32, forwards: [Api.PublicForward], nextOffset: String?, chats: [Api.Chat], users: [Api.User]) @@ -1286,59 +1480,3 @@ public extension Api.updates { } } -public extension Api.upload { - enum CdnFile: TypeConstructorDescription { - case cdnFile(bytes: Buffer) - case cdnFileReuploadNeeded(requestToken: Buffer) - - public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { - switch self { - case .cdnFile(let bytes): - if boxed { - buffer.appendInt32(-1449145777) - } - serializeBytes(bytes, buffer: buffer, boxed: false) - break - case .cdnFileReuploadNeeded(let requestToken): - if boxed { - buffer.appendInt32(-290921362) - } - serializeBytes(requestToken, buffer: buffer, boxed: false) - break - } - } - - public func descriptionFields() -> (String, [(String, Any)]) { - switch self { - case .cdnFile(let bytes): - return ("cdnFile", [("bytes", bytes as Any)]) - case .cdnFileReuploadNeeded(let requestToken): - return ("cdnFileReuploadNeeded", [("requestToken", requestToken as Any)]) - } - } - - public static func parse_cdnFile(_ reader: BufferReader) -> CdnFile? { - var _1: Buffer? - _1 = parseBytes(reader) - let _c1 = _1 != nil - if _c1 { - return Api.upload.CdnFile.cdnFile(bytes: _1!) - } - else { - return nil - } - } - public static func parse_cdnFileReuploadNeeded(_ reader: BufferReader) -> CdnFile? { - var _1: Buffer? - _1 = parseBytes(reader) - let _c1 = _1 != nil - if _c1 { - return Api.upload.CdnFile.cdnFileReuploadNeeded(requestToken: _1!) - } - else { - return nil - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api38.swift b/submodules/TelegramApi/Sources/Api38.swift index df11f101cb..e76c07649a 100644 --- a/submodules/TelegramApi/Sources/Api38.swift +++ b/submodules/TelegramApi/Sources/Api38.swift @@ -1,3 +1,59 @@ +public extension Api.upload { + enum CdnFile: TypeConstructorDescription { + case cdnFile(bytes: Buffer) + case cdnFileReuploadNeeded(requestToken: Buffer) + + public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .cdnFile(let bytes): + if boxed { + buffer.appendInt32(-1449145777) + } + serializeBytes(bytes, buffer: buffer, boxed: false) + break + case .cdnFileReuploadNeeded(let requestToken): + if boxed { + buffer.appendInt32(-290921362) + } + serializeBytes(requestToken, buffer: buffer, boxed: false) + break + } + } + + public func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .cdnFile(let bytes): + return ("cdnFile", [("bytes", bytes as Any)]) + case .cdnFileReuploadNeeded(let requestToken): + return ("cdnFileReuploadNeeded", [("requestToken", requestToken as Any)]) + } + } + + public static func parse_cdnFile(_ reader: BufferReader) -> CdnFile? { + var _1: Buffer? + _1 = parseBytes(reader) + let _c1 = _1 != nil + if _c1 { + return Api.upload.CdnFile.cdnFile(bytes: _1!) + } + else { + return nil + } + } + public static func parse_cdnFileReuploadNeeded(_ reader: BufferReader) -> CdnFile? { + var _1: Buffer? + _1 = parseBytes(reader) + let _c1 = _1 != nil + if _c1 { + return Api.upload.CdnFile.cdnFileReuploadNeeded(requestToken: _1!) + } + else { + return nil + } + } + + } +} public extension Api.upload { enum File: TypeConstructorDescription { case file(type: Api.storage.FileType, mtime: Int32, bytes: Buffer) diff --git a/submodules/TelegramApi/Sources/Api39.swift b/submodules/TelegramApi/Sources/Api39.swift index 7bd3b42275..43e04f36f2 100644 --- a/submodules/TelegramApi/Sources/Api39.swift +++ b/submodules/TelegramApi/Sources/Api39.swift @@ -9326,6 +9326,43 @@ public extension Api.functions.payments { }) } } +public extension Api.functions.payments { + static func createStarGiftCollection(peer: Api.InputPeer, title: String, stargift: [Api.InputSavedStarGift]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(524947079) + peer.serialize(buffer, true) + serializeString(title, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(stargift.count)) + for item in stargift { + item.serialize(buffer, true) + } + return (FunctionDescription(name: "payments.createStarGiftCollection", parameters: [("peer", String(describing: peer)), ("title", String(describing: title)), ("stargift", String(describing: stargift))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StarGiftCollection? in + let reader = BufferReader(buffer) + var result: Api.StarGiftCollection? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.StarGiftCollection + } + return result + }) + } +} +public extension Api.functions.payments { + static func deleteStarGiftCollection(peer: Api.InputPeer, collectionId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1386854168) + peer.serialize(buffer, true) + serializeInt32(collectionId, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.deleteStarGiftCollection", parameters: [("peer", String(describing: peer)), ("collectionId", String(describing: collectionId))]), 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.payments { static func editConnectedStarRefBot(flags: Int32, peer: Api.InputPeer, link: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -9548,14 +9585,15 @@ public extension Api.functions.payments { } } public extension Api.functions.payments { - static func getSavedStarGifts(flags: Int32, peer: Api.InputPeer, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + static func getSavedStarGifts(flags: Int32, peer: Api.InputPeer, collectionId: Int32?, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() - buffer.appendInt32(595791337) + buffer.appendInt32(-1558583959) serializeInt32(flags, buffer: buffer, boxed: false) peer.serialize(buffer, true) + if Int(flags) & Int(1 << 6) != 0 {serializeInt32(collectionId!, buffer: buffer, boxed: false)} serializeString(offset, buffer: buffer, boxed: false) serializeInt32(limit, buffer: buffer, boxed: false) - return (FunctionDescription(name: "payments.getSavedStarGifts", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.SavedStarGifts? in + return (FunctionDescription(name: "payments.getSavedStarGifts", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("collectionId", String(describing: collectionId)), ("offset", String(describing: offset)), ("limit", String(describing: limit))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.SavedStarGifts? in let reader = BufferReader(buffer) var result: Api.payments.SavedStarGifts? if let signature = reader.readInt32() { @@ -9565,6 +9603,22 @@ public extension Api.functions.payments { }) } } +public extension Api.functions.payments { + static func getStarGiftCollections(peer: Api.InputPeer, hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1743023651) + peer.serialize(buffer, true) + serializeInt64(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "payments.getStarGiftCollections", parameters: [("peer", String(describing: peer)), ("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.payments.StarGiftCollections? in + let reader = BufferReader(buffer) + var result: Api.payments.StarGiftCollections? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.payments.StarGiftCollections + } + return result + }) + } +} public extension Api.functions.payments { static func getStarGiftUpgradePreview(giftId: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -9845,6 +9899,26 @@ public extension Api.functions.payments { }) } } +public extension Api.functions.payments { + static func reorderStarGiftCollections(peer: Api.InputPeer, order: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-1020594996) + peer.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order.count)) + for item in order { + serializeInt32(item, buffer: buffer, boxed: false) + } + return (FunctionDescription(name: "payments.reorderStarGiftCollections", parameters: [("peer", String(describing: peer)), ("order", String(describing: order))]), 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.payments { static func saveStarGift(flags: Int32, stargift: Api.InputSavedStarGift) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() @@ -9950,6 +10024,39 @@ public extension Api.functions.payments { }) } } +public extension Api.functions.payments { + static func updateStarGiftCollection(flags: Int32, peer: Api.InputPeer, collectionId: Int32, title: String?, deleteStargift: [Api.InputSavedStarGift]?, addStargift: [Api.InputSavedStarGift]?, order: [Api.InputSavedStarGift]?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1339932391) + serializeInt32(flags, buffer: buffer, boxed: false) + peer.serialize(buffer, true) + serializeInt32(collectionId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(deleteStargift!.count)) + for item in deleteStargift! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 2) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(addStargift!.count)) + for item in addStargift! { + item.serialize(buffer, true) + }} + if Int(flags) & Int(1 << 3) != 0 {buffer.appendInt32(481674261) + buffer.appendInt32(Int32(order!.count)) + for item in order! { + item.serialize(buffer, true) + }} + return (FunctionDescription(name: "payments.updateStarGiftCollection", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("collectionId", String(describing: collectionId)), ("title", String(describing: title)), ("deleteStargift", String(describing: deleteStargift)), ("addStargift", String(describing: addStargift)), ("order", String(describing: order))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StarGiftCollection? in + let reader = BufferReader(buffer) + var result: Api.StarGiftCollection? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.StarGiftCollection + } + return result + }) + } +} public extension Api.functions.payments { static func updateStarGiftPrice(stargift: Api.InputSavedStarGift, resellStars: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { let buffer = Buffer() diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index a57286aa8f..7cdd5ca5a6 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -284,13 +284,14 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: Refere let peerId: PeerId = chatPeerId.peerId switch replyTo { - case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset, _): + case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset, todoItemId): let _ = replyHeader let _ = replyMedia let _ = replyToTopId let _ = quoteText let _ = quoteEntities let _ = quoteOffset + let _ = todoItemId if let replyToMsgId = replyToMsgId { let targetId = MessageId(peerId: replyToPeerId?.peerId ?? peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId) @@ -307,13 +308,14 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> (replyIds: Refere case let .messageService(_, id, _, chatPeerId, _, replyHeader, _, _, _, _): if let replyHeader = replyHeader { switch replyHeader { - case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset, _): + case let .messageReplyHeader(_, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset, todoItemId): let _ = replyHeader let _ = replyMedia let _ = replyToTopId let _ = quoteText let _ = quoteEntities let _ = quoteOffset + let _ = todoItemId if let replyToMsgId = replyToMsgId { let targetId = MessageId(peerId: replyToPeerId?.peerId ?? chatPeerId.peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId) @@ -723,7 +725,7 @@ extension StoreMessage { if let replyTo = replyTo { var threadMessageId: MessageId? switch replyTo { - case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset, _): + case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset, todoItemId): let isForumTopic = (innerFlags & (1 << 3)) != 0 var quote: EngineMessageReplyQuote? @@ -766,7 +768,7 @@ extension StoreMessage { threadId = Int64(threadIdValue.id) } } - attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: threadMessageId, quote: quote, isQuote: isQuote)) + attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: threadMessageId, quote: quote, isQuote: isQuote, todoItemId: todoItemId)) } if let replyHeader = replyHeader { attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote)) @@ -1084,7 +1086,7 @@ extension StoreMessage { if chatPeerId.peerId.namespace == Namespaces.Peer.CloudChannel, let replyTo { switch replyTo { - case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, _, quoteText, quoteEntities, quoteOffset, _): + case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, _, quoteText, quoteEntities, quoteOffset, todoItemId): var quote: EngineMessageReplyQuote? let isQuote = (innerFlags & (1 << 9)) != 0 if quoteText != nil || replyMedia != nil { @@ -1093,7 +1095,7 @@ extension StoreMessage { if let replyToMsgId = replyToMsgId { let replyPeerId = replyToPeerId?.peerId ?? peerId - attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: nil, quote: quote, isQuote: isQuote)) + attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: nil, quote: quote, isQuote: isQuote, todoItemId: todoItemId)) } else if let replyHeader = replyHeader { attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote)) } @@ -1104,7 +1106,7 @@ extension StoreMessage { } else if let replyTo = replyTo { var threadMessageId: MessageId? switch replyTo { - case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset, _): + case let .messageReplyHeader(innerFlags, replyToMsgId, replyToPeerId, replyHeader, replyMedia, replyToTopId, quoteText, quoteEntities, quoteOffset, todoItemId): var quote: EngineMessageReplyQuote? let isQuote = (innerFlags & (1 << 9)) != 0 if quoteText != nil || replyMedia != nil { @@ -1130,7 +1132,7 @@ extension StoreMessage { default: break } - attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: threadMessageId, quote: quote, isQuote: isQuote)) + attributes.append(ReplyMessageAttribute(messageId: MessageId(peerId: replyPeerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), threadMessageId: threadMessageId, quote: quote, isQuote: isQuote, todoItemId: todoItemId)) } else if let replyHeader = replyHeader { attributes.append(QuotedReplyMessageAttribute(apiHeader: replyHeader, quote: quote, isQuote: isQuote)) } diff --git a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift index 20ae224114..c33c4e39f8 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/EnqueueMessage.swift @@ -82,10 +82,12 @@ public struct EngineMessageReplyQuote: Codable, Equatable { public struct EngineMessageReplySubject: Codable, Equatable { public var messageId: EngineMessage.Id public var quote: EngineMessageReplyQuote? + public var todoItemId: Int32? - public init(messageId: EngineMessage.Id, quote: EngineMessageReplyQuote?) { + public init(messageId: EngineMessage.Id, quote: EngineMessageReplyQuote?, todoItemId: Int32?) { self.messageId = messageId self.quote = quote + self.todoItemId = todoItemId } } @@ -385,7 +387,7 @@ public func enqueueMessagesToMultiplePeers(account: Account, peerIds: [PeerId], for peerId in peerIds { var replyToMessageId: EngineMessageReplySubject? if let threadIds = threadIds[peerId] { - replyToMessageId = EngineMessageReplySubject(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadIds)), quote: nil) + replyToMessageId = EngineMessageReplySubject(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadIds)), quote: nil, todoItemId: nil) } var messages = messages if let replyToMessageId = replyToMessageId { @@ -431,7 +433,7 @@ public func resendMessages(account: Account, messageIds: [MessageId]) -> Signal< var forwardSource: MessageId? inner: for attribute in message.attributes { if let attribute = attribute as? ReplyMessageAttribute { - replyToMessageId = EngineMessageReplySubject(messageId: attribute.messageId, quote: attribute.quote) + replyToMessageId = EngineMessageReplySubject(messageId: attribute.messageId, quote: attribute.quote, todoItemId: attribute.todoItemId) } else if let attribute = attribute as? ReplyStoryAttribute { replyToStoryId = attribute.storyId } else if let attribute = attribute as? OutgoingMessageInfoAttribute { @@ -526,7 +528,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, mediaReference = .standalone(media: media) } } - updatedMessages.append((transformedMedia, .message(text: sourceMessage.text, attributes: sourceMessage.attributes, inlineStickers: [:], mediaReference: mediaReference, threadId: threadId, replyToMessageId: threadId.flatMap { EngineMessageReplySubject(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: $0)), quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))) + updatedMessages.append((transformedMedia, .message(text: sourceMessage.text, attributes: sourceMessage.attributes, inlineStickers: [:], mediaReference: mediaReference, threadId: threadId, replyToMessageId: threadId.flatMap { EngineMessageReplySubject(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: $0)), quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: []))) continue outer } } @@ -659,7 +661,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, quote = EngineMessageReplyQuote(text: replyMessage.text, offset: nil, entities: messageTextEntitiesInRange(entities: replyMessage.textEntitiesAttribute?.entities ?? [], range: NSRange(location: 0, length: nsText.length), onlyQuoteable: true), media: replyMedia) } } - attributes.append(ReplyMessageAttribute(messageId: replyToMessageId.messageId, threadMessageId: threadMessageId, quote: quote, isQuote: isQuote)) + attributes.append(ReplyMessageAttribute(messageId: replyToMessageId.messageId, threadMessageId: threadMessageId, quote: quote, isQuote: isQuote, todoItemId: replyToMessageId.todoItemId)) } if let replyToStoryId = replyToStoryId { attributes.append(ReplyStoryAttribute(storyId: replyToStoryId)) diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index f4639fcf53..f1c8a7855a 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -171,7 +171,7 @@ public func standaloneSendEnqueueMessages( } if let replyToMessageId = message.replyToMessageId { - attributes.append(ReplyMessageAttribute(messageId: replyToMessageId, threadMessageId: nil, quote: nil, isQuote: false)) + attributes.append(ReplyMessageAttribute(messageId: replyToMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)) } if let forwardOptions = message.forwardOptions { attributes.append(ForwardOptionsMessageAttribute(hideNames: forwardOptions.hideNames, hideCaptions: forwardOptions.hideCaptions)) @@ -338,6 +338,7 @@ private func sendUploadedMessageContent( } } var replyToStoryId: StoryId? + var replyTodoItemId: Int32? var scheduleTime: Int32? var videoTimestamp: Int32? var sendAsPeerId: PeerId? @@ -350,6 +351,7 @@ private func sendUploadedMessageContent( for attribute in attributes { if let replyAttribute = attribute as? ReplyMessageAttribute { replyMessageId = replyAttribute.messageId.id + replyTodoItemId = replyAttribute.todoItemId } else if let attribute = attribute as? ReplyStoryAttribute { replyToStoryId = attribute.storyId } else if let outgoingInfo = attribute as? OutgoingMessageInfoAttribute { @@ -435,7 +437,10 @@ private func sendUploadedMessageContent( if monoforumPeerId != nil { replyFlags |= 1 << 5 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId, todoItemId: nil) + if let _ = replyTodoItemId { + replyFlags |= 1 << 6 + } + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId, todoItemId: replyTodoItemId) } else if let replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 @@ -466,7 +471,10 @@ private func sendUploadedMessageContent( if monoforumPeerId != nil { replyFlags |= 1 << 5 } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId, todoItemId: nil) + if let _ = replyTodoItemId { + replyFlags |= 1 << 6 + } + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: monoforumPeerId, todoItemId: replyTodoItemId) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 @@ -693,7 +701,7 @@ private func sendMessageContent(account: Account, peerId: PeerId, attributes: [M replyTo = .inputReplyToMessage(flags: flags, replyToMsgId: threadId, topMsgId: threadId, replyToPeerId: nil, quoteText: nil, quoteEntities: nil, quoteOffset: nil, monoforumPeerId: nil, todoItemId: nil) } - sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: nil)) + sendMessageRequest = account.network.request(Api.functions.messages.sendMessage(flags: flags, peer: inputPeer, replyTo: replyTo, message: text, randomId: uniqueId, replyMarkup: nil, entities: messageEntities, scheduleDate: scheduleTime, sendAs: sendAsInputPeer, quickReplyShortcut: nil, effect: nil, allowPaidStars: allowPaidStars, suggestedPost: nil)) |> `catch` { _ -> Signal in return .complete() } diff --git a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift index 66996c5ca0..ad6c54879f 100644 --- a/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift +++ b/submodules/TelegramCore/Sources/State/AccountStateManagementUtils.swift @@ -1595,7 +1595,7 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: } if let replyToMsgHeader { switch replyToMsgHeader { - case let .inputReplyToMessage(_, replyToMsgId, topMsgId, replyToPeerId, quoteText, quoteEntities, quoteOffset, monoforumPeerId, _): + case let .inputReplyToMessage(_, replyToMsgId, topMsgId, replyToPeerId, quoteText, quoteEntities, quoteOffset, monoforumPeerId, todoItemId): let _ = topMsgId let _ = monoforumPeerId @@ -1631,7 +1631,8 @@ private func finalStateWithUpdatesAndServerTime(accountPeerId: PeerId, postbox: replySubject = EngineMessageReplySubject( messageId: MessageId(peerId: parsedReplyToPeerId ?? peer.peerId, namespace: Namespaces.Message.Cloud, id: replyToMsgId), - quote: quote + quote: quote, + todoItemId: todoItemId ) case .inputReplyToStory: break diff --git a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift index 284b50dae7..46ea7677de 100644 --- a/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift +++ b/submodules/TelegramCore/Sources/State/ManagedSynchronizeChatInputStateOperations.swift @@ -197,6 +197,8 @@ private func synchronizeChatInputState(transaction: Transaction, postbox: Postbo } } + let replyTodoItemId = replySubject.todoItemId + if replyToPeer != nil { innerFlags |= 1 << 1 } @@ -209,9 +211,11 @@ private func synchronizeChatInputState(transaction: Transaction, postbox: Postbo if quoteOffset != nil { innerFlags |= 1 << 4 } - + if let _ = replyTodoItemId { + innerFlags |= 1 << 6 + } if !discard { - replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: replySubject.messageId.id, topMsgId: topMsgId, replyToPeerId: replyToPeer, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: nil) + replyTo = .inputReplyToMessage(flags: innerFlags, replyToMsgId: replySubject.messageId.id, topMsgId: topMsgId, replyToPeerId: replyToPeer, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: replyTodoItemId) } } else if let topMsgId { flags |= 1 << 0 diff --git a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift index bf6e560a32..81fc98af0a 100644 --- a/submodules/TelegramCore/Sources/State/PendingMessageManager.swift +++ b/submodules/TelegramCore/Sources/State/PendingMessageManager.swift @@ -854,6 +854,7 @@ public final class PendingMessageManager { var replyPeerId: PeerId? var replyQuote: EngineMessageReplyQuote? var replyToStoryId: StoryId? + var replyTodoItemId: Int32? var scheduleTime: Int32? var videoTimestamp: Int32? var sendAsPeerId: PeerId? @@ -873,6 +874,7 @@ public final class PendingMessageManager { if replyAttribute.isQuote { replyQuote = replyAttribute.quote } + replyTodoItemId = replyAttribute.todoItemId } else if let attribute = attribute as? ReplyStoryAttribute { replyToStoryId = attribute.storyId } else if let _ = attribute as? ForwardSourceInfoAttribute { @@ -1108,7 +1110,11 @@ public final class PendingMessageManager { } } - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: nil) + if let _ = replyTodoItemId { + replyFlags |= 1 << 6 + } + + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: replyTodoItemId) } else if let replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 @@ -1349,6 +1355,7 @@ public final class PendingMessageManager { var replyPeerId: PeerId? var replyQuote: EngineMessageReplyQuote? var replyToStoryId: StoryId? + var replyTodoItemId: Int32? var scheduleTime: Int32? var videoTimestamp: Int32? var sendAsPeerId: PeerId? @@ -1381,6 +1388,7 @@ public final class PendingMessageManager { if replyAttribute.isQuote { replyQuote = replyAttribute.quote } + replyTodoItemId = replyAttribute.todoItemId } else if let attribute = attribute as? ReplyStoryAttribute { replyToStoryId = attribute.storyId } else if let outgoingInfo = attribute as? OutgoingMessageInfoAttribute { @@ -1490,9 +1498,10 @@ public final class PendingMessageManager { } } - - - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: nil) + if let _ = replyTodoItemId { + replyFlags |= 1 << 6 + } + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: replyTodoItemId) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 @@ -1586,8 +1595,11 @@ public final class PendingMessageManager { quoteEntities = apiEntitiesFromMessageTextEntities(replyQuote.entities, associatedPeers: associatedPeers) } } - - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: nil) + + if let _ = replyTodoItemId { + replyFlags |= 1 << 6 + } + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: topMsgId, replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: replyTodoItemId) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 @@ -1731,8 +1743,11 @@ public final class PendingMessageManager { quoteEntities = apiEntitiesFromMessageTextEntities(replyQuote.entities, associatedPeers: associatedPeers) } } - - replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: nil) + + if let _ = replyTodoItemId { + replyFlags |= 1 << 6 + } + replyTo = .inputReplyToMessage(flags: replyFlags, replyToMsgId: replyMessageId, topMsgId: message.threadId.flatMap(Int32.init(clamping:)), replyToPeerId: replyToPeerId, quoteText: quoteText, quoteEntities: quoteEntities, quoteOffset: quoteOffset, monoforumPeerId: monoforumPeerId, todoItemId: replyTodoItemId) } else if let replyToStoryId = replyToStoryId { if let inputPeer = transaction.getPeer(replyToStoryId.peerId).flatMap(apiInputPeer) { flags |= 1 << 0 diff --git a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift index 5d955da2ff..a0a260b165 100644 --- a/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift +++ b/submodules/TelegramCore/Sources/State/ProcessSecretChatIncomingDecryptedOperations.swift @@ -881,7 +881,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) { - attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)) + attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)) } var entitiesAttribute: TextEntitiesMessageAttribute? @@ -1113,7 +1113,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) { - attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)) + attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)) } var entitiesAttribute: TextEntitiesMessageAttribute? @@ -1392,7 +1392,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) { - attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)) + attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)) } var entitiesAttribute: TextEntitiesMessageAttribute? @@ -1593,7 +1593,7 @@ private func parseMessage(peerId: PeerId, authorId: PeerId, tagLocalIndex: Int32 } if let replyToRandomId = replyToRandomId, let replyMessageId = messageIdForGloballyUniqueMessageId(replyToRandomId) { - attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)) + attributes.append(ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)) } var entitiesAttribute: TextEntitiesMessageAttribute? diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift index d3450a6f61..722bff1e4e 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_Namespaces.swift @@ -144,6 +144,7 @@ public struct Namespaces { public static let channelsForPublicReaction: Int8 = 45 public static let cachedGroupsInCommon: Int8 = 46 public static let groupCallPersistentSettings: Int8 = 47 + public static let cachedProfileGiftsCollections: Int8 = 48 } public struct UnorderedItemList { diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift index a0cad0ee13..296320e969 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_ReplyMessageAttribute.swift @@ -7,16 +7,18 @@ public class ReplyMessageAttribute: MessageAttribute { public let threadMessageId: MessageId? public let quote: EngineMessageReplyQuote? public let isQuote: Bool + public let todoItemId: Int32? public var associatedMessageIds: [MessageId] { return [self.messageId] } - public init(messageId: MessageId, threadMessageId: MessageId?, quote: EngineMessageReplyQuote?, isQuote: Bool) { + public init(messageId: MessageId, threadMessageId: MessageId?, quote: EngineMessageReplyQuote?, isQuote: Bool, todoItemId: Int32?) { self.messageId = messageId self.threadMessageId = threadMessageId self.quote = quote self.isQuote = isQuote + self.todoItemId = todoItemId } required public init(decoder: PostboxDecoder) { @@ -31,6 +33,7 @@ public class ReplyMessageAttribute: MessageAttribute { self.quote = decoder.decodeCodable(EngineMessageReplyQuote.self, forKey: "qu") self.isQuote = decoder.decodeBoolForKey("iq", orElse: self.quote != nil) + self.todoItemId = decoder.decodeOptionalInt32ForKey("tid") } public func encode(_ encoder: PostboxEncoder) { @@ -48,6 +51,11 @@ public class ReplyMessageAttribute: MessageAttribute { encoder.encodeNil(forKey: "qu") } encoder.encodeBool(self.isQuote, forKey: "iq") + if let todoItemId = self.todoItemId { + encoder.encodeInt32(todoItemId, forKey: "tid") + } else { + encoder.encodeNil(forKey: "tid") + } } } diff --git a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift index cd2ab46054..c514083406 100644 --- a/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift +++ b/submodules/TelegramCore/Sources/SyncCore/SyncCore_SynchronizeableChatInputState.swift @@ -40,7 +40,7 @@ public struct SynchronizeableChatInputState: Codable, Equatable { self.replySubject = replySubject } else { if let messageIdPeerId = try? container.decodeIfPresent(Int64.self, forKey: "m.p"), let messageIdNamespace = try? container.decodeIfPresent(Int32.self, forKey: "m.n"), let messageIdId = try? container.decodeIfPresent(Int32.self, forKey: "m.i") { - self.replySubject = EngineMessageReplySubject(messageId: MessageId(peerId: PeerId(messageIdPeerId), namespace: messageIdNamespace, id: messageIdId), quote: nil) + self.replySubject = EngineMessageReplySubject(messageId: MessageId(peerId: PeerId(messageIdPeerId), namespace: messageIdNamespace, id: messageIdId), quote: nil, todoItemId: nil) } else { self.replySubject = nil } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/OutgoingMessageWithChatContextResult.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/OutgoingMessageWithChatContextResult.swift index 3302c7fafd..fe290487dd 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/OutgoingMessageWithChatContextResult.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/OutgoingMessageWithChatContextResult.swift @@ -13,7 +13,7 @@ func _internal_enqueueOutgoingMessageWithChatContextResult(account: Account, to func _internal_outgoingMessageWithChatContextResult(to peerId: PeerId, threadId: Int64?, botId: PeerId, result: ChatContextResult, replyToMessageId: EngineMessageReplySubject?, replyToStoryId: StoryId?, hideVia: Bool, silentPosting: Bool, scheduleTime: Int32?, sendPaidMessageStars: StarsAmount?, postpone: Bool, correlationId: Int64?) -> EnqueueMessage? { var replyToMessageId = replyToMessageId if replyToMessageId == nil, let threadId = threadId { - replyToMessageId = EngineMessageReplySubject(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: MessageId.Id(clamping: threadId)), quote: nil) + replyToMessageId = EngineMessageReplySubject(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: MessageId.Id(clamping: threadId)), quote: nil, todoItemId: nil) } var webpageUrl: String? diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index c94e4f0145..07ccc2de08 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -42,6 +42,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } public static let isBirthdayGift = Flags(rawValue: 1 << 0) + public static let requiresPremium = Flags(rawValue: 1 << 1) } enum CodingKeys: String, CodingKey { @@ -55,6 +56,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { case flags case upgradeStars case releasedBy + case perUserLimit } public struct Availability: Equatable, Codable, PostboxCoding { @@ -137,6 +139,31 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } } + public struct PerUserLimit: Equatable, Codable, PostboxCoding { + enum CodingKeys: String, CodingKey { + case total + case remains + } + + public let total: Int32 + public let remains: Int32 + + public init(total: Int32, remains: Int32) { + self.total = total + self.remains = remains + } + + public init(decoder: PostboxDecoder) { + self.total = decoder.decodeInt32ForKey(CodingKeys.total.rawValue, orElse: 0) + self.remains = decoder.decodeInt32ForKey(CodingKeys.remains.rawValue, orElse: 0) + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.total, forKey: CodingKeys.total.rawValue) + encoder.encodeInt32(self.remains, forKey: CodingKeys.remains.rawValue) + } + } + public enum DecodingError: Error { case generic } @@ -151,8 +178,9 @@ public enum StarGift: Equatable, Codable, PostboxCoding { public let flags: Flags public let upgradeStars: Int64? public let releasedBy: EnginePeer.Id? + public let perUserLimit: PerUserLimit? - public init(id: Int64, title: String?, file: TelegramMediaFile, price: Int64, convertStars: Int64, availability: Availability?, soldOut: SoldOut?, flags: Flags, upgradeStars: Int64?, releasedBy: EnginePeer.Id?) { + public init(id: Int64, title: String?, file: TelegramMediaFile, price: Int64, convertStars: Int64, availability: Availability?, soldOut: SoldOut?, flags: Flags, upgradeStars: Int64?, releasedBy: EnginePeer.Id?, perUserLimit: PerUserLimit?) { self.id = id self.title = title self.file = file @@ -163,6 +191,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.flags = flags self.upgradeStars = upgradeStars self.releasedBy = releasedBy + self.perUserLimit = perUserLimit } public init(from decoder: Decoder) throws { @@ -183,6 +212,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.flags = Flags(rawValue: try container .decodeIfPresent(Int32.self, forKey: .flags) ?? 0) self.upgradeStars = try container.decodeIfPresent(Int64.self, forKey: .upgradeStars) self.releasedBy = try container.decodeIfPresent(EnginePeer.Id.self, forKey: .releasedBy) + self.perUserLimit = try container.decodeIfPresent(PerUserLimit.self, forKey: .perUserLimit) } public init(decoder: PostboxDecoder) { @@ -196,6 +226,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { self.flags = Flags(rawValue: decoder.decodeInt32ForKey(CodingKeys.flags.rawValue, orElse: 0)) self.upgradeStars = decoder.decodeOptionalInt64ForKey(CodingKeys.upgradeStars.rawValue) self.releasedBy = decoder.decodeOptionalInt64ForKey(CodingKeys.releasedBy.rawValue).flatMap { EnginePeer.Id($0) } + self.perUserLimit = decoder.decodeObjectForKey(CodingKeys.perUserLimit.rawValue, decoder: { StarGift.Gift.PerUserLimit(decoder: $0) }) as? StarGift.Gift.PerUserLimit } public func encode(to encoder: Encoder) throws { @@ -215,6 +246,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding { try container.encode(self.flags.rawValue, forKey: .flags) try container.encodeIfPresent(self.upgradeStars, forKey: .upgradeStars) try container.encodeIfPresent(self.releasedBy, forKey: .releasedBy) + try container.encodeIfPresent(self.perUserLimit, forKey: .perUserLimit) } public func encode(_ encoder: PostboxEncoder) { @@ -248,6 +280,11 @@ public enum StarGift: Equatable, Codable, PostboxCoding { } else { encoder.encodeNil(forKey: CodingKeys.releasedBy.rawValue) } + if let perUserLimit = self.perUserLimit { + encoder.encodeObject(perUserLimit, forKey: CodingKeys.perUserLimit.rawValue) + } else { + encoder.encodeNil(forKey: CodingKeys.perUserLimit.rawValue) + } } } @@ -720,11 +757,14 @@ public extension StarGift { extension StarGift { init?(apiStarGift: Api.StarGift) { switch apiStarGift { - case let .starGift(apiFlags, id, sticker, stars, availabilityRemains, availabilityTotal, availabilityResale, convertStars, firstSale, lastSale, upgradeStars, minResaleStars, title, releasedBy, _, _): + case let .starGift(apiFlags, id, sticker, stars, availabilityRemains, availabilityTotal, availabilityResale, convertStars, firstSale, lastSale, upgradeStars, minResaleStars, title, releasedBy, perUserTotal, perUserRemains): var flags = StarGift.Gift.Flags() if (apiFlags & (1 << 2)) != 0 { flags.insert(.isBirthdayGift) } + if (apiFlags & (1 << 7)) != 0 { + flags.insert(.requiresPremium) + } var availability: StarGift.Gift.Availability? if let availabilityRemains, let availabilityTotal { @@ -739,10 +779,14 @@ extension StarGift { if let firstSale, let lastSale { soldOut = StarGift.Gift.SoldOut(firstSale: firstSale, lastSale: lastSale) } + var perUserLimit: StarGift.Gift.PerUserLimit? + if let perUserTotal, let perUserRemains { + perUserLimit = StarGift.Gift.PerUserLimit(total: perUserTotal, remains: perUserRemains) + } guard let file = telegramMediaFileFromApiDocument(sticker, altDocuments: nil) else { return nil } - self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId)) + self = .generic(StarGift.Gift(id: id, title: title, file: file, price: stars, convertStars: convertStars, availability: availability, soldOut: soldOut, flags: flags, upgradeStars: upgradeStars, releasedBy: releasedBy?.peerId, perUserLimit: perUserLimit)) case let .starGiftUnique(_, id, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, resellStars, releasedBy): let owner: StarGift.UniqueGift.Owner if let ownerAddress { @@ -1075,7 +1119,7 @@ func _internal_starGiftUpgradePreview(account: Account, giftId: Int64) -> Signal } } -private final class CachedProfileGifts: Codable { +final class CachedProfileGifts: Codable { enum CodingKeys: String, CodingKey { case gifts case count @@ -1118,9 +1162,16 @@ private final class CachedProfileGifts: Codable { } } -private func entryId(peerId: EnginePeer.Id) -> ItemCacheEntryId { - let cacheKey = ValueBoxKey(length: 8) - cacheKey.setInt64(0, value: peerId.toInt64()) +func giftsEntryId(peerId: EnginePeer.Id, collectionId: Int32?) -> ItemCacheEntryId { + let cacheKey: ValueBoxKey + if let collectionId { + cacheKey = ValueBoxKey(length: 8 + 4) + cacheKey.setInt64(0, value: peerId.toInt64()) + cacheKey.setInt32(8, value: collectionId) + } else { + cacheKey = ValueBoxKey(length: 8) + cacheKey.setInt64(0, value: peerId.toInt64()) + } return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedProfileGifts, key: cacheKey) } @@ -1128,6 +1179,7 @@ private final class ProfileGiftsContextImpl { private let queue: Queue private let account: Account private let peerId: PeerId + private let collectionId: Int32? private let disposable = MetaDisposable() private let cacheDisposable = MetaDisposable() @@ -1156,12 +1208,14 @@ private final class ProfileGiftsContextImpl { queue: Queue, account: Account, peerId: EnginePeer.Id, + collectionId: Int32?, sorting: ProfileGiftsContext.Sorting, filter: ProfileGiftsContext.Filters ) { self.queue = queue self.account = account self.peerId = peerId + self.collectionId = collectionId self.sorting = sorting self.filter = filter @@ -1182,6 +1236,7 @@ private final class ProfileGiftsContextImpl { func loadMore(reload: Bool = false) { let peerId = self.peerId + let collectionId = self.collectionId let accountPeerId = self.account.peerId let network = self.account.network let postbox = self.account.postbox @@ -1200,7 +1255,7 @@ private final class ProfileGiftsContextImpl { if case let .ready(true, initialNextOffset) = dataState { if !isFiltered || isUniqueOnlyFilter, self.gifts.isEmpty, initialNextOffset == nil, !reload { self.cacheDisposable.set((self.account.postbox.transaction { transaction -> CachedProfileGifts? in - let cachedGifts = transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedProfileGifts.self) + let cachedGifts = transaction.retrieveItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId))?.get(CachedProfileGifts.self) cachedGifts?.render(transaction: transaction) return cachedGifts } |> deliverOn(self.queue)).start(next: { [weak self] cachedGifts in @@ -1248,6 +1303,9 @@ private final class ProfileGiftsContextImpl { return .single(([], 0, nil, nil)) } var flags: Int32 = 0 + if let _ = collectionId { + flags |= (1 << 6) + } if case .value = sorting { flags |= (1 << 5) } @@ -1266,7 +1324,7 @@ private final class ProfileGiftsContextImpl { if !filter.contains(.unique) { flags |= (1 << 4) } - return network.request(Api.functions.payments.getSavedStarGifts(flags: flags, peer: inputPeer, offset: initialNextOffset ?? "", limit: 36)) + return network.request(Api.functions.payments.getSavedStarGifts(flags: flags, peer: inputPeer, collectionId: collectionId, offset: initialNextOffset ?? "", limit: 36)) |> map(Optional.init) |> `catch` { _ -> Signal in return .single(nil) @@ -1319,7 +1377,7 @@ private final class ProfileGiftsContextImpl { self.gifts = gifts self.cacheDisposable.set(self.account.postbox.transaction { transaction in if let entry = CodableEntry(CachedProfileGifts(gifts: gifts, count: count, notificationsEnabled: notificationsEnabled)) { - transaction.putItemCacheEntry(id: entryId(peerId: peerId), entry: entry) + transaction.putItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId), entry: entry) } }.start()) } else { @@ -1590,6 +1648,88 @@ private final class ProfileGiftsContextImpl { self.pushState() } + func insertStarGifts(gifts: [ProfileGiftsContext.State.StarGift]) { + self.gifts.insert(contentsOf: gifts, at: 0) + self.pushState() + + let peerId = self.peerId + let collectionId = self.collectionId + self.cacheDisposable.set(self.account.postbox.transaction { transaction in + var updatedGifts: [ProfileGiftsContext.State.StarGift] = [] + var updatedCount: Int32 = 0 + if let cachedGifts = transaction.retrieveItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId))?.get(CachedProfileGifts.self) { + updatedGifts = cachedGifts.gifts + updatedCount = cachedGifts.count + } else { + updatedGifts = [] + } + updatedGifts.insert(contentsOf: gifts, at: 0) + updatedCount += Int32(gifts.count) + if let entry = CodableEntry(CachedProfileGifts(gifts: updatedGifts, count: updatedCount, notificationsEnabled: nil)) { + transaction.putItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId), entry: entry) + } + }.start()) + } + + func removeStarGifts(references: [StarGiftReference]) { + self.gifts.removeAll(where: { + if let reference = $0.reference { + return references.contains(reference) + } else { + return false + } + }) + self.pushState() + + let peerId = self.peerId + let collectionId = self.collectionId + self.cacheDisposable.set(self.account.postbox.transaction { transaction in + var updatedGifts: [ProfileGiftsContext.State.StarGift] = [] + var updatedCount: Int32 = 0 + if let cachedGifts = transaction.retrieveItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId))?.get(CachedProfileGifts.self) { + updatedGifts = cachedGifts.gifts + updatedCount = cachedGifts.count + } else { + updatedGifts = [] + } + updatedGifts = updatedGifts.filter { gift in + if let reference = gift.reference { + return !references.contains(reference) + } else { + return true + } + } + updatedCount -= Int32(references.count) + if let entry = CodableEntry(CachedProfileGifts(gifts: updatedGifts, count: updatedCount, notificationsEnabled: nil)) { + transaction.putItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId), entry: entry) + } + }.start()) + } + + func reorderStarGifts(references: [StarGiftReference]) { + let giftsSet = Set(references) + var giftsMap: [StarGiftReference: ProfileGiftsContext.State.StarGift] = [:] + for gift in self.gifts { + if let reference = gift.reference { + giftsMap[reference] = gift + } + } + var updatedGifts: [ProfileGiftsContext.State.StarGift] = [] + for reference in references { + if let gift = giftsMap[reference] { + updatedGifts.append(gift) + } + } + for gift in self.gifts { + if let reference = gift.reference, giftsSet.contains(reference) { + continue + } + updatedGifts.append(gift) + } + self.gifts = updatedGifts + self.pushState() + } + func upgradeStarGift(formId: Int64?, reference: StarGiftReference, keepOriginalInfo: Bool) -> Signal { return Signal { [weak self] subscriber in guard let self else { @@ -2016,15 +2156,22 @@ public final class ProfileGiftsContext { } } + public let peerId: EnginePeer.Id + public let collectionId: Int32? + public init( account: Account, peerId: EnginePeer.Id, + collectionId: Int32? = nil, sorting: ProfileGiftsContext.Sorting = .date, filter: ProfileGiftsContext.Filters = .All ) { + self.peerId = peerId + self.collectionId = collectionId + let queue = self.queue self.impl = QueueLocalObject(queue: queue, generate: { - return ProfileGiftsContextImpl(queue: queue, account: account, peerId: peerId, sorting: sorting, filter: filter) + return ProfileGiftsContextImpl(queue: queue, account: account, peerId: peerId, collectionId: collectionId, sorting: sorting, filter: filter) }) } @@ -2084,6 +2231,24 @@ public final class ProfileGiftsContext { } } + public func insertStarGifts(gifts: [ProfileGiftsContext.State.StarGift]) { + self.impl.with { impl in + impl.insertStarGifts(gifts: gifts) + } + } + + public func removeStarGifts(references: [StarGiftReference]) { + self.impl.with { impl in + impl.removeStarGifts(references: references) + } + } + + public func reorderStarGifts(references: [StarGiftReference]) { + self.impl.with { impl in + impl.reorderStarGifts(references: references) + } + } + public func transferStarGift(prepaid: Bool, reference: StarGiftReference, peerId: EnginePeer.Id) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsCollections.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsCollections.swift new file mode 100644 index 0000000000..153cf1619a --- /dev/null +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGiftsCollections.swift @@ -0,0 +1,436 @@ +import Foundation +import Postbox +import MtProtoKit +import SwiftSignalKit +import TelegramApi + +public struct StarGiftCollection: Codable, Equatable { + public let id: Int32 + public let title: String + public let icon: TelegramMediaFile? + public let count: Int32 + public let hash: Int64 + + public init(id: Int32, title: String, icon: TelegramMediaFile?, count: Int32, hash: Int64) { + self.id = id + self.title = title + self.icon = icon + self.count = count + self.hash = hash + } + + public static func ==(lhs: StarGiftCollection, rhs: StarGiftCollection) -> Bool { + if lhs.id != rhs.id { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.icon != rhs.icon { + return false + } + if lhs.count != rhs.count { + return false + } + if lhs.hash != rhs.hash { + return false + } + return true + } +} + +extension StarGiftCollection { + init?(apiStarGiftCollection: Api.StarGiftCollection) { + switch apiStarGiftCollection { + case let .starGiftCollection(_, collectionId, title, icon, giftsCount, hash): + self.id = collectionId + self.title = title + self.icon = icon.flatMap { telegramMediaFileFromApiDocument($0, altDocuments: nil) } + self.count = giftsCount + self.hash = hash + } + } +} + +private final class CachedProfileGiftsCollections: Codable { + enum CodingKeys: String, CodingKey { + case collections + } + + let collections: [StarGiftCollection] + + init(collections: [StarGiftCollection]) { + self.collections = collections + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + self.collections = try container.decode([StarGiftCollection].self, forKey: .collections) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + + try container.encode(self.collections, forKey: .collections) + } +} + +private func entryId(peerId: EnginePeer.Id) -> ItemCacheEntryId { + let cacheKey = ValueBoxKey(length: 8) + cacheKey.setInt64(0, value: peerId.toInt64()) + return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedProfileGiftsCollections, key: cacheKey) +} + +private func intListSimpleHash(_ list: [Int64]) -> Int64 { + var acc: Int64 = 0 + for value in list { + acc = ((acc * 20261) + Int64(0x80000000) + Int64(value)) % Int64(0x80000000) + } + return Int64(Int32(truncatingIfNeeded: acc)) +} + +private func _internal_getStarGiftCollections(postbox: Postbox, network: Network, peerId: EnginePeer.Id) -> Signal<[StarGiftCollection]?, NoError> { + return postbox.transaction { transaction -> (Api.InputPeer, [StarGiftCollection]?)? in + guard let inputPeer = transaction.getPeer(peerId).flatMap(apiInputPeer) else { + return nil + } + let collections = transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedProfileGiftsCollections.self) + return (inputPeer, collections?.collections) + } + |> mapToSignal { inputPeerAndHash -> Signal<[StarGiftCollection]?, NoError> in + guard let (inputPeer, cachedCollections) = inputPeerAndHash else { + return .single(nil) + } + + var hash: Int64 = 0 + if let cachedCollections { + hash = intListSimpleHash(cachedCollections.map { $0.hash }) + } + + return network.request(Api.functions.payments.getStarGiftCollections(peer: inputPeer, hash: hash)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> mapToSignal { result -> Signal<[StarGiftCollection]?, NoError> in + guard let result else { + return .single(nil) + } + return postbox.transaction { transaction -> [StarGiftCollection]? in + switch result { + case let .starGiftCollections(collections): + let collections = collections.compactMap { StarGiftCollection(apiStarGiftCollection: $0) } + if let entry = CodableEntry(CachedProfileGiftsCollections(collections: collections)) { + transaction.putItemCacheEntry(id: entryId(peerId: peerId), entry: entry) + } + return collections + case .starGiftCollectionsNotModified: + return cachedCollections ?? [] + } + } + } + } +} + +private func _internal_createStarGiftCollection(account: Account, peerId: EnginePeer.Id, title: String, starGifts: [ProfileGiftsContext.State.StarGift]) -> Signal { + return account.postbox.transaction { transaction -> (Api.InputPeer, [Api.InputSavedStarGift])? in + guard let inputPeer = transaction.getPeer(peerId).flatMap(apiInputPeer) else { + return nil + } + let inputStarGifts = starGifts.compactMap { $0.reference }.compactMap { $0.apiStarGiftReference(transaction: transaction) } + return (inputPeer, inputStarGifts) + } + |> mapToSignal { inputPeerAndGifts -> Signal in + guard let (inputPeer, inputStarGifts) = inputPeerAndGifts else { + return .single(nil) + } + + return account.network.request(Api.functions.payments.createStarGiftCollection(peer: inputPeer, title: title, stargift: inputStarGifts)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> StarGiftCollection? in + guard let result else { + return nil + } + return StarGiftCollection(apiStarGiftCollection: result) + } + |> beforeNext { collection in + let _ = account.postbox.transaction { transaction in + if let collection, let entry = CodableEntry(CachedProfileGifts(gifts: starGifts.map { $0.withPinnedToTop(false) }, count: Int32(starGifts.count), notificationsEnabled: nil)) { + transaction.putItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collection.id), entry: entry) + } + }.start() + } + } +} + +private func _internal_reorderStarGiftCollections(account: Account, peerId: EnginePeer.Id, order: [Int32]) -> Signal { + return account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer else { + return .single(false) + } + + return account.network.request(Api.functions.payments.reorderStarGiftCollections(peer: inputPeer, order: order)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> Bool in + if let result, case .boolTrue = result { + return true + } + return false + } + } +} + +private func _internal_updateStarGiftCollection(account: Account, peerId: EnginePeer.Id, collectionId: Int32, giftsContext: ProfileGiftsContext?, actions: [ProfileGiftsCollectionsContext.UpdateAction]) -> Signal { + for action in actions { + switch action { + case let .addGifts(gifts): + giftsContext?.insertStarGifts(gifts: gifts) + case let .removeGifts(gifts): + giftsContext?.removeStarGifts(references: gifts) + case let .reorderGifts(gifts): + giftsContext?.reorderStarGifts(references: gifts) + default: + break + } + } + + return account.postbox.transaction { transaction -> (Api.InputPeer, (FunctionDescription, Buffer, DeserializeFunctionResponse))? in + guard let inputPeer = transaction.getPeer(peerId).flatMap(apiInputPeer) else { + return nil + } + + var flags: Int32 = 0 + var title: String? + var deleteStarGift: [Api.InputSavedStarGift] = [] + var addStarGift: [Api.InputSavedStarGift] = [] + var order: [Api.InputSavedStarGift] = [] + + for action in actions { + switch action { + case let .updateTitle(newTitle): + flags |= (1 << 0) + title = newTitle + case let .addGifts(gifts): + flags |= (1 << 2) + addStarGift.append(contentsOf: gifts.compactMap { $0.reference }.compactMap { $0.apiStarGiftReference(transaction: transaction) }) + case let .removeGifts(gifts): + flags |= (1 << 1) + deleteStarGift.append(contentsOf: gifts.compactMap { $0.apiStarGiftReference(transaction: transaction) }) + case let .reorderGifts(gifts): + flags |= (1 << 3) + order = gifts.compactMap { $0.apiStarGiftReference(transaction: transaction) } + } + } + + let request = Api.functions.payments.updateStarGiftCollection(flags: flags, peer: inputPeer, collectionId: collectionId, title: title, deleteStargift: deleteStarGift, addStargift: addStarGift, order: order) + + return (inputPeer, request) + } + |> mapToSignal { peerAndRequest -> Signal in + guard let (_, request) = peerAndRequest else { + return .single(nil) + } + + return account.network.request(request) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> StarGiftCollection? in + guard let result else { + return nil + } + return StarGiftCollection(apiStarGiftCollection: result) + } + } +} + +private func _internal_deleteStarGiftCollection(account: Account, peerId: EnginePeer.Id, collectionId: Int32) -> Signal { + return account.postbox.transaction { transaction -> Api.InputPeer? in + return transaction.getPeer(peerId).flatMap(apiInputPeer) + } + |> mapToSignal { inputPeer -> Signal in + guard let inputPeer else { + return .single(false) + } + + return account.network.request(Api.functions.payments.deleteStarGiftCollection(peer: inputPeer, collectionId: collectionId)) + |> map(Optional.init) + |> `catch` { _ -> Signal in + return .single(nil) + } + |> map { result -> Bool in + if let result, case .boolTrue = result { + return true + } + return false + } + } +} + +public final class ProfileGiftsCollectionsContext { + public struct State: Equatable { + public var collections: [StarGiftCollection] + public var isLoading: Bool + } + + public enum UpdateAction { + case updateTitle(String) + case addGifts([ProfileGiftsContext.State.StarGift]) + case removeGifts([StarGiftReference]) + case reorderGifts([StarGiftReference]) + } + + private let queue: Queue = .mainQueue() + private let account: Account + private let peerId: EnginePeer.Id + + private let disposable = MetaDisposable() + + private var collections: [StarGiftCollection] = [] + private var giftsContexts: [Int32: ProfileGiftsContext] = [:] + private var isLoading: Bool = false + + private let stateValue = Promise() + public var state: Signal { + return self.stateValue.get() + } + + public init(account: Account, peerId: EnginePeer.Id) { + self.account = account + self.peerId = peerId + + self.reload() + } + + deinit { + self.disposable.dispose() + } + + public func giftsContextForCollection(id: Int32) -> ProfileGiftsContext { + if let current = self.giftsContexts[id] { + return current + } else { + let giftsContext = ProfileGiftsContext(account: self.account, peerId: self.peerId, collectionId: id) + self.giftsContexts[id] = giftsContext + return giftsContext + } + } + + public func reload() { + guard !self.isLoading else { return } + + self.isLoading = true + self.pushState() + + self.disposable.set((_internal_getStarGiftCollections(postbox: self.account.postbox, network: self.account.network, peerId: self.peerId) + |> deliverOn(self.queue)).start(next: { [weak self] collections in + guard let self else { + return + } + self.collections = collections ?? [] + self.isLoading = false + self.pushState() + })) + } + + public func createCollection(title: String, starGifts: [ProfileGiftsContext.State.StarGift]) -> Signal { + return _internal_createStarGiftCollection(account: self.account, peerId: self.peerId, title: title, starGifts: starGifts) + |> deliverOn(self.queue) + |> beforeNext { [weak self] collection in + guard let self else { + return + } + if let collection { + self.collections.append(collection) + self.pushState() + } + } + } + + public func updateCollection(id: Int32, actions: [UpdateAction]) -> Signal { + let giftsContext = self.giftsContextForCollection(id: id) + return _internal_updateStarGiftCollection(account: self.account, peerId: self.peerId, collectionId: id, giftsContext: giftsContext, actions: actions) + |> deliverOn(self.queue) + |> afterNext { [weak self] collection in + guard let self else { + return + } + if let collection { + if let index = self.collections.firstIndex(where: { $0.id == id }) { + self.collections[index] = collection + self.pushState() + } + } + } + } + + public func addGifts(id: Int32, gifts: [ProfileGiftsContext.State.StarGift]) -> Signal { + return self.updateCollection(id: id, actions: [.addGifts(gifts)]) + } + + public func removeGifts(id: Int32, gifts: [StarGiftReference]) -> Signal { + return self.updateCollection(id: id, actions: [.removeGifts(gifts)]) + } + + public func reorderGifts(id: Int32, gifts: [StarGiftReference]) -> Signal { + return self.updateCollection(id: id, actions: [.reorderGifts(gifts)]) + } + + public func renameCollection(id: Int32, title: String) -> Signal { + return self.updateCollection(id: id, actions: [.updateTitle(title)]) + } + + public func reorderCollections(order: [Int32]) -> Signal { + return _internal_reorderStarGiftCollections(account: self.account, peerId: self.peerId, order: order) + |> deliverOn(self.queue) + |> afterNext { [weak self] collection in + guard let self else { + return + } + var collectionMap: [Int32: StarGiftCollection] = [:] + for collection in self.collections { + collectionMap[collection.id] = collection + } + var collections: [StarGiftCollection] = [] + for id in order { + if let collection = collectionMap[id] { + collections.append(collection) + } + } + self.collections = collections + self.pushState() + } + } + + public func deleteCollection(id: Int32) -> Signal { + return _internal_deleteStarGiftCollection(account: self.account, peerId: self.peerId, collectionId: id) + |> deliverOn(self.queue) + |> afterNext { [weak self] _ in + guard let self else { + return + } + self.giftsContexts.removeValue(forKey: id) + self.collections.removeAll(where: { $0.id == id }) + self.pushState() + } + } + + private func pushState() { + let state = State( + collections: self.collections, + isLoading: self.isLoading + ) + self.stateValue.set(.single(state)) + } +} diff --git a/submodules/TelegramUI/Components/ButtonComponent/Sources/ButtonComponent.swift b/submodules/TelegramUI/Components/ButtonComponent/Sources/ButtonComponent.swift index 4ae316e7b1..099a2aa82b 100644 --- a/submodules/TelegramUI/Components/ButtonComponent/Sources/ButtonComponent.swift +++ b/submodules/TelegramUI/Components/ButtonComponent/Sources/ButtonComponent.swift @@ -383,7 +383,7 @@ public final class ButtonComponent: Component { isEnabled: Bool, tintWhenDisabled: Bool = true, allowActionWhenDisabled: Bool = false, - displaysProgress: Bool, + displaysProgress: Bool = false, action: @escaping () -> Void ) { self.background = background diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift index bf0edc3bc7..0e56b06aab 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageAnimatedStickerItemNode/Sources/ChatMessageAnimatedStickerItemNode.swift @@ -1140,6 +1140,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { var replyMessage: Message? var replyForward: QuotedReplyMessageAttribute? var replyQuote: (quote: EngineMessageReplyQuote, isQuote: Bool)? + var replyTodoItemId: Int32? var replyStory: StoryId? for attribute in item.message.attributes { if let attribute = attribute as? InlineBotMessageAttribute { @@ -1167,6 +1168,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { replyMessage = item.message.associatedMessages[replyAttribute.messageId] } replyQuote = replyAttribute.quote.flatMap { ($0, replyAttribute.isQuote) } + replyTodoItemId = replyAttribute.todoItemId } else if let quoteReplyAttribute = attribute as? QuotedReplyMessageAttribute { replyForward = quoteReplyAttribute } else if let attribute = attribute as? ReplyStoryAttribute { @@ -1206,6 +1208,7 @@ public class ChatMessageAnimatedStickerItemNode: ChatMessageItemView { message: replyMessage, replyForward: replyForward, quote: replyQuote, + todoItemId: replyTodoItemId, story: replyStory, parentMessage: item.message, constrainedSize: CGSize(width: availableContentWidth, height: CGFloat.greatestFiniteMagnitude), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift index db8fe19f70..e837fef543 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageBubbleItemNode/Sources/ChatMessageBubbleItemNode.swift @@ -1926,6 +1926,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI var replyMessage: Message? var replyForward: QuotedReplyMessageAttribute? var replyQuote: (quote: EngineMessageReplyQuote, isQuote: Bool)? + var replyTodoItemId: Int32? var replyStory: StoryId? var replyMarkup: ReplyMarkupMessageAttribute? var authorNameColor: UIColor? @@ -1943,6 +1944,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI replyMessage = firstMessage.associatedMessages[attribute.messageId] } replyQuote = attribute.quote.flatMap { ($0, attribute.isQuote) } + replyTodoItemId = attribute.todoItemId } else if let attribute = attribute as? QuotedReplyMessageAttribute { replyForward = attribute } else if let attribute = attribute as? ReplyStoryAttribute { @@ -2713,6 +2715,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI message: replyMessage, replyForward: replyForward, quote: replyQuote, + todoItemId: replyTodoItemId, story: replyStory, parentMessage: item.message, constrainedSize: CGSize(width: maximumNodeWidth - layoutConstants.text.bubbleInsets.left - layoutConstants.text.bubbleInsets.right - 6.0, height: CGFloat.greatestFiniteMagnitude), @@ -5195,7 +5198,7 @@ public class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewI if let replyInfoNode = self.replyInfoNode { progress = replyInfoNode.makeProgress() } - item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil, progress: progress)) + item.controllerInteraction.navigateToMessage(item.message.id, attribute.messageId, NavigateToMessageParams(timestamp: nil, quote: attribute.isQuote ? attribute.quote.flatMap { quote in NavigateToMessageParams.Quote(string: quote.text, offset: quote.offset) } : nil, todoTaskId: attribute.todoItemId, progress: progress)) }, contextMenuOnLongPress: true)) } else if let attribute = attribute as? ReplyStoryAttribute { return .action(InternalBubbleTapAction.Action({ diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift index ac2d939b28..105ae1fc04 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInstantVideoItemNode/Sources/ChatMessageInstantVideoItemNode.swift @@ -464,6 +464,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, ASGestureReco var replyMessage: Message? var replyForward: QuotedReplyMessageAttribute? var replyQuote: (quote: EngineMessageReplyQuote, isQuote: Bool)? + var replyTodoItemId: Int32? var replyStory: StoryId? for attribute in item.message.attributes { if let attribute = attribute as? InlineBotMessageAttribute { @@ -507,6 +508,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, ASGestureReco replyMessage = item.message.associatedMessages[replyAttribute.messageId] } replyQuote = replyAttribute.quote.flatMap { ($0, replyAttribute.isQuote) } + replyTodoItemId = replyAttribute.todoItemId } else if let attribute = attribute as? QuotedReplyMessageAttribute { replyForward = attribute } else if let attribute = attribute as? ReplyStoryAttribute { @@ -526,6 +528,7 @@ public class ChatMessageInstantVideoItemNode: ChatMessageItemView, ASGestureReco message: replyMessage, replyForward: replyForward, quote: replyQuote, + todoItemId: replyTodoItemId, story: replyStory, parentMessage: item.message, constrainedSize: CGSize(width: max(0, availableWidth), height: CGFloat.greatestFiniteMagnitude), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift index e9829eed60..13c092a76e 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageInteractiveInstantVideoNode/Sources/ChatMessageInteractiveInstantVideoNode.swift @@ -352,6 +352,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { var replyMessage: Message? var replyForward: QuotedReplyMessageAttribute? var replyQuote: (quote: EngineMessageReplyQuote, isQuote: Bool)? + var replyTodoItemId: Int32? var replyStory: StoryId? for attribute in item.message.attributes { @@ -382,6 +383,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { replyMessage = item.message.associatedMessages[replyAttribute.messageId] } replyQuote = replyAttribute.quote.flatMap { ($0, replyAttribute.isQuote) } + replyTodoItemId = replyAttribute.todoItemId } else if let attribute = attribute as? QuotedReplyMessageAttribute { replyForward = attribute } else if let attribute = attribute as? ReplyStoryAttribute { @@ -400,6 +402,7 @@ public class ChatMessageInteractiveInstantVideoNode: ASDisplayNode { message: replyMessage, replyForward: replyForward, quote: replyQuote, + todoItemId: replyTodoItemId, story: replyStory, parentMessage: item.message, constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/BUILD b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/BUILD index 2d5e53a9b2..e49f6f160f 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/BUILD +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/BUILD @@ -22,6 +22,7 @@ swift_library( "//submodules/TelegramStringFormatting", "//submodules/TextFormat", "//submodules/InvisibleInkDustNode", + "//submodules/CheckNode", "//submodules/TelegramUI/Components/TextNodeWithEntities", "//submodules/TelegramUI/Components/AnimationCache", "//submodules/TelegramUI/Components/MultiAnimationRenderer", diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift index edd5cacd3d..f60fbd37ec 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageReplyInfoNode/Sources/ChatMessageReplyInfoNode.swift @@ -17,6 +17,7 @@ import AnimationCache import MultiAnimationRenderer import ChatMessageItemCommon import MessageInlineBlockBackgroundView +import CheckNode public enum ChatMessageReplyInfoType { case bubble(incoming: Bool) @@ -81,6 +82,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { public let message: Message? public let replyForward: QuotedReplyMessageAttribute? public let quote: (quote: EngineMessageReplyQuote, isQuote: Bool)? + public let todoItemId: Int32? public let story: StoryId? public let parentMessage: Message public let constrainedSize: CGSize @@ -96,6 +98,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { message: Message?, replyForward: QuotedReplyMessageAttribute?, quote: (quote: EngineMessageReplyQuote, isQuote: Bool)?, + todoItemId: Int32?, story: StoryId?, parentMessage: Message, constrainedSize: CGSize, @@ -110,6 +113,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { self.message = message self.replyForward = replyForward self.quote = quote + self.todoItemId = todoItemId self.story = story self.parentMessage = parentMessage self.constrainedSize = constrainedSize @@ -136,6 +140,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { private var imageNode: TransformImageNode? private var previousMediaReference: AnyMediaReference? private var expiredStoryIconView: UIImageView? + private var checkLayer: CheckLayer? private var currentProgressDisposable: Disposable? @@ -417,8 +422,15 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { textColor = titleColor } + var textLeftInset: CGFloat = 0.0 let messageText: NSAttributedString - if isText, let message = arguments.message { + var todoItemCompleted: Bool? + if let todoItemId = arguments.todoItemId, let todo = arguments.message?.media.first(where: { $0 is TelegramMediaTodo }) as? TelegramMediaTodo, let todoItem = todo.items.first(where: { $0.id == todoItemId }) { + messageText = stringWithAppliedEntities(todoItem.text, entities: todoItem.entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: nil) + textLeftInset += 16.0 + + todoItemCompleted = todo.completions.contains(where: { $0.id == todoItemId }) + } else if isText, let message = arguments.message { var text: String var messageEntities: [MessageTextEntity] @@ -577,6 +589,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { textCutoutWidth = imageTextInset + 6.0 } } + adjustedConstrainedTextSize.width -= textLeftInset let (titleLayout, titleApply) = titleNodeLayout(TextNodeLayoutArguments(attributedString: titleString, backgroundColor: nil, maximumNumberOfLines: maxTitleNumberOfLines, truncationType: .end, constrainedSize: CGSize(width: contrainedTextSize.width - additionalTitleWidth, height: contrainedTextSize.height), alignment: .natural, cutout: nil, insets: textInsets)) if isExpiredStory || isStory { @@ -643,7 +656,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { } var size = CGSize() - size.width = max(titleLayout.size.width + additionalTitleWidth - textInsets.left - textInsets.right, textLayout.size.width - textInsets.left - textInsets.right - textCutoutWidth) + leftInset + 6.0 + size.width = max(titleLayout.size.width + additionalTitleWidth - textInsets.left - textInsets.right, textLeftInset + textLayout.size.width - textInsets.left - textInsets.right - textCutoutWidth) + leftInset + 6.0 size.height = titleLayout.size.height + textLayout.size.height - 2 * (textInsets.top + textInsets.bottom) + 2 * spacing size.height += 2.0 if isExpiredStory || isStory { @@ -713,7 +726,7 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { 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 - textCutoutWidth, y: titleNode.frame.maxY - textInsets.bottom + spacing - textInsets.top - 2.0), size: textLayout.size) + let textFrame = CGRect(origin: CGPoint(x: textLeftInset + leftInset - textInsets.left - 2.0 - textCutoutWidth, y: titleNode.frame.maxY - textInsets.bottom + spacing - textInsets.top - 2.0), size: textLayout.size) let effectiveTextFrame = textFrame.offsetBy(dx: (isExpiredStory || isStory) ? 18.0 : 0.0, dy: 0.0) if textNode.textNode.bounds.isEmpty || !animation.isAnimated || textNode.textNode.bounds.height == effectiveTextFrame.height { @@ -841,11 +854,33 @@ public class ChatMessageReplyInfoNode: ASDisplayNode { quoteIconView.frame = quoteIconFrame } quoteIconView.tintColor = mainColor - } else { - if let quoteIconView = node.quoteIconView { - node.quoteIconView = nil - quoteIconView.removeFromSuperview() + } else if let quoteIconView = node.quoteIconView { + node.quoteIconView = nil + quoteIconView.removeFromSuperview() + } + + if let todoItemCompleted { + let checkLayerFrame = CGRect(origin: CGPoint(x: textFrame.minX - 16.0, y: textFrame.minY + 5.0), size: CGSize(width: 12.0, height: 12.0)) + let checkTheme = CheckNodeTheme(backgroundColor: titleColor, strokeColor: .clear, borderColor: titleColor, overlayBorder: false, hasInset: true, hasShadow: false, borderWidth: 1.0) + + let checkLayer: CheckLayer + if let current = node.checkLayer { + checkLayer = current + + checkLayer.setSelected(todoItemCompleted, animated: true) + animation.animator.updateFrame(layer: checkLayer, frame: checkLayerFrame, completion: nil) + } else { + checkLayer = CheckLayer(theme: checkTheme) + node.checkLayer = checkLayer + node.contentNode.layer.addSublayer(checkLayer) + + checkLayer.setSelected(todoItemCompleted, animated: false) + checkLayer.frame = checkLayerFrame } + checkLayer.theme = checkTheme + } else if let checkLayer = node.checkLayer { + node.checkLayer = nil + checkLayer.removeFromSuperlayer() } node.contentNode.frame = CGRect(origin: CGPoint(), size: size) diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift index c4465ef56e..845afbcf54 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageStickerItemNode/Sources/ChatMessageStickerItemNode.swift @@ -705,6 +705,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { var replyMessage: Message? var replyForward: QuotedReplyMessageAttribute? var replyQuote: (quote: EngineMessageReplyQuote, isQuote: Bool)? + var replyTodoItemId: Int32? var replyStory: StoryId? for attribute in item.message.attributes { if let attribute = attribute as? InlineBotMessageAttribute { @@ -733,6 +734,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { replyMessage = item.message.associatedMessages[replyAttribute.messageId] } replyQuote = replyAttribute.quote.flatMap { ($0, replyAttribute.isQuote) } + replyTodoItemId = replyAttribute.todoItemId } else if let attribute = attribute as? QuotedReplyMessageAttribute { replyForward = attribute } else if let attribute = attribute as? ReplyStoryAttribute { @@ -772,6 +774,7 @@ public class ChatMessageStickerItemNode: ChatMessageItemView { message: replyMessage, replyForward: replyForward, quote: replyQuote, + todoItemId: replyTodoItemId, story: replyStory, parentMessage: item.message, constrainedSize: CGSize(width: availableWidth, height: CGFloat.greatestFiniteMagnitude), diff --git a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift index c7c84b197b..9d4625c0f0 100644 --- a/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift +++ b/submodules/TelegramUI/Components/Chat/ChatMessageTextBubbleContentNode/Sources/ChatMessageTextBubbleContentNode.swift @@ -413,7 +413,7 @@ public class ChatMessageTextBubbleContentNode: ChatMessageBubbleContentNode { } - if item.associatedData.isSuspiciousPeer, let entities = messageEntities { + if incoming && item.associatedData.isSuspiciousPeer, let entities = messageEntities { messageEntities = entities.filter { entity in switch entity.type { case .Url, .TextUrl, .Mention, .TextMention, .Hashtag, .Email, .BankCard: diff --git a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift index 1ccce6c161..395378a09d 100644 --- a/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift +++ b/submodules/TelegramUI/Components/Chat/ChatRecentActionsController/Sources/ChatRecentActionsController.swift @@ -55,7 +55,7 @@ public final class ChatRecentActionsController: TelegramBaseController { self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style - self.panelInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in + self.panelInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _, _ in }, setupEditMessage: { _, _ in }, beginMessageSelection: { _, _ in }, cancelMessageSelection: { _ in diff --git a/submodules/TelegramUI/Components/Chat/FactCheckAlertController/Sources/FactCheckAlertController.swift b/submodules/TelegramUI/Components/Chat/FactCheckAlertController/Sources/FactCheckAlertController.swift index 8591c9993a..0df9fdc4ec 100644 --- a/submodules/TelegramUI/Components/Chat/FactCheckAlertController/Sources/FactCheckAlertController.swift +++ b/submodules/TelegramUI/Components/Chat/FactCheckAlertController/Sources/FactCheckAlertController.swift @@ -53,7 +53,7 @@ private final class FactCheckAlertContentNode: AlertContentNode { return self.isUserInteractionEnabled } - init(context: AccountContext, theme: AlertControllerTheme, presentationTheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, value: String, entities: [MessageTextEntity], characterLimit: Int) { + init(context: AccountContext, theme: AlertControllerTheme, presentationTheme: PresentationTheme, strings: PresentationStrings, actions: [TextAlertAction], text: String, value: String, entities: [MessageTextEntity]) { self.context = context self.theme = theme self.presentationTheme = presentationTheme @@ -362,7 +362,7 @@ private final class FactCheckAlertContentNode: AlertContentNode { } } -public func factCheckAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, value: String, entities: [MessageTextEntity], characterLimit: Int = 1000, apply: @escaping (String, [MessageTextEntity]) -> Void) -> AlertController { +public func factCheckAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal)? = nil, value: String, entities: [MessageTextEntity], apply: @escaping (String, [MessageTextEntity]) -> Void) -> AlertController { let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 } var dismissImpl: ((Bool) -> Void)? @@ -375,7 +375,7 @@ public func factCheckAlertController(context: AccountContext, updatedPresentatio applyImpl?() })] - let contentNode = FactCheckAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), presentationTheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: presentationData.strings.FactCheck_Title, value: value, entities: entities, characterLimit: characterLimit) + let contentNode = FactCheckAlertContentNode(context: context, theme: AlertControllerTheme(presentationData: presentationData), presentationTheme: presentationData.theme, strings: presentationData.strings, actions: actions, text: presentationData.strings.FactCheck_Title, value: value, entities: entities) contentNode.complete = { applyImpl?() } diff --git a/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift index d1210c4e14..56d329e3be 100644 --- a/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Components/Chat/ReplyAccessoryPanelNode/Sources/ReplyAccessoryPanelNode.swift @@ -26,6 +26,7 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { public let chatPeerId: EnginePeer.Id public let messageId: MessageId public let quote: EngineMessageReplyQuote? + public let todoItemId: Int32? private var previousMediaReference: AnyMediaReference? @@ -46,10 +47,11 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { private var validLayout: (size: CGSize, inset: CGFloat, interfaceState: ChatPresentationInterfaceState)? - public init(context: AccountContext, chatPeerId: EnginePeer.Id, messageId: MessageId, quote: EngineMessageReplyQuote?, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, animationCache: AnimationCache?, animationRenderer: MultiAnimationRenderer?) { + public init(context: AccountContext, chatPeerId: EnginePeer.Id, messageId: MessageId, quote: EngineMessageReplyQuote?, todoItemId: Int32?, theme: PresentationTheme, strings: PresentationStrings, nameDisplayOrder: PresentationPersonNameOrder, dateTimeFormat: PresentationDateTimeFormat, animationCache: AnimationCache?, animationRenderer: MultiAnimationRenderer?) { self.chatPeerId = chatPeerId self.messageId = messageId self.quote = quote + self.todoItemId = todoItemId self.context = context self.theme = theme @@ -116,7 +118,7 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { if let strongSelf = self { if messageView.message == nil { Queue.mainQueue().justDispatch { - strongSelf.interfaceInteraction?.setupReplyMessage(nil, { _, _ in }) + strongSelf.interfaceInteraction?.setupReplyMessage(nil, nil, { _, _ in }) } return } @@ -267,7 +269,10 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { } } } else { - if let _ = strongSelf.quote { + if let _ = strongSelf.todoItemId { + let string = strongSelf.strings.Chat_ReplyPanel_ReplyToTodoItem + titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor))] + } else if let _ = strongSelf.quote { let string = strongSelf.strings.Chat_ReplyPanel_ReplyToQuoteBy(authorName).string titleText = [.text(NSAttributedString(string: string, font: Font.medium(15.0), textColor: strongSelf.theme.chat.inputPanel.panelControlAccentColor))] } else { @@ -298,6 +303,10 @@ public final class ReplyAccessoryPanelNode: AccessoryPanelNode { let quoteText = stringWithAppliedEntities(trimToLineCount(quote.text, lineCount: 1), entities: quote.entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message) strongSelf.textNode.attributedText = quoteText + } else if let todoItemId = strongSelf.todoItemId, let todo = message?.media.first(where: { $0 is TelegramMediaTodo }) as? TelegramMediaTodo, let todoItem = todo.items.first(where: { $0.id == todoItemId }) { + let textColor = strongSelf.theme.chat.inputPanel.primaryTextColor + let itemText = stringWithAppliedEntities(trimToLineCount(todoItem.text, lineCount: 1), entities: todoItem.entities, baseColor: textColor, linkColor: textColor, baseFont: textFont, linkFont: textFont, boldFont: textFont, italicFont: textFont, boldItalicFont: textFont, fixedFont: textFont, blockQuoteFont: textFont, underlineLinks: false, message: message) + strongSelf.textNode.attributedText = itemText } strongSelf.titleNode.components = titleText diff --git a/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift b/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift index 11bf50912b..17082f97b6 100644 --- a/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift +++ b/submodules/TelegramUI/Components/ComposeTodoScreen/Sources/ComposeTodoScreen.swift @@ -237,7 +237,7 @@ final class ComposeTodoScreenComponent: Component { self.reorderingItem = nil for (itemId, itemView) in self.todoItemsSectionContainer.itemViews { if itemId == reorderingItem.id, let view = itemView.contents.view { - let viewFrame = view.convert(view.bounds, to: self) + let viewFrame = view.convert(view.bounds, to: self.scrollView) let transition = ComponentTransition.spring(duration: 0.3) transition.setPosition(view: reorderingItem.snapshotView, position: viewFrame.center) transition.setAlpha(view: reorderingItem.backgroundView, alpha: 0.0, completion: { _ in @@ -260,13 +260,14 @@ final class ComposeTodoScreenComponent: Component { snapshotView.center = targetPosition + let localPoint = self.todoItemsSectionContainer.convert(targetPosition, from: self.scrollView) for (itemId, itemView) in self.todoItemsSectionContainer.itemViews { if itemId == id { continue } if let view = itemView.contents.view { - let viewFrame = view.convert(view.bounds, to: self) - if viewFrame.contains(targetPosition) { + let viewFrame = view.convert(view.bounds, to: self.todoItemsSectionContainer) + if viewFrame.contains(localPoint) { if let targetIndex = self.todoItems.firstIndex(where: { AnyHashable($0.id) == itemId }), let reorderingItem = self.todoItems.first(where: { AnyHashable($0.id) == id }) { self.reorderIfPossible(item: reorderingItem, toIndex: targetIndex) } @@ -1662,7 +1663,7 @@ public class ComposeTodoScreen: ViewControllerComponentContainer, AttachmentCont let presentationData = context.sharedContext.currentPresentationData.with { $0 } if !initialData.canEdit && initialData.existingTodo != nil { - self.title = presentationData.strings.CreateTodo_Title + self.title = presentationData.strings.CreateTodo_AddTitle } else { self.title = initialData.existingTodo != nil ? presentationData.strings.CreateTodo_EditTitle : presentationData.strings.CreateTodo_Title } diff --git a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift index ae3d159ef2..93a02c9335 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftItemComponent/Sources/GiftItemComponent.swift @@ -15,6 +15,7 @@ import ItemShimmeringLoadingComponent import AvatarNode import PeerInfoCoverComponent import Markdown +import CheckNode public final class GiftItemComponent: Component { public enum Subject: Equatable { @@ -29,6 +30,7 @@ public final class GiftItemComponent: Component { case blue case purple case green + case orange case custom(Int32, Int32) func colors(theme: PresentationTheme) -> [UIColor] { @@ -67,6 +69,11 @@ public final class GiftItemComponent: Component { UIColor(rgb: 0x4bb121), UIColor(rgb: 0x53d654) ] + case .orange: + return [ + UIColor(rgb: 0xea8b01), + UIColor(rgb: 0xfab625) + ] case let .custom(topColor, _): return [ UIColor(rgb: UInt32(bitPattern: topColor)).withMultiplied(hue: 0.97, saturation: 1.45, brightness: 0.89), @@ -100,6 +107,20 @@ public final class GiftItemComponent: Component { } } + public enum Outline: Equatable { + case orange + + func colors(theme: PresentationTheme) -> [UIColor] { + switch self { + case .orange: + return [ + UIColor(rgb: 0xfab625), + UIColor(rgb: 0xea8b01) + ] + } + } + } + public enum Peer: Equatable { case peer(EnginePeer) case anonymous @@ -111,6 +132,7 @@ public final class GiftItemComponent: Component { case thumbnail case preview case grid + case select } let context: AccountContext @@ -122,6 +144,7 @@ public final class GiftItemComponent: Component { let subtitle: String? let label: String? let ribbon: Ribbon? + let outline: Outline? let resellPrice: Int64? let isLoading: Bool let isHidden: Bool @@ -143,6 +166,7 @@ public final class GiftItemComponent: Component { subtitle: String? = nil, label: String? = nil, ribbon: Ribbon? = nil, + outline: Outline? = nil, resellPrice: Int64? = nil, isLoading: Bool = false, isHidden: Bool = false, @@ -163,6 +187,7 @@ public final class GiftItemComponent: Component { self.subtitle = subtitle self.label = label self.ribbon = ribbon + self.outline = outline self.resellPrice = resellPrice self.isLoading = isLoading self.isHidden = isHidden @@ -203,6 +228,9 @@ public final class GiftItemComponent: Component { if lhs.ribbon != rhs.ribbon { return false } + if lhs.outline != rhs.outline { + return false + } if lhs.resellPrice != rhs.resellPrice { return false } @@ -256,6 +284,8 @@ public final class GiftItemComponent: Component { private var animationLayer: InlineStickerItemLayer? private var selectionLayer: SimpleShapeLayer? + private var checkLayer: CheckLayer? + private var outlineLayer: SimpleLayer? private var animationFile: TelegramMediaFile? @@ -326,7 +356,7 @@ public final class GiftItemComponent: Component { } iconSize = CGSize(width: 88.0, height: 88.0) cornerRadius = 10.0 - case .profile: + case .profile, .select: size = availableSize let side = floor(88.0 * availableSize.height / 116.0) iconSize = CGSize(width: side, height: side) @@ -718,7 +748,7 @@ public final class GiftItemComponent: Component { } } - if let peer = component.peer, !component.isPinned { + if let peer = component.peer, !component.isPinned && component.mode != .select { let avatarNode: AvatarNode if let current = self.avatarNode { avatarNode = current @@ -854,7 +884,6 @@ public final class GiftItemComponent: Component { }) hiddenIcon.layer.animateScale(from: 1.0, to: 0.01, duration: 0.2, removeOnCompletion: false) } - if let resellPrice = component.resellPrice { let labelColor = UIColor.white @@ -965,6 +994,91 @@ public final class GiftItemComponent: Component { } } + if case .select = component.mode { + var checkFrame = CGRect(origin: CGPoint(x: 4.0, y: 4.0), size: CGSize(width: 26.0, height: 26.0)) + let checkTheme: CheckNodeTheme + if case .uniqueGift = component.subject { + checkTheme = CheckNodeTheme(theme: component.theme, style: .overlay) + } else { + checkTheme = CheckNodeTheme(theme: component.theme, style: .plain) + checkFrame = checkFrame.insetBy(dx: 2.0, dy: 2.0) + } + + var isAnimated = true + let checkLayer: CheckLayer + if let current = self.checkLayer { + checkLayer = current + } else { + isAnimated = false + checkLayer = CheckLayer(theme: checkTheme) + self.checkLayer = checkLayer + self.layer.addSublayer(checkLayer) + } + + checkLayer.theme = checkTheme + checkLayer.frame = checkFrame + checkLayer.setSelected(component.isSelected, animated: isAnimated) + } + + if let outline = component.outline { + let lineWidth: CGFloat = 2.0 + let outlineFrame = backgroundFrame + + let outlineLayer: SimpleLayer + if let current = self.outlineLayer { + outlineLayer = current + } else { + outlineLayer = SimpleLayer() + self.outlineLayer = outlineLayer + if self.ribbon.layer.superlayer != nil { + self.layer.insertSublayer(outlineLayer, below: self.ribbon.layer) + } else { + self.layer.addSublayer(outlineLayer) + } + + let image = generateImage(outlineFrame.size, rotatedContext: { size, context in + context.clear(CGRect(origin: .zero, size: size)) + + context.addPath(CGPath(roundedRect: CGRect(origin: .zero, size: outlineFrame.size), cornerWidth: 10.0, cornerHeight: 10.0, transform: nil)) + context.addPath(CGPath(roundedRect: CGRect(origin: .zero, size: outlineFrame.size).insetBy(dx: lineWidth, dy: lineWidth), cornerWidth: 8.0, cornerHeight: 8.0, transform: nil)) + + context.clip(using: .evenOdd) + + var locations: [CGFloat] = [0.0, 1.0] + let colors: [CGColor] = outline.colors(theme: component.theme).map { $0.cgColor } + + let colorSpace = CGColorSpaceCreateDeviceRGB() + let gradient = CGGradient(colorsSpace: colorSpace, colors: colors as CFArray, locations: &locations)! + + context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: 0.0, y: size.height), options: CGGradientDrawingOptions()) + + context.resetClip() + + if let _ = component.ribbon, let ribbonOutline = ribbonOutlineImage, let cgImage = ribbonOutline.cgImage { + context.saveGState() + + context.translateBy(x: 0.0, y: size.height) + context.scaleBy(x: 1.0, y: -1.0) + + //58 + context.clip(to: CGRect(origin: CGPoint(x: 68.0, y: 91.0 - UIScreenPixel), size: ribbonOutline.size), mask: cgImage) + context.setBlendMode(.clear) + context.setFillColor(UIColor.clear.cgColor) + context.fill(CGRect(origin: .zero, size: size)) + + context.restoreGState() + } + }) + outlineLayer.contents = image?.cgImage + + outlineLayer.frame = outlineFrame + } + + } else if let outlineLayer = self.outlineLayer { + self.outlineLayer = nil + outlineLayer.removeFromSuperlayer() + } + if let _ = component.action { self.addSubview(self.containerButton) self.containerButton.isUserInteractionEnabled = true diff --git a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift index 66dd6e9af9..f1579efcf9 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftOptionsScreen/Sources/GiftOptionsScreen.swift @@ -369,6 +369,7 @@ final class GiftOptionsScreenComponent: Component { } var ribbon: GiftItemComponent.Ribbon? + var outline: GiftItemComponent.Outline? var isSoldOut = false switch gift { case let .generic(gift): @@ -391,6 +392,14 @@ final class GiftOptionsScreenComponent: Component { color: .blue ) } + + if gift.flags.contains(.requiresPremium) { + ribbon = GiftItemComponent.Ribbon( + text: "premium", + color: .orange + ) + outline = .orange + } case let .unique(gift): var ribbonColor: GiftItemComponent.Ribbon.Color = .blue for attribute in gift.attributes { @@ -435,6 +444,7 @@ final class GiftOptionsScreenComponent: Component { peer: nil, subject: subject, ribbon: ribbon, + outline: outline, isSoldOut: isSoldOut ) ), @@ -449,6 +459,19 @@ final class GiftOptionsScreenComponent: Component { mainController = controller } if case let .generic(gift) = gift { + if let perUserLimit = gift.perUserLimit, perUserLimit.remains == 0 { + //TODO:localize + let presentationData = component.context.sharedContext.currentPresentationData.with { $0 } + let controller = UndoOverlayController(presentationData: presentationData, content: .sticker(context: component.context, file: gift.file, loop: true, title: nil, text: "You've already sent \(perUserLimit.total) of these gifts, and it's the limit.", undoText: nil, customAction: nil), action: { _ in return false }) + mainController.present(controller, in: .current) + return + } + if gift.flags.contains(.requiresPremium) && !component.context.isPremium { + let controller = component.context.sharedContext.makePremiumIntroController(context: component.context, source: .premiumGift(gift.file), forceDark: false, dismissed: nil) + mainController.push(controller) + return + } + if let availability = gift.availability, availability.remains == 0 { if availability.resale > 0 { let storeController = component.context.sharedContext.makeGiftStoreController( @@ -533,8 +556,6 @@ final class GiftOptionsScreenComponent: Component { } } - - var topPanelHeight = environment.navigationHeight let tabSelectorThreshold = self.tabSelectorOrigin - 8.0 if contentOffset > tabSelectorThreshold - environment.navigationHeight { diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index b0875d295a..3f2bb8baec 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -2368,7 +2368,7 @@ private final class GiftViewSheetContent: CombinedComponent { transition: .immediate ) context.add(descriptionButton - .position(CGPoint(x: context.availableSize.width / 2.0, y: 207.0 + descriptionOffset + description.size.height / 2.0 - UIScreenPixel)) + .position(CGPoint(x: context.availableSize.width / 2.0, y: 207.0 + descriptionOffset + description.size.height / 2.0 - 1.0)) .appear(.default(alpha: true)) .disappear(.default(alpha: true)) ) diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index 7413982b8e..fdc4c5c9d0 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -315,7 +315,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode { self.selectionPanel = ChatMessageSelectionInputPanelNode(theme: presentationData.theme, strings: presentationData.strings, peerMedia: true) self.selectionPanel.context = context - let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in + let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _, _ in }, setupEditMessage: { _, _ in }, beginMessageSelection: { _, _ in }, cancelMessageSelection: { _ in diff --git a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift index 360adb14eb..a151b5416f 100644 --- a/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift +++ b/submodules/TelegramUI/Components/PeerSelectionController/Sources/PeerSelectionControllerNode.swift @@ -381,7 +381,7 @@ final class PeerSelectionControllerNode: ASDisplayNode { self.indexChanged(1) } - self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in + self.interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _, _ in }, setupEditMessage: { _, _ in }, beginMessageSelection: { _, _ in }, cancelMessageSelection: { _ in diff --git a/submodules/TelegramUI/Components/PlainButtonComponent/Sources/PlainButtonComponent.swift b/submodules/TelegramUI/Components/PlainButtonComponent/Sources/PlainButtonComponent.swift index c6c87d9bf7..d36b09e40f 100644 --- a/submodules/TelegramUI/Components/PlainButtonComponent/Sources/PlainButtonComponent.swift +++ b/submodules/TelegramUI/Components/PlainButtonComponent/Sources/PlainButtonComponent.swift @@ -25,7 +25,7 @@ public final class PlainButtonComponent: Component { public init( content: AnyComponent, background: AnyComponent? = nil, - effectAlignment: EffectAlignment, + effectAlignment: EffectAlignment = .center, minSize: CGSize? = nil, contentInsets: UIEdgeInsets = UIEdgeInsets(), action: @escaping () -> Void, diff --git a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift index f59ec62296..98a113ffe3 100644 --- a/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift +++ b/submodules/TelegramUI/Components/Settings/PeerNameColorScreen/Sources/PeerNameColorChatPreviewItem.swift @@ -240,7 +240,7 @@ final class PeerNameColorChatPreviewItemNode: ListViewItemNode { media.append(TelegramMediaWebpage(webpageId: MediaId(namespace: 0, id: 0), content: .Loaded(TelegramMediaWebpageLoadedContent(url: "", displayUrl: "", hash: 0, type: nil, websiteName: site, title: title, text: text, embedUrl: nil, embedType: nil, embedSize: nil, duration: nil, author: nil, isMediaLargeByDefault: nil, imageIsVideoCover: false, image: nil, file: nil, story: nil, attributes: [], instantPage: nil)))) } - let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[authorPeerId], text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] : [], media: media, peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message = Message(stableId: 1, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: messageItem.outgoing ? [] : [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[authorPeerId], text: messageItem.text, attributes: messageItem.reply != nil ? [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)] : [], media: media, peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) items.append(item.context.sharedContext.makeChatMessagePreviewItem(context: item.context, messages: [message], theme: item.componentTheme, strings: item.strings, wallpaper: item.wallpaper, fontSize: item.fontSize, chatBubbleCorners: item.chatBubbleCorners, dateTimeFormat: item.dateTimeFormat, nameOrder: item.nameDisplayOrder, forcedResourceStatus: nil, tapMessage: nil, clickThroughMessage: nil, backgroundNode: currentBackgroundNode, availableReactions: nil, accountPeer: nil, isCentered: false, isPreview: true, isStandalone: false)) } diff --git a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift index 6e3d99c64a..d1b3fb04d6 100644 --- a/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift +++ b/submodules/TelegramUI/Components/Settings/ThemeAccentColorScreen/Sources/ThemeAccentColorControllerNode.swift @@ -1071,7 +1071,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate messages[message4.id] = message4 sampleMessages.append(message4) - let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message5 = Message(stableId: 5, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 5), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66004, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_1_Text, attributes: [ReplyMessageAttribute(messageId: message4.id, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) messages[message5.id] = message5 sampleMessages.append(message5) @@ -1082,7 +1082,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, ASScrollViewDelegate let message6 = Message(stableId: 6, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 6), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66005, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: "", attributes: [], media: [voiceMedia], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message6) - let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil, quote: nil, isQuote: false)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) + let message7 = Message(stableId: 7, stableVersion: 0, id: MessageId(peerId: peerId, namespace: 0, id: 7), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66006, flags: [.Incoming], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[peerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_2_Text, attributes: [ReplyMessageAttribute(messageId: message5.id, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) sampleMessages.append(message7) let message8 = Message(stableId: 8, stableVersion: 0, id: MessageId(peerId: otherPeerId, namespace: 0, id: 8), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66007, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: peers[otherPeerId], text: self.presentationData.strings.Appearance_ThemePreview_Chat_3_Text, attributes: [], media: [], peers: peers, associatedMessages: messages, associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) diff --git a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift index 6de7af8267..5201e1ced0 100644 --- a/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift +++ b/submodules/TelegramUI/Components/Settings/WallpaperGalleryScreen/Sources/WallpaperGalleryItem.swift @@ -1524,7 +1524,7 @@ final class WallpaperGalleryItemNode: GalleryItemNode { let replyMessageId = MessageId(peerId: peerId, namespace: 0, id: 3) messages[replyMessageId] = Message(stableId: 3, stableVersion: 0, id: replyMessageId, globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 66000, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: messageAuthor, text: self.presentationData.strings.WallpaperPreview_ChannelReplyText, attributes: [], media: [], peers: peers, associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:]) - messageAttributes = [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false)] + messageAttributes = [ReplyMessageAttribute(messageId: replyMessageId, threadMessageId: nil, quote: nil, isQuote: false, todoItemId: nil)] } peers[peerId] = messageAuthor diff --git a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift index 24ccaedbe6..b101e9d4c9 100644 --- a/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift +++ b/submodules/TelegramUI/Components/Stars/StarsWithdrawalScreen/Sources/StarsWithdrawalScreen.swift @@ -1432,6 +1432,8 @@ private final class AmountFieldComponent: Component { private weak var state: EmptyComponentState? private var isUpdating: Bool = false + private var didSetValueOnce = false + override init(frame: CGRect) { self.placeholderView = ComponentView() self.textField = TextFieldNodeView(frame: .zero) @@ -1470,7 +1472,7 @@ private final class AmountFieldComponent: Component { } self.textField.textColor = component.textColor - if self.component?.currency != component.currency { + if self.component?.currency != component.currency || ((self.textField.text ?? "").isEmpty && !self.didSetValueOnce) { if let value = component.value, value != .zero { var text = "" switch component.currency { @@ -1480,6 +1482,7 @@ private final class AmountFieldComponent: Component { text = "\(formatTonAmountText(value, dateTimeFormat: PresentationDateTimeFormat(timeFormat: component.dateTimeFormat.timeFormat, dateFormat: component.dateTimeFormat.dateFormat, dateSeparator: "", dateSuffix: "", requiresFullYear: false, decimalSeparator: ".", groupingSeparator: "")))" } self.textField.text = text + self.didSetValueOnce = true } else { self.textField.text = "" } diff --git a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift index 4b192ed122..6997291775 100644 --- a/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift +++ b/submodules/TelegramUI/Components/Stories/StoryContainerScreen/Sources/StoryItemSetContainerViewSendMessage.swift @@ -2271,7 +2271,7 @@ final class StoryItemSetContainerSendMessage { } let file = TelegramMediaFile(fileId: EngineMedia.Id(namespace: Namespaces.Media.LocalFile, id: fileId), partialReference: nil, resource: ICloudFileResource(urlData: item.urlData, thumbnail: false), previewRepresentations: previewRepresentations, videoThumbnails: [], immediateThumbnailData: nil, mimeType: mimeType, size: Int64(item.fileSize), attributes: attributes, alternativeRepresentations: []) - let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), threadId: nil, replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: replyToStoryId, localGroupingKey: groupingKey, correlationId: nil, bubbleUpEmojiOrStickersets: []) + let message: EnqueueMessage = .message(text: "", attributes: [], inlineStickers: [:], mediaReference: .standalone(media: file), threadId: nil, replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: replyToStoryId, localGroupingKey: groupingKey, correlationId: nil, bubbleUpEmojiOrStickersets: []) messages.append(message) } if let _ = groupingKey, messages.count % 10 == 0 { @@ -2376,7 +2376,7 @@ final class StoryItemSetContainerSendMessage { threadId: nil, botId: results.botId, result: result, - replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, + replyToMessageId: replyMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: storyId, hideVia: hideVia, silentPosting: silentPosting, @@ -2764,7 +2764,7 @@ final class StoryItemSetContainerSendMessage { mappedMessages.append(message) } - strongSelf.sendMessages(view: view, peer: peer, messages: mappedMessages.map { $0.withUpdatedReplyToMessageId(replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }).withUpdatedReplyToStoryId(replyToStoryId) }, silentPosting: silentPosting, scheduleTime: scheduleTime) + strongSelf.sendMessages(view: view, peer: peer, messages: mappedMessages.map { $0.withUpdatedReplyToMessageId(replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }).withUpdatedReplyToStoryId(replyToStoryId) }, silentPosting: silentPosting, scheduleTime: scheduleTime) completion() } diff --git a/submodules/TelegramUI/Sources/AppDelegate.swift b/submodules/TelegramUI/Sources/AppDelegate.swift index fa814c6142..4c836d070c 100644 --- a/submodules/TelegramUI/Sources/AppDelegate.swift +++ b/submodules/TelegramUI/Sources/AppDelegate.swift @@ -2775,7 +2775,7 @@ private func extractAccountManagerState(records: AccountRecordsView map { messageIds -> MessageId? in if messageIds.isEmpty { return nil diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift index 49c9f3e815..602daed2a3 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerLoadDisplayNode.swift @@ -1539,7 +1539,7 @@ extension ChatControllerImpl { strongSelf.window?.presentInGlobalOverlay(controller) } - let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { [weak self] messageId, completion in + let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { [weak self] messageId, todoItemId, completion in guard let strongSelf = self, strongSelf.isNodeLoaded else { return } @@ -1565,7 +1565,8 @@ extension ChatControllerImpl { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject( messageId: message.id, - quote: nil + quote: nil, + todoItemId: todoItemId )) }).updatedReplyMessage(message).updatedSearch(nil).updatedShowCommands(false) }, completion: { t in completion(t, {}) @@ -1588,7 +1589,8 @@ extension ChatControllerImpl { } else { let replySubject = ChatInterfaceState.ReplyMessageSubject( messageId: messageId, - quote: nil + quote: nil, + todoItemId: todoItemId ) completion(.immediate, { @@ -4885,7 +4887,7 @@ extension ChatControllerImpl { if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(mappedId) { if toSubject.setupReply { Queue.mainQueue().after(0.1) { - strongSelf.interfaceInteraction?.setupReplyMessage(mappedId, { _, f in f() }) + strongSelf.interfaceInteraction?.setupReplyMessage(mappedId, nil, { _, f in f() }) } } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageFactCheck.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageFactCheck.swift index 3fefe30c17..481e550d2b 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageFactCheck.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenMessageFactCheck.swift @@ -16,7 +16,7 @@ extension ChatControllerImpl { break } } - let controller = factCheckAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, value: currentText, entities: currentEntities, characterLimit: 4096, apply: { [weak self] text, entities in + let controller = factCheckAlertController(context: self.context, updatedPresentationData: self.updatedPresentationData, value: currentText, entities: currentEntities, apply: { [weak self] text, entities in guard let self else { return } diff --git a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTodoContextMenu.swift b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTodoContextMenu.swift index 6788436e63..5b16755848 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTodoContextMenu.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatControllerOpenTodoContextMenu.swift @@ -128,6 +128,22 @@ extension ChatControllerImpl { } } + if "".isEmpty { + //TODO:localize + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Chat_Todo_ReplyToItem, icon: { theme in + return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor) + }, action: { [weak self] c, _ in + guard let self else { + return + } + self.interfaceInteraction?.setupReplyMessage(message.id, todoItem.id, { transition, completed in + c?.dismiss(result: .custom(transition), completion: { + completed() + }) + }) + }))) + } + items.append(.action(ContextMenuActionItem(text: self.presentationData.strings.Conversation_ContextMenuCopy, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Copy"), color: theme.contextMenu.primaryColor) }, action: { [weak self] _, f in f(.default) diff --git a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift index 410da4a718..e81ab0238b 100644 --- a/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift +++ b/submodules/TelegramUI/Sources/Chat/ChatMessageActionOptions.swift @@ -316,7 +316,7 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch quote = EngineMessageReplyQuote(text: trimmedText.string, offset: textSelection.offset, entities: trimmedText.entities, media: nil) } - selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote)).withoutSelectionState() }) }) + selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote, todoItemId: nil)).withoutSelectionState() }) }) } let items = combineLatest(queue: .mainQueue(), @@ -381,7 +381,7 @@ private func generateChatReplyOptionItems(selfController: ChatControllerImpl, ch quote = EngineMessageReplyQuote(text: trimmedText.string, offset: textSelection.offset, entities: trimmedText.entities, media: nil) } - selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote)).withoutSelectionState() }) }) + selfController.updateChatPresentationInterfaceState(animated: false, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject(messageId: replySubject.messageId, quote: quote, todoItemId: nil)).withoutSelectionState() }) }) f(.default) }))) diff --git a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift index 3d60834687..dafe1781bf 100644 --- a/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift +++ b/submodules/TelegramUI/Sources/Chat/UpdateChatPresentationInterfaceState.swift @@ -44,7 +44,8 @@ func updateChatPresentationInterfaceStateImpl( temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject( messageId: keyboardButtonsMessage.id, - quote: nil + quote: nil, + todoItemId: nil )).withUpdatedMessageActionsState({ value in var value = value value.processedSetupReplyMessageId = keyboardButtonsMessage.id @@ -67,7 +68,8 @@ func updateChatPresentationInterfaceStateImpl( if temporaryChatPresentationInterfaceState.interfaceState.replyMessageSubject == nil && temporaryChatPresentationInterfaceState.interfaceState.messageActionsState.processedSetupReplyMessageId != keyboardButtonsMessage.id { temporaryChatPresentationInterfaceState = temporaryChatPresentationInterfaceState.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject( messageId: keyboardButtonsMessage.id, - quote: nil + quote: nil, + todoItemId: nil )).withUpdatedMessageActionsState({ value in var value = value value.processedSetupReplyMessageId = keyboardButtonsMessage.id diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 85005a457b..aea795e301 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -2915,7 +2915,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G } var replyToMessageId: EngineMessageReplySubject? if postAsReply, let messageId { - replyToMessageId = EngineMessageReplySubject(messageId: messageId, quote: nil) + replyToMessageId = EngineMessageReplySubject(messageId: messageId, quote: nil, todoItemId: nil) } strongSelf.sendMessages([.message(text: command, attributes: attributes, inlineStickers: [:], mediaReference: nil, threadId: strongSelf.chatLocation.threadId, replyToMessageId: replyToMessageId, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])]) } @@ -3167,7 +3167,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G }) }, openSearch: { }, setupReply: { [weak self] messageId in - self?.interfaceInteraction?.setupReplyMessage(messageId, { _, f in f() }) + self?.interfaceInteraction?.setupReplyMessage(messageId, nil, { _, f in f() }) }, canSetupReply: { [weak self] message in if message.adAttribute != nil { return .none @@ -3766,9 +3766,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G let replySubject = ChatInterfaceState.ReplyMessageSubject( messageId: message.id, - quote: quoteData + quote: quoteData, + todoItemId: nil ) - if canSendMessagesToChat(strongSelf.presentationInterfaceState) { let _ = strongSelf.presentVoiceMessageDiscardAlert(action: { strongSelf.updateChatPresentationInterfaceState(animated: true, interactive: true, { $0.updatedInterfaceState({ $0.withUpdatedReplyMessageSubject(replySubject) }).updatedSearch(nil).updatedShowCommands(false) }, completion: completion) @@ -8085,13 +8085,13 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G break case let .replyThread(replyThreadMessage): if let effectiveMessageId = replyThreadMessage.effectiveMessageId { - defaultReplyMessageSubject = EngineMessageReplySubject(messageId: effectiveMessageId, quote: nil) + defaultReplyMessageSubject = EngineMessageReplySubject(messageId: effectiveMessageId, quote: nil, todoItemId: nil) } case .customChatContents: break } if let postSuggestionState = self.presentationInterfaceState.interfaceState.postSuggestionState, let editingOriginalMessageId = postSuggestionState.editingOriginalMessageId { - defaultReplyMessageSubject = EngineMessageReplySubject(messageId: editingOriginalMessageId, quote: nil) + defaultReplyMessageSubject = EngineMessageReplySubject(messageId: editingOriginalMessageId, quote: nil, todoItemId: nil) } return messages.map { message in diff --git a/submodules/TelegramUI/Sources/ChatControllerKeyShortcuts.swift b/submodules/TelegramUI/Sources/ChatControllerKeyShortcuts.swift index d95c665015..65ad49f397 100644 --- a/submodules/TelegramUI/Sources/ChatControllerKeyShortcuts.swift +++ b/submodules/TelegramUI/Sources/ChatControllerKeyShortcuts.swift @@ -164,7 +164,8 @@ extension ChatControllerImpl { var updatedState = state.updatedInterfaceState({ state in return state.withUpdatedReplyMessageSubject(ChatInterfaceState.ReplyMessageSubject( messageId: message.id, - quote: nil + quote: nil, + todoItemId: nil )) }) if updatedState.inputMode == .none { @@ -184,7 +185,8 @@ extension ChatControllerImpl { return state.withUpdatedReplyMessageSubject((lastMessage?.id).flatMap { id in return ChatInterfaceState.ReplyMessageSubject( messageId: id, - quote: nil + quote: nil, + todoItemId: nil ) }) }) @@ -218,7 +220,7 @@ extension ChatControllerImpl { if replyMessageSubject.messageId == lastMessage?.id { updatedReplyMessageSubject = nil } else if let message = strongSelf.chatDisplayNode.historyNode.messageInCurrentHistoryView(after: replyMessageSubject.messageId) { - updatedReplyMessageSubject = ChatInterfaceState.ReplyMessageSubject(messageId: message.id, quote: nil) + updatedReplyMessageSubject = ChatInterfaceState.ReplyMessageSubject(messageId: message.id, quote: nil, todoItemId: nil) } strongSelf.updateChatPresentationInterfaceState(interactive: true, { state in diff --git a/submodules/TelegramUI/Sources/ChatControllerNode.swift b/submodules/TelegramUI/Sources/ChatControllerNode.swift index 83be2a0282..a434dbe01c 100644 --- a/submodules/TelegramUI/Sources/ChatControllerNode.swift +++ b/submodules/TelegramUI/Sources/ChatControllerNode.swift @@ -636,7 +636,7 @@ class ChatControllerNode: ASDisplayNode, ASScrollViewDelegate { mappedQuote = EngineMessageReplyQuote(text: quote, offset: nil, entities: [], media: nil) } - attributes.append(ReplyMessageAttribute(messageId: replyMessage.id, threadMessageId: nil, quote: mappedQuote, isQuote: mappedQuote != nil)) + attributes.append(ReplyMessageAttribute(messageId: replyMessage.id, threadMessageId: nil, quote: mappedQuote, isQuote: mappedQuote != nil, todoItemId: nil)) } let message = Message( diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift index a86c11bb33..eee20d1fe2 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateAccessoryPanels.swift @@ -86,7 +86,7 @@ func accessoryPanelForChatPresentationIntefaceState(_ chatPresentationInterfaceS } if let chatPeerId { - let panelNode = ReplyAccessoryPanelNode(context: context, chatPeerId: chatPeerId, messageId: replyMessageSubject.messageId, quote: replyMessageSubject.quote, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder, dateTimeFormat: chatPresentationInterfaceState.dateTimeFormat, animationCache: chatControllerInteraction?.presentationContext.animationCache, animationRenderer: chatControllerInteraction?.presentationContext.animationRenderer) + let panelNode = ReplyAccessoryPanelNode(context: context, chatPeerId: chatPeerId, messageId: replyMessageSubject.messageId, quote: replyMessageSubject.quote, todoItemId: replyMessageSubject.todoItemId, theme: chatPresentationInterfaceState.theme, strings: chatPresentationInterfaceState.strings, nameDisplayOrder: chatPresentationInterfaceState.nameDisplayOrder, dateTimeFormat: chatPresentationInterfaceState.dateTimeFormat, animationCache: chatControllerInteraction?.presentationContext.animationCache, animationRenderer: chatControllerInteraction?.presentationContext.animationRenderer) panelNode.interfaceInteraction = interfaceInteraction return panelNode } else { diff --git a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift index ac76d68048..f7c4df78dd 100644 --- a/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift +++ b/submodules/TelegramUI/Sources/ChatInterfaceStateContextMenus.swift @@ -1162,7 +1162,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState actions.append(.action(ContextMenuActionItem(text: chatPresentationInterfaceState.strings.Conversation_ContextMenuReply, icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Reply"), color: theme.actionSheet.primaryTextColor) }, action: { c, _ in - interfaceInteraction.setupReplyMessage(messages[0].id, { transition, completed in + interfaceInteraction.setupReplyMessage(messages[0].id, nil, { transition, completed in c?.dismiss(result: .custom(transition), completion: { completed() }) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index 2a81f9f50b..48ad0ae251 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -2732,6 +2732,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSource = .todo case let .auth(price): mappedSource = .auth(price) + case let .premiumGift(file): + mappedSource = .premiumGift(file) } return mappedSource }