mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Merge branch 'unlockable-media'
This commit is contained in:
commit
39374476bb
@ -466,6 +466,7 @@ associated_domains_fragment = "" if telegram_bundle_id not in official_bundle_id
|
||||
<array>
|
||||
<string>applinks:telegram.me</string>
|
||||
<string>applinks:t.me</string>
|
||||
<string>applinks:*.t.me</string>
|
||||
</array>
|
||||
"""
|
||||
|
||||
|
@ -7999,6 +7999,7 @@ Sorry for the inconvenience.";
|
||||
"Login.WrongCodeError" = "Wrong code, please try again.";
|
||||
|
||||
"PrivacySettings.LoginEmail" = "Login Email";
|
||||
"PrivacySettings.LoginEmailInfo" = "Change your email address for Telegram login codes.";
|
||||
"Login.EmailChanged" = "Your email has been changed.";
|
||||
|
||||
"Login.InvalidEmailAddressError" = "An error occurred. Please try again.";
|
||||
|
@ -338,47 +338,22 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
),
|
||||
prefix: codePrefix,
|
||||
count: codeLength,
|
||||
width: layout.size.width - 28.0
|
||||
width: layout.size.width - 28.0,
|
||||
compact: layout.size.width <= 320.0
|
||||
)
|
||||
|
||||
var items: [AuthorizationLayoutItem] = []
|
||||
items.append(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
self.animationNode.updateLayout(size: animationSize)
|
||||
if layout.size.width > 320.0 {
|
||||
items.append(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
self.animationNode.updateLayout(size: animationSize)
|
||||
} else {
|
||||
insets.top = navigationBarHeight
|
||||
}
|
||||
|
||||
var additionalBottomInset: CGFloat = 20.0
|
||||
if let codeType = self.codeType {
|
||||
switch codeType {
|
||||
case .otherSession:
|
||||
self.titleIconNode.isHidden = false
|
||||
|
||||
if self.titleIconNode.image == nil {
|
||||
self.titleIconNode.image = generateImage(CGSize(width: 81.0, height: 52.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setFillColor(theme.list.itemPrimaryTextColor.cgColor)
|
||||
context.setStrokeColor(theme.list.itemPrimaryTextColor.cgColor)
|
||||
context.setLineWidth(2.97)
|
||||
let _ = try? drawSvgPath(context, path: "M9.87179487,9.04664384 C9.05602951,9.04664384 8.39525641,9.70682916 8.39525641,10.5205479 L8.39525641,44.0547945 C8.39525641,44.8685133 9.05602951,45.5286986 9.87179487,45.5286986 L65.1538462,45.5286986 C65.9696115,45.5286986 66.6303846,44.8685133 66.6303846,44.0547945 L66.6303846,10.5205479 C66.6303846,9.70682916 65.9696115,9.04664384 65.1538462,9.04664384 L9.87179487,9.04664384 S ")
|
||||
|
||||
let _ = try? drawSvgPath(context, path: "M0,44.0547945 L75.025641,44.0547945 C75.025641,45.2017789 74.2153348,46.1893143 73.0896228,46.4142565 L66.1123641,47.8084669 C65.4749109,47.9358442 64.8264231,48 64.1763458,48 L10.8492952,48 C10.1992179,48 9.55073017,47.9358442 8.91327694,47.8084669 L1.93601826,46.4142565 C0.810306176,46.1893143 0,45.2017789 0,44.0547945 Z ")
|
||||
|
||||
let _ = try? drawSvgPath(context, path: "M2.96153846,16.4383562 L14.1495726,16.4383562 C15.7851852,16.4383562 17.1111111,17.7631027 17.1111111,19.3972603 L17.1111111,45.0410959 C17.1111111,46.6752535 15.7851852,48 14.1495726,48 L2.96153846,48 C1.32592593,48 0,46.6752535 0,45.0410959 L0,19.3972603 C0,17.7631027 1.32592593,16.4383562 2.96153846,16.4383562 Z ")
|
||||
|
||||
context.setStrokeColor(theme.list.plainBackgroundColor.cgColor)
|
||||
context.setLineWidth(1.65)
|
||||
let _ = try? drawSvgPath(context, path: "M2.96153846,15.6133562 L14.1495726,15.6133562 C16.2406558,15.6133562 17.9361111,17.3073033 17.9361111,19.3972603 L17.9361111,45.0410959 C17.9361111,47.1310529 16.2406558,48.825 14.1495726,48.825 L2.96153846,48.825 C0.870455286,48.825 -0.825,47.1310529 -0.825,45.0410959 L-0.825,19.3972603 C-0.825,17.3073033 0.870455286,15.6133562 2.96153846,15.6133562 S ")
|
||||
|
||||
context.setFillColor(theme.list.plainBackgroundColor.cgColor)
|
||||
let _ = try? drawSvgPath(context, path: "M1.64529915,20.3835616 L15.465812,20.3835616 L15.465812,44.0547945 L1.64529915,44.0547945 Z ")
|
||||
|
||||
context.setFillColor(theme.list.itemAccentColor.cgColor)
|
||||
let _ = try? drawSvgPath(context, path: "M66.4700855,0.0285884455 C60.7084674,0.0285884455 55.9687848,4.08259697 55.9687848,9.14830256 C55.9687848,12.0875991 57.5993165,14.6795278 60.0605723,16.3382966 C60.0568181,16.4358994 60.0611217,16.5884309 59.9318097,17.067302 C59.7721478,17.6586615 59.4575977,18.4958519 58.8015608,19.4258487 L58.3294314,20.083383 L59.1449275,20.0976772 C61.9723538,20.1099725 63.6110772,18.2528913 63.8662207,17.9535438 C64.7014993,18.1388449 65.5698144,18.2680167 66.4700855,18.2680167 C72.2312622,18.2680167 76.9713861,14.2140351 76.9713861,9.14830256 C76.9713861,4.08256999 72.2312622,0.0285884455 66.4700855,0.0285884455 Z ")
|
||||
|
||||
let _ = try? drawSvgPath(context, path: "M64.1551769,18.856071 C63.8258967,19.1859287 63.4214479,19.5187 62.9094963,19.840779 C61.8188563,20.5269227 60.5584776,20.9288319 59.1304689,20.9225505 L56.7413094,20.8806727 L57.6592902,19.6022014 L58.127415,18.9502938 C58.6361919,18.2290526 58.9525079,17.5293964 59.1353377,16.8522267 C59.1487516,16.8025521 59.1603548,16.7584153 59.1703974,16.7187893 C56.653362,14.849536 55.1437848,12.1128655 55.1437848,9.14830256 C55.1437848,3.61947515 60.2526259,-0.796411554 66.4700855,-0.796411554 C72.6872626,-0.796411554 77.7963861,3.61958236 77.7963861,9.14830256 C77.7963861,14.6770228 72.6872626,19.0930167 66.4700855,19.0930167 C65.7185957,19.0930167 64.9627196,19.0118067 64.1551769,18.856071 S ")
|
||||
})
|
||||
}
|
||||
|
||||
// items.append(AuthorizationLayoutItem(node: self.titleIconNode, size: self.titleIconNode.image!.size, spacingBefore: AuthorizationLayoutItemSpacing(weight: 41.0, maxValue: 41.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
items.append(AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
items.append(AuthorizationLayoutItem(node: self.currentOptionNode, size: currentOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
|
||||
@ -410,14 +385,11 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
||||
|
||||
items.append(AuthorizationLayoutItem(node: self.nextOptionButtonNode, size: nextOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
default:
|
||||
self.titleIconNode.isHidden = true
|
||||
items.append(AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
items.append(AuthorizationLayoutItem(node: self.currentOptionNode, size: currentOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
|
||||
items.append(AuthorizationLayoutItem(node: self.codeInputView, size: codeFieldSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 30.0, maxValue: 30.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 104.0, maxValue: 104.0)))
|
||||
/*items.append(AuthorizationLayoutItem(node: self.codeField, size: CGSize(width: layout.size.width - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 40.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
items.append(AuthorizationLayoutItem(node: self.codeSeparatorNode, size: CGSize(width: layout.size.width - 88.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))*/
|
||||
|
||||
|
||||
if self.appleSignInAllowed, let signInWithAppleButton = self.signInWithAppleButton {
|
||||
additionalBottomInset = 80.0
|
||||
|
||||
|
@ -172,11 +172,17 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
||||
private func updateButtonsVisibility(transition: ContainedViewLayoutTransition) {
|
||||
if self.currentEmail.isEmpty && self.appleSignInAllowed {
|
||||
transition.updateAlpha(node: self.proceedNode, alpha: 0.0)
|
||||
if self.proceedNode.isHidden {
|
||||
transition.updateAlpha(node: self.dividerNode, alpha: 1.0)
|
||||
}
|
||||
if let signInWithAppleButton = self.signInWithAppleButton {
|
||||
transition.updateAlpha(layer: signInWithAppleButton.layer, alpha: 1.0)
|
||||
}
|
||||
} else {
|
||||
transition.updateAlpha(node: self.proceedNode, alpha: 1.0)
|
||||
if self.proceedNode.isHidden {
|
||||
transition.updateAlpha(node: self.dividerNode, alpha: 0.0)
|
||||
}
|
||||
if let signInWithAppleButton = self.signInWithAppleButton {
|
||||
transition.updateAlpha(layer: signInWithAppleButton.layer, alpha: 0.0)
|
||||
}
|
||||
@ -216,7 +222,7 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
||||
items.append(AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: titleInset, maxValue: titleInset), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
items.append(AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
|
||||
items.append(AuthorizationLayoutItem(node: self.codeField, size: CGSize(width: layout.size.width - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 22.0, maxValue: 40.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
items.append(AuthorizationLayoutItem(node: self.codeField, size: CGSize(width: layout.size.width - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 30.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
items.append(AuthorizationLayoutItem(node: self.codeSeparatorNode, size: CGSize(width: layout.size.width - 48.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
||||
|
||||
if layout.size.width > 320.0 {
|
||||
@ -244,7 +250,7 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
||||
self.dividerNode.isHidden = true
|
||||
}
|
||||
|
||||
let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 80.0)), items: items, transition: transition, failIfDoesNotFit: false)
|
||||
let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 110.0)), items: items, transition: transition, failIfDoesNotFit: false)
|
||||
|
||||
if let signInWithAppleButton = self.signInWithAppleButton, self.appleSignInAllowed {
|
||||
signInWithAppleButton.isHidden = false
|
||||
|
@ -907,7 +907,7 @@ final class PhoneConfirmationController: ViewController {
|
||||
self.codeTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
|
||||
self.codeTargetNode.layer.animatePosition(from: self.codeTargetNode.position, to: self.codeSourceNode.position, duration: duration)
|
||||
|
||||
Queue.mainQueue().after(0.23) {
|
||||
Queue.mainQueue().after(0.2) {
|
||||
codeNode.isHidden = false
|
||||
numberNode.isHidden = false
|
||||
buttonNode.isHidden = false
|
||||
|
@ -121,6 +121,7 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate {
|
||||
private var theme: Theme?
|
||||
private var count: Int?
|
||||
private var prefix: String = ""
|
||||
private var compact = false
|
||||
|
||||
private var textValue: String = ""
|
||||
public var text: String {
|
||||
@ -261,7 +262,7 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate {
|
||||
|
||||
let fontSize: CGFloat
|
||||
if self.prefix.isEmpty {
|
||||
let height: CGFloat = 51.0
|
||||
let height: CGFloat = self.compact ? 44.0 : 51.0
|
||||
fontSize = floor(13.0 * height / 28.0)
|
||||
} else {
|
||||
let height: CGFloat = 28.0
|
||||
@ -291,10 +292,11 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
public func update(theme: Theme, prefix: String, count: Int, width: CGFloat) -> CGSize {
|
||||
public func update(theme: Theme, prefix: String, count: Int, width: CGFloat, compact: Bool) -> CGSize {
|
||||
self.theme = theme
|
||||
self.count = count
|
||||
self.prefix = prefix
|
||||
self.compact = compact
|
||||
|
||||
if theme.isDark {
|
||||
self.textField.keyboardAppearance = .dark
|
||||
@ -305,7 +307,7 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate {
|
||||
let fontSize: CGFloat
|
||||
let height: CGFloat
|
||||
if prefix.isEmpty {
|
||||
height = 51.0
|
||||
height = compact ? 44.0 : 51.0
|
||||
fontSize = floor(13.0 * height / 28.0)
|
||||
} else {
|
||||
height = 28.0
|
||||
|
@ -109,7 +109,10 @@ public func chatMessageGalleryControllerData(context: AccountContext, chatLocati
|
||||
}
|
||||
}
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction {
|
||||
if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia, case let .full(fullMedia) = extendedMedia {
|
||||
standalone = true
|
||||
galleryMedia = fullMedia
|
||||
} else if let action = media as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case let .photoUpdated(image):
|
||||
if let peer = messageMainPeer(EngineMessage(message)), let image = image {
|
||||
|
@ -43,7 +43,9 @@ private func tagsForMessage(_ message: Message) -> MessageTags? {
|
||||
}
|
||||
|
||||
private func galleryMediaForMedia(media: Media) -> Media? {
|
||||
if let media = media as? TelegramMediaImage {
|
||||
if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia, case let .full(fullMedia) = extendedMedia {
|
||||
return fullMedia
|
||||
} else if let media = media as? TelegramMediaImage {
|
||||
return media
|
||||
} else if let file = media as? TelegramMediaFile {
|
||||
if file.mimeType.hasPrefix("audio/") {
|
||||
|
@ -130,7 +130,9 @@ class ChatImageGalleryItem: GalleryItem {
|
||||
|
||||
node.setMessage(self.message, displayInfo: !self.displayInfoOnTop)
|
||||
for media in self.message.media {
|
||||
if let image = media as? TelegramMediaImage {
|
||||
if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia, case let .full(fullMedia) = extendedMedia, let image = fullMedia as? TelegramMediaImage {
|
||||
node.setImage(imageReference: .message(message: MessageReference(self.message), media: image))
|
||||
} else if let image = media as? TelegramMediaImage {
|
||||
node.setImage(imageReference: .message(message: MessageReference(self.message), media: image))
|
||||
break
|
||||
} else if let file = media as? TelegramMediaFile, file.mimeType.hasPrefix("image/") {
|
||||
|
@ -256,6 +256,8 @@ public func chatMessagePhotoDatas(postbox: Postbox, photoReference: ImageMediaRe
|
||||
})
|
||||
|
||||
return signal
|
||||
} else if let decodedThumbnailData = photoReference.media.immediateThumbnailData.flatMap(decodeTinyThumbnail) {
|
||||
return .single(Tuple(decodedThumbnailData, nil, .blurred, false))
|
||||
} else {
|
||||
return .never()
|
||||
}
|
||||
|
@ -114,14 +114,18 @@ class EmojiHeaderComponent: Component {
|
||||
|
||||
let initialPosition = self.statusView.center
|
||||
let targetPosition = self.statusView.superview!.convert(self.statusView.center, to: containerView)
|
||||
let sourcePosition = animateFrom.superview!.convert(animateFrom.center, to: containerView).offsetBy(dx: 0.0, dy: -20.0)
|
||||
let sourcePosition = animateFrom.superview!.convert(animateFrom.center, to: containerView).offsetBy(dx: 0.0, dy: 0.0)
|
||||
|
||||
containerView.addSubview(self.statusView)
|
||||
self.statusView.center = targetPosition
|
||||
|
||||
animateFrom.alpha = 0.0
|
||||
self.statusView.layer.animateScale(from: 0.05, to: 1.0, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
self.statusView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
||||
self.statusView.layer.animateScale(from: 0.24, to: 1.0, duration: 0.36, timingFunction: CAMediaTimingFunctionName.linear.rawValue)
|
||||
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.36, curve: .linear)
|
||||
transition.animatePositionWithKeyframes(layer: self.statusView.layer, keyframes: generateParabollicMotionKeyframes(from: sourcePosition, to: targetPosition, elevation: 50.0))
|
||||
|
||||
Queue.mainQueue().after(0.55, {
|
||||
self.addSubview(self.statusView)
|
||||
self.statusView.center = initialPosition
|
||||
})
|
||||
@ -170,3 +174,37 @@ class EmojiHeaderComponent: Component {
|
||||
return view.update(component: self, availableSize: availableSize, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func generateParabollicMotionKeyframes(from sourcePoint: CGPoint, to targetPosition: CGPoint, elevation: CGFloat) -> [CGPoint] {
|
||||
let midPoint = CGPoint(x: (sourcePoint.x + targetPosition.x) / 2.0, y: sourcePoint.y - elevation)
|
||||
|
||||
let x1 = sourcePoint.x
|
||||
let y1 = sourcePoint.y
|
||||
let x2 = midPoint.x
|
||||
let y2 = midPoint.y
|
||||
let x3 = targetPosition.x
|
||||
let y3 = targetPosition.y
|
||||
|
||||
var keyframes: [CGPoint] = []
|
||||
if abs(y1 - y3) < 5.0 && abs(x1 - x3) < 5.0 {
|
||||
for i in 0 ..< 10 {
|
||||
let k = CGFloat(i) / CGFloat(10 - 1)
|
||||
let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k
|
||||
let y = sourcePoint.y * (1.0 - k) + targetPosition.y * k
|
||||
keyframes.append(CGPoint(x: x, y: y))
|
||||
}
|
||||
} else {
|
||||
let a = (x3 * (y2 - y1) + x2 * (y1 - y3) + x1 * (y3 - y2)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
|
||||
let b = (x1 * x1 * (y2 - y3) + x3 * x3 * (y1 - y2) + x2 * x2 * (y3 - y1)) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
|
||||
let c = (x2 * x2 * (x3 * y1 - x1 * y3) + x2 * (x1 * x1 * y3 - x3 * x3 * y1) + x1 * x3 * (x3 - x1) * y2) / ((x1 - x2) * (x1 - x3) * (x2 - x3))
|
||||
|
||||
for i in 0 ..< 10 {
|
||||
let k = CGFloat(i) / CGFloat(10 - 1)
|
||||
let x = sourcePoint.x * (1.0 - k) + targetPosition.x * k
|
||||
let y = a * x * x + b * x + c
|
||||
keyframes.append(CGPoint(x: x, y: y))
|
||||
}
|
||||
}
|
||||
|
||||
return keyframes
|
||||
}
|
||||
|
@ -92,6 +92,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
||||
case passcode(PresentationTheme, String, Bool, String)
|
||||
case twoStepVerification(PresentationTheme, String, String, TwoStepVerificationAccessConfiguration?)
|
||||
case loginEmail(PresentationTheme, String, String?)
|
||||
case loginEmailInfo(PresentationTheme, String)
|
||||
case activeSessions(PresentationTheme, String, String)
|
||||
case autoArchiveHeader(String)
|
||||
case autoArchive(String, Bool)
|
||||
@ -104,7 +105,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
||||
|
||||
var section: ItemListSectionId {
|
||||
switch self {
|
||||
case .blockedPeers, .activeSessions, .passcode, .twoStepVerification, .loginEmail:
|
||||
case .blockedPeers, .activeSessions, .passcode, .twoStepVerification, .loginEmail, .loginEmailInfo:
|
||||
return PrivacyAndSecuritySection.general.rawValue
|
||||
case .privacyHeader, .phoneNumberPrivacy, .lastSeenPrivacy, .profilePhotoPrivacy, .forwardPrivacy, .groupPrivacy, .voiceCallPrivacy, .voiceMessagePrivacy, .selectivePrivacyInfo:
|
||||
return PrivacyAndSecuritySection.privacy.rawValue
|
||||
@ -129,40 +130,42 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
||||
return 4
|
||||
case .loginEmail:
|
||||
return 5
|
||||
case .privacyHeader:
|
||||
case .loginEmailInfo:
|
||||
return 6
|
||||
case .phoneNumberPrivacy:
|
||||
case .privacyHeader:
|
||||
return 7
|
||||
case .lastSeenPrivacy:
|
||||
case .phoneNumberPrivacy:
|
||||
return 8
|
||||
case .profilePhotoPrivacy:
|
||||
case .lastSeenPrivacy:
|
||||
return 9
|
||||
case .voiceCallPrivacy:
|
||||
case .profilePhotoPrivacy:
|
||||
return 10
|
||||
case .voiceMessagePrivacy:
|
||||
case .voiceCallPrivacy:
|
||||
return 11
|
||||
case .forwardPrivacy:
|
||||
case .voiceMessagePrivacy:
|
||||
return 12
|
||||
case .groupPrivacy:
|
||||
case .forwardPrivacy:
|
||||
return 13
|
||||
case .selectivePrivacyInfo:
|
||||
case .groupPrivacy:
|
||||
return 14
|
||||
case .autoArchiveHeader:
|
||||
case .selectivePrivacyInfo:
|
||||
return 15
|
||||
case .autoArchive:
|
||||
case .autoArchiveHeader:
|
||||
return 16
|
||||
case .autoArchiveInfo:
|
||||
case .autoArchive:
|
||||
return 17
|
||||
case .accountHeader:
|
||||
case .autoArchiveInfo:
|
||||
return 18
|
||||
case .accountTimeout:
|
||||
case .accountHeader:
|
||||
return 19
|
||||
case .accountInfo:
|
||||
case .accountTimeout:
|
||||
return 20
|
||||
case .dataSettings:
|
||||
case .accountInfo:
|
||||
return 21
|
||||
case .dataSettingsInfo:
|
||||
case .dataSettings:
|
||||
return 22
|
||||
case .dataSettingsInfo:
|
||||
return 23
|
||||
}
|
||||
}
|
||||
|
||||
@ -246,6 +249,12 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .loginEmailInfo(lhsTheme, lhsText):
|
||||
if case let .loginEmailInfo(rhsTheme, rhsText) = rhs, lhsTheme === rhsTheme, lhsText == rhsText {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .activeSessions(lhsTheme, lhsText, lhsValue):
|
||||
if case let .activeSessions(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||
return true
|
||||
@ -358,6 +367,8 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/LoginEmail")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openEmailSettings(emailPattern)
|
||||
})
|
||||
case let .loginEmailInfo(_, text):
|
||||
return ItemListTextItem(presentationData: presentationData, text: .plain(text), sectionId: self.section)
|
||||
case let .activeSessions(_, text, value):
|
||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Websites")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||
arguments.openActiveSessions()
|
||||
@ -474,7 +485,8 @@ private func privacyAndSecurityControllerEntries(
|
||||
entries.append(.twoStepVerification(presentationData.theme, presentationData.strings.PrivacySettings_TwoStepAuth, twoStepAuthString, twoStepAuthData))
|
||||
|
||||
if loginEmail != nil {
|
||||
entries.append(.loginEmail(presentationData.theme, presentationData.strings.PrivacySettings_LoginEmail, loginEmail))
|
||||
entries.append(.loginEmail(presentationData.theme, presentationData.strings.PrivacySettings_LoginEmail, loginEmail))
|
||||
entries.append(.loginEmailInfo(presentationData.theme, presentationData.strings.PrivacySettings_LoginEmailInfo))
|
||||
}
|
||||
|
||||
entries.append(.privacyHeader(presentationData.theme, presentationData.strings.PrivacySettings_PrivacyTitle))
|
||||
|
@ -729,8 +729,19 @@ private final class StickerPackContainer: ASDisplayNode {
|
||||
case .unknown, .none:
|
||||
backgroundAlpha = 1.0
|
||||
}
|
||||
self.actionAreaBackgroundNode.alpha = backgroundAlpha
|
||||
self.actionAreaSeparatorNode.alpha = backgroundAlpha
|
||||
|
||||
let transition: ContainedViewLayoutTransition
|
||||
var delay: Double = 0.0
|
||||
if backgroundAlpha >= self.actionAreaBackgroundNode.alpha || abs(backgroundAlpha - self.actionAreaBackgroundNode.alpha) < 0.01 {
|
||||
transition = .immediate
|
||||
} else {
|
||||
transition = .animated(duration: 0.2, curve: .linear)
|
||||
if abs(backgroundAlpha - self.actionAreaBackgroundNode.alpha) > 0.9 {
|
||||
delay = 0.2
|
||||
}
|
||||
}
|
||||
transition.updateAlpha(node: self.actionAreaBackgroundNode, alpha: backgroundAlpha, delay: delay)
|
||||
transition.updateAlpha(node: self.actionAreaSeparatorNode, alpha: backgroundAlpha, delay: delay)
|
||||
}
|
||||
|
||||
private func updateStickerPackContents(_ contents: [LoadedStickerPack], hasPremium: Bool) {
|
||||
|
@ -300,7 +300,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($0) }
|
||||
dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) }
|
||||
dict[-104578748] = { return Api.InputMedia.parse_inputMediaGeoPoint($0) }
|
||||
dict[-646342540] = { return Api.InputMedia.parse_inputMediaInvoice($0) }
|
||||
dict[-1900697899] = { return Api.InputMedia.parse_inputMediaInvoice($0) }
|
||||
dict[-1279654347] = { return Api.InputMedia.parse_inputMediaPhoto($0) }
|
||||
dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) }
|
||||
dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) }
|
||||
@ -354,7 +354,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[42402760] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmoji($0) }
|
||||
dict[215889721] = { return Api.InputStickerSet.parse_inputStickerSetAnimatedEmojiAnimations($0) }
|
||||
dict[-427863538] = { return Api.InputStickerSet.parse_inputStickerSetDice($0) }
|
||||
dict[701560302] = { return Api.InputStickerSet.parse_inputStickerSetEmojiDefaultStatuses($0) }
|
||||
dict[80008398] = { return Api.InputStickerSet.parse_inputStickerSetEmojiGenericAnimations($0) }
|
||||
dict[-4838507] = { return Api.InputStickerSet.parse_inputStickerSetEmpty($0) }
|
||||
dict[-1645763991] = { return Api.InputStickerSet.parse_inputStickerSetID($0) }
|
||||
@ -467,6 +466,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1672577397] = { return Api.MessageEntity.parse_messageEntityUnderline($0) }
|
||||
dict[-1148011883] = { return Api.MessageEntity.parse_messageEntityUnknown($0) }
|
||||
dict[1859134776] = { return Api.MessageEntity.parse_messageEntityUrl($0) }
|
||||
dict[-297296796] = { return Api.MessageExtendedMedia.parse_messageExtendedMedia($0) }
|
||||
dict[-1386050360] = { return Api.MessageExtendedMedia.parse_messageExtendedMediaPreview($0) }
|
||||
dict[1601666510] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) }
|
||||
dict[-1387279939] = { return Api.MessageInteractionCounters.parse_messageInteractionCounters($0) }
|
||||
dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) }
|
||||
@ -476,7 +477,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) }
|
||||
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }
|
||||
dict[-1186937242] = { return Api.MessageMedia.parse_messageMediaGeoLive($0) }
|
||||
dict[-2074799289] = { return Api.MessageMedia.parse_messageMediaInvoice($0) }
|
||||
dict[-156940077] = { return Api.MessageMedia.parse_messageMediaInvoice($0) }
|
||||
dict[1766936791] = { return Api.MessageMedia.parse_messageMediaPhoto($0) }
|
||||
dict[1272375192] = { return Api.MessageMedia.parse_messageMediaPoll($0) }
|
||||
dict[-1618676578] = { return Api.MessageMedia.parse_messageMediaUnsupported($0) }
|
||||
@ -799,6 +800,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1442983757] = { return Api.Update.parse_updateLangPack($0) }
|
||||
dict[1180041828] = { return Api.Update.parse_updateLangPackTooLong($0) }
|
||||
dict[1448076945] = { return Api.Update.parse_updateLoginToken($0) }
|
||||
dict[1517529484] = { return Api.Update.parse_updateMessageExtendedMedia($0) }
|
||||
dict[1318109142] = { return Api.Update.parse_updateMessageID($0) }
|
||||
dict[-1398708869] = { return Api.Update.parse_updateMessagePoll($0) }
|
||||
dict[274961865] = { return Api.Update.parse_updateMessagePollVote($0) }
|
||||
@ -1410,6 +1412,8 @@ public extension Api {
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageEntity:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageExtendedMedia:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageFwdHeader:
|
||||
_1.serialize(buffer, boxed)
|
||||
case let _1 as Api.MessageInteractionCounters:
|
||||
|
@ -540,6 +540,82 @@ public extension Api {
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum MessageExtendedMedia: TypeConstructorDescription {
|
||||
case messageExtendedMedia(media: Api.MessageMedia)
|
||||
case messageExtendedMediaPreview(flags: Int32, w: Int32?, h: Int32?, thumb: Api.PhotoSize?, videoDuration: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .messageExtendedMedia(let media):
|
||||
if boxed {
|
||||
buffer.appendInt32(-297296796)
|
||||
}
|
||||
media.serialize(buffer, true)
|
||||
break
|
||||
case .messageExtendedMediaPreview(let flags, let w, let h, let thumb, let videoDuration):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1386050360)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(w!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(h!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {thumb!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(videoDuration!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .messageExtendedMedia(let media):
|
||||
return ("messageExtendedMedia", [("media", String(describing: media))])
|
||||
case .messageExtendedMediaPreview(let flags, let w, let h, let thumb, let videoDuration):
|
||||
return ("messageExtendedMediaPreview", [("flags", String(describing: flags)), ("w", String(describing: w)), ("h", String(describing: h)), ("thumb", String(describing: thumb)), ("videoDuration", String(describing: videoDuration))])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_messageExtendedMedia(_ reader: BufferReader) -> MessageExtendedMedia? {
|
||||
var _1: Api.MessageMedia?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.MessageMedia
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.MessageExtendedMedia.messageExtendedMedia(media: _1!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_messageExtendedMediaPreview(_ reader: BufferReader) -> MessageExtendedMedia? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_2 = reader.readInt32() }
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
var _4: Api.PhotoSize?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.PhotoSize
|
||||
} }
|
||||
var _5: Int32?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_5 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.MessageExtendedMedia.messageExtendedMediaPreview(flags: _1!, w: _2, h: _3, thumb: _4, videoDuration: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum MessageFwdHeader: TypeConstructorDescription {
|
||||
case messageFwdHeader(flags: Int32, fromId: Api.Peer?, fromName: String?, date: Int32, channelPost: Int32?, postAuthor: String?, savedFromPeer: Api.Peer?, savedFromMsgId: Int32?, psaType: String?)
|
||||
@ -657,7 +733,7 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum MessageMedia: TypeConstructorDescription {
|
||||
indirect enum MessageMedia: TypeConstructorDescription {
|
||||
case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int64)
|
||||
case messageMediaDice(value: Int32, emoticon: String)
|
||||
case messageMediaDocument(flags: Int32, document: Api.Document?, ttlSeconds: Int32?)
|
||||
@ -665,7 +741,7 @@ public extension Api {
|
||||
case messageMediaGame(game: Api.Game)
|
||||
case messageMediaGeo(geo: Api.GeoPoint)
|
||||
case messageMediaGeoLive(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32, proximityNotificationRadius: Int32?)
|
||||
case messageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.WebDocument?, receiptMsgId: Int32?, currency: String, totalAmount: Int64, startParam: String)
|
||||
case messageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.WebDocument?, receiptMsgId: Int32?, currency: String, totalAmount: Int64, startParam: String, extendedMedia: Api.MessageExtendedMedia?)
|
||||
case messageMediaPhoto(flags: Int32, photo: Api.Photo?, ttlSeconds: Int32?)
|
||||
case messageMediaPoll(poll: Api.Poll, results: Api.PollResults)
|
||||
case messageMediaUnsupported
|
||||
@ -727,9 +803,9 @@ public extension Api {
|
||||
serializeInt32(period, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .messageMediaInvoice(let flags, let title, let description, let photo, let receiptMsgId, let currency, let totalAmount, let startParam):
|
||||
case .messageMediaInvoice(let flags, let title, let description, let photo, let receiptMsgId, let currency, let totalAmount, let startParam, let extendedMedia):
|
||||
if boxed {
|
||||
buffer.appendInt32(-2074799289)
|
||||
buffer.appendInt32(-156940077)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
@ -739,6 +815,7 @@ public extension Api {
|
||||
serializeString(currency, buffer: buffer, boxed: false)
|
||||
serializeInt64(totalAmount, buffer: buffer, boxed: false)
|
||||
serializeString(startParam, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 4) != 0 {extendedMedia!.serialize(buffer, true)}
|
||||
break
|
||||
case .messageMediaPhoto(let flags, let photo, let ttlSeconds):
|
||||
if boxed {
|
||||
@ -797,8 +874,8 @@ public extension Api {
|
||||
return ("messageMediaGeo", [("geo", String(describing: geo))])
|
||||
case .messageMediaGeoLive(let flags, let geo, let heading, let period, let proximityNotificationRadius):
|
||||
return ("messageMediaGeoLive", [("flags", String(describing: flags)), ("geo", String(describing: geo)), ("heading", String(describing: heading)), ("period", String(describing: period)), ("proximityNotificationRadius", String(describing: proximityNotificationRadius))])
|
||||
case .messageMediaInvoice(let flags, let title, let description, let photo, let receiptMsgId, let currency, let totalAmount, let startParam):
|
||||
return ("messageMediaInvoice", [("flags", String(describing: flags)), ("title", String(describing: title)), ("description", String(describing: description)), ("photo", String(describing: photo)), ("receiptMsgId", String(describing: receiptMsgId)), ("currency", String(describing: currency)), ("totalAmount", String(describing: totalAmount)), ("startParam", String(describing: startParam))])
|
||||
case .messageMediaInvoice(let flags, let title, let description, let photo, let receiptMsgId, let currency, let totalAmount, let startParam, let extendedMedia):
|
||||
return ("messageMediaInvoice", [("flags", String(describing: flags)), ("title", String(describing: title)), ("description", String(describing: description)), ("photo", String(describing: photo)), ("receiptMsgId", String(describing: receiptMsgId)), ("currency", String(describing: currency)), ("totalAmount", String(describing: totalAmount)), ("startParam", String(describing: startParam)), ("extendedMedia", String(describing: extendedMedia))])
|
||||
case .messageMediaPhoto(let flags, let photo, let ttlSeconds):
|
||||
return ("messageMediaPhoto", [("flags", String(describing: flags)), ("photo", String(describing: photo)), ("ttlSeconds", String(describing: ttlSeconds))])
|
||||
case .messageMediaPoll(let poll, let results):
|
||||
@ -941,6 +1018,10 @@ public extension Api {
|
||||
_7 = reader.readInt64()
|
||||
var _8: String?
|
||||
_8 = parseString(reader)
|
||||
var _9: Api.MessageExtendedMedia?
|
||||
if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() {
|
||||
_9 = Api.parse(reader, signature: signature) as? Api.MessageExtendedMedia
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -949,8 +1030,9 @@ public extension Api {
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||
return Api.MessageMedia.messageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, receiptMsgId: _5, currency: _6!, totalAmount: _7!, startParam: _8!)
|
||||
let _c9 = (Int(_1!) & Int(1 << 4) == 0) || _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.MessageMedia.messageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, receiptMsgId: _5, currency: _6!, totalAmount: _7!, startParam: _8!, extendedMedia: _9)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -608,6 +608,7 @@ public extension Api {
|
||||
case updateLangPack(difference: Api.LangPackDifference)
|
||||
case updateLangPackTooLong(langCode: String)
|
||||
case updateLoginToken
|
||||
case updateMessageExtendedMedia(peer: Api.Peer, msgId: Int32, extendedMedia: Api.MessageExtendedMedia)
|
||||
case updateMessageID(id: Int32, randomId: Int64)
|
||||
case updateMessagePoll(flags: Int32, pollId: Int64, poll: Api.Poll?, results: Api.PollResults)
|
||||
case updateMessagePollVote(pollId: Int64, userId: Int64, options: [Buffer], qts: Int32)
|
||||
@ -1148,6 +1149,14 @@ public extension Api {
|
||||
buffer.appendInt32(1448076945)
|
||||
}
|
||||
|
||||
break
|
||||
case .updateMessageExtendedMedia(let peer, let msgId, let extendedMedia):
|
||||
if boxed {
|
||||
buffer.appendInt32(1517529484)
|
||||
}
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
extendedMedia.serialize(buffer, true)
|
||||
break
|
||||
case .updateMessageID(let id, let randomId):
|
||||
if boxed {
|
||||
@ -1679,6 +1688,8 @@ public extension Api {
|
||||
return ("updateLangPackTooLong", [("langCode", String(describing: langCode))])
|
||||
case .updateLoginToken:
|
||||
return ("updateLoginToken", [])
|
||||
case .updateMessageExtendedMedia(let peer, let msgId, let extendedMedia):
|
||||
return ("updateMessageExtendedMedia", [("peer", String(describing: peer)), ("msgId", String(describing: msgId)), ("extendedMedia", String(describing: extendedMedia))])
|
||||
case .updateMessageID(let id, let randomId):
|
||||
return ("updateMessageID", [("id", String(describing: id)), ("randomId", String(describing: randomId))])
|
||||
case .updateMessagePoll(let flags, let pollId, let poll, let results):
|
||||
@ -2805,6 +2816,27 @@ public extension Api {
|
||||
public static func parse_updateLoginToken(_ reader: BufferReader) -> Update? {
|
||||
return Api.Update.updateLoginToken
|
||||
}
|
||||
public static func parse_updateMessageExtendedMedia(_ reader: BufferReader) -> Update? {
|
||||
var _1: Api.Peer?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.Peer
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: Api.MessageExtendedMedia?
|
||||
if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.MessageExtendedMedia
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.Update.updateMessageExtendedMedia(peer: _1!, msgId: _2!, extendedMedia: _3!)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_updateMessageID(_ reader: BufferReader) -> Update? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
|
@ -4278,6 +4278,26 @@ public extension Api.functions.messages {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getExtendedMedia(peer: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-2064119788)
|
||||
peer.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(id.count))
|
||||
for item in id {
|
||||
serializeInt32(item, buffer: buffer, boxed: false)
|
||||
}
|
||||
return (FunctionDescription(name: "messages.getExtendedMedia", parameters: [("peer", String(describing: peer)), ("id", String(describing: id))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.messages {
|
||||
static func getFavedStickers(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.FavedStickers>) {
|
||||
let buffer = Buffer()
|
||||
|
@ -101,7 +101,7 @@ public extension Api {
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum InputMedia: TypeConstructorDescription {
|
||||
indirect enum InputMedia: TypeConstructorDescription {
|
||||
case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String)
|
||||
case inputMediaDice(emoticon: String)
|
||||
case inputMediaDocument(flags: Int32, id: Api.InputDocument, ttlSeconds: Int32?, query: String?)
|
||||
@ -110,7 +110,7 @@ public extension Api {
|
||||
case inputMediaGame(id: Api.InputGame)
|
||||
case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?)
|
||||
case inputMediaGeoPoint(geoPoint: Api.InputGeoPoint)
|
||||
case inputMediaInvoice(flags: Int32, title: String, description: String, photo: Api.InputWebDocument?, invoice: Api.Invoice, payload: Buffer, provider: String, providerData: Api.DataJSON, startParam: String?)
|
||||
case inputMediaInvoice(flags: Int32, title: String, description: String, photo: Api.InputWebDocument?, invoice: Api.Invoice, payload: Buffer, provider: String, providerData: Api.DataJSON, startParam: String?, extendedMedia: Api.InputMedia?)
|
||||
case inputMediaPhoto(flags: Int32, id: Api.InputPhoto, ttlSeconds: Int32?)
|
||||
case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?)
|
||||
case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?)
|
||||
@ -180,9 +180,9 @@ public extension Api {
|
||||
}
|
||||
geoPoint.serialize(buffer, true)
|
||||
break
|
||||
case .inputMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let startParam):
|
||||
case .inputMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let startParam, let extendedMedia):
|
||||
if boxed {
|
||||
buffer.appendInt32(-646342540)
|
||||
buffer.appendInt32(-1900697899)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
@ -193,6 +193,7 @@ public extension Api {
|
||||
serializeString(provider, buffer: buffer, boxed: false)
|
||||
providerData.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {extendedMedia!.serialize(buffer, true)}
|
||||
break
|
||||
case .inputMediaPhoto(let flags, let id, let ttlSeconds):
|
||||
if boxed {
|
||||
@ -293,8 +294,8 @@ public extension Api {
|
||||
return ("inputMediaGeoLive", [("flags", String(describing: flags)), ("geoPoint", String(describing: geoPoint)), ("heading", String(describing: heading)), ("period", String(describing: period)), ("proximityNotificationRadius", String(describing: proximityNotificationRadius))])
|
||||
case .inputMediaGeoPoint(let geoPoint):
|
||||
return ("inputMediaGeoPoint", [("geoPoint", String(describing: geoPoint))])
|
||||
case .inputMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let startParam):
|
||||
return ("inputMediaInvoice", [("flags", String(describing: flags)), ("title", String(describing: title)), ("description", String(describing: description)), ("photo", String(describing: photo)), ("invoice", String(describing: invoice)), ("payload", String(describing: payload)), ("provider", String(describing: provider)), ("providerData", String(describing: providerData)), ("startParam", String(describing: startParam))])
|
||||
case .inputMediaInvoice(let flags, let title, let description, let photo, let invoice, let payload, let provider, let providerData, let startParam, let extendedMedia):
|
||||
return ("inputMediaInvoice", [("flags", String(describing: flags)), ("title", String(describing: title)), ("description", String(describing: description)), ("photo", String(describing: photo)), ("invoice", String(describing: invoice)), ("payload", String(describing: payload)), ("provider", String(describing: provider)), ("providerData", String(describing: providerData)), ("startParam", String(describing: startParam)), ("extendedMedia", String(describing: extendedMedia))])
|
||||
case .inputMediaPhoto(let flags, let id, let ttlSeconds):
|
||||
return ("inputMediaPhoto", [("flags", String(describing: flags)), ("id", String(describing: id)), ("ttlSeconds", String(describing: ttlSeconds))])
|
||||
case .inputMediaPhotoExternal(let flags, let url, let ttlSeconds):
|
||||
@ -459,6 +460,10 @@ public extension Api {
|
||||
}
|
||||
var _9: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_9 = parseString(reader) }
|
||||
var _10: Api.InputMedia?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_10 = Api.parse(reader, signature: signature) as? Api.InputMedia
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
@ -468,8 +473,9 @@ public extension Api {
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
|
||||
return Api.InputMedia.inputMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7!, providerData: _8!, startParam: _9)
|
||||
let _c10 = (Int(_1!) & Int(1 << 2) == 0) || _10 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 {
|
||||
return Api.InputMedia.inputMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, invoice: _5!, payload: _6!, provider: _7!, providerData: _8!, startParam: _9, extendedMedia: _10)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -677,7 +677,6 @@ public extension Api {
|
||||
case inputStickerSetAnimatedEmoji
|
||||
case inputStickerSetAnimatedEmojiAnimations
|
||||
case inputStickerSetDice(emoticon: String)
|
||||
case inputStickerSetEmojiDefaultStatuses
|
||||
case inputStickerSetEmojiGenericAnimations
|
||||
case inputStickerSetEmpty
|
||||
case inputStickerSetID(id: Int64, accessHash: Int64)
|
||||
@ -703,12 +702,6 @@ public extension Api {
|
||||
buffer.appendInt32(-427863538)
|
||||
}
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputStickerSetEmojiDefaultStatuses:
|
||||
if boxed {
|
||||
buffer.appendInt32(701560302)
|
||||
}
|
||||
|
||||
break
|
||||
case .inputStickerSetEmojiGenericAnimations:
|
||||
if boxed {
|
||||
@ -752,8 +745,6 @@ public extension Api {
|
||||
return ("inputStickerSetAnimatedEmojiAnimations", [])
|
||||
case .inputStickerSetDice(let emoticon):
|
||||
return ("inputStickerSetDice", [("emoticon", String(describing: emoticon))])
|
||||
case .inputStickerSetEmojiDefaultStatuses:
|
||||
return ("inputStickerSetEmojiDefaultStatuses", [])
|
||||
case .inputStickerSetEmojiGenericAnimations:
|
||||
return ("inputStickerSetEmojiGenericAnimations", [])
|
||||
case .inputStickerSetEmpty:
|
||||
@ -784,9 +775,6 @@ public extension Api {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_inputStickerSetEmojiDefaultStatuses(_ reader: BufferReader) -> InputStickerSet? {
|
||||
return Api.InputStickerSet.inputStickerSetEmojiDefaultStatuses
|
||||
}
|
||||
public static func parse_inputStickerSetEmojiGenericAnimations(_ reader: BufferReader) -> InputStickerSet? {
|
||||
return Api.InputStickerSet.inputStickerSetEmojiGenericAnimations
|
||||
}
|
||||
|
@ -115,6 +115,7 @@ enum AccountStateMutationOperation {
|
||||
case UpdateAttachMenuBots
|
||||
case UpdateAudioTranscription(messageId: MessageId, id: Int64, isPending: Bool, text: String)
|
||||
case UpdateConfig
|
||||
case UpdateExtendedMedia(MessageId, Api.MessageExtendedMedia)
|
||||
}
|
||||
|
||||
struct HoleFromPreviousState {
|
||||
@ -271,7 +272,7 @@ struct AccountMutableState {
|
||||
mutating func updateMessageReactions(_ messageId: MessageId, reactions: Api.MessageReactions, eventTimestamp: Int32?) {
|
||||
self.addOperation(.UpdateMessageReactions(messageId, reactions, eventTimestamp))
|
||||
}
|
||||
|
||||
|
||||
mutating func updateMedia(_ id: MediaId, media: Media?) {
|
||||
self.addOperation(.UpdateMedia(id, media))
|
||||
}
|
||||
@ -522,9 +523,13 @@ struct AccountMutableState {
|
||||
self.addOperation(.UpdateConfig)
|
||||
}
|
||||
|
||||
mutating func updateExtendedMedia(_ messageId: MessageId, extendedMedia: Api.MessageExtendedMedia) {
|
||||
self.addOperation(.UpdateExtendedMedia(messageId, extendedMedia))
|
||||
}
|
||||
|
||||
mutating func addOperation(_ operation: AccountStateMutationOperation) {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig:
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .ReadOutbox, .ReadGroupFeedInbox, .MergePeerPresences, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdatePeerChatUnreadMark, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilterOrder, .UpdateChatListFilter, .UpdateReadThread, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateMessagesPinned, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia:
|
||||
break
|
||||
case let .AddMessages(messages, location):
|
||||
for message in messages {
|
||||
|
@ -189,6 +189,7 @@ private var declaredEncodables: Void = {
|
||||
declareEncodable(SendAsMessageAttribute.self, f: { SendAsMessageAttribute(decoder: $0) })
|
||||
declareEncodable(AudioTranscriptionMessageAttribute.self, f: { AudioTranscriptionMessageAttribute(decoder: $0) })
|
||||
declareEncodable(NonPremiumMessageAttribute.self, f: { NonPremiumMessageAttribute(decoder: $0) })
|
||||
declareEncodable(TelegramExtendedMedia.self, f: { TelegramExtendedMedia(decoder: $0) })
|
||||
return
|
||||
}()
|
||||
|
||||
|
@ -478,7 +478,7 @@ extension ChatContextResultMessage {
|
||||
if let replyMarkup = replyMarkup {
|
||||
parsedReplyMarkup = ReplyMarkupMessageAttribute(apiMarkup: replyMarkup)
|
||||
}
|
||||
self = .invoice(media: TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: nil, currency: currency, totalAmount: totalAmount, startParam: "", flags: parsedFlags), replyMarkup: parsedReplyMarkup)
|
||||
self = .invoice(media: TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: nil, currency: currency, totalAmount: totalAmount, startParam: "", extendedMedia: nil, flags: parsedFlags), replyMarkup: parsedReplyMarkup)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ func apiMessageAssociatedMessageIds(_ message: Api.Message) -> [MessageId]? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId:PeerId) -> (Media?, Int32?, Bool?) {
|
||||
func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerId: PeerId) -> (Media?, Int32?, Bool?) {
|
||||
if let media = media {
|
||||
switch media {
|
||||
case let .messageMediaPhoto(_, photo, ttlSeconds):
|
||||
@ -298,7 +298,7 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
||||
break
|
||||
case let .messageMediaGame(game):
|
||||
return (TelegramMediaGame(apiGame: game), nil, nil)
|
||||
case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam):
|
||||
case let .messageMediaInvoice(flags, title, description, photo, receiptMsgId, currency, totalAmount, startParam, apiExtendedMedia):
|
||||
var parsedFlags = TelegramMediaInvoiceFlags()
|
||||
if (flags & (1 << 3)) != 0 {
|
||||
parsedFlags.insert(.isTest)
|
||||
@ -306,7 +306,33 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
||||
if (flags & (1 << 1)) != 0 {
|
||||
parsedFlags.insert(.shippingAddressRequested)
|
||||
}
|
||||
return (TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, flags: parsedFlags), nil, nil)
|
||||
|
||||
let extendedMedia: TelegramExtendedMedia?
|
||||
if let apiExtendedMedia = apiExtendedMedia {
|
||||
switch apiExtendedMedia {
|
||||
case let .messageExtendedMediaPreview(_, width, height, thumb, videoDuration):
|
||||
var dimensions: PixelDimensions?
|
||||
if let width = width, let height = height {
|
||||
dimensions = PixelDimensions(width: width, height: height)
|
||||
}
|
||||
var immediateThumbnailData: Data?
|
||||
if let thumb = thumb, case let .photoStrippedSize(_, bytes) = thumb {
|
||||
immediateThumbnailData = bytes.makeData()
|
||||
}
|
||||
extendedMedia = .preview(dimensions: dimensions, immediateThumbnailData: immediateThumbnailData, videoDuration: videoDuration)
|
||||
case let .messageExtendedMedia(apiMedia):
|
||||
let (media, _, _) = textMediaAndExpirationTimerFromApiMedia(apiMedia, peerId)
|
||||
if let media = media {
|
||||
extendedMedia = .full(media: media)
|
||||
} else {
|
||||
extendedMedia = nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
extendedMedia = nil
|
||||
}
|
||||
|
||||
return (TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: receiptMsgId.flatMap { MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: $0) }, currency: currency, totalAmount: totalAmount, startParam: startParam, extendedMedia: extendedMedia, flags: parsedFlags), nil, nil)
|
||||
case let .messageMediaPoll(poll, results):
|
||||
switch poll {
|
||||
case let .poll(id, flags, question, answers, closePeriod, _):
|
||||
|
@ -66,8 +66,6 @@ extension StickerPackReference {
|
||||
self = .premiumGifts
|
||||
case .inputStickerSetEmojiGenericAnimations:
|
||||
self = .emojiGenericAnimations
|
||||
case .inputStickerSetEmojiDefaultStatuses:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1545,6 +1545,8 @@ private func finalStateWithUpdatesAndServerTime(postbox: Postbox, network: Netwo
|
||||
updatedState.addDismissWebView(queryId)
|
||||
case .updateConfig:
|
||||
updatedState.reloadConfig()
|
||||
case let .updateMessageExtendedMedia(peer, msgId, extendedMedia):
|
||||
updatedState.updateExtendedMedia(MessageId(peerId: peer.peerId, namespace: Namespaces.Message.Cloud, id: msgId), extendedMedia: extendedMedia)
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -2370,7 +2372,7 @@ private func optimizedOperations(_ operations: [AccountStateMutationOperation])
|
||||
var currentAddScheduledMessages: OptimizeAddMessagesState?
|
||||
for operation in operations {
|
||||
switch operation {
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig:
|
||||
case .DeleteMessages, .DeleteMessagesWithGlobalIds, .EditMessage, .UpdateMessagePoll, .UpdateMessageReactions, .UpdateMedia, .MergeApiChats, .MergeApiUsers, .MergePeerPresences, .UpdatePeer, .ReadInbox, .ReadOutbox, .ReadGroupFeedInbox, .ResetReadState, .ResetIncomingReadState, .UpdatePeerChatUnreadMark, .ResetMessageTagSummary, .UpdateNotificationSettings, .UpdateGlobalNotificationSettings, .UpdateSecretChat, .AddSecretMessages, .ReadSecretOutbox, .AddPeerInputActivity, .UpdateCachedPeerData, .UpdatePinnedItemIds, .ReadMessageContents, .UpdateMessageImpressionCount, .UpdateMessageForwardsCount, .UpdateInstalledStickerPacks, .UpdateRecentGifs, .UpdateChatInputState, .UpdateCall, .AddCallSignalingData, .UpdateLangPack, .UpdateMinAvailableMessage, .UpdateIsContact, .UpdatePeerChatInclusion, .UpdatePeersNearby, .UpdateTheme, .SyncChatListFilters, .UpdateChatListFilter, .UpdateChatListFilterOrder, .UpdateReadThread, .UpdateMessagesPinned, .UpdateGroupCallParticipants, .UpdateGroupCall, .UpdateAutoremoveTimeout, .UpdateAttachMenuBots, .UpdateAudioTranscription, .UpdateConfig, .UpdateExtendedMedia:
|
||||
if let currentAddMessages = currentAddMessages, !currentAddMessages.messages.isEmpty {
|
||||
result.append(.AddMessages(currentAddMessages.messages, currentAddMessages.location))
|
||||
}
|
||||
@ -3430,6 +3432,50 @@ func replayFinalState(
|
||||
})
|
||||
case .UpdateConfig:
|
||||
updateConfig = true
|
||||
case let .UpdateExtendedMedia(messageId, apiExtendedMedia):
|
||||
transaction.updateMessage(messageId, update: { currentMessage in
|
||||
var media = currentMessage.media
|
||||
let invoice = media.first(where: { $0 is TelegramMediaInvoice }) as? TelegramMediaInvoice
|
||||
let currentExtendedMedia = invoice?.extendedMedia
|
||||
|
||||
var storeForwardInfo: StoreMessageForwardInfo?
|
||||
if let forwardInfo = currentMessage.forwardInfo {
|
||||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags)
|
||||
}
|
||||
|
||||
let updatedExtendedMedia: TelegramExtendedMedia?
|
||||
switch apiExtendedMedia {
|
||||
case let .messageExtendedMediaPreview(_, width, height, thumb, videoDuration):
|
||||
var dimensions: PixelDimensions?
|
||||
if let width = width, let height = height {
|
||||
dimensions = PixelDimensions(width: width, height: height)
|
||||
}
|
||||
var immediateThumbnailData: Data?
|
||||
if let thumb = thumb, case let .photoStrippedSize(_, bytes) = thumb {
|
||||
immediateThumbnailData = bytes.makeData()
|
||||
}
|
||||
updatedExtendedMedia = .preview(dimensions: dimensions, immediateThumbnailData: immediateThumbnailData, videoDuration: videoDuration)
|
||||
case let .messageExtendedMedia(apiMedia):
|
||||
let (media, _, _) = textMediaAndExpirationTimerFromApiMedia(apiMedia, currentMessage.id.peerId)
|
||||
if let media = media {
|
||||
updatedExtendedMedia = .full(media: media)
|
||||
} else {
|
||||
updatedExtendedMedia = currentExtendedMedia
|
||||
}
|
||||
}
|
||||
|
||||
if let updatedExtendedMedia = updatedExtendedMedia, var invoice = invoice {
|
||||
if let currentExtendedMedia = currentExtendedMedia, case .full = currentExtendedMedia, case .preview = updatedExtendedMedia {
|
||||
|
||||
} else {
|
||||
media = media.filter { !($0 is TelegramMediaInvoice) }
|
||||
invoice = invoice.withUpdatedExtendedMedia(updatedExtendedMedia)
|
||||
media.append(invoice)
|
||||
}
|
||||
}
|
||||
|
||||
return .update(StoreMessage(id: currentMessage.id, globallyUniqueId: currentMessage.globallyUniqueId, groupingKey: currentMessage.groupingKey, threadId: currentMessage.threadId, timestamp: currentMessage.timestamp, flags: StoreMessageFlags(currentMessage.flags), tags: currentMessage.tags, globalTags: currentMessage.globalTags, localTags: currentMessage.localTags, forwardInfo: storeForwardInfo, authorId: currentMessage.author?.id, text: currentMessage.text, attributes: currentMessage.attributes, media: media))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 145
|
||||
return 146
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
@ -16,6 +16,77 @@ public struct TelegramMediaInvoiceFlags: OptionSet {
|
||||
public static let shippingAddressRequested = TelegramMediaInvoiceFlags(rawValue: 1 << 1)
|
||||
}
|
||||
|
||||
public enum TelegramExtendedMedia: PostboxCoding, Equatable {
|
||||
public static func == (lhs: TelegramExtendedMedia, rhs: TelegramExtendedMedia) -> Bool {
|
||||
switch lhs {
|
||||
case let .preview(lhsDimensions, lhsImmediateThumbnailData, lhsVideoDuration):
|
||||
if case let .preview(rhsDimensions, rhsImmediateThumbnailData, rhsVideoDuration) = rhs, lhsDimensions == rhsDimensions, lhsImmediateThumbnailData == rhsImmediateThumbnailData, lhsVideoDuration == rhsVideoDuration {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .full(lhsMedia):
|
||||
if case let .full(rhsMedia) = rhs, lhsMedia.isEqual(to: rhsMedia) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case preview(dimensions: PixelDimensions?, immediateThumbnailData: Data?, videoDuration: Int32?)
|
||||
case full(media: Media)
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
let type = decoder.decodeInt32ForKey("t", orElse: 0)
|
||||
switch type {
|
||||
case 0:
|
||||
let width = decoder.decodeOptionalInt32ForKey("width")
|
||||
let height = decoder.decodeOptionalInt32ForKey("width")
|
||||
var dimensions: PixelDimensions?
|
||||
if let width = width, let height = height {
|
||||
dimensions = PixelDimensions(width: width, height: height)
|
||||
}
|
||||
let immediateThumbnailData = decoder.decodeDataForKey("thumb")
|
||||
let videoDuration = decoder.decodeOptionalInt32ForKey("duration")
|
||||
self = .preview(dimensions: dimensions, immediateThumbnailData: immediateThumbnailData, videoDuration: videoDuration)
|
||||
case 1:
|
||||
let media = decoder.decodeObjectForKey("media") as! Media
|
||||
self = .full(media: media)
|
||||
default:
|
||||
self = .preview(dimensions: nil, immediateThumbnailData: nil, videoDuration: nil)
|
||||
fatalError()
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(_ encoder: PostboxEncoder) {
|
||||
switch self {
|
||||
case let .preview(dimensions, immediateThumbnailData, videoDuration):
|
||||
encoder.encodeInt32(0, forKey: "t")
|
||||
if let dimensions = dimensions {
|
||||
encoder.encodeInt32(dimensions.width, forKey: "width")
|
||||
encoder.encodeInt32(dimensions.height, forKey: "height")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "width")
|
||||
encoder.encodeNil(forKey: "height")
|
||||
}
|
||||
if let immediateThumbnailData = immediateThumbnailData {
|
||||
encoder.encodeData(immediateThumbnailData, forKey: "thumb")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "thumb")
|
||||
}
|
||||
if let videoDuration = videoDuration {
|
||||
encoder.encodeInt32(videoDuration, forKey: "duration")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "duration")
|
||||
}
|
||||
case let .full(media):
|
||||
encoder.encodeInt32(1, forKey: "t")
|
||||
encoder.encodeObject(media, forKey: "media")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final class TelegramMediaInvoice: Media {
|
||||
public var peerIds: [PeerId] = []
|
||||
|
||||
@ -29,8 +100,9 @@ public final class TelegramMediaInvoice: Media {
|
||||
public let startParam: String
|
||||
public let photo: TelegramMediaWebFile?
|
||||
public let flags: TelegramMediaInvoiceFlags
|
||||
public let extendedMedia: TelegramExtendedMedia?
|
||||
|
||||
public init(title: String, description: String, photo: TelegramMediaWebFile?, receiptMessageId: MessageId?, currency: String, totalAmount: Int64, startParam: String, flags: TelegramMediaInvoiceFlags) {
|
||||
public init(title: String, description: String, photo: TelegramMediaWebFile?, receiptMessageId: MessageId?, currency: String, totalAmount: Int64, startParam: String, extendedMedia: TelegramExtendedMedia?, flags: TelegramMediaInvoiceFlags) {
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.photo = photo
|
||||
@ -39,6 +111,7 @@ public final class TelegramMediaInvoice: Media {
|
||||
self.totalAmount = totalAmount
|
||||
self.startParam = startParam
|
||||
self.flags = flags
|
||||
self.extendedMedia = extendedMedia
|
||||
}
|
||||
|
||||
public init(decoder: PostboxDecoder) {
|
||||
@ -49,6 +122,7 @@ public final class TelegramMediaInvoice: Media {
|
||||
self.startParam = decoder.decodeStringForKey("sp", orElse: "")
|
||||
self.photo = decoder.decodeObjectForKey("p") as? TelegramMediaWebFile
|
||||
self.flags = TelegramMediaInvoiceFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0))
|
||||
self.extendedMedia = decoder.decodeObjectForKey("m", decoder: { TelegramExtendedMedia(decoder: $0) }) as? TelegramExtendedMedia
|
||||
|
||||
if let receiptMessageIdPeerId = decoder.decodeOptionalInt64ForKey("r.p") as Int64?, let receiptMessageIdNamespace = decoder.decodeOptionalInt32ForKey("r.n") as Int32?, let receiptMessageIdId = decoder.decodeOptionalInt32ForKey("r.i") as Int32? {
|
||||
self.receiptMessageId = MessageId(peerId: PeerId(receiptMessageIdPeerId), namespace: receiptMessageIdNamespace, id: receiptMessageIdId)
|
||||
@ -65,12 +139,18 @@ public final class TelegramMediaInvoice: Media {
|
||||
encoder.encodeString(self.startParam, forKey: "sp")
|
||||
encoder.encodeInt32(self.flags.rawValue, forKey: "f")
|
||||
|
||||
if let photo = photo {
|
||||
if let photo = self.photo {
|
||||
encoder.encodeObject(photo, forKey: "p")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "p")
|
||||
}
|
||||
|
||||
if let extendedMedia = self.extendedMedia {
|
||||
encoder.encodeObject(extendedMedia, forKey: "m")
|
||||
} else {
|
||||
encoder.encodeNil(forKey: "m")
|
||||
}
|
||||
|
||||
if let receiptMessageId = self.receiptMessageId {
|
||||
encoder.encodeInt64(receiptMessageId.peerId.toInt64(), forKey: "r.p")
|
||||
encoder.encodeInt32(receiptMessageId.namespace, forKey: "r.n")
|
||||
@ -121,4 +201,17 @@ public final class TelegramMediaInvoice: Media {
|
||||
public func isSemanticallyEqual(to other: Media) -> Bool {
|
||||
return self.isEqual(to: other)
|
||||
}
|
||||
|
||||
public func withUpdatedExtendedMedia(_ extendedMedia: TelegramExtendedMedia) -> TelegramMediaInvoice {
|
||||
return TelegramMediaInvoice(
|
||||
title: self.title,
|
||||
description: self.description,
|
||||
photo: self.photo,
|
||||
receiptMessageId: self.receiptMessageId,
|
||||
currency: self.currency,
|
||||
totalAmount: self.totalAmount,
|
||||
startParam: self.startParam,
|
||||
extendedMedia: extendedMedia,
|
||||
flags: self.flags)
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +239,7 @@ func _internal_fetchBotPaymentInvoice(postbox: Postbox, network: Network, source
|
||||
parsedFlags.insert(.shippingAddressRequested)
|
||||
}
|
||||
|
||||
return TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: nil, currency: parsedInvoice.currency, totalAmount: 0, startParam: "", flags: parsedFlags)
|
||||
return TelegramMediaInvoice(title: title, description: description, photo: photo.flatMap(TelegramMediaWebFile.init), receiptMessageId: nil, currency: parsedInvoice.currency, totalAmount: 0, startParam: "", extendedMedia: nil, flags: parsedFlags)
|
||||
}
|
||||
}
|
||||
|> mapError { _ -> BotPaymentFormRequestError in }
|
||||
@ -612,6 +612,7 @@ func _internal_requestBotPaymentReceipt(account: Account, messageId: MessageId)
|
||||
currency: currency,
|
||||
totalAmount: totalAmount,
|
||||
startParam: "",
|
||||
extendedMedia: nil,
|
||||
flags: []
|
||||
)
|
||||
|
||||
|
@ -636,6 +636,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
|
||||
var openMessageByAction = false
|
||||
var isLocation = false
|
||||
|
||||
for media in message.media {
|
||||
if media is TelegramMediaMap {
|
||||
isLocation = true
|
||||
@ -646,7 +647,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
||||
strongSelf.chatDisplayNode.dismissInput()
|
||||
}
|
||||
}
|
||||
if let action = media as? TelegramMediaAction {
|
||||
if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia {
|
||||
switch extendedMedia {
|
||||
case .preview:
|
||||
strongSelf.controllerInteraction?.openCheckoutOrReceipt(message.id)
|
||||
return true
|
||||
case .full:
|
||||
break
|
||||
}
|
||||
} else if let action = media as? TelegramMediaAction {
|
||||
switch action.action {
|
||||
case .pinnedMessageUpdated:
|
||||
for attribute in message.attributes {
|
||||
|
@ -114,9 +114,13 @@ private func contentNodeMessagesAndClassesForItem(_ item: ChatMessageItem) -> ([
|
||||
result.append((message, ChatMessageGameBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
||||
needReactions = false
|
||||
break inner
|
||||
} else if let _ = media as? TelegramMediaInvoice {
|
||||
skipText = true
|
||||
result.append((message, ChatMessageInvoiceBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
||||
} else if let invoice = media as? TelegramMediaInvoice {
|
||||
if let _ = invoice.extendedMedia {
|
||||
result.append((message, ChatMessageMediaBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .media, neighborSpacing: .default)))
|
||||
} else {
|
||||
skipText = true
|
||||
result.append((message, ChatMessageInvoiceBubbleContentNode.self, itemAttributes, BubbleItemAttributes(isAttachment: false, neighborType: .freeform, neighborSpacing: .default)))
|
||||
}
|
||||
needReactions = false
|
||||
break inner
|
||||
} else if let _ = media as? TelegramMediaContact {
|
||||
@ -1376,7 +1380,33 @@ class ChatMessageBubbleItemNode: ChatMessageItemView, ChatMessagePreviewItemNode
|
||||
replyMessage = firstMessage.associatedMessages[attribute.messageId]
|
||||
}
|
||||
} else if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty && !isPreview {
|
||||
replyMarkup = attribute
|
||||
var isExtendedMedia = false
|
||||
for media in firstMessage.media {
|
||||
if let invoice = media as? TelegramMediaInvoice, let _ = invoice.extendedMedia {
|
||||
isExtendedMedia = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isExtendedMedia {
|
||||
var updatedRows: [ReplyMarkupRow] = []
|
||||
for row in attribute.rows {
|
||||
let updatedButtons = row.buttons.filter { button in
|
||||
if case .payment = button.action {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if !updatedButtons.isEmpty {
|
||||
updatedRows.append(ReplyMarkupRow(buttons: updatedButtons))
|
||||
}
|
||||
}
|
||||
if !updatedRows.isEmpty {
|
||||
replyMarkup = ReplyMarkupMessageAttribute(rows: updatedRows, flags: attribute.flags, placeholder: attribute.placeholder)
|
||||
}
|
||||
} else {
|
||||
replyMarkup = attribute
|
||||
}
|
||||
} else if let attribute = attribute as? AuthorSignatureMessageAttribute {
|
||||
if let chatPeer = firstMessage.peers[firstMessage.id.peerId] as? TelegramChannel, case .group = chatPeer.info, firstMessage.author is TelegramChannel, !attribute.signature.isEmpty {
|
||||
authorRank = .custom(attribute.signature)
|
||||
|
@ -75,6 +75,89 @@ struct ChatMessageDateAndStatus {
|
||||
var dateText: String
|
||||
}
|
||||
|
||||
private class ExtendedMediaOverlayNode: ASDisplayNode {
|
||||
private let buttonNode: HighlightTrackingButtonNode
|
||||
private let blurNode: NavigationBackgroundNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
private let iconNode: ASImageNode
|
||||
private let textNode: ImmediateTextNode
|
||||
|
||||
override init() {
|
||||
self.buttonNode = HighlightTrackingButtonNode()
|
||||
self.buttonNode.clipsToBounds = true
|
||||
self.buttonNode.cornerRadius = 16.0
|
||||
|
||||
self.blurNode = NavigationBackgroundNode(color: .clear)
|
||||
|
||||
self.highlightedBackgroundNode = ASDisplayNode()
|
||||
self.highlightedBackgroundNode.backgroundColor = UIColor(rgb: 0xffffff, alpha: 0.3)
|
||||
self.highlightedBackgroundNode.alpha = 0.0
|
||||
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.displaysAsynchronously = false
|
||||
self.iconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/SmallLock"), color: .white)
|
||||
|
||||
self.textNode = ImmediateTextNode()
|
||||
|
||||
super.init()
|
||||
|
||||
self.clipsToBounds = true
|
||||
self.cornerRadius = 16.0
|
||||
self.isUserInteractionEnabled = false
|
||||
|
||||
self.addSubnode(self.buttonNode)
|
||||
self.buttonNode.addSubnode(self.blurNode)
|
||||
self.buttonNode.addSubnode(self.highlightedBackgroundNode)
|
||||
self.addSubnode(self.iconNode)
|
||||
self.addSubnode(self.textNode)
|
||||
|
||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||
if let strongSelf = self {
|
||||
if highlighted {
|
||||
strongSelf.highlightedBackgroundNode.layer.removeAnimation(forKey: "opacity")
|
||||
strongSelf.highlightedBackgroundNode.alpha = 1.0
|
||||
} else {
|
||||
strongSelf.highlightedBackgroundNode.alpha = 0.0
|
||||
strongSelf.highlightedBackgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||
}
|
||||
|
||||
@objc private func buttonPressed() {
|
||||
|
||||
}
|
||||
|
||||
override func didLoad() {
|
||||
super.didLoad()
|
||||
|
||||
if #available(iOS 13.0, *) {
|
||||
self.buttonNode.layer.cornerCurve = .continuous
|
||||
}
|
||||
}
|
||||
|
||||
func update(size: CGSize, text: String) {
|
||||
let spacing: CGFloat = 2.0
|
||||
let padding: CGFloat = 10.0
|
||||
|
||||
self.textNode.attributedText = NSAttributedString(string: text, font: Font.semibold(14.0), textColor: .white, paragraphAlignment: .center)
|
||||
let textSize = self.textNode.updateLayout(size)
|
||||
if let iconSize = self.iconNode.image?.size {
|
||||
let contentSize = CGSize(width: iconSize.width + textSize.width + spacing + padding * 2.0, height: 32.0)
|
||||
self.buttonNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - contentSize.width) / 2.0), y: floorToScreenPixels((size.height - contentSize.height) / 2.0)), size: contentSize)
|
||||
self.highlightedBackgroundNode.frame = CGRect(origin: .zero, size: contentSize)
|
||||
self.blurNode.frame = self.highlightedBackgroundNode.frame
|
||||
self.blurNode.update(size: self.blurNode.frame.size, transition: .immediate)
|
||||
self.blurNode.updateColor(color: UIColor(rgb: 0xffffff, alpha: 0.2), enableBlur: true, transition: .immediate)
|
||||
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(x: self.buttonNode.frame.minX + padding, y: self.buttonNode.frame.minY + floorToScreenPixels((contentSize.height - iconSize.height) / 2.0) + 1.0), size: iconSize)
|
||||
self.textNode.frame = CGRect(origin: CGPoint(x: self.iconNode.frame.maxX + spacing, y: self.buttonNode.frame.minY + floorToScreenPixels((contentSize.height - textSize.height) / 2.0)), size: textSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitionNode {
|
||||
private let pinchContainerNode: PinchSourceContainerNode
|
||||
private let imageNode: TransformImageNode
|
||||
@ -92,6 +175,9 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
let dateAndStatusNode: ChatMessageDateAndStatusNode
|
||||
private var badgeNode: ChatMessageInteractiveMediaBadge?
|
||||
|
||||
private var extendedMediaOverlayNode: ExtendedMediaOverlayNode?
|
||||
|
||||
//private var tapRecognizer: TapLongTapOrDoubleTapGestureRecognizer?
|
||||
|
||||
private var context: AccountContext?
|
||||
@ -152,7 +238,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
var activateLocalContent: (InteractiveMediaNodeActivateContent) -> Void = { _ in }
|
||||
var activatePinch: ((PinchSourceContainerNode) -> Void)?
|
||||
var updateMessageReaction: ((Message, ChatControllerInteractionReaction) -> Void)?
|
||||
|
||||
|
||||
override init() {
|
||||
self.pinchContainerNode = PinchSourceContainerNode()
|
||||
|
||||
@ -167,7 +253,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
|
||||
self.imageNode.displaysAsynchronously = false
|
||||
self.pinchContainerNode.contentNode.addSubnode(self.imageNode)
|
||||
|
||||
|
||||
self.pinchContainerNode.activate = { [weak self] sourceNode in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
@ -308,11 +394,17 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
case .Fetching:
|
||||
if let context = self.context, let message = self.message, message.flags.isSending {
|
||||
let _ = context.engine.messages.deleteMessagesInteractively(messageIds: [message.id], type: .forEveryone).start()
|
||||
} else if let media = media, let context = self.context, let message = message {
|
||||
} else if let media = self.media, let context = self.context, let message = self.message {
|
||||
if let media = media as? TelegramMediaFile {
|
||||
messageMediaFileCancelInteractiveFetch(context: context, messageId: message.id, file: media)
|
||||
} else if let media = media as? TelegramMediaImage, let resource = largestImageRepresentation(media.representations)?.resource {
|
||||
messageMediaImageCancelInteractiveFetch(context: context, messageId: message.id, image: media, resource: resource)
|
||||
} else if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia, case let .full(media) = extendedMedia {
|
||||
if let media = media as? TelegramMediaFile {
|
||||
messageMediaFileCancelInteractiveFetch(context: context, messageId: message.id, file: media)
|
||||
} else if let media = media as? TelegramMediaImage, let resource = largestImageRepresentation(media.representations)?.resource {
|
||||
messageMediaImageCancelInteractiveFetch(context: context, messageId: message.id, image: media, resource: resource)
|
||||
}
|
||||
}
|
||||
}
|
||||
if let cancel = self.fetchControls.with({ return $0?.cancel }) {
|
||||
@ -347,7 +439,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
self.progressPressed(canActivate: true)
|
||||
}
|
||||
} else {
|
||||
self.progressPressed(canActivate: true)
|
||||
if let invoice = self.media as? TelegramMediaInvoice, let _ = invoice.extendedMedia {
|
||||
self.activateLocalContent(.default)
|
||||
} else {
|
||||
self.progressPressed(canActivate: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -420,6 +516,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
}
|
||||
|
||||
var isExtendedMediaPreview = false
|
||||
var isInlinePlayableVideo = false
|
||||
var isSticker = false
|
||||
var maxDimensions = layoutConstants.image.maxDimensions
|
||||
@ -482,6 +579,42 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
case .color, .gradient:
|
||||
unboundSize = CGSize(width: 128.0, height: 128.0)
|
||||
}
|
||||
} else if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia {
|
||||
switch extendedMedia {
|
||||
case let .preview(dimensions, _, _):
|
||||
if let dimensions = dimensions {
|
||||
unboundSize = CGSize(width: max(10.0, floor(dimensions.cgSize.width * 0.5)), height: max(10.0, floor(dimensions.cgSize.height * 0.5)))
|
||||
} else {
|
||||
unboundSize = CGSize(width: 200.0, height: 100.0)
|
||||
}
|
||||
isExtendedMediaPreview = true
|
||||
case let .full(media):
|
||||
if let image = media as? TelegramMediaImage, let dimensions = largestImageRepresentation(image.representations)?.dimensions {
|
||||
unboundSize = CGSize(width: max(10.0, floor(dimensions.cgSize.width * 0.5)), height: max(10.0, floor(dimensions.cgSize.height * 0.5)))
|
||||
} else if let file = media as? TelegramMediaFile, var dimensions = file.dimensions {
|
||||
if let thumbnail = file.previewRepresentations.first {
|
||||
let dimensionsVertical = dimensions.width < dimensions.height
|
||||
let thumbnailVertical = thumbnail.dimensions.width < thumbnail.dimensions.height
|
||||
if dimensionsVertical != thumbnailVertical {
|
||||
dimensions = PixelDimensions(CGSize(width: dimensions.cgSize.height, height: dimensions.cgSize.width))
|
||||
}
|
||||
}
|
||||
unboundSize = CGSize(width: floor(dimensions.cgSize.width * 0.5), height: floor(dimensions.cgSize.height * 0.5))
|
||||
if file.isAnimated {
|
||||
unboundSize = unboundSize.aspectFilled(CGSize(width: 480.0, height: 480.0))
|
||||
} else if file.isVideo && !file.isAnimated, case let .constrained(constrainedSize) = sizeCalculation {
|
||||
if unboundSize.width > unboundSize.height {
|
||||
maxDimensions = CGSize(width: constrainedSize.width, height: layoutConstants.video.maxHorizontalHeight)
|
||||
} else {
|
||||
maxDimensions = CGSize(width: constrainedSize.width, height: layoutConstants.video.maxVerticalHeight)
|
||||
}
|
||||
maxHeight = maxDimensions.height
|
||||
}
|
||||
isInlinePlayableVideo = file.isVideo && !isSecretMedia
|
||||
} else {
|
||||
unboundSize = CGSize(width: 54.0, height: 54.0)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unboundSize = CGSize(width: 54.0, height: 54.0)
|
||||
}
|
||||
@ -653,6 +786,17 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
|
||||
if mediaUpdated || isSendingUpdated || automaticPlaybackUpdated {
|
||||
var media = media
|
||||
if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia {
|
||||
switch extendedMedia {
|
||||
case let .preview(_, immediateThumbnailData, _):
|
||||
let thumbnailMedia = TelegramMediaImage(imageId: MediaId(namespace: 0, id: 0), representations: [], immediateThumbnailData: immediateThumbnailData, reference: nil, partialReference: nil, flags: [])
|
||||
media = thumbnailMedia
|
||||
case let .full(fullMedia):
|
||||
media = fullMedia
|
||||
}
|
||||
}
|
||||
|
||||
if let image = media as? TelegramMediaImage {
|
||||
if hasCurrentVideoNode {
|
||||
replaceVideoNode = true
|
||||
@ -830,6 +974,11 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
|
||||
if statusUpdated {
|
||||
var media = media
|
||||
if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia, case let .full(fullMedia) = extendedMedia {
|
||||
media = fullMedia
|
||||
}
|
||||
|
||||
if let image = media as? TelegramMediaImage {
|
||||
if message.flags.isSending {
|
||||
updatedStatusSignal = combineLatest(chatMessagePhotoStatus(context: context, messageId: message.id, photoReference: .message(message: MessageReference(message), media: image)), context.account.pendingMessageManager.pendingMessageStatus(message.id) |> map { $0.0 })
|
||||
@ -869,9 +1018,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
let arguments = TransformImageArguments(corners: corners, imageSize: drawingSize, boundingSize: boundingSize, intrinsicInsets: UIEdgeInsets(), resizeMode: isInlinePlayableVideo ? .fill(.black) : .blurBackground, emptyColor: emptyColor, custom: patternArguments)
|
||||
|
||||
let imageFrame = CGRect(origin: CGPoint(x: -arguments.insets.left, y: -arguments.insets.top), size: arguments.drawingSize).ensuredValid
|
||||
@ -1089,6 +1236,12 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
|
||||
if let updatedFetchControls = updatedFetchControls {
|
||||
let _ = strongSelf.fetchControls.swap(updatedFetchControls)
|
||||
|
||||
var media = media
|
||||
if let invoice = media as? TelegramMediaInvoice, let extendedMedia = invoice.extendedMedia, case let .full(fullMedia) = extendedMedia {
|
||||
media = fullMedia
|
||||
}
|
||||
|
||||
if case .full = automaticDownload {
|
||||
if let _ = media as? TelegramMediaImage {
|
||||
updatedFetchControls.fetch(false)
|
||||
@ -1131,7 +1284,7 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
|
||||
strongSelf.updateStatus(animated: synchronousLoads)
|
||||
|
||||
strongSelf.pinchContainerNode.isPinchGestureEnabled = !isSecretMedia
|
||||
strongSelf.pinchContainerNode.isPinchGestureEnabled = !isSecretMedia && !isExtendedMediaPreview
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -1250,23 +1403,29 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
var mediaDownloadState: ChatMessageInteractiveMediaDownloadState?
|
||||
let messageTheme = theme.chat.message
|
||||
if let invoice = invoice {
|
||||
let string = NSMutableAttributedString()
|
||||
if invoice.receiptMessageId != nil {
|
||||
var title = strings.Checkout_Receipt_Title.uppercased()
|
||||
if invoice.flags.contains(.isTest) {
|
||||
title += " (Test)"
|
||||
if let extendedMedia = invoice.extendedMedia {
|
||||
if case let .preview(_, _, maybeVideoDuration) = extendedMedia, let videoDuration = maybeVideoDuration {
|
||||
badgeContent = .text(inset: 0.0, backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, text: NSAttributedString(string: stringForDuration(videoDuration, position: nil)))
|
||||
}
|
||||
string.append(NSAttributedString(string: title))
|
||||
} else {
|
||||
string.append(NSAttributedString(string: "\(formatCurrencyAmount(invoice.totalAmount, currency: invoice.currency)) ", attributes: [ChatTextInputAttributes.bold: true as NSNumber]))
|
||||
|
||||
var title = strings.Message_InvoiceLabel
|
||||
if invoice.flags.contains(.isTest) {
|
||||
title += " (Test)"
|
||||
let string = NSMutableAttributedString()
|
||||
if invoice.receiptMessageId != nil {
|
||||
var title = strings.Checkout_Receipt_Title.uppercased()
|
||||
if invoice.flags.contains(.isTest) {
|
||||
title += " (Test)"
|
||||
}
|
||||
string.append(NSAttributedString(string: title))
|
||||
} else {
|
||||
string.append(NSAttributedString(string: "\(formatCurrencyAmount(invoice.totalAmount, currency: invoice.currency)) ", attributes: [ChatTextInputAttributes.bold: true as NSNumber]))
|
||||
|
||||
var title = strings.Message_InvoiceLabel
|
||||
if invoice.flags.contains(.isTest) {
|
||||
title += " (Test)"
|
||||
}
|
||||
string.append(NSAttributedString(string: title))
|
||||
}
|
||||
string.append(NSAttributedString(string: title))
|
||||
badgeContent = .text(inset: 0.0, backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, text: string)
|
||||
}
|
||||
badgeContent = .text(inset: 0.0, backgroundColor: messageTheme.mediaDateAndStatusFillColor, foregroundColor: messageTheme.mediaDateAndStatusTextColor, text: string)
|
||||
}
|
||||
var animated = animated
|
||||
if let updatingMedia = attributes.updatingMedia, case .update = updatingMedia.media {
|
||||
@ -1513,7 +1672,15 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
if let badgeContent = badgeContent {
|
||||
if self.badgeNode == nil {
|
||||
let badgeNode = ChatMessageInteractiveMediaBadge()
|
||||
badgeNode.frame = CGRect(origin: CGPoint(x: 6.0, y: 6.0), size: CGSize(width: radialStatusSize, height: radialStatusSize))
|
||||
|
||||
let incoming: Bool
|
||||
if let context = self.context, let message = self.message {
|
||||
incoming = message.effectivelyIncoming(context.account.peerId)
|
||||
} else {
|
||||
incoming = false
|
||||
}
|
||||
|
||||
badgeNode.frame = CGRect(origin: CGPoint(x: incoming ? 10.0 : 6.0, y: 6.0), size: CGSize(width: radialStatusSize, height: radialStatusSize))
|
||||
badgeNode.pressed = { [weak self] in
|
||||
guard let strongSelf = self, let fetchStatus = strongSelf.fetchStatus else {
|
||||
return
|
||||
@ -1536,6 +1703,36 @@ final class ChatMessageInteractiveMediaNode: ASDisplayNode, GalleryItemTransitio
|
||||
badgeNode.removeFromSupernode()
|
||||
}
|
||||
|
||||
if let invoice = invoice, let extendedMedia = invoice.extendedMedia, case .preview = extendedMedia {
|
||||
if self.extendedMediaOverlayNode == nil {
|
||||
let extendedMediaOverlayNode = ExtendedMediaOverlayNode()
|
||||
self.extendedMediaOverlayNode = extendedMediaOverlayNode
|
||||
self.pinchContainerNode.contentNode.insertSubnode(extendedMediaOverlayNode, aboveSubnode: self.imageNode)
|
||||
}
|
||||
self.extendedMediaOverlayNode?.frame = self.imageNode.frame
|
||||
|
||||
var paymentText: String = ""
|
||||
outer: for attribute in message.attributes {
|
||||
if let attribute = attribute as? ReplyMarkupMessageAttribute {
|
||||
for row in attribute.rows {
|
||||
for button in row.buttons {
|
||||
if case .payment = button.action {
|
||||
paymentText = button.title
|
||||
break outer
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
self.extendedMediaOverlayNode?.update(size: self.imageNode.frame.size, text: paymentText)
|
||||
} else if let extendedMediaOverlayNode = self.extendedMediaOverlayNode {
|
||||
self.extendedMediaOverlayNode = nil
|
||||
extendedMediaOverlayNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak extendedMediaOverlayNode] _ in
|
||||
extendedMediaOverlayNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
||||
if isSecretMedia, secretBeginTimeAndTimeout?.0 != nil {
|
||||
if self.secretTimer == nil {
|
||||
self.secretTimer = SwiftSignalKit.Timer(timeout: 0.3, repeat: true, completion: { [weak self] in
|
||||
|
@ -114,6 +114,39 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
}
|
||||
}
|
||||
contentMode = .aspectFill
|
||||
} else if let invoice = media as? TelegramMediaInvoice {
|
||||
selectedMedia = invoice
|
||||
|
||||
if let extendedMedia = invoice.extendedMedia, case let .full(media) = extendedMedia {
|
||||
if let telegramImage = media as? TelegramMediaImage {
|
||||
if shouldDownloadMediaAutomatically(settings: item.controllerInteraction.automaticMediaDownloadSettings, peerType: item.associatedData.automaticDownloadPeerType, networkType: item.associatedData.automaticDownloadNetworkType, authorPeerId: item.message.author?.id, contactsPeerIds: item.associatedData.contactsPeerIds, media: telegramImage) {
|
||||
automaticDownload = .full
|
||||
}
|
||||
} else if let telegramFile = media as? TelegramMediaFile {
|
||||
if shouldDownloadMediaAutomatically(settings: item.controllerInteraction.automaticMediaDownloadSettings, peerType: item.associatedData.automaticDownloadPeerType, networkType: item.associatedData.automaticDownloadNetworkType, authorPeerId: item.message.author?.id, contactsPeerIds: item.associatedData.contactsPeerIds, media: telegramFile) {
|
||||
automaticDownload = .full
|
||||
} else if shouldPredownloadMedia(settings: item.controllerInteraction.automaticMediaDownloadSettings, peerType: item.associatedData.automaticDownloadPeerType, networkType: item.associatedData.automaticDownloadNetworkType, media: telegramFile) {
|
||||
automaticDownload = .prefetch
|
||||
}
|
||||
|
||||
if !item.message.containsSecretMedia {
|
||||
if telegramFile.isAnimated && item.controllerInteraction.automaticMediaDownloadSettings.autoplayGifs {
|
||||
if case .full = automaticDownload {
|
||||
automaticPlayback = true
|
||||
} else {
|
||||
automaticPlayback = item.context.account.postbox.mediaBox.completedResourcePath(telegramFile.resource) != nil
|
||||
}
|
||||
} else if (telegramFile.isVideo && !telegramFile.isAnimated) && item.controllerInteraction.automaticMediaDownloadSettings.autoplayVideos {
|
||||
if case .full = automaticDownload {
|
||||
automaticPlayback = true
|
||||
} else {
|
||||
automaticPlayback = item.context.account.postbox.mediaBox.completedResourcePath(telegramFile.resource) != nil
|
||||
}
|
||||
}
|
||||
}
|
||||
contentMode = .aspectFill
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,7 +154,33 @@ class ChatMessageMediaBubbleContentNode: ChatMessageBubbleContentNode {
|
||||
var hasReplyMarkup: Bool = false
|
||||
for attribute in item.message.attributes {
|
||||
if let attribute = attribute as? ReplyMarkupMessageAttribute, attribute.flags.contains(.inline), !attribute.rows.isEmpty {
|
||||
hasReplyMarkup = true
|
||||
var isExtendedMedia = false
|
||||
for media in item.message.media {
|
||||
if let invoice = media as? TelegramMediaInvoice, let _ = invoice.extendedMedia {
|
||||
isExtendedMedia = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if isExtendedMedia {
|
||||
var updatedRows: [ReplyMarkupRow] = []
|
||||
for row in attribute.rows {
|
||||
let updatedButtons = row.buttons.filter { button in
|
||||
if case .payment = button.action {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if !updatedButtons.isEmpty {
|
||||
updatedRows.append(ReplyMarkupRow(buttons: updatedButtons))
|
||||
}
|
||||
}
|
||||
if !updatedRows.isEmpty {
|
||||
hasReplyMarkup = true
|
||||
}
|
||||
} else {
|
||||
hasReplyMarkup = true
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user