mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-08-17 11:00:07 +00:00
571 lines
28 KiB
Swift
571 lines
28 KiB
Swift
import Foundation
|
|
#if os(macOS)
|
|
import PostboxMac
|
|
#else
|
|
import Postbox
|
|
#endif
|
|
|
|
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
|
|
}
|
|
|
|
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: [RichText], 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 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 channelBanner(TelegramChannel?)
|
|
|
|
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: decoder.decodeObjectArrayWithDecoderForKey("l"), 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)
|
|
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)
|
|
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)
|
|
}
|
|
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)
|
|
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: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText)
|
|
case InstantPageBlockType.collage.rawValue:
|
|
self = .collage(items: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText)
|
|
case InstantPageBlockType.slideshow.rawValue:
|
|
self = .slideshow(items: decoder.decodeObjectArrayWithDecoderForKey("b"), caption: decoder.decodeObjectForKey("c", decoder: { RichText(decoder: $0) }) as! RichText)
|
|
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)
|
|
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: "l")
|
|
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):
|
|
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")
|
|
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.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")
|
|
}
|
|
encoder.encodeInt32(Int32(dimensions.width), forKey: "sw")
|
|
encoder.encodeInt32(Int32(dimensions.height), forKey: "sh")
|
|
encoder.encodeObject(caption, forKey: "c")
|
|
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: "c")
|
|
case let .collage(items, caption):
|
|
encoder.encodeInt32(InstantPageBlockType.collage.rawValue, forKey: "r")
|
|
encoder.encodeObjectArray(items, forKey: "b")
|
|
encoder.encodeObject(caption, forKey: "c")
|
|
case let .slideshow(items, caption):
|
|
encoder.encodeInt32(InstantPageBlockType.slideshow.rawValue, forKey: "r")
|
|
encoder.encodeObjectArray(items, forKey: "b")
|
|
encoder.encodeObject(caption, forKey: "c")
|
|
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: "c")
|
|
}
|
|
}
|
|
|
|
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(id, caption):
|
|
if case .image(id, caption) = rhs {
|
|
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
init(blocks: [InstantPageBlock], media: [MediaId: Media], isComplete: Bool) {
|
|
self.blocks = blocks
|
|
self.media = media
|
|
self.isComplete = isComplete
|
|
}
|
|
|
|
public init(decoder: PostboxDecoder) {
|
|
self.blocks = decoder.decodeObjectArrayWithDecoderForKey("b")
|
|
self.media = MediaDictionary(decoder: decoder).dict
|
|
self.isComplete = decoder.decodeInt32ForKey("c", 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")
|
|
}
|
|
|
|
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
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
|
|
extension InstantPageBlock {
|
|
init(apiBlock: Api.PageBlock) {
|
|
switch apiBlock {
|
|
case .pageBlockUnsupported:
|
|
self = .unsupported
|
|
case let .pageBlockTitle(text):
|
|
self = .title(RichText(apiText: text))
|
|
case let .pageBlockSubtitle(text):
|
|
self = .subtitle(RichText(apiText: text))
|
|
case let .pageBlockAuthorDate(author, publishedDate):
|
|
self = .authorDate(author: RichText(apiText: author), date: publishedDate)
|
|
case let .pageBlockHeader(text):
|
|
self = .header(RichText(apiText: text))
|
|
case let .pageBlockSubheader(text):
|
|
self = .subheader(RichText(apiText: text))
|
|
case let .pageBlockParagraph(text):
|
|
self = .paragraph(RichText(apiText: text))
|
|
case let .pageBlockPreformatted(text, _):
|
|
self = .preformatted(RichText(apiText: text))
|
|
case let .pageBlockFooter(text):
|
|
self = .footer(RichText(apiText: text))
|
|
case .pageBlockDivider:
|
|
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 .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)
|
|
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) })
|
|
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))
|
|
case let .pageBlockCollage(items, caption):
|
|
self = .collage(items: items.map({ InstantPageBlock(apiBlock: $0) }), caption: RichText(apiText: caption))
|
|
case let .pageBlockSlideshow(items, caption):
|
|
self = .slideshow(items: items.map({ InstantPageBlock(apiBlock: $0) }), caption: RichText(apiText: 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))
|
|
}
|
|
}
|
|
}
|
|
|
|
extension InstantPage {
|
|
convenience init(apiPage: Api.Page) {
|
|
let blocks: [Api.PageBlock]
|
|
let photos: [Api.Photo]
|
|
let files: [Api.Document]
|
|
let isComplete: Bool
|
|
switch apiPage {
|
|
case let .pageFull(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
|
|
}
|
|
var media: [MediaId: Media] = [:]
|
|
for photo in photos {
|
|
if let image = telegramMediaImageFromApiPhoto(photo), let id = image.id {
|
|
media[id] = image
|
|
}
|
|
}
|
|
for file in files {
|
|
if let file = telegramMediaFileFromApiDocument(file), let id = file.id {
|
|
media[id] = file
|
|
}
|
|
}
|
|
self.init(blocks: blocks.map({ InstantPageBlock(apiBlock: $0) }), media: media, isComplete: isComplete)
|
|
}
|
|
}
|