From 7cd014ac5ac21c5614d42826f26d2d990562805b Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 2 Nov 2018 11:58:47 +0400 Subject: [PATCH 1/2] Instant View 2.0 support Password pending recent sessions Server call P2P privacy setting --- TelegramCore/Api0.swift | 59 +- TelegramCore/Api1.swift | 1150 +++++++++++++---- TelegramCore/Api3.swift | 44 + TelegramCore/CallSessionManager.swift | 4 +- TelegramCore/ChannelAdminEventLogs.swift | 2 +- TelegramCore/InstantPage.swift | 595 ++++++++- TelegramCore/LocalizationInfo.swift | 35 +- TelegramCore/Localizations.swift | 120 +- ...ManagedLocalizationUpdatesOperations.swift | 4 +- TelegramCore/RecentAccountSession.swift | 30 +- TelegramCore/RichText.swift | 89 ++ TelegramCore/Serialization.swift | 2 +- TelegramCore/StickerPack.swift | 2 +- 13 files changed, 1753 insertions(+), 383 deletions(-) diff --git a/TelegramCore/Api0.swift b/TelegramCore/Api0.swift index 4869a83bd4..883ae9ee02 100644 --- a/TelegramCore/Api0.swift +++ b/TelegramCore/Api0.swift @@ -30,18 +30,24 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1216809369] = { return Api.PageBlock.parse_pageBlockFooter($0) } dict[-618614392] = { return Api.PageBlock.parse_pageBlockDivider($0) } dict[-837994576] = { return Api.PageBlock.parse_pageBlockAnchor($0) } - dict[978896884] = { return Api.PageBlock.parse_pageBlockList($0) } dict[641563686] = { return Api.PageBlock.parse_pageBlockBlockquote($0) } dict[1329878739] = { return Api.PageBlock.parse_pageBlockPullquote($0) } - dict[-372860542] = { return Api.PageBlock.parse_pageBlockPhoto($0) } - dict[-640214938] = { return Api.PageBlock.parse_pageBlockVideo($0) } dict[972174080] = { return Api.PageBlock.parse_pageBlockCover($0) } - dict[-840826671] = { return Api.PageBlock.parse_pageBlockEmbed($0) } - dict[690781161] = { return Api.PageBlock.parse_pageBlockEmbedPost($0) } - dict[145955919] = { return Api.PageBlock.parse_pageBlockCollage($0) } - dict[319588707] = { return Api.PageBlock.parse_pageBlockSlideshow($0) } dict[-283684427] = { return Api.PageBlock.parse_pageBlockChannel($0) } - dict[834148991] = { return Api.PageBlock.parse_pageBlockAudio($0) } + dict[504660880] = { return Api.PageBlock.parse_pageBlockKicker($0) } + dict[-1085412734] = { return Api.PageBlock.parse_pageBlockTable($0) } + dict[391759200] = { return Api.PageBlock.parse_pageBlockPhoto($0) } + dict[2089805750] = { return Api.PageBlock.parse_pageBlockVideo($0) } + dict[-2143067670] = { return Api.PageBlock.parse_pageBlockAudio($0) } + dict[-1468953147] = { return Api.PageBlock.parse_pageBlockEmbed($0) } + dict[-229005301] = { return Api.PageBlock.parse_pageBlockEmbedPost($0) } + dict[1705048653] = { return Api.PageBlock.parse_pageBlockCollage($0) } + dict[52401552] = { return Api.PageBlock.parse_pageBlockSlideshow($0) } + dict[-454524911] = { return Api.PageBlock.parse_pageBlockList($0) } + dict[-1702174239] = { return Api.PageBlock.parse_pageBlockOrderedList($0) } + dict[1987480557] = { return Api.PageBlock.parse_pageBlockDetails($0) } + dict[370236054] = { return Api.PageBlock.parse_pageBlockRelatedArticles($0) } + dict[-1538310410] = { return Api.PageBlock.parse_pageBlockMap($0) } dict[-614138572] = { return Api.account.TmpPassword.parse_tmpPassword($0) } dict[-2103600678] = { return Api.SecureRequiredType.parse_secureRequiredType($0) } dict[41187252] = { return Api.SecureRequiredType.parse_secureRequiredTypeOneOf($0) } @@ -73,12 +79,17 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1009288385] = { return Api.RichText.parse_textUrl($0) } dict[-564523562] = { return Api.RichText.parse_textEmail($0) } dict[2120376535] = { return Api.RichText.parse_textConcat($0) } + dict[-311786236] = { return Api.RichText.parse_textSubscript($0) } + dict[-939827711] = { return Api.RichText.parse_textSuperscript($0) } + dict[55281185] = { return Api.RichText.parse_textMarked($0) } + dict[483104362] = { return Api.RichText.parse_textPhone($0) } + dict[136105807] = { return Api.RichText.parse_textImage($0) } dict[253890367] = { return Api.UserFull.parse_userFull($0) } dict[-292807034] = { return Api.InputChannel.parse_inputChannelEmpty($0) } dict[-1343524562] = { return Api.InputChannel.parse_inputChannel($0) } dict[414687501] = { return Api.DcOption.parse_dcOption($0) } dict[-1705233435] = { return Api.account.PasswordSettings.parse_passwordSettings($0) } - dict[292985073] = { return Api.LangPackLanguage.parse_langPackLanguage($0) } + dict[711286046] = { return Api.LangPackLanguage.parse_langPackLanguage($0) } dict[-1987579119] = { return Api.help.AppUpdate.parse_appUpdate($0) } dict[-1000708810] = { return Api.help.AppUpdate.parse_noAppUpdate($0) } dict[-209337866] = { return Api.LangPackDifference.parse_langPackDifference($0) } @@ -133,6 +144,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1137792208] = { return Api.PrivacyKey.parse_privacyKeyStatusTimestamp($0) } dict[1343122938] = { return Api.PrivacyKey.parse_privacyKeyChatInvite($0) } dict[1030105979] = { return Api.PrivacyKey.parse_privacyKeyPhoneCall($0) } + dict[961092808] = { return Api.PrivacyKey.parse_privacyKeyPhoneP2P($0) } dict[522914557] = { return Api.Update.parse_updateNewMessage($0) } dict[1318109142] = { return Api.Update.parse_updateMessageID($0) } dict[-1576161051] = { return Api.Update.parse_updateDeleteMessages($0) } @@ -337,6 +349,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[569137759] = { return Api.SecurePlainData.parse_securePlainEmail($0) } dict[-1269012015] = { return Api.messages.AffectedHistory.parse_affectedHistory($0) } dict[-1036572727] = { return Api.account.PasswordInputSettings.parse_passwordInputSettings($0) } + dict[878078826] = { return Api.PageTableCell.parse_pageTableCell($0) } dict[649453030] = { return Api.messages.MessageEditData.parse_messageEditData($0) } dict[-886477832] = { return Api.LabeledPrice.parse_labeledPrice($0) } dict[-438840932] = { return Api.messages.ChatFull.parse_chatFull($0) } @@ -349,7 +362,9 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[505595789] = { return Api.ReportReason.parse_inputReportReasonViolence($0) } dict[777640226] = { return Api.ReportReason.parse_inputReportReasonPornography($0) } dict[-512463606] = { return Api.ReportReason.parse_inputReportReasonOther($0) } + dict[-1685456582] = { return Api.ReportReason.parse_inputReportReasonCopyright($0) } dict[-247351839] = { return Api.InputEncryptedChat.parse_inputEncryptedChat($0) } + dict[-524237339] = { return Api.PageTableRow.parse_pageTableRow($0) } dict[-40996577] = { return Api.DraftMessage.parse_draftMessage($0) } dict[453805082] = { return Api.DraftMessage.parse_draftMessageEmpty($0) } dict[1568467877] = { return Api.ChannelAdminRights.parse_channelAdminRights($0) } @@ -372,6 +387,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1335282456] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyStatusTimestamp($0) } dict[-1107622874] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyChatInvite($0) } dict[-88417185] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyPhoneCall($0) } + dict[-610373422] = { return Api.InputPrivacyKey.parse_inputPrivacyKeyPhoneP2P($0) } dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) } dict[-1606526075] = { return Api.ReplyMarkup.parse_replyKeyboardHide($0) } dict[-200242528] = { return Api.ReplyMarkup.parse_replyKeyboardForceReply($0) } @@ -384,11 +400,12 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-316748368] = { return Api.SecureValueHash.parse_secureValueHash($0) } dict[1444661369] = { return Api.ContactBlocked.parse_contactBlocked($0) } dict[-2128698738] = { return Api.auth.CheckedPhone.parse_checkedPhone($0) } + dict[-1188055347] = { return Api.PageListItem.parse_pageListItemText($0) } + dict[635466748] = { return Api.PageListItem.parse_pageListItemBlocks($0) } dict[-1182234929] = { return Api.InputUser.parse_inputUserEmpty($0) } dict[-138301121] = { return Api.InputUser.parse_inputUserSelf($0) } dict[-668391402] = { return Api.InputUser.parse_inputUser($0) } - dict[-1908433218] = { return Api.Page.parse_pagePart($0) } - dict[1433323434] = { return Api.Page.parse_pageFull($0) } + dict[-241590104] = { return Api.Page.parse_page($0) } dict[871426631] = { return Api.SecureCredentialsEncrypted.parse_secureCredentialsEncrypted($0) } dict[157948117] = { return Api.upload.File.parse_file($0) } dict[-242427324] = { return Api.upload.File.parse_fileCdnRedirect($0) } @@ -458,6 +475,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[307276766] = { return Api.account.Authorizations.parse_authorizations($0) } dict[935395612] = { return Api.ChatPhoto.parse_chatPhotoEmpty($0) } dict[1632839530] = { return Api.ChatPhoto.parse_chatPhoto($0) } + dict[1869903447] = { return Api.PageCaption.parse_pageCaption($0) } dict[1062645411] = { return Api.payments.PaymentForm.parse_paymentForm($0) } dict[1342771681] = { return Api.payments.PaymentReceipt.parse_paymentReceipt($0) } dict[863093588] = { return Api.messages.PeerDialogs.parse_peerDialogs($0) } @@ -554,7 +572,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1776236393] = { return Api.ExportedChatInvite.parse_chatInviteEmpty($0) } dict[-64092740] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) } dict[-1389486888] = { return Api.account.AuthorizationForm.parse_authorizationForm($0) } - dict[2079516406] = { return Api.Authorization.parse_authorization($0) } + dict[-1392388579] = { return Api.Authorization.parse_authorization($0) } dict[-1361650766] = { return Api.MaskCoords.parse_maskCoords($0) } dict[-395967805] = { return Api.messages.AllStickers.parse_allStickersNotModified($0) } dict[-302170017] = { return Api.messages.AllStickers.parse_allStickers($0) } @@ -613,8 +631,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[462375633] = { return Api.PhoneCall.parse_phoneCallWaiting($0) } dict[-2089411356] = { return Api.PhoneCall.parse_phoneCallRequested($0) } dict[1828732223] = { return Api.PhoneCall.parse_phoneCallAccepted($0) } - dict[-1660057] = { return Api.PhoneCall.parse_phoneCall($0) } dict[1355435489] = { return Api.PhoneCall.parse_phoneCallDiscarded($0) } + dict[-419832333] = { return Api.PhoneCall.parse_phoneCall($0) } dict[-483352705] = { return Api.help.TermsOfServiceUpdate.parse_termsOfServiceUpdateEmpty($0) } dict[686618977] = { return Api.help.TermsOfServiceUpdate.parse_termsOfServiceUpdate($0) } dict[-445792507] = { return Api.DialogPeer.parse_dialogPeer($0) } @@ -629,6 +647,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[1889961234] = { return Api.PeerNotifySettings.parse_peerNotifySettingsEmpty($0) } dict[-1353671392] = { return Api.PeerNotifySettings.parse_peerNotifySettings($0) } dict[-1995686519] = { return Api.InputBotInlineMessageID.parse_inputBotInlineMessageID($0) } + dict[-242812612] = { return Api.PageRelatedArticle.parse_pageRelatedArticle($0) } dict[313694676] = { return Api.StickerPack.parse_stickerPack($0) } dict[1326562017] = { return Api.UserProfilePhoto.parse_userProfilePhotoEmpty($0) } dict[-715532088] = { return Api.UserProfilePhoto.parse_userProfilePhoto($0) } @@ -662,6 +681,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = { dict[-1073693790] = { return Api.auth.SentCodeType.parse_sentCodeTypeSms($0) } dict[1398007207] = { return Api.auth.SentCodeType.parse_sentCodeTypeCall($0) } dict[-1425815847] = { return Api.auth.SentCodeType.parse_sentCodeTypeFlashCall($0) } + dict[1577484359] = { return Api.PageListOrderedItem.parse_pageListOrderedItemText($0) } + dict[-1730311882] = { return Api.PageListOrderedItem.parse_pageListOrderedItemBlocks($0) } dict[-1417756512] = { return Api.EncryptedChat.parse_encryptedChatEmpty($0) } dict[1006044124] = { return Api.EncryptedChat.parse_encryptedChatWaiting($0) } dict[-931638658] = { return Api.EncryptedChat.parse_encryptedChatRequested($0) } @@ -913,6 +934,8 @@ struct Api { _1.serialize(buffer, boxed) case let _1 as Api.account.PasswordInputSettings: _1.serialize(buffer, boxed) + case let _1 as Api.PageTableCell: + _1.serialize(buffer, boxed) case let _1 as Api.messages.MessageEditData: _1.serialize(buffer, boxed) case let _1 as Api.LabeledPrice: @@ -931,6 +954,8 @@ struct Api { _1.serialize(buffer, boxed) case let _1 as Api.InputEncryptedChat: _1.serialize(buffer, boxed) + case let _1 as Api.PageTableRow: + _1.serialize(buffer, boxed) case let _1 as Api.DraftMessage: _1.serialize(buffer, boxed) case let _1 as Api.ChannelAdminRights: @@ -965,6 +990,8 @@ struct Api { _1.serialize(buffer, boxed) case let _1 as Api.auth.CheckedPhone: _1.serialize(buffer, boxed) + case let _1 as Api.PageListItem: + _1.serialize(buffer, boxed) case let _1 as Api.InputUser: _1.serialize(buffer, boxed) case let _1 as Api.Page: @@ -1029,6 +1056,8 @@ struct Api { _1.serialize(buffer, boxed) case let _1 as Api.ChatPhoto: _1.serialize(buffer, boxed) + case let _1 as Api.PageCaption: + _1.serialize(buffer, boxed) case let _1 as Api.payments.PaymentForm: _1.serialize(buffer, boxed) case let _1 as Api.payments.PaymentReceipt: @@ -1157,6 +1186,8 @@ struct Api { _1.serialize(buffer, boxed) case let _1 as Api.InputBotInlineMessageID: _1.serialize(buffer, boxed) + case let _1 as Api.PageRelatedArticle: + _1.serialize(buffer, boxed) case let _1 as Api.StickerPack: _1.serialize(buffer, boxed) case let _1 as Api.UserProfilePhoto: @@ -1177,6 +1208,8 @@ struct Api { _1.serialize(buffer, boxed) case let _1 as Api.auth.SentCodeType: _1.serialize(buffer, boxed) + case let _1 as Api.PageListOrderedItem: + _1.serialize(buffer, boxed) case let _1 as Api.EncryptedChat: _1.serialize(buffer, boxed) case let _1 as Api.Document: diff --git a/TelegramCore/Api1.swift b/TelegramCore/Api1.swift index 36fa5d0d45..88c3891a95 100644 --- a/TelegramCore/Api1.swift +++ b/TelegramCore/Api1.swift @@ -365,18 +365,24 @@ extension Api { case pageBlockFooter(text: Api.RichText) case pageBlockDivider case pageBlockAnchor(name: String) - case pageBlockList(ordered: Api.Bool, items: [Api.RichText]) case pageBlockBlockquote(text: Api.RichText, caption: Api.RichText) case pageBlockPullquote(text: Api.RichText, caption: Api.RichText) - case pageBlockPhoto(photoId: Int64, caption: Api.RichText) - case pageBlockVideo(flags: Int32, videoId: Int64, caption: Api.RichText) case pageBlockCover(cover: Api.PageBlock) - case pageBlockEmbed(flags: Int32, url: String?, html: String?, posterPhotoId: Int64?, w: Int32, h: Int32, caption: Api.RichText) - case pageBlockEmbedPost(url: String, webpageId: Int64, authorPhotoId: Int64, author: String, date: Int32, blocks: [Api.PageBlock], caption: Api.RichText) - case pageBlockCollage(items: [Api.PageBlock], caption: Api.RichText) - case pageBlockSlideshow(items: [Api.PageBlock], caption: Api.RichText) case pageBlockChannel(channel: Api.Chat) - case pageBlockAudio(audioId: Int64, caption: Api.RichText) + case pageBlockKicker(text: Api.RichText) + case pageBlockTable(flags: Int32, title: Api.RichText, rows: [Api.PageTableRow]) + case pageBlockPhoto(flags: Int32, photoId: Int64, caption: Api.PageCaption, url: String?, webpageId: Int64?) + case pageBlockVideo(flags: Int32, videoId: Int64, caption: Api.PageCaption) + case pageBlockAudio(audioId: Int64, caption: Api.PageCaption) + case pageBlockEmbed(flags: Int32, url: String?, html: String?, posterPhotoId: Int64?, w: Int32?, h: Int32?, caption: Api.PageCaption) + case pageBlockEmbedPost(url: String, webpageId: Int64, authorPhotoId: Int64, author: String, date: Int32, blocks: [Api.PageBlock], caption: Api.PageCaption) + case pageBlockCollage(items: [Api.PageBlock], caption: Api.PageCaption) + case pageBlockSlideshow(items: [Api.PageBlock], caption: Api.PageCaption) + case pageBlockList(items: [Api.PageListItem]) + case pageBlockOrderedList(items: [Api.PageListOrderedItem]) + case pageBlockDetails(flags: Int32, blocks: [Api.PageBlock], title: Api.RichText) + case pageBlockRelatedArticles(title: Api.RichText, articles: [Api.PageRelatedArticle]) + case pageBlockMap(geo: Api.GeoPoint, zoom: Int32, w: Int32, h: Int32, caption: Api.PageCaption) func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -448,17 +454,6 @@ extension Api { } serializeString(name, buffer: buffer, boxed: false) break - case .pageBlockList(let ordered, let items): - if boxed { - buffer.appendInt32(978896884) - } - ordered.serialize(buffer, true) - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(items.count)) - for item in items { - item.serialize(buffer, true) - } - break case .pageBlockBlockquote(let text, let caption): if boxed { buffer.appendInt32(641563686) @@ -473,42 +468,76 @@ extension Api { text.serialize(buffer, true) caption.serialize(buffer, true) break - case .pageBlockPhoto(let photoId, let caption): - if boxed { - buffer.appendInt32(-372860542) - } - serializeInt64(photoId, buffer: buffer, boxed: false) - caption.serialize(buffer, true) - break - case .pageBlockVideo(let flags, let videoId, let caption): - if boxed { - buffer.appendInt32(-640214938) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(videoId, buffer: buffer, boxed: false) - caption.serialize(buffer, true) - break case .pageBlockCover(let cover): if boxed { buffer.appendInt32(972174080) } cover.serialize(buffer, true) break + case .pageBlockChannel(let channel): + if boxed { + buffer.appendInt32(-283684427) + } + channel.serialize(buffer, true) + break + case .pageBlockKicker(let text): + if boxed { + buffer.appendInt32(504660880) + } + text.serialize(buffer, true) + break + case .pageBlockTable(let flags, let title, let rows): + if boxed { + buffer.appendInt32(-1085412734) + } + serializeInt32(flags, buffer: buffer, boxed: false) + title.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(rows.count)) + for item in rows { + item.serialize(buffer, true) + } + break + case .pageBlockPhoto(let flags, let photoId, let caption, let url, let webpageId): + if boxed { + buffer.appendInt32(391759200) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(photoId, buffer: buffer, boxed: false) + caption.serialize(buffer, true) + if Int(flags) & Int(1 << 0) != 0 {serializeString(url!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 0) != 0 {serializeInt64(webpageId!, buffer: buffer, boxed: false)} + break + case .pageBlockVideo(let flags, let videoId, let caption): + if boxed { + buffer.appendInt32(2089805750) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(videoId, buffer: buffer, boxed: false) + caption.serialize(buffer, true) + break + case .pageBlockAudio(let audioId, let caption): + if boxed { + buffer.appendInt32(-2143067670) + } + serializeInt64(audioId, buffer: buffer, boxed: false) + caption.serialize(buffer, true) + break case .pageBlockEmbed(let flags, let url, let html, let posterPhotoId, let w, let h, let caption): if boxed { - buffer.appendInt32(-840826671) + buffer.appendInt32(-1468953147) } serializeInt32(flags, buffer: buffer, boxed: false) if Int(flags) & Int(1 << 1) != 0 {serializeString(url!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 2) != 0 {serializeString(html!, buffer: buffer, boxed: false)} if Int(flags) & Int(1 << 4) != 0 {serializeInt64(posterPhotoId!, buffer: buffer, boxed: false)} - serializeInt32(w, buffer: buffer, boxed: false) - serializeInt32(h, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 5) != 0 {serializeInt32(w!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 5) != 0 {serializeInt32(h!, buffer: buffer, boxed: false)} caption.serialize(buffer, true) break case .pageBlockEmbedPost(let url, let webpageId, let authorPhotoId, let author, let date, let blocks, let caption): if boxed { - buffer.appendInt32(690781161) + buffer.appendInt32(-229005301) } serializeString(url, buffer: buffer, boxed: false) serializeInt64(webpageId, buffer: buffer, boxed: false) @@ -524,7 +553,7 @@ extension Api { break case .pageBlockCollage(let items, let caption): if boxed { - buffer.appendInt32(145955919) + buffer.appendInt32(1705048653) } buffer.appendInt32(481674261) buffer.appendInt32(Int32(items.count)) @@ -535,7 +564,7 @@ extension Api { break case .pageBlockSlideshow(let items, let caption): if boxed { - buffer.appendInt32(319588707) + buffer.appendInt32(52401552) } buffer.appendInt32(481674261) buffer.appendInt32(Int32(items.count)) @@ -544,17 +573,57 @@ extension Api { } caption.serialize(buffer, true) break - case .pageBlockChannel(let channel): + case .pageBlockList(let items): if boxed { - buffer.appendInt32(-283684427) + buffer.appendInt32(-454524911) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(items.count)) + for item in items { + item.serialize(buffer, true) } - channel.serialize(buffer, true) break - case .pageBlockAudio(let audioId, let caption): + case .pageBlockOrderedList(let items): if boxed { - buffer.appendInt32(834148991) + buffer.appendInt32(-1702174239) } - serializeInt64(audioId, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(items.count)) + for item in items { + item.serialize(buffer, true) + } + break + case .pageBlockDetails(let flags, let blocks, let title): + if boxed { + buffer.appendInt32(1987480557) + } + serializeInt32(flags, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(blocks.count)) + for item in blocks { + item.serialize(buffer, true) + } + title.serialize(buffer, true) + break + case .pageBlockRelatedArticles(let title, let articles): + if boxed { + buffer.appendInt32(370236054) + } + title.serialize(buffer, true) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(articles.count)) + for item in articles { + item.serialize(buffer, true) + } + break + case .pageBlockMap(let geo, let zoom, let w, let h, let caption): + if boxed { + buffer.appendInt32(-1538310410) + } + geo.serialize(buffer, true) + serializeInt32(zoom, buffer: buffer, boxed: false) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) caption.serialize(buffer, true) break } @@ -584,18 +653,24 @@ extension Api { return ("pageBlockDivider", []) case .pageBlockAnchor(let name): return ("pageBlockAnchor", [("name", name)]) - case .pageBlockList(let ordered, let items): - return ("pageBlockList", [("ordered", ordered), ("items", items)]) case .pageBlockBlockquote(let text, let caption): return ("pageBlockBlockquote", [("text", text), ("caption", caption)]) case .pageBlockPullquote(let text, let caption): return ("pageBlockPullquote", [("text", text), ("caption", caption)]) - case .pageBlockPhoto(let photoId, let caption): - return ("pageBlockPhoto", [("photoId", photoId), ("caption", caption)]) - case .pageBlockVideo(let flags, let videoId, let caption): - return ("pageBlockVideo", [("flags", flags), ("videoId", videoId), ("caption", caption)]) case .pageBlockCover(let cover): return ("pageBlockCover", [("cover", cover)]) + case .pageBlockChannel(let channel): + return ("pageBlockChannel", [("channel", channel)]) + case .pageBlockKicker(let text): + return ("pageBlockKicker", [("text", text)]) + case .pageBlockTable(let flags, let title, let rows): + return ("pageBlockTable", [("flags", flags), ("title", title), ("rows", rows)]) + case .pageBlockPhoto(let flags, let photoId, let caption, let url, let webpageId): + return ("pageBlockPhoto", [("flags", flags), ("photoId", photoId), ("caption", caption), ("url", url), ("webpageId", webpageId)]) + case .pageBlockVideo(let flags, let videoId, let caption): + return ("pageBlockVideo", [("flags", flags), ("videoId", videoId), ("caption", caption)]) + case .pageBlockAudio(let audioId, let caption): + return ("pageBlockAudio", [("audioId", audioId), ("caption", caption)]) case .pageBlockEmbed(let flags, let url, let html, let posterPhotoId, let w, let h, let caption): return ("pageBlockEmbed", [("flags", flags), ("url", url), ("html", html), ("posterPhotoId", posterPhotoId), ("w", w), ("h", h), ("caption", caption)]) case .pageBlockEmbedPost(let url, let webpageId, let authorPhotoId, let author, let date, let blocks, let caption): @@ -604,10 +679,16 @@ extension Api { return ("pageBlockCollage", [("items", items), ("caption", caption)]) case .pageBlockSlideshow(let items, let caption): return ("pageBlockSlideshow", [("items", items), ("caption", caption)]) - case .pageBlockChannel(let channel): - return ("pageBlockChannel", [("channel", channel)]) - case .pageBlockAudio(let audioId, let caption): - return ("pageBlockAudio", [("audioId", audioId), ("caption", caption)]) + case .pageBlockList(let items): + return ("pageBlockList", [("items", items)]) + case .pageBlockOrderedList(let items): + return ("pageBlockOrderedList", [("items", items)]) + case .pageBlockDetails(let flags, let blocks, let title): + return ("pageBlockDetails", [("flags", flags), ("blocks", blocks), ("title", title)]) + case .pageBlockRelatedArticles(let title, let articles): + return ("pageBlockRelatedArticles", [("title", title), ("articles", articles)]) + case .pageBlockMap(let geo, let zoom, let w, let h, let caption): + return ("pageBlockMap", [("geo", geo), ("zoom", zoom), ("w", w), ("h", h), ("caption", caption)]) } } @@ -738,24 +819,6 @@ extension Api { return nil } } - static func parse_pageBlockList(_ reader: BufferReader) -> PageBlock? { - var _1: Api.Bool? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Bool - } - var _2: [Api.RichText]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RichText.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockList(ordered: _1!, items: _2!) - } - else { - return nil - } - } static func parse_pageBlockBlockquote(_ reader: BufferReader) -> PageBlock? { var _1: Api.RichText? if let signature = reader.readInt32() { @@ -792,17 +855,86 @@ extension Api { return nil } } - static func parse_pageBlockPhoto(_ reader: BufferReader) -> PageBlock? { - var _1: Int64? - _1 = reader.readInt64() + static func parse_pageBlockCover(_ reader: BufferReader) -> PageBlock? { + var _1: Api.PageBlock? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.PageBlock + } + let _c1 = _1 != nil + if _c1 { + return Api.PageBlock.pageBlockCover(cover: _1!) + } + else { + return nil + } + } + static func parse_pageBlockChannel(_ reader: BufferReader) -> PageBlock? { + var _1: Api.Chat? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.Chat + } + let _c1 = _1 != nil + if _c1 { + return Api.PageBlock.pageBlockChannel(channel: _1!) + } + else { + return nil + } + } + static func parse_pageBlockKicker(_ reader: BufferReader) -> PageBlock? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.PageBlock.pageBlockKicker(text: _1!) + } + else { + return nil + } + } + static func parse_pageBlockTable(_ reader: BufferReader) -> PageBlock? { + var _1: Int32? + _1 = reader.readInt32() var _2: Api.RichText? if let signature = reader.readInt32() { _2 = Api.parse(reader, signature: signature) as? Api.RichText } + var _3: [Api.PageTableRow]? + if let _ = reader.readInt32() { + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageTableRow.self) + } let _c1 = _1 != nil let _c2 = _2 != nil - if _c1 && _c2 { - return Api.PageBlock.pageBlockPhoto(photoId: _1!, caption: _2!) + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.PageBlock.pageBlockTable(flags: _1!, title: _2!, rows: _3!) + } + else { + return nil + } + } + static func parse_pageBlockPhoto(_ reader: BufferReader) -> PageBlock? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Api.PageCaption? + if let signature = reader.readInt32() { + _3 = Api.parse(reader, signature: signature) as? Api.PageCaption + } + var _4: String? + if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } + var _5: Int64? + if Int(_1!) & Int(1 << 0) != 0 {_5 = reader.readInt64() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 0) == 0) || _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.PageBlock.pageBlockPhoto(flags: _1!, photoId: _2!, caption: _3!, url: _4, webpageId: _5) } else { return nil @@ -813,9 +945,9 @@ extension Api { _1 = reader.readInt32() var _2: Int64? _2 = reader.readInt64() - var _3: Api.RichText? + var _3: Api.PageCaption? if let signature = reader.readInt32() { - _3 = Api.parse(reader, signature: signature) as? Api.RichText + _3 = Api.parse(reader, signature: signature) as? Api.PageCaption } let _c1 = _1 != nil let _c2 = _2 != nil @@ -827,14 +959,17 @@ extension Api { return nil } } - static func parse_pageBlockCover(_ reader: BufferReader) -> PageBlock? { - var _1: Api.PageBlock? + static func parse_pageBlockAudio(_ reader: BufferReader) -> PageBlock? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Api.PageCaption? if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.PageBlock + _2 = Api.parse(reader, signature: signature) as? Api.PageCaption } let _c1 = _1 != nil - if _c1 { - return Api.PageBlock.pageBlockCover(cover: _1!) + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.PageBlock.pageBlockAudio(audioId: _1!, caption: _2!) } else { return nil @@ -850,22 +985,22 @@ extension Api { var _4: Int64? if Int(_1!) & Int(1 << 4) != 0 {_4 = reader.readInt64() } var _5: Int32? - _5 = reader.readInt32() + if Int(_1!) & Int(1 << 5) != 0 {_5 = reader.readInt32() } var _6: Int32? - _6 = reader.readInt32() - var _7: Api.RichText? + if Int(_1!) & Int(1 << 5) != 0 {_6 = reader.readInt32() } + var _7: Api.PageCaption? if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.RichText + _7 = Api.parse(reader, signature: signature) as? Api.PageCaption } let _c1 = _1 != nil let _c2 = (Int(_1!) & Int(1 << 1) == 0) || _2 != nil let _c3 = (Int(_1!) & Int(1 << 2) == 0) || _3 != nil let _c4 = (Int(_1!) & Int(1 << 4) == 0) || _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil + let _c5 = (Int(_1!) & Int(1 << 5) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 5) == 0) || _6 != nil let _c7 = _7 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { - return Api.PageBlock.pageBlockEmbed(flags: _1!, url: _2, html: _3, posterPhotoId: _4, w: _5!, h: _6!, caption: _7!) + return Api.PageBlock.pageBlockEmbed(flags: _1!, url: _2, html: _3, posterPhotoId: _4, w: _5, h: _6, caption: _7!) } else { return nil @@ -886,9 +1021,9 @@ extension Api { if let _ = reader.readInt32() { _6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) } - var _7: Api.RichText? + var _7: Api.PageCaption? if let signature = reader.readInt32() { - _7 = Api.parse(reader, signature: signature) as? Api.RichText + _7 = Api.parse(reader, signature: signature) as? Api.PageCaption } let _c1 = _1 != nil let _c2 = _2 != nil @@ -909,9 +1044,9 @@ extension Api { if let _ = reader.readInt32() { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) } - var _2: Api.RichText? + var _2: Api.PageCaption? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.RichText + _2 = Api.parse(reader, signature: signature) as? Api.PageCaption } let _c1 = _1 != nil let _c2 = _2 != nil @@ -927,9 +1062,9 @@ extension Api { if let _ = reader.readInt32() { _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) } - var _2: Api.RichText? + var _2: Api.PageCaption? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.RichText + _2 = Api.parse(reader, signature: signature) as? Api.PageCaption } let _c1 = _1 != nil let _c2 = _2 != nil @@ -940,30 +1075,93 @@ extension Api { return nil } } - static func parse_pageBlockChannel(_ reader: BufferReader) -> PageBlock? { - var _1: Api.Chat? - if let signature = reader.readInt32() { - _1 = Api.parse(reader, signature: signature) as? Api.Chat + static func parse_pageBlockList(_ reader: BufferReader) -> PageBlock? { + var _1: [Api.PageListItem]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageListItem.self) } let _c1 = _1 != nil if _c1 { - return Api.PageBlock.pageBlockChannel(channel: _1!) + return Api.PageBlock.pageBlockList(items: _1!) } else { return nil } } - static func parse_pageBlockAudio(_ reader: BufferReader) -> PageBlock? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Api.RichText? + static func parse_pageBlockOrderedList(_ reader: BufferReader) -> PageBlock? { + var _1: [Api.PageListOrderedItem]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageListOrderedItem.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.PageBlock.pageBlockOrderedList(items: _1!) + } + else { + return nil + } + } + static func parse_pageBlockDetails(_ reader: BufferReader) -> PageBlock? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.PageBlock]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) + } + var _3: Api.RichText? if let signature = reader.readInt32() { - _2 = Api.parse(reader, signature: signature) as? Api.RichText + _3 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.PageBlock.pageBlockDetails(flags: _1!, blocks: _2!, title: _3!) + } + else { + return nil + } + } + static func parse_pageBlockRelatedArticles(_ reader: BufferReader) -> PageBlock? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + var _2: [Api.PageRelatedArticle]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageRelatedArticle.self) } let _c1 = _1 != nil let _c2 = _2 != nil if _c1 && _c2 { - return Api.PageBlock.pageBlockAudio(audioId: _1!, caption: _2!) + return Api.PageBlock.pageBlockRelatedArticles(title: _1!, articles: _2!) + } + else { + return nil + } + } + static func parse_pageBlockMap(_ reader: BufferReader) -> PageBlock? { + var _1: Api.GeoPoint? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.GeoPoint + } + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + var _4: Int32? + _4 = reader.readInt32() + var _5: Api.PageCaption? + if let signature = reader.readInt32() { + _5 = Api.parse(reader, signature: signature) as? Api.PageCaption + } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 { + return Api.PageBlock.pageBlockMap(geo: _1!, zoom: _2!, w: _3!, h: _4!, caption: _5!) } else { return nil @@ -1632,6 +1830,11 @@ extension Api { case textUrl(text: Api.RichText, url: String, webpageId: Int64) case textEmail(text: Api.RichText, email: String) case textConcat(texts: [Api.RichText]) + case textSubscript(text: Api.RichText) + case textSuperscript(text: Api.RichText) + case textMarked(text: Api.RichText) + case textPhone(text: Api.RichText, phone: String) + case textImage(documentId: Int64, w: Int32, h: Int32) func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -1702,6 +1905,39 @@ extension Api { item.serialize(buffer, true) } break + case .textSubscript(let text): + if boxed { + buffer.appendInt32(-311786236) + } + text.serialize(buffer, true) + break + case .textSuperscript(let text): + if boxed { + buffer.appendInt32(-939827711) + } + text.serialize(buffer, true) + break + case .textMarked(let text): + if boxed { + buffer.appendInt32(55281185) + } + text.serialize(buffer, true) + break + case .textPhone(let text, let phone): + if boxed { + buffer.appendInt32(483104362) + } + text.serialize(buffer, true) + serializeString(phone, buffer: buffer, boxed: false) + break + case .textImage(let documentId, let w, let h): + if boxed { + buffer.appendInt32(136105807) + } + serializeInt64(documentId, buffer: buffer, boxed: false) + serializeInt32(w, buffer: buffer, boxed: false) + serializeInt32(h, buffer: buffer, boxed: false) + break } } @@ -1727,6 +1963,16 @@ extension Api { return ("textEmail", [("text", text), ("email", email)]) case .textConcat(let texts): return ("textConcat", [("texts", texts)]) + case .textSubscript(let text): + return ("textSubscript", [("text", text)]) + case .textSuperscript(let text): + return ("textSuperscript", [("text", text)]) + case .textMarked(let text): + return ("textMarked", [("text", text)]) + case .textPhone(let text, let phone): + return ("textPhone", [("text", text), ("phone", phone)]) + case .textImage(let documentId, let w, let h): + return ("textImage", [("documentId", documentId), ("w", w), ("h", h)]) } } @@ -1857,6 +2103,78 @@ extension Api { return nil } } + static func parse_textSubscript(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textSubscript(text: _1!) + } + else { + return nil + } + } + static func parse_textSuperscript(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textSuperscript(text: _1!) + } + else { + return nil + } + } + static func parse_textMarked(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.RichText.textMarked(text: _1!) + } + else { + return nil + } + } + static func parse_textPhone(_ reader: BufferReader) -> RichText? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + var _2: String? + _2 = parseString(reader) + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.RichText.textPhone(text: _1!, phone: _2!) + } + else { + return nil + } + } + static func parse_textImage(_ reader: BufferReader) -> RichText? { + var _1: Int64? + _1 = reader.readInt64() + var _2: Int32? + _2 = reader.readInt32() + var _3: Int32? + _3 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + if _c1 && _c2 && _c3 { + return Api.RichText.textImage(documentId: _1!, w: _2!, h: _3!) + } + else { + return nil + } + } } enum UserFull: TypeConstructorDescription { @@ -2032,40 +2350,56 @@ extension Api { } enum LangPackLanguage: TypeConstructorDescription { - case langPackLanguage(name: String, nativeName: String, langCode: String) + case langPackLanguage(flags: Int32, name: String, nativeName: String, langCode: String, baseLangCode: String?, stringsCount: Int32, translatedCount: Int32) func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .langPackLanguage(let name, let nativeName, let langCode): + case .langPackLanguage(let flags, let name, let nativeName, let langCode, let baseLangCode, let stringsCount, let translatedCount): if boxed { - buffer.appendInt32(292985073) + buffer.appendInt32(711286046) } + serializeInt32(flags, buffer: buffer, boxed: false) serializeString(name, buffer: buffer, boxed: false) serializeString(nativeName, buffer: buffer, boxed: false) serializeString(langCode, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 1) != 0 {serializeString(baseLangCode!, buffer: buffer, boxed: false)} + serializeInt32(stringsCount, buffer: buffer, boxed: false) + serializeInt32(translatedCount, buffer: buffer, boxed: false) break } } func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .langPackLanguage(let name, let nativeName, let langCode): - return ("langPackLanguage", [("name", name), ("nativeName", nativeName), ("langCode", langCode)]) + case .langPackLanguage(let flags, let name, let nativeName, let langCode, let baseLangCode, let stringsCount, let translatedCount): + return ("langPackLanguage", [("flags", flags), ("name", name), ("nativeName", nativeName), ("langCode", langCode), ("baseLangCode", baseLangCode), ("stringsCount", stringsCount), ("translatedCount", translatedCount)]) } } static func parse_langPackLanguage(_ reader: BufferReader) -> LangPackLanguage? { - var _1: String? - _1 = parseString(reader) + var _1: Int32? + _1 = reader.readInt32() var _2: String? _2 = parseString(reader) var _3: String? _3 = parseString(reader) + var _4: String? + _4 = parseString(reader) + var _5: String? + if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) } + var _6: Int32? + _6 = reader.readInt32() + var _7: Int32? + _7 = reader.readInt32() let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.LangPackLanguage.langPackLanguage(name: _1!, nativeName: _2!, langCode: _3!) + let _c4 = _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 { + return Api.LangPackLanguage.langPackLanguage(flags: _1!, name: _2!, nativeName: _3!, langCode: _4!, baseLangCode: _5, stringsCount: _6!, translatedCount: _7!) } else { return nil @@ -2919,6 +3253,7 @@ extension Api { case privacyKeyStatusTimestamp case privacyKeyChatInvite case privacyKeyPhoneCall + case privacyKeyPhoneP2P func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -2939,6 +3274,12 @@ extension Api { buffer.appendInt32(1030105979) } + break + case .privacyKeyPhoneP2P: + if boxed { + buffer.appendInt32(961092808) + } + break } } @@ -2951,6 +3292,8 @@ extension Api { return ("privacyKeyChatInvite", []) case .privacyKeyPhoneCall: return ("privacyKeyPhoneCall", []) + case .privacyKeyPhoneP2P: + return ("privacyKeyPhoneP2P", []) } } @@ -2963,6 +3306,9 @@ extension Api { static func parse_privacyKeyPhoneCall(_ reader: BufferReader) -> PrivacyKey? { return Api.PrivacyKey.privacyKeyPhoneCall } + static func parse_privacyKeyPhoneP2P(_ reader: BufferReader) -> PrivacyKey? { + return Api.PrivacyKey.privacyKeyPhoneP2P + } } enum Update: TypeConstructorDescription { @@ -8122,6 +8468,54 @@ extension Api { } } + } + enum PageTableCell: TypeConstructorDescription { + case pageTableCell(flags: Int32, text: Api.RichText?, colspan: Int32?, rowspan: Int32?) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .pageTableCell(let flags, let text, let colspan, let rowspan): + if boxed { + buffer.appendInt32(878078826) + } + serializeInt32(flags, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 7) != 0 {text!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(colspan!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt32(rowspan!, buffer: buffer, boxed: false)} + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .pageTableCell(let flags, let text, let colspan, let rowspan): + return ("pageTableCell", [("flags", flags), ("text", text), ("colspan", colspan), ("rowspan", rowspan)]) + } + } + + static func parse_pageTableCell(_ reader: BufferReader) -> PageTableCell? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Api.RichText? + if Int(_1!) & Int(1 << 7) != 0 {if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.RichText + } } + var _3: Int32? + if Int(_1!) & Int(1 << 1) != 0 {_3 = reader.readInt32() } + var _4: Int32? + if Int(_1!) & Int(1 << 2) != 0 {_4 = reader.readInt32() } + let _c1 = _1 != nil + let _c2 = (Int(_1!) & Int(1 << 7) == 0) || _2 != nil + let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil + let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.PageTableCell.pageTableCell(flags: _1!, text: _2, colspan: _3, rowspan: _4) + } + else { + return nil + } + } + } enum LabeledPrice: TypeConstructorDescription { case labeledPrice(label: String, amount: Int64) @@ -8256,6 +8650,7 @@ extension Api { case inputReportReasonViolence case inputReportReasonPornography case inputReportReasonOther(text: String) + case inputReportReasonCopyright func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -8282,6 +8677,12 @@ extension Api { buffer.appendInt32(-512463606) } serializeString(text, buffer: buffer, boxed: false) + break + case .inputReportReasonCopyright: + if boxed { + buffer.appendInt32(-1685456582) + } + break } } @@ -8296,6 +8697,8 @@ extension Api { return ("inputReportReasonPornography", []) case .inputReportReasonOther(let text): return ("inputReportReasonOther", [("text", text)]) + case .inputReportReasonCopyright: + return ("inputReportReasonCopyright", []) } } @@ -8319,6 +8722,9 @@ extension Api { return nil } } + static func parse_inputReportReasonCopyright(_ reader: BufferReader) -> ReportReason? { + return Api.ReportReason.inputReportReasonCopyright + } } enum InputEncryptedChat: TypeConstructorDescription { @@ -8358,6 +8764,46 @@ extension Api { } } + } + enum PageTableRow: TypeConstructorDescription { + case pageTableRow(cells: [Api.PageTableCell]) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .pageTableRow(let cells): + if boxed { + buffer.appendInt32(-524237339) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(cells.count)) + for item in cells { + item.serialize(buffer, true) + } + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .pageTableRow(let cells): + return ("pageTableRow", [("cells", cells)]) + } + } + + static func parse_pageTableRow(_ reader: BufferReader) -> PageTableRow? { + var _1: [Api.PageTableCell]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageTableCell.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.PageTableRow.pageTableRow(cells: _1!) + } + else { + return nil + } + } + } enum DraftMessage: TypeConstructorDescription { case draftMessage(flags: Int32, replyToMsgId: Int32?, message: String, entities: [Api.MessageEntity]?, date: Int32) @@ -8937,6 +9383,7 @@ extension Api { case inputPrivacyKeyStatusTimestamp case inputPrivacyKeyChatInvite case inputPrivacyKeyPhoneCall + case inputPrivacyKeyPhoneP2P func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -8957,6 +9404,12 @@ extension Api { buffer.appendInt32(-88417185) } + break + case .inputPrivacyKeyPhoneP2P: + if boxed { + buffer.appendInt32(-610373422) + } + break } } @@ -8969,6 +9422,8 @@ extension Api { return ("inputPrivacyKeyChatInvite", []) case .inputPrivacyKeyPhoneCall: return ("inputPrivacyKeyPhoneCall", []) + case .inputPrivacyKeyPhoneP2P: + return ("inputPrivacyKeyPhoneP2P", []) } } @@ -8981,6 +9436,9 @@ extension Api { static func parse_inputPrivacyKeyPhoneCall(_ reader: BufferReader) -> InputPrivacyKey? { return Api.InputPrivacyKey.inputPrivacyKeyPhoneCall } + static func parse_inputPrivacyKeyPhoneP2P(_ reader: BufferReader) -> InputPrivacyKey? { + return Api.InputPrivacyKey.inputPrivacyKeyPhoneP2P + } } enum ReplyMarkup: TypeConstructorDescription { @@ -9346,6 +9804,68 @@ extension Api { } } + } + enum PageListItem: TypeConstructorDescription { + case pageListItemText(text: Api.RichText) + case pageListItemBlocks(blocks: [Api.PageBlock]) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .pageListItemText(let text): + if boxed { + buffer.appendInt32(-1188055347) + } + text.serialize(buffer, true) + break + case .pageListItemBlocks(let blocks): + if boxed { + buffer.appendInt32(635466748) + } + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(blocks.count)) + for item in blocks { + item.serialize(buffer, true) + } + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .pageListItemText(let text): + return ("pageListItemText", [("text", text)]) + case .pageListItemBlocks(let blocks): + return ("pageListItemBlocks", [("blocks", blocks)]) + } + } + + static func parse_pageListItemText(_ reader: BufferReader) -> PageListItem? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + if _c1 { + return Api.PageListItem.pageListItemText(text: _1!) + } + else { + return nil + } + } + static func parse_pageListItemBlocks(_ reader: BufferReader) -> PageListItem? { + var _1: [Api.PageBlock]? + if let _ = reader.readInt32() { + _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) + } + let _c1 = _1 != nil + if _c1 { + return Api.PageListItem.pageListItemBlocks(blocks: _1!) + } + else { + return nil + } + } + } enum InputUser: TypeConstructorDescription { case inputUserEmpty @@ -9410,35 +9930,15 @@ extension Api { } enum Page: TypeConstructorDescription { - case pagePart(blocks: [Api.PageBlock], photos: [Api.Photo], documents: [Api.Document]) - case pageFull(blocks: [Api.PageBlock], photos: [Api.Photo], documents: [Api.Document]) + case page(flags: Int32, blocks: [Api.PageBlock], photos: [Api.Photo], documents: [Api.Document]) func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .pagePart(let blocks, let photos, let documents): + case .page(let flags, let blocks, let photos, let documents): if boxed { - buffer.appendInt32(-1908433218) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(blocks.count)) - for item in blocks { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(photos.count)) - for item in photos { - item.serialize(buffer, true) - } - buffer.appendInt32(481674261) - buffer.appendInt32(Int32(documents.count)) - for item in documents { - item.serialize(buffer, true) - } - break - case .pageFull(let blocks, let photos, let documents): - if boxed { - buffer.appendInt32(1433323434) + buffer.appendInt32(-241590104) } + serializeInt32(flags, buffer: buffer, boxed: false) buffer.appendInt32(481674261) buffer.appendInt32(Int32(blocks.count)) for item in blocks { @@ -9460,54 +9960,32 @@ extension Api { func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .pagePart(let blocks, let photos, let documents): - return ("pagePart", [("blocks", blocks), ("photos", photos), ("documents", documents)]) - case .pageFull(let blocks, let photos, let documents): - return ("pageFull", [("blocks", blocks), ("photos", photos), ("documents", documents)]) + case .page(let flags, let blocks, let photos, let documents): + return ("page", [("flags", flags), ("blocks", blocks), ("photos", photos), ("documents", documents)]) } } - static func parse_pagePart(_ reader: BufferReader) -> Page? { - var _1: [Api.PageBlock]? + static func parse_page(_ reader: BufferReader) -> Page? { + var _1: Int32? + _1 = reader.readInt32() + var _2: [Api.PageBlock]? if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) } - var _2: [Api.Photo]? + var _3: [Api.Photo]? if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self) + _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self) } - var _3: [Api.Document]? + var _4: [Api.Document]? if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) + _4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) } let _c1 = _1 != nil let _c2 = _2 != nil let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Page.pagePart(blocks: _1!, photos: _2!, documents: _3!) - } - else { - return nil - } - } - static func parse_pageFull(_ reader: BufferReader) -> Page? { - var _1: [Api.PageBlock]? - if let _ = reader.readInt32() { - _1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) - } - var _2: [Api.Photo]? - if let _ = reader.readInt32() { - _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Photo.self) - } - var _3: [Api.Document]? - if let _ = reader.readInt32() { - _3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self) - } - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - if _c1 && _c2 && _c3 { - return Api.Page.pageFull(blocks: _1!, photos: _2!, documents: _3!) + let _c4 = _4 != nil + if _c1 && _c2 && _c3 && _c4 { + return Api.Page.page(flags: _1!, blocks: _2!, photos: _3!, documents: _4!) } else { return nil @@ -11486,6 +11964,48 @@ extension Api { } } + } + enum PageCaption: TypeConstructorDescription { + case pageCaption(text: Api.RichText, credit: Api.RichText) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .pageCaption(let text, let credit): + if boxed { + buffer.appendInt32(1869903447) + } + text.serialize(buffer, true) + credit.serialize(buffer, true) + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .pageCaption(let text, let credit): + return ("pageCaption", [("text", text), ("credit", credit)]) + } + } + + static func parse_pageCaption(_ reader: BufferReader) -> PageCaption? { + var _1: Api.RichText? + if let signature = reader.readInt32() { + _1 = Api.parse(reader, signature: signature) as? Api.RichText + } + var _2: Api.RichText? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.PageCaption.pageCaption(text: _1!, credit: _2!) + } + else { + return nil + } + } + } enum InputStickerSet: TypeConstructorDescription { case inputStickerSetEmpty @@ -13776,16 +14296,16 @@ extension Api { } enum Authorization: TypeConstructorDescription { - case authorization(hash: Int64, flags: Int32, deviceModel: String, platform: String, systemVersion: String, apiId: Int32, appName: String, appVersion: String, dateCreated: Int32, dateActive: Int32, ip: String, country: String, region: String) + case authorization(flags: Int32, hash: Int64, deviceModel: String, platform: String, systemVersion: String, apiId: Int32, appName: String, appVersion: String, dateCreated: Int32, dateActive: Int32, ip: String, country: String, region: String) func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { - case .authorization(let hash, let flags, let deviceModel, let platform, let systemVersion, let apiId, let appName, let appVersion, let dateCreated, let dateActive, let ip, let country, let region): + case .authorization(let flags, let hash, let deviceModel, let platform, let systemVersion, let apiId, let appName, let appVersion, let dateCreated, let dateActive, let ip, let country, let region): if boxed { - buffer.appendInt32(2079516406) + buffer.appendInt32(-1392388579) } - serializeInt64(hash, buffer: buffer, boxed: false) serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(hash, buffer: buffer, boxed: false) serializeString(deviceModel, buffer: buffer, boxed: false) serializeString(platform, buffer: buffer, boxed: false) serializeString(systemVersion, buffer: buffer, boxed: false) @@ -13803,16 +14323,16 @@ extension Api { func descriptionFields() -> (String, [(String, Any)]) { switch self { - case .authorization(let hash, let flags, let deviceModel, let platform, let systemVersion, let apiId, let appName, let appVersion, let dateCreated, let dateActive, let ip, let country, let region): - return ("authorization", [("hash", hash), ("flags", flags), ("deviceModel", deviceModel), ("platform", platform), ("systemVersion", systemVersion), ("apiId", apiId), ("appName", appName), ("appVersion", appVersion), ("dateCreated", dateCreated), ("dateActive", dateActive), ("ip", ip), ("country", country), ("region", region)]) + case .authorization(let flags, let hash, let deviceModel, let platform, let systemVersion, let apiId, let appName, let appVersion, let dateCreated, let dateActive, let ip, let country, let region): + return ("authorization", [("flags", flags), ("hash", hash), ("deviceModel", deviceModel), ("platform", platform), ("systemVersion", systemVersion), ("apiId", apiId), ("appName", appName), ("appVersion", appVersion), ("dateCreated", dateCreated), ("dateActive", dateActive), ("ip", ip), ("country", country), ("region", region)]) } } static func parse_authorization(_ reader: BufferReader) -> Authorization? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int32? - _2 = reader.readInt32() + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() var _3: String? _3 = parseString(reader) var _4: String? @@ -13849,7 +14369,7 @@ extension Api { let _c12 = _12 != nil let _c13 = _13 != nil if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 { - return Api.Authorization.authorization(hash: _1!, flags: _2!, deviceModel: _3!, platform: _4!, systemVersion: _5!, apiId: _6!, appName: _7!, appVersion: _8!, dateCreated: _9!, dateActive: _10!, ip: _11!, country: _12!, region: _13!) + return Api.Authorization.authorization(flags: _1!, hash: _2!, deviceModel: _3!, platform: _4!, systemVersion: _5!, apiId: _6!, appName: _7!, appVersion: _8!, dateCreated: _9!, dateActive: _10!, ip: _11!, country: _12!, region: _13!) } else { return nil @@ -15024,8 +15544,8 @@ extension Api { case phoneCallWaiting(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int32, participantId: Int32, protocol: Api.PhoneCallProtocol, receiveDate: Int32?) case phoneCallRequested(id: Int64, accessHash: Int64, date: Int32, adminId: Int32, participantId: Int32, gAHash: Buffer, protocol: Api.PhoneCallProtocol) case phoneCallAccepted(id: Int64, accessHash: Int64, date: Int32, adminId: Int32, participantId: Int32, gB: Buffer, protocol: Api.PhoneCallProtocol) - case phoneCall(id: Int64, accessHash: Int64, date: Int32, adminId: Int32, participantId: Int32, gAOrB: Buffer, keyFingerprint: Int64, protocol: Api.PhoneCallProtocol, connection: Api.PhoneConnection, alternativeConnections: [Api.PhoneConnection], startDate: Int32) case phoneCallDiscarded(flags: Int32, id: Int64, reason: Api.PhoneCallDiscardReason?, duration: Int32?) + case phoneCall(flags: Int32, id: Int64, accessHash: Int64, date: Int32, adminId: Int32, participantId: Int32, gAOrB: Buffer, keyFingerprint: Int64, protocol: Api.PhoneCallProtocol, connection: Api.PhoneConnection, alternativeConnections: [Api.PhoneConnection], startDate: Int32) func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { switch self { @@ -15072,10 +15592,20 @@ extension Api { serializeBytes(gB, buffer: buffer, boxed: false) `protocol`.serialize(buffer, true) break - case .phoneCall(let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connection, let alternativeConnections, let startDate): + case .phoneCallDiscarded(let flags, let id, let reason, let duration): if boxed { - buffer.appendInt32(-1660057) + buffer.appendInt32(1355435489) } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeInt64(id, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {reason!.serialize(buffer, true)} + if Int(flags) & Int(1 << 1) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} + break + case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connection, let alternativeConnections, let startDate): + if boxed { + buffer.appendInt32(-419832333) + } + serializeInt32(flags, buffer: buffer, boxed: false) serializeInt64(id, buffer: buffer, boxed: false) serializeInt64(accessHash, buffer: buffer, boxed: false) serializeInt32(date, buffer: buffer, boxed: false) @@ -15092,15 +15622,6 @@ extension Api { } serializeInt32(startDate, buffer: buffer, boxed: false) break - case .phoneCallDiscarded(let flags, let id, let reason, let duration): - if boxed { - buffer.appendInt32(1355435489) - } - serializeInt32(flags, buffer: buffer, boxed: false) - serializeInt64(id, buffer: buffer, boxed: false) - if Int(flags) & Int(1 << 0) != 0 {reason!.serialize(buffer, true)} - if Int(flags) & Int(1 << 1) != 0 {serializeInt32(duration!, buffer: buffer, boxed: false)} - break } } @@ -15114,10 +15635,10 @@ extension Api { return ("phoneCallRequested", [("id", id), ("accessHash", accessHash), ("date", date), ("adminId", adminId), ("participantId", participantId), ("gAHash", gAHash), ("`protocol`", `protocol`)]) case .phoneCallAccepted(let id, let accessHash, let date, let adminId, let participantId, let gB, let `protocol`): return ("phoneCallAccepted", [("id", id), ("accessHash", accessHash), ("date", date), ("adminId", adminId), ("participantId", participantId), ("gB", gB), ("`protocol`", `protocol`)]) - case .phoneCall(let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connection, let alternativeConnections, let startDate): - return ("phoneCall", [("id", id), ("accessHash", accessHash), ("date", date), ("adminId", adminId), ("participantId", participantId), ("gAOrB", gAOrB), ("keyFingerprint", keyFingerprint), ("`protocol`", `protocol`), ("connection", connection), ("alternativeConnections", alternativeConnections), ("startDate", startDate)]) case .phoneCallDiscarded(let flags, let id, let reason, let duration): return ("phoneCallDiscarded", [("flags", flags), ("id", id), ("reason", reason), ("duration", duration)]) + case .phoneCall(let flags, let id, let accessHash, let date, let adminId, let participantId, let gAOrB, let keyFingerprint, let `protocol`, let connection, let alternativeConnections, let startDate): + return ("phoneCall", [("flags", flags), ("id", id), ("accessHash", accessHash), ("date", date), ("adminId", adminId), ("participantId", participantId), ("gAOrB", gAOrB), ("keyFingerprint", keyFingerprint), ("`protocol`", `protocol`), ("connection", connection), ("alternativeConnections", alternativeConnections), ("startDate", startDate)]) } } @@ -15228,53 +15749,6 @@ extension Api { return nil } } - static func parse_phoneCall(_ reader: BufferReader) -> PhoneCall? { - var _1: Int64? - _1 = reader.readInt64() - var _2: Int64? - _2 = reader.readInt64() - var _3: Int32? - _3 = reader.readInt32() - var _4: Int32? - _4 = reader.readInt32() - var _5: Int32? - _5 = reader.readInt32() - var _6: Buffer? - _6 = parseBytes(reader) - var _7: Int64? - _7 = reader.readInt64() - var _8: Api.PhoneCallProtocol? - if let signature = reader.readInt32() { - _8 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol - } - var _9: Api.PhoneConnection? - if let signature = reader.readInt32() { - _9 = Api.parse(reader, signature: signature) as? Api.PhoneConnection - } - var _10: [Api.PhoneConnection]? - if let _ = reader.readInt32() { - _10 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhoneConnection.self) - } - var _11: Int32? - _11 = reader.readInt32() - let _c1 = _1 != nil - let _c2 = _2 != nil - let _c3 = _3 != nil - let _c4 = _4 != nil - let _c5 = _5 != nil - let _c6 = _6 != nil - let _c7 = _7 != nil - let _c8 = _8 != nil - let _c9 = _9 != nil - let _c10 = _10 != nil - let _c11 = _11 != nil - if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 { - return Api.PhoneCall.phoneCall(id: _1!, accessHash: _2!, date: _3!, adminId: _4!, participantId: _5!, gAOrB: _6!, keyFingerprint: _7!, protocol: _8!, connection: _9!, alternativeConnections: _10!, startDate: _11!) - } - else { - return nil - } - } static func parse_phoneCallDiscarded(_ reader: BufferReader) -> PhoneCall? { var _1: Int32? _1 = reader.readInt32() @@ -15297,6 +15771,56 @@ extension Api { return nil } } + static func parse_phoneCall(_ reader: BufferReader) -> PhoneCall? { + var _1: Int32? + _1 = reader.readInt32() + var _2: Int64? + _2 = reader.readInt64() + var _3: Int64? + _3 = reader.readInt64() + var _4: Int32? + _4 = reader.readInt32() + var _5: Int32? + _5 = reader.readInt32() + var _6: Int32? + _6 = reader.readInt32() + var _7: Buffer? + _7 = parseBytes(reader) + var _8: Int64? + _8 = reader.readInt64() + var _9: Api.PhoneCallProtocol? + if let signature = reader.readInt32() { + _9 = Api.parse(reader, signature: signature) as? Api.PhoneCallProtocol + } + var _10: Api.PhoneConnection? + if let signature = reader.readInt32() { + _10 = Api.parse(reader, signature: signature) as? Api.PhoneConnection + } + var _11: [Api.PhoneConnection]? + if let _ = reader.readInt32() { + _11 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PhoneConnection.self) + } + var _12: Int32? + _12 = reader.readInt32() + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = _4 != nil + let _c5 = _5 != nil + let _c6 = _6 != nil + let _c7 = _7 != nil + let _c8 = _8 != nil + let _c9 = _9 != nil + let _c10 = _10 != nil + let _c11 = _11 != nil + let _c12 = _12 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 { + return Api.PhoneCall.phoneCall(flags: _1!, id: _2!, accessHash: _3!, date: _4!, adminId: _5!, participantId: _6!, gAOrB: _7!, keyFingerprint: _8!, protocol: _9!, connection: _10!, alternativeConnections: _11!, startDate: _12!) + } + else { + return nil + } + } } enum DialogPeer: TypeConstructorDescription { @@ -15632,6 +16156,60 @@ extension Api { } } + } + enum PageRelatedArticle: TypeConstructorDescription { + case pageRelatedArticle(flags: Int32, url: String, webpageId: Int64, title: String?, description: String?, photoId: Int64?) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .pageRelatedArticle(let flags, let url, let webpageId, let title, let description, let photoId): + if boxed { + buffer.appendInt32(-242812612) + } + serializeInt32(flags, buffer: buffer, boxed: false) + serializeString(url, buffer: buffer, boxed: false) + serializeInt64(webpageId, buffer: buffer, boxed: false) + if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 1) != 0 {serializeString(description!, buffer: buffer, boxed: false)} + if Int(flags) & Int(1 << 2) != 0 {serializeInt64(photoId!, buffer: buffer, boxed: false)} + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .pageRelatedArticle(let flags, let url, let webpageId, let title, let description, let photoId): + return ("pageRelatedArticle", [("flags", flags), ("url", url), ("webpageId", webpageId), ("title", title), ("description", description), ("photoId", photoId)]) + } + } + + static func parse_pageRelatedArticle(_ reader: BufferReader) -> PageRelatedArticle? { + var _1: Int32? + _1 = reader.readInt32() + var _2: String? + _2 = parseString(reader) + var _3: Int64? + _3 = reader.readInt64() + var _4: String? + if Int(_1!) & Int(1 << 0) != 0 {_4 = parseString(reader) } + var _5: String? + if Int(_1!) & Int(1 << 1) != 0 {_5 = parseString(reader) } + var _6: Int64? + if Int(_1!) & Int(1 << 2) != 0 {_6 = reader.readInt64() } + let _c1 = _1 != nil + let _c2 = _2 != nil + let _c3 = _3 != nil + let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil + let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil + let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil + if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 { + return Api.PageRelatedArticle.pageRelatedArticle(flags: _1!, url: _2!, webpageId: _3!, title: _4, description: _5, photoId: _6) + } + else { + return nil + } + } + } enum StickerPack: TypeConstructorDescription { case stickerPack(emoticon: String, documents: [Int64]) @@ -16226,6 +16804,76 @@ extension Api { } } + } + enum PageListOrderedItem: TypeConstructorDescription { + case pageListOrderedItemText(num: String, text: Api.RichText) + case pageListOrderedItemBlocks(num: String, blocks: [Api.PageBlock]) + + func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) { + switch self { + case .pageListOrderedItemText(let num, let text): + if boxed { + buffer.appendInt32(1577484359) + } + serializeString(num, buffer: buffer, boxed: false) + text.serialize(buffer, true) + break + case .pageListOrderedItemBlocks(let num, let blocks): + if boxed { + buffer.appendInt32(-1730311882) + } + serializeString(num, buffer: buffer, boxed: false) + buffer.appendInt32(481674261) + buffer.appendInt32(Int32(blocks.count)) + for item in blocks { + item.serialize(buffer, true) + } + break + } + } + + func descriptionFields() -> (String, [(String, Any)]) { + switch self { + case .pageListOrderedItemText(let num, let text): + return ("pageListOrderedItemText", [("num", num), ("text", text)]) + case .pageListOrderedItemBlocks(let num, let blocks): + return ("pageListOrderedItemBlocks", [("num", num), ("blocks", blocks)]) + } + } + + static func parse_pageListOrderedItemText(_ reader: BufferReader) -> PageListOrderedItem? { + var _1: String? + _1 = parseString(reader) + var _2: Api.RichText? + if let signature = reader.readInt32() { + _2 = Api.parse(reader, signature: signature) as? Api.RichText + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.PageListOrderedItem.pageListOrderedItemText(num: _1!, text: _2!) + } + else { + return nil + } + } + static func parse_pageListOrderedItemBlocks(_ reader: BufferReader) -> PageListOrderedItem? { + var _1: String? + _1 = parseString(reader) + var _2: [Api.PageBlock]? + if let _ = reader.readInt32() { + _2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PageBlock.self) + } + let _c1 = _1 != nil + let _c2 = _2 != nil + if _c1 && _c2 { + return Api.PageListOrderedItem.pageListOrderedItemBlocks(num: _1!, blocks: _2!) + } + else { + return nil + } + } + } enum EncryptedChat: TypeConstructorDescription { case encryptedChatEmpty(id: Int32) diff --git a/TelegramCore/Api3.swift b/TelegramCore/Api3.swift index 1699348f01..17289fa4c6 100644 --- a/TelegramCore/Api3.swift +++ b/TelegramCore/Api3.swift @@ -3882,6 +3882,20 @@ extension Api { return result }) } + + static func getContactIDs(hash: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<[Int32]>) { + let buffer = Buffer() + buffer.appendInt32(749357634) + serializeInt32(hash, buffer: buffer, boxed: false) + return (FunctionDescription(name: "contacts.getContactIDs", parameters: [("hash", hash)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> [Int32]? in + let reader = BufferReader(buffer) + var result: [Int32]? + if let _ = reader.readInt32() { + result = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self) + } + return result + }) + } } struct help { static func getConfig() -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { @@ -4908,6 +4922,21 @@ extension Api { return result }) } + + static func confirmPasswordEmail(email: String, code: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(-558870880) + serializeString(email, buffer: buffer, boxed: false) + serializeString(code, buffer: buffer, boxed: false) + return (FunctionDescription(name: "account.confirmPasswordEmail", parameters: [("email", email), ("code", code)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in + let reader = BufferReader(buffer) + var result: Api.Bool? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.Bool + } + return result + }) + } } struct langpack { static func getDifference(fromVersion: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { @@ -4972,6 +5001,21 @@ extension Api { return result }) } + + static func getLanguage(langPack: String, langCode: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { + let buffer = Buffer() + buffer.appendInt32(1784243458) + serializeString(langPack, buffer: buffer, boxed: false) + serializeString(langCode, buffer: buffer, boxed: false) + return (FunctionDescription(name: "langpack.getLanguage", parameters: [("langPack", langPack), ("langCode", langCode)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.LangPackLanguage? in + let reader = BufferReader(buffer) + var result: Api.LangPackLanguage? + if let signature = reader.readInt32() { + result = Api.parse(reader, signature: signature) as? Api.LangPackLanguage + } + return result + }) + } } struct photos { static func updateProfilePhoto(id: Api.InputPhoto) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { diff --git a/TelegramCore/CallSessionManager.swift b/TelegramCore/CallSessionManager.swift index 84389ecf36..f453cf43da 100644 --- a/TelegramCore/CallSessionManager.swift +++ b/TelegramCore/CallSessionManager.swift @@ -557,7 +557,7 @@ private final class CallSessionManagerContext { //assertionFailure() } } - case let .phoneCall(id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connection, alternativeConnections, startDate): + case let .phoneCall(_, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connection, alternativeConnections, startDate): if let internalId = self.contextIdByStableId[id] { if let context = self.contexts[internalId] { switch context.state { @@ -820,7 +820,7 @@ private func acceptCallSession(postbox: Postbox, network: Network, stableId: Cal return .failed case .phoneCallWaiting: return .success(.waiting(config: config)) - case let .phoneCall(id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connection, alternativeConnections, startDate): + case let .phoneCall(_, id, _, _, _, _, gAOrB, keyFingerprint, callProtocol, connection, alternativeConnections, startDate): if id == stableId { switch callProtocol{ case let .phoneCallProtocol(_, _, maxLayer): diff --git a/TelegramCore/ChannelAdminEventLogs.swift b/TelegramCore/ChannelAdminEventLogs.swift index 90d298b5ba..ba7f546ee9 100644 --- a/TelegramCore/ChannelAdminEventLogs.swift +++ b/TelegramCore/ChannelAdminEventLogs.swift @@ -58,7 +58,7 @@ public enum ChannelAdminLogEventError { case generic } -public struct AdminLogEventsFlags : OptionSet { +public struct AdminLogEventsFlags: OptionSet { public var rawValue: UInt32 public init(rawValue: UInt32) { diff --git a/TelegramCore/InstantPage.swift b/TelegramCore/InstantPage.swift index 2d759ee293..73d5a494e1 100644 --- a/TelegramCore/InstantPage.swift +++ b/TelegramCore/InstantPage.swift @@ -29,6 +29,30 @@ private enum InstantPageBlockType: Int32 { case slideshow = 20 case channelBanner = 21 case audio = 22 + case kicker = 23 + case table = 24 + case details = 25 + case relatedArticles = 26 + case map = 27 +} + +private func decodeListItems(_ decoder: PostboxDecoder) -> [InstantPageListItem] { + let legacyItems: [RichText] = decoder.decodeObjectArrayWithDecoderForKey("l") + if !legacyItems.isEmpty { + var items: [InstantPageListItem] = [] + for item in legacyItems { + items.append(.text(item, nil)) + } + return items + } + return decoder.decodeObjectArrayWithDecoderForKey("ml") +} + +private func decodeCaption(_ decoder: PostboxDecoder) -> InstantPageCaption { + if let legacyCaption = decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as? RichText { + return InstantPageCaption(text: legacyCaption, credit: .empty) + } + return decoder.decodeObjectForKey("mc", decoder: { InstantPageCaption(decoder: $0) }) as! InstantPageCaption } public indirect enum InstantPageBlock: PostboxCoding, Equatable { @@ -43,18 +67,23 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { case footer(RichText) case divider case anchor(String) - case list(items: [RichText], ordered: Bool) + case list(items: [InstantPageListItem], ordered: Bool) case blockQuote(text: RichText, caption: RichText) case pullQuote(text: RichText, caption: RichText) - case image(id: MediaId, caption: RichText) - case video(id: MediaId, caption: RichText, autoplay: Bool, loop: Bool) - case audio(id: MediaId, caption: RichText) + case image(id: MediaId, caption: InstantPageCaption, url: String?, webpageId: MediaId?) + case video(id: MediaId, caption: InstantPageCaption, autoplay: Bool, loop: Bool) + case audio(id: MediaId, caption: InstantPageCaption) case cover(InstantPageBlock) - case webEmbed(url: String?, html: String?, dimensions: CGSize, caption: RichText, stretchToWidth: Bool, allowScrolling: Bool, coverId: MediaId?) - case postEmbed(url: String, webpageId: MediaId?, avatarId: MediaId?, author: String, date: Int32, blocks: [InstantPageBlock], caption: RichText) - case collage(items: [InstantPageBlock], caption: RichText) - case slideshow(items: [InstantPageBlock], caption: RichText) + case webEmbed(url: String?, html: String?, dimensions: CGSize?, caption: InstantPageCaption, stretchToWidth: Bool, allowScrolling: Bool, coverId: MediaId?) + case postEmbed(url: String, webpageId: MediaId?, avatarId: MediaId?, author: String, date: Int32, blocks: [InstantPageBlock], caption: InstantPageCaption) + case collage(items: [InstantPageBlock], caption: InstantPageCaption) + case slideshow(items: [InstantPageBlock], caption: InstantPageCaption) case channelBanner(TelegramChannel?) + case kicker(RichText) + case table(title: RichText, rows: [InstantPageTableRow], bordered: Bool, striped: Bool) + case details(title: RichText, blocks: [InstantPageBlock], open: Bool) + case relatedArticles(title: RichText, articles: [InstantPageRelatedArticle]) + case map(latitude: Double, longitude: Double, zoom: Int32, dimensions: CGSize, caption: InstantPageCaption) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("r", orElse: 0) { @@ -81,15 +110,19 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { case InstantPageBlockType.anchor.rawValue: self = .anchor(decoder.decodeStringForKey("s", orElse: "")) case InstantPageBlockType.list.rawValue: - self = .list(items: decoder.decodeObjectArrayWithDecoderForKey("l"), ordered: decoder.decodeOptionalInt32ForKey("o") != 0) + self = .list(items: decodeListItems(decoder), ordered: decoder.decodeOptionalInt32ForKey("o") != 0) case InstantPageBlockType.blockQuote.rawValue: self = .blockQuote(text: decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText, caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText) case InstantPageBlockType.pullQuote.rawValue: self = .pullQuote(text: decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText, caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText) case InstantPageBlockType.image.rawValue: - self = .image(id: MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0)), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText) + var webpageId: MediaId? + if let webpageIdNamespace = decoder.decodeOptionalInt32ForKey("wi.n"), let webpageIdId = decoder.decodeOptionalInt64ForKey("wi.i") { + webpageId = MediaId(namespace: webpageIdNamespace, id: webpageIdId) + } + self = .image(id: MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0)), caption: decodeCaption(decoder), url: decoder.decodeOptionalStringForKey("u"), webpageId: webpageId) case InstantPageBlockType.video.rawValue: - self = .video(id: MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0)), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText, autoplay: decoder.decodeInt32ForKey("ap", orElse: 0) != 0, loop: decoder.decodeInt32ForKey("lo", orElse: 0) != 0) + self = .video(id: MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0)), caption: decodeCaption(decoder), autoplay: decoder.decodeInt32ForKey("ap", orElse: 0) != 0, loop: decoder.decodeInt32ForKey("lo", orElse: 0) != 0) case InstantPageBlockType.cover.rawValue: self = .cover(decoder.decodeObjectForKey("c", decoder: { InstantPageBlock(decoder: $0) }) as! InstantPageBlock) case InstantPageBlockType.webEmbed.rawValue: @@ -97,7 +130,11 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { if let coverIdNamespace = decoder.decodeOptionalInt32ForKey("ci.n"), let coverIdId = decoder.decodeOptionalInt64ForKey("ci.i") { coverId = MediaId(namespace: coverIdNamespace, id: coverIdId) } - self = .webEmbed(url: decoder.decodeOptionalStringForKey("u"), html: decoder.decodeOptionalStringForKey("h"), dimensions: CGSize(width: CGFloat(decoder.decodeInt32ForKey("sw", orElse: 0)), height: CGFloat(decoder.decodeInt32ForKey("sh", orElse: 0))), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText, stretchToWidth: decoder.decodeInt32ForKey("st", orElse: 0) != 0, allowScrolling: decoder.decodeInt32ForKey("as", orElse: 0) != 0, coverId: coverId) + var dimensions: CGSize? + if let width = decoder.decodeOptionalInt32ForKey("sw"), let height = decoder.decodeOptionalInt32ForKey("sh") { + dimensions = CGSize(width: CGFloat(width), height: CGFloat(height)) + } + self = .webEmbed(url: decoder.decodeOptionalStringForKey("u"), html: decoder.decodeOptionalStringForKey("h"), dimensions: dimensions, caption: decodeCaption(decoder), stretchToWidth: decoder.decodeInt32ForKey("st", orElse: 0) != 0, allowScrolling: decoder.decodeInt32ForKey("as", orElse: 0) != 0, coverId: coverId) case InstantPageBlockType.postEmbed.rawValue: var avatarId: MediaId? let avatarIdNamespace: Int32? = decoder.decodeOptionalInt32ForKey("av.n") @@ -105,15 +142,25 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { if let avatarIdNamespace = avatarIdNamespace, let avatarIdId = avatarIdId { avatarId = MediaId(namespace: avatarIdNamespace, id: avatarIdId) } - self = .postEmbed(url: decoder.decodeStringForKey("u", orElse: ""), webpageId: MediaId(namespace: decoder.decodeInt32ForKey("w.n", orElse: 0), id: decoder.decodeInt64ForKey("w.i", orElse: 0)), avatarId: avatarId, author: decoder.decodeStringForKey("a", orElse: ""), date: decoder.decodeInt32ForKey("d", orElse: 0), blocks: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText) + self = .postEmbed(url: decoder.decodeStringForKey("u", orElse: ""), webpageId: MediaId(namespace: decoder.decodeInt32ForKey("w.n", orElse: 0), id: decoder.decodeInt64ForKey("w.i", orElse: 0)), avatarId: avatarId, author: decoder.decodeStringForKey("a", orElse: ""), date: decoder.decodeInt32ForKey("d", orElse: 0), blocks: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decodeCaption(decoder)) case InstantPageBlockType.collage.rawValue: - self = .collage(items: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText) + self = .collage(items: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decodeCaption(decoder)) case InstantPageBlockType.slideshow.rawValue: - self = .slideshow(items: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText) + self = .slideshow(items: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decodeCaption(decoder)) case InstantPageBlockType.channelBanner.rawValue: self = .channelBanner(decoder.decodeObjectForKey("c") as? TelegramChannel) case InstantPageBlockType.audio.rawValue: - self = .audio(id: MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0)), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText) + self = .audio(id: MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0)), caption: decodeCaption(decoder)) + case InstantPageBlockType.kicker.rawValue: + self = .kicker(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) + case InstantPageBlockType.table.rawValue: + self = .table(title: decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText, rows: decoder.decodeObjectArrayWithDecoderForKey("r"), bordered: decoder.decodeInt32ForKey("b", orElse: 0) != 0, striped: decoder.decodeInt32ForKey("s", orElse: 0) != 0) + case InstantPageBlockType.details.rawValue: + self = .details(title: decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText, blocks: decoder.decodeObjectArrayWithDecoderForKey("b"), open: decoder.decodeInt32ForKey("o", orElse: 0) != 0) + case InstantPageBlockType.relatedArticles.rawValue: + self = .relatedArticles(title: decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText, articles: decoder.decodeObjectArrayWithDecoderForKey("a")) + case InstantPageBlockType.map.rawValue: + self = .map(latitude: decoder.decodeDoubleForKey("lat", orElse: 0.0), longitude: decoder.decodeDoubleForKey("lon", orElse: 0.0), zoom: decoder.decodeInt32ForKey("z", orElse: 0), dimensions: CGSize(width: CGFloat(decoder.decodeInt32ForKey("sw", orElse: 0)), height: CGFloat(decoder.decodeInt32ForKey("sh", orElse: 0))), caption: decodeCaption(decoder)) default: self = .unsupported } @@ -155,7 +202,7 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { encoder.encodeString(anchor, forKey: "s") case let .list(items, ordered): encoder.encodeInt32(InstantPageBlockType.list.rawValue, forKey: "r") - encoder.encodeObjectArray(items, forKey: "l") + encoder.encodeObjectArray(items, forKey: "ml") encoder.encodeInt32(ordered ? 1 : 0, forKey: "o") case let .blockQuote(text, caption): encoder.encodeInt32(InstantPageBlockType.blockQuote.rawValue, forKey: "r") @@ -165,16 +212,28 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { encoder.encodeInt32(InstantPageBlockType.pullQuote.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") encoder.encodeObject(caption, forKey: "c") - case let .image(id, caption): + case let .image(id, caption, url, webpageId): encoder.encodeInt32(InstantPageBlockType.image.rawValue, forKey: "r") encoder.encodeInt32(id.namespace, forKey: "i.n") encoder.encodeInt64(id.id, forKey: "i.i") - encoder.encodeObject(caption, forKey: "c") + encoder.encodeObject(caption, forKey: "mc") + if let url = url { + encoder.encodeString(url, forKey: "u") + } else { + encoder.encodeNil(forKey: "u") + } + if let webpageId = webpageId { + encoder.encodeInt32(webpageId.namespace, forKey: "wi.n") + encoder.encodeInt64(webpageId.id, forKey: "wi.i") + } else { + encoder.encodeNil(forKey: "wi.n") + encoder.encodeNil(forKey: "wi.i") + } case let .video(id, caption, autoplay, loop): encoder.encodeInt32(InstantPageBlockType.video.rawValue, forKey: "r") encoder.encodeInt32(id.namespace, forKey: "i.n") encoder.encodeInt64(id.id, forKey: "i.i") - encoder.encodeObject(caption, forKey: "c") + encoder.encodeObject(caption, forKey: "mc") encoder.encodeInt32(autoplay ? 1 : 0, forKey: "ap") encoder.encodeInt32(loop ? 1 : 0, forKey: "lo") case let .cover(block): @@ -199,9 +258,14 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { } else { encoder.encodeNil(forKey: "h") } - encoder.encodeInt32(Int32(dimensions.width), forKey: "sw") - encoder.encodeInt32(Int32(dimensions.height), forKey: "sh") - encoder.encodeObject(caption, forKey: "c") + if let dimensions = dimensions { + encoder.encodeInt32(Int32(dimensions.width), forKey: "sw") + encoder.encodeInt32(Int32(dimensions.height), forKey: "sh") + } else { + encoder.encodeNil(forKey: "sw") + encoder.encodeNil(forKey: "sh") + } + encoder.encodeObject(caption, forKey: "mc") encoder.encodeInt32(stretchToWidth ? 1 : 0, forKey: "st") encoder.encodeInt32(allowScrolling ? 1 : 0, forKey: "as") case let .postEmbed(url, webpageId, avatarId, author, date, blocks, caption): @@ -224,15 +288,15 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { encoder.encodeString(author, forKey: "a") encoder.encodeInt32(date, forKey: "d") encoder.encodeObjectArray(blocks, forKey: "b") - encoder.encodeObject(caption, forKey: "c") + encoder.encodeObject(caption, forKey: "mc") case let .collage(items, caption): encoder.encodeInt32(InstantPageBlockType.collage.rawValue, forKey: "r") encoder.encodeObjectArray(items, forKey: "b") - encoder.encodeObject(caption, forKey: "c") + encoder.encodeObject(caption, forKey: "mc") case let .slideshow(items, caption): encoder.encodeInt32(InstantPageBlockType.slideshow.rawValue, forKey: "r") encoder.encodeObjectArray(items, forKey: "b") - encoder.encodeObject(caption, forKey: "c") + encoder.encodeObject(caption, forKey: "mc") case let .channelBanner(channel): encoder.encodeInt32(InstantPageBlockType.channelBanner.rawValue, forKey: "r") if let channel = channel { @@ -244,7 +308,33 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { encoder.encodeInt32(InstantPageBlockType.audio.rawValue, forKey: "r") encoder.encodeInt32(id.namespace, forKey: "i.n") encoder.encodeInt64(id.id, forKey: "i.i") - encoder.encodeObject(caption, forKey: "c") + encoder.encodeObject(caption, forKey: "mc") + case let .kicker(text): + encoder.encodeInt32(InstantPageBlockType.kicker.rawValue, forKey: "r") + encoder.encodeObject(text, forKey: "t") + case let .table(title, rows, bordered, striped): + encoder.encodeInt32(InstantPageBlockType.table.rawValue, forKey: "r") + encoder.encodeObject(title, forKey: "t") + encoder.encodeObjectArray(rows, forKey: "r") + encoder.encodeInt32(bordered ? 1 : 0, forKey: "b") + encoder.encodeInt32(striped ? 1 : 0, forKey: "s") + case let .details(title, blocks, open): + encoder.encodeInt32(InstantPageBlockType.details.rawValue, forKey: "r") + encoder.encodeObject(title, forKey: "t") + encoder.encodeObjectArray(blocks, forKey: "b") + encoder.encodeInt32(open ? 1 : 0, forKey: "o") + case let .relatedArticles(title, articles): + encoder.encodeInt32(InstantPageBlockType.relatedArticles.rawValue, forKey: "r") + encoder.encodeObject(title, forKey: "t") + encoder.encodeObjectArray(articles, forKey: "a") + case let .map(latitude, longitude, zoom, dimensions, caption): + encoder.encodeInt32(InstantPageBlockType.map.rawValue, forKey: "r") + encoder.encodeDouble(latitude, forKey: "lat") + encoder.encodeDouble(longitude, forKey: "lon") + encoder.encodeInt32(zoom, forKey: "z") + encoder.encodeInt32(Int32(dimensions.width), forKey: "sw") + encoder.encodeInt32(Int32(dimensions.height), forKey: "sh") + encoder.encodeObject(caption, forKey: "mc") } } @@ -334,8 +424,8 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { } else { return false } - case let .image(id, caption): - if case .image(id, caption) = rhs { + case let .image(lhsId, lhsCaption, lhsUrl, lhsWebpageId): + if case let .image(rhsId, rhsCaption, rhsUrl, rhsWebpageId) = rhs, lhsId == rhsId, lhsCaption == rhsCaption, lhsUrl == rhsUrl, lhsWebpageId == rhsWebpageId { return true } else { return false @@ -395,10 +485,307 @@ public indirect enum InstantPageBlock: PostboxCoding, Equatable { } else { return false } + case let .kicker(text): + if case .kicker(text) = rhs { + return true + } else { + return false + } + case let .table(lhsTitle, lhsRows, lhsBordered, lhsStriped): + if case let .table(rhsTitle, rhsRows, rhsBordered, rhsStriped) = rhs, lhsTitle == rhsTitle, lhsRows == rhsRows, lhsBordered == rhsBordered, lhsStriped == rhsStriped { + return true + } else { + return false + } + case let .details(lhsTitle, lhsBlocks, lhsOpen): + if case let .details(rhsTitle, rhsBlocks, rhsOpen) = rhs, lhsTitle == rhsTitle, lhsBlocks == rhsBlocks, lhsOpen == rhsOpen { + return true + } else { + return false + } + case let .relatedArticles(lhsTitle, lhsArticles): + if case let .relatedArticles(rhsTitle, rhsArticles) = rhs, lhsTitle == rhsTitle, lhsArticles == rhsArticles { + return true + } else { + return false + } + case let .map(latitude, longitude, zoom, dimensions, caption): + if case .map(latitude, longitude, zoom, dimensions, caption) = rhs { + return true + } else { + return false + } } } } +public final class InstantPageCaption: PostboxCoding, Equatable { + public let text: RichText + public let credit: RichText + + init(text: RichText, credit: RichText) { + self.text = text + self.credit = credit + } + + public init(decoder: PostboxDecoder) { + self.text = decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText + self.credit = decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeObject(self.text, forKey: "t") + encoder.encodeObject(self.credit, forKey: "c") + } + + public static func ==(lhs: InstantPageCaption, rhs: InstantPageCaption) -> Bool { + if lhs.text != rhs.text { + return false + } + if lhs.credit != rhs.credit { + return false + } + return true + } +} + +private enum InstantPageListItemType: Int32 { + case unknown = 0 + case text = 1 + case blocks = 2 +} + +public indirect enum InstantPageListItem: PostboxCoding, Equatable { + case unknown + case text(RichText, String?) + case blocks([InstantPageBlock], String?) + + public init(decoder: PostboxDecoder) { + switch decoder.decodeInt32ForKey("r", orElse: 0) { + case InstantPageListItemType.text.rawValue: + self = .text(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText, decoder.decodeOptionalStringForKey("n")) + case InstantPageListItemType.blocks.rawValue: + self = .blocks(decoder.decodeObjectArrayWithDecoderForKey("b"), decoder.decodeOptionalStringForKey("n")) + default: + self = .unknown + } + } + + public func encode(_ encoder: PostboxEncoder) { + switch self { + case let .text(text, num): + encoder.encodeInt32(InstantPageListItemType.text.rawValue, forKey: "r") + encoder.encodeObject(text, forKey: "t") + if let num = num { + encoder.encodeString(num, forKey: "n") + } else { + encoder.encodeNil(forKey: "n") + } + case let .blocks(blocks, num): + encoder.encodeInt32(InstantPageListItemType.blocks.rawValue, forKey: "r") + encoder.encodeObjectArray(blocks, forKey: "b") + if let num = num { + encoder.encodeString(num, forKey: "n") + } else { + encoder.encodeNil(forKey: "n") + } + default: + break + } + } + + public static func ==(lhs: InstantPageListItem, rhs: InstantPageListItem) -> Bool { + switch lhs { + case .unknown: + if case .unknown = rhs { + return true + } else { + return false + } + case let .text(lhsText, lhsNum): + if case let .text(rhsText, rhsNum) = rhs, lhsText == rhsText, lhsNum == rhsNum { + return true + } else { + return false + } + case let .blocks(lhsBlocks, lhsNum): + if case let .blocks(rhsBlocks, rhsNum) = rhs, lhsBlocks == rhsBlocks, lhsNum == rhsNum { + return true + } else { + return false + } + } + } +} + +public enum TableHorizontalAlignment: Int32 { + case left = 0 + case center = 1 + case right = 2 +} + +public enum TableVerticalAlignment: Int32 { + case top = 0 + case middle = 1 + case bottom = 2 +} + +public final class InstantPageTableCell: PostboxCoding, Equatable { + public let text: RichText? + public let header: Bool + public let alignment: TableHorizontalAlignment + public let verticalAlignment: TableVerticalAlignment + public let colspan: Int32 + public let rowspan: Int32 + + public init(text: RichText?, header: Bool, alignment: TableHorizontalAlignment, verticalAlignment: TableVerticalAlignment, colspan: Int32, rowspan: Int32) { + self.text = text + self.header = header + self.alignment = alignment + self.verticalAlignment = verticalAlignment + self.colspan = colspan + self.rowspan = rowspan + } + + public init(decoder: PostboxDecoder) { + self.text = decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as? RichText + self.header = decoder.decodeInt32ForKey("h", orElse: 0) != 0 + self.alignment = TableHorizontalAlignment(rawValue: decoder.decodeInt32ForKey("ha", orElse: 0))! + self.verticalAlignment = TableVerticalAlignment(rawValue: decoder.decodeInt32ForKey("va", orElse: 0))! + self.colspan = decoder.decodeInt32ForKey("sc", orElse: 0) + self.rowspan = decoder.decodeInt32ForKey("sr", orElse: 0) + } + + public func encode(_ encoder: PostboxEncoder) { + if let text = self.text { + encoder.encodeObject(text, forKey: "t") + } else { + encoder.encodeNil(forKey: "t") + } + encoder.encodeInt32(self.header ? 1 : 0, forKey: "h") + encoder.encodeInt32(self.alignment.rawValue, forKey: "ha") + encoder.encodeInt32(self.verticalAlignment.rawValue, forKey: "va") + encoder.encodeInt32(self.colspan, forKey: "sc") + encoder.encodeInt32(self.rowspan, forKey: "sr") + } + + public static func ==(lhs: InstantPageTableCell, rhs: InstantPageTableCell) -> Bool { + if lhs.text != rhs.text { + return false + } + if lhs.header != rhs.header { + return false + } + if lhs.alignment != rhs.alignment { + return false + } + if lhs.verticalAlignment != rhs.verticalAlignment { + return false + } + if lhs.colspan != rhs.colspan { + return false + } + if lhs.rowspan != rhs.rowspan { + return false + } + return true + } +} + +public final class InstantPageTableRow: PostboxCoding, Equatable { + public let cells: [InstantPageTableCell] + + public init(cells: [InstantPageTableCell]) { + self.cells = cells + } + + public init(decoder: PostboxDecoder) { + self.cells = decoder.decodeObjectArrayWithDecoderForKey("c") + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeObjectArray(self.cells, forKey: "c") + } + + public static func ==(lhs: InstantPageTableRow, rhs: InstantPageTableRow) -> Bool { + return lhs.cells == rhs.cells + } +} + +public final class InstantPageRelatedArticle: PostboxCoding, Equatable { + public let url: String + public let webpageId: MediaId + public let title: String? + public let description: String? + public let photoId: MediaId? + + init(url: String, webpageId: MediaId, title: String?, description: String?, photoId: MediaId?) { + self.url = url + self.webpageId = webpageId + self.title = title + self.description = description + self.photoId = photoId + } + + public init(decoder: PostboxDecoder) { + self.url = decoder.decodeStringForKey("u", orElse: "") + let webpageIdNamespace = decoder.decodeInt32ForKey("w.n", orElse: 0) + let webpageIdId = decoder.decodeInt64ForKey("w.i", orElse: 0) + self.webpageId = MediaId(namespace: webpageIdNamespace, id: webpageIdId) + + self.title = decoder.decodeOptionalStringForKey("t") + self.description = decoder.decodeOptionalStringForKey("d") + + var photoId: MediaId? + if let photoIdNamespace = decoder.decodeOptionalInt32ForKey("p.n"), let photoIdId = decoder.decodeOptionalInt64ForKey("p.i") { + photoId = MediaId(namespace: photoIdNamespace, id: photoIdId) + } + self.photoId = photoId + } + + public func encode(_ encoder: PostboxEncoder) { + encoder.encodeString(self.url, forKey: "u") + encoder.encodeInt32(self.webpageId.namespace, forKey: "w.n") + encoder.encodeInt64(self.webpageId.id, forKey: "w.i") + if let title = self.title { + encoder.encodeString(title, forKey: "t") + } else { + encoder.encodeNil(forKey: "t") + } + if let description = self.description { + encoder.encodeString(description, forKey: "d") + } else { + encoder.encodeNil(forKey: "d") + } + if let photoId = photoId { + encoder.encodeInt32(photoId.namespace, forKey: "p.n") + encoder.encodeInt64(photoId.id, forKey: "p.i") + } else { + encoder.encodeNil(forKey: "p.n") + encoder.encodeNil(forKey: "p.i") + } + } + + public static func ==(lhs: InstantPageRelatedArticle, rhs: InstantPageRelatedArticle) -> Bool { + if lhs.url != rhs.url { + return false + } + if lhs.webpageId != rhs.webpageId { + return false + } + if lhs.title != rhs.title { + return false + } + if lhs.description != rhs.description { + return false + } + if lhs.photoId != rhs.photoId { + return false + } + return true + } +} + private final class MediaDictionary: PostboxCoding { let dict: [MediaId: Media] @@ -438,30 +825,33 @@ public final class InstantPage: PostboxCoding, Equatable { public let blocks: [InstantPageBlock] public let media: [MediaId: Media] public let isComplete: Bool + public let rtl: Bool - init(blocks: [InstantPageBlock], media: [MediaId: Media], isComplete: Bool) { + init(blocks: [InstantPageBlock], media: [MediaId: Media], isComplete: Bool, rtl: Bool) { self.blocks = blocks self.media = media self.isComplete = isComplete + self.rtl = rtl } public init(decoder: PostboxDecoder) { self.blocks = decoder.decodeObjectArrayWithDecoderForKey("b") self.media = MediaDictionary(decoder: decoder).dict self.isComplete = decoder.decodeInt32ForKey("c", orElse: 0) != 0 + self.rtl = decoder.decodeInt32ForKey("r", orElse: 0) != 0 } public func encode(_ encoder: PostboxEncoder) { encoder.encodeObjectArray(self.blocks, forKey: "b") MediaDictionary(dict: self.media).encode(encoder) encoder.encodeInt32(self.isComplete ? 1 : 0, forKey: "c") + encoder.encodeInt32(self.rtl ? 1 : 0, forKey: "r") } public static func ==(lhs: InstantPage, rhs: InstantPage) -> Bool { if lhs.blocks != rhs.blocks { return false } - if lhs.media.count != rhs.media.count { return false } else { @@ -475,14 +865,101 @@ public final class InstantPage: PostboxCoding, Equatable { } } } - if lhs.isComplete != rhs.isComplete { return false } + if lhs.rtl != rhs.rtl { + return false + } return true } } +extension InstantPageCaption { + convenience init(apiCaption: Api.PageCaption) { + switch apiCaption { + case let .pageCaption(text, credit): + self.init(text: RichText(apiText: text), credit: RichText(apiText: credit)) + } + } +} + +public extension InstantPageListItem { + public var num: String? { + switch self { + case let .text(_, num): + return num + case let .blocks(_, num): + return num + default: + return nil + } + } +} + +extension InstantPageListItem { + init(apiListItem: Api.PageListItem) { + switch apiListItem { + case let .pageListItemText(text): + self = .text(RichText(apiText: text), nil) + case let .pageListItemBlocks(blocks): + self = .blocks(blocks.map({ InstantPageBlock(apiBlock: $0) }), nil) + } + } + + init(apiListOrderedItem: Api.PageListOrderedItem) { + switch apiListOrderedItem { + case let .pageListOrderedItemText(num, text): + self = .text(RichText(apiText: text), num) + case let .pageListOrderedItemBlocks(num, blocks): + self = .blocks(blocks.map({ InstantPageBlock(apiBlock: $0) }), num) + } + } +} + +extension InstantPageTableCell { + convenience init(apiTableCell: Api.PageTableCell) { + switch apiTableCell { + case let .pageTableCell(flags, text, colspan, rowspan): + var alignment = TableHorizontalAlignment.left + if (flags & (1 << 3)) != 0 { + alignment = .center + } else if (flags & (1 << 4)) != 0 { + alignment = .right + } + var verticalAlignment = TableVerticalAlignment.top + if (flags & (1 << 5)) != 0 { + verticalAlignment = .middle + } else if (flags & (1 << 6)) != 0 { + verticalAlignment = .bottom + } + self.init(text: text != nil ? RichText(apiText: text!) : nil, header: (flags & (1 << 0)) != 0, alignment: alignment, verticalAlignment: verticalAlignment, colspan: colspan ?? 0, rowspan: rowspan ?? 0) + } + } +} + +extension InstantPageTableRow { + convenience init(apiTableRow: Api.PageTableRow) { + switch apiTableRow { + case let .pageTableRow(cells): + self.init(cells: cells.map({ InstantPageTableCell(apiTableCell: $0) })) + } + } +} + +extension InstantPageRelatedArticle { + convenience init(apiRelatedArticle: Api.PageRelatedArticle) { + switch apiRelatedArticle { + case let .pageRelatedArticle(flags, url, webpageId, title, description, photoId): + var posterPhotoId: MediaId? + if let photoId = photoId { + posterPhotoId = MediaId(namespace: Namespaces.Media.CloudImage, id: photoId) + } + self.init(url: url, webpageId: MediaId(namespace: Namespaces.Media.CloudWebpage, id: webpageId), title: title, description: description, photoId: posterPhotoId) + } + } +} + extension InstantPageBlock { init(apiBlock: Api.PageBlock) { switch apiBlock { @@ -508,30 +985,51 @@ extension InstantPageBlock { self = .divider case let .pageBlockAnchor(name): self = .anchor(name) - case let .pageBlockList(ordered, items): - self = .list(items: items.map({ RichText(apiText: $0) }), ordered: ordered == .boolTrue) case let .pageBlockBlockquote(text, caption): self = .blockQuote(text: RichText(apiText: text), caption: RichText(apiText: caption)) case let .pageBlockPullquote(text, caption): self = .pullQuote(text: RichText(apiText: text), caption: RichText(apiText: caption)) - case let .pageBlockPhoto(photoId, caption): - self = .image(id: MediaId(namespace: Namespaces.Media.CloudImage, id: photoId), caption: RichText(apiText: caption)) + case let .pageBlockPhoto(_, photoId, caption, url, webpageId): + self = .image(id: MediaId(namespace: Namespaces.Media.CloudImage, id: photoId), caption: InstantPageCaption(apiCaption: caption), url: url, webpageId: webpageId != nil ? MediaId(namespace: Namespaces.Media.CloudWebpage, id: webpageId!) : nil) case let .pageBlockVideo(flags, videoId, caption): - self = .video(id: MediaId(namespace: Namespaces.Media.CloudFile, id: videoId), caption: RichText(apiText: caption), autoplay: (flags & (1 << 0)) != 0, loop: (flags & (1 << 1)) != 0) + self = .video(id: MediaId(namespace: Namespaces.Media.CloudFile, id: videoId), caption: InstantPageCaption(apiCaption: caption), autoplay: (flags & (1 << 0)) != 0, loop: (flags & (1 << 1)) != 0) case let .pageBlockCover(cover): self = .cover(InstantPageBlock(apiBlock: cover)) case let .pageBlockEmbed(flags, url, html, posterPhotoId, w, h, caption): - self = .webEmbed(url: url, html: html, dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), caption: RichText(apiText: caption), stretchToWidth: (flags & (1 << 0)) != 0, allowScrolling: (flags & (1 << 3)) != 0, coverId: posterPhotoId.flatMap { MediaId(namespace: Namespaces.Media.CloudImage, id: $0) }) + var dimensions: CGSize? + if let w = w, let h = h { + dimensions = CGSize(width: CGFloat(w), height: CGFloat(h)) + } + self = .webEmbed(url: url, html: html, dimensions: dimensions, caption: InstantPageCaption(apiCaption: caption), stretchToWidth: (flags & (1 << 0)) != 0, allowScrolling: (flags & (1 << 3)) != 0, coverId: posterPhotoId.flatMap { MediaId(namespace: Namespaces.Media.CloudImage, id: $0) }) case let .pageBlockEmbedPost(url, webpageId, authorPhotoId, author, date, blocks, caption): - self = .postEmbed(url: url, webpageId: webpageId == 0 ? nil : MediaId(namespace: Namespaces.Media.CloudWebpage, id: webpageId), avatarId: authorPhotoId == 0 ? nil : MediaId(namespace: Namespaces.Media.CloudImage, id: authorPhotoId), author: author, date: date, blocks: blocks.map({ InstantPageBlock(apiBlock: $0) }), caption: RichText(apiText: caption)) + self = .postEmbed(url: url, webpageId: webpageId == 0 ? nil : MediaId(namespace: Namespaces.Media.CloudWebpage, id: webpageId), avatarId: authorPhotoId == 0 ? nil : MediaId(namespace: Namespaces.Media.CloudImage, id: authorPhotoId), author: author, date: date, blocks: blocks.map({ InstantPageBlock(apiBlock: $0) }), caption: InstantPageCaption(apiCaption: caption)) case let .pageBlockCollage(items, caption): - self = .collage(items: items.map({ InstantPageBlock(apiBlock: $0) }), caption: RichText(apiText: caption)) + self = .collage(items: items.map({ InstantPageBlock(apiBlock: $0) }), caption: InstantPageCaption(apiCaption: caption)) case let .pageBlockSlideshow(items, caption): - self = .slideshow(items: items.map({ InstantPageBlock(apiBlock: $0) }), caption: RichText(apiText: caption)) + self = .slideshow(items: items.map({ InstantPageBlock(apiBlock: $0) }), caption: InstantPageCaption(apiCaption: caption)) case let .pageBlockChannel(channel: apiChat): self = .channelBanner(parseTelegramGroupOrChannel(chat: apiChat) as? TelegramChannel) case let .pageBlockAudio(audioId, caption): - self = .audio(id: MediaId(namespace: Namespaces.Media.CloudFile, id: audioId), caption: RichText(apiText: caption)) + self = .audio(id: MediaId(namespace: Namespaces.Media.CloudFile, id: audioId), caption: InstantPageCaption(apiCaption: caption)) + case let .pageBlockKicker(text): + self = .kicker(RichText(apiText: text)) + case let .pageBlockTable(flags, title, rows): + self = .table(title: RichText(apiText: title), rows: rows.map({ InstantPageTableRow(apiTableRow: $0) }), bordered: (flags & (1 << 0)) != 0, striped: (flags & (1 << 1)) != 0) + case let .pageBlockList(items): + self = .list(items: items.map({ InstantPageListItem(apiListItem: $0) }), ordered: false) + case let .pageBlockOrderedList(items): + self = .list(items: items.map({ InstantPageListItem(apiListOrderedItem: $0) }), ordered: true) + case let .pageBlockDetails(flags, blocks, title): + self = .details(title: RichText(apiText: title), blocks: blocks.map({ InstantPageBlock(apiBlock: $0) }), open: (flags & (1 << 0)) != 0) + case let .pageBlockRelatedArticles(title, articles): + self = .relatedArticles(title: RichText(apiText: title), articles: articles.map({ InstantPageRelatedArticle(apiRelatedArticle: $0) })) + case let .pageBlockMap(geo, zoom, w, h, caption): + switch geo { + case let .geoPoint(long, lat, _): + self = .map(latitude: lat, longitude: long, zoom: zoom, dimensions: CGSize(width: CGFloat(w), height: CGFloat(h)), caption: InstantPageCaption(apiCaption: caption)) + default: + self = .unsupported + } } } } @@ -542,17 +1040,14 @@ extension InstantPage { let photos: [Api.Photo] let files: [Api.Document] let isComplete: Bool + let rtl: Bool switch apiPage { - case let .pageFull(apiBlocks, apiPhotos, apiVideos): + case let .page(flags, apiBlocks, apiPhotos, apiVideos): blocks = apiBlocks photos = apiPhotos files = apiVideos - isComplete = true - case let .pagePart(apiBlocks, apiPhotos, apiVideos): - blocks = apiBlocks - photos = apiPhotos - files = apiVideos - isComplete = false + isComplete = (flags & (1 << 0)) == 0 + rtl = (flags & (1 << 1)) != 0 } var media: [MediaId: Media] = [:] for photo in photos { @@ -565,6 +1060,6 @@ extension InstantPage { media[id] = file } } - self.init(blocks: blocks.map({ InstantPageBlock(apiBlock: $0) }), media: media, isComplete: isComplete) + self.init(blocks: blocks.map({ InstantPageBlock(apiBlock: $0) }), media: media, isComplete: isComplete, rtl: rtl) } } diff --git a/TelegramCore/LocalizationInfo.swift b/TelegramCore/LocalizationInfo.swift index a90aa2008f..fd597e0407 100644 --- a/TelegramCore/LocalizationInfo.swift +++ b/TelegramCore/LocalizationInfo.swift @@ -1,31 +1,60 @@ import Foundation #if os(macOS) - import PostboxMac +import PostboxMac #else - import Postbox +import Postbox #endif public final class LocalizationInfo: PostboxCoding { public let languageCode: String + public let nativeLanguageCode: String? public let title: String public let localizedTitle: String + public let isOfficial: Bool + public let totalStringCount: Int32 + public let translatedStringCount: Int32 - public init(languageCode: String, title: String, localizedTitle: String) { + public init(languageCode: String, nativeLanguageCode: String?, title: String, localizedTitle: String, isOfficial: Bool, totalStringCount: Int32, translatedStringCount: Int32) { self.languageCode = languageCode + self.nativeLanguageCode = nativeLanguageCode self.title = title self.localizedTitle = localizedTitle + self.isOfficial = isOfficial + self.totalStringCount = totalStringCount + self.translatedStringCount = translatedStringCount } public init(decoder: PostboxDecoder) { self.languageCode = decoder.decodeStringForKey("lc", orElse: "") + self.nativeLanguageCode = decoder.decodeOptionalStringForKey("nlc") self.title = decoder.decodeStringForKey("t", orElse: "") self.localizedTitle = decoder.decodeStringForKey("lt", orElse: "") + self.isOfficial = decoder.decodeInt32ForKey("of", orElse: 0) != 0 + self.totalStringCount = decoder.decodeInt32ForKey("tsc", orElse: 0) + self.translatedStringCount = decoder.decodeInt32ForKey("lsc", orElse: 0) } public func encode(_ encoder: PostboxEncoder) { encoder.encodeString(self.languageCode, forKey: "lc") + if let nativeLanguageCode = self.nativeLanguageCode { + encoder.encodeString(nativeLanguageCode, forKey: "nlc") + } else { + encoder.encodeNil(forKey: "nlc") + } encoder.encodeString(self.title, forKey: "t") encoder.encodeString(self.localizedTitle, forKey: "lt") + encoder.encodeInt32(self.isOfficial ? 1 : 0, forKey: "of") + encoder.encodeInt32(self.totalStringCount, forKey: "tsc") + encoder.encodeInt32(self.translatedStringCount, forKey: "lsc") + } +} + +extension LocalizationInfo { + convenience init(apiLanguage: Api.LangPackLanguage) { + switch apiLanguage { + case let .langPackLanguage(language): + self.init(languageCode: language.langCode, nativeLanguageCode: language.baseLangCode, title: language.name, localizedTitle: language.nativeName, isOfficial: (language.flags & (1 << 0)) != 0, totalStringCount: language.stringsCount, translatedStringCount: language.translatedCount) + } } } diff --git a/TelegramCore/Localizations.swift b/TelegramCore/Localizations.swift index 83a7c4d9d1..1ee45e72f9 100644 --- a/TelegramCore/Localizations.swift +++ b/TelegramCore/Localizations.swift @@ -1,10 +1,10 @@ import Foundation #if os(macOS) - import PostboxMac - import SwiftSignalKitMac +import PostboxMac +import SwiftSignalKitMac #else - import Postbox - import SwiftSignalKit +import Postbox +import SwiftSignalKit #endif public func currentlySuggestedLocalization(network: Network, extractKeys: [String]) -> Signal { @@ -12,14 +12,14 @@ public func currentlySuggestedLocalization(network: Network, extractKeys: [Strin |> retryRequest |> mapToSignal { result -> Signal in switch result { - case let .config(config): - if let suggestedLangCode = config.suggestedLangCode { - return suggestedLocalizationInfo(network: network, languageCode: suggestedLangCode, extractKeys: extractKeys) |> map(Optional.init) - } else { - return .single(nil) - } + case let .config(config): + if let suggestedLangCode = config.suggestedLangCode { + return suggestedLocalizationInfo(network: network, languageCode: suggestedLangCode, extractKeys: extractKeys) |> map(Optional.init) + } else { + return .single(nil) + } } - } + } } public func suggestedLocalizationInfo(network: Network, languageCode: String, extractKeys: [String]) -> Signal { @@ -29,23 +29,17 @@ public func suggestedLocalizationInfo(network: Network, languageCode: String, ex var entries: [LocalizationEntry] = [] for string in strings { switch string { - case let .langPackString(key, value): - entries.append(.string(key: key, value: value)) - case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue): - entries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue)) - case let .langPackStringDeleted(key): - entries.append(.string(key: key, value: "")) - } - } - var infos: [LocalizationInfo] = [] - for language in languages { - switch language { - case let .langPackLanguage(name, nativeName, langCode): - infos.append(LocalizationInfo(languageCode: langCode, title: name, localizedTitle: nativeName)) + case let .langPackString(key, value): + entries.append(.string(key: key, value: value)) + case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue): + entries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue)) + case let .langPackStringDeleted(key): + entries.append(.string(key: key, value: "")) } } + let infos: [LocalizationInfo] = languages.map(LocalizationInfo.init(apiLanguage:)) return SuggestedLocalizationInfo(languageCode: languageCode, extractedEntries: entries, availableLocalizations: infos) - } + } } final class CachedLocalizationInfos: PostboxCoding { @@ -72,58 +66,65 @@ public func availableLocalizations(postbox: Postbox, network: Network, allowCach return .single(entry.list) } return .complete() - } |> switchToLatest + } |> switchToLatest } else { cached = .complete() } let remote = network.request(Api.functions.langpack.getLanguages(langPack: "")) - |> retryRequest - |> mapToSignal { languages -> Signal<[LocalizationInfo], NoError> in - var infos: [LocalizationInfo] = [] - for language in languages { - switch language { - case let .langPackLanguage(name, nativeName, langCode): - infos.append(LocalizationInfo(languageCode: langCode, title: name, localizedTitle: nativeName)) + |> retryRequest + |> mapToSignal { languages -> Signal<[LocalizationInfo], NoError> in + let infos: [LocalizationInfo] = languages.map(LocalizationInfo.init(apiLanguage:)) + return postbox.transaction { transaction -> [LocalizationInfo] in + transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedAvailableLocalizations, key: ValueBoxKey(length: 0)), entry: CachedLocalizationInfos(list: infos), collectionSpec: ItemCacheCollectionSpec(lowWaterItemCount: 1, highWaterItemCount: 1)) + return infos } - } - return postbox.transaction { transaction -> [LocalizationInfo] in - transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedAvailableLocalizations, key: ValueBoxKey(length: 0)), entry: CachedLocalizationInfos(list: infos), collectionSpec: ItemCacheCollectionSpec(lowWaterItemCount: 1, highWaterItemCount: 1)) - return infos - } } return cached |> then(remote) } -public func downloadLocalization(network: Network, languageCode: String) -> Signal { +public enum DownloadLocalizationError { + case generic +} + +public func downloadLocalization(network: Network, languageCode: String) -> Signal { return network.request(Api.functions.langpack.getLangPack(langPack: "", langCode: languageCode)) - |> retryRequest - |> map { result -> Localization in - let version: Int32 - var entries: [LocalizationEntry] = [] - switch result { + |> mapError { _ -> DownloadLocalizationError in + return .generic + } + |> map { result -> Localization in + let version: Int32 + var entries: [LocalizationEntry] = [] + switch result { case let .langPackDifference(_, _, versionValue, strings): version = versionValue for string in strings { switch string { - case let .langPackString(key, value): - entries.append(.string(key: key, value: value)) - case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue): - entries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue)) - case let .langPackStringDeleted(key): - entries.append(.string(key: key, value: "")) + case let .langPackString(key, value): + entries.append(.string(key: key, value: value)) + case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue): + entries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue)) + case let .langPackStringDeleted(key): + entries.append(.string(key: key, value: "")) } } - } - - return Localization(version: version, entries: entries) + } + + return Localization(version: version, entries: entries) } } -public func downoadAndApplyLocalization(postbox: Postbox, network: Network, languageCode: String) -> Signal { +public enum DownoadAndApplyLocalizationError { + case generic +} + +public func downoadAndApplyLocalization(postbox: Postbox, network: Network, languageCode: String) -> Signal { return downloadLocalization(network: network, languageCode: languageCode) - |> mapToSignal { language -> Signal in - return postbox.transaction { transaction -> Signal in + |> mapError { _ -> DownoadAndApplyLocalizationError in + return .generic + } + |> mapToSignal { language -> Signal in + return postbox.transaction { transaction -> Signal in transaction.updatePreferencesEntry(key: PreferencesKeys.localizationSettings, { _ in return LocalizationSettings(languageCode: languageCode, localization: language) }) @@ -139,6 +140,9 @@ public func downoadAndApplyLocalization(postbox: Postbox, network: Network, lang |> mapToSignal { _ -> Signal in return .complete() } - } |> switchToLatest - } + |> introduceError(DownoadAndApplyLocalizationError.self) + } + |> introduceError(DownoadAndApplyLocalizationError.self) + |> switchToLatest + } } diff --git a/TelegramCore/ManagedLocalizationUpdatesOperations.swift b/TelegramCore/ManagedLocalizationUpdatesOperations.swift index 457061eca4..727ca5be26 100644 --- a/TelegramCore/ManagedLocalizationUpdatesOperations.swift +++ b/TelegramCore/ManagedLocalizationUpdatesOperations.swift @@ -206,7 +206,9 @@ private func synchronizeLocalizationUpdates(transaction: Transaction, postbox: P return postbox.transaction { transaction -> Signal in let (code, _, _) = getLocalization(transaction) return downoadAndApplyLocalization(postbox: postbox, network: network, languageCode: code) - |> introduceError(Void.self) + |> mapError { _ -> Void in + return Void() + } } |> introduceError(Void.self) |> switchToLatest diff --git a/TelegramCore/RecentAccountSession.swift b/TelegramCore/RecentAccountSession.swift index 7b0b6ce47e..5e2e299be0 100644 --- a/TelegramCore/RecentAccountSession.swift +++ b/TelegramCore/RecentAccountSession.swift @@ -1,5 +1,20 @@ import Foundation +public struct AccountSessionFlags: OptionSet { + public var rawValue: Int32 + + public init() { + self.rawValue = 0 + } + + public init(rawValue: Int32) { + self.rawValue = rawValue + } + + public static let isOfficial = AccountSessionFlags(rawValue: (1 << 1)) + public static let passwordPending = AccountSessionFlags(rawValue: (1 << 2)) +} + public struct RecentAccountSession: Equatable { public let hash: Int64 public let deviceModel: String @@ -13,6 +28,7 @@ public struct RecentAccountSession: Equatable { public let ip: String public let country: String public let region: String + public let flags: AccountSessionFlags public var isCurrent: Bool { return self.hash == 0 @@ -55,6 +71,9 @@ public struct RecentAccountSession: Equatable { if lhs.region != rhs.region { return false } + if lhs.flags != rhs.flags { + return false + } return true } } @@ -62,8 +81,15 @@ public struct RecentAccountSession: Equatable { extension RecentAccountSession { init(apiAuthorization: Api.Authorization) { switch apiAuthorization { - case let .authorization(hash, flags, deviceModel, platform, systemVersion, apiId, appName, appVersion, dateCreated, dateActive, ip, country, region): - self.init(hash: hash, deviceModel: deviceModel, platform: platform, systemVersion: systemVersion, apiId: apiId, appName: appName, appVersion: appVersion, creationDate: dateCreated, activityDate: dateActive, ip: ip, country: country, region: region) + case let .authorization(flags, hash, deviceModel, platform, systemVersion, apiId, appName, appVersion, dateCreated, dateActive, ip, country, region): + var accountSessionFlags: AccountSessionFlags = [] + if (flags & (1 << 1)) != 0 { + accountSessionFlags.insert(.isOfficial) + } + if (flags & (1 << 2)) != 0 { + accountSessionFlags.insert(.passwordPending) + } + self.init(hash: hash, deviceModel: deviceModel, platform: platform, systemVersion: systemVersion, apiId: apiId, appName: appName, appVersion: appVersion, creationDate: dateCreated, activityDate: dateActive, ip: ip, country: country, region: region, flags: accountSessionFlags) } } } diff --git a/TelegramCore/RichText.swift b/TelegramCore/RichText.swift index 65d35af655..f737ff6c7c 100644 --- a/TelegramCore/RichText.swift +++ b/TelegramCore/RichText.swift @@ -16,6 +16,11 @@ private enum RichTextTypes: Int32 { case url = 7 case email = 8 case concat = 9 + case `subscript` = 10 + case superscript = 11 + case marked = 12 + case phone = 13 + case image = 14 } public indirect enum RichText: PostboxCoding, Equatable { @@ -29,6 +34,11 @@ public indirect enum RichText: PostboxCoding, Equatable { case url(text: RichText, url: String, webpageId: MediaId?) case email(text: RichText, email: String) case concat([RichText]) + case `subscript`(RichText) + case superscript(RichText) + case marked(RichText) + case phone(text: RichText, phone: String) + case image(id: MediaId, dimensions: CGSize) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("r", orElse: 0) { @@ -58,6 +68,16 @@ public indirect enum RichText: PostboxCoding, Equatable { self = .email(text: decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText, email: decoder.decodeStringForKey("e", orElse: "")) case RichTextTypes.concat.rawValue: self = .concat(decoder.decodeObjectArrayWithDecoderForKey("a")) + case RichTextTypes.subscript.rawValue: + self = .subscript(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) + case RichTextTypes.superscript.rawValue: + self = .superscript(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) + case RichTextTypes.marked.rawValue: + self = .marked(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) + case RichTextTypes.phone.rawValue: + self = .phone(text: decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText, phone: decoder.decodeStringForKey("p", orElse: "")) + case RichTextTypes.image.rawValue: + self = .image(id: MediaId(namespace: decoder.decodeInt32ForKey("i.n", orElse: 0), id: decoder.decodeInt64ForKey("i.i", orElse: 0)), dimensions: CGSize(width: CGFloat(decoder.decodeInt32ForKey("sw", orElse: 0)), height: CGFloat(decoder.decodeInt32ForKey("sh", orElse: 0)))) default: self = .empty } @@ -103,6 +123,25 @@ public indirect enum RichText: PostboxCoding, Equatable { case let .concat(texts): encoder.encodeInt32(RichTextTypes.concat.rawValue, forKey: "r") encoder.encodeObjectArray(texts, forKey: "a") + case let .subscript(text): + encoder.encodeInt32(RichTextTypes.subscript.rawValue, forKey: "r") + encoder.encodeObject(text, forKey: "t") + case let .superscript(text): + encoder.encodeInt32(RichTextTypes.superscript.rawValue, forKey: "r") + encoder.encodeObject(text, forKey: "t") + case let .marked(text): + encoder.encodeInt32(RichTextTypes.marked.rawValue, forKey: "r") + encoder.encodeObject(text, forKey: "t") + case let .phone(text, phone): + encoder.encodeInt32(RichTextTypes.phone.rawValue, forKey: "r") + encoder.encodeObject(text, forKey: "t") + encoder.encodeString(phone, forKey: "p") + case let .image(id, dimensions): + encoder.encodeInt32(RichTextTypes.image.rawValue, forKey: "r") + encoder.encodeInt32(id.namespace, forKey: "i.n") + encoder.encodeInt64(id.id, forKey: "i.i") + encoder.encodeInt32(Int32(dimensions.width), forKey: "sw") + encoder.encodeInt32(Int32(dimensions.height), forKey: "sh") } } @@ -168,6 +207,36 @@ public indirect enum RichText: PostboxCoding, Equatable { } else { return false } + case let .subscript(text): + if case .subscript(text) = rhs { + return true + } else { + return false + } + case let .superscript(text): + if case .superscript(text) = rhs { + return true + } else { + return false + } + case let .marked(text): + if case .marked(text) = rhs { + return true + } else { + return false + } + case let .phone(text, phone): + if case .phone(text, phone) = rhs { + return true + } else { + return false + } + case let .image(id, dimensions): + if case .image(id, dimensions) = rhs { + return true + } else { + return false + } } } } @@ -199,6 +268,16 @@ public extension RichText { string += text.plainText } return string + case let .subscript(text): + return text.plainText + case let .superscript(text): + return text.plainText + case let .marked(text): + return text.plainText + case let .phone(text, _): + return text.plainText + case .image: + return "" } } } @@ -226,6 +305,16 @@ extension RichText { self = .email(text: RichText(apiText: text), email: email) case let .textConcat(texts): self = .concat(texts.map({ RichText(apiText: $0) })) + case let .textSubscript(text): + self = .subscript(RichText(apiText: text)) + case let .textSuperscript(text): + self = .superscript(RichText(apiText: text)) + case let .textMarked(text): + self = .marked(RichText(apiText: text)) + case let .textPhone(text, phone): + self = .phone(text: RichText(apiText: text), phone: phone) + case let .textImage(documentId, w, h): + self = .image(id: MediaId(namespace: Namespaces.Media.CloudFile, id: documentId), dimensions: CGSize(width: CGFloat(w), height: CGFloat(h))) } } } diff --git a/TelegramCore/Serialization.swift b/TelegramCore/Serialization.swift index 32c39556ca..f11dded846 100644 --- a/TelegramCore/Serialization.swift +++ b/TelegramCore/Serialization.swift @@ -202,7 +202,7 @@ public class BoxedMessage: NSObject { public class Serialization: NSObject, MTSerialization { public func currentLayer() -> UInt { - return 86 + return 88 } public func parseMessage(_ data: Data!) -> Any! { diff --git a/TelegramCore/StickerPack.swift b/TelegramCore/StickerPack.swift index c3afc014eb..f9fa1a35c3 100644 --- a/TelegramCore/StickerPack.swift +++ b/TelegramCore/StickerPack.swift @@ -5,7 +5,7 @@ import Foundation import Postbox #endif -public struct StickerPackCollectionInfoFlags : OptionSet { +public struct StickerPackCollectionInfoFlags: OptionSet { public var rawValue: Int32 public init(rawValue: Int32) { From 74d0a746476f3447a7584cc1249acdd0d8073478 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Fri, 2 Nov 2018 12:29:19 +0400 Subject: [PATCH 2/2] no message --- TelegramCore.xcodeproj/project.pbxproj | 6 + TelegramCore/Account.swift | 40 +- .../AccountStateManagementUtils.swift | 33 ++ TelegramCore/CachedChannelData.swift | 37 +- TelegramCore/EnqueueMessage.swift | 8 +- TelegramCore/Fetch.swift | 13 +- TelegramCore/FetchChatList.swift | 221 +++++---- TelegramCore/FetchSecretFileResource.swift | 4 +- TelegramCore/FetchedMediaResource.swift | 6 +- TelegramCore/Holes.swift | 465 +++++++++--------- TelegramCore/LocalizationInfo.swift | 20 +- TelegramCore/LocalizationPreview.swift | 24 + TelegramCore/Localizations.swift | 130 ++--- TelegramCore/ManagedRecentStickers.swift | 26 +- .../ManagedSecretChatOutgoingOperations.swift | 13 + TelegramCore/MultipartFetch.swift | 256 +++++----- TelegramCore/Namespaces.swift | 6 + TelegramCore/PendingMessageManager.swift | 2 +- .../PendingMessageUploadedContent.swift | 5 +- TelegramCore/StandaloneUploadedMedia.swift | 80 ++- TelegramCore/StringFormat.swift | 12 +- TelegramCore/TelegramMediaAction.swift | 125 +---- TelegramCore/TwoStepVerification.swift | 33 +- TelegramCore/UpdateCachedPeerData.swift | 123 +++-- TelegramCore/UpdatesApiUtils.swift | 99 +++- TelegramCore/WebpagePreview.swift | 5 + 26 files changed, 983 insertions(+), 809 deletions(-) create mode 100644 TelegramCore/LocalizationPreview.swift diff --git a/TelegramCore.xcodeproj/project.pbxproj b/TelegramCore.xcodeproj/project.pbxproj index 4beb83b7bb..d69407daa1 100644 --- a/TelegramCore.xcodeproj/project.pbxproj +++ b/TelegramCore.xcodeproj/project.pbxproj @@ -401,6 +401,8 @@ D07827CB1E02F5B200071108 /* RichText.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07827CA1E02F5B200071108 /* RichText.swift */; }; D07E413F208A769D00FCA8F0 /* ProxyServersStatuses.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E413E208A769D00FCA8F0 /* ProxyServersStatuses.swift */; }; D07E4140208A769D00FCA8F0 /* ProxyServersStatuses.swift in Sources */ = {isa = PBXBuildFile; fileRef = D07E413E208A769D00FCA8F0 /* ProxyServersStatuses.swift */; }; + D081E10A217F5ADE003CD921 /* LocalizationPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D081E109217F5ADE003CD921 /* LocalizationPreview.swift */; }; + D081E10B217F5ADE003CD921 /* LocalizationPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = D081E109217F5ADE003CD921 /* LocalizationPreview.swift */; }; D08774FC1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08774FB1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift */; }; D08774FE1E3E3A3500A97350 /* GlobalNotificationSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08774FD1E3E3A3500A97350 /* GlobalNotificationSettings.swift */; }; D08984F22114B97400918162 /* ClearCloudDrafts.swift in Sources */ = {isa = PBXBuildFile; fileRef = D08984F12114B97400918162 /* ClearCloudDrafts.swift */; }; @@ -971,6 +973,7 @@ D07827C81E02F59C00071108 /* InstantPage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InstantPage.swift; sourceTree = ""; }; D07827CA1E02F5B200071108 /* RichText.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RichText.swift; sourceTree = ""; }; D07E413E208A769D00FCA8F0 /* ProxyServersStatuses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyServersStatuses.swift; sourceTree = ""; }; + D081E109217F5ADE003CD921 /* LocalizationPreview.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationPreview.swift; sourceTree = ""; }; D08774FB1E3E39F600A97350 /* ManagedGlobalNotificationSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedGlobalNotificationSettings.swift; sourceTree = ""; }; D08774FD1E3E3A3500A97350 /* GlobalNotificationSettings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GlobalNotificationSettings.swift; sourceTree = ""; }; D08984F12114B97400918162 /* ClearCloudDrafts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClearCloudDrafts.swift; sourceTree = ""; }; @@ -1678,6 +1681,7 @@ isa = PBXGroup; children = ( D08CAA8B1ED81EDF0000FDA8 /* Localizations.swift */, + D081E109217F5ADE003CD921 /* LocalizationPreview.swift */, ); name = Localization; sourceTree = ""; @@ -2270,6 +2274,7 @@ D03B0CD61D62245300955575 /* TelegramUser.swift in Sources */, D02395D61F8D09A50070F5C2 /* ChannelHistoryAvailabilitySettings.swift in Sources */, D019B1CC1E2E3B6A00F80DB3 /* SecretChatRekeySession.swift in Sources */, + D081E10A217F5ADE003CD921 /* LocalizationPreview.swift in Sources */, D03B0CD91D62245B00955575 /* PeerUtils.swift in Sources */, D0F19F6620E6620D00EEC860 /* MultiplexedRequestManager.swift in Sources */, D053B41B1F18DEF500E2D58A /* TelegramMediaExpiredContent.swift in Sources */, @@ -2590,6 +2595,7 @@ D0C50E351E93A86600F62E39 /* CallSessionManager.swift in Sources */, D0A3E448214802C7008ACEF6 /* VoipConfiguration.swift in Sources */, D018EE062045E95000CBB130 /* CheckPeerChatServiceActions.swift in Sources */, + D081E10B217F5ADE003CD921 /* LocalizationPreview.swift in Sources */, D0B8442D1DAB91E0005F29E1 /* NBMetadataCoreTest.m in Sources */, D0C27B431F4B58C000A4E170 /* PeerSpecificStickerPack.swift in Sources */, D0B844131DAB91CD005F29E1 /* StringFormat.swift in Sources */, diff --git a/TelegramCore/Account.swift b/TelegramCore/Account.swift index 284142c5ca..5444a97f19 100644 --- a/TelegramCore/Account.swift +++ b/TelegramCore/Account.swift @@ -293,7 +293,18 @@ let telegramPostboxSeedConfiguration: SeedConfiguration = { initializeMessageNamespacesWithHoles.append((peerNamespace, Namespaces.Message.Cloud)) } - return SeedConfiguration(initializeChatListWithHole: (topLevel: ChatListHole(index: MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: 0), namespace: Namespaces.Message.Cloud, id: 1), timestamp: Int32.max - 1)), groups: ChatListHole(index: MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: 0), namespace: Namespaces.Message.Cloud, id: 1), timestamp: 1))), initializeMessageNamespacesWithHoles: initializeMessageNamespacesWithHoles, existingMessageTags: MessageTags.all, messageTagsWithSummary: MessageTags.unseenPersonalMessage, existingGlobalMessageTags: GlobalMessageTags.all, peerNamespacesRequiringMessageTextIndex: [Namespaces.Peer.SecretChat]) + return SeedConfiguration(initializeChatListWithHole: (topLevel: ChatListHole(index: MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: 0), namespace: Namespaces.Message.Cloud, id: 1), timestamp: Int32.max - 1)), groups: ChatListHole(index: MessageIndex(id: MessageId(peerId: PeerId(namespace: Namespaces.Peer.Empty, id: 0), namespace: Namespaces.Message.Cloud, id: 1), timestamp: 1))), initializeMessageNamespacesWithHoles: initializeMessageNamespacesWithHoles, existingMessageTags: MessageTags.all, messageTagsWithSummary: MessageTags.unseenPersonalMessage, existingGlobalMessageTags: GlobalMessageTags.all, peerNamespacesRequiringMessageTextIndex: [Namespaces.Peer.SecretChat], peerSummaryCounterTags: { peer in + if let peer = peer as? TelegramChannel, let addressName = peer.addressName, !addressName.isEmpty { + switch peer.info { + case .group: + return [.publicGroups] + case .broadcast: + return [.channels] + } + } else { + return [.regularChatsAndPrivateGroups] + } + }) }() public func accountWithId(networkArguments: NetworkInitializationArguments, id: AccountRecordId, supplementary: Bool, rootPath: String, beginWithTestingEnvironment: Bool, auxiliaryMethods: AccountAuxiliaryMethods, shouldKeepAutoConnection: Bool = true) -> Signal { @@ -755,10 +766,10 @@ public enum AccountNetworkState: Equatable { public final class AccountAuxiliaryMethods { public let updatePeerChatInputState: (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState? - public let fetchResource: (Account, MediaResource, Signal, MediaResourceFetchParameters?) -> Signal? + public let fetchResource: (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal? public let fetchResourceMediaReferenceHash: (MediaResource) -> Signal - public init(updatePeerChatInputState: @escaping (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?, fetchResource: @escaping (Account, MediaResource, Signal, MediaResourceFetchParameters?) -> Signal?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal) { + public init(updatePeerChatInputState: @escaping (PeerChatInterfaceState?, SynchronizeableChatInputState?) -> PeerChatInterfaceState?, fetchResource: @escaping (Account, MediaResource, Signal<[(Range, MediaBoxFetchPriority)], NoError>, MediaResourceFetchParameters?) -> Signal?, fetchResourceMediaReferenceHash: @escaping (MediaResource) -> Signal) { self.updatePeerChatInputState = updatePeerChatInputState self.fetchResource = fetchResource self.fetchResourceMediaReferenceHash = fetchResourceMediaReferenceHash @@ -1012,11 +1023,11 @@ public class Account { return .single(!value) } } - let networkStateSignal = combineLatest(self.stateManager.isUpdating |> deliverOn(networkStateQueue), network.connectionStatus |> deliverOn(networkStateQueue), delayNetworkStatus |> deliverOn(networkStateQueue)) - |> map { isUpdating, connectionStatus, delayNetworkStatus -> AccountNetworkState in - if delayNetworkStatus { + let networkStateSignal = combineLatest(self.stateManager.isUpdating |> deliverOn(networkStateQueue), network.connectionStatus |> deliverOn(networkStateQueue)/*, delayNetworkStatus |> deliverOn(networkStateQueue)*/) + |> map { isUpdating, connectionStatus/*, delayNetworkStatus*/ -> AccountNetworkState in + /*if delayNetworkStatus { return .online(proxy: nil) - } + }*/ switch connectionStatus { case .waitingForNetwork: @@ -1046,7 +1057,8 @@ public class Account { } } } - self.networkStateValue.set(networkStateSignal |> distinctUntilChanged) + self.networkStateValue.set(networkStateSignal + |> distinctUntilChanged) self.networkTypeValue.set(currentNetworkType()) @@ -1278,16 +1290,16 @@ public func updateAccountNetworkUsageStats(account: Account, category: MediaReso updateNetworkUsageStats(basePath: account.basePath, category: category, delta: delta) } -public typealias FetchCachedResourceRepresentation = (_ account: Account, _ resource: MediaResource, _ resourceData: MediaResourceData, _ representation: CachedMediaResourceRepresentation) -> Signal +public typealias FetchCachedResourceRepresentation = (_ account: Account, _ resource: MediaResource, _ representation: CachedMediaResourceRepresentation) -> Signal public typealias TransformOutgoingMessageMedia = (_ postbox: Postbox, _ network: Network, _ media: AnyMediaReference, _ userInteractive: Bool) -> Signal public func setupAccount(_ account: Account, fetchCachedResourceRepresentation: FetchCachedResourceRepresentation? = nil, transformOutgoingMessageMedia: TransformOutgoingMessageMedia? = nil, preFetchedResourcePath: @escaping (MediaResource) -> String? = { _ in return nil }) { account.postbox.mediaBox.preFetchedResourcePath = preFetchedResourcePath - account.postbox.mediaBox.fetchResource = { [weak account] resource, ranges, parameters -> Signal in + account.postbox.mediaBox.fetchResource = { [weak account] resource, intervals, parameters -> Signal in if let strongAccount = account { - if let result = fetchResource(account: strongAccount, resource: resource, ranges: ranges, parameters: parameters) { + if let result = fetchResource(account: strongAccount, resource: resource, intervals: intervals, parameters: parameters) { return result - } else if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, ranges, parameters) { + } else if let result = strongAccount.auxiliaryMethods.fetchResource(strongAccount, resource, intervals, parameters) { return result } else { return .never() @@ -1297,9 +1309,9 @@ public func setupAccount(_ account: Account, fetchCachedResourceRepresentation: } } - account.postbox.mediaBox.fetchCachedResourceRepresentation = { [weak account] resource, resourceData, representation in + account.postbox.mediaBox.fetchCachedResourceRepresentation = { [weak account] resource, representation in if let strongAccount = account, let fetchCachedResourceRepresentation = fetchCachedResourceRepresentation { - return fetchCachedResourceRepresentation(strongAccount, resource, resourceData, representation) + return fetchCachedResourceRepresentation(strongAccount, resource, representation) } else { return .never() } diff --git a/TelegramCore/AccountStateManagementUtils.swift b/TelegramCore/AccountStateManagementUtils.swift index 80c43a8cb4..fed52c7940 100644 --- a/TelegramCore/AccountStateManagementUtils.swift +++ b/TelegramCore/AccountStateManagementUtils.swift @@ -101,6 +101,13 @@ private func locallyGeneratedMessageTimestampsFromUpdateGroups(_ groups: [Update messageTimestamps[peerId]!.append((Namespaces.Message.Local, date)) } } + case let .updateContactRegistered(userId, date): + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + if messageTimestamps[peerId] == nil { + messageTimestamps[peerId] = [(Namespaces.Message.Local, date)] + } else { + messageTimestamps[peerId]!.append((Namespaces.Message.Local, date)) + } default: break } @@ -299,6 +306,13 @@ private func locallyGeneratedMessageTimestampsFromDifference(_ difference: Api.u messageTimestamps[peerId]!.append((Namespaces.Message.Local, date)) } } + case let .updateContactRegistered(userId, date): + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + if messageTimestamps[peerId] == nil { + messageTimestamps[peerId] = [(Namespaces.Message.Local, date)] + } else { + messageTimestamps[peerId]!.append((Namespaces.Message.Local, date)) + } default: break } @@ -906,6 +920,25 @@ private func finalStateWithUpdatesAndServerTime(account: Account, state: Account } else { updatedState.addDisplayAlert(text) } + case let .updateContactRegistered(userId, date): + let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: userId) + + var alreadyStored = false + if let storedMessages = updatedState.storedMessagesByPeerIdAndTimestamp[peerId] { + for index in storedMessages { + if index.timestamp == date { + alreadyStored = true + break + } + } + } + + if alreadyStored { + Logger.shared.log("State", "skipping joined message at \(date) for \(peerId): already stored") + } else { + let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: date, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "", attributes: [], media: [TelegramMediaAction(action: .peerJoined)]) + updatedState.addMessages([message], location: .UpperHistoryBlock) + } case let .updateReadChannelInbox(channelId, maxId): updatedState.readInbox(MessageId(peerId: PeerId(namespace: Namespaces.Peer.CloudChannel, id: channelId), namespace: Namespaces.Message.Cloud, id: maxId)) case let .updateReadChannelOutbox(channelId, maxId): diff --git a/TelegramCore/CachedChannelData.swift b/TelegramCore/CachedChannelData.swift index cf5d33e292..12c518c27f 100644 --- a/TelegramCore/CachedChannelData.swift +++ b/TelegramCore/CachedChannelData.swift @@ -125,6 +125,7 @@ public struct ChannelMigrationReference: PostboxCoding, Equatable { } public final class CachedChannelData: CachedPeerData { + public let isNotAccessible: Bool public let flags: CachedChannelFlags public let about: String? public let participantsSummary: CachedChannelParticipantsSummary @@ -144,6 +145,7 @@ public final class CachedChannelData: CachedPeerData { } init() { + self.isNotAccessible = false self.flags = [] self.about = nil self.participantsSummary = CachedChannelParticipantsSummary(memberCount: nil, adminCount: nil, bannedCount: nil, kickedCount: nil) @@ -159,7 +161,8 @@ public final class CachedChannelData: CachedPeerData { self.migrationReference = nil } - init(flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], topParticipants: CachedChannelParticipants?, reportStatus: PeerReportStatus, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?) { + init(isNotAccessible: Bool, flags: CachedChannelFlags, about: String?, participantsSummary: CachedChannelParticipantsSummary, exportedInvitation: ExportedInvitation?, botInfos: [CachedPeerBotInfo], topParticipants: CachedChannelParticipants?, reportStatus: PeerReportStatus, pinnedMessageId: MessageId?, stickerPack: StickerPackCollectionInfo?, minAvailableMessageId: MessageId?, migrationReference: ChannelMigrationReference?) { + self.isNotAccessible = isNotAccessible self.flags = flags self.about = about self.participantsSummary = participantsSummary @@ -190,51 +193,56 @@ public final class CachedChannelData: CachedPeerData { self.messageIds = messageIds } + func withUpdatedisNotAccessible(_ isNotAccessible: Bool) -> CachedChannelData { + return CachedChannelData(isNotAccessible: isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + } + func withUpdatedFlags(_ flags: CachedChannelFlags) -> CachedChannelData { - return CachedChannelData(flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedAbout(_ about: String?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedParticipantsSummary(_ participantsSummary: CachedChannelParticipantsSummary) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedExportedInvitation(_ exportedInvitation: ExportedInvitation?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedBotInfos(_ botInfos: [CachedPeerBotInfo]) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedTopParticipants(_ topParticipants: CachedChannelParticipants?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedReportStatus(_ reportStatus: PeerReportStatus) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedPinnedMessageId(_ pinnedMessageId: MessageId?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedStickerPack(_ stickerPack: StickerPackCollectionInfo?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedMinAvailableMessageId(_ minAvailableMessageId: MessageId?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: minAvailableMessageId, migrationReference: self.migrationReference) } func withUpdatedMigrationReference(_ migrationReference: ChannelMigrationReference?) -> CachedChannelData { - return CachedChannelData(flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference) + return CachedChannelData(isNotAccessible: self.isNotAccessible, flags: self.flags, about: self.about, participantsSummary: self.participantsSummary, exportedInvitation: self.exportedInvitation, botInfos: self.botInfos, topParticipants: self.topParticipants, reportStatus: self.reportStatus, pinnedMessageId: self.pinnedMessageId, stickerPack: self.stickerPack, minAvailableMessageId: self.minAvailableMessageId, migrationReference: migrationReference) } public init(decoder: PostboxDecoder) { + self.isNotAccessible = decoder.decodeInt32ForKey("isNotAccessible", orElse: 0) != 0 self.flags = CachedChannelFlags(rawValue: decoder.decodeInt32ForKey("f", orElse: 0)) self.about = decoder.decodeOptionalStringForKey("a") self.participantsSummary = CachedChannelParticipantsSummary(decoder: decoder) @@ -282,6 +290,7 @@ public final class CachedChannelData: CachedPeerData { } public func encode(_ encoder: PostboxEncoder) { + encoder.encodeInt32(self.isNotAccessible ? 1 : 0, forKey: "isNotAccessible") encoder.encodeInt32(self.flags.rawValue, forKey: "f") if let about = self.about { encoder.encodeString(about, forKey: "a") @@ -336,6 +345,10 @@ public final class CachedChannelData: CachedPeerData { return false } + if other.isNotAccessible != self.isNotAccessible { + return false + } + if other.flags != self.flags { return false } diff --git a/TelegramCore/EnqueueMessage.swift b/TelegramCore/EnqueueMessage.swift index b863b9ebe9..6fca23c4e7 100644 --- a/TelegramCore/EnqueueMessage.swift +++ b/TelegramCore/EnqueueMessage.swift @@ -186,7 +186,7 @@ public func enqueueMessagesToMultiplePeers(account: Account, peerIds: [PeerId], return account.postbox.transaction { transaction -> [MessageId] in var messageIds: [MessageId] = [] for peerId in peerIds { - for id in enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: messages) { + for id in enqueueMessages(transaction: transaction, account: account, peerId: peerId, messages: messages, disableAutoremove: true) { if let id = id { messageIds.append(id) } @@ -222,7 +222,7 @@ public func resendMessages(account: Account, messageIds: [MessageId]) -> Signal< } } -func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, messages: [(Bool, EnqueueMessage)]) -> [MessageId?] { +func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, messages: [(Bool, EnqueueMessage)], disableAutoremove: Bool = false) -> [MessageId?] { var updatedMessages: [(Bool, EnqueueMessage)] = [] outer: for (transformedMedia, message) in messages { switch message { @@ -295,7 +295,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, if let _ = mediaReference?.media as? TelegramMediaAction { isAction = true } - if let messageAutoremoveTimeout = peer.messageAutoremoveTimeout, !isAction { + if !disableAutoremove, let messageAutoremoveTimeout = peer.messageAutoremoveTimeout, !isAction { attributes.append(AutoremoveTimeoutMessageAttribute(timeout: messageAutoremoveTimeout, countdownBeginTime: nil)) } } @@ -387,7 +387,7 @@ func enqueueMessages(transaction: Transaction, account: Account, peerId: PeerId, break } } - if let messageAutoremoveTimeout = peer.messageAutoremoveTimeout, !isAction { + if !disableAutoremove, let messageAutoremoveTimeout = peer.messageAutoremoveTimeout, !isAction { attributes.append(AutoremoveTimeoutMessageAttribute(timeout: messageAutoremoveTimeout, countdownBeginTime: nil)) } } diff --git a/TelegramCore/Fetch.swift b/TelegramCore/Fetch.swift index b8d98b014d..cfc952d3ae 100644 --- a/TelegramCore/Fetch.swift +++ b/TelegramCore/Fetch.swift @@ -9,8 +9,8 @@ import SwiftSignalKit import Photos #endif -private func fetchCloudMediaLocation(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int?, ranges: Signal, parameters: MediaResourceFetchParameters?) -> Signal { - return multipartFetch(account: account, resource: resource, datacenterId: datacenterId, size: size, ranges: ranges, parameters: parameters) +private func fetchCloudMediaLocation(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int?, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal { + return multipartFetch(account: account, resource: resource, datacenterId: datacenterId, size: size, intervals: intervals, parameters: parameters) } private func fetchLocalFileResource(path: String, move: Bool) -> Signal { @@ -30,21 +30,22 @@ private func fetchLocalFileResource(path: String, move: Bool) -> Signal, parameters: MediaResourceFetchParameters?) -> Signal? { +func fetchResource(account: Account, resource: MediaResource, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal? { if let _ = resource as? EmptyMediaResource { return .single(.reset) |> then(.never()) } else if let secretFileResource = resource as? SecretFileMediaResource { return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) - |> then(fetchSecretFileResource(account: account, resource: secretFileResource, ranges: ranges, parameters: parameters)) + |> then(fetchSecretFileResource(account: account, resource: secretFileResource, intervals: intervals, parameters: parameters)) } else if let cloudResource = resource as? TelegramMultipartFetchableResource { - return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, datacenterId: cloudResource.datacenterId, size: resource.size == 0 ? nil : resource.size, ranges: ranges, parameters: parameters)) + return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) + |> then(fetchCloudMediaLocation(account: account, resource: cloudResource, datacenterId: cloudResource.datacenterId, size: resource.size == 0 ? nil : resource.size, intervals: intervals, parameters: parameters)) } else if let webFileResource = resource as? WebFileReferenceMediaResource { return currentWebDocumentsHostDatacenterId(postbox: account.postbox, isTestingEnvironment: account.testingEnvironment) |> introduceError(MediaResourceDataFetchError.self) |> mapToSignal { datacenterId -> Signal in return .single(.dataPart(resourceOffset: 0, data: Data(), range: 0 ..< 0, complete: false)) - |> then(fetchCloudMediaLocation(account: account, resource: webFileResource, datacenterId: Int(datacenterId), size: resource.size == 0 ? nil : resource.size, ranges: ranges, parameters: parameters)) + |> then(fetchCloudMediaLocation(account: account, resource: webFileResource, datacenterId: Int(datacenterId), size: resource.size == 0 ? nil : resource.size, intervals: intervals, parameters: parameters)) } } else if let localFileResource = resource as? LocalFileReferenceMediaResource { return fetchLocalFileResource(path: localFileResource.localFilePath, move: localFileResource.isUniquelyReferencedTemporaryFile) diff --git a/TelegramCore/FetchChatList.swift b/TelegramCore/FetchChatList.swift index 2ad894be7c..885c75063d 100644 --- a/TelegramCore/FetchChatList.swift +++ b/TelegramCore/FetchChatList.swift @@ -189,127 +189,138 @@ struct FetchedChatList { } func fetchChatList(postbox: Postbox, network: Network, location: FetchChatListLocation, upperBound: MessageIndex) -> Signal { - let offset: Signal<(Int32, Int32, Api.InputPeer), NoError> - if upperBound.id.peerId.namespace == Namespaces.Peer.Empty { - offset = single((0, 0, Api.InputPeer.inputPeerEmpty), NoError.self) - } else { - offset = postbox.loadedPeerWithId(upperBound.id.peerId) - |> take(1) - |> map { peer in - return (upperBound.timestamp, upperBound.id.id + 1, apiInputPeer(peer) ?? .inputPeerEmpty) + return postbox.stateView() + |> mapToSignal { view -> Signal in + if let state = view.state as? AuthorizedAccountState { + return .single(state) + } else { + return .complete() } } - - return offset - |> mapToSignal { (timestamp, id, peer) -> Signal in - let additionalPinnedChats: Signal - if case .general = location, case .inputPeerEmpty = peer, timestamp == 0 { - additionalPinnedChats = network.request(Api.functions.messages.getPinnedDialogs()) - |> retryRequest - |> map(Optional.init) + |> take(1) + |> mapToSignal { _ -> Signal in + let offset: Signal<(Int32, Int32, Api.InputPeer), NoError> + if upperBound.id.peerId.namespace == Namespaces.Peer.Empty { + offset = single((0, 0, Api.InputPeer.inputPeerEmpty), NoError.self) } else { - additionalPinnedChats = .single(nil) + offset = postbox.loadedPeerWithId(upperBound.id.peerId) + |> take(1) + |> map { peer in + return (upperBound.timestamp, upperBound.id.id + 1, apiInputPeer(peer) ?? .inputPeerEmpty) + } } - var flags: Int32 = 0 - var requestFeedId: Int32? - - switch location { - case .general: - break - case let .group(groupId): - /*feed*/ - /*requestFeedId = groupId.rawValue - flags |= 1 << 1*/ - break - } - let requestChats = network.request(Api.functions.messages.getDialogs(flags: flags/*feed*//*, feedId: requestFeedId*/, offsetDate: timestamp, offsetId: id, offsetPeer: peer, limit: 100, hash: 0)) - |> retryRequest - - return combineLatest(requestChats, additionalPinnedChats) - |> mapToSignal { remoteChats, pinnedChats -> Signal in - let extractedRemoteDialogs = extractDialogsData(dialogs: remoteChats) - let parsedRemoteChats = parseDialogs(apiDialogs: extractedRemoteDialogs.apiDialogs, apiMessages: extractedRemoteDialogs.apiMessages, apiChats: extractedRemoteDialogs.apiChats, apiUsers: extractedRemoteDialogs.apiUsers, apiIsAtLowestBoundary: extractedRemoteDialogs.apiIsAtLowestBoundary) - var parsedPinnedChats: ParsedDialogs? - if let pinnedChats = pinnedChats { - let extractedPinnedChats = extractDialogsData(peerDialogs: pinnedChats) - parsedPinnedChats = parseDialogs(apiDialogs: extractedPinnedChats.apiDialogs, apiMessages: extractedPinnedChats.apiMessages, apiChats: extractedPinnedChats.apiChats, apiUsers: extractedPinnedChats.apiUsers, apiIsAtLowestBoundary: extractedPinnedChats.apiIsAtLowestBoundary) + return offset + |> mapToSignal { (timestamp, id, peer) -> Signal in + let additionalPinnedChats: Signal + if case .general = location, case .inputPeerEmpty = peer, timestamp == 0 { + additionalPinnedChats = network.request(Api.functions.messages.getPinnedDialogs()) + |> retryRequest + |> map(Optional.init) + } else { + additionalPinnedChats = .single(nil) } - var combinedReferencedFeeds = Set() - combinedReferencedFeeds.formUnion(parsedRemoteChats.referencedFeeds) - if let parsedPinnedChats = parsedPinnedChats { - combinedReferencedFeeds.formUnion(parsedPinnedChats.referencedFeeds) - } + var flags: Int32 = 0 + var requestFeedId: Int32? - var feedSignals: [Signal<(PeerGroupId, ParsedDialogs), NoError>] = [] - if case .general = location { - /*feed*/ - /*for groupId in combinedReferencedFeeds { - let flags: Int32 = 1 << 1 - let requestFeed = network.request(Api.functions.messages.getDialogs(flags: flags, feedId: groupId.rawValue, offsetDate: 0, offsetId: 0, offsetPeer: .inputPeerEmpty, limit: 4)) - |> retryRequest - |> map { result -> (PeerGroupId, ParsedDialogs) in - let extractedData = extractDialogsData(dialogs: result) - let parsedChats = parseDialogs(apiDialogs: extractedData.apiDialogs, apiMessages: extractedData.apiMessages, apiChats: extractedData.apiChats, apiUsers: extractedData.apiUsers, apiIsAtLowestBoundary: extractedData.apiIsAtLowestBoundary) - return (groupId, parsedChats) - } - feedSignals.append(requestFeed) - }*/ + switch location { + case .general: + break + case let .group(groupId): + /*feed*/ + /*requestFeedId = groupId.rawValue + flags |= 1 << 1*/ + break } + let requestChats = network.request(Api.functions.messages.getDialogs(flags: flags/*feed*//*, feedId: requestFeedId*/, offsetDate: timestamp, offsetId: id, offsetPeer: peer, limit: 100, hash: 0)) + |> retryRequest - return combineLatest(feedSignals) - |> map { feeds -> FetchedChatList in - var peers: [Peer] = [] - var peerPresences: [PeerId: PeerPresence] = [:] - var notificationSettings: [PeerId: PeerNotificationSettings] = [:] - var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] - var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] - var chatStates: [PeerId: PeerChatState] = [:] - var storeMessages: [StoreMessage] = [] - - peers.append(contentsOf: parsedRemoteChats.peers) - peerPresences.merge(parsedRemoteChats.peerPresences, uniquingKeysWith: { _, updated in updated }) - notificationSettings.merge(parsedRemoteChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) - readStates.merge(parsedRemoteChats.readStates, uniquingKeysWith: { _, updated in updated }) - mentionTagSummaries.merge(parsedRemoteChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) - chatStates.merge(parsedRemoteChats.chatStates, uniquingKeysWith: { _, updated in updated }) - storeMessages.append(contentsOf: parsedRemoteChats.storeMessages) + return combineLatest(requestChats, additionalPinnedChats) + |> mapToSignal { remoteChats, pinnedChats -> Signal in + let extractedRemoteDialogs = extractDialogsData(dialogs: remoteChats) + let parsedRemoteChats = parseDialogs(apiDialogs: extractedRemoteDialogs.apiDialogs, apiMessages: extractedRemoteDialogs.apiMessages, apiChats: extractedRemoteDialogs.apiChats, apiUsers: extractedRemoteDialogs.apiUsers, apiIsAtLowestBoundary: extractedRemoteDialogs.apiIsAtLowestBoundary) + var parsedPinnedChats: ParsedDialogs? + if let pinnedChats = pinnedChats { + let extractedPinnedChats = extractDialogsData(peerDialogs: pinnedChats) + parsedPinnedChats = parseDialogs(apiDialogs: extractedPinnedChats.apiDialogs, apiMessages: extractedPinnedChats.apiMessages, apiChats: extractedPinnedChats.apiChats, apiUsers: extractedPinnedChats.apiUsers, apiIsAtLowestBoundary: extractedPinnedChats.apiIsAtLowestBoundary) + } + var combinedReferencedFeeds = Set() + combinedReferencedFeeds.formUnion(parsedRemoteChats.referencedFeeds) if let parsedPinnedChats = parsedPinnedChats { - peers.append(contentsOf: parsedPinnedChats.peers) - peerPresences.merge(parsedPinnedChats.peerPresences, uniquingKeysWith: { _, updated in updated }) - notificationSettings.merge(parsedPinnedChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) - readStates.merge(parsedPinnedChats.readStates, uniquingKeysWith: { _, updated in updated }) - mentionTagSummaries.merge(parsedPinnedChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) - chatStates.merge(parsedPinnedChats.chatStates, uniquingKeysWith: { _, updated in updated }) - storeMessages.append(contentsOf: parsedPinnedChats.storeMessages) + combinedReferencedFeeds.formUnion(parsedPinnedChats.referencedFeeds) } - for (_, feedChats) in feeds { - peers.append(contentsOf: feedChats.peers) - peerPresences.merge(feedChats.peerPresences, uniquingKeysWith: { _, updated in updated }) - notificationSettings.merge(feedChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) - readStates.merge(feedChats.readStates, uniquingKeysWith: { _, updated in updated }) - mentionTagSummaries.merge(feedChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) - chatStates.merge(feedChats.chatStates, uniquingKeysWith: { _, updated in updated }) - storeMessages.append(contentsOf: feedChats.storeMessages) + var feedSignals: [Signal<(PeerGroupId, ParsedDialogs), NoError>] = [] + if case .general = location { + /*feed*/ + /*for groupId in combinedReferencedFeeds { + let flags: Int32 = 1 << 1 + let requestFeed = network.request(Api.functions.messages.getDialogs(flags: flags, feedId: groupId.rawValue, offsetDate: 0, offsetId: 0, offsetPeer: .inputPeerEmpty, limit: 4)) + |> retryRequest + |> map { result -> (PeerGroupId, ParsedDialogs) in + let extractedData = extractDialogsData(dialogs: result) + let parsedChats = parseDialogs(apiDialogs: extractedData.apiDialogs, apiMessages: extractedData.apiMessages, apiChats: extractedData.apiChats, apiUsers: extractedData.apiUsers, apiIsAtLowestBoundary: extractedData.apiIsAtLowestBoundary) + return (groupId, parsedChats) + } + feedSignals.append(requestFeed) + }*/ } - return FetchedChatList( - peers: peers, - peerPresences: peerPresences, - notificationSettings: notificationSettings, - readStates: readStates, - mentionTagSummaries: mentionTagSummaries, - chatStates: chatStates, - storeMessages: storeMessages, - - lowerNonPinnedIndex: parsedRemoteChats.lowerNonPinnedIndex, - - pinnedItemIds: parsedPinnedChats.flatMap { $0.itemIds }, - feeds: feeds.map { ($0.0, $0.1.lowerNonPinnedIndex) } - ) + return combineLatest(feedSignals) + |> map { feeds -> FetchedChatList in + var peers: [Peer] = [] + var peerPresences: [PeerId: PeerPresence] = [:] + var notificationSettings: [PeerId: PeerNotificationSettings] = [:] + var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] + var mentionTagSummaries: [PeerId: MessageHistoryTagNamespaceSummary] = [:] + var chatStates: [PeerId: PeerChatState] = [:] + var storeMessages: [StoreMessage] = [] + + peers.append(contentsOf: parsedRemoteChats.peers) + peerPresences.merge(parsedRemoteChats.peerPresences, uniquingKeysWith: { _, updated in updated }) + notificationSettings.merge(parsedRemoteChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) + readStates.merge(parsedRemoteChats.readStates, uniquingKeysWith: { _, updated in updated }) + mentionTagSummaries.merge(parsedRemoteChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) + chatStates.merge(parsedRemoteChats.chatStates, uniquingKeysWith: { _, updated in updated }) + storeMessages.append(contentsOf: parsedRemoteChats.storeMessages) + + if let parsedPinnedChats = parsedPinnedChats { + peers.append(contentsOf: parsedPinnedChats.peers) + peerPresences.merge(parsedPinnedChats.peerPresences, uniquingKeysWith: { _, updated in updated }) + notificationSettings.merge(parsedPinnedChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) + readStates.merge(parsedPinnedChats.readStates, uniquingKeysWith: { _, updated in updated }) + mentionTagSummaries.merge(parsedPinnedChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) + chatStates.merge(parsedPinnedChats.chatStates, uniquingKeysWith: { _, updated in updated }) + storeMessages.append(contentsOf: parsedPinnedChats.storeMessages) + } + + for (_, feedChats) in feeds { + peers.append(contentsOf: feedChats.peers) + peerPresences.merge(feedChats.peerPresences, uniquingKeysWith: { _, updated in updated }) + notificationSettings.merge(feedChats.notificationSettings, uniquingKeysWith: { _, updated in updated }) + readStates.merge(feedChats.readStates, uniquingKeysWith: { _, updated in updated }) + mentionTagSummaries.merge(feedChats.mentionTagSummaries, uniquingKeysWith: { _, updated in updated }) + chatStates.merge(feedChats.chatStates, uniquingKeysWith: { _, updated in updated }) + storeMessages.append(contentsOf: feedChats.storeMessages) + } + + return FetchedChatList( + peers: peers, + peerPresences: peerPresences, + notificationSettings: notificationSettings, + readStates: readStates, + mentionTagSummaries: mentionTagSummaries, + chatStates: chatStates, + storeMessages: storeMessages, + + lowerNonPinnedIndex: parsedRemoteChats.lowerNonPinnedIndex, + + pinnedItemIds: parsedPinnedChats.flatMap { $0.itemIds }, + feeds: feeds.map { ($0.0, $0.1.lowerNonPinnedIndex) } + ) + } } } } diff --git a/TelegramCore/FetchSecretFileResource.swift b/TelegramCore/FetchSecretFileResource.swift index be2f614856..54040e435c 100644 --- a/TelegramCore/FetchSecretFileResource.swift +++ b/TelegramCore/FetchSecretFileResource.swift @@ -9,6 +9,6 @@ import Foundation import MtProtoKitDynamic #endif -func fetchSecretFileResource(account: Account, resource: SecretFileMediaResource, ranges: Signal, parameters: MediaResourceFetchParameters?) -> Signal { - return multipartFetch(account: account, resource: resource, datacenterId: resource.datacenterId, size: resource.size, ranges: ranges, parameters: parameters, encryptionKey: resource.key, decryptedSize: resource.decryptedSize) +func fetchSecretFileResource(account: Account, resource: SecretFileMediaResource, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?) -> Signal { + return multipartFetch(account: account, resource: resource, datacenterId: resource.datacenterId, size: resource.size, intervals: intervals, parameters: parameters, encryptionKey: resource.key, decryptedSize: resource.decryptedSize) } diff --git a/TelegramCore/FetchedMediaResource.swift b/TelegramCore/FetchedMediaResource.swift index 29bbbcc369..3d884b8a46 100644 --- a/TelegramCore/FetchedMediaResource.swift +++ b/TelegramCore/FetchedMediaResource.swift @@ -467,9 +467,9 @@ final class TelegramCloudMediaResourceFetchInfo: MediaResourceFetchInfo { } } -public func fetchedMediaResource(postbox: Postbox, reference: MediaResourceReference, range: Range? = nil, statsCategory: MediaResourceStatsCategory = .generic, reportResultStatus: Bool = false, preferBackgroundReferenceRevalidation: Bool = false) -> Signal { - if let range = range { - return postbox.mediaBox.fetchedResourceData(reference.resource, in: range, parameters: MediaResourceFetchParameters(tag: TelegramMediaResourceFetchTag(statsCategory: statsCategory), info: TelegramCloudMediaResourceFetchInfo(reference: reference, preferBackgroundReferenceRevalidation: preferBackgroundReferenceRevalidation))) +public func fetchedMediaResource(postbox: Postbox, reference: MediaResourceReference, range: (Range, MediaBoxFetchPriority)? = nil, statsCategory: MediaResourceStatsCategory = .generic, reportResultStatus: Bool = false, preferBackgroundReferenceRevalidation: Bool = false) -> Signal { + if let (range, priority) = range { + return postbox.mediaBox.fetchedResourceData(reference.resource, in: range, priority: priority, parameters: MediaResourceFetchParameters(tag: TelegramMediaResourceFetchTag(statsCategory: statsCategory), info: TelegramCloudMediaResourceFetchInfo(reference: reference, preferBackgroundReferenceRevalidation: preferBackgroundReferenceRevalidation))) |> map { _ in .local } } else { return postbox.mediaBox.fetchedResource(reference.resource, parameters: MediaResourceFetchParameters(tag: TelegramMediaResourceFetchTag(statsCategory: statsCategory), info: TelegramCloudMediaResourceFetchInfo(reference: reference, preferBackgroundReferenceRevalidation: preferBackgroundReferenceRevalidation)), implNext: reportResultStatus) diff --git a/TelegramCore/Holes.swift b/TelegramCore/Holes.swift index 218ec08925..dcda189b37 100644 --- a/TelegramCore/Holes.swift +++ b/TelegramCore/Holes.swift @@ -125,237 +125,248 @@ func withResolvedAssociatedMessages(postbox: Postbox, source: FetchMessageHistor func fetchMessageHistoryHole(source: FetchMessageHistoryHoleSource, postbox: Postbox, hole: MessageHistoryHole, direction: MessageHistoryViewRelativeHoleDirection, tagMask: MessageTags?, limit: Int = 100) -> Signal { assert(tagMask == nil || tagMask!.rawValue != 0) - return postbox.loadedPeerWithId(hole.maxIndex.id.peerId) - |> take(1) - |> mapToSignal { peer in - if let inputPeer = forceApiInputPeer(peer) { - print("fetchMessageHistoryHole for \(peer.displayTitle) \(direction)") - let request: Signal - var maxIndexRequest: Signal = .single(nil) - var implicitelyFillHole = false - if let tagMask = tagMask { - if tagMask == MessageTags.unseenPersonalMessage { - let offsetId: Int32 - let addOffset: Int32 - let selectedLimit = limit - let maxId: Int32 - let minId: Int32 - - switch direction { - case .UpperToLower: - offsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) - addOffset = 0 - maxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) - minId = 1 - case .LowerToUpper: - offsetId = hole.min <= 1 ? 1 : (hole.min - 1) - addOffset = Int32(-selectedLimit) - maxId = Int32.max - minId = hole.min - 1 - case let .AroundId(id): - offsetId = id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 - case let .AroundIndex(index): - offsetId = index.id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 - } - request = source.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) - } else if tagMask == .liveLocation { - let selectedLimit = limit - - switch direction { - case .UpperToLower: - implicitelyFillHole = true - default: - assertionFailure() - } - request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit), hash: 0)) - } else if let filter = messageFilterForTagMask(tagMask) { - let offsetId: Int32 - let addOffset: Int32 - let selectedLimit = limit - let maxId: Int32 - let minId: Int32 - - switch direction { - case .UpperToLower: - offsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) - addOffset = 0 - maxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) - minId = 1 - case .LowerToUpper: - offsetId = hole.min <= 1 ? 1 : (hole.min - 1) - addOffset = Int32(-selectedLimit) - maxId = Int32.max - minId = hole.min - 1 - case let .AroundId(id): - offsetId = id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 - case let .AroundIndex(index): - offsetId = index.id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 - } - request = source.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: filter, minDate: 0, maxDate: hole.maxIndex.timestamp, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) - } else { - assertionFailure() - request = .never() - } - } else { - let offsetId: Int32 - let addOffset: Int32 - let selectedLimit = limit - let maxId: Int32 - let minId: Int32 - - switch direction { - case .UpperToLower: - offsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) - addOffset = 0 - maxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) - minId = 1 - case .LowerToUpper: - offsetId = hole.min <= 1 ? 1 : (hole.min - 1) - addOffset = Int32(-selectedLimit) - maxId = Int32.max - minId = hole.min - 1 - if hole.maxIndex.timestamp == Int32.max { - let innerOffsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) - let innerMaxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) - maxIndexRequest = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: innerOffsetId, offsetDate: hole.maxIndex.timestamp, addOffset: 0, limit: 1, maxId: innerMaxId, minId: 1, hash: 0)) - |> map(Optional.init) - } - case let .AroundId(id): - offsetId = id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 - case let .AroundIndex(index): - offsetId = index.id.id - addOffset = Int32(-selectedLimit / 2) - maxId = Int32.max - minId = 1 - } - - request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: hole.maxIndex.timestamp, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) - } - - return combineLatest(request |> retryRequest, maxIndexRequest |> retryRequest) - |> mapToSignal { result, maxIndexResult in - let messages: [Api.Message] - let chats: [Api.Chat] - let users: [Api.User] - var channelPts: Int32? - switch result { - case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - case let .messagesSlice(_, messages: apiMessages, chats: apiChats, users: apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers): - messages = apiMessages - chats = apiChats - users = apiUsers - channelPts = pts - case .messagesNotModified: - messages = [] - chats = [] - users = [] - } - var updatedMaxIndex: MessageIndex? - if let maxIndexResult = maxIndexResult { - let maxIndexMessages: [Api.Message] - switch maxIndexResult { - case let .messages(apiMessages, _, _): - maxIndexMessages = apiMessages - case let .messagesSlice(_, apiMessages, _, _): - maxIndexMessages = apiMessages - case let .channelMessages(_, _, _, apiMessages, _, _): - maxIndexMessages = apiMessages - case .messagesNotModified: - maxIndexMessages = [] - } - if !maxIndexMessages.isEmpty { - assert(maxIndexMessages.count == 1) - if let storeMessage = StoreMessage(apiMessage: maxIndexMessages[0]), case let .Id(id) = storeMessage.id { - updatedMaxIndex = MessageIndex(id: id, timestamp: storeMessage.timestamp) - } - } - } - - var storeMessages: [StoreMessage] = [] - - for message in messages { - if let storeMessage = StoreMessage(apiMessage: message) { - if let channelPts = channelPts { - var attributes = storeMessage.attributes - attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) - storeMessages.append(storeMessage.withUpdatedAttributes(attributes)) - } else { - storeMessages.append(storeMessage) - } - } - } - - return withResolvedAssociatedMessages(postbox: postbox, source: source, storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages in - let fillDirection: HoleFillDirection - switch direction { - case .UpperToLower: - fillDirection = .UpperToLower(updatedMinIndex: nil, clippingMaxIndex: nil) - case .LowerToUpper: - fillDirection = .LowerToUpper(updatedMaxIndex: updatedMaxIndex, clippingMinIndex: nil) - case let .AroundId(id): - fillDirection = .AroundId(id, lowerComplete: false, upperComplete: false) - case let .AroundIndex(index): - fillDirection = .AroundId(index.id, lowerComplete: false, upperComplete: false) - } - - var completeFill = messages.count == 0 || implicitelyFillHole - if tagMask == .liveLocation { - completeFill = false - } - transaction.fillMultipleHoles(hole, fillType: HoleFill(complete: completeFill, direction: fillDirection), tagMask: tagMask, messages: storeMessages) - let _ = transaction.addMessages(additionalMessages, location: .Random) - - var peers: [Peer] = additionalPeers - var peerPresences: [PeerId: PeerPresence] = [:] - for chat in chats { - if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { - peers.append(groupOrChannel) - } - } - for user in users { - let telegramUser = TelegramUser(user: user) - peers.append(telegramUser) - if let presence = TelegramUserPresence(apiUser: user) { - peerPresences[telegramUser.id] = presence - } - } - - updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in - return updated - }) - transaction.updatePeerPresences(peerPresences) - - print("fetchMessageHistoryHole for \(peer.displayTitle) done") - - return - }) - } + return postbox.stateView() + |> mapToSignal { view -> Signal in + if let state = view.state as? AuthorizedAccountState { + return .single(state) } else { return .complete() } } + |> take(1) + |> mapToSignal { _ -> Signal in + return postbox.loadedPeerWithId(hole.maxIndex.id.peerId) + |> take(1) + |> mapToSignal { peer in + if let inputPeer = forceApiInputPeer(peer) { + print("fetchMessageHistoryHole for \(peer.displayTitle) \(direction)") + let request: Signal + var maxIndexRequest: Signal = .single(nil) + var implicitelyFillHole = false + if let tagMask = tagMask { + if tagMask == MessageTags.unseenPersonalMessage { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = limit + let maxId: Int32 + let minId: Int32 + + switch direction { + case .UpperToLower: + offsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + addOffset = 0 + maxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + minId = 1 + case .LowerToUpper: + offsetId = hole.min <= 1 ? 1 : (hole.min - 1) + addOffset = Int32(-selectedLimit) + maxId = Int32.max + minId = hole.min - 1 + case let .AroundId(id): + offsetId = id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + case let .AroundIndex(index): + offsetId = index.id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + } + request = source.request(Api.functions.messages.getUnreadMentions(peer: inputPeer, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId)) + } else if tagMask == .liveLocation { + let selectedLimit = limit + + switch direction { + case .UpperToLower: + implicitelyFillHole = true + default: + assertionFailure() + } + request = source.request(Api.functions.messages.getRecentLocations(peer: inputPeer, limit: Int32(selectedLimit), hash: 0)) + } else if let filter = messageFilterForTagMask(tagMask) { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = limit + let maxId: Int32 + let minId: Int32 + + switch direction { + case .UpperToLower: + offsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + addOffset = 0 + maxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + minId = 1 + case .LowerToUpper: + offsetId = hole.min <= 1 ? 1 : (hole.min - 1) + addOffset = Int32(-selectedLimit) + maxId = Int32.max + minId = hole.min - 1 + case let .AroundId(id): + offsetId = id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + case let .AroundIndex(index): + offsetId = index.id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + } + request = source.request(Api.functions.messages.search(flags: 0, peer: inputPeer, q: "", fromId: nil, filter: filter, minDate: 0, maxDate: hole.maxIndex.timestamp, offsetId: offsetId, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) + } else { + assertionFailure() + request = .never() + } + } else { + let offsetId: Int32 + let addOffset: Int32 + let selectedLimit = limit + let maxId: Int32 + let minId: Int32 + + switch direction { + case .UpperToLower: + offsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + addOffset = 0 + maxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + minId = 1 + case .LowerToUpper: + offsetId = hole.min <= 1 ? 1 : (hole.min - 1) + addOffset = Int32(-selectedLimit) + maxId = Int32.max + minId = hole.min - 1 + if hole.maxIndex.timestamp == Int32.max { + let innerOffsetId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + let innerMaxId = hole.maxIndex.id.id == Int32.max ? hole.maxIndex.id.id : (hole.maxIndex.id.id + 1) + maxIndexRequest = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: innerOffsetId, offsetDate: hole.maxIndex.timestamp, addOffset: 0, limit: 1, maxId: innerMaxId, minId: 1, hash: 0)) + |> map(Optional.init) + } + case let .AroundId(id): + offsetId = id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + case let .AroundIndex(index): + offsetId = index.id.id + addOffset = Int32(-selectedLimit / 2) + maxId = Int32.max + minId = 1 + } + + request = source.request(Api.functions.messages.getHistory(peer: inputPeer, offsetId: offsetId, offsetDate: hole.maxIndex.timestamp, addOffset: addOffset, limit: Int32(selectedLimit), maxId: maxId, minId: minId, hash: 0)) + } + + return combineLatest(request |> retryRequest, maxIndexRequest |> retryRequest) + |> mapToSignal { result, maxIndexResult in + let messages: [Api.Message] + let chats: [Api.Chat] + let users: [Api.User] + var channelPts: Int32? + switch result { + case let .messages(messages: apiMessages, chats: apiChats, users: apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case let .messagesSlice(_, messages: apiMessages, chats: apiChats, users: apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + case let .channelMessages(_, pts, _, apiMessages, apiChats, apiUsers): + messages = apiMessages + chats = apiChats + users = apiUsers + channelPts = pts + case .messagesNotModified: + messages = [] + chats = [] + users = [] + } + var updatedMaxIndex: MessageIndex? + if let maxIndexResult = maxIndexResult { + let maxIndexMessages: [Api.Message] + switch maxIndexResult { + case let .messages(apiMessages, _, _): + maxIndexMessages = apiMessages + case let .messagesSlice(_, apiMessages, _, _): + maxIndexMessages = apiMessages + case let .channelMessages(_, _, _, apiMessages, _, _): + maxIndexMessages = apiMessages + case .messagesNotModified: + maxIndexMessages = [] + } + if !maxIndexMessages.isEmpty { + assert(maxIndexMessages.count == 1) + if let storeMessage = StoreMessage(apiMessage: maxIndexMessages[0]), case let .Id(id) = storeMessage.id { + updatedMaxIndex = MessageIndex(id: id, timestamp: storeMessage.timestamp) + } + } + } + + var storeMessages: [StoreMessage] = [] + + for message in messages { + if let storeMessage = StoreMessage(apiMessage: message) { + if let channelPts = channelPts { + var attributes = storeMessage.attributes + attributes.append(ChannelMessageStateVersionAttribute(pts: channelPts)) + storeMessages.append(storeMessage.withUpdatedAttributes(attributes)) + } else { + storeMessages.append(storeMessage) + } + } + } + + return withResolvedAssociatedMessages(postbox: postbox, source: source, storeMessages: storeMessages, { transaction, additionalPeers, additionalMessages in + let fillDirection: HoleFillDirection + switch direction { + case .UpperToLower: + fillDirection = .UpperToLower(updatedMinIndex: nil, clippingMaxIndex: nil) + case .LowerToUpper: + fillDirection = .LowerToUpper(updatedMaxIndex: updatedMaxIndex, clippingMinIndex: nil) + case let .AroundId(id): + fillDirection = .AroundId(id, lowerComplete: false, upperComplete: false) + case let .AroundIndex(index): + fillDirection = .AroundId(index.id, lowerComplete: false, upperComplete: false) + } + + var completeFill = messages.count == 0 || implicitelyFillHole + if tagMask == .liveLocation { + completeFill = false + } + transaction.fillMultipleHoles(hole, fillType: HoleFill(complete: completeFill, direction: fillDirection), tagMask: tagMask, messages: storeMessages) + let _ = transaction.addMessages(additionalMessages, location: .Random) + + var peers: [Peer] = additionalPeers + var peerPresences: [PeerId: PeerPresence] = [:] + for chat in chats { + if let groupOrChannel = parseTelegramGroupOrChannel(chat: chat) { + peers.append(groupOrChannel) + } + } + for user in users { + let telegramUser = TelegramUser(user: user) + peers.append(telegramUser) + if let presence = TelegramUserPresence(apiUser: user) { + peerPresences[telegramUser.id] = presence + } + } + + updatePeers(transaction: transaction, peers: peers, update: { _, updated -> Peer in + return updated + }) + transaction.updatePeerPresences(peerPresences) + + print("fetchMessageHistoryHole for \(peer.displayTitle) done") + + return + }) + } + } else { + return .complete() + } + } + } } func groupBoundaryPeer(_ peerId: PeerId, accountPeerId: PeerId) -> Api.Peer { diff --git a/TelegramCore/LocalizationInfo.swift b/TelegramCore/LocalizationInfo.swift index fd597e0407..4d0bb659e3 100644 --- a/TelegramCore/LocalizationInfo.swift +++ b/TelegramCore/LocalizationInfo.swift @@ -1,22 +1,22 @@ import Foundation #if os(macOS) -import PostboxMac + import PostboxMac #else -import Postbox + import Postbox #endif public final class LocalizationInfo: PostboxCoding { public let languageCode: String - public let nativeLanguageCode: String? + public let baseLanguageCode: String? public let title: String public let localizedTitle: String public let isOfficial: Bool public let totalStringCount: Int32 public let translatedStringCount: Int32 - public init(languageCode: String, nativeLanguageCode: String?, title: String, localizedTitle: String, isOfficial: Bool, totalStringCount: Int32, translatedStringCount: Int32) { + public init(languageCode: String, baseLanguageCode: String?, title: String, localizedTitle: String, isOfficial: Bool, totalStringCount: Int32, translatedStringCount: Int32) { self.languageCode = languageCode - self.nativeLanguageCode = nativeLanguageCode + self.baseLanguageCode = baseLanguageCode self.title = title self.localizedTitle = localizedTitle self.isOfficial = isOfficial @@ -26,7 +26,7 @@ public final class LocalizationInfo: PostboxCoding { public init(decoder: PostboxDecoder) { self.languageCode = decoder.decodeStringForKey("lc", orElse: "") - self.nativeLanguageCode = decoder.decodeOptionalStringForKey("nlc") + self.baseLanguageCode = decoder.decodeOptionalStringForKey("nlc") self.title = decoder.decodeStringForKey("t", orElse: "") self.localizedTitle = decoder.decodeStringForKey("lt", orElse: "") self.isOfficial = decoder.decodeInt32ForKey("of", orElse: 0) != 0 @@ -36,8 +36,8 @@ public final class LocalizationInfo: PostboxCoding { public func encode(_ encoder: PostboxEncoder) { encoder.encodeString(self.languageCode, forKey: "lc") - if let nativeLanguageCode = self.nativeLanguageCode { - encoder.encodeString(nativeLanguageCode, forKey: "nlc") + if let baseLanguageCode = self.baseLanguageCode { + encoder.encodeString(baseLanguageCode, forKey: "nlc") } else { encoder.encodeNil(forKey: "nlc") } @@ -52,8 +52,8 @@ public final class LocalizationInfo: PostboxCoding { extension LocalizationInfo { convenience init(apiLanguage: Api.LangPackLanguage) { switch apiLanguage { - case let .langPackLanguage(language): - self.init(languageCode: language.langCode, nativeLanguageCode: language.baseLangCode, title: language.name, localizedTitle: language.nativeName, isOfficial: (language.flags & (1 << 0)) != 0, totalStringCount: language.stringsCount, translatedStringCount: language.translatedCount) + case let .langPackLanguage(language): + self.init(languageCode: language.langCode, baseLanguageCode: nil/*language.baseLangCode*/, title: language.name, localizedTitle: language.nativeName, isOfficial: true/*(language.flags & (1 << 0)) != 0*/, totalStringCount: 1/*language.stringsCount*/, translatedStringCount: 1/*language.translatedCount*/) } } } diff --git a/TelegramCore/LocalizationPreview.swift b/TelegramCore/LocalizationPreview.swift new file mode 100644 index 0000000000..591850e81d --- /dev/null +++ b/TelegramCore/LocalizationPreview.swift @@ -0,0 +1,24 @@ +#if os(macOS) +import PostboxMac +import SwiftSignalKitMac +import MtProtoKitMac +#else +import Postbox +import SwiftSignalKit +import MtProtoKitDynamic +#endif + +public enum RequestLocalizationPreviewError { + case generic +} + +public func requestLocalizationPreview(postbox: Postbox, network: Network, identifier: String) -> Signal { + return .never() + /*return network.request(Api.functions.langpack.getLanguage(langPack: "", langCode: identifier)) + |> mapError { _ -> RequestLocalizationPreviewError in + return .generic + } + |> map { language -> LocalizationInfo in + return LocalizationInfo(apiLanguage: language) + }*/ +} diff --git a/TelegramCore/Localizations.swift b/TelegramCore/Localizations.swift index 1ee45e72f9..35cd4579bf 100644 --- a/TelegramCore/Localizations.swift +++ b/TelegramCore/Localizations.swift @@ -1,10 +1,10 @@ import Foundation #if os(macOS) -import PostboxMac -import SwiftSignalKitMac + import PostboxMac + import SwiftSignalKitMac #else -import Postbox -import SwiftSignalKit + import Postbox + import SwiftSignalKit #endif public func currentlySuggestedLocalization(network: Network, extractKeys: [String]) -> Signal { @@ -12,14 +12,14 @@ public func currentlySuggestedLocalization(network: Network, extractKeys: [Strin |> retryRequest |> mapToSignal { result -> Signal in switch result { - case let .config(config): - if let suggestedLangCode = config.suggestedLangCode { - return suggestedLocalizationInfo(network: network, languageCode: suggestedLangCode, extractKeys: extractKeys) |> map(Optional.init) - } else { - return .single(nil) - } + case let .config(config): + if let suggestedLangCode = config.suggestedLangCode { + return suggestedLocalizationInfo(network: network, languageCode: suggestedLangCode, extractKeys: extractKeys) |> map(Optional.init) + } else { + return .single(nil) + } } - } + } } public func suggestedLocalizationInfo(network: Network, languageCode: String, extractKeys: [String]) -> Signal { @@ -29,17 +29,17 @@ public func suggestedLocalizationInfo(network: Network, languageCode: String, ex var entries: [LocalizationEntry] = [] for string in strings { switch string { - case let .langPackString(key, value): - entries.append(.string(key: key, value: value)) - case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue): - entries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue)) - case let .langPackStringDeleted(key): - entries.append(.string(key: key, value: "")) + case let .langPackString(key, value): + entries.append(.string(key: key, value: value)) + case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue): + entries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue)) + case let .langPackStringDeleted(key): + entries.append(.string(key: key, value: "")) } } let infos: [LocalizationInfo] = languages.map(LocalizationInfo.init(apiLanguage:)) return SuggestedLocalizationInfo(languageCode: languageCode, extractedEntries: entries, availableLocalizations: infos) - } + } } final class CachedLocalizationInfos: PostboxCoding { @@ -66,18 +66,18 @@ public func availableLocalizations(postbox: Postbox, network: Network, allowCach return .single(entry.list) } return .complete() - } |> switchToLatest + } |> switchToLatest } else { cached = .complete() } let remote = network.request(Api.functions.langpack.getLanguages(langPack: "")) - |> retryRequest - |> mapToSignal { languages -> Signal<[LocalizationInfo], NoError> in - let infos: [LocalizationInfo] = languages.map(LocalizationInfo.init(apiLanguage:)) - return postbox.transaction { transaction -> [LocalizationInfo] in - transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedAvailableLocalizations, key: ValueBoxKey(length: 0)), entry: CachedLocalizationInfos(list: infos), collectionSpec: ItemCacheCollectionSpec(lowWaterItemCount: 1, highWaterItemCount: 1)) - return infos - } + |> retryRequest + |> mapToSignal { languages -> Signal<[LocalizationInfo], NoError> in + let infos: [LocalizationInfo] = languages.map(LocalizationInfo.init(apiLanguage:)) + return postbox.transaction { transaction -> [LocalizationInfo] in + transaction.putItemCacheEntry(id: ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.cachedAvailableLocalizations, key: ValueBoxKey(length: 0)), entry: CachedLocalizationInfos(list: infos), collectionSpec: ItemCacheCollectionSpec(lowWaterItemCount: 1, highWaterItemCount: 1)) + return infos + } } return cached |> then(remote) @@ -89,28 +89,28 @@ public enum DownloadLocalizationError { public func downloadLocalization(network: Network, languageCode: String) -> Signal { return network.request(Api.functions.langpack.getLangPack(langPack: "", langCode: languageCode)) - |> mapError { _ -> DownloadLocalizationError in - return .generic - } - |> map { result -> Localization in - let version: Int32 - var entries: [LocalizationEntry] = [] - switch result { + |> mapError { _ -> DownloadLocalizationError in + return .generic + } + |> map { result -> Localization in + let version: Int32 + var entries: [LocalizationEntry] = [] + switch result { case let .langPackDifference(_, _, versionValue, strings): version = versionValue for string in strings { switch string { - case let .langPackString(key, value): - entries.append(.string(key: key, value: value)) - case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue): - entries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue)) - case let .langPackStringDeleted(key): - entries.append(.string(key: key, value: "")) + case let .langPackString(key, value): + entries.append(.string(key: key, value: value)) + case let .langPackStringPluralized(_, key, zeroValue, oneValue, twoValue, fewValue, manyValue, otherValue): + entries.append(.pluralizedString(key: key, zero: zeroValue, one: oneValue, two: twoValue, few: fewValue, many: manyValue, other: otherValue)) + case let .langPackStringDeleted(key): + entries.append(.string(key: key, value: "")) } } - } - - return Localization(version: version, entries: entries) + } + + return Localization(version: version, entries: entries) } } @@ -120,29 +120,29 @@ public enum DownoadAndApplyLocalizationError { public func downoadAndApplyLocalization(postbox: Postbox, network: Network, languageCode: String) -> Signal { return downloadLocalization(network: network, languageCode: languageCode) - |> mapError { _ -> DownoadAndApplyLocalizationError in - return .generic + |> mapError { _ -> DownoadAndApplyLocalizationError in + return .generic + } + |> mapToSignal { language -> Signal in + return postbox.transaction { transaction -> Signal in + transaction.updatePreferencesEntry(key: PreferencesKeys.localizationSettings, { _ in + return LocalizationSettings(languageCode: languageCode, localization: language) + }) + + network.context.updateApiEnvironment { current in + return current?.withUpdatedLangPackCode(languageCode) + } + + return network.request(Api.functions.help.test()) + |> `catch` { _ -> Signal in + return .complete() + } + |> mapToSignal { _ -> Signal in + return .complete() + } + |> introduceError(DownoadAndApplyLocalizationError.self) } - |> mapToSignal { language -> Signal in - return postbox.transaction { transaction -> Signal in - transaction.updatePreferencesEntry(key: PreferencesKeys.localizationSettings, { _ in - return LocalizationSettings(languageCode: languageCode, localization: language) - }) - - network.context.updateApiEnvironment { current in - return current?.withUpdatedLangPackCode(languageCode) - } - - return network.request(Api.functions.help.test()) - |> `catch` { _ -> Signal in - return .complete() - } - |> mapToSignal { _ -> Signal in - return .complete() - } - |> introduceError(DownoadAndApplyLocalizationError.self) - } - |> introduceError(DownoadAndApplyLocalizationError.self) - |> switchToLatest + |> introduceError(DownoadAndApplyLocalizationError.self) + |> switchToLatest } } diff --git a/TelegramCore/ManagedRecentStickers.swift b/TelegramCore/ManagedRecentStickers.swift index a060c3a24a..a676e9e5be 100644 --- a/TelegramCore/ManagedRecentStickers.swift +++ b/TelegramCore/ManagedRecentStickers.swift @@ -44,21 +44,21 @@ private func managedRecentMedia(postbox: Postbox, network: Network, collectionId func managedRecentStickers(postbox: Postbox, network: Network) -> Signal { return managedRecentMedia(postbox: postbox, network: network, collectionId: Namespaces.OrderedItemList.CloudRecentStickers, reverseHashOrder: false, forceFetch: false, fetch: { hash in return network.request(Api.functions.messages.getRecentStickers(flags: 0, hash: hash)) - |> retryRequest - |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in - switch result { - case .recentStickersNotModified: - return .single(nil) - case let .recentStickers(_, _, stickers, _): - var items: [OrderedItemListEntry] = [] - for sticker in stickers { - if let file = telegramMediaFileFromApiDocument(sticker), let id = file.id { - items.append(OrderedItemListEntry(id: RecentMediaItemId(id).rawValue, contents: RecentMediaItem(file))) - } + |> retryRequest + |> mapToSignal { result -> Signal<[OrderedItemListEntry]?, NoError> in + switch result { + case .recentStickersNotModified: + return .single(nil) + case let .recentStickers(_, _, stickers, _): + var items: [OrderedItemListEntry] = [] + for sticker in stickers { + if let file = telegramMediaFileFromApiDocument(sticker), let id = file.id { + items.append(OrderedItemListEntry(id: RecentMediaItemId(id).rawValue, contents: RecentMediaItem(file))) } - return .single(items) - } + } + return .single(items) } + } }) } diff --git a/TelegramCore/ManagedSecretChatOutgoingOperations.swift b/TelegramCore/ManagedSecretChatOutgoingOperations.swift index ac0dddd9f8..9119f74253 100644 --- a/TelegramCore/ManagedSecretChatOutgoingOperations.swift +++ b/TelegramCore/ManagedSecretChatOutgoingOperations.swift @@ -1184,6 +1184,19 @@ private func sendMessage(postbox: Postbox, network: Network, messageId: MessageI }) maybeReadSecretOutgoingMessage(transaction: transaction, index: MessageIndex(id: message.id, timestamp: timestamp)) + + var sentStickers: [TelegramMediaFile] = [] + for media in message.media { + if let file = media as? TelegramMediaFile { + if file.isSticker { + sentStickers.append(file) + } + } + } + + for file in sentStickers { + transaction.addOrMoveToFirstPositionOrderedItemListItem(collectionId: Namespaces.OrderedItemList.CloudRecentStickers, item: OrderedItemListEntry(id: RecentMediaItemId(file.fileId).rawValue, contents: RecentMediaItem(file)), removeTailIfCountExceeds: 20) + } } } } else { diff --git a/TelegramCore/MultipartFetch.swift b/TelegramCore/MultipartFetch.swift index 48b3fcc38b..13bd0a328c 100644 --- a/TelegramCore/MultipartFetch.swift +++ b/TelegramCore/MultipartFetch.swift @@ -394,7 +394,7 @@ private final class MultipartFetchManager { let queue = Queue() - var currentRanges: IndexSet? + var currentIntervals: [(Range, MediaBoxFetchPriority)]? var currentFilledRanges = IndexSet() var completeSize: Int? @@ -409,6 +409,7 @@ private final class MultipartFetchManager { private var source: MultipartFetchSource var fetchingParts: [Int: (Int, Disposable)] = [:] + var nextFetchingPartId = 0 var fetchedParts: [Int: (Int, Data)] = [:] var cachedPartHashes: [Int: Data] = [:] @@ -423,7 +424,7 @@ private final class MultipartFetchManager { var rangesDisposable: Disposable? - init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int?, ranges: Signal, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int32?, location: MultipartFetchMasterLocation, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, partReady: @escaping (Int, Data) -> Void, reportCompleteSize: @escaping (Int) -> Void) { + init(resource: TelegramMediaResource, parameters: MediaResourceFetchParameters?, size: Int?, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, encryptionKey: SecretFileEncryptionKey?, decryptedSize: Int32?, location: MultipartFetchMasterLocation, postbox: Postbox, network: Network, revalidationContext: MediaReferenceRevalidationContext, partReady: @escaping (Int, Data) -> Void, reportCompleteSize: @escaping (Int) -> Void) { self.resource = resource self.parameters = parameters self.consumerId = arc4random64() @@ -447,14 +448,14 @@ private final class MultipartFetchManager { self.partReady = partReady self.reportCompleteSize = reportCompleteSize - self.rangesDisposable = (ranges |> deliverOn(self.queue)).start(next: { [weak self] ranges in + self.rangesDisposable = (intervals + |> deliverOn(self.queue)).start(next: { [weak self] intervals in if let strongSelf = self { - if let _ = strongSelf.currentRanges { - let updatedRanges = ranges.subtracting(strongSelf.currentFilledRanges) - strongSelf.currentRanges = updatedRanges + if let _ = strongSelf.currentIntervals { + strongSelf.currentIntervals = intervals strongSelf.checkState() } else { - strongSelf.currentRanges = ranges + strongSelf.currentIntervals = intervals strongSelf.checkState() } } @@ -486,18 +487,20 @@ private final class MultipartFetchManager { } func checkState() { - guard let currentRanges = self.currentRanges else { + guard let currentIntervals = self.currentIntervals else { return } - var rangesToFetch = currentRanges.subtracting(self.currentFilledRanges) - let isSingleContinuousRange = rangesToFetch.rangeView.count == 1 + + var removeFromFetchIntervals = self.currentFilledRanges + + let isSingleContiguousRange = currentIntervals.count == 1 for offset in self.fetchedParts.keys.sorted() { if let (_, data) = self.fetchedParts[offset] { let partRange = offset ..< (offset + data.count) - rangesToFetch.remove(integersIn: partRange) + removeFromFetchIntervals.insert(integersIn: partRange) var hasEarlierFetchingPart = false - if isSingleContinuousRange { + if isSingleContiguousRange { inner: for key in self.fetchingParts.keys { if key < offset { hasEarlierFetchingPart = true @@ -515,13 +518,26 @@ private final class MultipartFetchManager { } for (offset, (size, _)) in self.fetchingParts { - rangesToFetch.remove(integersIn: offset ..< (offset + size)) + removeFromFetchIntervals.insert(integersIn: offset ..< (offset + size)) } if let completeSize = self.completeSize { self.currentFilledRanges.insert(integersIn: completeSize ..< Int.max) - rangesToFetch.remove(integersIn: completeSize ..< Int.max) - if rangesToFetch.isEmpty && self.fetchingParts.isEmpty && !self.completeSizeReported { + removeFromFetchIntervals.insert(integersIn: completeSize ..< Int.max) + } + + var intervalsToFetch: [(Range, MediaBoxFetchPriority)] = [] + for (interval, priority) in currentIntervals { + var intervalIndexSet = IndexSet(integersIn: interval) + intervalIndexSet.subtract(removeFromFetchIntervals) + for cleanInterval in intervalIndexSet.rangeView { + assert(!cleanInterval.isEmpty) + intervalsToFetch.append((cleanInterval, priority)) + } + } + + if let completeSize = self.completeSize { + if intervalsToFetch.isEmpty && self.fetchingParts.isEmpty && !self.completeSizeReported { self.completeSizeReported = true assert(self.fetchedParts.isEmpty) if let decryptedSize = self.state.decryptedSize { @@ -532,110 +548,126 @@ private final class MultipartFetchManager { } } - while !rangesToFetch.isEmpty && self.fetchingParts.count < self.parallelParts && !self.reuploadingToCdn && !self.revalidatingMediaReference { - var selectedRange: (Range, Range)? - for range in rangesToFetch.rangeView { - var dataRange: Range = range.lowerBound ..< min(range.lowerBound + self.defaultPartSize, range.upperBound) - let rawRange: Range = dataRange - if dataRange.lowerBound % self.partAlignment != 0 { - let previousBoundary = (dataRange.lowerBound / self.partAlignment) * self.partAlignment - dataRange = previousBoundary ..< dataRange.upperBound + while !intervalsToFetch.isEmpty && self.fetchingParts.count < self.parallelParts && !self.reuploadingToCdn && !self.revalidatingMediaReference { + var elevatedIndices: [Int] = [] + for i in 0 ..< intervalsToFetch.count { + if case .elevated = intervalsToFetch[i].1 { + elevatedIndices.append(i) } - if dataRange.lowerBound / (1024 * 1024) != (dataRange.upperBound - 1) / (1024 * 1024) { - let nextBoundary = (dataRange.lowerBound / (1024 * 1024) + 1) * (1024 * 1024) - dataRange = dataRange.lowerBound ..< nextBoundary - } - selectedRange = (rawRange, dataRange) - break } - if let (rawRange, downloadRange) = selectedRange { - rangesToFetch.remove(integersIn: downloadRange) - var requestLimit = downloadRange.count - if requestLimit % self.partAlignment != 0 { - requestLimit = (requestLimit / self.partAlignment + 1) * self.partAlignment - } - - let part = self.source.request(offset: Int32(downloadRange.lowerBound), limit: Int32(requestLimit), tag: self.parameters?.tag, fileReference: self.fileReference) - |> deliverOn(self.queue) - let partDisposable = MetaDisposable() - self.fetchingParts[downloadRange.lowerBound] = (downloadRange.count, partDisposable) - - partDisposable.set(part.start(next: { [weak self] data in - if let strongSelf = self { - var data = data - if data.count < downloadRange.count { - strongSelf.completeSize = downloadRange.lowerBound + data.count - } - let _ = strongSelf.fetchingParts.removeValue(forKey: downloadRange.lowerBound) - strongSelf.fetchedParts[downloadRange.lowerBound] = (rawRange.lowerBound, data) - strongSelf.checkState() - } - }, error: { [weak self] error in - if let strongSelf = self { - let _ = strongSelf.fetchingParts.removeValue(forKey: downloadRange.lowerBound) - switch error { - case .generic: - break - case .revalidateMediaReference: - if !strongSelf.revalidatingMediaReference && !strongSelf.revalidatedMediaReference { - strongSelf.revalidatingMediaReference = true - if let info = strongSelf.parameters?.info as? TelegramCloudMediaResourceFetchInfo { - strongSelf.revalidateMediaReferenceDisposable.set((revalidateMediaResourceReference(postbox: strongSelf.postbox, network: strongSelf.network, revalidationContext: strongSelf.revalidationContext, info: info, resource: strongSelf.resource) - |> deliverOn(strongSelf.queue)).start(next: { fileReference in - if let strongSelf = self { - strongSelf.revalidatingMediaReference = false - strongSelf.revalidatedMediaReference = true - strongSelf.fileReference = fileReference - strongSelf.checkState() - } - }, error: { _ in - if let strongSelf = self { - } - })) - } else { - Logger.shared.log("MultipartFetch", "reference invalidation requested, but no valid reference given") - } - } - case let .switchToCdn(id, token, key, iv, partHashes): - switch strongSelf.source { - case let .master(location, download): - strongSelf.source = .cdn(masterDatacenterId: location.datacenterId, fileToken: token, key: key, iv: iv, download: DownloadWrapper(consumerId: strongSelf.consumerId, datacenterId: id, isCdn: true, network: strongSelf.network), masterDownload: download, hashSource: MultipartCdnHashSource(queue: strongSelf.queue, fileToken: token, hashes: partHashes, masterDownload: download)) - strongSelf.checkState() - case .cdn, .none: - break - } - case let .reuploadToCdn(_, token): - switch strongSelf.source { - case .master, .none: - break - case let .cdn(_, fileToken, _, _, _, masterDownload, _): - if !strongSelf.reuploadingToCdn { - strongSelf.reuploadingToCdn = true - let reupload: Signal<[Api.FileHash], NoError> = masterDownload.request(Api.functions.upload.reuploadCdnFile(fileToken: Buffer(data: fileToken), requestToken: Buffer(data: token)), tag: nil) - |> `catch` { _ -> Signal<[Api.FileHash], NoError> in - return .single([]) - } - strongSelf.reuploadToCdnDisposable.set((reupload |> deliverOn(strongSelf.queue)).start(next: { _ in - if let strongSelf = self { - strongSelf.reuploadingToCdn = false - strongSelf.checkState() - } - })) - } - } - case .hashesMissing: - break - } - } - })) + + let currentIntervalIndex: Int + if !elevatedIndices.isEmpty { + currentIntervalIndex = elevatedIndices[self.nextFetchingPartId % elevatedIndices.count] } else { - break + currentIntervalIndex = self.nextFetchingPartId % intervalsToFetch.count } + self.nextFetchingPartId += 1 + let (firstInterval, priority) = intervalsToFetch[currentIntervalIndex] + var downloadRange: Range = firstInterval.lowerBound ..< min(firstInterval.lowerBound + self.defaultPartSize, firstInterval.upperBound) + let rawRange: Range = downloadRange + if downloadRange.lowerBound % self.partAlignment != 0 { + let previousBoundary = (downloadRange.lowerBound / self.partAlignment) * self.partAlignment + downloadRange = previousBoundary ..< downloadRange.upperBound + } + if downloadRange.lowerBound / (1024 * 1024) != (downloadRange.upperBound - 1) / (1024 * 1024) { + let nextBoundary = (downloadRange.lowerBound / (1024 * 1024) + 1) * (1024 * 1024) + downloadRange = downloadRange.lowerBound ..< nextBoundary + } + + var intervalIndexSet = IndexSet(integersIn: intervalsToFetch[currentIntervalIndex].0) + intervalIndexSet.remove(integersIn: downloadRange) + intervalsToFetch.remove(at: currentIntervalIndex) + var insertIndex = currentIntervalIndex + for interval in intervalIndexSet.rangeView { + intervalsToFetch.insert((interval, priority), at: insertIndex) + insertIndex += 1 + } + var requestLimit = downloadRange.count + if requestLimit % self.partAlignment != 0 { + requestLimit = (requestLimit / self.partAlignment + 1) * self.partAlignment + } + + let part = self.source.request(offset: Int32(downloadRange.lowerBound), limit: Int32(requestLimit), tag: self.parameters?.tag, fileReference: self.fileReference) + |> deliverOn(self.queue) + let partDisposable = MetaDisposable() + self.fetchingParts[downloadRange.lowerBound] = (downloadRange.count, partDisposable) + + partDisposable.set(part.start(next: { [weak self] data in + guard let strongSelf = self else { + return + } + var data = data + if data.count < downloadRange.count { + strongSelf.completeSize = downloadRange.lowerBound + data.count + } + let _ = strongSelf.fetchingParts.removeValue(forKey: downloadRange.lowerBound) + strongSelf.fetchedParts[downloadRange.lowerBound] = (rawRange.lowerBound, data) + strongSelf.checkState() + }, error: { [weak self] error in + guard let strongSelf = self else { + return + } + let _ = strongSelf.fetchingParts.removeValue(forKey: downloadRange.lowerBound) + switch error { + case .generic: + break + case .revalidateMediaReference: + if !strongSelf.revalidatingMediaReference && !strongSelf.revalidatedMediaReference { + strongSelf.revalidatingMediaReference = true + if let info = strongSelf.parameters?.info as? TelegramCloudMediaResourceFetchInfo { + strongSelf.revalidateMediaReferenceDisposable.set((revalidateMediaResourceReference(postbox: strongSelf.postbox, network: strongSelf.network, revalidationContext: strongSelf.revalidationContext, info: info, resource: strongSelf.resource) + |> deliverOn(strongSelf.queue)).start(next: { fileReference in + if let strongSelf = self { + strongSelf.revalidatingMediaReference = false + strongSelf.revalidatedMediaReference = true + strongSelf.fileReference = fileReference + strongSelf.checkState() + } + }, error: { _ in + if let strongSelf = self { + } + })) + } else { + Logger.shared.log("MultipartFetch", "reference invalidation requested, but no valid reference given") + } + } + case let .switchToCdn(id, token, key, iv, partHashes): + switch strongSelf.source { + case let .master(location, download): + strongSelf.source = .cdn(masterDatacenterId: location.datacenterId, fileToken: token, key: key, iv: iv, download: DownloadWrapper(consumerId: strongSelf.consumerId, datacenterId: id, isCdn: true, network: strongSelf.network), masterDownload: download, hashSource: MultipartCdnHashSource(queue: strongSelf.queue, fileToken: token, hashes: partHashes, masterDownload: download)) + strongSelf.checkState() + case .cdn, .none: + break + } + case let .reuploadToCdn(_, token): + switch strongSelf.source { + case .master, .none: + break + case let .cdn(_, fileToken, _, _, _, masterDownload, _): + if !strongSelf.reuploadingToCdn { + strongSelf.reuploadingToCdn = true + let reupload: Signal<[Api.FileHash], NoError> = masterDownload.request(Api.functions.upload.reuploadCdnFile(fileToken: Buffer(data: fileToken), requestToken: Buffer(data: token)), tag: nil) + |> `catch` { _ -> Signal<[Api.FileHash], NoError> in + return .single([]) + } + strongSelf.reuploadToCdnDisposable.set((reupload |> deliverOn(strongSelf.queue)).start(next: { _ in + if let strongSelf = self { + strongSelf.reuploadingToCdn = false + strongSelf.checkState() + } + })) + } + } + case .hashesMissing: + break + } + })) } } } -func multipartFetch(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int?, ranges: Signal, parameters: MediaResourceFetchParameters?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int32? = nil) -> Signal { +func multipartFetch(account: Account, resource: TelegramMediaResource, datacenterId: Int, size: Int?, intervals: Signal<[(Range, MediaBoxFetchPriority)], NoError>, parameters: MediaResourceFetchParameters?, encryptionKey: SecretFileEncryptionKey? = nil, decryptedSize: Int32? = nil) -> Signal { return Signal { subscriber in let location: MultipartFetchMasterLocation if let resource = resource as? TelegramCloudMediaResource { @@ -653,7 +685,7 @@ func multipartFetch(account: Account, resource: TelegramMediaResource, datacente subscriber.putNext(.reset) } - let manager = MultipartFetchManager(resource: resource, parameters: parameters, size: size, ranges: ranges, encryptionKey: encryptionKey, decryptedSize: decryptedSize, location: location, postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, partReady: { dataOffset, data in + let manager = MultipartFetchManager(resource: resource, parameters: parameters, size: size, intervals: intervals, encryptionKey: encryptionKey, decryptedSize: decryptedSize, location: location, postbox: account.postbox, network: account.network, revalidationContext: account.mediaReferenceRevalidationContext, partReady: { dataOffset, data in subscriber.putNext(.dataPart(resourceOffset: dataOffset, data: data, range: 0 ..< data.count, complete: false)) }, reportCompleteSize: { size in subscriber.putNext(.resourceSizeUpdated(size)) diff --git a/TelegramCore/Namespaces.swift b/TelegramCore/Namespaces.swift index e83bccfbdc..f3f336d893 100644 --- a/TelegramCore/Namespaces.swift +++ b/TelegramCore/Namespaces.swift @@ -127,6 +127,12 @@ public struct OperationLogTags { static let SynchronizeMarkAllUnseenPersonalMessages = PeerOperationLogTag(value: 16) } +public extension PeerSummaryCounterTags { + public static let regularChatsAndPrivateGroups = PeerSummaryCounterTags(rawValue: 1 << 0) + public static let publicGroups = PeerSummaryCounterTags(rawValue: 1 << 1) + public static let channels = PeerSummaryCounterTags(rawValue: 1 << 2) +} + private enum PreferencesKeyValues: Int32 { case globalNotifications = 0 case cacheStorageSettings = 1 diff --git a/TelegramCore/PendingMessageManager.swift b/TelegramCore/PendingMessageManager.swift index 974ff3fe27..ab081d51b9 100644 --- a/TelegramCore/PendingMessageManager.swift +++ b/TelegramCore/PendingMessageManager.swift @@ -748,7 +748,7 @@ public final class PendingMessageManager { |> switchToLatest } - private static func sendSecretMessageContent(transaction: Transaction, message: Message, content: PendingMessageUploadedContentAndReuploadInfo) { + static func sendSecretMessageContent(transaction: Transaction, message: Message, content: PendingMessageUploadedContentAndReuploadInfo) { var secretFile: SecretChatOutgoingFile? switch content.content { case let .secretMedia(file, size, key): diff --git a/TelegramCore/PendingMessageUploadedContent.swift b/TelegramCore/PendingMessageUploadedContent.swift index af07ce1907..b3245a26ca 100644 --- a/TelegramCore/PendingMessageUploadedContent.swift +++ b/TelegramCore/PendingMessageUploadedContent.swift @@ -82,7 +82,10 @@ func messageContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods } func mediaContentToUpload(network: Network, postbox: Postbox, auxiliaryMethods: AccountAuxiliaryMethods, transformOutgoingMessageMedia: TransformOutgoingMessageMedia?, messageMediaPreuploadManager: MessageMediaPreuploadManager, revalidationContext: MediaReferenceRevalidationContext, forceReupload: Bool, peerId: PeerId, media: Media, text: String, autoremoveAttribute: AutoremoveTimeoutMessageAttribute?, messageId: MessageId?, attributes: [MessageAttribute]) -> Signal? { - if let image = media as? TelegramMediaImage, let _ = largestImageRepresentation(image.representations) { + if let image = media as? TelegramMediaImage, let largest = largestImageRepresentation(image.representations) { + if peerId.namespace == Namespaces.Peer.SecretChat, let resource = largest.resource as? SecretFileMediaResource { + return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .secretMedia(.inputEncryptedFile(id: resource.fileId, accessHash: resource.accessHash), resource.decryptedSize, resource.key), reuploadInfo: nil))) + } if peerId.namespace != Namespaces.Peer.SecretChat, let reference = image.reference, case let .cloud(id, accessHash, maybeFileReference) = reference, let fileReference = maybeFileReference { return .single(.content(PendingMessageUploadedContentAndReuploadInfo(content: .media(Api.InputMedia.inputMediaPhoto(flags: 0, id: Api.InputPhoto.inputPhoto(id: id, accessHash: accessHash, fileReference: Buffer(data: fileReference)), ttlSeconds: nil), text), reuploadInfo: nil))) } else { diff --git a/TelegramCore/StandaloneUploadedMedia.swift b/TelegramCore/StandaloneUploadedMedia.swift index 820252b199..3c29ea1cbf 100644 --- a/TelegramCore/StandaloneUploadedMedia.swift +++ b/TelegramCore/StandaloneUploadedMedia.swift @@ -1,9 +1,11 @@ import Foundation #if os(macOS) import PostboxMac + import TelegramCoreMac import SwiftSignalKitMac #else import Postbox + import TelegramCore import SwiftSignalKit #endif @@ -11,13 +13,23 @@ public enum StandaloneUploadMediaError { case generic } -public enum StandaloneUploadMediaEvent { - case progress(Float) - case result(AnyMediaReference) +public struct StandaloneUploadSecretFile { + let file: Api.InputEncryptedFile + let size: Int32 + let key: SecretFileEncryptionKey } -public func standaloneUploadedImage(account: Account, peerId: PeerId, text: String, data: Data) -> Signal { - return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: false, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false) +public enum StandaloneUploadMediaResult { + case media(AnyMediaReference) +} + +public enum StandaloneUploadMediaEvent { + case progress(Float) + case result(StandaloneUploadMediaResult) +} + +public func standaloneUploadedImage(account: Account, peerId: PeerId, text: String, data: Data, dimensions: CGSize) -> Signal { + return multipartUpload(network: account.network, postbox: account.postbox, source: .data(data), encrypt: peerId.namespace == Namespaces.Peer.SecretChat, tag: TelegramMediaResourceFetchTag(statsCategory: .image), hintFileSize: nil, hintFileIsLarge: false) |> mapError { _ -> StandaloneUploadMediaError in return .generic } |> mapToSignal { next -> Signal in switch next { @@ -35,7 +47,7 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri case let .messageMediaPhoto(_, photo, _): if let photo = photo { if let mediaImage = telegramMediaImageFromApiPhoto(photo) { - return .single(.result(.standalone(media: mediaImage))) + return .single(.result(.media(.standalone(media: mediaImage)))) } } default: @@ -47,8 +59,30 @@ public func standaloneUploadedImage(account: Account, peerId: PeerId, text: Stri return .fail(.generic) } } - case .inputSecretFile: - preconditionFailure() + case let .inputSecretFile(file, _, key): + return account.postbox.transaction { transaction -> Api.InputEncryptedChat? in + if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { + return Api.InputEncryptedChat.inputEncryptedChat(chatId: peer.id.id, accessHash: peer.accessHash) + } + return nil + } + |> introduceError(StandaloneUploadMediaError.self) + |> mapToSignal { inputChat -> Signal in + guard let inputChat = inputChat else { + return .fail(.generic) + } + return account.network.request(Api.functions.messages.uploadEncryptedFile(peer: inputChat, file: file)) + |> mapError { _ -> StandaloneUploadMediaError in return .generic + } + |> mapToSignal { result -> Signal in + switch result { + case let .encryptedFile(id, accessHash, size, dcId, _): + return .single(.result(.media(.standalone(media: TelegramMediaImage(imageId: MediaId(namespace: Namespaces.Media.LocalImage, id: arc4random64()), representations: [TelegramMediaImageRepresentation(dimensions: dimensions, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: Int32(data.count), datacenterId: Int(dcId), key: key))], reference: nil, partialReference: nil))))) + case .encryptedFileEmpty: + return .fail(.generic) + } + } + } case let .progress(progress): return .single(.progress(progress)) } @@ -74,7 +108,7 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin case let .messageMediaDocument(_, document, _): if let document = document { if let mediaFile = telegramMediaFileFromApiDocument(document) { - return .single(.result(.standalone(media: mediaFile))) + return .single(.result(.media(.standalone(media: mediaFile)))) } } default: @@ -86,8 +120,32 @@ public func standaloneUploadedFile(account: Account, peerId: PeerId, text: Strin return .fail(.generic) } } - case .inputSecretFile: - preconditionFailure() + case let .inputSecretFile(file, size, key): + return account.postbox.transaction { transaction -> Api.InputEncryptedChat? in + if let peer = transaction.getPeer(peerId) as? TelegramSecretChat { + return Api.InputEncryptedChat.inputEncryptedChat(chatId: peer.id.id, accessHash: peer.accessHash) + } + return nil + } + |> introduceError(StandaloneUploadMediaError.self) + |> mapToSignal { inputChat -> Signal in + guard let inputChat = inputChat else { + return .fail(.generic) + } + return account.network.request(Api.functions.messages.uploadEncryptedFile(peer: inputChat, file: file)) + |> mapError { _ -> StandaloneUploadMediaError in return .generic + } + |> mapToSignal { result -> Signal in + switch result { + case let .encryptedFile(id, accessHash, size, dcId, _): + let media = TelegramMediaFile(fileId: MediaId(namespace: Namespaces.Media.LocalFile, id: arc4random64()), partialReference: nil, resource: SecretFileMediaResource(fileId: id, accessHash: accessHash, containerSize: size, decryptedSize: size, datacenterId: Int(dcId), key: key), previewRepresentations: [], mimeType: mimeType, size: Int(size), attributes: attributes) + + return .single(.result(.media(.standalone(media: media)))) + case .encryptedFileEmpty: + return .fail(.generic) + } + } + } case let .progress(progress): return .single(.progress(progress)) } diff --git a/TelegramCore/StringFormat.swift b/TelegramCore/StringFormat.swift index 4d529c3679..0165737708 100644 --- a/TelegramCore/StringFormat.swift +++ b/TelegramCore/StringFormat.swift @@ -1,25 +1,25 @@ -public func dataSizeString(_ size: Int) -> String { - return dataSizeString(Int64(size)) +public func dataSizeString(_ size: Int, forceDecimal: Bool = false) -> String { + return dataSizeString(Int64(size), forceDecimal: forceDecimal) } -public func dataSizeString(_ size: Int64) -> String { +public func dataSizeString(_ size: Int64, forceDecimal: Bool = false) -> String { if size >= 1024 * 1024 * 1024 { let remainder = (size % (1024 * 1024 * 1024)) / (1024 * 1024 * 102) - if remainder != 0 { + if remainder != 0 || forceDecimal { return "\(size / (1024 * 1024 * 1024)),\(remainder) GB" } else { return "\(size / (1024 * 1024 * 1024)) GB" } } else if size >= 1024 * 1024 { let remainder = (size % (1024 * 1024)) / (1024 * 102) - if remainder != 0 { + if remainder != 0 || forceDecimal { return "\(size / (1024 * 1024)),\(remainder) MB" } else { return "\(size / (1024 * 1024)) MB" } } else if size >= 1024 { let remainder = (size % (1024)) / (102) - if remainder != 0 { + if remainder != 0 || forceDecimal { return "\(size / 1024),\(remainder) KB" } else { return "\(size / 1024) KB" diff --git a/TelegramCore/TelegramMediaAction.swift b/TelegramCore/TelegramMediaAction.swift index d7e31ce19f..2d3142c99b 100644 --- a/TelegramCore/TelegramMediaAction.swift +++ b/TelegramCore/TelegramMediaAction.swift @@ -48,6 +48,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case customText(text: String, entities: [MessageTextEntity]) case botDomainAccessGranted(domain: String) case botSentSecureValues(types: [SentSecureValueType]) + case peerJoined public init(decoder: PostboxDecoder) { let rawValue: Int32 = decoder.decodeInt32ForKey("_rawValue", orElse: 0) @@ -94,6 +95,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { self = .botSentSecureValues(types: decoder.decodeInt32ArrayForKey("ty").map { value -> SentSecureValueType in return SentSecureValueType(rawValue: value) ?? .personalDetails }) + case 19: + self = .peerJoined default: self = .unknown } @@ -174,6 +177,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { case let .botSentSecureValues(types): encoder.encodeInt32(18, forKey: "_rawValue") encoder.encodeInt32Array(types.map { $0.rawValue }, forKey: "ty") + case .peerJoined: + encoder.encodeInt32(19, forKey: "_rawValue") } } @@ -195,126 +200,6 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable { } } -public func ==(lhs: TelegramMediaActionType, rhs: TelegramMediaActionType) -> Bool { - switch lhs { - case .unknown: - if case .unknown = rhs { - return true - } - case let .groupCreated(lhsTitle): - if case let .groupCreated(rhsTitle) = rhs, lhsTitle == rhsTitle { - return true - } - case let .addedMembers(peerIds): - if case let .addedMembers(rhsPeerIds) = rhs { - if peerIds.count == rhsPeerIds.count { - for i in 0 ..< peerIds.count { - if peerIds[i] != rhsPeerIds[i] { - return false - } - } - return true - } - } - case let .removedMembers(peerIds): - if case let .removedMembers(rhsPeerIds) = rhs { - if peerIds.count == rhsPeerIds.count { - for i in 0 ..< peerIds.count { - if peerIds[i] != rhsPeerIds[i] { - return false - } - } - return true - } - } - case let .photoUpdated(image): - if case let .photoUpdated(rhsImage) = rhs { - if let image = image { - if let rhsImage = rhsImage { - return image == rhsImage - } else { - return false - } - } else { - return rhsImage == nil - } - } - case let .titleUpdated(title): - if case .titleUpdated(title) = rhs { - return true - } - case .pinnedMessageUpdated: - if case .pinnedMessageUpdated = rhs { - return true - } - case let .joinedByLink(inviter): - if case .joinedByLink(inviter) = rhs { - return true - } - case let .channelMigratedFromGroup(title, groupId): - if case .channelMigratedFromGroup(title, groupId) = rhs { - return true - } - case let .groupMigratedToChannel(channelId): - if case .groupMigratedToChannel(channelId) = rhs { - return true - } - case .historyCleared: - if case .historyCleared = rhs { - return true - } - case .historyScreenshot: - if case .historyScreenshot = rhs { - return true - } else { - return false - } - case let .messageAutoremoveTimeoutUpdated(timeout): - if case .messageAutoremoveTimeoutUpdated(timeout) = rhs { - return true - } else { - return false - } - case let .gameScore(gameId, score): - if case .gameScore(gameId, score) = rhs { - return true - } else { - return false - } - case let .paymentSent(currency, totalAmount): - if case .paymentSent(currency, totalAmount) = rhs { - return true - } else { - return false - } - case let .phoneCall(lhsCallId, lhsDiscardReason, lhsDuration): - if case let .phoneCall(rhsCallId, rhsDiscardReason, rhsDuration) = rhs, lhsCallId == rhsCallId && lhsDiscardReason == rhsDiscardReason && lhsDuration == rhsDuration { - return true - } else { - return false - } - case let .customText(lhsText, lhsEntities): - if case let .customText(rhsText, rhsEntities) = rhs, lhsText == rhsText, lhsEntities == rhsEntities { - return true - } else { - return false - } - case let .botDomainAccessGranted(domain): - if case .botDomainAccessGranted(domain) = rhs { - return true - } else { - return false - } - case let .botSentSecureValues(lhsTypes): - if case let .botSentSecureValues(rhsTypes) = rhs, lhsTypes == rhsTypes { - return true - } else { - return false - } - } - return false -} - public final class TelegramMediaAction: Media { public let id: MediaId? = nil public var peerIds: [PeerId] { diff --git a/TelegramCore/TwoStepVerification.swift b/TelegramCore/TwoStepVerification.swift index 4b4f845bd7..119f46d4f4 100644 --- a/TelegramCore/TwoStepVerification.swift +++ b/TelegramCore/TwoStepVerification.swift @@ -94,9 +94,14 @@ public enum UpdateTwoStepVerificationPasswordError { case invalidEmail } +public struct TwoStepVerificationPendingEmail { + public let pattern: String + public let codeLength: Int32? +} + public enum UpdateTwoStepVerificationPasswordResult { case none - case password(password: String, pendingEmailPattern: String?) + case password(password: String, pendingEmail: TwoStepVerificationPendingEmail?) } public enum UpdatedTwoStepVerificationPassword { @@ -188,13 +193,19 @@ public func updateTwoStepVerificationPassword(network: Network, currentPassword: return network.request(Api.functions.account.updatePasswordSettings(password: checkPassword, newSettings: Api.account.PasswordInputSettings.passwordInputSettings(flags: flags, newAlgo: updatedPasswordDerivation.apiAlgo, newPasswordHash: Buffer(data: updatedPasswordHash), hint: hint, email: email, newSecureSettings: updatedSecureSettings)), automaticFloodWait: false) |> map { _ -> UpdateTwoStepVerificationPasswordResult in - return .password(password: password, pendingEmailPattern: nil) + return .password(password: password, pendingEmail: nil) } |> `catch` { error -> Signal in - if error.errorDescription == "EMAIL_UNCONFIRMED" { + if error.errorDescription.hasPrefix("EMAIL_UNCONFIRMED") { + var codeLength: Int32? + if error.errorDescription.hasPrefix("EMAIL_UNCONFIRMED_") { + if let value = Int32(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "EMAIL_UNCONFIRMED_".count)...]) { + codeLength = value + } + } return twoStepAuthData(network) |> map { result -> UpdateTwoStepVerificationPasswordResult in - return .password(password: password, pendingEmailPattern: result.unconfirmedEmailPattern) + return .password(password: password, pendingEmail: result.unconfirmedEmailPattern.flatMap({ TwoStepVerificationPendingEmail(pattern: $0, codeLength: codeLength) })) } } else { return .fail(error) @@ -269,13 +280,19 @@ public func updateTwoStepVerificationEmail(account: Account, currentPassword: St let flags: Int32 = 1 << 1 return account.network.request(Api.functions.account.updatePasswordSettings(password: checkPassword, newSettings: Api.account.PasswordInputSettings.passwordInputSettings(flags: flags, newAlgo: nil, newPasswordHash: nil, hint: nil, email: updatedEmail, newSecureSettings: nil)), automaticFloodWait: false) |> map { _ -> UpdateTwoStepVerificationPasswordResult in - return .password(password: currentPassword, pendingEmailPattern: nil) + return .password(password: currentPassword, pendingEmail: nil) } |> `catch` { error -> Signal in - if error.errorDescription == "EMAIL_UNCONFIRMED" { + if error.errorDescription.hasPrefix("EMAIL_UNCONFIRMED") { return twoStepAuthData(account.network) - |> map { result -> UpdateTwoStepVerificationPasswordResult in - return .password(password: currentPassword, pendingEmailPattern: result.unconfirmedEmailPattern) + |> map { result -> UpdateTwoStepVerificationPasswordResult in + var codeLength: Int32? + if error.errorDescription.hasPrefix("EMAIL_UNCONFIRMED_") { + if let value = Int32(error.errorDescription[error.errorDescription.index(error.errorDescription.startIndex, offsetBy: "EMAIL_UNCONFIRMED_".count)...]) { + codeLength = value + } + } + return .password(password: currentPassword, pendingEmail: result.unconfirmedEmailPattern.flatMap({ TwoStepVerificationPendingEmail(pattern: $0, codeLength: codeLength) })) } } else { return .fail(error) diff --git a/TelegramCore/UpdateCachedPeerData.swift b/TelegramCore/UpdateCachedPeerData.swift index 79b382fe25..9560e22a8f 100644 --- a/TelegramCore/UpdateCachedPeerData.swift +++ b/TelegramCore/UpdateCachedPeerData.swift @@ -53,48 +53,48 @@ func fetchAndUpdateSupplementalCachedPeerData(peerId: PeerId, network: Network, } } else if let inputPeer = apiInputPeer(peer) { return network.request(Api.functions.messages.getPeerSettings(peer: inputPeer)) - |> retryRequest - |> mapToSignal { peerSettings -> Signal in - let reportStatus: PeerReportStatus - switch peerSettings { - case let .peerSettings(flags): - reportStatus = (flags & (1 << 0) != 0) ? .canReport : .none - } - - return postbox.transaction { transaction -> Void in - transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in - switch peerId.namespace { - case Namespaces.Peer.CloudUser: - let previous: CachedUserData - if let current = current as? CachedUserData { - previous = current - } else { - previous = CachedUserData() - } - return previous.withUpdatedReportStatus(reportStatus) - case Namespaces.Peer.CloudGroup: - let previous: CachedGroupData - if let current = current as? CachedGroupData { - previous = current - } else { - previous = CachedGroupData() - } - return previous.withUpdatedReportStatus(reportStatus) - case Namespaces.Peer.CloudChannel: - let previous: CachedChannelData - if let current = current as? CachedChannelData { - previous = current - } else { - previous = CachedChannelData() - } - return previous.withUpdatedReportStatus(reportStatus) - default: - break - } - return current - }) - } + |> retryRequest + |> mapToSignal { peerSettings -> Signal in + let reportStatus: PeerReportStatus + switch peerSettings { + case let .peerSettings(flags): + reportStatus = (flags & (1 << 0) != 0) ? .canReport : .none } + + return postbox.transaction { transaction -> Void in + transaction.updatePeerCachedData(peerIds: Set([peerId]), update: { _, current in + switch peerId.namespace { + case Namespaces.Peer.CloudUser: + let previous: CachedUserData + if let current = current as? CachedUserData { + previous = current + } else { + previous = CachedUserData() + } + return previous.withUpdatedReportStatus(reportStatus) + case Namespaces.Peer.CloudGroup: + let previous: CachedGroupData + if let current = current as? CachedGroupData { + previous = current + } else { + previous = CachedGroupData() + } + return previous.withUpdatedReportStatus(reportStatus) + case Namespaces.Peer.CloudChannel: + let previous: CachedChannelData + if let current = current as? CachedChannelData { + previous = current + } else { + previous = CachedChannelData() + } + return previous.withUpdatedReportStatus(reportStatus) + default: + break + } + return current + }) + } + } } else { return .complete() } @@ -226,9 +226,16 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos } } else if let inputChannel = apiInputChannel(peer) { return network.request(Api.functions.channels.getFullChannel(channel: inputChannel)) - |> retryRequest - |> mapToSignal { result -> Signal in - return postbox.transaction { transaction -> Void in + |> map(Optional.init) + |> `catch` { error -> Signal in + if error.errorDescription == "CHANNEL_PRIVATE" { + return .single(nil) + } + return .complete() + } + |> mapToSignal { result -> Signal in + return postbox.transaction { transaction -> Void in + if let result = result { switch result { case let .chatFull(fullChat, chats, users): switch fullChat { @@ -239,8 +246,6 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos } switch fullChat { - // case channelFull(flags: Int32, id: Int32, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite, botInfo: [Api.BotInfo], migratedFromChatId: Int32?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?) - case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, apiReadInboxMaxId, apiReadOutboxMaxId, apiUnreadCount, _, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId): var channelFlags = CachedChannelFlags() if (flags & (1 << 3)) != 0 { @@ -304,21 +309,6 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos }) transaction.updatePeerPresences(peerPresences) - - -// let readState = transaction.getReadState(peerId) -// -// let hasReadState: Bool = false//readState?.hasNamespace(Namespaces.Message.Cloud) ?? false -// -// if !hasReadState { -// var readStates: [PeerId: [MessageId.Namespace: PeerReadState]] = [:] -// if readStates[peerId] == nil { -// readStates[peerId] = [:] -// } -// readStates[peerId]![Namespaces.Message.Cloud] = .idBased(maxIncomingReadId: apiReadInboxMaxId, maxOutgoingReadId: apiReadOutboxMaxId, maxKnownId: readState?.maxKnownId ?? 0, count: apiUnreadCount, markedUnread: readState?.markedUnread ?? false) -// transaction.resetIncomingReadStates(readStates) -// } -// let stickerPack: StickerPackCollectionInfo? = stickerSet.flatMap { apiSet -> StickerPackCollectionInfo in @@ -337,13 +327,15 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos var minAvailableMessageIdUpdated = false transaction.updatePeerCachedData(peerIds: [peerId], update: { _, current in - let previous: CachedChannelData + var previous: CachedChannelData if let current = current as? CachedChannelData { previous = current } else { previous = CachedChannelData() } + previous = previous.withUpdatedisNotAccessible(false) + minAvailableMessageIdUpdated = previous.minAvailableMessageId != minAvailableMessageId return previous.withUpdatedFlags(channelFlags) @@ -364,8 +356,15 @@ func fetchAndUpdateCachedPeerData(peerId: PeerId, network: Network, postbox: Pos break } } + } else { + transaction.updatePeerCachedData(peerIds: [peerId], update: { _, _ in + var updated = CachedChannelData() + updated = updated.withUpdatedisNotAccessible(true) + return updated + }) } } + } } else { return .complete() } diff --git a/TelegramCore/UpdatesApiUtils.swift b/TelegramCore/UpdatesApiUtils.swift index bce9d3044f..ead41ef055 100644 --- a/TelegramCore/UpdatesApiUtils.swift +++ b/TelegramCore/UpdatesApiUtils.swift @@ -5,45 +5,90 @@ import Foundation import Postbox #endif +private func collectPreCachedResources(for photo: Api.Photo) -> [(MediaResource, Data)]? { + switch photo { + case let .photo(_, _, _, _, _, sizes): + for size in sizes { + switch size { + case let .photoCachedSize(_, location, _, _, bytes): + switch location { + case let .fileLocation(dcId, volumeId, localId, secret, fileReference): + let data = bytes.makeData() + let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: data.count, fileReference: fileReference.makeData()) + return [(resource, data)] + default: + break + } + default: + break + } + } + return nil + default: + return nil + } +} + +private func collectPreCachedResources(for document: Api.Document) -> [(MediaResource, Data)]? { + switch document { + case let .document(content): + switch content.thumb { + case let .photoCachedSize(_, location, _, _, bytes): + switch location { + case let .fileLocation(dcId, volumeId, localId, secret, fileReference): + let data = bytes.makeData() + let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: data.count, fileReference: fileReference.makeData()) + return [(resource, data)] + default: + break + } + default: + break + } + default: + break + } + return nil +} + extension Api.MessageMedia { var preCachedResources: [(MediaResource, Data)]? { switch self { case let .messageMediaPhoto(_, photo, _): if let photo = photo { - switch photo { - case let .photo(_, _, _, _, _, sizes): - for size in sizes { - switch size { - case let .photoCachedSize(_, location, _, _, bytes): - switch location { - case let .fileLocation(dcId, volumeId, localId, secret, fileReference): - let data = bytes.makeData() - let resource = CloudFileMediaResource(datacenterId: Int(dcId), volumeId: volumeId, localId: localId, secret: secret, size: data.count, fileReference: fileReference.makeData()) - return [(resource, data)] - default: - break - } - default: - break - } - } - return nil - default: - return nil - } + return collectPreCachedResources(for: photo) } else { return nil } case let .messageMediaDocument(_, document, _): if let document = document { - switch document { - case .document: - break - default: - break - } + return collectPreCachedResources(for: document) } return nil + case let .messageMediaWebPage(webPage): + var result: [(MediaResource, Data)]? + switch webPage { + case let .webPage(content): + if let photo = content.photo { + if let photoResult = collectPreCachedResources(for: photo) { + if result == nil { + result = [] + } + result!.append(contentsOf: photoResult) + } + } + if let file = content.document { + if let fileResult = collectPreCachedResources(for: file) { + if result == nil { + result = [] + } + result!.append(contentsOf: fileResult) + } + } + default: + break + } + return result default: return nil } diff --git a/TelegramCore/WebpagePreview.swift b/TelegramCore/WebpagePreview.swift index 4fc76ced2f..75442af54f 100644 --- a/TelegramCore/WebpagePreview.swift +++ b/TelegramCore/WebpagePreview.swift @@ -19,6 +19,11 @@ public func webpagePreview(account: Account, url: String, webpageId: MediaId? = return .single(.messageMediaEmpty) } |> mapToSignal { result -> Signal in + if let preCachedResources = result.preCachedResources { + for (resource, data) in preCachedResources { + account.postbox.mediaBox.storeResourceData(resource.id, data: data) + } + } switch result { case let .messageMediaWebPage(webpage): if let media = telegramMediaWebpageFromApiWebpage(webpage, url: url) {