diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 09174f58ca..b2c01b8a1a 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -13753,3 +13753,11 @@ Sorry for the inconvenience."; "Gift.Withdraw.EnterPassword.Title" = "Enter Password"; "Gift.Withdraw.EnterPassword.Text" = "Please enter your Two-Step Verification password to complete this action."; "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"; diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 665078df1e..25b6b413d5 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -389,8 +389,8 @@ 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[1946579745] = { return Api.InputMedia.parse_inputMediaDocument($0) } - dict[-149933938] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) } + dict[-1468646731] = { return Api.InputMedia.parse_inputMediaDocument($0) } + dict[2006319353] = { return Api.InputMedia.parse_inputMediaDocumentExternal($0) } dict[-1771768449] = { return Api.InputMedia.parse_inputMediaEmpty($0) } dict[-750828557] = { return Api.InputMedia.parse_inputMediaGame($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[261416433] = { return Api.InputMedia.parse_inputMediaPoll($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[-1052959727] = { return Api.InputMedia.parse_inputMediaVenue($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[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($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[-38694904] = { return Api.MessageMedia.parse_messageMediaGame($0) } dict[1457575028] = { return Api.MessageMedia.parse_messageMediaGeo($0) } diff --git a/submodules/TelegramApi/Sources/Api10.swift b/submodules/TelegramApi/Sources/Api10.swift index b88fe5ed63..19ef7783e9 100644 --- a/submodules/TelegramApi/Sources/Api10.swift +++ b/submodules/TelegramApi/Sources/Api10.swift @@ -432,8 +432,8 @@ 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, videoCover: Api.InputPhoto?, ttlSeconds: Int32?, query: String?) - case inputMediaDocumentExternal(flags: Int32, url: String, ttlSeconds: Int32?, videoCover: Api.InputPhoto?) + 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?, videoTimestamp: Int32?) case inputMediaEmpty case inputMediaGame(id: Api.InputGame) 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 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]?, 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 inputMediaVenue(geoPoint: Api.InputGeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String) case inputMediaWebPage(flags: Int32, url: String) @@ -466,24 +466,26 @@ public extension Api { } serializeString(emoticon, buffer: buffer, boxed: false) 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 { - buffer.appendInt32(1946579745) + buffer.appendInt32(-1468646731) } 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 << 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 << 1) != 0 {serializeString(query!, buffer: buffer, boxed: false)} 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 { - buffer.appendInt32(-149933938) + buffer.appendInt32(2006319353) } serializeInt32(flags, 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 << 2) != 0 {videoCover!.serialize(buffer, true)} + if Int(flags) & Int(1 << 3) != 0 {serializeInt32(videoTimestamp!, buffer: buffer, boxed: false)} break case .inputMediaEmpty: if boxed { @@ -582,9 +584,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 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 { - buffer.appendInt32(-264125395) + buffer.appendInt32(58495792) } serializeInt32(flags, buffer: buffer, boxed: false) file.serialize(buffer, true) @@ -601,6 +603,7 @@ public extension Api { item.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)} break 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)]) case .inputMediaDice(let emoticon): return ("inputMediaDice", [("emoticon", emoticon 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, let videoCover): - return ("inputMediaDocumentExternal", [("flags", flags as Any), ("url", url as Any), ("ttlSeconds", ttlSeconds as Any), ("videoCover", videoCover as Any)]) + 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), ("videoTimestamp", videoTimestamp as Any), ("ttlSeconds", ttlSeconds as Any), ("query", query as Any)]) + 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), ("videoTimestamp", videoTimestamp as Any)]) case .inputMediaEmpty: return ("inputMediaEmpty", []) 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)]) 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 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 .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), ("videoTimestamp", videoTimestamp 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): @@ -721,16 +724,19 @@ public extension Api { _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) } + if Int(_1!) & Int(1 << 4) != 0 {_4 = reader.readInt32() } + var _5: Int32? + 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 _c2 = _2 != nil 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) + let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.InputMedia.inputMediaDocument(flags: _1!, id: _2!, videoCover: _3, videoTimestamp: _4, ttlSeconds: _5, query: _6) } else { return nil @@ -747,12 +753,15 @@ public extension Api { if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() { _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 _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil - if _c1 && _c2 && _c3 && _c4 { - return Api.InputMedia.inputMediaDocumentExternal(flags: _1!, url: _2!, ttlSeconds: _3, videoCover: _4) + let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.InputMedia.inputMediaDocumentExternal(flags: _1!, url: _2!, ttlSeconds: _3, videoCover: _4, videoTimestamp: _5) } else { return nil @@ -987,7 +996,9 @@ public extension Api { _7 = Api.parse(reader, signature: signature) as? Api.InputPhoto } } 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 _c2 = _2 != nil let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil @@ -995,9 +1006,10 @@ public extension Api { let _c5 = _5 != nil let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil 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) + let _c8 = (Int(_1!) & Int(1 << 7) == 0) || _8 != nil + let _c9 = (Int(_1!) & Int(1 << 1) == 0) || _9 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 { + return Api.InputMedia.inputMediaUploadedDocument(flags: _1!, file: _2!, thumb: _3, mimeType: _4!, attributes: _5!, stickers: _6, videoCover: _7, videoTimestamp: _8, ttlSeconds: _9) } else { return nil diff --git a/submodules/TelegramApi/Sources/Api16.swift b/submodules/TelegramApi/Sources/Api16.swift index 69a2c00f73..c52bd5fd42 100644 --- a/submodules/TelegramApi/Sources/Api16.swift +++ b/submodules/TelegramApi/Sources/Api16.swift @@ -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]?, 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 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 videoCover, let ttlSeconds): + case .messageMediaDocument(let flags, let document, let altDocuments, let videoCover, let videoTimestamp, let ttlSeconds): if boxed { - buffer.appendInt32(-608307692) + buffer.appendInt32(1389939929) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 0) != 0 {document!.serialize(buffer, true)} @@ -757,6 +757,7 @@ public extension Api { item.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)} break 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)]) 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 videoCover, let ttlSeconds): - return ("messageMediaDocument", [("flags", flags as Any), ("document", document as Any), ("altDocuments", altDocuments as Any), ("videoCover", videoCover as Any), ("ttlSeconds", ttlSeconds as Any)]) + 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), ("videoTimestamp", videoTimestamp as Any), ("ttlSeconds", ttlSeconds as Any)]) case .messageMediaEmpty: return ("messageMediaEmpty", []) case .messageMediaGame(let game): @@ -996,14 +997,17 @@ public extension Api { _4 = Api.parse(reader, signature: signature) as? Api.Photo } } 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 _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 << 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, videoCover: _4, ttlSeconds: _5) + let _c5 = (Int(_1!) & Int(1 << 10) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.MessageMedia.messageMediaDocument(flags: _1!, document: _2, altDocuments: _3, videoCover: _4, videoTimestamp: _5, ttlSeconds: _6) } else { return nil diff --git a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift index 169b5025a4..b9286941eb 100644 --- a/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift +++ b/submodules/TelegramCore/Sources/ApiUtils/StoreMessage_Telegram.swift @@ -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, coverPhoto, ttlSeconds): + case let .messageMediaDocument(flags, document, altDocuments, coverPhoto, videoTimestamp, ttlSeconds): + let _ = videoTimestamp if let document = document { if let mediaFile = telegramMediaFileFromApiDocument(document, altDocuments: altDocuments, videoCover: coverPhoto) { return (mediaFile, ttlSeconds, (flags & (1 << 3)) != 0, (flags & (1 << 4)) != 0, nil) diff --git a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift index 4b59f20489..9cb72f36af 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/PendingMessageUploadedContent.swift @@ -229,7 +229,7 @@ func mediaContentToUpload(accountPeerId: PeerId, network: Network, postbox: Post } |> mapToSignal { validatedResource -> Signal 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)), 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 { 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 { 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))) |> 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 @@ -1122,11 +1122,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, 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 { - 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)) } @@ -1137,11 +1137,11 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili |> mapError { _ -> PendingMessageUploadError in } |> mapToSignal { inputPeer -> Signal 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, 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 } |> mapToSignal { result -> Signal 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? @@ -1156,7 +1156,7 @@ private func uploadedMediaFileContent(network: Network, postbox: Postbox, auxili 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 { return .single(result) } else { diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift index 064f8d9991..1a147217d9 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneSendMessage.swift @@ -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, 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: preconditionFailure() case let .progress(progress): diff --git a/submodules/TelegramCore/Sources/PendingMessages/StandaloneUploadedMedia.swift b/submodules/TelegramCore/Sources/PendingMessages/StandaloneUploadedMedia.swift index 2d195b87a2..c10fa752db 100644 --- a/submodules/TelegramCore/Sources/PendingMessages/StandaloneUploadedMedia.swift +++ b/submodules/TelegramCore/Sources/PendingMessages/StandaloneUploadedMedia.swift @@ -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, 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 } |> mapToSignal { media -> Signal 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)))) diff --git a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift index bd2b2f20f0..fa8a2f8bc3 100644 --- a/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift +++ b/submodules/TelegramCore/Sources/State/UpdatesApiUtils.swift @@ -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, _, videoCover, ttlSeconds): + case let .inputMediaUploadedDocument(flags, file, thumb, mimeType, attributes, _, videoCover, videoTimestamp, 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, 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): var flags = flags if let _ = stickers { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/HistoryImport/TelegramEngineHistoryImport.swift b/submodules/TelegramCore/Sources/TelegramEngine/HistoryImport/TelegramEngineHistoryImport.swift index 9a044ea5aa..46f9494b56 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/HistoryImport/TelegramEngineHistoryImport.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/HistoryImport/TelegramEngineHistoryImport.swift @@ -153,7 +153,7 @@ public extension TelegramEngine { default: 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): return .single(value) diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift index b498a4667d..3176bcd769 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/Stories.swift @@ -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())), 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 { @@ -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())), 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 @@ -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, 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 } 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: []) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift index 9502d36aea..97bd70c9af 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/StoryListContext.swift @@ -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())), 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)) } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift index 8fb4497a39..a97e9441b4 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Payments/StarGifts.swift @@ -925,7 +925,10 @@ private final class ProfileGiftsContextImpl { private let cacheDisposable = 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 filteredGifts: [ProfileGiftsContext.State.StarGift] = [] private var count: Int32? private var dataState: ProfileGiftsContext.State.DataState = .ready(canLoadMore: true, nextOffset: nil) private var notificationsEnabled: Bool? @@ -1111,13 +1114,47 @@ private final class ProfileGiftsContextImpl { 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() { - self._state = ProfileGiftsContext.State(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))) + let state = ProfileGiftsContext.State(filter: self.filter, sorting: self.sorting, 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 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 StarGift: Equatable, Codable { enum CodingKeys: String, CodingKey { @@ -1273,6 +1310,9 @@ public final class ProfileGiftsContext { case ready(canLoadMore: Bool, nextOffset: String?) } + + public var filter: Filters + public var sorting: Sorting public var gifts: [ProfileGiftsContext.State.StarGift] public var count: Int32? 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? { var state: ProfileGiftsContext.State? self.impl.syncWith { impl in diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift index e8b8a2ef60..484b8bf4fd 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Stickers/ImportStickers.swift @@ -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, 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 } |> mapToSignal { media -> Signal 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) }) { diff --git a/submodules/TelegramNotices/Sources/Notices.swift b/submodules/TelegramNotices/Sources/Notices.swift index d084d5ea6f..519b939af8 100644 --- a/submodules/TelegramNotices/Sources/Notices.swift +++ b/submodules/TelegramNotices/Sources/Notices.swift @@ -201,6 +201,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 { case dismissedBusinessChatbotsBadge = 74 case captionAboveMediaTooltip = 75 case channelSendGiftTooltip = 76 + case starGiftWearTips = 77 var key: ValueBoxKey { let v = ValueBoxKey(length: 4) @@ -549,6 +550,10 @@ private struct ApplicationSpecificNoticeKeys { static func channelSendGiftTooltip() -> NoticeEntryKey { 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 { @@ -2335,4 +2340,31 @@ public struct ApplicationSpecificNotice { return Int(previousValue) } } + + public static func getStarGiftWearTips(accountManager: AccountManager) -> Signal { + 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, count: Int = 1) -> Signal { + 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) + } + } } diff --git a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift index 342eb23fc4..5e3428a26b 100644 --- a/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift +++ b/submodules/TelegramPresentationData/Sources/Resources/PresentationResourcesRootController.swift @@ -193,4 +193,10 @@ public struct PresentationResourcesRootController { 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) + }) + } } diff --git a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift index 12bebfbcc0..9fffa987bd 100644 --- a/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift +++ b/submodules/TelegramStringFormatting/Sources/ServiceMessageStrings.swift @@ -1113,7 +1113,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme, } case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, _, _, _): if case let .unique(gift) = gift { - if !forAdditionalServiceMessage { + if !forAdditionalServiceMessage && !"".isEmpty { attributedString = NSAttributedString(string: "\(gift.title) #\(gift.number)", font: titleFont, textColor: primaryTextColor) } else if let messagePeer = message.peers[message.id.peerId] { let peerName = EnginePeer(messagePeer).compactDisplayTitle diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD index 0155831897..788dc255c7 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/BUILD @@ -20,6 +20,7 @@ swift_library( "//submodules/AccountContext", "//submodules/PresentationDataUtils", "//submodules/Markdown", + "//submodules/TelegramNotices", "//submodules/ComponentFlow", "//submodules/Components/ComponentDisplayAdapters", "//submodules/Components/ViewControllerComponent", diff --git a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift index 9192736c90..316321a93e 100644 --- a/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift +++ b/submodules/TelegramUI/Components/Gifts/GiftViewScreen/Sources/GiftViewScreen.swift @@ -31,6 +31,7 @@ import TooltipUI import GiftAnimationComponent import LottieComponent import ContextUI +import TelegramNotices private let modelButtonTag = GenericComponentViewTag() private let backdropButtonTag = GenericComponentViewTag() @@ -286,6 +287,25 @@ private final class GiftViewSheetContent: CombinedComponent { 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() { guard let arguments = self.subject.arguments, let peerId = arguments.peerId, let starsContext = self.context.starsContext, let starsState = starsContext.currentState else { return @@ -1117,6 +1137,7 @@ private final class GiftViewSheetContent: CombinedComponent { if peer.id == component.context.account.peerId, peer.isPremium { let animationContent: EmojiStatusComponent.Content var color: UIColor? + var statusId: Int64 = 1 if state.pendingWear { var fileId: Int64? for attribute in uniqueGift.attributes { @@ -1128,6 +1149,7 @@ private final class GiftViewSheetContent: CombinedComponent { } } 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)) } else { animationContent = .premium(color: tableLinkColor) @@ -1168,7 +1190,7 @@ private final class GiftViewSheetContent: CombinedComponent { )) ), AnyComponentWithIdentity( - id: AnyHashable(1), + id: AnyHashable(statusId), component: AnyComponent(EmojiStatusComponent( context: component.context, animationCache: component.context.animationCache, @@ -1351,17 +1373,26 @@ private final class GiftViewSheetContent: CombinedComponent { action: { [weak state] in if let state { if isWearing { - state.pendingTakeOff = true - state.pendingWear = false - state.updated(transition: .spring(duration: 0.4)) - + state.commitTakeOff() + component.showAttributeInfo(statusTag, strings.Gift_View_TookOff("\(uniqueGift.title) #\(uniqueGift.number)").string) } else { if let controller = controller() as? GiftViewScreen { controller.dismissAllTooltips() } - state.requestWearPreview() + 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() + } 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 ), availableSize: CGSize(width: context.availableSize.width - sideInset * 2.0, height: .greatestFiniteMagnitude), - transition: .immediate + transition: context.transition ) context.add(table .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)) } else { - 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() - + state.commitWear(uniqueGift) Queue.mainQueue().after(0.2) { component.showAttributeInfo(statusTag, strings.Gift_View_PutOn("\(uniqueGift.title) #\(uniqueGift.number)").string) } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift index 5d5bd6f198..82f9568a4a 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoCoverComponent/Sources/PeerInfoCoverComponent.swift @@ -439,13 +439,7 @@ 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)) 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) 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 { let patternColor = UIColor(rgb: UInt32(bitPattern: patternColorValue)) - self.avatarBackgroundPatternContentsLayer.compositingFilter = nil + self.avatarBackgroundPatternContentsLayer.compositingFilter = "overlayBlendMode" self.avatarBackgroundPatternContentsLayer.colors = [ patternColor.withAlphaComponent(0.6).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.0).cgColor ] - } else { self.avatarBackgroundPatternContentsLayer.compositingFilter = nil let baseWhite: CGFloat = component.isDark ? 0.5 : 0.3 diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift index 1d2a7e3d41..b4822b8f27 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButton.swift @@ -282,6 +282,10 @@ final class PeerInfoHeaderNavigationButton: HighlightableButtonNode { text = "" accessibilityText = presentationData.strings.Story_Privacy_PostStory icon = PresentationResourcesRootController.navigationPostStoryIcon(presentationData.theme) + case .sort: + text = "" + accessibilityText = presentationData.strings.Common_More + icon = PresentationResourcesRootController.navigationSortIcon(presentationData.theme) } self.accessibilityLabel = accessibilityText self.containerNode.isGestureEnabled = isGestureEnabled diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift index b8a3018272..65c4d9288e 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNavigationButtonContainerNode.swift @@ -21,6 +21,7 @@ enum PeerInfoHeaderNavigationButtonKey { case qrCode case moreToSearch case postStory + case sort } struct PeerInfoHeaderNavigationButtonSpec: Equatable { diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift index 19416cf367..0d8b4904fe 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoHeaderNode.swift @@ -2306,11 +2306,11 @@ final class PeerInfoHeaderNode: ASDisplayNode { Queue.mainQueue().after(0.2) { backgroundCoverView.animateIn() } - Queue.mainQueue().after(0.44) { + Queue.mainQueue().after(0.5) { self.invokeDisplayGiftInfo() } } else { - Queue.mainQueue().after(0.44) { + Queue.mainQueue().after(0.5) { self.invokeDisplayGiftInfo() } } diff --git a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift index f66995b36d..2e43048545 100644 --- a/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Components/PeerInfo/PeerInfoScreen/Sources/PeerInfoScreen.swift @@ -4430,14 +4430,27 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro case .search, .searchWithTags, .standaloneSearch: strongSelf.activateSearch() case .more: - if let currentPaneKey = strongSelf.paneContainerNode.currentPaneKey, case .savedMessagesChats = currentPaneKey { - if let controller = strongSelf.controller, let source { - PeerInfoScreenImpl.openSavedMessagesMoreMenu(context: strongSelf.context, sourceController: controller, isViewingAsTopics: true, sourceView: source.view, gesture: gesture) + guard let source else { + 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) + } + default: + break } } 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: strongSelf.openQrCode() @@ -4651,6 +4664,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro style: .customBlur(backgroundColor, -4.0), arrowStyle: .small, location: .point(sourceRect, .bottom), + isShimmering: true, cornerRadius: 10.0, shouldDismissOnTouch: { _, _ in return .dismiss(consume: false) @@ -10883,6 +10897,126 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro return .dismiss(consume: false) }), 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 = 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?) { 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) { 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: break } diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SortDate.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Peer Info/SortDate.imageset/Contents.json new file mode 100644 index 0000000000..09b3c16e5c --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Peer Info/SortDate.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "sortdate_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SortDate.imageset/sortdate_24.pdf b/submodules/TelegramUI/Images.xcassets/Peer Info/SortDate.imageset/sortdate_24.pdf new file mode 100644 index 0000000000..c48b8c4224 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Peer Info/SortDate.imageset/sortdate_24.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SortIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Peer Info/SortIcon.imageset/Contents.json new file mode 100644 index 0000000000..36c8ed3192 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Peer Info/SortIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "sort_30.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SortIcon.imageset/sort_30.pdf b/submodules/TelegramUI/Images.xcassets/Peer Info/SortIcon.imageset/sort_30.pdf new file mode 100644 index 0000000000..40d0f20410 Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Peer Info/SortIcon.imageset/sort_30.pdf differ diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SortValue.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Peer Info/SortValue.imageset/Contents.json new file mode 100644 index 0000000000..be20837727 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Peer Info/SortValue.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "sortvalue_24.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Peer Info/SortValue.imageset/sortvalue_24.pdf b/submodules/TelegramUI/Images.xcassets/Peer Info/SortValue.imageset/sortvalue_24.pdf new file mode 100644 index 0000000000..d0ad9ba3da Binary files /dev/null and b/submodules/TelegramUI/Images.xcassets/Peer Info/SortValue.imageset/sortvalue_24.pdf differ diff --git a/submodules/TooltipUI/BUILD b/submodules/TooltipUI/BUILD index 551a211a51..3233d2069b 100644 --- a/submodules/TooltipUI/BUILD +++ b/submodules/TooltipUI/BUILD @@ -26,6 +26,7 @@ swift_library( "//submodules/TelegramUI/Components/Stories/AvatarStoryIndicatorComponent", "//submodules/Components/BalancedTextComponent", "//submodules/Components/MultilineTextWithEntitiesComponent", + "//submodules/ShimmerEffect", ], visibility = [ "//visibility:public", diff --git a/submodules/TooltipUI/Sources/TooltipScreen.swift b/submodules/TooltipUI/Sources/TooltipScreen.swift index cc1f641314..85d4e81d37 100644 --- a/submodules/TooltipUI/Sources/TooltipScreen.swift +++ b/submodules/TooltipUI/Sources/TooltipScreen.swift @@ -18,6 +18,7 @@ import AccountContext import Markdown import BalancedTextComponent import MultilineTextWithEntitiesComponent +import ShimmerEffect public enum TooltipActiveTextItem { case url(String, Bool) @@ -126,6 +127,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode { } } } + private let isShimmering: Bool private let displayDuration: TooltipScreen.DisplayDuration private let shouldDismissOnTouch: (CGPoint, CGRect) -> TooltipScreen.DismissOnTouch private let requestDismiss: () -> Void @@ -149,6 +151,9 @@ private final class TooltipScreenNode: ViewControllerTracingNode { private var closeButtonNode: HighlightableButtonNode? private var actionButtonNode: HighlightableButtonNode? + private var shimmerContainerView: UIView? + private var shimmerView: ShimmerEffectForegroundView? + private var isArrowInverted: Bool = false private let fontSize: CGFloat @@ -172,6 +177,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode { displayDuration: TooltipScreen.DisplayDuration, inset: CGFloat = 12.0, cornerRadius: CGFloat? = nil, + isShimmering: Bool = false, shouldDismissOnTouch: @escaping (CGPoint, CGRect) -> TooltipScreen.DismissOnTouch, requestDismiss: @escaping () -> Void, openActiveTextItem: ((TooltipActiveTextItem, TooltipActiveTextAction) -> Void)?) { self.context = context @@ -181,6 +187,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode { self.action = action self.location = location self.displayDuration = displayDuration + self.isShimmering = isShimmering self.inset = inset self.shouldDismissOnTouch = shouldDismissOnTouch self.requestDismiss = requestDismiss @@ -446,7 +453,7 @@ private final class TooltipScreenNode: ViewControllerTracingNode { self.actionButtonNode?.addTarget(self, action: #selector(self.actionPressed), forControlEvents: .touchUpInside) self.closeButtonNode?.addTarget(self, action: #selector(self.closePressed), forControlEvents: .touchUpInside) } - + @objc private func actionPressed() { if let action = self.action { action.action() @@ -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.backgroundClipNode, frame: CGRect(origin: CGPoint(x: 10.0, y: 10.0), size: backgroundFrame.size)) + let effectFrame = CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -10.0, dy: -10.0) if let effectNode = self.effectNode { - let effectFrame = CGRect(origin: CGPoint(), size: backgroundFrame.size).insetBy(dx: -10.0, dy: -10.0) transition.updateFrame(node: effectNode, frame: effectFrame) effectNode.update(size: effectFrame.size, transition: transition) } @@ -843,6 +850,69 @@ private final class TooltipScreenNode: ViewControllerTracingNode { transition.updateFrame(node: avatarNode, frame: avatarFrame) 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 @@ -1064,6 +1134,7 @@ public final class TooltipScreen: ViewController { } } } + private let isShimmering: Bool private let displayDuration: DisplayDuration private let inset: CGFloat private let cornerRadius: CGFloat? @@ -1098,6 +1169,7 @@ public final class TooltipScreen: ViewController { action: TooltipScreen.Action? = nil, location: TooltipScreen.Location, displayDuration: DisplayDuration = .default, + isShimmering: Bool = false, inset: CGFloat = 12.0, cornerRadius: CGFloat? = nil, shouldDismissOnTouch: @escaping (CGPoint, CGRect) -> TooltipScreen.DismissOnTouch, @@ -1116,6 +1188,7 @@ public final class TooltipScreen: ViewController { self.action = action self.location = location self.displayDuration = displayDuration + self.isShimmering = isShimmering self.inset = inset self.cornerRadius = cornerRadius self.shouldDismissOnTouch = shouldDismissOnTouch @@ -1177,7 +1250,7 @@ public final class TooltipScreen: ViewController { } 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 { return }