import Postbox private enum InstantPageBlockType: Int32 { case unsupported = 0 case title = 1 case subtitle = 2 case authorDate = 3 case header = 4 case subheader = 5 case paragraph = 6 case preformatted = 7 case footer = 8 case divider = 9 case anchor = 10 case list = 11 case blockQuote = 12 case pullQuote = 13 case image = 14 case video = 15 case cover = 16 case webEmbed = 17 case postEmbed = 18 case collage = 19 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 { case unsupported case title(RichText) case subtitle(RichText) case authorDate(author: RichText, date: Int32) case header(RichText) case subheader(RichText) case paragraph(RichText) case preformatted(RichText) case footer(RichText) case divider case anchor(String) case list(items: [InstantPageListItem], ordered: Bool) case blockQuote(text: RichText, caption: RichText) case pullQuote(text: RichText, 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: PixelDimensions?, 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], expanded: Bool) case relatedArticles(title: RichText, articles: [InstantPageRelatedArticle]) case map(latitude: Double, longitude: Double, zoom: Int32, dimensions: PixelDimensions, caption: InstantPageCaption) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("r", orElse: 0) { case InstantPageBlockType.unsupported.rawValue: self = .unsupported case InstantPageBlockType.title.rawValue: self = .title(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) case InstantPageBlockType.subtitle.rawValue: self = .subtitle(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) case InstantPageBlockType.authorDate.rawValue: self = .authorDate(author: decoder.decodeObjectForKey("a", decoder: { RichText(decoder: $0) }) as! RichText, date: decoder.decodeInt32ForKey("d", orElse: 0)) case InstantPageBlockType.header.rawValue: self = .header(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) case InstantPageBlockType.subheader.rawValue: self = .subheader(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) case InstantPageBlockType.paragraph.rawValue: self = .paragraph(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) case InstantPageBlockType.preformatted.rawValue: self = .preformatted(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) case InstantPageBlockType.footer.rawValue: self = .footer(decoder.decodeObjectForKey("t", decoder: { RichText(decoder: $0) }) as! RichText) case InstantPageBlockType.divider.rawValue: self = .divider case InstantPageBlockType.anchor.rawValue: self = .anchor(decoder.decodeStringForKey("s", orElse: "")) case InstantPageBlockType.list.rawValue: 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: 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: 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: var coverId: MediaId? if let coverIdNamespace = decoder.decodeOptionalInt32ForKey("ci.n"), let coverIdId = decoder.decodeOptionalInt64ForKey("ci.i") { coverId = MediaId(namespace: coverIdNamespace, id: coverIdId) } var dimensions: PixelDimensions? if let width = decoder.decodeOptionalInt32ForKey("sw"), let height = decoder.decodeOptionalInt32ForKey("sh") { dimensions = PixelDimensions(width: width, height: 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") let avatarIdId: Int64? = decoder.decodeOptionalInt64ForKey("av.i") 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: decodeCaption(decoder)) case InstantPageBlockType.collage.rawValue: self = .collage(items: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decodeCaption(decoder)) case InstantPageBlockType.slideshow.rawValue: 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: 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"), expanded: 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: PixelDimensions(width: decoder.decodeInt32ForKey("sw", orElse: 0), height: decoder.decodeInt32ForKey("sh", orElse: 0)), caption: decodeCaption(decoder)) default: self = .unsupported } } public func encode(_ encoder: PostboxEncoder) { switch self { case .unsupported: encoder.encodeInt32(InstantPageBlockType.unsupported.rawValue, forKey: "r") case let .title(text): encoder.encodeInt32(InstantPageBlockType.title.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") case let .subtitle(text): encoder.encodeInt32(InstantPageBlockType.subtitle.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") case let .authorDate(author, date): encoder.encodeInt32(InstantPageBlockType.authorDate.rawValue, forKey: "r") encoder.encodeObject(author, forKey: "a") encoder.encodeInt32(date, forKey: "d") case let .header(text): encoder.encodeInt32(InstantPageBlockType.header.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") case let .subheader(text): encoder.encodeInt32(InstantPageBlockType.subheader.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") case let .paragraph(text): encoder.encodeInt32(InstantPageBlockType.paragraph.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") case let .preformatted(text): encoder.encodeInt32(InstantPageBlockType.preformatted.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") case let .footer(text): encoder.encodeInt32(InstantPageBlockType.footer.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") case .divider: encoder.encodeInt32(InstantPageBlockType.divider.rawValue, forKey: "r") case let .anchor(anchor): encoder.encodeInt32(InstantPageBlockType.anchor.rawValue, forKey: "r") encoder.encodeString(anchor, forKey: "s") case let .list(items, ordered): encoder.encodeInt32(InstantPageBlockType.list.rawValue, forKey: "r") encoder.encodeObjectArray(items, forKey: "ml") encoder.encodeInt32(ordered ? 1 : 0, forKey: "o") case let .blockQuote(text, caption): encoder.encodeInt32(InstantPageBlockType.blockQuote.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") encoder.encodeObject(caption, forKey: "c") case let .pullQuote(text, caption): encoder.encodeInt32(InstantPageBlockType.pullQuote.rawValue, forKey: "r") encoder.encodeObject(text, forKey: "t") encoder.encodeObject(caption, forKey: "c") 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: "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: "mc") encoder.encodeInt32(autoplay ? 1 : 0, forKey: "ap") encoder.encodeInt32(loop ? 1 : 0, forKey: "lo") case let .cover(block): encoder.encodeInt32(InstantPageBlockType.cover.rawValue, forKey: "r") encoder.encodeObject(block, forKey: "c") case let .webEmbed(url, html, dimensions, caption, stretchToWidth, allowScrolling, coverId): encoder.encodeInt32(InstantPageBlockType.webEmbed.rawValue, forKey: "r") if let coverId = coverId { encoder.encodeInt32(coverId.namespace, forKey: "ci.n") encoder.encodeInt64(coverId.id, forKey: "ci.i") } else { encoder.encodeNil(forKey: "ci.n") encoder.encodeNil(forKey: "ci.i") } if let url = url { encoder.encodeString(url, forKey: "u") } else { encoder.encodeNil(forKey: "u") } if let html = html { encoder.encodeString(html, forKey: "h") } else { encoder.encodeNil(forKey: "h") } 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): encoder.encodeInt32(InstantPageBlockType.postEmbed.rawValue, forKey: "r") if let avatarId = avatarId { encoder.encodeInt32(avatarId.namespace, forKey: "av.n") encoder.encodeInt64(avatarId.id, forKey: "av.i") } else { encoder.encodeNil(forKey: "av.n") encoder.encodeNil(forKey: "av.i") } encoder.encodeString(url, forKey: "u") if let webpageId = webpageId { encoder.encodeInt32(webpageId.namespace, forKey: "w.n") encoder.encodeInt64(webpageId.id, forKey: "w.i") } else { encoder.encodeNil(forKey: "w.n") encoder.encodeNil(forKey: "w.i") } encoder.encodeString(author, forKey: "a") encoder.encodeInt32(date, forKey: "d") encoder.encodeObjectArray(blocks, forKey: "b") 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: "mc") case let .slideshow(items, caption): encoder.encodeInt32(InstantPageBlockType.slideshow.rawValue, forKey: "r") encoder.encodeObjectArray(items, forKey: "b") encoder.encodeObject(caption, forKey: "mc") case let .channelBanner(channel): encoder.encodeInt32(InstantPageBlockType.channelBanner.rawValue, forKey: "r") if let channel = channel { encoder.encodeObject(channel, forKey: "c") } else { encoder.encodeNil(forKey: "c") } case let .audio(id, caption): 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: "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, expanded): encoder.encodeInt32(InstantPageBlockType.details.rawValue, forKey: "r") encoder.encodeObject(title, forKey: "t") encoder.encodeObjectArray(blocks, forKey: "b") encoder.encodeInt32(expanded ? 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") } } public static func ==(lhs: InstantPageBlock, rhs: InstantPageBlock) -> Bool { switch lhs { case .unsupported: if case .unsupported = rhs { return true } else { return false } case let .title(text): if case .title(text) = rhs { return true } else { return false } case let .subtitle(text): if case .subtitle(text) = rhs { return true } else { return false } case let .authorDate(author, date): if case .authorDate(author, date) = rhs { return true } else { return false } case let .header(text): if case .header(text) = rhs { return true } else { return false } case let .subheader(text): if case .subheader(text) = rhs { return true } else { return false } case let .paragraph(text): if case .paragraph(text) = rhs { return true } else { return false } case let .preformatted(text): if case .preformatted(text) = rhs { return true } else { return false } case let .footer(text): if case .footer(text) = rhs { return true } else { return false } case .divider: if case .divider = rhs { return true } else { return false } case let .anchor(anchor): if case .anchor(anchor) = rhs { return true } else { return false } case let .list(lhsItems, lhsOrdered): if case let .list(rhsItems, rhsOrdered) = rhs, lhsItems == rhsItems, lhsOrdered == rhsOrdered { return true } else { return false } case let .blockQuote(text, caption): if case .blockQuote(text, caption) = rhs { return true } else { return false } case let .pullQuote(text, caption): if case .pullQuote(text, caption) = rhs { return true } else { return false } 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 } case let .video(id, caption, autoplay, loop): if case .video(id, caption, autoplay, loop) = rhs { return true } else { return false } case let .cover(block): if case .cover(block) = rhs { return true } else { return false } case let .webEmbed(lhsUrl, lhsHtml, lhsDimensions, lhsCaption, lhsStretchToWidth, lhsAllowScrolling, lhsCoverId): if case let .webEmbed(rhsUrl, rhsHtml, rhsDimensions, rhsCaption, rhsStretchToWidth, rhsAllowScrolling, rhsCoverId) = rhs, lhsUrl == rhsUrl && lhsHtml == rhsHtml && lhsDimensions == rhsDimensions && lhsCaption == rhsCaption && lhsStretchToWidth == rhsStretchToWidth && lhsAllowScrolling == rhsAllowScrolling && lhsCoverId == rhsCoverId { return true } else { return false } case let .postEmbed(lhsUrl, lhsWebpageId, lhsAvatarId, lhsAuthor, lhsDate, lhsBlocks, lhsCaption): if case let .postEmbed(rhsUrl, rhsWebpageId, rhsAvatarId, rhsAuthor, rhsDate, rhsBlocks, rhsCaption) = rhs, lhsUrl == rhsUrl && lhsWebpageId == rhsWebpageId && lhsAvatarId == rhsAvatarId && lhsAuthor == rhsAuthor && lhsDate == rhsDate && lhsBlocks == rhsBlocks && lhsCaption == rhsCaption { return true } else { return false } case let .collage(lhsItems, lhsCaption): if case let .collage(rhsItems, rhsCaption) = rhs, lhsItems == rhsItems && lhsCaption == rhsCaption { return true } else { return false } case let .slideshow(lhsItems, lhsCaption): if case let .slideshow(rhsItems, rhsCaption) = rhs, lhsItems == rhsItems && lhsCaption == rhsCaption { return true } else { return false } case let .channelBanner(lhsChannel): if case let .channelBanner(rhsChannel) = rhs { if let lhsChannel = lhsChannel, let rhsChannel = rhsChannel { if !lhsChannel.isEqual(rhsChannel) { return false } } else if (lhsChannel != nil) != (rhsChannel != nil) { return false } return true } else { return false } case let .audio(id, caption): if case .audio(id, caption) = rhs { return true } 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, lhsExpanded): if case let .details(rhsTitle, rhsBlocks, rhsExpanded) = rhs, lhsTitle == rhsTitle, lhsBlocks == rhsBlocks, lhsExpanded == rhsExpanded { 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 public 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? public let author: String? public let date: Int32? public init(url: String, webpageId: MediaId, title: String?, description: String?, photoId: MediaId?, author: String?, date: Int32?) { self.url = url self.webpageId = webpageId self.title = title self.description = description self.photoId = photoId self.author = author self.date = date } 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 self.author = decoder.decodeOptionalStringForKey("a") self.date = decoder.decodeOptionalInt32ForKey("d") } 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") } if let author = self.author { encoder.encodeString(author, forKey: "a") } else { encoder.encodeNil(forKey: "a") } if let date = self.date { encoder.encodeInt32(date, forKey: "d") } else { encoder.encodeNil(forKey: "d") } } 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 } if lhs.author != rhs.author { return false } if lhs.date != rhs.date { return false } return true } } private final class MediaDictionary: PostboxCoding { let dict: [MediaId: Media] init(dict: [MediaId: Media]) { self.dict = dict } init(decoder: PostboxDecoder) { let idsBufer = decoder.decodeBytesForKey("i")! let mediaIds = MediaId.decodeArrayFromBuffer(idsBufer) let medias = decoder.decodeObjectArrayForKey("m") var dict: [MediaId: Media] = [:] assert(mediaIds.count == medias.count) for i in 0 ..< mediaIds.count { if let media = medias[i] as? Media { dict[mediaIds[i]] = media } } self.dict = dict } func encode(_ encoder: PostboxEncoder) { var mediaIds: [MediaId] = [] var medias: [PostboxCoding] = [] for mediaId in self.dict.keys { mediaIds.append(mediaId) medias.append(self.dict[mediaId]!) } let buffer = WriteBuffer() MediaId.encodeArrayToBuffer(mediaIds, buffer: buffer) encoder.encodeBytes(buffer, forKey: "i") encoder.encodeGenericObjectArray(medias, forKey: "m") } } public final class InstantPage: PostboxCoding, Equatable { public let blocks: [InstantPageBlock] public let media: [MediaId: Media] public let isComplete: Bool public let rtl: Bool public let url: String public let views: Int32? public init(blocks: [InstantPageBlock], media: [MediaId: Media], isComplete: Bool, rtl: Bool, url: String, views: Int32?) { self.blocks = blocks self.media = media self.isComplete = isComplete self.rtl = rtl self.url = url self.views = views } 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 self.url = decoder.decodeStringForKey("url", orElse: "") self.views = decoder.decodeOptionalInt32ForKey("v") } 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") encoder.encodeString(self.url, forKey: "url") if let views = self.views { encoder.encodeInt32(views, forKey: "v") } else { encoder.encodeNil(forKey: "v") } } 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 { for (lhsKey, lhsValue) in lhs.media { if let media = rhs.media[lhsKey] { if !lhsValue.isEqual(to: media) { return false } } else { return false } } } if lhs.isComplete != rhs.isComplete { return false } if lhs.rtl != rhs.rtl { return false } if lhs.url != rhs.url { return false } if lhs.views != rhs.views { return false } return true } }