mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-11-06 17:00:13 +00:00
Update API [skip ci]
This commit is contained in:
parent
b0511f146e
commit
b6f8cce7ea
@ -13651,6 +13651,21 @@ Sorry for the inconvenience.";
|
||||
|
||||
"Camera.OpenChat" = "Open Chat";
|
||||
|
||||
"Gift.Wear.Wear" = "Wear %@";
|
||||
"Gift.Wear.GetBenefits" = "and get these benefits:";
|
||||
"Gift.Wear.Badge.Title" = "Radiant Badge";
|
||||
"Gift.Wear.Badge.Text" = "The glittering icon of this item will be displayed next to your name.";
|
||||
"Gift.Wear.Design.Title" = "Unqiue Profile Design";
|
||||
"Gift.Wear.Design.Text" = "Your profile page will get the color and the symbol of this item.";
|
||||
"Gift.Wear.Proof.Title" = "Proof of Ownership";
|
||||
"Gift.Wear.Proof.Text" = "Tapping the icon of this item next to your name will show its info and owner.";
|
||||
"Gift.Wear.Start" = "Start Wearing";
|
||||
|
||||
"Gift.View.Header.Transfer" = "transfer";
|
||||
"Gift.View.Header.Wear" = "wear";
|
||||
"Gift.View.Header.TakeOff" = "take off";
|
||||
"Gift.View.Header.Share" = "share";
|
||||
|
||||
"Conversation.AddToContactsLong" = "Add to Contacts";
|
||||
|
||||
"PeerInfo.PaneRecommendedBots" = "Similar Bots";
|
||||
|
||||
@ -2145,6 +2145,7 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
var currentCredibilityIconContent: EmojiStatusComponent.Content?
|
||||
var currentVerifiedIconContent: EmojiStatusComponent.Content?
|
||||
var currentStatusIconContent: EmojiStatusComponent.Content?
|
||||
var currentStatusIconParticleColor: UIColor?
|
||||
var currentSecretIconImage: UIImage?
|
||||
var currentForwardedIcon: UIImage?
|
||||
var currentStoryIcon: UIImage?
|
||||
@ -3102,6 +3103,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
|
||||
} else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
|
||||
currentStatusIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
|
||||
if case let .starGift(_, _, _, _, _, innerColor, _, _, _) = emojiStatus.content {
|
||||
currentStatusIconParticleColor = UIColor(rgb: UInt32(bitPattern: innerColor))
|
||||
}
|
||||
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||
currentCredibilityIconContent = .premium(color: item.presentationData.theme.list.itemAccentColor)
|
||||
}
|
||||
@ -3130,6 +3134,9 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
currentCredibilityIconContent = .text(color: item.presentationData.theme.chat.message.incoming.scamColor, string: item.presentationData.strings.Message_FakeAccount.uppercased())
|
||||
} else if let emojiStatus = peer.emojiStatus, !premiumConfiguration.isPremiumDisabled {
|
||||
currentStatusIconContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor, themeColor: item.presentationData.theme.list.itemAccentColor, loopMode: .count(2))
|
||||
if case let .starGift(_, _, _, _, _, innerColor, _, _, _) = emojiStatus.content {
|
||||
currentStatusIconParticleColor = UIColor(rgb: UInt32(bitPattern: innerColor))
|
||||
}
|
||||
} else if peer.isPremium && !premiumConfiguration.isPremiumDisabled {
|
||||
currentCredibilityIconContent = .premium(color: item.presentationData.theme.list.itemAccentColor)
|
||||
}
|
||||
@ -4543,12 +4550,13 @@ public class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.statusIconView = statusIconView
|
||||
strongSelf.mainContentContainerNode.view.addSubview(statusIconView)
|
||||
}
|
||||
|
||||
|
||||
let statusIconComponent = EmojiStatusComponent(
|
||||
context: item.context,
|
||||
animationCache: item.interaction.animationCache,
|
||||
animationRenderer: item.interaction.animationRenderer,
|
||||
content: currentStatusIconContent,
|
||||
particleColor: currentStatusIconParticleColor,
|
||||
isVisibleForAnimations: strongSelf.visibilityStatus && item.context.sharedContext.energyUsageSettings.loopEmoji,
|
||||
action: nil
|
||||
)
|
||||
|
||||
@ -267,9 +267,10 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1275374751] = { return Api.EmojiLanguage.parse_emojiLanguage($0) }
|
||||
dict[2048790993] = { return Api.EmojiList.parse_emojiList($0) }
|
||||
dict[1209970170] = { return Api.EmojiList.parse_emojiListNotModified($0) }
|
||||
dict[-1835310691] = { return Api.EmojiStatus.parse_emojiStatus($0) }
|
||||
dict[-402717046] = { return Api.EmojiStatus.parse_emojiStatus($0) }
|
||||
dict[1904500795] = { return Api.EmojiStatus.parse_emojiStatusCollectible($0) }
|
||||
dict[769727150] = { return Api.EmojiStatus.parse_emojiStatusEmpty($0) }
|
||||
dict[-97474361] = { return Api.EmojiStatus.parse_emojiStatusUntil($0) }
|
||||
dict[118758847] = { return Api.EmojiStatus.parse_inputEmojiStatusCollectible($0) }
|
||||
dict[-1519029347] = { return Api.EmojiURL.parse_emojiURL($0) }
|
||||
dict[1643173063] = { return Api.EncryptedChat.parse_encryptedChat($0) }
|
||||
dict[505183301] = { return Api.EncryptedChat.parse_encryptedChatDiscarded($0) }
|
||||
@ -388,7 +389,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1710230755] = { return Api.InputInvoice.parse_inputInvoiceStars($0) }
|
||||
dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) }
|
||||
dict[-428884101] = { return Api.InputMedia.parse_inputMediaDice($0) }
|
||||
dict[860303448] = { return Api.InputMedia.parse_inputMediaDocument($0) }
|
||||
dict[1946579745] = { return Api.InputMedia.parse_inputMediaDocument($0) }
|
||||
dict[-78455655] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) }
|
||||
dict[-1771768449] = { return Api.InputMedia.parse_inputMediaEmpty($0) }
|
||||
dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($0) }
|
||||
@ -400,7 +401,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) }
|
||||
dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) }
|
||||
dict[-1979852936] = { return Api.InputMedia.parse_inputMediaStory($0) }
|
||||
dict[1530447553] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
|
||||
dict[-264125395] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
|
||||
dict[505969924] = { return Api.InputMedia.parse_inputMediaUploadedPhoto($0) }
|
||||
dict[-1052959727] = { return Api.InputMedia.parse_inputMediaVenue($0) }
|
||||
dict[-1038383031] = { return Api.InputMedia.parse_inputMediaWebPage($0) }
|
||||
@ -617,7 +618,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[1313731771] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) }
|
||||
dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) }
|
||||
dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) }
|
||||
dict[-581497899] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
|
||||
dict[1838230743] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
|
||||
dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) }
|
||||
dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) }
|
||||
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }
|
||||
|
||||
@ -428,7 +428,7 @@ public extension Api {
|
||||
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?)
|
||||
case inputMediaDocument(flags: Int32, id: Api.InputDocument, videoCover: Api.InputPhoto?, ttlSeconds: Int32?, query: String?)
|
||||
case inputMediaDocumentExternal(flags: Int32, url: String, ttlSeconds: Int32?)
|
||||
case inputMediaEmpty
|
||||
case inputMediaGame(id: Api.InputGame)
|
||||
@ -440,7 +440,7 @@ public extension Api {
|
||||
case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?)
|
||||
case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?)
|
||||
case inputMediaStory(peer: Api.InputPeer, id: Int32)
|
||||
case inputMediaUploadedDocument(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, mimeType: String, attributes: [Api.DocumentAttribute], stickers: [Api.InputDocument]?, ttlSeconds: Int32?)
|
||||
case inputMediaUploadedDocument(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, mimeType: String, attributes: [Api.DocumentAttribute], stickers: [Api.InputDocument]?, videoCover: Api.InputPhoto?, ttlSeconds: Int32?)
|
||||
case inputMediaUploadedPhoto(flags: Int32, file: Api.InputFile, stickers: [Api.InputDocument]?, ttlSeconds: Int32?)
|
||||
case inputMediaVenue(geoPoint: Api.InputGeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String)
|
||||
case inputMediaWebPage(flags: Int32, url: String)
|
||||
@ -462,12 +462,13 @@ public extension Api {
|
||||
}
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputMediaDocument(let flags, let id, let ttlSeconds, let query):
|
||||
case .inputMediaDocument(let flags, let id, let videoCover, let ttlSeconds, let query):
|
||||
if boxed {
|
||||
buffer.appendInt32(860303448)
|
||||
buffer.appendInt32(1946579745)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
id.serialize(buffer, true)
|
||||
if Int(flags) & Int(1 << 3) != 0 {videoCover!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(query!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
@ -576,9 +577,9 @@ public extension Api {
|
||||
peer.serialize(buffer, true)
|
||||
serializeInt32(id, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .inputMediaUploadedDocument(let flags, let file, let thumb, let mimeType, let attributes, let stickers, let ttlSeconds):
|
||||
case .inputMediaUploadedDocument(let flags, let file, let thumb, let mimeType, let attributes, let stickers, let videoCover, let ttlSeconds):
|
||||
if boxed {
|
||||
buffer.appendInt32(1530447553)
|
||||
buffer.appendInt32(-264125395)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
file.serialize(buffer, true)
|
||||
@ -594,6 +595,7 @@ public extension Api {
|
||||
for item in stickers! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 6) != 0 {videoCover!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .inputMediaUploadedPhoto(let flags, let file, let stickers, let ttlSeconds):
|
||||
@ -636,8 +638,8 @@ public extension Api {
|
||||
return ("inputMediaContact", [("phoneNumber", phoneNumber as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("vcard", vcard as Any)])
|
||||
case .inputMediaDice(let emoticon):
|
||||
return ("inputMediaDice", [("emoticon", emoticon as Any)])
|
||||
case .inputMediaDocument(let flags, let id, let ttlSeconds, let query):
|
||||
return ("inputMediaDocument", [("flags", flags as Any), ("id", id as Any), ("ttlSeconds", ttlSeconds as Any), ("query", query as Any)])
|
||||
case .inputMediaDocument(let flags, let id, let videoCover, let ttlSeconds, let query):
|
||||
return ("inputMediaDocument", [("flags", flags as Any), ("id", id as Any), ("videoCover", videoCover as Any), ("ttlSeconds", ttlSeconds as Any), ("query", query as Any)])
|
||||
case .inputMediaDocumentExternal(let flags, let url, let ttlSeconds):
|
||||
return ("inputMediaDocumentExternal", [("flags", flags as Any), ("url", url as Any), ("ttlSeconds", ttlSeconds as Any)])
|
||||
case .inputMediaEmpty:
|
||||
@ -660,8 +662,8 @@ public extension Api {
|
||||
return ("inputMediaPoll", [("flags", flags as Any), ("poll", poll as Any), ("correctAnswers", correctAnswers as Any), ("solution", solution as Any), ("solutionEntities", solutionEntities as Any)])
|
||||
case .inputMediaStory(let peer, let id):
|
||||
return ("inputMediaStory", [("peer", peer as Any), ("id", id as Any)])
|
||||
case .inputMediaUploadedDocument(let flags, let file, let thumb, let mimeType, let attributes, let stickers, let ttlSeconds):
|
||||
return ("inputMediaUploadedDocument", [("flags", flags as Any), ("file", file as Any), ("thumb", thumb as Any), ("mimeType", mimeType as Any), ("attributes", attributes as Any), ("stickers", stickers as Any), ("ttlSeconds", ttlSeconds as Any)])
|
||||
case .inputMediaUploadedDocument(let flags, let file, let thumb, let mimeType, let attributes, let stickers, let videoCover, let ttlSeconds):
|
||||
return ("inputMediaUploadedDocument", [("flags", flags as Any), ("file", file as Any), ("thumb", thumb as Any), ("mimeType", mimeType as Any), ("attributes", attributes as Any), ("stickers", stickers as Any), ("videoCover", videoCover as Any), ("ttlSeconds", ttlSeconds as Any)])
|
||||
case .inputMediaUploadedPhoto(let flags, let file, let stickers, let ttlSeconds):
|
||||
return ("inputMediaUploadedPhoto", [("flags", flags as Any), ("file", file as Any), ("stickers", stickers as Any), ("ttlSeconds", ttlSeconds as Any)])
|
||||
case .inputMediaVenue(let geoPoint, let title, let address, let provider, let venueId, let venueType):
|
||||
@ -709,16 +711,21 @@ public extension Api {
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.InputDocument
|
||||
}
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
var _4: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_4 = parseString(reader) }
|
||||
var _3: Api.InputPhoto?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
||||
_3 = Api.parse(reader, signature: signature) as? Api.InputPhoto
|
||||
} }
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() }
|
||||
var _5: String?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.InputMedia.inputMediaDocument(flags: _1!, id: _2!, ttlSeconds: _3, query: _4)
|
||||
let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.InputMedia.inputMediaDocument(flags: _1!, id: _2!, videoCover: _3, ttlSeconds: _4, query: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -965,17 +972,22 @@ public extension Api {
|
||||
if Int(_1!) & Int(1 << 0) != 0 {if let _ = reader.readInt32() {
|
||||
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.InputDocument.self)
|
||||
} }
|
||||
var _7: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_7 = reader.readInt32() }
|
||||
var _7: Api.InputPhoto?
|
||||
if Int(_1!) & Int(1 << 6) != 0 {if let signature = reader.readInt32() {
|
||||
_7 = Api.parse(reader, signature: signature) as? Api.InputPhoto
|
||||
} }
|
||||
var _8: Int32?
|
||||
if Int(_1!) & Int(1 << 1) != 0 {_8 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||
let _c7 = (Int(_1!) & Int(1 << 1) == 0) || _7 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
||||
return Api.InputMedia.inputMediaUploadedDocument(flags: _1!, file: _2!, thumb: _3, mimeType: _4!, attributes: _5!, stickers: _6, ttlSeconds: _7)
|
||||
let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil
|
||||
let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
|
||||
return Api.InputMedia.inputMediaUploadedDocument(flags: _1!, file: _2!, thumb: _3, mimeType: _4!, attributes: _5!, stickers: _6, videoCover: _7, ttlSeconds: _8)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
||||
@ -710,7 +710,7 @@ public extension Api {
|
||||
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?, altDocuments: [Api.Document]?, ttlSeconds: Int32?)
|
||||
case messageMediaDocument(flags: Int32, document: Api.Document?, altDocuments: [Api.Document]?, coverPhoto: Api.Photo?, ttlSeconds: Int32?)
|
||||
case messageMediaEmpty
|
||||
case messageMediaGame(game: Api.Game)
|
||||
case messageMediaGeo(geo: Api.GeoPoint)
|
||||
@ -745,9 +745,9 @@ public extension Api {
|
||||
serializeInt32(value, buffer: buffer, boxed: false)
|
||||
serializeString(emoticon, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .messageMediaDocument(let flags, let document, let altDocuments, let ttlSeconds):
|
||||
case .messageMediaDocument(let flags, let document, let altDocuments, let coverPhoto, let ttlSeconds):
|
||||
if boxed {
|
||||
buffer.appendInt32(-581497899)
|
||||
buffer.appendInt32(1838230743)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {document!.serialize(buffer, true)}
|
||||
@ -756,6 +756,7 @@ public extension Api {
|
||||
for item in altDocuments! {
|
||||
item.serialize(buffer, true)
|
||||
}}
|
||||
if Int(flags) & Int(1 << 9) != 0 {coverPhoto!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .messageMediaEmpty:
|
||||
@ -909,8 +910,8 @@ public extension Api {
|
||||
return ("messageMediaContact", [("phoneNumber", phoneNumber as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("vcard", vcard as Any), ("userId", userId as Any)])
|
||||
case .messageMediaDice(let value, let emoticon):
|
||||
return ("messageMediaDice", [("value", value as Any), ("emoticon", emoticon as Any)])
|
||||
case .messageMediaDocument(let flags, let document, let altDocuments, let ttlSeconds):
|
||||
return ("messageMediaDocument", [("flags", flags as Any), ("document", document as Any), ("altDocuments", altDocuments as Any), ("ttlSeconds", ttlSeconds as Any)])
|
||||
case .messageMediaDocument(let flags, let document, let altDocuments, let coverPhoto, let ttlSeconds):
|
||||
return ("messageMediaDocument", [("flags", flags as Any), ("document", document as Any), ("altDocuments", altDocuments as Any), ("coverPhoto", coverPhoto as Any), ("ttlSeconds", ttlSeconds as Any)])
|
||||
case .messageMediaEmpty:
|
||||
return ("messageMediaEmpty", [])
|
||||
case .messageMediaGame(let game):
|
||||
@ -990,14 +991,19 @@ public extension Api {
|
||||
if Int(_1!) & Int(1 << 5) != 0 {if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
} }
|
||||
var _4: Int32?
|
||||
if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() }
|
||||
var _4: Api.Photo?
|
||||
if Int(_1!) & Int(1 << 9) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.Photo
|
||||
} }
|
||||
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 << 5) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.MessageMedia.messageMediaDocument(flags: _1!, document: _2, altDocuments: _3, ttlSeconds: _4)
|
||||
let _c4 = (Int(_1!) & Int(1 << 9) == 0) || _4 != nil
|
||||
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.MessageMedia.messageMediaDocument(flags: _1!, document: _2, altDocuments: _3, coverPhoto: _4, ttlSeconds: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
||||
@ -464,6 +464,21 @@ public extension Api.functions.account {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.account {
|
||||
static func getCollectibleEmojiStatuses(hash: Int64) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.account.EmojiStatuses>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(779830595)
|
||||
serializeInt64(hash, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "account.getCollectibleEmojiStatuses", parameters: [("hash", String(describing: hash))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.account.EmojiStatuses? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.account.EmojiStatuses?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.account.EmojiStatuses
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.account {
|
||||
static func getConnectedBots() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.account.ConnectedBots>) {
|
||||
let buffer = Buffer()
|
||||
|
||||
@ -796,17 +796,36 @@ public extension Api {
|
||||
}
|
||||
public extension Api {
|
||||
enum EmojiStatus: TypeConstructorDescription {
|
||||
case emojiStatus(documentId: Int64)
|
||||
case emojiStatus(flags: Int32, documentId: Int64, until: Int32?)
|
||||
case emojiStatusCollectible(flags: Int32, collectibleId: Int64, documentId: Int64, title: String, slug: String, patternDocumentId: Int64, centerColor: Int32, edgeColor: Int32, patternColor: Int32, textColor: Int32, until: Int32?)
|
||||
case emojiStatusEmpty
|
||||
case emojiStatusUntil(documentId: Int64, until: Int32)
|
||||
case inputEmojiStatusCollectible(flags: Int32, collectibleId: Int64, until: Int32?)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .emojiStatus(let documentId):
|
||||
case .emojiStatus(let flags, let documentId, let until):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1835310691)
|
||||
buffer.appendInt32(-402717046)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(documentId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(until!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .emojiStatusCollectible(let flags, let collectibleId, let documentId, let title, let slug, let patternDocumentId, let centerColor, let edgeColor, let patternColor, let textColor, let until):
|
||||
if boxed {
|
||||
buffer.appendInt32(1904500795)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(collectibleId, buffer: buffer, boxed: false)
|
||||
serializeInt64(documentId, buffer: buffer, boxed: false)
|
||||
serializeString(title, buffer: buffer, boxed: false)
|
||||
serializeString(slug, buffer: buffer, boxed: false)
|
||||
serializeInt64(patternDocumentId, buffer: buffer, boxed: false)
|
||||
serializeInt32(centerColor, buffer: buffer, boxed: false)
|
||||
serializeInt32(edgeColor, buffer: buffer, boxed: false)
|
||||
serializeInt32(patternColor, buffer: buffer, boxed: false)
|
||||
serializeInt32(textColor, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(until!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .emojiStatusEmpty:
|
||||
if boxed {
|
||||
@ -814,33 +833,83 @@ public extension Api {
|
||||
}
|
||||
|
||||
break
|
||||
case .emojiStatusUntil(let documentId, let until):
|
||||
case .inputEmojiStatusCollectible(let flags, let collectibleId, let until):
|
||||
if boxed {
|
||||
buffer.appendInt32(-97474361)
|
||||
buffer.appendInt32(118758847)
|
||||
}
|
||||
serializeInt64(documentId, buffer: buffer, boxed: false)
|
||||
serializeInt32(until, buffer: buffer, boxed: false)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
serializeInt64(collectibleId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(until!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .emojiStatus(let documentId):
|
||||
return ("emojiStatus", [("documentId", documentId as Any)])
|
||||
case .emojiStatus(let flags, let documentId, let until):
|
||||
return ("emojiStatus", [("flags", flags as Any), ("documentId", documentId as Any), ("until", until as Any)])
|
||||
case .emojiStatusCollectible(let flags, let collectibleId, let documentId, let title, let slug, let patternDocumentId, let centerColor, let edgeColor, let patternColor, let textColor, let until):
|
||||
return ("emojiStatusCollectible", [("flags", flags as Any), ("collectibleId", collectibleId as Any), ("documentId", documentId as Any), ("title", title as Any), ("slug", slug as Any), ("patternDocumentId", patternDocumentId as Any), ("centerColor", centerColor as Any), ("edgeColor", edgeColor as Any), ("patternColor", patternColor as Any), ("textColor", textColor as Any), ("until", until as Any)])
|
||||
case .emojiStatusEmpty:
|
||||
return ("emojiStatusEmpty", [])
|
||||
case .emojiStatusUntil(let documentId, let until):
|
||||
return ("emojiStatusUntil", [("documentId", documentId as Any), ("until", until as Any)])
|
||||
case .inputEmojiStatusCollectible(let flags, let collectibleId, let until):
|
||||
return ("inputEmojiStatusCollectible", [("flags", flags as Any), ("collectibleId", collectibleId as Any), ("until", until as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_emojiStatus(_ reader: BufferReader) -> EmojiStatus? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.EmojiStatus.emojiStatus(documentId: _1!)
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.EmojiStatus.emojiStatus(flags: _1!, documentId: _2!, until: _3)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_emojiStatusCollectible(_ reader: BufferReader) -> EmojiStatus? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int64?
|
||||
_3 = reader.readInt64()
|
||||
var _4: String?
|
||||
_4 = parseString(reader)
|
||||
var _5: String?
|
||||
_5 = parseString(reader)
|
||||
var _6: Int64?
|
||||
_6 = reader.readInt64()
|
||||
var _7: Int32?
|
||||
_7 = reader.readInt32()
|
||||
var _8: Int32?
|
||||
_8 = reader.readInt32()
|
||||
var _9: Int32?
|
||||
_9 = reader.readInt32()
|
||||
var _10: Int32?
|
||||
_10 = reader.readInt32()
|
||||
var _11: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_11 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
let _c5 = _5 != nil
|
||||
let _c6 = _6 != nil
|
||||
let _c7 = _7 != nil
|
||||
let _c8 = _8 != nil
|
||||
let _c9 = _9 != nil
|
||||
let _c10 = _10 != nil
|
||||
let _c11 = (Int(_1!) & Int(1 << 0) == 0) || _11 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 {
|
||||
return Api.EmojiStatus.emojiStatusCollectible(flags: _1!, collectibleId: _2!, documentId: _3!, title: _4!, slug: _5!, patternDocumentId: _6!, centerColor: _7!, edgeColor: _8!, patternColor: _9!, textColor: _10!, until: _11)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
@ -849,15 +918,18 @@ public extension Api {
|
||||
public static func parse_emojiStatusEmpty(_ reader: BufferReader) -> EmojiStatus? {
|
||||
return Api.EmojiStatus.emojiStatusEmpty
|
||||
}
|
||||
public static func parse_emojiStatusUntil(_ reader: BufferReader) -> EmojiStatus? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
public static func parse_inputEmojiStatusCollectible(_ reader: BufferReader) -> EmojiStatus? {
|
||||
var _1: Int32?
|
||||
_1 = reader.readInt32()
|
||||
var _2: Int64?
|
||||
_2 = reader.readInt64()
|
||||
var _3: Int32?
|
||||
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.EmojiStatus.emojiStatusUntil(documentId: _1!, until: _2!)
|
||||
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.EmojiStatus.inputEmojiStatusCollectible(flags: _1!, collectibleId: _2!, until: _3)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
||||
@ -350,7 +350,8 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
|
||||
case let .messageMediaGeoLive(_, geo, heading, period, proximityNotificationRadius):
|
||||
let mediaMap = telegramMediaMapFromApiGeoPoint(geo, title: nil, address: nil, provider: nil, venueId: nil, venueType: nil, liveBroadcastingTimeout: period, liveProximityNotificationRadius: proximityNotificationRadius, heading: heading)
|
||||
return (mediaMap, nil, nil, nil, nil)
|
||||
case let .messageMediaDocument(flags, document, altDocuments, ttlSeconds):
|
||||
case let .messageMediaDocument(flags, document, altDocuments, coverPhoto, ttlSeconds):
|
||||
let _ = coverPhoto
|
||||
if let document = document {
|
||||
if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments) {
|
||||
return (mediaFile, ttlSeconds, (flags & (1 << 3)) != 0, (flags & (1 << 4)) != 0, nil)
|
||||
|
||||
@ -229,7 +229,7 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post
|
||||
}
|
||||
|> mapToSignal { validatedResource -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
if let validatedResource = validatedResource.updatedResource as? TelegramCloudMediaResourceWithFileReference, let reference = validatedResource.fileReference {
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: reference)), ttlSeconds: nil, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: 0, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: reference)), videoCover: nil, ttlSeconds: nil, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
} else {
|
||||
return .fail(.generic)
|
||||
}
|
||||
@ -246,7 +246,7 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post
|
||||
}
|
||||
}
|
||||
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil, query: emojiSearchQuery), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), videoCover: nil, ttlSeconds: nil, query: emojiSearchQuery), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
}
|
||||
} else {
|
||||
return uploadedMediaFileContent(network: network, postbox: postbox, auxiliaryMethods: auxiliaryMethods, transformOutgoingMessageMedia: transformOutgoingMessageMedia, messageMediaPreuploadManager: messageMediaPreuploadManager, forceReupload: forceReupload, isGrouped: isGrouped, isPaid: false, passFetchProgress: passFetchProgress, forceNoBigParts: forceNoBigParts, peerId: peerId, messageId: messageId, text: text, attributes: attributes, autoremoveMessageAttribute: autoremoveMessageAttribute, autoclearMessageAttribute: autoclearMessageAttribute, file: file)
|
||||
@ -828,7 +828,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
|
||||
return .single(.progress(PendingMessageUploadedContentProgress(progress: 1.0)))
|
||||
|> then(
|
||||
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
.single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaDocument(flags: flags, id: Api.InputDocument.inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), videoCover: nil, ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
|
||||
)
|
||||
}
|
||||
referenceKey = key
|
||||
@ -1049,11 +1049,11 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
}
|
||||
|
||||
if ttlSeconds != nil {
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: referenceKey)))
|
||||
return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, videoCover: nil, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: referenceKey)))
|
||||
}
|
||||
|
||||
if !isGrouped {
|
||||
let resultInfo = PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: referenceKey)
|
||||
let resultInfo = PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, videoCover: nil, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: referenceKey)
|
||||
|
||||
return .single(.content(resultInfo))
|
||||
}
|
||||
@ -1064,11 +1064,11 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
|> mapError { _ -> PendingMessageUploadError in }
|
||||
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
if let inputPeer = inputPeer {
|
||||
return network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, ttlSeconds: ttlSeconds)))
|
||||
return network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: .inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, videoCover: nil, ttlSeconds: ttlSeconds)))
|
||||
|> mapError { _ -> PendingMessageUploadError in return .generic }
|
||||
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
|
||||
switch result {
|
||||
case let .messageMediaDocument(_, document, altDocuments, _):
|
||||
case let .messageMediaDocument(_, document, altDocuments, _, _):
|
||||
if let document = document, let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments), let resource = mediaFile.resource as? CloudDocumentMediaResource, let fileReference = resource.fileReference {
|
||||
var flags: Int32 = 0
|
||||
var ttlSeconds: Int32?
|
||||
@ -1080,7 +1080,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|
||||
flags |= (1 << 2)
|
||||
}
|
||||
|
||||
let result: PendingMessageUploadedContentResult = .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaDocument(flags: flags, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil))
|
||||
let result: PendingMessageUploadedContentResult = .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaDocument(flags: flags, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), videoCover: nil, ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil))
|
||||
if let _ = ttlSeconds {
|
||||
return .single(result)
|
||||
} else {
|
||||
|
||||
@ -702,7 +702,7 @@ private func uploadedFile(account: Account, data: Data, mimeType: String, attrib
|
||||
|> map { next -> UploadMediaEvent in
|
||||
switch next {
|
||||
case let .inputFile(inputFile):
|
||||
return .result(Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil))
|
||||
return .result(Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, videoCover: nil, ttlSeconds: nil))
|
||||
case .inputSecretFile:
|
||||
preconditionFailure()
|
||||
case let .progress(progress):
|
||||
|
||||
@ -158,11 +158,11 @@ public func standaloneUploadedFile(postbox: Postbox, network: Network, peerId: P
|
||||
if let _ = thumbnailFile {
|
||||
flags |= 1 << 2
|
||||
}
|
||||
return network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, ttlSeconds: nil)))
|
||||
return network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, videoCover: nil, ttlSeconds: nil)))
|
||||
|> mapError { _ -> StandaloneUploadMediaError in return .generic }
|
||||
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
|
||||
switch media {
|
||||
case let .messageMediaDocument(_, document, altDocuments, _):
|
||||
case let .messageMediaDocument(_, document, altDocuments, _, _):
|
||||
if let document = document {
|
||||
if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments) {
|
||||
return .single(.result(.media(.standalone(media: mediaFile))))
|
||||
|
||||
@ -108,6 +108,7 @@ final class AccountTaskManager {
|
||||
tasks.add(managedRecentStatusEmoji(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
tasks.add(managedFeaturedStatusEmoji(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
tasks.add(managedFeaturedChannelStatusEmoji(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
tasks.add(managedUniqueStarGifts(accountPeerId: self.accountPeerId, postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
tasks.add(managedProfilePhotoEmoji(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
tasks.add(managedGroupPhotoEmoji(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
tasks.add(managedBackgroundIconEmoji(postbox: self.stateManager.postbox, network: self.stateManager.network).start())
|
||||
|
||||
@ -213,11 +213,11 @@ func managedRecentStatusEmoji(postbox: Postbox, network: Network) -> Signal<Void
|
||||
case let .emojiStatuses(_, statuses):
|
||||
let parsedStatuses = statuses.compactMap(PeerEmojiStatus.init(apiStatus:))
|
||||
|
||||
return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: parsedStatuses.map(\.fileId))
|
||||
return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: parsedStatuses.compactMap(\.emojiFileId))
|
||||
|> map { files -> [OrderedItemListEntry] in
|
||||
var items: [OrderedItemListEntry] = []
|
||||
for status in parsedStatuses {
|
||||
guard let file = files[status.fileId] else {
|
||||
guard let fileId = status.emojiFileId, let file = files[fileId] else {
|
||||
continue
|
||||
}
|
||||
if let entry = CodableEntry(RecentMediaItem(file)) {
|
||||
@ -243,11 +243,11 @@ func managedFeaturedStatusEmoji(postbox: Postbox, network: Network) -> Signal<Vo
|
||||
case let .emojiStatuses(_, statuses):
|
||||
let parsedStatuses = statuses.compactMap(PeerEmojiStatus.init(apiStatus:))
|
||||
|
||||
return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: parsedStatuses.map(\.fileId))
|
||||
return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: parsedStatuses.compactMap(\.emojiFileId))
|
||||
|> map { files -> [OrderedItemListEntry] in
|
||||
var items: [OrderedItemListEntry] = []
|
||||
for status in parsedStatuses {
|
||||
guard let file = files[status.fileId] else {
|
||||
guard let fileId = status.emojiFileId, let file = files[fileId] else {
|
||||
continue
|
||||
}
|
||||
if let entry = CodableEntry(RecentMediaItem(file)) {
|
||||
@ -273,11 +273,11 @@ func managedFeaturedChannelStatusEmoji(postbox: Postbox, network: Network) -> Si
|
||||
case let .emojiStatuses(_, statuses):
|
||||
let parsedStatuses = statuses.compactMap(PeerEmojiStatus.init(apiStatus:))
|
||||
|
||||
return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: parsedStatuses.map(\.fileId))
|
||||
return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: parsedStatuses.compactMap(\.emojiFileId))
|
||||
|> map { files -> [OrderedItemListEntry] in
|
||||
var items: [OrderedItemListEntry] = []
|
||||
for status in parsedStatuses {
|
||||
guard let file = files[status.fileId] else {
|
||||
guard let fileId = status.emojiFileId, let file = files[fileId] else {
|
||||
continue
|
||||
}
|
||||
if let entry = CodableEntry(RecentMediaItem(file)) {
|
||||
@ -292,6 +292,55 @@ func managedFeaturedChannelStatusEmoji(postbox: Postbox, network: Network) -> Si
|
||||
return (poll |> then(.complete() |> suspendAwareDelay(3.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
|
||||
}
|
||||
|
||||
func managedUniqueStarGifts(accountPeerId: PeerId, postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudUniqueStarGifts, extractItemId: { RecentStarGiftItemId($0).id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
|
||||
return network.request(Api.functions.account.getCollectibleEmojiStatuses(hash: hash))
|
||||
|> retryRequest
|
||||
|> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in
|
||||
switch result {
|
||||
case .emojiStatusesNotModified:
|
||||
return .single(nil)
|
||||
case let .emojiStatuses(_, statuses):
|
||||
let parsedStatuses = statuses.compactMap(PeerEmojiStatus.init(apiStatus:))
|
||||
|
||||
return _internal_resolveInlineStickers(postbox: postbox, network: network, fileIds: parsedStatuses.flatMap(\.associatedFileIds))
|
||||
|> map { files -> [OrderedItemListEntry] in
|
||||
var items: [OrderedItemListEntry] = []
|
||||
for status in parsedStatuses {
|
||||
switch status.content {
|
||||
case let .starGift(id, fileId, title, slug, patternFileId, innerColor, outerColor, patternColor, textColor):
|
||||
let slugComponents = slug.components(separatedBy: "-")
|
||||
if let file = files[fileId], let patternFile = files[patternFileId], let numberString = slugComponents.last, let number = Int32(numberString) {
|
||||
let gift = StarGift.UniqueGift(
|
||||
id: id,
|
||||
title: title,
|
||||
number: number,
|
||||
slug: slug,
|
||||
owner: .peerId(accountPeerId),
|
||||
attributes: [
|
||||
.model(name: "", file: file, rarity: 0),
|
||||
.pattern(name: "", file: patternFile, rarity: 0),
|
||||
.backdrop(name: "", innerColor: innerColor, outerColor: outerColor, patternColor: patternColor, textColor: textColor, rarity: 0)
|
||||
],
|
||||
availability: StarGift.UniqueGift.Availability(issued: 0, total: 0)
|
||||
)
|
||||
if let entry = CodableEntry(RecentStarGiftItem(gift)) {
|
||||
items.append(OrderedItemListEntry(id: RecentStarGiftItemId(id).rawValue, contents: entry))
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
return (poll |> then(.complete() |> suspendAwareDelay(1.0 * 60.0 * 60.0, queue: Queue.concurrentDefaultQueue()))) |> restart
|
||||
}
|
||||
|
||||
|
||||
func managedProfilePhotoEmoji(postbox: Postbox, network: Network) -> Signal<Void, NoError> {
|
||||
let poll = managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudFeaturedProfilePhotoEmoji, extractItemId: { RecentMediaItemId($0).mediaId.id }, reverseHashOrder: false, forceFetch: false, fetch: { hash in
|
||||
return network.request(Api.functions.account.getDefaultProfilePhotoEmojis(hash: hash))
|
||||
|
||||
@ -210,7 +210,7 @@ public class BoxedMessage: NSObject {
|
||||
|
||||
public class Serialization: NSObject, MTSerialization {
|
||||
public func currentLayer() -> UInt {
|
||||
return 197
|
||||
return 198
|
||||
}
|
||||
|
||||
public func parseMessage(_ data: Data!) -> Any! {
|
||||
|
||||
@ -52,7 +52,7 @@ extension Api.MessageMedia {
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
case let .messageMediaDocument(_, document, _, _):
|
||||
case let .messageMediaDocument(_, document, _, _, _):
|
||||
if let document = document {
|
||||
return collectPreCachedResources(for: document)
|
||||
}
|
||||
@ -626,14 +626,14 @@ extension Api.EncryptedMessage {
|
||||
extension Api.InputMedia {
|
||||
func withUpdatedStickers(_ stickers: [Api.InputDocument]?) -> Api.InputMedia {
|
||||
switch self {
|
||||
case let .inputMediaUploadedDocument(flags, file, thumb, mimeType, attributes, _, ttlSeconds):
|
||||
case let .inputMediaUploadedDocument(flags, file, thumb, mimeType, attributes, _, videoCover, ttlSeconds):
|
||||
var flags = flags
|
||||
var attributes = attributes
|
||||
if let _ = stickers {
|
||||
flags |= (1 << 0)
|
||||
attributes.append(.documentAttributeHasStickers)
|
||||
}
|
||||
return .inputMediaUploadedDocument(flags: flags, file: file, thumb: thumb, mimeType: mimeType, attributes: attributes, stickers: stickers, ttlSeconds: ttlSeconds)
|
||||
return .inputMediaUploadedDocument(flags: flags, file: file, thumb: thumb, mimeType: mimeType, attributes: attributes, stickers: stickers, videoCover: videoCover, ttlSeconds: ttlSeconds)
|
||||
case let .inputMediaUploadedPhoto(flags, file, _, ttlSeconds):
|
||||
var flags = flags
|
||||
if let _ = stickers {
|
||||
|
||||
@ -260,11 +260,62 @@ public enum PeerNameColor: Hashable {
|
||||
}
|
||||
|
||||
public struct PeerEmojiStatus: Equatable, Codable {
|
||||
public var fileId: Int64
|
||||
public enum Content: Equatable, Codable {
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case discriminator
|
||||
case fileId
|
||||
case id
|
||||
case title
|
||||
case slug
|
||||
case patternFileId
|
||||
case innerColor
|
||||
case outerColor
|
||||
case patternColor
|
||||
case textColor
|
||||
}
|
||||
|
||||
case emoji(fileId: Int64)
|
||||
case starGift(id: Int64, fileId: Int64, title: String, slug: String, patternFileId: Int64, innerColor: Int32, outerColor: Int32, patternColor: Int32, textColor: Int32)
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
switch try container.decode(Int32.self, forKey: .discriminator) {
|
||||
case 0:
|
||||
self = .emoji(fileId: try container.decode(Int64.self, forKey: .fileId))
|
||||
case 1:
|
||||
self = .starGift(id: try container.decode(Int64.self, forKey: .id), fileId: try container.decode(Int64.self, forKey: .fileId), title: try container.decode(String.self, forKey: .title), slug: try container.decode(String.self, forKey: .slug), patternFileId: try container.decode(Int64.self, forKey: .patternFileId), innerColor: try container.decode(Int32.self, forKey: .innerColor), outerColor: try container.decode(Int32.self, forKey: .outerColor), patternColor: try container.decode(Int32.self, forKey: .patternColor), textColor: try container.decode(Int32.self, forKey: .textColor))
|
||||
default:
|
||||
throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "content"))
|
||||
}
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
|
||||
switch self {
|
||||
case let .emoji(fileId):
|
||||
try container.encode(0 as Int32, forKey: .discriminator)
|
||||
try container.encode(fileId, forKey: .fileId)
|
||||
case let .starGift(id, fileId, title, slug, patternFileId, innerColor, outerColor, patternColor, textColor):
|
||||
try container.encode(1 as Int32, forKey: .discriminator)
|
||||
try container.encode(id, forKey: .id)
|
||||
try container.encode(fileId, forKey: .fileId)
|
||||
try container.encode(title, forKey: .title)
|
||||
try container.encode(slug, forKey: .slug)
|
||||
try container.encode(patternFileId, forKey: .patternFileId)
|
||||
try container.encode(innerColor, forKey: .innerColor)
|
||||
try container.encode(outerColor, forKey: .outerColor)
|
||||
try container.encode(patternColor, forKey: .patternColor)
|
||||
try container.encode(textColor, forKey: .textColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
public var content: Content
|
||||
public var expirationDate: Int32?
|
||||
|
||||
public init(fileId: Int64, expirationDate: Int32?) {
|
||||
self.fileId = fileId
|
||||
public init(content: Content, expirationDate: Int32?) {
|
||||
self.content = content
|
||||
self.expirationDate = expirationDate
|
||||
}
|
||||
}
|
||||
@ -272,15 +323,52 @@ public struct PeerEmojiStatus: Equatable, Codable {
|
||||
extension PeerEmojiStatus {
|
||||
init?(apiStatus: Api.EmojiStatus) {
|
||||
switch apiStatus {
|
||||
case let .emojiStatus(documentId):
|
||||
self.init(fileId: documentId, expirationDate: nil)
|
||||
case let .emojiStatusUntil(documentId, until):
|
||||
self.init(fileId: documentId, expirationDate: until)
|
||||
case .emojiStatusEmpty:
|
||||
case let .emojiStatus(_, documentId, until):
|
||||
self.init(content: .emoji(fileId: documentId), expirationDate: until)
|
||||
case let .emojiStatusCollectible(_, collectibleId, documentId, title, slug, patternDocumentId, centerColor, edgeColor, patternColor, textColor, until):
|
||||
self.init(content: .starGift(id: collectibleId, fileId: documentId, title: title, slug: slug, patternFileId: patternDocumentId, innerColor: centerColor, outerColor: edgeColor, patternColor: patternColor, textColor: textColor), expirationDate: until)
|
||||
case .emojiStatusEmpty, .inputEmojiStatusCollectible:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
extension PeerEmojiStatus {
|
||||
var emojiFileId: Int64? {
|
||||
switch self.content {
|
||||
case let .emoji(fileId):
|
||||
return fileId
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var associatedFileIds: [Int64] {
|
||||
switch self.content {
|
||||
case let .emoji(fileId):
|
||||
return [fileId]
|
||||
case let .starGift(_, fileId, _, _, patternFileId, _, _, _, _):
|
||||
return [fileId, patternFileId]
|
||||
}
|
||||
}
|
||||
|
||||
public var fileId: Int64 {
|
||||
switch self.content {
|
||||
case let .emoji(fileId):
|
||||
return fileId
|
||||
case let .starGift(_, fileId, _, _, _, _, _, _, _):
|
||||
return fileId
|
||||
}
|
||||
}
|
||||
|
||||
public var color: Int32? {
|
||||
switch self.content {
|
||||
case .emoji:
|
||||
return nil
|
||||
case let .starGift(_, _, _, _, _, innerColor, _, _, _):
|
||||
return innerColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct CachedUserFlags: OptionSet {
|
||||
public var rawValue: Int32
|
||||
|
||||
@ -95,6 +95,7 @@ public struct Namespaces {
|
||||
public static let CloudFeaturedChannelStatusEmoji: Int32 = 27
|
||||
public static let CloudDisabledChannelStatusEmoji: Int32 = 28
|
||||
public static let CloudDefaultTagReactions: Int32 = 29
|
||||
public static let CloudUniqueStarGifts: Int32 = 30
|
||||
}
|
||||
|
||||
public struct CachedItemCollection {
|
||||
|
||||
@ -284,3 +284,47 @@ public final class RecentReactionItem: Codable, Equatable {
|
||||
return lhs.content == rhs.content
|
||||
}
|
||||
}
|
||||
|
||||
public struct RecentStarGiftItemId {
|
||||
public let rawValue: MemoryBuffer
|
||||
public let id: Int64
|
||||
|
||||
public init(_ rawValue: MemoryBuffer) {
|
||||
self.rawValue = rawValue
|
||||
assert(rawValue.length == 8)
|
||||
var id: Int64 = 0
|
||||
memcpy(&id, rawValue.memory, 8)
|
||||
self.id = id
|
||||
}
|
||||
|
||||
public init(_ id: Int64) {
|
||||
var id = id
|
||||
self.id = id
|
||||
self.rawValue = MemoryBuffer(memory: malloc(8)!, capacity: 8, length: 8, freeWhenDone: true)
|
||||
memcpy(self.rawValue.memory, &id, 8)
|
||||
}
|
||||
}
|
||||
|
||||
public final class RecentStarGiftItem: Codable, Equatable {
|
||||
public let starGift: StarGift.UniqueGift
|
||||
|
||||
public init(_ starGift: StarGift.UniqueGift) {
|
||||
self.starGift = starGift
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
self.starGift = try container.decode(StarGift.UniqueGift.self, forKey: "g")
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
try container.encode(self.starGift, forKey: "g")
|
||||
}
|
||||
|
||||
public static func ==(lhs: RecentStarGiftItem, rhs: RecentStarGiftItem) -> Bool {
|
||||
return lhs.starGift == rhs.starGift
|
||||
}
|
||||
}
|
||||
|
||||
@ -186,22 +186,26 @@ public final class TelegramChannel: Peer, Equatable {
|
||||
}
|
||||
|
||||
public var associatedMediaIds: [MediaId]? {
|
||||
if let emojiStatus = self.emojiStatus, let backgroundEmojiId = self.backgroundEmojiId {
|
||||
return [
|
||||
MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId),
|
||||
MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)
|
||||
]
|
||||
} else if let emojiStatus = self.emojiStatus {
|
||||
return [
|
||||
MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId)
|
||||
]
|
||||
} else if let backgroundEmojiId = self.backgroundEmojiId {
|
||||
return [
|
||||
MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)
|
||||
]
|
||||
} else {
|
||||
var mediaIds: [MediaId] = []
|
||||
if let emojiStatus = self.emojiStatus {
|
||||
switch emojiStatus.content {
|
||||
case let .emoji(fileId):
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId))
|
||||
case let .starGift(_, fileId, _, _, patternFileId, _, _, _, _):
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId))
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: patternFileId))
|
||||
}
|
||||
}
|
||||
if let backgroundEmojiId = self.backgroundEmojiId {
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId))
|
||||
}
|
||||
if let profileBackgroundEmojiId = self.profileBackgroundEmojiId {
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: profileBackgroundEmojiId))
|
||||
}
|
||||
guard !mediaIds.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return mediaIds
|
||||
}
|
||||
|
||||
public let associatedPeerId: PeerId? = nil
|
||||
|
||||
@ -174,22 +174,26 @@ public final class TelegramUser: Peer, Equatable {
|
||||
}
|
||||
|
||||
public var associatedMediaIds: [MediaId]? {
|
||||
if let emojiStatus = self.emojiStatus, let backgroundEmojiId = self.backgroundEmojiId {
|
||||
return [
|
||||
MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId),
|
||||
MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)
|
||||
]
|
||||
} else if let emojiStatus = self.emojiStatus {
|
||||
return [
|
||||
MediaId(namespace: Namespaces.Media.CloudFile, id: emojiStatus.fileId)
|
||||
]
|
||||
} else if let backgroundEmojiId = self.backgroundEmojiId {
|
||||
return [
|
||||
MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId)
|
||||
]
|
||||
} else {
|
||||
var mediaIds: [MediaId] = []
|
||||
if let emojiStatus = self.emojiStatus {
|
||||
switch emojiStatus.content {
|
||||
case let .emoji(fileId):
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId))
|
||||
case let .starGift(_, fileId, _, _, patternFileId, _, _, _, _):
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: fileId))
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: patternFileId))
|
||||
}
|
||||
}
|
||||
if let backgroundEmojiId = self.backgroundEmojiId {
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: backgroundEmojiId))
|
||||
}
|
||||
if let profileBackgroundEmojiId = self.profileBackgroundEmojiId {
|
||||
mediaIds.append(MediaId(namespace: Namespaces.Media.CloudFile, id: profileBackgroundEmojiId))
|
||||
}
|
||||
guard !mediaIds.isEmpty else {
|
||||
return nil
|
||||
}
|
||||
return mediaIds
|
||||
}
|
||||
|
||||
public let associatedPeerId: PeerId? = nil
|
||||
|
||||
@ -75,15 +75,75 @@ public extension TelegramEngine {
|
||||
return _internal_removeAccountPhoto(account: self.account, reference: reference, fallback: true)
|
||||
}
|
||||
|
||||
public func setStarGiftStatus(starGift: StarGift.UniqueGift, expirationDate: Int32?) -> Signal<Never, NoError> {
|
||||
let peerId = self.account.peerId
|
||||
|
||||
var flags: Int32 = 0
|
||||
if let _ = expirationDate {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
var file: TelegramMediaFile?
|
||||
var patternFile: TelegramMediaFile?
|
||||
var innerColor: Int32?
|
||||
var outerColor: Int32?
|
||||
var patternColor: Int32?
|
||||
var textColor: Int32?
|
||||
for attribute in starGift.attributes {
|
||||
switch attribute {
|
||||
case let .model(_, fileValue, _):
|
||||
file = fileValue
|
||||
case let .pattern(_, patternFileValue, _):
|
||||
patternFile = patternFileValue
|
||||
case let .backdrop(_, innerColorValue, outerColorValue, patternColorValue, textColorValue, _):
|
||||
innerColor = innerColorValue
|
||||
outerColor = outerColorValue
|
||||
patternColor = patternColorValue
|
||||
textColor = textColorValue
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
let apiEmojiStatus: Api.EmojiStatus
|
||||
var emojiStatus: PeerEmojiStatus?
|
||||
if let file, let patternFile, let innerColor, let outerColor, let patternColor, let textColor {
|
||||
apiEmojiStatus = .inputEmojiStatusCollectible(flags: flags, collectibleId: starGift.id, until: expirationDate)
|
||||
emojiStatus = PeerEmojiStatus(content: .starGift(id: starGift.id, fileId: file.fileId.id, title: starGift.title, slug: starGift.slug, patternFileId: patternFile.fileId.id, innerColor: innerColor, outerColor: outerColor, patternColor: patternColor, textColor: textColor), expirationDate: expirationDate)
|
||||
} else {
|
||||
apiEmojiStatus = .emojiStatusEmpty
|
||||
}
|
||||
|
||||
let remoteApply = self.account.network.request(Api.functions.account.updateEmojiStatus(emojiStatus: apiEmojiStatus))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
}
|
||||
|> ignoreValues
|
||||
|
||||
return self.account.postbox.transaction { transaction -> Void in
|
||||
if let file, let patternFile {
|
||||
transaction.storeMediaIfNotPresent(media: file)
|
||||
transaction.storeMediaIfNotPresent(media: patternFile)
|
||||
}
|
||||
if let peer = transaction.getPeer(peerId) as? TelegramUser {
|
||||
updatePeersCustom(transaction: transaction, peers: [
|
||||
peer.withUpdatedEmojiStatus(emojiStatus)
|
||||
], update: { _, updated in
|
||||
updated
|
||||
})
|
||||
}
|
||||
}
|
||||
|> ignoreValues
|
||||
|> then(remoteApply)
|
||||
}
|
||||
|
||||
public func setEmojiStatus(file: TelegramMediaFile?, expirationDate: Int32?) -> Signal<Never, NoError> {
|
||||
let peerId = self.account.peerId
|
||||
|
||||
let remoteApply = self.account.network.request(Api.functions.account.updateEmojiStatus(emojiStatus: file.flatMap({ file in
|
||||
if let expirationDate = expirationDate {
|
||||
return Api.EmojiStatus.emojiStatusUntil(documentId: file.fileId.id, until: expirationDate)
|
||||
} else {
|
||||
return Api.EmojiStatus.emojiStatus(documentId: file.fileId.id)
|
||||
var flags: Int32 = 0
|
||||
if let _ = expirationDate {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
return Api.EmojiStatus.emojiStatus(flags: flags, documentId: file.fileId.id, until: expirationDate)
|
||||
}) ?? Api.EmojiStatus.emojiStatusEmpty))
|
||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||
return .single(.boolFalse)
|
||||
@ -101,7 +161,7 @@ public extension TelegramEngine {
|
||||
}
|
||||
|
||||
if let peer = transaction.getPeer(peerId) as? TelegramUser {
|
||||
updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmojiStatus(file.flatMap({ PeerEmojiStatus(fileId: $0.fileId.id, expirationDate: expirationDate) }))], update: { _, updated in
|
||||
updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmojiStatus(file.flatMap({ PeerEmojiStatus(content: .emoji(fileId: $0.fileId.id), expirationDate: expirationDate) }))], update: { _, updated in
|
||||
updated
|
||||
})
|
||||
}
|
||||
|
||||
@ -153,7 +153,7 @@ public extension TelegramEngine {
|
||||
default:
|
||||
break
|
||||
}
|
||||
inputMedia = .inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: resolvedMimeType, attributes: attributes, stickers: nil, ttlSeconds: nil)
|
||||
inputMedia = .inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: resolvedMimeType, attributes: attributes, stickers: nil, videoCover: nil, ttlSeconds: nil)
|
||||
}
|
||||
case let .progress(value):
|
||||
return .single(value)
|
||||
|
||||
@ -1405,7 +1405,7 @@ func _internal_deleteBotPreviews(account: Account, peerId: PeerId, language: Str
|
||||
inputMedia.append(.inputMediaPhoto(flags: 0, id: .inputPhoto(id: resource.photoId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), ttlSeconds: nil))
|
||||
inputMedia.append(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: resource.photoId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), ttlSeconds: nil))
|
||||
} else if let file = item as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource {
|
||||
inputMedia.append(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil, query: nil))
|
||||
inputMedia.append(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), videoCover: nil, ttlSeconds: nil, query: nil))
|
||||
}
|
||||
}
|
||||
if language == nil {
|
||||
@ -1463,7 +1463,7 @@ func _internal_deleteBotPreviewsLanguage(account: Account, peerId: PeerId, langu
|
||||
inputMedia.append(.inputMediaPhoto(flags: 0, id: .inputPhoto(id: resource.photoId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), ttlSeconds: nil))
|
||||
inputMedia.append(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: resource.photoId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), ttlSeconds: nil))
|
||||
} else if let file = item as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource {
|
||||
inputMedia.append(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil, query: nil))
|
||||
inputMedia.append(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), videoCover: nil, ttlSeconds: nil, query: nil))
|
||||
}
|
||||
}
|
||||
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current -> CachedPeerData? in
|
||||
@ -1528,7 +1528,7 @@ func _internal_editStory(account: Account, peerId: PeerId, id: Int32, media: Eng
|
||||
if let result = result, case let .content(uploadedContent) = result, case let .media(media, _) = uploadedContent.content {
|
||||
inputMedia = media
|
||||
} else if case let .existing(media) = media, let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource {
|
||||
inputMedia = .inputMediaUploadedDocument(flags: 0, file: .inputFileStoryDocument(id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference))), thumb: nil, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, ttlSeconds: nil)
|
||||
inputMedia = .inputMediaUploadedDocument(flags: 0, file: .inputFileStoryDocument(id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference))), thumb: nil, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: nil, videoCover: nil, ttlSeconds: nil)
|
||||
updatingCoverTime = true
|
||||
} else {
|
||||
inputMedia = nil
|
||||
@ -2099,7 +2099,7 @@ extension Stories.StoredItem {
|
||||
|
||||
var parsedAlternativeMedia: [Media] = []
|
||||
switch media {
|
||||
case let .messageMediaDocument(_, _, altDocuments, _):
|
||||
case let .messageMediaDocument(_, _, altDocuments, _, _):
|
||||
if let altDocuments {
|
||||
parsedAlternativeMedia = altDocuments.compactMap { telegramMediaFileFromApiDocument($0, altDocuments: []) }
|
||||
}
|
||||
|
||||
@ -2615,7 +2615,7 @@ public final class BotPreviewStoryListContext: StoryListContext {
|
||||
inputMedia.append(.inputMediaPhoto(flags: 0, id: .inputPhoto(id: resource.photoId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), ttlSeconds: nil))
|
||||
inputMedia.append(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: resource.photoId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference)), ttlSeconds: nil))
|
||||
} else if let file = item as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource {
|
||||
inputMedia.append(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), ttlSeconds: nil, query: nil))
|
||||
inputMedia.append(.inputMediaDocument(flags: 0, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: resource.fileReference ?? Data())), videoCover: nil, ttlSeconds: nil, query: nil))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -274,7 +274,7 @@ public enum UpdatePeerEmojiStatusError {
|
||||
func _internal_updatePeerEmojiStatus(account: Account, peerId: PeerId, fileId: Int64?, expirationDate: Int32?) -> Signal<Never, UpdatePeerEmojiStatusError> {
|
||||
return account.postbox.transaction { transaction -> Api.InputChannel? in
|
||||
let updatedStatus = fileId.flatMap {
|
||||
PeerEmojiStatus(fileId: $0, expirationDate: expirationDate)
|
||||
PeerEmojiStatus(content: .emoji(fileId: $0), expirationDate: expirationDate)
|
||||
}
|
||||
if let peer = transaction.getPeer(peerId) as? TelegramChannel {
|
||||
updatePeersCustom(transaction: transaction, peers: [peer.withUpdatedEmojiStatus(updatedStatus)], update: { _, updated in updated })
|
||||
@ -289,11 +289,11 @@ func _internal_updatePeerEmojiStatus(account: Account, peerId: PeerId, fileId: I
|
||||
}
|
||||
let mappedStatus: Api.EmojiStatus
|
||||
if let fileId = fileId {
|
||||
if let expirationDate = expirationDate {
|
||||
mappedStatus = .emojiStatusUntil(documentId: fileId, until: expirationDate)
|
||||
} else {
|
||||
mappedStatus = .emojiStatus(documentId: fileId)
|
||||
var flags: Int32 = 0
|
||||
if let _ = expirationDate {
|
||||
flags |= (1 << 0)
|
||||
}
|
||||
mappedStatus = .emojiStatus(flags: flags, documentId: fileId, until: expirationDate)
|
||||
} else {
|
||||
mappedStatus = .emojiStatusEmpty
|
||||
}
|
||||
|
||||
@ -83,11 +83,11 @@ func _internal_uploadSticker(account: Account, peer: Peer, resource: MediaResour
|
||||
attributes.append(.documentAttributeVideo(flags: 0, duration: duration, w: dimensions.width, h: dimensions.height, preloadPrefixSize: nil, videoStartTs: nil, videoCodec: nil))
|
||||
}
|
||||
attributes.append(.documentAttributeImageSize(w: dimensions.width, h: dimensions.height))
|
||||
return account.network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: file, thumb: thumbnailFile, mimeType: mimeType, attributes: attributes, stickers: nil, ttlSeconds: nil)))
|
||||
return account.network.request(Api.functions.messages.uploadMedia(flags: 0, businessConnectionId: nil, peer: inputPeer, media: Api.InputMedia.inputMediaUploadedDocument(flags: flags, file: file, thumb: thumbnailFile, mimeType: mimeType, attributes: attributes, stickers: nil, videoCover: nil, ttlSeconds: nil)))
|
||||
|> mapError { _ -> UploadStickerError in return .generic }
|
||||
|> mapToSignal { media -> Signal<UploadStickerStatus, UploadStickerError> in
|
||||
switch media {
|
||||
case let .messageMediaDocument(_, document, altDocuments, _):
|
||||
case let .messageMediaDocument(_, document, altDocuments, _, _):
|
||||
if let document = document, let file = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments), let uploadedResource = file.resource as? CloudDocumentMediaResource {
|
||||
account.postbox.mediaBox.copyResourceData(from: resource.id, to: uploadedResource.id, synchronous: true)
|
||||
if let thumbnail, let previewRepresentation = file.previewRepresentations.first(where: { $0.dimensions == PixelDimensions(width: 320, height: 320) }) {
|
||||
|
||||
@ -284,7 +284,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
|
||||
titleCredibilityIcon = .verified
|
||||
}
|
||||
if let verificationIconFileId = peer.verificationIconFileId {
|
||||
titleVerifiedIcon = .emojiStatus(PeerEmojiStatus(fileId: verificationIconFileId, expirationDate: nil))
|
||||
titleVerifiedIcon = .emojiStatus(PeerEmojiStatus(content: .emoji(fileId: verificationIconFileId), expirationDate: nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,22 +59,26 @@ public final class EmojiStatusComponent: Component {
|
||||
public let animationCache: AnimationCache
|
||||
public let animationRenderer: MultiAnimationRenderer
|
||||
public let content: Content
|
||||
public let particleColor: UIColor?
|
||||
public let size: CGSize?
|
||||
public let isVisibleForAnimations: Bool
|
||||
public let useSharedAnimation: Bool
|
||||
public let action: (() -> Void)?
|
||||
public let emojiFileUpdated: ((TelegramMediaFile?) -> Void)?
|
||||
public let tag: AnyObject?
|
||||
|
||||
public convenience init(
|
||||
context: AccountContext,
|
||||
animationCache: AnimationCache,
|
||||
animationRenderer: MultiAnimationRenderer,
|
||||
content: Content,
|
||||
particleColor: UIColor? = nil,
|
||||
size: CGSize? = nil,
|
||||
isVisibleForAnimations: Bool,
|
||||
useSharedAnimation: Bool = false,
|
||||
action: (() -> Void)?,
|
||||
emojiFileUpdated: ((TelegramMediaFile?) -> Void)? = nil
|
||||
emojiFileUpdated: ((TelegramMediaFile?) -> Void)? = nil,
|
||||
tag: AnyObject? = nil
|
||||
) {
|
||||
self.init(
|
||||
postbox: context.account.postbox,
|
||||
@ -85,11 +89,13 @@ public final class EmojiStatusComponent: Component {
|
||||
animationCache: animationCache,
|
||||
animationRenderer: animationRenderer,
|
||||
content: content,
|
||||
particleColor: particleColor,
|
||||
size: size,
|
||||
isVisibleForAnimations: isVisibleForAnimations,
|
||||
useSharedAnimation: useSharedAnimation,
|
||||
action: action,
|
||||
emojiFileUpdated: emojiFileUpdated
|
||||
emojiFileUpdated: emojiFileUpdated,
|
||||
tag: tag
|
||||
)
|
||||
}
|
||||
|
||||
@ -100,11 +106,13 @@ public final class EmojiStatusComponent: Component {
|
||||
animationCache: AnimationCache,
|
||||
animationRenderer: MultiAnimationRenderer,
|
||||
content: Content,
|
||||
particleColor: UIColor? = nil,
|
||||
size: CGSize? = nil,
|
||||
isVisibleForAnimations: Bool,
|
||||
useSharedAnimation: Bool = false,
|
||||
action: (() -> Void)?,
|
||||
emojiFileUpdated: ((TelegramMediaFile?) -> Void)? = nil
|
||||
emojiFileUpdated: ((TelegramMediaFile?) -> Void)? = nil,
|
||||
tag: AnyObject? = nil
|
||||
) {
|
||||
self.postbox = postbox
|
||||
self.energyUsageSettings = energyUsageSettings
|
||||
@ -112,11 +120,13 @@ public final class EmojiStatusComponent: Component {
|
||||
self.animationCache = animationCache
|
||||
self.animationRenderer = animationRenderer
|
||||
self.content = content
|
||||
self.particleColor = particleColor
|
||||
self.size = size
|
||||
self.isVisibleForAnimations = isVisibleForAnimations
|
||||
self.useSharedAnimation = useSharedAnimation
|
||||
self.action = action
|
||||
self.emojiFileUpdated = emojiFileUpdated
|
||||
self.tag = tag
|
||||
}
|
||||
|
||||
public func withVisibleForAnimations(_ isVisibleForAnimations: Bool) -> EmojiStatusComponent {
|
||||
@ -127,11 +137,13 @@ public final class EmojiStatusComponent: Component {
|
||||
animationCache: self.animationCache,
|
||||
animationRenderer: self.animationRenderer,
|
||||
content: self.content,
|
||||
particleColor: self.particleColor,
|
||||
size: self.size,
|
||||
isVisibleForAnimations: isVisibleForAnimations,
|
||||
useSharedAnimation: self.useSharedAnimation,
|
||||
action: self.action,
|
||||
emojiFileUpdated: self.emojiFileUpdated
|
||||
emojiFileUpdated: self.emojiFileUpdated,
|
||||
tag: self.tag
|
||||
)
|
||||
}
|
||||
|
||||
@ -151,6 +163,9 @@ public final class EmojiStatusComponent: Component {
|
||||
if lhs.content != rhs.content {
|
||||
return false
|
||||
}
|
||||
if lhs.particleColor != rhs.particleColor {
|
||||
return false
|
||||
}
|
||||
if lhs.size != rhs.size {
|
||||
return false
|
||||
}
|
||||
@ -160,10 +175,23 @@ public final class EmojiStatusComponent: Component {
|
||||
if lhs.useSharedAnimation != rhs.useSharedAnimation {
|
||||
return false
|
||||
}
|
||||
if lhs.tag !== rhs.tag {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: UIView {
|
||||
public final class View: UIView, ComponentTaggedView {
|
||||
public func matches(tag: Any) -> Bool {
|
||||
if let component = self.component, let componentTag = component.tag {
|
||||
let tag = tag as AnyObject
|
||||
if componentTag === tag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private final class AnimationFileProperties {
|
||||
let path: String
|
||||
let coloredComposition: Animation?
|
||||
@ -195,6 +223,7 @@ public final class EmojiStatusComponent: Component {
|
||||
|
||||
private weak var state: EmptyComponentState?
|
||||
private var component: EmojiStatusComponent?
|
||||
private var starsLayer: StarsEffectLayer?
|
||||
private var iconView: UIImageView?
|
||||
private var animationLayer: InlineStickerItemLayer?
|
||||
private var lottieAnimationView: AnimationView?
|
||||
@ -255,6 +284,24 @@ public final class EmojiStatusComponent: Component {
|
||||
|
||||
self.isUserInteractionEnabled = component.action != nil
|
||||
|
||||
if let particleColor = component.particleColor {
|
||||
let starsLayer: StarsEffectLayer
|
||||
if let current = self.starsLayer {
|
||||
starsLayer = current
|
||||
} else {
|
||||
starsLayer = StarsEffectLayer()
|
||||
self.layer.insertSublayer(starsLayer, at: 0)
|
||||
self.starsLayer = starsLayer
|
||||
}
|
||||
let side = floor(availableSize.width * 1.25)
|
||||
let starsFrame = CGSize(width: side, height: side).centered(in: CGRect(origin: .zero, size: availableSize))
|
||||
starsLayer.frame = starsFrame
|
||||
starsLayer.update(color: particleColor, size: starsFrame.size)
|
||||
} else if let starsLayer = self.starsLayer {
|
||||
self.starsLayer = nil
|
||||
starsLayer.removeFromSuperlayer()
|
||||
}
|
||||
|
||||
//let previousContent = self.component?.content
|
||||
if self.component?.content != component.content {
|
||||
switch component.content {
|
||||
@ -662,3 +709,57 @@ public func topicIconColors(for color: Int32) -> ([UInt32], [UInt32]) {
|
||||
|
||||
return topicColors[color] ?? ([0x6FB9F0, 0x0261E4], [0x026CB5, 0x064BB7])
|
||||
}
|
||||
|
||||
public final class StarsEffectLayer: SimpleLayer {
|
||||
private let emitterLayer = CAEmitterLayer()
|
||||
|
||||
public override init() {
|
||||
super.init()
|
||||
|
||||
self.addSublayer(self.emitterLayer)
|
||||
}
|
||||
|
||||
override init(layer: Any) {
|
||||
super.init(layer: layer)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func setup(color: UIColor, size: CGSize) {
|
||||
let emitter = CAEmitterCell()
|
||||
emitter.name = "emitter"
|
||||
emitter.contents = UIImage(bundleImageName: "Premium/Stars/Particle")?.cgImage
|
||||
emitter.birthRate = 8.0
|
||||
emitter.lifetime = 2.0
|
||||
emitter.velocity = 0.1
|
||||
emitter.scale = (size.width / 32.0) * 0.12
|
||||
emitter.scaleRange = 0.02
|
||||
emitter.alphaRange = 0.1
|
||||
emitter.emissionRange = .pi * 2.0
|
||||
|
||||
let staticColors: [Any] = [
|
||||
color.withAlphaComponent(0.0).cgColor,
|
||||
color.withAlphaComponent(0.58).cgColor,
|
||||
color.withAlphaComponent(0.58).cgColor,
|
||||
color.withAlphaComponent(0.0).cgColor
|
||||
]
|
||||
let staticColorBehavior = CAEmitterCell.createEmitterBehavior(type: "colorOverLife")
|
||||
staticColorBehavior.setValue(staticColors, forKey: "colors")
|
||||
emitter.setValue([staticColorBehavior], forKey: "emitterBehaviors")
|
||||
self.emitterLayer.emitterCells = [emitter]
|
||||
}
|
||||
|
||||
public func update(color: UIColor, size: CGSize) {
|
||||
if self.emitterLayer.emitterCells == nil {
|
||||
self.setup(color: color, size: size)
|
||||
}
|
||||
self.emitterLayer.seed = UInt32.random(in: .min ..< .max)
|
||||
self.emitterLayer.emitterShape = .circle
|
||||
self.emitterLayer.emitterSize = size
|
||||
self.emitterLayer.emitterMode = .surface
|
||||
self.emitterLayer.frame = CGRect(origin: .zero, size: size)
|
||||
self.emitterLayer.emitterPosition = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1336,8 +1336,13 @@ public final class EmojiStatusSelectionController: ViewController {
|
||||
|
||||
switch controller.mode {
|
||||
case .statusSelection:
|
||||
let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil)
|
||||
|> deliverOnMainQueue).start()
|
||||
if let gift = item?.itemGift {
|
||||
let _ = (self.context.engine.accountData.setStarGiftStatus(starGift: gift, expirationDate: nil)
|
||||
|> deliverOnMainQueue).start()
|
||||
} else {
|
||||
let _ = (self.context.engine.accountData.setEmojiStatus(file: item?.itemFile, expirationDate: nil)
|
||||
|> deliverOnMainQueue).start()
|
||||
}
|
||||
case let .backgroundSelection(completion):
|
||||
completion(item?.itemFile)
|
||||
case let .customStatusSelection(completion):
|
||||
|
||||
@ -53,6 +53,7 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
||||
private var iconLayer: SimpleLayer?
|
||||
private var tintIconLayer: SimpleLayer?
|
||||
|
||||
private(set) var underlyingContentLayer: SimpleLayer?
|
||||
private(set) var tintContentLayer: SimpleLayer?
|
||||
|
||||
private var badge: Badge?
|
||||
@ -93,6 +94,9 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
||||
if let mirrorLayer = self.tintContentLayer {
|
||||
mirrorLayer.position = value
|
||||
}
|
||||
if let mirrorLayer = self.underlyingContentLayer {
|
||||
mirrorLayer.position = value
|
||||
}
|
||||
super.position = value
|
||||
}
|
||||
}
|
||||
@ -104,6 +108,9 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
||||
if let mirrorLayer = self.tintContentLayer {
|
||||
mirrorLayer.bounds = value
|
||||
}
|
||||
if let mirrorLayer = self.underlyingContentLayer {
|
||||
mirrorLayer.bounds = value
|
||||
}
|
||||
super.bounds = value
|
||||
}
|
||||
}
|
||||
@ -112,7 +119,9 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
||||
if let mirrorLayer = self.tintContentLayer {
|
||||
mirrorLayer.add(animation, forKey: key)
|
||||
}
|
||||
|
||||
if let mirrorLayer = self.underlyingContentLayer {
|
||||
mirrorLayer.add(animation, forKey: key)
|
||||
}
|
||||
super.add(animation, forKey: key)
|
||||
}
|
||||
|
||||
@ -120,7 +129,9 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
||||
if let mirrorLayer = self.tintContentLayer {
|
||||
mirrorLayer.removeAllAnimations()
|
||||
}
|
||||
|
||||
if let mirrorLayer = self.underlyingContentLayer {
|
||||
mirrorLayer.removeAllAnimations()
|
||||
}
|
||||
super.removeAllAnimations()
|
||||
}
|
||||
|
||||
@ -128,7 +139,9 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
||||
if let mirrorLayer = self.tintContentLayer {
|
||||
mirrorLayer.removeAnimation(forKey: forKey)
|
||||
}
|
||||
|
||||
if let mirrorLayer = self.underlyingContentLayer {
|
||||
mirrorLayer.removeAnimation(forKey: forKey)
|
||||
}
|
||||
super.removeAnimation(forKey: forKey)
|
||||
}
|
||||
|
||||
@ -233,6 +246,16 @@ public final class EmojiKeyboardItemLayer: MultiAnimationRenderTarget {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if let particleColor = animationData.particleColor {
|
||||
let underlyingContentLayer = SimpleLayer()
|
||||
self.underlyingContentLayer = underlyingContentLayer
|
||||
|
||||
let starsLayer = StarsEffectLayer()
|
||||
starsLayer.frame = CGRect(origin: CGPoint(x: -3.0, y: -3.0), size: CGSize(width: 42.0, height: 42.0))
|
||||
starsLayer.update(color: particleColor, size: CGSize(width: 42.0, height: 42.0))
|
||||
underlyingContentLayer.addSublayer(starsLayer)
|
||||
}
|
||||
case let .staticEmoji(staticEmoji):
|
||||
let image = generateImage(pointSize, opaque: false, scale: min(UIScreenScale, 3.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
@ -40,6 +40,7 @@ public final class EntityKeyboardAnimationData: Equatable {
|
||||
public enum Id: Hashable {
|
||||
case file(MediaId)
|
||||
case stickerPackThumbnail(ItemCollectionId)
|
||||
case gift(String)
|
||||
}
|
||||
|
||||
public enum ItemType {
|
||||
@ -66,8 +67,9 @@ public final class EntityKeyboardAnimationData: Equatable {
|
||||
public let immediateThumbnailData: Data?
|
||||
public let isReaction: Bool
|
||||
public let isTemplate: Bool
|
||||
public let particleColor: UIColor?
|
||||
|
||||
public init(id: Id, type: ItemType, resource: MediaResourceReference, dimensions: CGSize, immediateThumbnailData: Data?, isReaction: Bool, isTemplate: Bool) {
|
||||
public init(id: Id, type: ItemType, resource: MediaResourceReference, dimensions: CGSize, immediateThumbnailData: Data?, isReaction: Bool, isTemplate: Bool, particleColor: UIColor? = nil) {
|
||||
self.id = id
|
||||
self.type = type
|
||||
self.resource = resource
|
||||
@ -75,6 +77,7 @@ public final class EntityKeyboardAnimationData: Equatable {
|
||||
self.immediateThumbnailData = immediateThumbnailData
|
||||
self.isReaction = isReaction
|
||||
self.isTemplate = isTemplate
|
||||
self.particleColor = particleColor
|
||||
}
|
||||
|
||||
public convenience init(file: TelegramMediaFile, isReaction: Bool = false, partialReference: PartialMediaReference? = nil) {
|
||||
@ -97,6 +100,25 @@ public final class EntityKeyboardAnimationData: Equatable {
|
||||
self.init(id: .file(file.fileId), type: type, resource: resourceReference, dimensions: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), immediateThumbnailData: file.immediateThumbnailData, isReaction: isReaction, isTemplate: isTemplate)
|
||||
}
|
||||
|
||||
public convenience init?(gift: StarGift.UniqueGift) {
|
||||
var file: TelegramMediaFile?
|
||||
var color: UIColor?
|
||||
for attribute in gift.attributes {
|
||||
if case let .model(_, fileValue, _) = attribute {
|
||||
file = fileValue
|
||||
} else if case let .backdrop(_, innerColor, outerColor, _, _, _) = attribute {
|
||||
color = UIColor(rgb: UInt32(bitPattern: innerColor))
|
||||
let _ = outerColor
|
||||
}
|
||||
}
|
||||
if let file, let color {
|
||||
let resourceReference: MediaResourceReference = .standalone(resource: file.resource)
|
||||
self.init(id: .gift(gift.slug), type: .lottie, resource: resourceReference, dimensions: file.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0), immediateThumbnailData: file.immediateThumbnailData, isReaction: false, isTemplate: false, particleColor: color)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
public static func ==(lhs: EntityKeyboardAnimationData, rhs: EntityKeyboardAnimationData) -> Bool {
|
||||
if lhs === rhs {
|
||||
return true
|
||||
@ -335,6 +357,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
case animation(EntityKeyboardAnimationData.Id)
|
||||
case staticEmoji(String)
|
||||
case icon(Icon)
|
||||
case starGift(String)
|
||||
}
|
||||
|
||||
public enum Icon: Equatable, Hashable {
|
||||
@ -379,6 +402,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
public let animationData: EntityKeyboardAnimationData?
|
||||
public let content: ItemContent
|
||||
public let itemFile: TelegramMediaFile?
|
||||
public let itemGift: StarGift.UniqueGift?
|
||||
public let subgroupId: Int32?
|
||||
public let icon: Icon
|
||||
public let tintMode: TintMode
|
||||
@ -387,6 +411,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
animationData: EntityKeyboardAnimationData?,
|
||||
content: ItemContent,
|
||||
itemFile: TelegramMediaFile?,
|
||||
itemGift: StarGift.UniqueGift? = nil,
|
||||
subgroupId: Int32?,
|
||||
icon: Icon,
|
||||
tintMode: TintMode
|
||||
@ -394,6 +419,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
self.animationData = animationData
|
||||
self.content = content
|
||||
self.itemFile = itemFile
|
||||
self.itemGift = itemGift
|
||||
self.subgroupId = subgroupId
|
||||
self.icon = icon
|
||||
self.tintMode = tintMode
|
||||
@ -412,6 +438,9 @@ public final class EmojiPagerContentComponent: Component {
|
||||
if lhs.itemFile?.fileId != rhs.itemFile?.fileId {
|
||||
return false
|
||||
}
|
||||
if lhs.itemGift?.id != rhs.itemGift?.id {
|
||||
return false
|
||||
}
|
||||
if lhs.subgroupId != rhs.subgroupId {
|
||||
return false
|
||||
}
|
||||
@ -3461,6 +3490,9 @@ public final class EmojiPagerContentComponent: Component {
|
||||
)
|
||||
|
||||
self.visibleItemLayers[itemId] = itemLayer
|
||||
if let underlyingContentLayer = itemLayer.underlyingContentLayer {
|
||||
self.scrollView.layer.addSublayer(underlyingContentLayer)
|
||||
}
|
||||
self.scrollView.layer.addSublayer(itemLayer)
|
||||
if let tintContentLayer = itemLayer.tintContentLayer {
|
||||
self.mirrorContentScrollView.layer.addSublayer(tintContentLayer)
|
||||
@ -3692,6 +3724,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
itemLayer.opacity = 0.0
|
||||
itemLayer.animateScale(from: 1.0, to: 0.01, duration: 0.16)
|
||||
itemLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.16, completion: { [weak itemLayer] _ in
|
||||
itemLayer?.underlyingContentLayer?.removeFromSuperlayer()
|
||||
itemLayer?.tintContentLayer?.removeFromSuperlayer()
|
||||
itemLayer?.removeFromSuperlayer()
|
||||
})
|
||||
@ -3712,6 +3745,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
}
|
||||
} else if let position = updatedItemPositions?[.item(id: id)], transitionHintInstalledGroupId != id.groupId {
|
||||
transition.setPosition(layer: itemLayer, position: position, completion: { [weak itemLayer] _ in
|
||||
itemLayer?.underlyingContentLayer?.removeFromSuperlayer()
|
||||
itemLayer?.tintContentLayer?.removeFromSuperlayer()
|
||||
itemLayer?.removeFromSuperlayer()
|
||||
})
|
||||
@ -3726,6 +3760,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
itemLayer.opacity = 0.0
|
||||
itemLayer.animateScale(from: 1.0, to: 0.01, duration: 0.2)
|
||||
itemLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, completion: { [weak itemLayer] _ in
|
||||
itemLayer?.underlyingContentLayer?.removeFromSuperlayer()
|
||||
itemLayer?.tintContentLayer?.removeFromSuperlayer()
|
||||
itemLayer?.removeFromSuperlayer()
|
||||
})
|
||||
@ -3746,6 +3781,7 @@ public final class EmojiPagerContentComponent: Component {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
itemLayer.underlyingContentLayer?.removeFromSuperlayer()
|
||||
itemLayer.tintContentLayer?.removeFromSuperlayer()
|
||||
itemLayer.removeFromSuperlayer()
|
||||
|
||||
|
||||
@ -126,6 +126,7 @@ public extension EmojiPagerContentComponent {
|
||||
if case .status = subject {
|
||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudFeaturedStatusEmoji)
|
||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudRecentStatusEmoji)
|
||||
orderedItemListCollectionIds.append(Namespaces.OrderedItemList.CloudUniqueStarGifts)
|
||||
|
||||
iconStatusEmoji = context.engine.stickers.loadedStickerPack(reference: .iconStatusEmoji, forceActualized: false)
|
||||
|> map { result -> [TelegramMediaFile] in
|
||||
@ -343,8 +344,11 @@ public extension EmojiPagerContentComponent {
|
||||
var featuredAvatarEmoji: OrderedItemListView?
|
||||
var featuredBackgroundIconEmoji: OrderedItemListView?
|
||||
var defaultTagReactions: OrderedItemListView?
|
||||
var uniqueGifts: OrderedItemListView?
|
||||
for orderedView in view.orderedItemListsViews {
|
||||
if orderedView.collectionId == Namespaces.OrderedItemList.LocalRecentEmoji {
|
||||
if orderedView.collectionId == Namespaces.OrderedItemList.CloudUniqueStarGifts {
|
||||
uniqueGifts = orderedView
|
||||
} else if orderedView.collectionId == Namespaces.OrderedItemList.LocalRecentEmoji {
|
||||
recentEmoji = orderedView
|
||||
} else if orderedView.collectionId == Namespaces.OrderedItemList.CloudFeaturedStatusEmoji {
|
||||
featuredStatusEmoji = orderedView
|
||||
@ -608,6 +612,38 @@ public extension EmojiPagerContentComponent {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let uniqueGifts, !uniqueGifts.items.isEmpty {
|
||||
//TODO:localize
|
||||
let groupId = "collectible"
|
||||
let groupIndex: Int
|
||||
if let current = itemGroupIndexById[groupId] {
|
||||
groupIndex = current
|
||||
} else {
|
||||
groupIndex = itemGroups.count
|
||||
itemGroupIndexById[groupId] = groupIndex
|
||||
itemGroups.append(ItemGroup(supergroupId: groupId, id: groupId, title: "COLLECTIBLES".uppercased(), subtitle: nil, badge: nil, isPremiumLocked: false, isFeatured: false, collapsedLineCount: 2, isClearable: false, headerItem: nil, items: []))
|
||||
}
|
||||
|
||||
for item in uniqueGifts.items {
|
||||
guard let item = item.contents.get(RecentStarGiftItem.self) else {
|
||||
continue
|
||||
}
|
||||
guard let animationData = EntityKeyboardAnimationData(gift: item.starGift) else {
|
||||
continue
|
||||
}
|
||||
let resultItem = EmojiPagerContentComponent.Item(
|
||||
animationData: animationData,
|
||||
content: .animation(animationData),
|
||||
itemFile: nil,
|
||||
itemGift: item.starGift,
|
||||
subgroupId: nil,
|
||||
icon: .none,
|
||||
tintMode: .none
|
||||
)
|
||||
itemGroups[groupIndex].items.append(resultItem)
|
||||
}
|
||||
}
|
||||
} else if case .channelStatus = subject {
|
||||
let resultItem = EmojiPagerContentComponent.Item(
|
||||
animationData: nil,
|
||||
|
||||
@ -25,6 +25,7 @@ swift_library(
|
||||
"//submodules/TextFormat",
|
||||
"//submodules/TelegramUI/Components/EmojiTextAttachmentView",
|
||||
"//submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
"//submodules/AnimatedStickerNode",
|
||||
"//submodules/TelegramAnimatedStickerNode",
|
||||
],
|
||||
|
||||
@ -12,6 +12,7 @@ import TextFormat
|
||||
import PeerInfoCoverComponent
|
||||
import AnimatedStickerNode
|
||||
import TelegramAnimatedStickerNode
|
||||
import EmojiStatusComponent
|
||||
|
||||
public final class GiftCompositionComponent: Component {
|
||||
public class ExternalState {
|
||||
@ -30,6 +31,9 @@ public final class GiftCompositionComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let subject: Subject
|
||||
let animationOffset: CGPoint?
|
||||
let animationScale: CGFloat?
|
||||
let displayAnimationStars: Bool
|
||||
let externalState: ExternalState?
|
||||
let requestUpdate: () -> Void
|
||||
|
||||
@ -37,12 +41,18 @@ public final class GiftCompositionComponent: Component {
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
subject: Subject,
|
||||
animationOffset: CGPoint? = nil,
|
||||
animationScale: CGFloat? = nil,
|
||||
displayAnimationStars: Bool = false,
|
||||
externalState: ExternalState? = nil,
|
||||
requestUpdate: @escaping () -> Void = {}
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.subject = subject
|
||||
self.animationOffset = animationOffset
|
||||
self.animationScale = animationScale
|
||||
self.displayAnimationStars = displayAnimationStars
|
||||
self.externalState = externalState
|
||||
self.requestUpdate = requestUpdate
|
||||
}
|
||||
@ -57,6 +67,15 @@ public final class GiftCompositionComponent: Component {
|
||||
if lhs.subject != rhs.subject {
|
||||
return false
|
||||
}
|
||||
if lhs.animationOffset != rhs.animationOffset {
|
||||
return false
|
||||
}
|
||||
if lhs.animationScale != rhs.animationScale {
|
||||
return false
|
||||
}
|
||||
if lhs.displayAnimationStars != rhs.displayAnimationStars {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@ -64,6 +83,8 @@ public final class GiftCompositionComponent: Component {
|
||||
private var component: GiftCompositionComponent?
|
||||
private weak var componentState: EmptyComponentState?
|
||||
|
||||
private var starsLayer: StarsEffectLayer?
|
||||
|
||||
private let background = ComponentView<Empty>()
|
||||
private var animationNode: AnimatedStickerNode?
|
||||
|
||||
@ -256,6 +277,12 @@ public final class GiftCompositionComponent: Component {
|
||||
if animateTransition, let backgroundView = self.background.view as? PeerInfoCoverComponent.View {
|
||||
backgroundView.animateTransition()
|
||||
}
|
||||
|
||||
var avatarCenter = CGPoint(x: availableSize.width / 2.0, y: 104.0)
|
||||
if let _ = component.animationScale {
|
||||
avatarCenter = CGPoint(x: avatarCenter.x, y: 67.0)
|
||||
}
|
||||
|
||||
let _ = self.background.update(
|
||||
transition: backgroundTransition,
|
||||
component: AnyComponent(PeerInfoCoverComponent(
|
||||
@ -263,9 +290,10 @@ public final class GiftCompositionComponent: Component {
|
||||
subject: .custom(backgroundColor, secondBackgroundColor, patternColor, patternFile?.fileId.id),
|
||||
files: files,
|
||||
isDark: false,
|
||||
avatarCenter: CGPoint(x: availableSize.width / 2.0, y: 104.0),
|
||||
avatarCenter: avatarCenter,
|
||||
avatarScale: 1.0,
|
||||
defaultHeight: availableSize.height,
|
||||
defaultHeight: 300.0,
|
||||
gradientOnTop: true,
|
||||
avatarTransitionFraction: 0.0,
|
||||
patternTransitionFraction: 0.0
|
||||
)),
|
||||
@ -292,17 +320,20 @@ public final class GiftCompositionComponent: Component {
|
||||
let iconSize = CGSize(width: 136.0, height: 136.0)
|
||||
|
||||
var startFromIndex: Int?
|
||||
var animationTransition = transition
|
||||
if animateTransition, let disappearingAnimationNode = self.animationNode {
|
||||
self.animationNode = nil
|
||||
startFromIndex = disappearingAnimationNode.currentFrameIndex
|
||||
disappearingAnimationNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
disappearingAnimationNode.view.removeFromSuperview()
|
||||
})
|
||||
animationTransition = .immediate
|
||||
}
|
||||
|
||||
if let file = animationFile {
|
||||
let animationNode: AnimatedStickerNode
|
||||
if self.animationNode == nil {
|
||||
animationTransition = .immediate
|
||||
animationNode = DefaultAnimatedStickerNodeImpl()
|
||||
animationNode.isUserInteractionEnabled = false
|
||||
self.animationNode = animationNode
|
||||
@ -333,9 +364,41 @@ public final class GiftCompositionComponent: Component {
|
||||
}
|
||||
}
|
||||
if let animationNode = self.animationNode {
|
||||
transition.setFrame(layer: animationNode.layer, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - iconSize.width) / 2.0), y: 20.0), size: iconSize))
|
||||
let offset = component.animationOffset ?? .zero
|
||||
var size = CGSize(width: iconSize.width, height: iconSize.height)
|
||||
if let scale = component.animationScale {
|
||||
size = CGSize(width: size.width * scale, height: size.height * scale)
|
||||
}
|
||||
let animationFrame = CGRect(origin: CGPoint(x: availableSize.width / 2.0 + offset.x - size.width / 2.0, y: 88.0 + offset.y - size.height / 2.0), size: size)
|
||||
animationNode.layer.bounds = CGRect(origin: .zero, size: iconSize)
|
||||
animationTransition.setPosition(layer: animationNode.layer, position: animationFrame.center)
|
||||
animationTransition.setScale(layer: animationNode.layer, scale: size.width / iconSize.width)
|
||||
|
||||
if component.displayAnimationStars {
|
||||
var starsTransition = transition
|
||||
let starsLayer: StarsEffectLayer
|
||||
if let current = self.starsLayer {
|
||||
starsLayer = current
|
||||
} else {
|
||||
starsTransition = .immediate
|
||||
starsLayer = StarsEffectLayer()
|
||||
self.layer.insertSublayer(starsLayer, below: animationNode.layer)
|
||||
self.starsLayer = starsLayer
|
||||
starsLayer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
}
|
||||
let starsSize = CGSize(width: 36.0, height: 36.0)
|
||||
starsLayer.update(color: .white, size: starsSize)
|
||||
starsLayer.bounds = CGRect(origin: .zero, size: starsSize)
|
||||
starsTransition.setPosition(layer: starsLayer, position: animationFrame.center)
|
||||
} else if let starsLayer = self.starsLayer {
|
||||
self.starsLayer = nil
|
||||
transition.setPosition(layer: starsLayer, position: animationFrame.center)
|
||||
starsLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
starsLayer.removeFromSuperlayer()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
@ -348,3 +411,56 @@ public final class GiftCompositionComponent: Component {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private final class StarsEffectLayer: SimpleLayer {
|
||||
private let emitterLayer = CAEmitterLayer()
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
|
||||
self.addSublayer(self.emitterLayer)
|
||||
}
|
||||
|
||||
override init(layer: Any) {
|
||||
super.init(layer: layer)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func setup(color: UIColor) {
|
||||
let emitter = CAEmitterCell()
|
||||
emitter.name = "emitter"
|
||||
emitter.contents = UIImage(bundleImageName: "Premium/Stars/Particle")?.cgImage
|
||||
emitter.birthRate = 8.0
|
||||
emitter.lifetime = 2.0
|
||||
emitter.velocity = 0.1
|
||||
emitter.scale = 0.12
|
||||
emitter.scaleRange = 0.02
|
||||
emitter.alphaRange = 0.1
|
||||
emitter.emissionRange = .pi * 2.0
|
||||
|
||||
let staticColors: [Any] = [
|
||||
color.withAlphaComponent(0.0).cgColor,
|
||||
color.withAlphaComponent(0.55).cgColor,
|
||||
color.withAlphaComponent(0.55).cgColor,
|
||||
color.withAlphaComponent(0.0).cgColor
|
||||
]
|
||||
let staticColorBehavior = CAEmitterCell.createEmitterBehavior(type: "colorOverLife")
|
||||
staticColorBehavior.setValue(staticColors, forKey: "colors")
|
||||
emitter.setValue([staticColorBehavior], forKey: "emitterBehaviors")
|
||||
self.emitterLayer.emitterCells = [emitter]
|
||||
}
|
||||
|
||||
func update(color: UIColor, size: CGSize) {
|
||||
if self.emitterLayer.emitterCells == nil {
|
||||
self.setup(color: color)
|
||||
}
|
||||
self.emitterLayer.emitterShape = .circle
|
||||
self.emitterLayer.emitterSize = size
|
||||
self.emitterLayer.emitterMode = .surface
|
||||
self.emitterLayer.frame = CGRect(origin: .zero, size: size)
|
||||
self.emitterLayer.emitterPosition = CGPoint(x: size.width / 2.0, y: size.height / 2.0)
|
||||
}
|
||||
}
|
||||
|
||||
@ -43,6 +43,7 @@ swift_library(
|
||||
"//submodules/TooltipUI",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftItemComponent",
|
||||
"//submodules/MoreButtonNode",
|
||||
"//submodules/TelegramUI/Components/EmojiStatusComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
||||
@ -22,6 +22,7 @@ import TextFormat
|
||||
import TelegramStringFormatting
|
||||
import StarsAvatarComponent
|
||||
import EmojiTextAttachmentView
|
||||
import EmojiStatusComponent
|
||||
import UndoUI
|
||||
import ConfettiEffect
|
||||
import PlainButtonComponent
|
||||
@ -34,6 +35,7 @@ import ContextUI
|
||||
private let modelButtonTag = GenericComponentViewTag()
|
||||
private let backdropButtonTag = GenericComponentViewTag()
|
||||
private let symbolButtonTag = GenericComponentViewTag()
|
||||
private let statusTag = GenericComponentViewTag()
|
||||
|
||||
private final class GiftViewSheetContent: CombinedComponent {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
@ -49,7 +51,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
let openMyGifts: () -> Void
|
||||
let transferGift: () -> Void
|
||||
let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
|
||||
let showAttributeInfo: (Any, Float) -> Void
|
||||
let shareGift: () -> Void
|
||||
let showAttributeInfo: (Any, String) -> Void
|
||||
let viewUpgraded: (EngineMessage.Id) -> Void
|
||||
let openMore: (ASDisplayNode, ContextGesture?) -> Void
|
||||
let getController: () -> ViewController?
|
||||
@ -66,7 +69,8 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
openMyGifts: @escaping () -> Void,
|
||||
transferGift: @escaping () -> Void,
|
||||
upgradeGift: @escaping ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>),
|
||||
showAttributeInfo: @escaping (Any, Float) -> Void,
|
||||
shareGift: @escaping () -> Void,
|
||||
showAttributeInfo: @escaping (Any, String) -> Void,
|
||||
viewUpgraded: @escaping (EngineMessage.Id) -> Void,
|
||||
openMore: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
||||
getController: @escaping () -> ViewController?
|
||||
@ -82,6 +86,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
self.openMyGifts = openMyGifts
|
||||
self.transferGift = transferGift
|
||||
self.upgradeGift = upgradeGift
|
||||
self.shareGift = shareGift
|
||||
self.showAttributeInfo = showAttributeInfo
|
||||
self.viewUpgraded = viewUpgraded
|
||||
self.openMore = openMore
|
||||
@ -123,6 +128,10 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
var upgradeFormDisposable: Disposable?
|
||||
var upgradeDisposable: Disposable?
|
||||
|
||||
var inWearPreview = false
|
||||
var pendingWear = false
|
||||
var pendingTakeOff = false
|
||||
|
||||
var sampleGiftAttributes: [StarGift.UniqueGift.Attribute]?
|
||||
let sampleDisposable = DisposableSet()
|
||||
|
||||
@ -135,13 +144,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
}
|
||||
private let optionsPromise = ValuePromise<[StarsTopUpOption]?>(nil)
|
||||
|
||||
var mockFiles: [TelegramMediaFile] = []
|
||||
var mockIconFiles: [TelegramMediaFile] = []
|
||||
var upgradedMockId: Int = 0
|
||||
var upgradedMockBackgroundColor: UIColor = .white
|
||||
var upgradedMockIcon: TelegramMediaFile?
|
||||
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
subject: GiftViewScreen.Subject,
|
||||
@ -278,6 +281,11 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
self.updated(transition: .spring(duration: 0.4))
|
||||
}
|
||||
|
||||
func requestWearPreview() {
|
||||
self.inWearPreview = true
|
||||
self.updated(transition: .spring(duration: 0.4))
|
||||
}
|
||||
|
||||
func commitUpgrade() {
|
||||
guard let arguments = self.subject.arguments, let peerId = arguments.peerId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else {
|
||||
return
|
||||
@ -343,6 +351,18 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
let animation = Child(GiftCompositionComponent.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let description = Child(MultilineTextComponent.self)
|
||||
|
||||
let transferButton = Child(PlainButtonComponent.self)
|
||||
let wearButton = Child(PlainButtonComponent.self)
|
||||
let shareButton = Child(PlainButtonComponent.self)
|
||||
|
||||
let wearAvatar = Child(AvatarComponent.self)
|
||||
let wearPeerName = Child(MultilineTextComponent.self)
|
||||
let wearPeerStatus = Child(MultilineTextComponent.self)
|
||||
let wearTitle = Child(MultilineTextComponent.self)
|
||||
let wearDescription = Child(MultilineTextComponent.self)
|
||||
let wearPerks = Child(List<Empty>.self)
|
||||
|
||||
let hiddenText = Child(MultilineTextComponent.self)
|
||||
let table = Child(TableComponent.self)
|
||||
let additionalText = Child(MultilineTextComponent.self)
|
||||
@ -456,12 +476,15 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
component: ButtonsComponent(
|
||||
theme: theme,
|
||||
isOverlay: showUpgradePreview || uniqueGift != nil,
|
||||
showMoreButton: uniqueGift != nil,
|
||||
showMoreButton: uniqueGift != nil && !state.inWearPreview,
|
||||
closePressed: { [weak state] in
|
||||
guard let state else {
|
||||
return
|
||||
}
|
||||
if state.inUpgradePreview {
|
||||
if state.inWearPreview {
|
||||
state.inWearPreview = false
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
} else if state.inUpgradePreview {
|
||||
state.inUpgradePreview = false
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
} else {
|
||||
@ -477,46 +500,247 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
)
|
||||
|
||||
var originY: CGFloat = 0.0
|
||||
|
||||
let animationHeight: CGFloat
|
||||
let animationSubject: GiftCompositionComponent.Subject?
|
||||
|
||||
let headerHeight: CGFloat
|
||||
let headerSubject: GiftCompositionComponent.Subject?
|
||||
if let uniqueGift {
|
||||
animationHeight = 240.0
|
||||
animationSubject = .unique(uniqueGift)
|
||||
if state.inWearPreview {
|
||||
headerHeight = 200.0
|
||||
} else if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId {
|
||||
headerHeight = 314.0
|
||||
} else {
|
||||
headerHeight = 240.0
|
||||
}
|
||||
headerSubject = .unique(uniqueGift)
|
||||
} else if state.inUpgradePreview, let attributes = state.sampleGiftAttributes {
|
||||
animationHeight = 258.0
|
||||
animationSubject = .preview(attributes)
|
||||
headerHeight = 258.0
|
||||
headerSubject = .preview(attributes)
|
||||
} else if case let .upgradePreview(attributes, _) = component.subject {
|
||||
animationHeight = 258.0
|
||||
animationSubject = .preview(attributes)
|
||||
headerHeight = 258.0
|
||||
headerSubject = .preview(attributes)
|
||||
} else if let animationFile {
|
||||
animationHeight = 210.0
|
||||
animationSubject = .generic(animationFile)
|
||||
headerHeight = 210.0
|
||||
headerSubject = .generic(animationFile)
|
||||
} else {
|
||||
animationHeight = 210.0
|
||||
animationSubject = nil
|
||||
headerHeight = 210.0
|
||||
headerSubject = nil
|
||||
}
|
||||
if let animationSubject {
|
||||
|
||||
var wearPeerNameChild: _UpdatedChildComponent?
|
||||
if state.inWearPreview, let uniqueGift {
|
||||
var peerName = ""
|
||||
if let accountPeer = state.peerMap[component.context.account.peerId] {
|
||||
peerName = accountPeer.displayTitle(strings: strings, displayOrder: nameDisplayOrder)
|
||||
}
|
||||
wearPeerNameChild = wearPeerName.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: peerName,
|
||||
font: Font.bold(28.0),
|
||||
textColor: .white,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let wearTitle = wearTitle.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: strings.Gift_Wear_Wear("\(uniqueGift.title) #\(uniqueGift.number)").string,
|
||||
font: Font.bold(24.0),
|
||||
textColor: theme.actionSheet.primaryTextColor,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
let wearDescription = wearDescription.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: strings.Gift_Wear_GetBenefits,
|
||||
font: Font.regular(15.0),
|
||||
textColor: theme.actionSheet.primaryTextColor,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1,
|
||||
lineSpacing: 0.2
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
var titleOriginY = headerHeight + 18.0
|
||||
context.add(wearTitle
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: titleOriginY + wearTitle.size.height))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
titleOriginY += wearTitle.size.height
|
||||
titleOriginY += 18.0
|
||||
|
||||
context.add(wearDescription
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: titleOriginY + wearDescription.size.height))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
}
|
||||
|
||||
var animationOffset: CGPoint?
|
||||
var animationScale: CGFloat?
|
||||
if let wearPeerNameChild {
|
||||
animationOffset = CGPoint(x: wearPeerNameChild.size.width / 2.0 + 20.0 - 12.0, y: 56.0)
|
||||
animationScale = 0.19
|
||||
}
|
||||
|
||||
if let headerSubject {
|
||||
let animation = animation.update(
|
||||
component: GiftCompositionComponent(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
subject: animationSubject,
|
||||
subject: headerSubject,
|
||||
animationOffset: animationOffset,
|
||||
animationScale: animationScale,
|
||||
displayAnimationStars: state.inWearPreview,
|
||||
externalState: giftCompositionExternalState,
|
||||
requestUpdate: { [weak state] in
|
||||
state?.updated()
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width, height: animationHeight),
|
||||
transition: .immediate
|
||||
availableSize: CGSize(width: context.availableSize.width, height: headerHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(animation
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: animationHeight / 2.0))
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: headerHeight / 2.0))
|
||||
)
|
||||
}
|
||||
originY += animationHeight
|
||||
|
||||
if showUpgradePreview {
|
||||
originY += headerHeight
|
||||
|
||||
let vibrantColor: UIColor
|
||||
if let previewPatternColor = giftCompositionExternalState.previewPatternColor {
|
||||
vibrantColor = previewPatternColor.withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3)
|
||||
} else {
|
||||
vibrantColor = UIColor.white.withAlphaComponent(0.6)
|
||||
}
|
||||
|
||||
if let wearPeerNameChild, let accountPeer = state.peerMap[component.context.account.peerId] {
|
||||
let wearAvatar = wearAvatar.update(
|
||||
component: AvatarComponent(
|
||||
context: component.context,
|
||||
theme: theme,
|
||||
peer: accountPeer
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: 100.0, height: 100.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(wearAvatar
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 67.0))
|
||||
.appear(.default(scale: true, alpha: true))
|
||||
.disappear(.default(scale: true, alpha: true))
|
||||
)
|
||||
|
||||
let wearPeerStatus = wearPeerStatus.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: strings.Presence_online,
|
||||
font: Font.regular(17.0),
|
||||
textColor: vibrantColor,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 5,
|
||||
lineSpacing: 0.2
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 50.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
context.add(wearPeerNameChild
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0 - 12.0, y: 144.0))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
context.add(wearPeerStatus
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 174.0))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
originY += 18.0
|
||||
originY += 28.0
|
||||
originY += 18.0
|
||||
originY += 20.0
|
||||
originY += 24.0
|
||||
|
||||
let textColor = theme.actionSheet.primaryTextColor
|
||||
let secondaryTextColor = theme.actionSheet.secondaryTextColor
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
|
||||
var items: [AnyComponentWithIdentity<Empty>] = []
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "badge",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: strings.Gift_Wear_Badge_Title,
|
||||
titleColor: textColor,
|
||||
text: strings.Gift_Wear_Badge_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/Collectible/Badge",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "design",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: strings.Gift_Wear_Design_Title,
|
||||
titleColor: textColor,
|
||||
text: strings.Gift_Wear_Design_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/BoostPerk/CoverColor",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
items.append(
|
||||
AnyComponentWithIdentity(
|
||||
id: "proof",
|
||||
component: AnyComponent(ParagraphComponent(
|
||||
title: strings.Gift_Wear_Proof_Title,
|
||||
titleColor: textColor,
|
||||
text: strings.Gift_Wear_Proof_Text,
|
||||
textColor: secondaryTextColor,
|
||||
accentColor: linkColor,
|
||||
iconName: "Premium/Collectible/Proof",
|
||||
iconColor: linkColor
|
||||
))
|
||||
)
|
||||
)
|
||||
|
||||
let perksSideInset = sideInset + 16.0
|
||||
let wearPerks = wearPerks.update(
|
||||
component: List(items),
|
||||
availableSize: CGSize(width: context.availableSize.width - perksSideInset * 2.0, height: 10000.0),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(wearPerks
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + wearPerks.size.height / 2.0))
|
||||
.appear(.default(alpha: true))
|
||||
.disappear(.default(alpha: true))
|
||||
)
|
||||
originY += wearPerks.size.height
|
||||
originY += 16.0
|
||||
} else if showUpgradePreview {
|
||||
let title: String
|
||||
let description: String
|
||||
let uniqueText: String
|
||||
@ -550,18 +774,12 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0 - 60.0, height: CGFloat.greatestFiniteMagnitude),
|
||||
transition: .immediate
|
||||
)
|
||||
let descriptionColor: UIColor
|
||||
if let previewPatternColor = giftCompositionExternalState.previewPatternColor {
|
||||
descriptionColor = previewPatternColor.withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3)
|
||||
} else {
|
||||
descriptionColor = UIColor.white.withAlphaComponent(0.6)
|
||||
}
|
||||
let upgradeDescription = upgradeDescription.update(
|
||||
component: BalancedTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: description,
|
||||
font: Font.regular(13.0),
|
||||
textColor: descriptionColor,
|
||||
textColor: vibrantColor,
|
||||
paragraphAlignment: .center
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
@ -637,7 +855,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
)
|
||||
)
|
||||
|
||||
let perksSideInset = sideInset + 12.0
|
||||
let perksSideInset = sideInset + 16.0
|
||||
let upgradePerks = upgradePerks.update(
|
||||
component: List(items),
|
||||
availableSize: CGSize(width: context.availableSize.width - perksSideInset * 2.0, height: 10000.0),
|
||||
@ -784,11 +1002,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
let textColor: UIColor
|
||||
if let _ = uniqueGift {
|
||||
textFont = Font.regular(13.0)
|
||||
if let previewPatternColor = giftCompositionExternalState.previewPatternColor {
|
||||
textColor = previewPatternColor.withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3)
|
||||
} else {
|
||||
textColor = UIColor.white.withAlphaComponent(0.6)
|
||||
}
|
||||
textColor = vibrantColor
|
||||
} else {
|
||||
textFont = soldOut ? Font.medium(15.0) : Font.regular(15.0)
|
||||
textColor = soldOut ? theme.list.itemDestructiveColor : theme.list.itemPrimaryTextColor
|
||||
@ -883,13 +1097,46 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
let tableLinkColor = theme.list.itemAccentColor
|
||||
var tableItems: [TableComponent.Item] = []
|
||||
|
||||
var isWearing = state.pendingWear
|
||||
|
||||
if !soldOut {
|
||||
if let uniqueGift {
|
||||
switch uniqueGift.owner {
|
||||
case let .peerId(peerId):
|
||||
if let peer = state.peerMap[peerId] {
|
||||
let ownerComponent: AnyComponent<Empty>
|
||||
if let _ = subject.arguments?.transferStars {
|
||||
if peer.id == component.context.account.peerId, peer.isPremium {
|
||||
let animationContent: EmojiStatusComponent.Content
|
||||
var color: UIColor?
|
||||
if state.pendingWear {
|
||||
var fileId: Int64?
|
||||
for attribute in uniqueGift.attributes {
|
||||
if case let .model(_, file, _) = attribute {
|
||||
fileId = file.fileId.id
|
||||
}
|
||||
if case let .backdrop(_, innerColor, _, _, _, _) = attribute {
|
||||
color = UIColor(rgb: UInt32(bitPattern: innerColor))
|
||||
}
|
||||
}
|
||||
if let fileId {
|
||||
animationContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 18.0, height: 18.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: tableLinkColor, loopMode: .count(2))
|
||||
} else {
|
||||
animationContent = .premium(color: tableLinkColor)
|
||||
}
|
||||
} else if let emojiStatus = peer.emojiStatus, !state.pendingTakeOff {
|
||||
animationContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 18.0, height: 18.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: tableLinkColor, loopMode: .count(2))
|
||||
if case let .starGift(id, _, _, _, _, innerColor, _, _, _) = emojiStatus.content {
|
||||
color = UIColor(rgb: UInt32(bitPattern: innerColor))
|
||||
if id == uniqueGift.id {
|
||||
isWearing = true
|
||||
state.pendingWear = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
animationContent = .premium(color: tableLinkColor)
|
||||
state.pendingTakeOff = false
|
||||
}
|
||||
|
||||
ownerComponent = AnyComponent(
|
||||
HStack([
|
||||
AnyComponentWithIdentity(
|
||||
@ -913,21 +1160,21 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
),
|
||||
AnyComponentWithIdentity(
|
||||
id: AnyHashable(1),
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(ButtonContentComponent(
|
||||
context: component.context,
|
||||
text: strings.Gift_Unique_Transfer,
|
||||
color: theme.list.itemAccentColor
|
||||
)),
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
context: component.context,
|
||||
animationCache: component.context.animationCache,
|
||||
animationRenderer: component.context.animationRenderer,
|
||||
content: animationContent,
|
||||
particleColor: color,
|
||||
size: CGSize(width: 18.0, height: 18.0),
|
||||
isVisibleForAnimations: true,
|
||||
action: {
|
||||
component.transferGift()
|
||||
Queue.mainQueue().after(1.0, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
|
||||
},
|
||||
tag: statusTag
|
||||
))
|
||||
)
|
||||
], spacing: 4.0)
|
||||
], spacing: 2.0)
|
||||
)
|
||||
} else {
|
||||
ownerComponent = AnyComponent(Button(
|
||||
@ -1052,6 +1299,93 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
|
||||
if let uniqueGift {
|
||||
if case let .peerId(peerId) = uniqueGift.owner, peerId == component.context.account.peerId {
|
||||
let buttonSpacing: CGFloat = 10.0
|
||||
let buttonWidth = floor(context.availableSize.width - sideInset * 2.0 - buttonSpacing * 2.0) / 3.0
|
||||
let buttonHeight: CGFloat = 58.0
|
||||
|
||||
let transferButton = transferButton.update(
|
||||
component: PlainButtonComponent(
|
||||
content: AnyComponent(
|
||||
HeaderButtonComponent(
|
||||
title: strings.Gift_View_Header_Transfer,
|
||||
iconName: "Premium/Collectible/Transfer"
|
||||
)
|
||||
),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
component.transferGift()
|
||||
Queue.mainQueue().after(1.0, {
|
||||
component.cancel(false)
|
||||
})
|
||||
}
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: buttonWidth, height: buttonHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(transferButton
|
||||
.position(CGPoint(x: sideInset + buttonWidth / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0))
|
||||
)
|
||||
|
||||
let controller = environment.controller
|
||||
let wearButton = wearButton.update(
|
||||
component: PlainButtonComponent(
|
||||
content: AnyComponent(
|
||||
HeaderButtonComponent(
|
||||
title: isWearing ? strings.Gift_View_Header_TakeOff : strings.Gift_View_Header_Wear,
|
||||
iconName: isWearing ? "Premium/Collectible/Unwear" : "Premium/Collectible/Wear"
|
||||
)
|
||||
),
|
||||
effectAlignment: .center,
|
||||
action: { [weak state] in
|
||||
if let state {
|
||||
if isWearing {
|
||||
state.pendingTakeOff = true
|
||||
state.pendingWear = false
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
|
||||
component.showAttributeInfo(statusTag, "You took off \(uniqueGift.title) #\(uniqueGift.number)")
|
||||
} else {
|
||||
if let controller = controller() as? GiftViewScreen {
|
||||
controller.dismissAllTooltips()
|
||||
}
|
||||
|
||||
state.requestWearPreview()
|
||||
}
|
||||
}
|
||||
}
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: buttonWidth, height: buttonHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(wearButton
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0))
|
||||
)
|
||||
|
||||
let shareButton = shareButton.update(
|
||||
component: PlainButtonComponent(
|
||||
content: AnyComponent(
|
||||
HeaderButtonComponent(
|
||||
title: strings.Gift_View_Header_Share,
|
||||
iconName: "Premium/Collectible/Share"
|
||||
)
|
||||
),
|
||||
effectAlignment: .center,
|
||||
action: {
|
||||
component.shareGift()
|
||||
}
|
||||
),
|
||||
environment: {},
|
||||
availableSize: CGSize(width: buttonWidth, height: buttonHeight),
|
||||
transition: context.transition
|
||||
)
|
||||
context.add(shareButton
|
||||
.position(CGPoint(x: context.availableSize.width - sideInset - buttonWidth / 2.0, y: headerHeight - buttonHeight / 2.0 - 16.0))
|
||||
)
|
||||
}
|
||||
|
||||
let showAttributeInfo = component.showAttributeInfo
|
||||
|
||||
let order: [StarGift.UniqueGift.Attribute.AttributeType] = [
|
||||
@ -1191,7 +1525,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
color: theme.list.itemAccentColor
|
||||
)),
|
||||
action: {
|
||||
showAttributeInfo(tag, percentage)
|
||||
showAttributeInfo(tag, strings.Gift_Unique_AttributeDescription(formatPercentage(percentage)).string)
|
||||
}
|
||||
).tagged(tag))
|
||||
))
|
||||
@ -1393,7 +1727,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
originY += table.size.height + 23.0
|
||||
}
|
||||
|
||||
if incoming && !converted && !upgraded && !showUpgradePreview {
|
||||
if incoming && !converted && !upgraded && !showUpgradePreview && !state.inWearPreview {
|
||||
let linkColor = theme.actionSheet.controlAccentColor
|
||||
if state.cachedSmallChevronImage == nil || state.cachedSmallChevronImage?.1 !== environment.theme {
|
||||
state.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: linkColor)!, theme)
|
||||
@ -1452,8 +1786,41 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
originY += 16.0
|
||||
}
|
||||
|
||||
let buttonSize = CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0)
|
||||
let buttonBackground = ButtonComponent.Background(
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9)
|
||||
)
|
||||
let buttonChild: _UpdatedChildComponent
|
||||
if state.inUpgradePreview {
|
||||
if state.inWearPreview, let uniqueGift {
|
||||
buttonChild = button.update(
|
||||
component: ButtonComponent(
|
||||
background: buttonBackground,
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("wear"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Gift_Wear_Start, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
),
|
||||
isEnabled: true,
|
||||
displaysProgress: false,
|
||||
action: { [weak state] in
|
||||
if let state {
|
||||
state.pendingWear = true
|
||||
state.pendingTakeOff = false
|
||||
state.inWearPreview = false
|
||||
state.updated(transition: .spring(duration: 0.4))
|
||||
|
||||
let _ = component.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).start()
|
||||
|
||||
Queue.mainQueue().after(0.2) {
|
||||
component.showAttributeInfo(statusTag, "You put on \(uniqueGift.title) #\(uniqueGift.number)")
|
||||
}
|
||||
}
|
||||
}),
|
||||
availableSize: buttonSize,
|
||||
transition: context.transition
|
||||
)
|
||||
} else if state.inUpgradePreview {
|
||||
if state.cachedStarImage == nil || state.cachedStarImage?.1 !== theme {
|
||||
state.cachedStarImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/PremiumIcon"), color: theme.list.itemCheckColors.foregroundColor)!, theme)
|
||||
}
|
||||
@ -1470,12 +1837,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
}
|
||||
buttonChild = button.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
cornerRadius: 10.0
|
||||
),
|
||||
background: buttonBackground,
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("upgrade"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(buttonAttributedString)))
|
||||
@ -1485,7 +1847,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
action: { [weak state] in
|
||||
state?.commitUpgrade()
|
||||
}),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
availableSize: buttonSize,
|
||||
transition: context.transition
|
||||
)
|
||||
} else if upgraded, let upgradeMessageIdId = subject.arguments?.upgradeMessageId, let originalMessageId = subject.arguments?.messageId {
|
||||
@ -1493,12 +1855,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
let buttonTitle = strings.Gift_View_ViewUpgraded
|
||||
buttonChild = button.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
cornerRadius: 10.0
|
||||
),
|
||||
background: buttonBackground,
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("button"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: buttonTitle, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
@ -1509,20 +1866,14 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
component.cancel(true)
|
||||
component.viewUpgraded(upgradeMessageId)
|
||||
}),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
availableSize: buttonSize,
|
||||
transition: context.transition
|
||||
)
|
||||
} else if incoming && !converted && !upgraded, let upgradeStars, upgradeStars > 0 {
|
||||
let buttonTitle = strings.Gift_View_UpgradeForFree
|
||||
buttonChild = button.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
cornerRadius: 10.0,
|
||||
isShimmering: true
|
||||
),
|
||||
background: buttonBackground.withIsShimmering(true),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("freeUpgrade"),
|
||||
component: AnyComponent(HStack([
|
||||
@ -1546,19 +1897,14 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
state?.requestUpgradePreview()
|
||||
}
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
availableSize: buttonSize,
|
||||
transition: context.transition
|
||||
)
|
||||
} else if incoming && !converted && !savedToProfile {
|
||||
let buttonTitle = savedToProfile ? strings.Gift_View_Hide : strings.Gift_View_Display
|
||||
buttonChild = button.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
cornerRadius: 10.0
|
||||
),
|
||||
background: buttonBackground,
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("button"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: buttonTitle, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
@ -1568,18 +1914,13 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
action: {
|
||||
component.updateSavedToProfile(!savedToProfile)
|
||||
}),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
availableSize: buttonSize,
|
||||
transition: context.transition
|
||||
)
|
||||
} else {
|
||||
buttonChild = button.update(
|
||||
component: ButtonComponent(
|
||||
background: ButtonComponent.Background(
|
||||
color: theme.list.itemCheckColors.fillColor,
|
||||
foreground: theme.list.itemCheckColors.foregroundColor,
|
||||
pressedColor: theme.list.itemCheckColors.fillColor.withMultipliedAlpha(0.9),
|
||||
cornerRadius: 10.0
|
||||
),
|
||||
background: buttonBackground,
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable("ok"),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSAttributedString(string: strings.Common_OK, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
@ -1589,7 +1930,7 @@ private final class GiftViewSheetContent: CombinedComponent {
|
||||
action: {
|
||||
component.cancel(true)
|
||||
}),
|
||||
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: 50.0),
|
||||
availableSize: buttonSize,
|
||||
transition: context.transition
|
||||
)
|
||||
}
|
||||
@ -1625,9 +1966,10 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
||||
let openMyGifts: () -> Void
|
||||
let transferGift: () -> Void
|
||||
let upgradeGift: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)
|
||||
let shareGift: () -> Void
|
||||
let viewUpgraded: (EngineMessage.Id) -> Void
|
||||
let openMore: (ASDisplayNode, ContextGesture?) -> Void
|
||||
let showAttributeInfo: (Any, Float) -> Void
|
||||
let showAttributeInfo: (Any, String) -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
@ -1640,9 +1982,10 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
||||
openMyGifts: @escaping () -> Void,
|
||||
transferGift: @escaping () -> Void,
|
||||
upgradeGift: @escaping ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>),
|
||||
shareGift: @escaping () -> Void,
|
||||
viewUpgraded: @escaping (EngineMessage.Id) -> Void,
|
||||
openMore: @escaping (ASDisplayNode, ContextGesture?) -> Void,
|
||||
showAttributeInfo: @escaping (Any, Float) -> Void
|
||||
showAttributeInfo: @escaping (Any, String) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
@ -1654,6 +1997,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
||||
self.openMyGifts = openMyGifts
|
||||
self.transferGift = transferGift
|
||||
self.upgradeGift = upgradeGift
|
||||
self.shareGift = shareGift
|
||||
self.viewUpgraded = viewUpgraded
|
||||
self.openMore = openMore
|
||||
self.showAttributeInfo = showAttributeInfo
|
||||
@ -1704,6 +2048,7 @@ private final class GiftViewSheetComponent: CombinedComponent {
|
||||
openMyGifts: context.component.openMyGifts,
|
||||
transferGift: context.component.transferGift,
|
||||
upgradeGift: context.component.upgradeGift,
|
||||
shareGift: context.component.shareGift,
|
||||
showAttributeInfo: context.component.showAttributeInfo,
|
||||
viewUpgraded: context.component.viewUpgraded,
|
||||
openMore: context.component.openMore,
|
||||
@ -1845,10 +2190,10 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
var sendGiftImpl: ((EnginePeer.Id) -> Void)?
|
||||
var openMyGiftsImpl: (() -> Void)?
|
||||
var transferGiftImpl: (() -> Void)?
|
||||
var showAttributeInfoImpl: ((Any, Float) -> Void)?
|
||||
var upgradeGiftImpl: ((Int64?, Bool) -> Signal<ProfileGiftsContext.State.StarGift, UpgradeStarGiftError>)?
|
||||
var shareGiftImpl: (() -> Void)?
|
||||
var openMoreImpl: ((ASDisplayNode, ContextGesture?) -> Void)?
|
||||
var showAttributeInfoImpl: ((Any, String) -> Void)?
|
||||
var viewUpgradedImpl: ((EngineMessage.Id) -> Void)?
|
||||
|
||||
super.init(
|
||||
@ -1880,14 +2225,17 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
upgradeGift: { formId, keepOriginalInfo in
|
||||
return upgradeGiftImpl?(formId, keepOriginalInfo) ?? .complete()
|
||||
},
|
||||
shareGift: {
|
||||
shareGiftImpl?()
|
||||
},
|
||||
viewUpgraded: { messageId in
|
||||
viewUpgradedImpl?(messageId)
|
||||
},
|
||||
openMore: { node, gesture in
|
||||
openMoreImpl?(node, gesture)
|
||||
},
|
||||
showAttributeInfo: { tag, rarity in
|
||||
showAttributeInfoImpl?(tag, rarity)
|
||||
showAttributeInfo: { tag, text in
|
||||
showAttributeInfoImpl?(tag, text)
|
||||
}
|
||||
),
|
||||
navigationBarAppearance: .none,
|
||||
@ -2224,7 +2572,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
})
|
||||
}
|
||||
|
||||
showAttributeInfoImpl = { [weak self] tag, rarity in
|
||||
showAttributeInfoImpl = { [weak self] tag, text in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
@ -2235,7 +2583,7 @@ public class GiftViewScreen: ViewControllerComponentContainer {
|
||||
}
|
||||
|
||||
let location = CGRect(origin: CGPoint(x: absoluteLocation.x, y: absoluteLocation.y - 12.0), size: CGSize())
|
||||
let controller = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: presentationData.strings.Gift_Unique_AttributeDescription(formatPercentage(rarity)).string), location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in
|
||||
let controller = TooltipScreen(account: self.context.account, sharedContext: self.context.sharedContext, text: .plain(text: text), style: .wide, location: .point(location, .bottom), displayDuration: .default, inset: 16.0, shouldDismissOnTouch: { _, _ in
|
||||
return .ignore
|
||||
})
|
||||
self.present(controller, in: .current)
|
||||
@ -3042,3 +3390,147 @@ private final class GiftViewContextReferenceContentSource: ContextReferenceConte
|
||||
return ContextControllerReferenceViewInfo(referenceView: self.sourceNode.view, contentAreaInScreenSpace: UIScreen.main.bounds)
|
||||
}
|
||||
}
|
||||
|
||||
private final class HeaderButtonComponent: CombinedComponent {
|
||||
let title: String
|
||||
let iconName: String
|
||||
|
||||
public init(
|
||||
title: String,
|
||||
iconName: String
|
||||
) {
|
||||
self.title = title
|
||||
self.iconName = iconName
|
||||
}
|
||||
|
||||
static func ==(lhs: HeaderButtonComponent, rhs: HeaderButtonComponent) -> Bool {
|
||||
if lhs.title != rhs.title {
|
||||
return false
|
||||
}
|
||||
if lhs.iconName != rhs.iconName {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
static var body: Body {
|
||||
let background = Child(RoundedRectangle.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let icon = Child(BundleIconComponent.self)
|
||||
|
||||
return { context in
|
||||
let component = context.component
|
||||
|
||||
let background = background.update(
|
||||
component: RoundedRectangle(
|
||||
color: UIColor.white.withAlphaComponent(0.16),
|
||||
cornerRadius: 10.0
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(background
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: context.availableSize.height / 2.0))
|
||||
)
|
||||
|
||||
let icon = icon.update(
|
||||
component: BundleIconComponent(
|
||||
name: component.iconName,
|
||||
tintColor: UIColor.white
|
||||
),
|
||||
availableSize: context.availableSize,
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(icon
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 22.0))
|
||||
)
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: component.title,
|
||||
font: Font.regular(11.0),
|
||||
textColor: UIColor.white,
|
||||
paragraphAlignment: .natural
|
||||
)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 1
|
||||
),
|
||||
availableSize: CGSize(width: context.availableSize.width - 16.0, height: context.availableSize.height),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(title
|
||||
.position(CGPoint(x: context.availableSize.width / 2.0, y: 42.0))
|
||||
)
|
||||
|
||||
return context.availableSize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final class AvatarComponent: Component {
|
||||
let context: AccountContext
|
||||
let theme: PresentationTheme
|
||||
let peer: EnginePeer
|
||||
|
||||
init(context: AccountContext, theme: PresentationTheme, peer: EnginePeer) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.peer = peer
|
||||
}
|
||||
|
||||
static func ==(lhs: AvatarComponent, rhs: AvatarComponent) -> Bool {
|
||||
if lhs.context !== rhs.context {
|
||||
return false
|
||||
}
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
if lhs.peer != rhs.peer {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let avatarNode: AvatarNode
|
||||
|
||||
private var component: AvatarComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.avatarNode = AvatarNode(font: avatarPlaceholderFont(size: 26.0))
|
||||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addSubnode(self.avatarNode)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: AvatarComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
self.avatarNode.frame = CGRect(origin: .zero, size: availableSize)
|
||||
self.avatarNode.setPeer(
|
||||
context: component.context,
|
||||
theme: component.theme,
|
||||
peer: component.peer,
|
||||
synchronousLoad: true
|
||||
)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,6 +102,7 @@ public final class PeerInfoCoverComponent: Component {
|
||||
public let avatarCenter: CGPoint
|
||||
public let avatarScale: CGFloat
|
||||
public let defaultHeight: CGFloat
|
||||
public let gradientOnTop: Bool
|
||||
public let avatarTransitionFraction: CGFloat
|
||||
public let patternTransitionFraction: CGFloat
|
||||
|
||||
@ -113,6 +114,7 @@ public final class PeerInfoCoverComponent: Component {
|
||||
avatarCenter: CGPoint,
|
||||
avatarScale: CGFloat,
|
||||
defaultHeight: CGFloat,
|
||||
gradientOnTop: Bool = false,
|
||||
avatarTransitionFraction: CGFloat,
|
||||
patternTransitionFraction: CGFloat
|
||||
) {
|
||||
@ -123,6 +125,7 @@ public final class PeerInfoCoverComponent: Component {
|
||||
self.avatarCenter = avatarCenter
|
||||
self.avatarScale = avatarScale
|
||||
self.defaultHeight = defaultHeight
|
||||
self.gradientOnTop = gradientOnTop
|
||||
self.avatarTransitionFraction = avatarTransitionFraction
|
||||
self.patternTransitionFraction = patternTransitionFraction
|
||||
}
|
||||
@ -149,6 +152,9 @@ public final class PeerInfoCoverComponent: Component {
|
||||
if lhs.defaultHeight != rhs.defaultHeight {
|
||||
return false
|
||||
}
|
||||
if lhs.gradientOnTop != rhs.gradientOnTop {
|
||||
return false
|
||||
}
|
||||
if lhs.avatarTransitionFraction != rhs.avatarTransitionFraction {
|
||||
return false
|
||||
}
|
||||
@ -166,6 +172,7 @@ public final class PeerInfoCoverComponent: Component {
|
||||
private let avatarBackgroundGradientLayer: SimpleGradientLayer
|
||||
private let backgroundPatternContainer: UIView
|
||||
|
||||
private var currentSize: CGSize?
|
||||
private var component: PeerInfoCoverComponent?
|
||||
private var state: EmptyComponentState?
|
||||
|
||||
@ -180,6 +187,7 @@ public final class PeerInfoCoverComponent: Component {
|
||||
self.backgroundGradientLayer = SimpleGradientLayer()
|
||||
|
||||
self.avatarBackgroundGradientLayer = SimpleGradientLayer()
|
||||
self.avatarBackgroundGradientLayer.opacity = 0.0
|
||||
let baseAvatarGradientAlpha: CGFloat = 0.4
|
||||
let numSteps = 6
|
||||
self.avatarBackgroundGradientLayer.colors = (0 ..< numSteps).map { i in
|
||||
@ -221,13 +229,41 @@ public final class PeerInfoCoverComponent: Component {
|
||||
self.patternImageDisposable?.dispose()
|
||||
}
|
||||
|
||||
public func willAnimateIn() {
|
||||
for layer in self.avatarPatternContentLayers {
|
||||
layer.opacity = 0.0
|
||||
}
|
||||
}
|
||||
|
||||
public func animateIn() {
|
||||
guard let _ = self.currentSize, let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
for layer in self.avatarPatternContentLayers {
|
||||
layer.opacity = 1.0
|
||||
layer.animatePosition(
|
||||
from: component.avatarCenter,
|
||||
to: layer.position,
|
||||
duration: 0.4,
|
||||
timingFunction: kCAMediaTimingFunctionSpring
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public func animateTransition() {
|
||||
if let gradientSnapshotLayer = self.backgroundGradientLayer.snapshotContentTree() {
|
||||
gradientSnapshotLayer.frame = self.backgroundGradientLayer.frame
|
||||
self.layer.insertSublayer(gradientSnapshotLayer, above: self.backgroundGradientLayer)
|
||||
gradientSnapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
gradientSnapshotLayer.removeFromSuperlayer()
|
||||
let backgroundSnapshotLayer = SimpleLayer()
|
||||
backgroundSnapshotLayer.allowsGroupOpacity = true
|
||||
backgroundSnapshotLayer.backgroundColor = self.backgroundView.backgroundColor?.cgColor
|
||||
backgroundSnapshotLayer.frame = self.backgroundView.frame
|
||||
self.layer.insertSublayer(backgroundSnapshotLayer, above: self.backgroundGradientLayer)
|
||||
|
||||
backgroundSnapshotLayer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
backgroundSnapshotLayer.removeFromSuperlayer()
|
||||
})
|
||||
gradientSnapshotLayer.frame = self.backgroundGradientLayer.convert(self.backgroundGradientLayer.bounds, to: self.backgroundView.layer)
|
||||
backgroundSnapshotLayer.addSublayer(gradientSnapshotLayer)
|
||||
}
|
||||
for layer in self.avatarPatternContentLayers {
|
||||
if let _ = layer.contents, let snapshot = layer.snapshotContentTree() {
|
||||
@ -295,6 +331,11 @@ public final class PeerInfoCoverComponent: Component {
|
||||
func update(component: PeerInfoCoverComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let previousComponent = self.component
|
||||
self.component = component
|
||||
self.currentSize = availableSize
|
||||
|
||||
if case .custom = component.subject {
|
||||
self.layer.allowsGroupOpacity = true
|
||||
}
|
||||
|
||||
if previousComponent?.subject?.fileId != component.subject?.fileId {
|
||||
if let fileId = component.subject?.fileId, fileId != 0 {
|
||||
@ -349,18 +390,18 @@ public final class PeerInfoCoverComponent: Component {
|
||||
secondaryBackgroundColor = .clear
|
||||
}
|
||||
|
||||
self.backgroundView.backgroundColor = secondaryBackgroundColor
|
||||
|
||||
let gradientWidth: CGFloat
|
||||
let gradientHeight: CGFloat = component.defaultHeight
|
||||
if case .custom = component.subject {
|
||||
if availableSize.width < availableSize.height {
|
||||
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.25)
|
||||
} else {
|
||||
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 0.5)
|
||||
}
|
||||
gradientWidth = gradientHeight
|
||||
self.backgroundView.backgroundColor = backgroundColor
|
||||
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: component.avatarCenter.y / gradientHeight)
|
||||
self.backgroundGradientLayer.endPoint = CGPoint(x: 1.0, y: 1.0)
|
||||
self.backgroundGradientLayer.type = .radial
|
||||
self.backgroundGradientLayer.colors = [secondaryBackgroundColor.cgColor, backgroundColor.cgColor]
|
||||
} else {
|
||||
gradientWidth = availableSize.width
|
||||
self.backgroundView.backgroundColor = secondaryBackgroundColor
|
||||
self.backgroundGradientLayer.startPoint = CGPoint(x: 0.5, y: 1.0)
|
||||
self.backgroundGradientLayer.endPoint = CGPoint(x: 0.5, y: 0.0)
|
||||
self.backgroundGradientLayer.type = .axial
|
||||
@ -368,8 +409,7 @@ public final class PeerInfoCoverComponent: Component {
|
||||
}
|
||||
self.backgroundGradientLayer.anchorPoint = CGPoint(x: 0.0, y: 1.0)
|
||||
|
||||
let gradientHeight: CGFloat = component.defaultHeight
|
||||
let backgroundGradientFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height - gradientHeight), size: CGSize(width: availableSize.width, height: gradientHeight))
|
||||
let backgroundGradientFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - gradientWidth) / 2.0), y: component.gradientOnTop ? 0.0 : availableSize.height - gradientHeight), size: CGSize(width: gradientWidth, height: gradientHeight))
|
||||
if !transition.animation.isImmediate {
|
||||
let previousPosition = self.backgroundGradientLayer.position
|
||||
let updatedPosition = CGPoint(x: backgroundGradientFrame.minX, y: backgroundGradientFrame.maxY)
|
||||
@ -426,11 +466,7 @@ public final class PeerInfoCoverComponent: Component {
|
||||
|
||||
let backgroundPatternContainerFrame = CGRect(origin: CGPoint(x: 0.0, y: availableSize.height), size: CGSize(width: availableSize.width, height: 0.0))
|
||||
transition.containedViewLayoutTransition.updateFrameAdditive(view: self.backgroundPatternContainer, frame: backgroundPatternContainerFrame)
|
||||
// if component.peer?.id == component.context.account.peerId {
|
||||
// transition.setAlpha(view: self.backgroundPatternContainer, alpha: 0.0)
|
||||
// } else {
|
||||
transition.setAlpha(view: self.backgroundPatternContainer, alpha: component.patternTransitionFraction)
|
||||
// }
|
||||
transition.setAlpha(view: self.backgroundPatternContainer, alpha: component.patternTransitionFraction)
|
||||
|
||||
var baseDistance: CGFloat = 72.0
|
||||
var baseRowDistance: CGFloat = 28.0
|
||||
|
||||
@ -166,6 +166,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
|
||||
var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Signal<(TelegramMediaFile, LoadedStickerPack)?, NoError>, Bool) -> Void)?
|
||||
var displayStatusPremiumIntro: (() -> Void)?
|
||||
var displayUniqueGiftInfo: ((UIView, String) -> Void)?
|
||||
|
||||
var navigateToForum: (() -> Void)?
|
||||
|
||||
@ -408,6 +409,16 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.displayPremiumIntro?(self.isAvatarExpanded ? self.titleExpandedCredibilityIconView : self.titleCredibilityIconView, nil, .never(), self.isAvatarExpanded)
|
||||
}
|
||||
|
||||
func invokeDisplayGiftInfo() {
|
||||
guard case let .emojiStatus(status) = self.currentStatusIcon, case let .starGift(_, _, title, slug, _, _, _, _, _) = status.content else {
|
||||
return
|
||||
}
|
||||
let slugComponents = slug.components(separatedBy: "-")
|
||||
if let numberString = slugComponents.last {
|
||||
self.displayUniqueGiftInfo?(self.isAvatarExpanded ? self.titleExpandedStatusIconView : self.titleStatusIconView, "\(title) #\(numberString)")
|
||||
}
|
||||
}
|
||||
|
||||
func initiateAvatarExpansion(gallery: Bool, first: Bool) {
|
||||
if let peer = self.peer, peer.profileImageRepresentations.isEmpty && gallery {
|
||||
self.requestOpenAvatarForEditing?(false)
|
||||
@ -554,7 +565,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
credibilityIcon = .verified
|
||||
}
|
||||
if let verificationIconFileId = peer.verificationIconFileId {
|
||||
verifiedIcon = .emojiStatus(PeerEmojiStatus(fileId: verificationIconFileId, expirationDate: nil))
|
||||
verifiedIcon = .emojiStatus(PeerEmojiStatus(content: .emoji(fileId: verificationIconFileId), expirationDate: nil))
|
||||
}
|
||||
}
|
||||
|
||||
@ -805,7 +816,7 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
emojiRegularStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: presentationData.theme.list.mediaPlaceholderColor, themeColor: navigationContentsAccentColor, loopMode: .forever)
|
||||
emojiExpandedStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: navigationContentsAccentColor, themeColor: navigationContentsAccentColor, loopMode: .forever)
|
||||
}
|
||||
|
||||
|
||||
let iconSize = self.titleCredibilityIconView.update(
|
||||
transition: ComponentTransition(navigationTransition),
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
@ -857,6 +868,8 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
self.currentStatusIcon = statusIcon
|
||||
|
||||
var currentEmojiStatus: PeerEmojiStatus?
|
||||
var particleColor: UIColor?
|
||||
|
||||
let emojiRegularStatusContent: EmojiStatusComponent.Content
|
||||
let emojiExpandedStatusContent: EmojiStatusComponent.Content
|
||||
switch statusIcon {
|
||||
@ -864,11 +877,15 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
currentEmojiStatus = emojiStatus
|
||||
emojiRegularStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: presentationData.theme.list.mediaPlaceholderColor, themeColor: navigationContentsAccentColor, loopMode: .forever)
|
||||
emojiExpandedStatusContent = .animation(content: .customEmoji(fileId: emojiStatus.fileId), size: CGSize(width: 80.0, height: 80.0), placeholderColor: navigationContentsAccentColor, themeColor: navigationContentsAccentColor, loopMode: .forever)
|
||||
|
||||
if let _ = emojiStatus.color {
|
||||
particleColor = UIColor.white
|
||||
}
|
||||
default:
|
||||
emojiRegularStatusContent = .none
|
||||
emojiExpandedStatusContent = .none
|
||||
}
|
||||
|
||||
|
||||
let iconSize = self.titleStatusIconView.update(
|
||||
transition: ComponentTransition(navigationTransition),
|
||||
component: AnyComponent(EmojiStatusComponent(
|
||||
@ -876,13 +893,18 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
animationCache: self.animationCache,
|
||||
animationRenderer: self.animationRenderer,
|
||||
content: emojiRegularStatusContent,
|
||||
particleColor: particleColor,
|
||||
isVisibleForAnimations: true,
|
||||
useSharedAnimation: true,
|
||||
action: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.displayPremiumIntro?(strongSelf.titleStatusIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), false)
|
||||
if let _ = particleColor {
|
||||
strongSelf.invokeDisplayGiftInfo()
|
||||
} else {
|
||||
strongSelf.displayPremiumIntro?(strongSelf.titleStatusIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), false)
|
||||
}
|
||||
},
|
||||
emojiFileUpdated: { [weak self] emojiFile in
|
||||
guard let strongSelf = self else {
|
||||
@ -932,13 +954,18 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
animationCache: self.animationCache,
|
||||
animationRenderer: self.animationRenderer,
|
||||
content: emojiExpandedStatusContent,
|
||||
particleColor: particleColor,
|
||||
isVisibleForAnimations: true,
|
||||
useSharedAnimation: true,
|
||||
action: { [weak self] in
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
strongSelf.displayPremiumIntro?(strongSelf.titleExpandedStatusIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), true)
|
||||
if let _ = particleColor {
|
||||
strongSelf.invokeDisplayGiftInfo()
|
||||
} else {
|
||||
strongSelf.displayPremiumIntro?(strongSelf.titleExpandedStatusIconView, currentEmojiStatus, strongSelf.emojiStatusFileAndPackTitle.get(), true)
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
@ -2217,11 +2244,22 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
transition.updateFrame(view: self.backgroundBannerView, frame: bannerFrame)
|
||||
}
|
||||
|
||||
let backgroundCoverSubject: PeerInfoCoverComponent.Subject?
|
||||
var backgroundCoverAnimateIn = false
|
||||
if let status = peer?.emojiStatus, case let .starGift(_, _, _, _, patternFileId, innerColor, outerColor, patternColor, _) = status.content {
|
||||
backgroundCoverSubject = .custom(UIColor(rgb: UInt32(bitPattern: innerColor)), UIColor(rgb: UInt32(bitPattern: outerColor)), UIColor(rgb: UInt32(bitPattern: patternColor)), patternFileId)
|
||||
backgroundCoverAnimateIn = true
|
||||
} else if let peer {
|
||||
backgroundCoverSubject = .peer(EnginePeer(peer))
|
||||
} else {
|
||||
backgroundCoverSubject = nil
|
||||
}
|
||||
|
||||
let backgroundCoverSize = self.backgroundCover.update(
|
||||
transition: ComponentTransition(transition),
|
||||
component: AnyComponent(PeerInfoCoverComponent(
|
||||
context: self.context,
|
||||
subject: peer.flatMap { .peer(EnginePeer($0)) },
|
||||
subject: backgroundCoverSubject,
|
||||
files: [:],
|
||||
isDark: presentationData.theme.overallDarkAppearance,
|
||||
avatarCenter: apparentAvatarFrame.center,
|
||||
@ -2233,9 +2271,25 @@ final class PeerInfoHeaderNode: ASDisplayNode {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: width + bannerInset * 2.0, height: apparentBackgroundHeight + bannerInset)
|
||||
)
|
||||
if let backgroundCoverView = self.backgroundCover.view {
|
||||
if let backgroundCoverView = self.backgroundCover.view as? PeerInfoCoverComponent.View {
|
||||
if backgroundCoverView.superview == nil {
|
||||
self.backgroundBannerView.addSubview(backgroundCoverView)
|
||||
|
||||
if backgroundCoverAnimateIn {
|
||||
if !self.isAvatarExpanded {
|
||||
backgroundCoverView.willAnimateIn()
|
||||
Queue.mainQueue().after(0.2) {
|
||||
backgroundCoverView.animateIn()
|
||||
}
|
||||
Queue.mainQueue().after(0.44) {
|
||||
self.invokeDisplayGiftInfo()
|
||||
}
|
||||
} else {
|
||||
Queue.mainQueue().after(0.44) {
|
||||
self.invokeDisplayGiftInfo()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if additive {
|
||||
transition.updateFrameAdditive(view: backgroundCoverView, frame: CGRect(origin: CGPoint(x: -3.0, y: bannerFrame.height - backgroundCoverSize.height - bannerInset), size: backgroundCoverSize))
|
||||
|
||||
@ -4661,6 +4661,27 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
||||
})
|
||||
}
|
||||
|
||||
self.headerNode.displayUniqueGiftInfo = { [weak self] sourceView, text in
|
||||
guard let self, let controller = self.controller else {
|
||||
return
|
||||
}
|
||||
let sourceRect = sourceView.convert(sourceView.bounds, to: controller.view)
|
||||
let tooltipController = TooltipScreen(
|
||||
context: self.context,
|
||||
account: self.context.account,
|
||||
sharedContext: self.context.sharedContext,
|
||||
text: .attributedString(text: NSAttributedString(string: text, font: Font.semibold(11.0), textColor: .white)),
|
||||
style: .customBlur(UIColor(rgb: self.headerNode.isAvatarExpanded ? 0x000000 : 0x92c8de, alpha: 0.65), -4.0),
|
||||
arrowStyle: .small,
|
||||
location: .point(sourceRect, .bottom),
|
||||
cornerRadius: 10.0,
|
||||
shouldDismissOnTouch: { _, _ in
|
||||
return .dismiss(consume: false)
|
||||
}
|
||||
)
|
||||
controller.present(tooltipController, in: .current)
|
||||
}
|
||||
|
||||
self.headerNode.displayStatusPremiumIntro = { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
|
||||
@ -727,7 +727,7 @@ final class ChannelAppearanceScreenComponent: Component {
|
||||
}
|
||||
|
||||
if let result {
|
||||
self.updatedPeerStatus = PeerEmojiStatus(fileId: result.fileId.id, expirationDate: timestamp)
|
||||
self.updatedPeerStatus = PeerEmojiStatus(content: .emoji(fileId: result.fileId.id), expirationDate: timestamp)
|
||||
} else {
|
||||
self.updatedPeerStatus = .some(nil)
|
||||
}
|
||||
|
||||
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Badge.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Badge.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "nft_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Badge.imageset/nft_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Badge.imageset/nft_30.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Proof.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Proof.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "badge_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Proof.imageset/badge_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Proof.imageset/badge_30.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Share.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Share.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "share_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Share.imageset/share_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Share.imageset/share_30.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transfer.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transfer.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "transfer_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transfer.imageset/transfer_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Transfer.imageset/transfer_30.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unwear.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unwear.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "wearoff_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unwear.imageset/wearoff_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Unwear.imageset/wearoff_30.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Wear.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Wear.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "wear_30.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Wear.imageset/wear_30.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Collectible/Wear.imageset/wear_30.pdf
vendored
Normal file
Binary file not shown.
@ -84,7 +84,7 @@ final class ChatVerifiedPeerTitlePanelNode: ChatTitleAccessoryPanelNode {
|
||||
let _ = ApplicationSpecificNotice.setDisplayedPeerVerification(accountManager: self.context.sharedContext.accountManager, peerId: peer.id).start()
|
||||
}
|
||||
|
||||
let emojiStatus = PeerEmojiStatus(fileId: verification.iconFileId, expirationDate: nil)
|
||||
let emojiStatus = PeerEmojiStatus(content: .emoji(fileId: verification.iconFileId), expirationDate: nil)
|
||||
let emojiStatusTextNode = self.emojiStatusTextNode
|
||||
|
||||
let description = verification.description
|
||||
|
||||
@ -600,7 +600,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
}
|
||||
}
|
||||
|
||||
let textSize: CGSize
|
||||
var textSize: CGSize
|
||||
|
||||
var isTextWithEntities = false
|
||||
switch self.text {
|
||||
@ -630,6 +630,9 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
|
||||
environment: {},
|
||||
containerSize: CGSize(width: containerWidth - contentInset * 2.0 - animationSize.width - animationSpacing - buttonInset, height: 1000000.0)
|
||||
)
|
||||
if case let .customBlur(_, inset) = self.tooltipStyle, inset < 0.0 {
|
||||
textSize.height -= 3.0
|
||||
}
|
||||
} else {
|
||||
textSize = self.textView.update(
|
||||
transition: .immediate,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user