Various improvements

This commit is contained in:
Ilya Laktyushin 2025-01-21 01:15:01 +04:00
parent c73f24f5f2
commit d6964efa67
32 changed files with 488 additions and 101 deletions

View File

@ -13753,3 +13753,11 @@ Sorry for the inconvenience.";
"Gift.Withdraw.EnterPassword.Title" = "Enter Password"; "Gift.Withdraw.EnterPassword.Title" = "Enter Password";
"Gift.Withdraw.EnterPassword.Text" = "Please enter your Two-Step Verification password to complete this action."; "Gift.Withdraw.EnterPassword.Text" = "Please enter your Two-Step Verification password to complete this action.";
"Gift.Withdraw.EnterPassword.Done" = "Proceed"; "Gift.Withdraw.EnterPassword.Done" = "Proceed";
"PeerInfo.Gifts.SortByValue" = "Sort by Value";
"PeerInfo.Gifts.SortByDate" = "Sort by Date";
"PeerInfo.Gifts.Unlimited" = "Unlimited";
"PeerInfo.Gifts.Limited" = "Limited";
"PeerInfo.Gifts.Unique" = "Unique";
"PeerInfo.Gifts.Displayed" = "Displayed";
"PeerInfo.Gifts.Hidden" = "Hidden";

View File

@ -389,8 +389,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1710230755] = { return Api.InputInvoice.parse_inputInvoiceStars($0) } dict[1710230755] = { return Api.InputInvoice.parse_inputInvoiceStars($0) }
dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) } dict[-122978821] = { return Api.InputMedia.parse_inputMediaContact($0) }
dict[-428884101] = { return Api.InputMedia.parse_inputMediaDice($0) } dict[-428884101] = { return Api.InputMedia.parse_inputMediaDice($0) }
dict[1946579745] = { return Api.InputMedia.parse_inputMediaDocument($0) } dict[-1468646731] = { return Api.InputMedia.parse_inputMediaDocument($0) }
dict[-149933938] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) } dict[2006319353] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) }
dict[-1771768449] = { return Api.InputMedia.parse_inputMediaEmpty($0) } dict[-1771768449] = { return Api.InputMedia.parse_inputMediaEmpty($0) }
dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($0) } dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($0) }
dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) } dict[-1759532989] = { return Api.InputMedia.parse_inputMediaGeoLive($0) }
@ -401,7 +401,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) } dict[-440664550] = { return Api.InputMedia.parse_inputMediaPhotoExternal($0) }
dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) } dict[261416433] = { return Api.InputMedia.parse_inputMediaPoll($0) }
dict[-1979852936] = { return Api.InputMedia.parse_inputMediaStory($0) } dict[-1979852936] = { return Api.InputMedia.parse_inputMediaStory($0) }
dict[-264125395] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) } dict[58495792] = { return Api.InputMedia.parse_inputMediaUploadedDocument($0) }
dict[505969924] = { return Api.InputMedia.parse_inputMediaUploadedPhoto($0) } dict[505969924] = { return Api.InputMedia.parse_inputMediaUploadedPhoto($0) }
dict[-1052959727] = { return Api.InputMedia.parse_inputMediaVenue($0) } dict[-1052959727] = { return Api.InputMedia.parse_inputMediaVenue($0) }
dict[-1038383031] = { return Api.InputMedia.parse_inputMediaWebPage($0) } dict[-1038383031] = { return Api.InputMedia.parse_inputMediaWebPage($0) }
@ -620,7 +620,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[1313731771] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) } dict[1313731771] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) }
dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) } dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) }
dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) } dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) }
dict[-608307692] = { return Api.MessageMedia.parse_messageMediaDocument($0) } dict[1389939929] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) } dict[1038967584] = { return Api.MessageMedia.parse_messageMediaEmpty($0) }
dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) } dict[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) }
dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) } dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) }

View File

@ -432,8 +432,8 @@ public extension Api {
indirect enum InputMedia: TypeConstructorDescription { indirect enum InputMedia: TypeConstructorDescription {
case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String) case inputMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String)
case inputMediaDice(emoticon: String) case inputMediaDice(emoticon: String)
case inputMediaDocument(flags: Int32, id: Api.InputDocument, videoCover: Api.InputPhoto?, ttlSeconds: Int32?, query: String?) case inputMediaDocument(flags: Int32, id: Api.InputDocument, videoCover: Api.InputPhoto?, videoTimestamp: Int32?, ttlSeconds: Int32?, query: String?)
case inputMediaDocumentExternal(flags: Int32, url: String, ttlSeconds: Int32?, videoCover: Api.InputPhoto?) case inputMediaDocumentExternal(flags: Int32, url: String, ttlSeconds: Int32?, videoCover: Api.InputPhoto?, videoTimestamp: Int32?)
case inputMediaEmpty case inputMediaEmpty
case inputMediaGame(id: Api.InputGame) case inputMediaGame(id: Api.InputGame)
case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?) case inputMediaGeoLive(flags: Int32, geoPoint: Api.InputGeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?)
@ -444,7 +444,7 @@ public extension Api {
case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?) case inputMediaPhotoExternal(flags: Int32, url: String, ttlSeconds: Int32?)
case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?) case inputMediaPoll(flags: Int32, poll: Api.Poll, correctAnswers: [Buffer]?, solution: String?, solutionEntities: [Api.MessageEntity]?)
case inputMediaStory(peer: Api.InputPeer, id: Int32) 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]?, videoCover: Api.InputPhoto?, ttlSeconds: Int32?) case inputMediaUploadedDocument(flags: Int32, file: Api.InputFile, thumb: Api.InputFile?, mimeType: String, attributes: [Api.DocumentAttribute], stickers: [Api.InputDocument]?, videoCover: Api.InputPhoto?, videoTimestamp: Int32?, ttlSeconds: Int32?)
case inputMediaUploadedPhoto(flags: Int32, file: Api.InputFile, stickers: [Api.InputDocument]?, 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 inputMediaVenue(geoPoint: Api.InputGeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String)
case inputMediaWebPage(flags: Int32, url: String) case inputMediaWebPage(flags: Int32, url: String)
@ -466,24 +466,26 @@ public extension Api {
} }
serializeString(emoticon, buffer: buffer, boxed: false) serializeString(emoticon, buffer: buffer, boxed: false)
break break
case .inputMediaDocument(let flags, let id, let videoCover, let ttlSeconds, let query): case .inputMediaDocument(let flags, let id, let videoCover, let videoTimestamp, let ttlSeconds, let query):
if boxed { if boxed {
buffer.appendInt32(1946579745) buffer.appendInt32(-1468646731)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
id.serialize(buffer, true) id.serialize(buffer, true)
if Int(flags) & Int(1 << 3) != 0 {videoCover!.serialize(buffer, true)} if Int(flags) & Int(1 << 3) != 0 {videoCover!.serialize(buffer, true)}
if Int(flags) & Int(1 << 4) != 0 {serializeInt32(videoTimestamp!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)} 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)} if Int(flags) & Int(1 << 1) != 0 {serializeString(query!, buffer: buffer, boxed: false)}
break break
case .inputMediaDocumentExternal(let flags, let url, let ttlSeconds, let videoCover): case .inputMediaDocumentExternal(let flags, let url, let ttlSeconds, let videoCover, let videoTimestamp):
if boxed { if boxed {
buffer.appendInt32(-149933938) buffer.appendInt32(2006319353)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(url, buffer: buffer, boxed: false) serializeString(url, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 0) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {videoCover!.serialize(buffer, true)} if Int(flags) & Int(1 << 2) != 0 {videoCover!.serialize(buffer, true)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(videoTimestamp!, buffer: buffer, boxed: false)}
break break
case .inputMediaEmpty: case .inputMediaEmpty:
if boxed { if boxed {
@ -582,9 +584,9 @@ public extension Api {
peer.serialize(buffer, true) peer.serialize(buffer, true)
serializeInt32(id, buffer: buffer, boxed: false) serializeInt32(id, buffer: buffer, boxed: false)
break break
case .inputMediaUploadedDocument(let flags, let file, let thumb, let mimeType, let attributes, let stickers, let videoCover, let ttlSeconds): case .inputMediaUploadedDocument(let flags, let file, let thumb, let mimeType, let attributes, let stickers, let videoCover, let videoTimestamp, let ttlSeconds):
if boxed { if boxed {
buffer.appendInt32(-264125395) buffer.appendInt32(58495792)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
file.serialize(buffer, true) file.serialize(buffer, true)
@ -601,6 +603,7 @@ public extension Api {
item.serialize(buffer, true) item.serialize(buffer, true)
}} }}
if Int(flags) & Int(1 << 6) != 0 {videoCover!.serialize(buffer, true)} if Int(flags) & Int(1 << 6) != 0 {videoCover!.serialize(buffer, true)}
if Int(flags) & Int(1 << 7) != 0 {serializeInt32(videoTimestamp!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 1) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)}
break break
case .inputMediaUploadedPhoto(let flags, let file, let stickers, let ttlSeconds): case .inputMediaUploadedPhoto(let flags, let file, let stickers, let ttlSeconds):
@ -643,10 +646,10 @@ public extension Api {
return ("inputMediaContact", [("phoneNumber", phoneNumber as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("vcard", vcard as Any)]) return ("inputMediaContact", [("phoneNumber", phoneNumber as Any), ("firstName", firstName as Any), ("lastName", lastName as Any), ("vcard", vcard as Any)])
case .inputMediaDice(let emoticon): case .inputMediaDice(let emoticon):
return ("inputMediaDice", [("emoticon", emoticon as Any)]) return ("inputMediaDice", [("emoticon", emoticon as Any)])
case .inputMediaDocument(let flags, let id, let videoCover, let ttlSeconds, let query): case .inputMediaDocument(let flags, let id, let videoCover, let videoTimestamp, 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)]) return ("inputMediaDocument", [("flags", flags as Any), ("id", id as Any), ("videoCover", videoCover as Any), ("videoTimestamp", videoTimestamp as Any), ("ttlSeconds", ttlSeconds as Any), ("query", query as Any)])
case .inputMediaDocumentExternal(let flags, let url, let ttlSeconds, let videoCover): case .inputMediaDocumentExternal(let flags, let url, let ttlSeconds, let videoCover, let videoTimestamp):
return ("inputMediaDocumentExternal", [("flags", flags as Any), ("url", url as Any), ("ttlSeconds", ttlSeconds as Any), ("videoCover", videoCover as Any)]) return ("inputMediaDocumentExternal", [("flags", flags as Any), ("url", url as Any), ("ttlSeconds", ttlSeconds as Any), ("videoCover", videoCover as Any), ("videoTimestamp", videoTimestamp as Any)])
case .inputMediaEmpty: case .inputMediaEmpty:
return ("inputMediaEmpty", []) return ("inputMediaEmpty", [])
case .inputMediaGame(let id): case .inputMediaGame(let id):
@ -667,8 +670,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)]) 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): case .inputMediaStory(let peer, let id):
return ("inputMediaStory", [("peer", peer as Any), ("id", id as Any)]) 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 videoCover, let ttlSeconds): case .inputMediaUploadedDocument(let flags, let file, let thumb, let mimeType, let attributes, let stickers, let videoCover, let videoTimestamp, 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)]) 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), ("videoTimestamp", videoTimestamp as Any), ("ttlSeconds", ttlSeconds as Any)])
case .inputMediaUploadedPhoto(let flags, let file, let stickers, let ttlSeconds): 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)]) 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): case .inputMediaVenue(let geoPoint, let title, let address, let provider, let venueId, let venueType):
@ -721,16 +724,19 @@ public extension Api {
_3 = Api.parse(reader, signature: signature) as? Api.InputPhoto _3 = Api.parse(reader, signature: signature) as? Api.InputPhoto
} } } }
var _4: Int32? var _4: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_4 = reader.readInt32() } if Int(_1!) & Int(1 << 4) != 0 {_4 = reader.readInt32() }
var _5: String? var _5: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) } if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt32() }
var _6: String?
if Int(_1!) & Int(1 << 1) != 0 {_6 = parseString(reader) }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil let _c3 = (Int(_1!) & Int(1 << 3) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 { let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
return Api.InputMedia.inputMediaDocument(flags: _1!, id: _2!, videoCover: _3, ttlSeconds: _4, query: _5) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.InputMedia.inputMediaDocument(flags: _1!, id: _2!, videoCover: _3, videoTimestamp: _4, ttlSeconds: _5, query: _6)
} }
else { else {
return nil return nil
@ -747,12 +753,15 @@ public extension Api {
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.InputPhoto _4 = Api.parse(reader, signature: signature) as? Api.InputPhoto
} } } }
var _5: Int32?
if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 { let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
return Api.InputMedia.inputMediaDocumentExternal(flags: _1!, url: _2!, ttlSeconds: _3, videoCover: _4) if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.InputMedia.inputMediaDocumentExternal(flags: _1!, url: _2!, ttlSeconds: _3, videoCover: _4, videoTimestamp: _5)
} }
else { else {
return nil return nil
@ -987,7 +996,9 @@ public extension Api {
_7 = Api.parse(reader, signature: signature) as? Api.InputPhoto _7 = Api.parse(reader, signature: signature) as? Api.InputPhoto
} } } }
var _8: Int32? var _8: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_8 = reader.readInt32() } if Int(_1!) & Int(1 << 7) != 0 {_8 = reader.readInt32() }
var _9: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_9 = reader.readInt32() }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = _2 != nil let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil
@ -995,9 +1006,10 @@ public extension Api {
let _c5 = _5 != nil let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil let _c7 = (Int(_1!) & Int(1 << 6) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 1) == 0) || _8 != nil let _c8 = (Int(_1!) & Int(1 << 7) == 0) || _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 { let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil
return Api.InputMedia.inputMediaUploadedDocument(flags: _1!, file: _2!, thumb: _3, mimeType: _4!, attributes: _5!, stickers: _6, videoCover: _7, ttlSeconds: _8) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.InputMedia.inputMediaUploadedDocument(flags: _1!, file: _2!, thumb: _3, mimeType: _4!, attributes: _5!, stickers: _6, videoCover: _7, videoTimestamp: _8, ttlSeconds: _9)
} }
else { else {
return nil return nil

View File

@ -710,7 +710,7 @@ public extension Api {
indirect enum MessageMedia: TypeConstructorDescription { indirect enum MessageMedia: TypeConstructorDescription {
case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int64) case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int64)
case messageMediaDice(value: Int32, emoticon: String) case messageMediaDice(value: Int32, emoticon: String)
case messageMediaDocument(flags: Int32, document: Api.Document?, altDocuments: [Api.Document]?, videoCover: Api.Photo?, ttlSeconds: Int32?) case messageMediaDocument(flags: Int32, document: Api.Document?, altDocuments: [Api.Document]?, videoCover: Api.Photo?, videoTimestamp: Int32?, ttlSeconds: Int32?)
case messageMediaEmpty case messageMediaEmpty
case messageMediaGame(game: Api.Game) case messageMediaGame(game: Api.Game)
case messageMediaGeo(geo: Api.GeoPoint) case messageMediaGeo(geo: Api.GeoPoint)
@ -745,9 +745,9 @@ public extension Api {
serializeInt32(value, buffer: buffer, boxed: false) serializeInt32(value, buffer: buffer, boxed: false)
serializeString(emoticon, buffer: buffer, boxed: false) serializeString(emoticon, buffer: buffer, boxed: false)
break break
case .messageMediaDocument(let flags, let document, let altDocuments, let videoCover, let ttlSeconds): case .messageMediaDocument(let flags, let document, let altDocuments, let videoCover, let videoTimestamp, let ttlSeconds):
if boxed { if boxed {
buffer.appendInt32(-608307692) buffer.appendInt32(1389939929)
} }
serializeInt32(flags, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {document!.serialize(buffer, true)} if Int(flags) & Int(1 << 0) != 0 {document!.serialize(buffer, true)}
@ -757,6 +757,7 @@ public extension Api {
item.serialize(buffer, true) item.serialize(buffer, true)
}} }}
if Int(flags) & Int(1 << 9) != 0 {videoCover!.serialize(buffer, true)} if Int(flags) & Int(1 << 9) != 0 {videoCover!.serialize(buffer, true)}
if Int(flags) & Int(1 << 10) != 0 {serializeInt32(videoTimestamp!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 2) != 0 {serializeInt32(ttlSeconds!, buffer: buffer, boxed: false)}
break break
case .messageMediaEmpty: case .messageMediaEmpty:
@ -910,8 +911,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)]) 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): case .messageMediaDice(let value, let emoticon):
return ("messageMediaDice", [("value", value as Any), ("emoticon", emoticon as Any)]) return ("messageMediaDice", [("value", value as Any), ("emoticon", emoticon as Any)])
case .messageMediaDocument(let flags, let document, let altDocuments, let videoCover, let ttlSeconds): case .messageMediaDocument(let flags, let document, let altDocuments, let videoCover, let videoTimestamp, let ttlSeconds):
return ("messageMediaDocument", [("flags", flags as Any), ("document", document as Any), ("altDocuments", altDocuments as Any), ("videoCover", videoCover as Any), ("ttlSeconds", ttlSeconds as Any)]) return ("messageMediaDocument", [("flags", flags as Any), ("document", document as Any), ("altDocuments", altDocuments as Any), ("videoCover", videoCover as Any), ("videoTimestamp", videoTimestamp as Any), ("ttlSeconds", ttlSeconds as Any)])
case .messageMediaEmpty: case .messageMediaEmpty:
return ("messageMediaEmpty", []) return ("messageMediaEmpty", [])
case .messageMediaGame(let game): case .messageMediaGame(let game):
@ -996,14 +997,17 @@ public extension Api {
_4 = Api.parse(reader, signature: signature) as? Api.Photo _4 = Api.parse(reader, signature: signature) as? Api.Photo
} } } }
var _5: Int32? var _5: Int32?
if Int(_1!) & Int(1 << 2) != 0 {_5 = reader.readInt32() } if Int(_1!) & Int(1 << 10) != 0 {_5 = reader.readInt32() }
var _6: Int32?
if Int(_1!) & Int(1 << 2) != 0 {_6 = reader.readInt32() }
let _c1 = _1 != nil let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 5) == 0) || _3 != nil let _c3 = (Int(_1!) & Int(1 << 5) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 9) == 0) || _4 != nil let _c4 = (Int(_1!) & Int(1 << 9) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil let _c5 = (Int(_1!) & Int(1 << 10) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 { let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
return Api.MessageMedia.messageMediaDocument(flags: _1!, document: _2, altDocuments: _3, videoCover: _4, ttlSeconds: _5) if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.MessageMedia.messageMediaDocument(flags: _1!, document: _2, altDocuments: _3, videoCover: _4, videoTimestamp: _5, ttlSeconds: _6)
} }
else { else {
return nil return nil

View File

@ -350,7 +350,8 @@ func textMediaAndExpirationTimerFromApiMedia(_ media: Api.MessageMedia?, _ peerI
case let .messageMediaGeoLive(_, geo, heading, period, proximityNotificationRadius): 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) 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) return (mediaMap, nil, nil, nil, nil)
case let .messageMediaDocument(flags, document, altDocuments, coverPhoto, ttlSeconds): case let .messageMediaDocument(flags, document, altDocuments, coverPhoto, videoTimestamp, ttlSeconds):
let _ = videoTimestamp
if let document = document { if let document = document {
if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments, videoCover: coverPhoto) { if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments, videoCover: coverPhoto) {
return (mediaFile, ttlSeconds, (flags & (1 << 3)) != 0, (flags & (1 << 4)) != 0, nil) return (mediaFile, ttlSeconds, (flags & (1 << 3)) != 0, (flags & (1 << 4)) != 0, nil)

View File

@ -229,7 +229,7 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post
} }
|> mapToSignal { validatedResource -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in |> mapToSignal { validatedResource -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
if let validatedResource = validatedResource.updatedResource as? TelegramCloudMediaResourceWithFileReference, let reference = validatedResource.fileReference { 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)), videoCover: nil, 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, videoTimestamp: nil, ttlSeconds: nil, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
} else { } else {
return .fail(.generic) 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())), videoCover: nil, 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, videoTimestamp: nil, ttlSeconds: nil, query: emojiSearchQuery), text), reuploadInfo: nil, cacheReferenceKey: nil)))
} }
} else { } 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) 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)
@ -866,7 +866,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
return .single(.progress(PendingMessageUploadedContentProgress(progress: 1.0))) return .single(.progress(PendingMessageUploadedContentProgress(progress: 1.0)))
|> then( |> 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)), videoCover: nil, 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, videoTimestamp: nil, ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil)))
) )
} }
referenceKey = key referenceKey = key
@ -1122,11 +1122,11 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
} }
if ttlSeconds != nil { 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, videoCover: videoCoverPhoto, 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: videoCoverPhoto, videoTimestamp: nil, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: referenceKey)))
} }
if !isGrouped { if !isGrouped {
let resultInfo = PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaUploadedDocument(flags: flags, file: inputFile, thumb: thumbnailFile, mimeType: file.mimeType, attributes: inputDocumentAttributesFromFileAttributes(file.attributes), stickers: stickers, videoCover: videoCoverPhoto, 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: videoCoverPhoto, videoTimestamp: nil, ttlSeconds: ttlSeconds), text), reuploadInfo: nil, cacheReferenceKey: referenceKey)
return .single(.content(resultInfo)) return .single(.content(resultInfo))
} }
@ -1137,11 +1137,11 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
|> mapError { _ -> PendingMessageUploadError in } |> mapError { _ -> PendingMessageUploadError in }
|> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in |> mapToSignal { inputPeer -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
if let inputPeer = inputPeer { 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, videoCover: videoCoverPhoto, 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: videoCoverPhoto, videoTimestamp: nil, ttlSeconds: ttlSeconds)))
|> mapError { _ -> PendingMessageUploadError in return .generic } |> mapError { _ -> PendingMessageUploadError in return .generic }
|> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in |> mapToSignal { result -> Signal<PendingMessageUploadedContentResult, PendingMessageUploadError> in
switch result { 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 { 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 flags: Int32 = 0
var ttlSeconds: Int32? var ttlSeconds: Int32?
@ -1156,7 +1156,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili
flags |= (1 << 3) flags |= (1 << 3)
} }
let result: PendingMessageUploadedContentResult = .content(PendingMessageUploadedContentAndReuploadInfo(content: .media(.inputMediaDocument(flags: flags, id: .inputDocument(id: resource.fileId, accessHash: resource.accessHash, fileReference: Buffer(data: fileReference)), videoCover: videoCoverPhoto, 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: videoCoverPhoto, videoTimestamp: nil, ttlSeconds: ttlSeconds, query: nil), text), reuploadInfo: nil, cacheReferenceKey: nil))
if let _ = ttlSeconds { if let _ = ttlSeconds {
return .single(result) return .single(result)
} else { } else {

View File

@ -702,7 +702,7 @@ private func uploadedFile(account: Account, data: Data, mimeType: String, attrib
|> map { next -> UploadMediaEvent in |> map { next -> UploadMediaEvent in
switch next { switch next {
case let .inputFile(inputFile): case let .inputFile(inputFile):
return .result(Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, videoCover: nil, ttlSeconds: nil)) return .result(Api.InputMedia.inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: mimeType, attributes: inputDocumentAttributesFromFileAttributes(attributes), stickers: nil, videoCover: nil, videoTimestamp: nil, ttlSeconds: nil))
case .inputSecretFile: case .inputSecretFile:
preconditionFailure() preconditionFailure()
case let .progress(progress): case let .progress(progress):

View File

@ -158,11 +158,11 @@ public func standaloneUploadedFile(postbox: Postbox, network: Network, peerId: P
if let _ = thumbnailFile { if let _ = thumbnailFile {
flags |= 1 << 2 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, videoCover: 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, videoTimestamp: nil, ttlSeconds: nil)))
|> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapError { _ -> StandaloneUploadMediaError in return .generic }
|> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in |> mapToSignal { media -> Signal<StandaloneUploadMediaEvent, StandaloneUploadMediaError> in
switch media { switch media {
case let .messageMediaDocument(_, document, altDocuments, _, _): case let .messageMediaDocument(_, document, altDocuments, _, _, _):
if let document = document { if let document = document {
if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments) { if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments) {
return .single(.result(.media(.standalone(media: mediaFile)))) return .single(.result(.media(.standalone(media: mediaFile))))

View File

@ -52,7 +52,7 @@ extension Api.MessageMedia {
} else { } else {
return nil return nil
} }
case let .messageMediaDocument(_, document, _, _, _): case let .messageMediaDocument(_, document, _, _, _, _):
if let document = document { if let document = document {
return collectPreCachedResources(for: document) return collectPreCachedResources(for: document)
} }
@ -626,14 +626,14 @@ extension Api.EncryptedMessage {
extension Api.InputMedia { extension Api.InputMedia {
func withUpdatedStickers(_ stickers: [Api.InputDocument]?) -> Api.InputMedia { func withUpdatedStickers(_ stickers: [Api.InputDocument]?) -> Api.InputMedia {
switch self { switch self {
case let .inputMediaUploadedDocument(flags, file, thumb, mimeType, attributes, _, videoCover, ttlSeconds): case let .inputMediaUploadedDocument(flags, file, thumb, mimeType, attributes, _, videoCover, videoTimestamp, ttlSeconds):
var flags = flags var flags = flags
var attributes = attributes var attributes = attributes
if let _ = stickers { if let _ = stickers {
flags |= (1 << 0) flags |= (1 << 0)
attributes.append(.documentAttributeHasStickers) attributes.append(.documentAttributeHasStickers)
} }
return .inputMediaUploadedDocument(flags: flags, file: file, thumb: thumb, mimeType: mimeType, attributes: attributes, stickers: stickers, videoCover: videoCover, ttlSeconds: ttlSeconds) return .inputMediaUploadedDocument(flags: flags, file: file, thumb: thumb, mimeType: mimeType, attributes: attributes, stickers: stickers, videoCover: videoCover, videoTimestamp: videoTimestamp, ttlSeconds: ttlSeconds)
case let .inputMediaUploadedPhoto(flags, file, _, ttlSeconds): case let .inputMediaUploadedPhoto(flags, file, _, ttlSeconds):
var flags = flags var flags = flags
if let _ = stickers { if let _ = stickers {

View File

@ -153,7 +153,7 @@ public extension TelegramEngine {
default: default:
break break
} }
inputMedia = .inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: resolvedMimeType, attributes: attributes, stickers: nil, videoCover: nil, ttlSeconds: nil) inputMedia = .inputMediaUploadedDocument(flags: 0, file: inputFile, thumb: nil, mimeType: resolvedMimeType, attributes: attributes, stickers: nil, videoCover: nil, videoTimestamp: nil, ttlSeconds: nil)
} }
case let .progress(value): case let .progress(value):
return .single(value) return .single(value)

View File

@ -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(.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)) 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 { } 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())), videoCover: nil, 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, videoTimestamp: nil, ttlSeconds: nil, query: nil))
} }
} }
if language == 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(.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)) 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 { } 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())), videoCover: nil, 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, videoTimestamp: nil, ttlSeconds: nil, query: nil))
} }
} }
transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current -> CachedPeerData? in 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 { if let result = result, case let .content(uploadedContent) = result, case let .media(media, _) = uploadedContent.content {
inputMedia = media inputMedia = media
} else if case let .existing(media) = media, let file = media as? TelegramMediaFile, let resource = file.resource as? CloudDocumentMediaResource { } 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, videoCover: 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, videoTimestamp: nil, ttlSeconds: nil)
updatingCoverTime = true updatingCoverTime = true
} else { } else {
inputMedia = nil inputMedia = nil
@ -2099,7 +2099,7 @@ extension Stories.StoredItem {
var parsedAlternativeMedia: [Media] = [] var parsedAlternativeMedia: [Media] = []
switch media { switch media {
case let .messageMediaDocument(_, _, altDocuments, _, _): case let .messageMediaDocument(_, _, altDocuments, _, _, _):
if let altDocuments { if let altDocuments {
parsedAlternativeMedia = altDocuments.compactMap { telegramMediaFileFromApiDocument($0, altDocuments: []) } parsedAlternativeMedia = altDocuments.compactMap { telegramMediaFileFromApiDocument($0, altDocuments: []) }
} }

View File

@ -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(.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)) 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 { } 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())), videoCover: nil, 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, videoTimestamp: nil, ttlSeconds: nil, query: nil))
} }
} }

View File

@ -925,7 +925,10 @@ private final class ProfileGiftsContextImpl {
private let cacheDisposable = MetaDisposable() private let cacheDisposable = MetaDisposable()
private let actionDisposable = MetaDisposable() private let actionDisposable = MetaDisposable()
private var sorting: ProfileGiftsContext.Sorting = .date
private var filter: ProfileGiftsContext.Filters = ProfileGiftsContext.Filters.All
private var gifts: [ProfileGiftsContext.State.StarGift] = [] private var gifts: [ProfileGiftsContext.State.StarGift] = []
private var filteredGifts: [ProfileGiftsContext.State.StarGift] = []
private var count: Int32? private var count: Int32?
private var dataState: ProfileGiftsContext.State.DataState = .ready(canLoadMore: true, nextOffset: nil) private var dataState: ProfileGiftsContext.State.DataState = .ready(canLoadMore: true, nextOffset: nil)
private var notificationsEnabled: Bool? private var notificationsEnabled: Bool?
@ -1111,13 +1114,47 @@ private final class ProfileGiftsContextImpl {
self.pushState() self.pushState()
} }
func updateFilter(_ filter: ProfileGiftsContext.Filters) {
self.filter = filter
self.pushState()
}
func updateSorting(_ sorting: ProfileGiftsContext.Sorting) {
self.sorting = sorting
self.pushState()
}
private func pushState() { private func pushState() {
self._state = ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState, notificationsEnabled: self.notificationsEnabled) let state = ProfileGiftsContext.State(filter: self.filter, sorting: self.sorting, gifts: self.gifts, count: self.count, dataState: self.dataState, notificationsEnabled: self.notificationsEnabled)
self.stateValue.set(.single(ProfileGiftsContext.State(gifts: self.gifts, count: self.count, dataState: self.dataState, notificationsEnabled: self.notificationsEnabled))) self._state = state
self.stateValue.set(.single(state))
} }
} }
public final class ProfileGiftsContext { public final class ProfileGiftsContext {
public struct Filters: OptionSet {
public var rawValue: Int32
public init(rawValue: Int32) {
self.rawValue = rawValue
}
public static let unlimited = Filters(rawValue: 1 << 0)
public static let limited = Filters(rawValue: 1 << 1)
public static let unique = Filters(rawValue: 1 << 2)
public static let displayed = Filters(rawValue: 1 << 3)
public static let hidden = Filters(rawValue: 1 << 4)
public static var All: Filters {
return [.unlimited, .limited, .unique, .displayed, .hidden]
}
}
public enum Sorting: Equatable {
case date
case value
}
public struct State: Equatable { public struct State: Equatable {
public struct StarGift: Equatable, Codable { public struct StarGift: Equatable, Codable {
enum CodingKeys: String, CodingKey { enum CodingKeys: String, CodingKey {
@ -1273,6 +1310,9 @@ public final class ProfileGiftsContext {
case ready(canLoadMore: Bool, nextOffset: String?) case ready(canLoadMore: Bool, nextOffset: String?)
} }
public var filter: Filters
public var sorting: Sorting
public var gifts: [ProfileGiftsContext.State.StarGift] public var gifts: [ProfileGiftsContext.State.StarGift]
public var count: Int32? public var count: Int32?
public var dataState: ProfileGiftsContext.State.DataState public var dataState: ProfileGiftsContext.State.DataState
@ -1349,6 +1389,18 @@ public final class ProfileGiftsContext {
} }
} }
public func updateFilter(_ filter: ProfileGiftsContext.Filters) {
self.impl.with { impl in
impl.updateFilter(filter)
}
}
public func updateSorting(_ sorting: ProfileGiftsContext.Sorting) {
self.impl.with { impl in
impl.updateSorting(sorting)
}
}
public var currentState: ProfileGiftsContext.State? { public var currentState: ProfileGiftsContext.State? {
var state: ProfileGiftsContext.State? var state: ProfileGiftsContext.State?
self.impl.syncWith { impl in self.impl.syncWith { impl in

View File

@ -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(.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)) 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, videoCover: 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, videoTimestamp: nil, ttlSeconds: nil)))
|> mapError { _ -> UploadStickerError in return .generic } |> mapError { _ -> UploadStickerError in return .generic }
|> mapToSignal { media -> Signal<UploadStickerStatus, UploadStickerError> in |> mapToSignal { media -> Signal<UploadStickerStatus, UploadStickerError> in
switch media { 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 { 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) 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) }) { if let thumbnail, let previewRepresentation = file.previewRepresentations.first(where: { $0.dimensions == PixelDimensions(width: 320, height: 320) }) {

View File

@ -201,6 +201,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
case dismissedBusinessChatbotsBadge = 74 case dismissedBusinessChatbotsBadge = 74
case captionAboveMediaTooltip = 75 case captionAboveMediaTooltip = 75
case channelSendGiftTooltip = 76 case channelSendGiftTooltip = 76
case starGiftWearTips = 77
var key: ValueBoxKey { var key: ValueBoxKey {
let v = ValueBoxKey(length: 4) let v = ValueBoxKey(length: 4)
@ -549,6 +550,10 @@ private struct ApplicationSpecificNoticeKeys {
static func channelSendGiftTooltip() -> NoticeEntryKey { static func channelSendGiftTooltip() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.channelSendGiftTooltip.key) return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.channelSendGiftTooltip.key)
} }
static func starGiftWearTips() -> NoticeEntryKey {
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.starGiftWearTips.key)
}
} }
public struct ApplicationSpecificNotice { public struct ApplicationSpecificNotice {
@ -2335,4 +2340,31 @@ public struct ApplicationSpecificNotice {
return Int(previousValue) return Int(previousValue)
} }
} }
public static func getStarGiftWearTips(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
return accountManager.transaction { transaction -> Int32 in
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.starGiftWearTips())?.get(ApplicationSpecificCounterNotice.self) {
return value.value
} else {
return 0
}
}
}
public static func incrementStarGiftWearTips(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int = 1) -> Signal<Int, NoError> {
return accountManager.transaction { transaction -> Int in
var currentValue: Int32 = 0
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.starGiftWearTips())?.get(ApplicationSpecificCounterNotice.self) {
currentValue = value.value
}
let previousValue = currentValue
currentValue += Int32(count)
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
transaction.setNotice(ApplicationSpecificNoticeKeys.starGiftWearTips(), entry)
}
return Int(previousValue)
}
}
} }

View File

@ -193,4 +193,10 @@ public struct PresentationResourcesRootController {
return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddStoryIcon"), color: .white) return generateTintedImage(image: UIImage(bundleImageName: "Chat List/AddStoryIcon"), color: .white)
}) })
} }
public static func navigationSortIcon(_ theme: PresentationTheme) -> UIImage? {
return theme.image(PresentationResourceKey.navigationPostStoryIcon.rawValue, { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Peer Info/SortIcon"), color: .white)
})
}
} }

View File

@ -1113,7 +1113,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
} }
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, _, _, _): case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, _, _, _):
if case let .unique(gift) = gift { if case let .unique(gift) = gift {
if !forAdditionalServiceMessage { if !forAdditionalServiceMessage && !"".isEmpty {
attributedString = NSAttributedString(string: "\(gift.title) #\(gift.number)", font: titleFont, textColor: primaryTextColor) attributedString = NSAttributedString(string: "\(gift.title) #\(gift.number)", font: titleFont, textColor: primaryTextColor)
} else if let messagePeer = message.peers[message.id.peerId] { } else if let messagePeer = message.peers[message.id.peerId] {
let peerName = EnginePeer(messagePeer).compactDisplayTitle let peerName = EnginePeer(messagePeer).compactDisplayTitle

View File

@ -20,6 +20,7 @@ swift_library(
"//submodules/AccountContext", "//submodules/AccountContext",
"//submodules/PresentationDataUtils", "//submodules/PresentationDataUtils",
"//submodules/Markdown", "//submodules/Markdown",
"//submodules/TelegramNotices",
"//submodules/ComponentFlow", "//submodules/ComponentFlow",
"//submodules/Components/ComponentDisplayAdapters", "//submodules/Components/ComponentDisplayAdapters",
"//submodules/Components/ViewControllerComponent", "//submodules/Components/ViewControllerComponent",

View File

@ -31,6 +31,7 @@ import TooltipUI
import GiftAnimationComponent import GiftAnimationComponent
import LottieComponent import LottieComponent
import ContextUI import ContextUI
import TelegramNotices
private let modelButtonTag = GenericComponentViewTag() private let modelButtonTag = GenericComponentViewTag()
private let backdropButtonTag = GenericComponentViewTag() private let backdropButtonTag = GenericComponentViewTag()
@ -286,6 +287,25 @@ private final class GiftViewSheetContent: CombinedComponent {
self.updated(transition: .spring(duration: 0.4)) self.updated(transition: .spring(duration: 0.4))
} }
func commitWear(_ uniqueGift: StarGift.UniqueGift) {
self.pendingWear = true
self.pendingTakeOff = false
self.inWearPreview = false
self.updated(transition: .spring(duration: 0.4))
let _ = self.context.engine.accountData.setStarGiftStatus(starGift: uniqueGift, expirationDate: nil).startStandalone()
let _ = ApplicationSpecificNotice.incrementStarGiftWearTips(accountManager: self.context.sharedContext.accountManager).startStandalone()
}
func commitTakeOff() {
self.pendingTakeOff = true
self.pendingWear = false
self.updated(transition: .spring(duration: 0.4))
let _ = self.context.engine.accountData.setEmojiStatus(file: nil, expirationDate: nil).startStandalone()
}
func commitUpgrade() { func commitUpgrade() {
guard let arguments = self.subject.arguments, let peerId = arguments.peerId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else { guard let arguments = self.subject.arguments, let peerId = arguments.peerId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else {
return return
@ -1117,6 +1137,7 @@ private final class GiftViewSheetContent: CombinedComponent {
if peer.id == component.context.account.peerId, peer.isPremium { if peer.id == component.context.account.peerId, peer.isPremium {
let animationContent: EmojiStatusComponent.Content let animationContent: EmojiStatusComponent.Content
var color: UIColor? var color: UIColor?
var statusId: Int64 = 1
if state.pendingWear { if state.pendingWear {
var fileId: Int64? var fileId: Int64?
for attribute in uniqueGift.attributes { for attribute in uniqueGift.attributes {
@ -1128,6 +1149,7 @@ private final class GiftViewSheetContent: CombinedComponent {
} }
} }
if let fileId { if let fileId {
statusId = fileId
animationContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 18.0, height: 18.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: tableLinkColor, loopMode: .count(2)) animationContent = .animation(content: .customEmoji(fileId: fileId), size: CGSize(width: 18.0, height: 18.0), placeholderColor: theme.list.mediaPlaceholderColor, themeColor: tableLinkColor, loopMode: .count(2))
} else { } else {
animationContent = .premium(color: tableLinkColor) animationContent = .premium(color: tableLinkColor)
@ -1168,7 +1190,7 @@ private final class GiftViewSheetContent: CombinedComponent {
)) ))
), ),
AnyComponentWithIdentity( AnyComponentWithIdentity(
id: AnyHashable(1), id: AnyHashable(statusId),
component: AnyComponent(EmojiStatusComponent( component: AnyComponent(EmojiStatusComponent(
context: component.context, context: component.context,
animationCache: component.context.animationCache, animationCache: component.context.animationCache,
@ -1351,9 +1373,7 @@ private final class GiftViewSheetContent: CombinedComponent {
action: { [weak state] in action: { [weak state] in
if let state { if let state {
if isWearing { if isWearing {
state.pendingTakeOff = true state.commitTakeOff()
state.pendingWear = false
state.updated(transition: .spring(duration: 0.4))
component.showAttributeInfo(statusTag, strings.Gift_View_TookOff("\(uniqueGift.title) #\(uniqueGift.number)").string) component.showAttributeInfo(statusTag, strings.Gift_View_TookOff("\(uniqueGift.title) #\(uniqueGift.number)").string)
} else { } else {
@ -1361,7 +1381,18 @@ private final class GiftViewSheetContent: CombinedComponent {
controller.dismissAllTooltips() controller.dismissAllTooltips()
} }
let _ = (ApplicationSpecificNotice.getStarGiftWearTips(accountManager: component.context.sharedContext.accountManager)
|> deliverOnMainQueue).start(next: { [weak state] count in
guard let state else {
return
}
if !component.context.isPremium || count < 3 {
state.requestWearPreview() state.requestWearPreview()
} else {
state.commitWear(uniqueGift)
component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(uniqueGift.number)").string)
}
})
} }
} }
} }
@ -1731,7 +1762,7 @@ private final class GiftViewSheetContent: CombinedComponent {
items: tableItems items: tableItems
), ),
availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude),
transition: .immediate transition: context.transition
) )
context.add(table context.add(table
.position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0)) .position(CGPoint(x: context.availableSize.width / 2.0, y: originY + table.size.height / 2.0))
@ -1866,13 +1897,7 @@ private final class GiftViewSheetContent: CombinedComponent {
) )
controller.present(tooltipController, in: .window(.root)) controller.present(tooltipController, in: .window(.root))
} else { } else {
state.pendingWear = true state.commitWear(uniqueGift)
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) { Queue.mainQueue().after(0.2) {
component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(uniqueGift.number)").string) component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(uniqueGift.number)").string)
} }

View File

@ -440,12 +440,6 @@ public final class PeerInfoCoverComponent: Component {
let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -1000.0 + availableSize.height), size: CGSize(width: availableSize.width, height: 1000.0)) let backgroundFrame = CGRect(origin: CGPoint(x: 0.0, y: -1000.0 + availableSize.height), size: CGSize(width: availableSize.width, height: 1000.0))
transition.containedViewLayoutTransition.updateFrameAdditive(view: self.backgroundView, frame: backgroundFrame) transition.containedViewLayoutTransition.updateFrameAdditive(view: self.backgroundView, frame: backgroundFrame)
/*let avatarBackgroundPatternContainerFrame = CGSize(width: 0.0, height: 0.0).centered(around: component.avatarCenter)
transition.containedViewLayoutTransition.updateFrameAdditive(view: self.avatarBackgroundPatternContainer, frame: avatarBackgroundPatternContainerFrame)
transition.containedViewLayoutTransition.updateSublayerTransformScaleAdditive(layer: self.avatarBackgroundPatternContainer.layer, scale: component.avatarScale)*/
//transition.setFrame(view: self.avatarBackgroundPatternView, frame: CGSize(width: 200.0, height: 200.0).centered(around: CGPoint()))
let avatarPatternFrame = CGSize(width: 380.0, height: floor(component.defaultHeight * 1.0)).centered(around: component.avatarCenter) let avatarPatternFrame = CGSize(width: 380.0, height: floor(component.defaultHeight * 1.0)).centered(around: component.avatarCenter)
transition.setFrame(layer: self.avatarBackgroundPatternContentsLayer, frame: avatarPatternFrame) transition.setFrame(layer: self.avatarBackgroundPatternContentsLayer, frame: avatarPatternFrame)
@ -457,7 +451,7 @@ public final class PeerInfoCoverComponent: Component {
] ]
} else if case let .status(status) = component.subject, case let .starGift(_, _, _, _, _, _, _, patternColorValue, _) = status.content { } else if case let .status(status) = component.subject, case let .starGift(_, _, _, _, _, _, _, patternColorValue, _) = status.content {
let patternColor = UIColor(rgb: UInt32(bitPattern: patternColorValue)) let patternColor = UIColor(rgb: UInt32(bitPattern: patternColorValue))
self.avatarBackgroundPatternContentsLayer.compositingFilter = nil self.avatarBackgroundPatternContentsLayer.compositingFilter = "overlayBlendMode"
self.avatarBackgroundPatternContentsLayer.colors = [ self.avatarBackgroundPatternContentsLayer.colors = [
patternColor.withAlphaComponent(0.6).cgColor, patternColor.withAlphaComponent(0.6).cgColor,
patternColor.withAlphaComponent(0.0).cgColor patternColor.withAlphaComponent(0.0).cgColor
@ -469,7 +463,6 @@ public final class PeerInfoCoverComponent: Component {
UIColor(white: 0.0, alpha: 0.6).cgColor, UIColor(white: 0.0, alpha: 0.6).cgColor,
UIColor(white: 0.0, alpha: 0.0).cgColor UIColor(white: 0.0, alpha: 0.0).cgColor
] ]
} else { } else {
self.avatarBackgroundPatternContentsLayer.compositingFilter = nil self.avatarBackgroundPatternContentsLayer.compositingFilter = nil
let baseWhite: CGFloat = component.isDark ? 0.5 : 0.3 let baseWhite: CGFloat = component.isDark ? 0.5 : 0.3

View File

@ -282,6 +282,10 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode {
text = "" text = ""
accessibilityText = presentationData.strings.Story_Privacy_PostStory accessibilityText = presentationData.strings.Story_Privacy_PostStory
icon = PresentationResourcesRootController.navigationPostStoryIcon(presentationData.theme) icon = PresentationResourcesRootController.navigationPostStoryIcon(presentationData.theme)
case .sort:
text = ""
accessibilityText = presentationData.strings.Common_More
icon = PresentationResourcesRootController.navigationSortIcon(presentationData.theme)
} }
self.accessibilityLabel = accessibilityText self.accessibilityLabel = accessibilityText
self.containerNode.isGestureEnabled = isGestureEnabled self.containerNode.isGestureEnabled = isGestureEnabled

View File

@ -21,6 +21,7 @@ enum PeerInfoHeaderNavigationButtonKey {
case qrCode case qrCode
case moreToSearch case moreToSearch
case postStory case postStory
case sort
} }
struct PeerInfoHeaderNavigationButtonSpec: Equatable { struct PeerInfoHeaderNavigationButtonSpec: Equatable {

View File

@ -2306,11 +2306,11 @@ final class PeerInfoHeaderNode: ASDisplayNode {
Queue.mainQueue().after(0.2) { Queue.mainQueue().after(0.2) {
backgroundCoverView.animateIn() backgroundCoverView.animateIn()
} }
Queue.mainQueue().after(0.44) { Queue.mainQueue().after(0.5) {
self.invokeDisplayGiftInfo() self.invokeDisplayGiftInfo()
} }
} else { } else {
Queue.mainQueue().after(0.44) { Queue.mainQueue().after(0.5) {
self.invokeDisplayGiftInfo() self.invokeDisplayGiftInfo()
} }
} }

View File

@ -4430,14 +4430,27 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
case .search, .searchWithTags, .standaloneSearch: case .search, .searchWithTags, .standaloneSearch:
strongSelf.activateSearch() strongSelf.activateSearch()
case .more: case .more:
if let currentPaneKey = strongSelf.paneContainerNode.currentPaneKey, case .savedMessagesChats = currentPaneKey { guard let source else {
if let controller = strongSelf.controller, let source { return
}
if let currentPaneKey = strongSelf.paneContainerNode.currentPaneKey {
switch currentPaneKey {
case .savedMessagesChats:
if let controller = strongSelf.controller {
PeerInfoScreenImpl.openSavedMessagesMoreMenu(context: strongSelf.context, sourceController: controller, isViewingAsTopics: true, sourceView: source.view, gesture: gesture) PeerInfoScreenImpl.openSavedMessagesMoreMenu(context: strongSelf.context, sourceController: controller, isViewingAsTopics: true, sourceView: source.view, gesture: gesture)
} }
default:
break
}
} else { } else {
if let source = source {
strongSelf.displayMediaGalleryContextMenu(source: source, gesture: gesture) strongSelf.displayMediaGalleryContextMenu(source: source, gesture: gesture)
} }
case .sort:
guard let source else {
return
}
if let currentPaneKey = strongSelf.paneContainerNode.currentPaneKey, case .gifts = currentPaneKey {
strongSelf.displayGiftsContextMenu(source: source, gesture: gesture)
} }
case .qrCode: case .qrCode:
strongSelf.openQrCode() strongSelf.openQrCode()
@ -4651,6 +4664,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
style: .customBlur(backgroundColor, -4.0), style: .customBlur(backgroundColor, -4.0),
arrowStyle: .small, arrowStyle: .small,
location: .point(sourceRect, .bottom), location: .point(sourceRect, .bottom),
isShimmering: true,
cornerRadius: 10.0, cornerRadius: 10.0,
shouldDismissOnTouch: { _, _ in shouldDismissOnTouch: { _, _ in
return .dismiss(consume: false) return .dismiss(consume: false)
@ -10884,6 +10898,126 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
}), in: .current) }), in: .current)
} }
private func displayGiftsContextMenu(source: ContextReferenceContentNode, gesture: ContextGesture?) {
guard let currentPaneKey = self.paneContainerNode.currentPaneKey, case .gifts = currentPaneKey else {
return
}
guard let controller = self.controller else {
return
}
guard let data = self.data, let channel = data.peer as? TelegramChannel, channel.hasPermission(.sendSomething), let giftsContext = data.profileGiftsContext else {
return
}
let strings = self.presentationData.strings
let items: Signal<ContextController.Items, NoError> = giftsContext.state
|> map { state in
return (state.filter, state.sorting)
}
|> distinctUntilChanged(isEqual: { lhs, rhs -> Bool in
let filterEquals = lhs.0 == rhs.0
let sortingEquals = lhs.1 == rhs.1
return filterEquals && sortingEquals
})
|> map { [weak giftsContext] filter, sorting -> ContextController.Items in
var items: [ContextMenuItem] = []
items.append(.action(ContextMenuActionItem(text: sorting == .date ? strings.PeerInfo_Gifts_SortByValue : strings.PeerInfo_Gifts_SortByDate, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: sorting == .date ? "Peer Info/SortValue" : "Peer Info/SortDate"), color: theme.contextMenu.primaryColor)
}, action: { [weak giftsContext] _, f in
f(.default)
giftsContext?.updateSorting(sorting == .date ? .value : .date)
})))
items.append(.separator)
let toggleFilter: (ProfileGiftsContext.Filters) -> Void = { [weak giftsContext] value in
var updatedFilter = filter
if updatedFilter.contains(value) {
updatedFilter.remove(value)
} else {
updatedFilter.insert(value)
}
giftsContext?.updateFilter(updatedFilter)
}
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unlimited, icon: { theme in
return filter.contains(.unlimited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
f(.default)
toggleFilter(.unlimited)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Limited, icon: { theme in
return filter.contains(.limited) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
f(.default)
toggleFilter(.limited)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Unique, icon: { theme in
return filter.contains(.unique) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
f(.default)
toggleFilter(.unique)
})))
items.append(.separator)
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Displayed, icon: { theme in
return filter.contains(.displayed) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
f(.default)
toggleFilter(.displayed)
})))
items.append(.action(ContextMenuActionItem(text: strings.PeerInfo_Gifts_Hidden, icon: { theme in
return filter.contains(.hidden) ? generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Check"), color: theme.contextMenu.primaryColor) : nil
}, action: { _, f in
f(.default)
toggleFilter(.hidden)
})))
return ContextController.Items(content: .list(items))
}
let contextController = ContextController(presentationData: self.presentationData, source: .reference(PeerInfoContextReferenceContentSource(controller: controller, sourceNode: source)), items: items, gesture: gesture)
contextController.passthroughTouchEvent = { [weak self] sourceView, point in
guard let strongSelf = self else {
return .ignore
}
let localPoint = strongSelf.view.convert(sourceView.convert(point, to: nil), from: nil)
guard let localResult = strongSelf.hitTest(localPoint, with: nil) else {
return .dismiss(consume: true, result: nil)
}
var testView: UIView? = localResult
while true {
if let testViewValue = testView {
if let node = testViewValue.asyncdisplaykit_node as? PeerInfoHeaderNavigationButton {
node.isUserInteractionEnabled = false
DispatchQueue.main.async {
node.isUserInteractionEnabled = true
}
return .dismiss(consume: false, result: nil)
} else {
testView = testViewValue.superview
}
} else {
break
}
}
return .dismiss(consume: true, result: nil)
}
self.mediaGalleryContextMenu = contextController
controller.presentInGlobalOverlay(contextController)
}
private func displayMediaGalleryContextMenu(source: ContextReferenceContentNode, gesture: ContextGesture?) { private func displayMediaGalleryContextMenu(source: ContextReferenceContentNode, gesture: ContextGesture?) {
let peerId = self.peerId let peerId = self.peerId
@ -11873,6 +12007,10 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
if let data = self.data, data.hasBotPreviewItems, let user = data.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) { if let data = self.data, data.hasBotPreviewItems, let user = data.peer as? TelegramUser, let botInfo = user.botInfo, botInfo.flags.contains(.canEdit) {
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .more, isForExpandedView: true)) rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .more, isForExpandedView: true))
} }
case .gifts:
if let data = self.data, let channel = data.peer as? TelegramChannel, channel.hasPermission(.sendSomething) {
rightNavigationButtons.append(PeerInfoHeaderNavigationButtonSpec(key: .sort, isForExpandedView: true))
}
default: default:
break break
} }

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "sortdate_24.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "sort_30.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "sortvalue_24.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -26,6 +26,7 @@ swift_library(
"//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent", "//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent",
"//submodules/Components/BalancedTextComponent", "//submodules/Components/BalancedTextComponent",
"//submodules/Components/MultilineTextWithEntitiesComponent", "//submodules/Components/MultilineTextWithEntitiesComponent",
"//submodules/ShimmerEffect",
], ],
visibility = [ visibility = [
"//visibility:public", "//visibility:public",

View File

@ -18,6 +18,7 @@ import AccountContext
import Markdown import Markdown
import BalancedTextComponent import BalancedTextComponent
import MultilineTextWithEntitiesComponent import MultilineTextWithEntitiesComponent
import ShimmerEffect
public enum TooltipActiveTextItem { public enum TooltipActiveTextItem {
case url(String, Bool) case url(String, Bool)
@ -126,6 +127,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
} }
} }
} }
private let isShimmering: Bool
private let displayDuration: TooltipScreen.DisplayDuration private let displayDuration: TooltipScreen.DisplayDuration
private let shouldDismissOnTouch: (CGPoint, CGRect) -> TooltipScreen.DismissOnTouch private let shouldDismissOnTouch: (CGPoint, CGRect) -> TooltipScreen.DismissOnTouch
private let requestDismiss: () -> Void private let requestDismiss: () -> Void
@ -149,6 +151,9 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
private var closeButtonNode: HighlightableButtonNode? private var closeButtonNode: HighlightableButtonNode?
private var actionButtonNode: HighlightableButtonNode? private var actionButtonNode: HighlightableButtonNode?
private var shimmerContainerView: UIView?
private var shimmerView: ShimmerEffectForegroundView?
private var isArrowInverted: Bool = false private var isArrowInverted: Bool = false
private let fontSize: CGFloat private let fontSize: CGFloat
@ -172,6 +177,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
displayDuration: TooltipScreen.DisplayDuration, displayDuration: TooltipScreen.DisplayDuration,
inset: CGFloat = 12.0, inset: CGFloat = 12.0,
cornerRadius: CGFloat? = nil, cornerRadius: CGFloat? = nil,
isShimmering: Bool = false,
shouldDismissOnTouch: @escaping (CGPoint, CGRect) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?) shouldDismissOnTouch: @escaping (CGPoint, CGRect) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?)
{ {
self.context = context self.context = context
@ -181,6 +187,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
self.action = action self.action = action
self.location = location self.location = location
self.displayDuration = displayDuration self.displayDuration = displayDuration
self.isShimmering = isShimmering
self.inset = inset self.inset = inset
self.shouldDismissOnTouch = shouldDismissOnTouch self.shouldDismissOnTouch = shouldDismissOnTouch
self.requestDismiss = requestDismiss self.requestDismiss = requestDismiss
@ -713,8 +720,8 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
transition.updateFrame(node: self.backgroundMaskNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -10.0, dy: -10.0)) transition.updateFrame(node: self.backgroundMaskNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -10.0, dy: -10.0))
transition.updateFrame(node: self.backgroundClipNode, frame: CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: backgroundFrame.size)) transition.updateFrame(node: self.backgroundClipNode, frame: CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: backgroundFrame.size))
if let effectNode = self.effectNode {
let effectFrame = CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -10.0, dy: -10.0) let effectFrame = CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -10.0, dy: -10.0)
if let effectNode = self.effectNode {
transition.updateFrame(node: effectNode, frame: effectFrame) transition.updateFrame(node: effectNode, frame: effectFrame)
effectNode.update(size: effectFrame.size, transition: transition) effectNode.update(size: effectFrame.size, transition: transition)
} }
@ -843,6 +850,69 @@ private final class TooltipScreenNode: ViewControllerTracingNode {
transition.updateFrame(node: avatarNode, frame: avatarFrame) transition.updateFrame(node: avatarNode, frame: avatarFrame)
avatarNode.updateSize(size: avatarFrame.size) avatarNode.updateSize(size: avatarFrame.size)
} }
if self.isShimmering {
let shimmerContainerView: UIView
let shimmerView: ShimmerEffectForegroundView
if let currentContainer = self.shimmerContainerView, let current = self.shimmerView {
shimmerContainerView = currentContainer
shimmerView = current
} else {
shimmerContainerView = UIView()
shimmerView = ShimmerEffectForegroundView()
if let outerSnapshot = self.backgroundMaskNode.layer.snapshotContentTree(), let innerSnapshot = self.backgroundMaskNode.layer.snapshotContentTree() {
outerSnapshot.backgroundColor = UIColor.black.cgColor
func tintLayers(_ layer: CALayer, color: UIColor, scale: CGFloat) {
if let sublayers = layer.sublayers {
for layer in sublayers {
if let shapeLayer = layer as? CAShapeLayer {
shapeLayer.fillColor = color.cgColor
} else {
if layer.cornerRadius > 0.0 {
layer.backgroundColor = color.cgColor
layer.bounds = CGRect(origin: .zero, size: CGSize(width: layer.bounds.width * scale, height: layer.bounds.height))
}
tintLayers(layer, color: color, scale: scale)
}
}
}
}
tintLayers(outerSnapshot, color: .white, scale: 1.0)
tintLayers(innerSnapshot, color: .black, scale: 1.085)
outerSnapshot.addSublayer(innerSnapshot)
innerSnapshot.transform = CATransform3DMakeScale(0.9, 0.9, 1.0)
innerSnapshot.position = innerSnapshot.position.offsetBy(dx: 10.0, dy: 10.0)
if let filter = CALayer.luminanceToAlpha() {
outerSnapshot.filters = [filter]
}
shimmerContainerView.layer.mask = outerSnapshot
}
self.shimmerContainerView = shimmerContainerView
self.backgroundContainerNode.view.addSubview(shimmerContainerView)
let shimmerFrame = effectFrame.insetBy(dx: -60.0, dy: 0.0)
shimmerView.frame = shimmerFrame
shimmerView.update(backgroundColor: .clear, foregroundColor: UIColor.white.withAlphaComponent(0.4), gradientSize: 60.0, globalTimeOffset: false, duration: 2.2, horizontal: true)
shimmerView.updateAbsoluteRect(shimmerFrame, within: shimmerFrame.size)
shimmerContainerView.addSubview(shimmerView)
}
shimmerContainerView.frame = effectFrame.offsetBy(dx: 10.0, dy: 10.0)
} else if let shimmerContainerView = self.shimmerContainerView, let shimmerView = self.shimmerView {
self.shimmerContainerView = nil
self.shimmerView = nil
shimmerContainerView.removeFromSuperview()
shimmerView.removeFromSuperview()
}
} }
private var didRequestDismiss = false private var didRequestDismiss = false
@ -1064,6 +1134,7 @@ public final class TooltipScreen: ViewController {
} }
} }
} }
private let isShimmering: Bool
private let displayDuration: DisplayDuration private let displayDuration: DisplayDuration
private let inset: CGFloat private let inset: CGFloat
private let cornerRadius: CGFloat? private let cornerRadius: CGFloat?
@ -1098,6 +1169,7 @@ public final class TooltipScreen: ViewController {
action: TooltipScreen.Action? = nil, action: TooltipScreen.Action? = nil,
location: TooltipScreen.Location, location: TooltipScreen.Location,
displayDuration: DisplayDuration = .default, displayDuration: DisplayDuration = .default,
isShimmering: Bool = false,
inset: CGFloat = 12.0, inset: CGFloat = 12.0,
cornerRadius: CGFloat? = nil, cornerRadius: CGFloat? = nil,
shouldDismissOnTouch: @escaping (CGPoint, CGRect) -> TooltipScreen.DismissOnTouch, shouldDismissOnTouch: @escaping (CGPoint, CGRect) -> TooltipScreen.DismissOnTouch,
@ -1116,6 +1188,7 @@ public final class TooltipScreen: ViewController {
self.action = action self.action = action
self.location = location self.location = location
self.displayDuration = displayDuration self.displayDuration = displayDuration
self.isShimmering = isShimmering
self.inset = inset self.inset = inset
self.cornerRadius = cornerRadius self.cornerRadius = cornerRadius
self.shouldDismissOnTouch = shouldDismissOnTouch self.shouldDismissOnTouch = shouldDismissOnTouch
@ -1177,7 +1250,7 @@ public final class TooltipScreen: ViewController {
} }
override public func loadDisplayNode() { override public func loadDisplayNode() {
self.displayNode = TooltipScreenNode(context: self.context, account: self.account, sharedContext: self.sharedContext, text: self.text, textAlignment: self.textAlignment, balancedTextLayout: self.balancedTextLayout, constrainWidth: self.constrainWidth, style: self.style, arrowStyle: self.arrowStyle, icon: self.icon, action: self.action, location: self.location, displayDuration: self.displayDuration, inset: self.inset, cornerRadius: self.cornerRadius, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in self.displayNode = TooltipScreenNode(context: self.context, account: self.account, sharedContext: self.sharedContext, text: self.text, textAlignment: self.textAlignment, balancedTextLayout: self.balancedTextLayout, constrainWidth: self.constrainWidth, style: self.style, arrowStyle: self.arrowStyle, icon: self.icon, action: self.action, location: self.location, displayDuration: self.displayDuration, inset: self.inset, cornerRadius: self.cornerRadius, isShimmering: self.isShimmering, shouldDismissOnTouch: self.shouldDismissOnTouch, requestDismiss: { [weak self] in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }