Merge branch 'master' into story-folders

# Conflicts:
#	submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift
This commit is contained in:
Mikhail Filimonov 2025-07-17 09:46:42 +01:00
commit 1df896f59a
74 changed files with 1622 additions and 556 deletions

View File

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

View File

@ -44,6 +44,7 @@ public enum PremiumIntroSource {
case messageEffects
case todo
case auth(String)
case premiumGift(TelegramMediaFile)
}
public enum PremiumGiftSource: Equatable {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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: {},

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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: [:])

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<Api.StarGiftCollection>) {
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<Api.Bool>) {
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<Api.payments.ConnectedStarRefBots>) {
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<Api.payments.SavedStarGifts>) {
static func getSavedStarGifts(flags: Int32, peer: Api.InputPeer, collectionId: Int32?, offset: String, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.payments.SavedStarGifts>) {
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<Api.payments.StarGiftCollections>) {
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<Api.payments.StarGiftUpgradePreview>) {
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<Api.Bool>) {
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<Api.Bool>) {
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<Api.StarGiftCollection>) {
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<Api.Updates>) {
let buffer = Buffer()

View File

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

View File

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

View File

@ -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<Api.Updates, NoError> in
return .complete()
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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")
}
}
}

View File

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

View File

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

View File

@ -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<Api.payments.SavedStarGifts?, NoError> 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<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError> {
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<Never, TransferStarGiftError> {
return Signal { subscriber in
let disposable = MetaDisposable()

View File

@ -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<Api.payments.StarGiftCollections?, NoError> 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<StarGiftCollection?, NoError> {
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<StarGiftCollection?, NoError> 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<Api.StarGiftCollection?, NoError> 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<Bool, NoError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<Bool, NoError> 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<Api.Bool?, NoError> 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<StarGiftCollection?, NoError> {
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<Api.StarGiftCollection>))? 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<StarGiftCollection?, NoError> in
guard let (_, request) = peerAndRequest else {
return .single(nil)
}
return account.network.request(request)
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.StarGiftCollection?, NoError> 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<Bool, NoError> {
return account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<Bool, NoError> 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<Api.Bool?, NoError> 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<State>()
public var state: Signal<State, NoError> {
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<StarGiftCollection?, NoError> {
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<StarGiftCollection?, NoError> {
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<StarGiftCollection?, NoError> {
return self.updateCollection(id: id, actions: [.addGifts(gifts)])
}
public func removeGifts(id: Int32, gifts: [StarGiftReference]) -> Signal<StarGiftCollection?, NoError> {
return self.updateCollection(id: id, actions: [.removeGifts(gifts)])
}
public func reorderGifts(id: Int32, gifts: [StarGiftReference]) -> Signal<StarGiftCollection?, NoError> {
return self.updateCollection(id: id, actions: [.reorderGifts(gifts)])
}
public func renameCollection(id: Int32, title: String) -> Signal<StarGiftCollection?, NoError> {
return self.updateCollection(id: id, actions: [.updateTitle(title)])
}
public func reorderCollections(order: [Int32]) -> Signal<Bool, NoError> {
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<Bool, NoError> {
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))
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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<PresentationData, NoError>)? = nil, value: String, entities: [MessageTextEntity], characterLimit: Int = 1000, apply: @escaping (String, [MessageTextEntity]) -> Void) -> AlertController {
public func factCheckAlertController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = 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?()
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@ public final class PlainButtonComponent: Component {
public init(
content: AnyComponent<Empty>,
background: AnyComponent<Empty>? = nil,
effectAlignment: EffectAlignment,
effectAlignment: EffectAlignment = .center,
minSize: CGSize? = nil,
contentInsets: UIEdgeInsets = UIEdgeInsets(),
action: @escaping () -> Void,

View File

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

View File

@ -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: [:])

View File

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

View File

@ -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<Empty>()
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 = ""
}

View File

@ -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()
}

View File

@ -2775,7 +2775,7 @@ private func extractAccountManagerState(records: AccountRecordsView<TelegramAcco
if let threadId {
replyToMessageId = MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: Int32(clamping: threadId))
}
return enqueueMessages(account: account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])])
return enqueueMessages(account: account, peerId: peerId, messages: [EnqueueMessage.message(text: text, attributes: [], inlineStickers: [:], mediaReference: nil, threadId: nil, replyToMessageId: replyToMessageId.flatMap { EngineMessageReplySubject(messageId: $0, quote: nil, todoItemId: nil) }, replyToStoryId: nil, localGroupingKey: nil, correlationId: nil, bubbleUpEmojiOrStickersets: [])])
|> map { messageIds -> MessageId? in
if messageIds.isEmpty {
return nil

View File

@ -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() })
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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()
})

View File

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