Sparse message API

This commit is contained in:
Ali 2021-10-01 21:13:57 +04:00
parent 38c7d6a38c
commit d73db2811f
17 changed files with 1450 additions and 371 deletions

View File

@ -45,8 +45,8 @@ final class GalleryTitleView: UIView, NavigationBarTitleView {
let leftInset: CGFloat = 0.0
let rightInset: CGFloat = 0.0
let authorNameSize = self.authorNameNode.measure(CGSize(width: size.width - 8.0 * 2.0 - leftInset - rightInset, height: CGFloat.greatestFiniteMagnitude))
let dateSize = self.dateNode.measure(CGSize(width: size.width - 8.0 * 2.0, height: CGFloat.greatestFiniteMagnitude))
let authorNameSize = self.authorNameNode.measure(CGSize(width: max(1.0, size.width - 8.0 * 2.0 - leftInset - rightInset), height: CGFloat.greatestFiniteMagnitude))
let dateSize = self.dateNode.measure(CGSize(width: max(1.0, size.width - 8.0 * 2.0), height: CGFloat.greatestFiniteMagnitude))
if authorNameSize.height.isZero {
self.dateNode.frame = CGRect(origin: CGPoint(x: floor((size.width - dateSize.width) / 2.0), y: floor((size.height - dateSize.height) / 2.0)), size: dateSize)

View File

@ -0,0 +1,21 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "SparseItemGrid",
module_name = "SparseItemGrid",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/Display:Display",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/ComponentFlow:ComponentFlow",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,8 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
public final class SparseItemGrid: ASDisplayNode {
}

View File

@ -0,0 +1,530 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import ComponentFlow
import SwiftSignalKit
private final class RoundedRectangle: Component {
let color: UIColor
init(color: UIColor) {
self.color = color
}
static func ==(lhs: RoundedRectangle, rhs: RoundedRectangle) -> Bool {
if !lhs.color.isEqual(rhs.color) {
return false
}
return true
}
final class View: UIView {
private let backgroundView: UIImageView
private var currentColor: UIColor?
private var currentDiameter: CGFloat?
init() {
self.backgroundView = UIImageView()
super.init(frame: CGRect())
self.addSubview(self.backgroundView)
}
required init?(coder aDecoder: NSCoder) {
preconditionFailure()
}
func update(component: RoundedRectangle, availableSize: CGSize, transition: Transition) -> CGSize {
let shadowInset: CGFloat = 0.0
let diameter = min(availableSize.width, availableSize.height)
var updated = false
if let currentColor = self.currentColor {
if !component.color.isEqual(currentColor) {
updated = true
}
} else {
updated = true
}
if self.currentDiameter != diameter || updated {
self.currentDiameter = diameter
self.currentColor = component.color
self.backgroundView.image = generateImage(CGSize(width: diameter + shadowInset * 2.0, height: diameter + shadowInset * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(component.color.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset, y: shadowInset), size: CGSize(width: size.width - shadowInset * 2.0, height: size.height - shadowInset * 2.0)))
})?.stretchableImage(withLeftCapWidth: Int(diameter + shadowInset * 2.0) / 2, topCapHeight: Int(diameter + shadowInset * 2.0) / 2)
}
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: -shadowInset, y: -shadowInset), size: CGSize(width: availableSize.width + shadowInset * 2.0, height: availableSize.height + shadowInset * 2.0)))
return availableSize
}
}
func makeView() -> View {
return View()
}
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, transition: transition)
}
}
private final class ShadowRoundedRectangle: Component {
let color: UIColor
init(color: UIColor) {
self.color = color
}
static func ==(lhs: ShadowRoundedRectangle, rhs: ShadowRoundedRectangle) -> Bool {
if !lhs.color.isEqual(rhs.color) {
return false
}
return true
}
final class View: UIView {
private let backgroundView: UIImageView
private var currentColor: UIColor?
private var currentDiameter: CGFloat?
init() {
self.backgroundView = UIImageView()
super.init(frame: CGRect())
self.addSubview(self.backgroundView)
}
required init?(coder aDecoder: NSCoder) {
preconditionFailure()
}
func update(component: ShadowRoundedRectangle, availableSize: CGSize, transition: Transition) -> CGSize {
let shadowInset: CGFloat = 10.0
let diameter = min(availableSize.width, availableSize.height)
var updated = false
if let currentColor = self.currentColor {
if !component.color.isEqual(currentColor) {
updated = true
}
} else {
updated = true
}
if self.currentDiameter != diameter || updated {
self.currentDiameter = diameter
self.currentColor = component.color
self.backgroundView.image = generateImage(CGSize(width: diameter + shadowInset * 2.0, height: diameter + shadowInset * 2.0), rotatedContext: { size, context in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(component.color.cgColor)
context.setShadow(offset: CGSize(width: 0.0, height: -2.0), blur: 5.0, color: UIColor(white: 0.0, alpha: 0.3).cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(x: shadowInset, y: shadowInset), size: CGSize(width: size.width - shadowInset * 2.0, height: size.height - shadowInset * 2.0)))
})?.stretchableImage(withLeftCapWidth: Int(diameter + shadowInset * 2.0) / 2, topCapHeight: Int(diameter + shadowInset * 2.0) / 2)
}
transition.setFrame(view: self.backgroundView, frame: CGRect(origin: CGPoint(x: -shadowInset, y: -shadowInset), size: CGSize(width: availableSize.width + shadowInset * 2.0, height: availableSize.height + shadowInset * 2.0)))
return availableSize
}
}
func makeView() -> View {
return View()
}
func update(view: View, availableSize: CGSize, transition: Transition) -> CGSize {
return view.update(component: self, availableSize: availableSize, transition: transition)
}
}
private final class SparseItemGridScrollingIndicatorComponent: CombinedComponent {
let backgroundColor: UIColor
let shadowColor: UIColor
let foregroundColor: UIColor
let dateString: String
init(
backgroundColor: UIColor,
shadowColor: UIColor,
foregroundColor: UIColor,
dateString: String
) {
self.backgroundColor = backgroundColor
self.shadowColor = shadowColor
self.foregroundColor = foregroundColor
self.dateString = dateString
}
static func ==(lhs: SparseItemGridScrollingIndicatorComponent, rhs: SparseItemGridScrollingIndicatorComponent) -> Bool {
if lhs.backgroundColor != rhs.backgroundColor {
return false
}
if lhs.shadowColor != rhs.shadowColor {
return false
}
if lhs.foregroundColor != rhs.foregroundColor {
return false
}
if lhs.dateString != rhs.dateString {
return false
}
return true
}
static var body: Body {
let rect = Child(ShadowRoundedRectangle.self)
let text = Child(Text.self)
return { context in
let text = text.update(
component: Text(
text: context.component.dateString,
font: Font.medium(13.0),
color: .black
),
availableSize: CGSize(width: 200.0, height: 100.0),
transition: .immediate
)
let rect = rect.update(
component: ShadowRoundedRectangle(
color: .white
),
availableSize: CGSize(width: text.size.width + 26.0, height: 32.0),
transition: .immediate
)
let rectFrame = rect.size.centered(around: CGPoint(
x: rect.size.width / 2.0,
y: rect.size.height / 2.0
))
context.add(rect
.position(CGPoint(x: rectFrame.midX, y: rectFrame.midY))
)
let textFrame = text.size.centered(in: rectFrame)
context.add(text
.position(CGPoint(x: textFrame.midX, y: textFrame.midY))
)
return rect.size
}
}
}
public final class SparseItemGridScrollingArea: ASDisplayNode {
private final class DragGesture: UIGestureRecognizer {
private let shouldBegin: (CGPoint) -> Bool
private let began: () -> Void
private let ended: () -> Void
private let moved: (CGFloat) -> Void
private var initialLocation: CGPoint?
public init(
shouldBegin: @escaping (CGPoint) -> Bool,
began: @escaping () -> Void,
ended: @escaping () -> Void,
moved: @escaping (CGFloat) -> Void
) {
self.shouldBegin = shouldBegin
self.began = began
self.ended = ended
self.moved = moved
super.init(target: nil, action: nil)
}
deinit {
}
override public func reset() {
super.reset()
self.initialLocation = nil
self.initialLocation = nil
}
override public func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if self.numberOfTouches > 1 {
self.state = .failed
self.ended()
return
}
if self.state == .possible {
if let location = touches.first?.location(in: self.view) {
if self.shouldBegin(location) {
self.initialLocation = location
self.state = .began
self.began()
} else {
self.state = .failed
}
} else {
self.state = .failed
}
}
}
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
self.initialLocation = nil
if self.state == .began || self.state == .changed {
self.ended()
self.state = .failed
}
}
override public func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesCancelled(touches, with: event)
self.initialLocation = nil
if self.state == .began || self.state == .changed {
self.ended()
self.state = .failed
}
}
override public func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesMoved(touches, with: event)
if (self.state == .began || self.state == .changed), let initialLocation = self.initialLocation, let location = touches.first?.location(in: self.view) {
self.state = .changed
let offset = location.y - initialLocation.y
self.moved(offset)
}
}
}
private let dateIndicator: ComponentHostView<Empty>
private let lineIndicator: ComponentHostView<Empty>
private var dragGesture: DragGesture?
public private(set) var isDragging: Bool = false
private weak var draggingScrollView: UIScrollView?
private var scrollingInitialOffset: CGFloat?
private var activityTimer: SwiftSignalKit.Timer?
public var beginScrolling: (() -> UIScrollView?)?
private struct ProjectionData {
var minY: CGFloat
var maxY: CGFloat
var indicatorHeight: CGFloat
var scrollableHeight: CGFloat
}
private var projectionData: ProjectionData?
override public init() {
self.dateIndicator = ComponentHostView<Empty>()
self.lineIndicator = ComponentHostView<Empty>()
self.dateIndicator.alpha = 0.0
self.lineIndicator.alpha = 0.0
super.init()
self.view.addSubview(self.dateIndicator)
self.view.addSubview(self.lineIndicator)
let dragGesture = DragGesture(
shouldBegin: { [weak self] point in
guard let strongSelf = self else {
return false
}
if !strongSelf.dateIndicator.frame.contains(point) {
return false
}
return true
},
began: { [weak self] in
guard let strongSelf = self else {
return
}
let transition: ContainedViewLayoutTransition = .animated(duration: 0.1, curve: .easeInOut)
transition.updateSublayerTransformOffset(layer: strongSelf.dateIndicator.layer, offset: CGPoint(x: -80.0, y: 0.0))
strongSelf.isDragging = true
if let scrollView = strongSelf.beginScrolling?() {
strongSelf.draggingScrollView = scrollView
strongSelf.scrollingInitialOffset = scrollView.contentOffset.y
scrollView.setContentOffset(scrollView.contentOffset, animated: false)
}
strongSelf.updateActivityTimer()
},
ended: { [weak self] in
guard let strongSelf = self else {
return
}
strongSelf.draggingScrollView = nil
let transition: ContainedViewLayoutTransition = .animated(duration: 0.25, curve: .spring)
transition.updateSublayerTransformOffset(layer: strongSelf.dateIndicator.layer, offset: CGPoint(x: 0.0, y: 0.0))
strongSelf.isDragging = false
strongSelf.updateActivityTimer()
},
moved: { [weak self] relativeOffset in
guard let strongSelf = self else {
return
}
guard let scrollView = strongSelf.draggingScrollView, let scrollingInitialOffset = strongSelf.scrollingInitialOffset else {
return
}
guard let projectionData = strongSelf.projectionData else {
return
}
let indicatorArea = projectionData.maxY - projectionData.minY
let scrollFraction = projectionData.scrollableHeight / indicatorArea
var offset = scrollingInitialOffset + scrollFraction * relativeOffset
if offset < 0.0 {
offset = 0.0
}
if offset > scrollView.contentSize.height - scrollView.bounds.height {
offset = scrollView.contentSize.height - scrollView.bounds.height
}
scrollView.setContentOffset(CGPoint(x: 0.0, y: offset), animated: false)
let _ = scrollView
let _ = projectionData
}
)
self.dragGesture = dragGesture
self.view.addGestureRecognizer(dragGesture)
}
public func update(
containerSize: CGSize,
containerInsets: UIEdgeInsets,
contentHeight: CGFloat,
contentOffset: CGFloat,
isScrolling: Bool,
dateString: String,
transition: ContainedViewLayoutTransition
) {
if isScrolling {
self.updateActivityTimer()
}
let indicatorSize = self.dateIndicator.update(
transition: .immediate,
component: AnyComponent(SparseItemGridScrollingIndicatorComponent(
backgroundColor: .white,
shadowColor: .black,
foregroundColor: .black,
dateString: dateString
)),
environment: {},
containerSize: containerSize
)
let scrollIndicatorHeightFraction = min(1.0, max(0.0, (containerSize.height - containerInsets.top - containerInsets.bottom) / contentHeight))
if scrollIndicatorHeightFraction >= 1.0 - .ulpOfOne {
self.dateIndicator.isHidden = true
self.lineIndicator.isHidden = true
} else {
self.dateIndicator.isHidden = false
self.lineIndicator.isHidden = false
}
let indicatorVerticalInset: CGFloat = 3.0
let topIndicatorInset: CGFloat = indicatorVerticalInset
let bottomIndicatorInset: CGFloat = indicatorVerticalInset + containerInsets.bottom
let scrollIndicatorHeight = max(35.0, ceil(scrollIndicatorHeightFraction * containerSize.height))
let indicatorPositionFraction = min(1.0, max(0.0, contentOffset / (contentHeight - containerSize.height)))
let indicatorTopPosition = topIndicatorInset
let indicatorBottomPosition = containerSize.height - bottomIndicatorInset - scrollIndicatorHeight
let dateIndicatorTopPosition = topIndicatorInset
let dateIndicatorBottomPosition = containerSize.height - bottomIndicatorInset - indicatorSize.height
let indicatorPosition = indicatorTopPosition * (1.0 - indicatorPositionFraction) + indicatorBottomPosition * indicatorPositionFraction
let dateIndicatorPosition = dateIndicatorTopPosition * (1.0 - indicatorPositionFraction) + dateIndicatorBottomPosition * indicatorPositionFraction
self.projectionData = ProjectionData(
minY: dateIndicatorTopPosition,
maxY: dateIndicatorBottomPosition,
indicatorHeight: indicatorSize.height,
scrollableHeight: contentHeight - containerSize.height
)
transition.updateFrame(view: self.dateIndicator, frame: CGRect(origin: CGPoint(x: containerSize.width - 12.0 - indicatorSize.width, y: dateIndicatorPosition), size: indicatorSize))
if isScrolling {
self.dateIndicator.alpha = 1.0
self.lineIndicator.alpha = 1.0
}
let lineIndicatorSize = CGSize(width: 3.0, height: scrollIndicatorHeight)
let _ = self.lineIndicator.update(
transition: .immediate,
component: AnyComponent(RoundedRectangle(
color: UIColor(white: 0.0, alpha: 0.3)
)),
environment: {},
containerSize: lineIndicatorSize
)
transition.updateFrame(view: self.lineIndicator, frame: CGRect(origin: CGPoint(x: containerSize.width - 3.0 - lineIndicatorSize.width, y: indicatorPosition), size: lineIndicatorSize))
}
private func updateActivityTimer() {
self.activityTimer?.invalidate()
if self.isDragging {
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)
transition.updateAlpha(layer: self.dateIndicator.layer, alpha: 1.0)
transition.updateAlpha(layer: self.lineIndicator.layer, alpha: 1.0)
} else {
self.activityTimer = SwiftSignalKit.Timer(timeout: 1.0, repeat: false, completion: { [weak self] in
guard let strongSelf = self else {
return
}
let transition: ContainedViewLayoutTransition = .animated(duration: 0.3, curve: .easeInOut)
transition.updateAlpha(layer: strongSelf.dateIndicator.layer, alpha: 0.0)
transition.updateAlpha(layer: strongSelf.lineIndicator.layer, alpha: 0.0)
}, queue: .mainQueue())
self.activityTimer?.start()
}
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.dateIndicator.frame.contains(point) {
return self.view
}
return nil
}
}

View File

@ -11,8 +11,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-457104426] = { return Api.InputGeoPoint.parse_inputGeoPointEmpty($0) }
dict[1210199983] = { return Api.InputGeoPoint.parse_inputGeoPoint($0) }
dict[-784000893] = { return Api.payments.ValidatedRequestedInfo.parse_validatedRequestedInfo($0) }
dict[1304281241] = { return Api.ChatFull.parse_chatFull($0) }
dict[-374179305] = { return Api.ChatFull.parse_channelFull($0) }
dict[-2097579871] = { return Api.ChatFull.parse_chatFull($0) }
dict[1084166537] = { return Api.ChatFull.parse_channelFull($0) }
dict[-591909213] = { return Api.PollResults.parse_pollResults($0) }
dict[-1070776313] = { return Api.ChatParticipant.parse_chatParticipant($0) }
dict[-462696732] = { return Api.ChatParticipant.parse_chatParticipantCreator($0) }
@ -72,7 +72,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[399807445] = { return Api.Chat.parse_channelForbidden($0) }
dict[1202287072] = { return Api.StatsURL.parse_statsURL($0) }
dict[1516793212] = { return Api.ChatInvite.parse_chatInviteAlready($0) }
dict[-540871282] = { return Api.ChatInvite.parse_chatInvite($0) }
dict[806110401] = { return Api.ChatInvite.parse_chatInvite($0) }
dict[1634294960] = { return Api.ChatInvite.parse_chatInvitePeek($0) }
dict[813821341] = { return Api.InlineQueryPeerType.parse_inlineQueryPeerTypeSameBotPM($0) }
dict[-2093215828] = { return Api.InlineQueryPeerType.parse_inlineQueryPeerTypePM($0) }
@ -163,6 +163,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-496024847] = { return Api.UserStatus.parse_userStatusRecently($0) }
dict[129960444] = { return Api.UserStatus.parse_userStatusLastWeek($0) }
dict[2011940674] = { return Api.UserStatus.parse_userStatusLastMonth($0) }
dict[-911191137] = { return Api.SearchResultsCalendarPeriod.parse_searchResultsCalendarPeriod($0) }
dict[-11252123] = { return Api.Folder.parse_folder($0) }
dict[739712882] = { return Api.Dialog.parse_dialog($0) }
dict[1908216652] = { return Api.Dialog.parse_dialogFolder($0) }
@ -334,6 +335,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-842824308] = { return Api.account.WallPapers.parse_wallPapers($0) }
dict[1012306921] = { return Api.InputTheme.parse_inputTheme($0) }
dict[-175567375] = { return Api.InputTheme.parse_inputThemeSlug($0) }
dict[2014782332] = { return Api.messages.SearchResultsRawMessages.parse_searchResultsRawMessages($0) }
dict[-2032041631] = { return Api.Poll.parse_poll($0) }
dict[-1195615476] = { return Api.InputNotifyPeer.parse_inputNotifyPeer($0) }
dict[423314455] = { return Api.InputNotifyPeer.parse_inputNotifyUsers($0) }
@ -467,7 +469,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-384910503] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionExportedInviteEdit($0) }
dict[1048537159] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionParticipantVolume($0) }
dict[1855199800] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeHistoryTTL($0) }
dict[-26672755] = { return Api.ChannelAdminLogEventAction.parse_channelAdminLogEventActionChangeTheme($0) }
dict[-1271602504] = { return Api.auth.ExportedAuthorization.parse_exportedAuthorization($0) }
dict[2103482845] = { return Api.SecurePlainData.parse_securePlainPhone($0) }
dict[569137759] = { return Api.SecurePlainData.parse_securePlainEmail($0) }
@ -566,6 +567,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-478701471] = { return Api.account.ResetPasswordResult.parse_resetPasswordFailedWait($0) }
dict[-370148227] = { return Api.account.ResetPasswordResult.parse_resetPasswordRequestedWait($0) }
dict[-383330754] = { return Api.account.ResetPasswordResult.parse_resetPasswordOk($0) }
dict[343859772] = { return Api.messages.SearchResultsCalendar.parse_searchResultsCalendar($0) }
dict[856375399] = { return Api.Config.parse_config($0) }
dict[-75283823] = { return Api.TopPeerCategoryPeers.parse_topPeerCategoryPeers($0) }
dict[-1107729093] = { return Api.Game.parse_game($0) }
@ -576,7 +578,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1032140601] = { return Api.BotCommand.parse_botCommand($0) }
dict[1474462241] = { return Api.account.ContentSettings.parse_contentSettings($0) }
dict[-193506890] = { return Api.phone.GroupParticipants.parse_groupParticipants($0) }
dict[190633460] = { return Api.ChatInviteImporter.parse_chatInviteImporter($0) }
dict[-1574303204] = { return Api.ChatInviteImporter.parse_chatInviteImporter($0) }
dict[-2066640507] = { return Api.messages.AffectedMessages.parse_affectedMessages($0) }
dict[-402498398] = { return Api.messages.SavedGifs.parse_savedGifsNotModified($0) }
dict[-2069878259] = { return Api.messages.SavedGifs.parse_savedGifs($0) }
@ -783,7 +785,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-1673717362] = { return Api.InputPeerNotifySettings.parse_inputPeerNotifySettings($0) }
dict[-1634752813] = { return Api.messages.FavedStickers.parse_favedStickersNotModified($0) }
dict[750063767] = { return Api.messages.FavedStickers.parse_favedStickers($0) }
dict[-1316944408] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) }
dict[19682803] = { return Api.ExportedChatInvite.parse_chatInviteExported($0) }
dict[-1389486888] = { return Api.account.AuthorizationForm.parse_authorizationForm($0) }
dict[-1392388579] = { return Api.Authorization.parse_authorization($0) }
dict[-1361650766] = { return Api.MaskCoords.parse_maskCoords($0) }
@ -1097,6 +1099,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.UserStatus:
_1.serialize(buffer, boxed)
case let _1 as Api.SearchResultsCalendarPeriod:
_1.serialize(buffer, boxed)
case let _1 as Api.Folder:
_1.serialize(buffer, boxed)
case let _1 as Api.Dialog:
@ -1145,6 +1149,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.InputTheme:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.SearchResultsRawMessages:
_1.serialize(buffer, boxed)
case let _1 as Api.Poll:
_1.serialize(buffer, boxed)
case let _1 as Api.InputNotifyPeer:
@ -1333,6 +1339,8 @@ public struct Api {
_1.serialize(buffer, boxed)
case let _1 as Api.account.ResetPasswordResult:
_1.serialize(buffer, boxed)
case let _1 as Api.messages.SearchResultsCalendar:
_1.serialize(buffer, boxed)
case let _1 as Api.Config:
_1.serialize(buffer, boxed)
case let _1 as Api.TopPeerCategoryPeers:

View File

@ -477,6 +477,56 @@ public struct messages {
}
}
}
public enum SearchResultsRawMessages: TypeConstructorDescription {
case searchResultsRawMessages(msgIds: [Int32], msgDates: [Int32])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .searchResultsRawMessages(let msgIds, let msgDates):
if boxed {
buffer.appendInt32(2014782332)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(msgIds.count))
for item in msgIds {
serializeInt32(item, buffer: buffer, boxed: false)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(msgDates.count))
for item in msgDates {
serializeInt32(item, buffer: buffer, boxed: false)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .searchResultsRawMessages(let msgIds, let msgDates):
return ("searchResultsRawMessages", [("msgIds", msgIds), ("msgDates", msgDates)])
}
}
public static func parse_searchResultsRawMessages(_ reader: BufferReader) -> SearchResultsRawMessages? {
var _1: [Int32]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
}
var _2: [Int32]?
if let _ = reader.readInt32() {
_2 = Api.parseVector(reader, elementSignature: -1471112230, elementType: Int32.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.messages.SearchResultsRawMessages.searchResultsRawMessages(msgIds: _1!, msgDates: _2!)
}
else {
return nil
}
}
}
public enum ExportedChatInvites: TypeConstructorDescription {
case exportedChatInvites(count: Int32, invites: [Api.ExportedChatInvite], users: [Api.User])
@ -1245,6 +1295,96 @@ public struct messages {
}
}
}
public enum SearchResultsCalendar: TypeConstructorDescription {
case searchResultsCalendar(flags: Int32, count: Int32, minDate: Int32, minMsgId: Int32, offsetIdOffset: Int32?, periods: [Api.SearchResultsCalendarPeriod], messages: [Api.Message], chats: [Api.Chat], users: [Api.User])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users):
if boxed {
buffer.appendInt32(343859772)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false)
serializeInt32(minDate, buffer: buffer, boxed: false)
serializeInt32(minMsgId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(offsetIdOffset!, buffer: buffer, boxed: false)}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(periods.count))
for item in periods {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(messages.count))
for item in messages {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
item.serialize(buffer, true)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .searchResultsCalendar(let flags, let count, let minDate, let minMsgId, let offsetIdOffset, let periods, let messages, let chats, let users):
return ("searchResultsCalendar", [("flags", flags), ("count", count), ("minDate", minDate), ("minMsgId", minMsgId), ("offsetIdOffset", offsetIdOffset), ("periods", periods), ("messages", messages), ("chats", chats), ("users", users)])
}
}
public static func parse_searchResultsCalendar(_ reader: BufferReader) -> SearchResultsCalendar? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
var _5: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_5 = reader.readInt32() }
var _6: [Api.SearchResultsCalendarPeriod]?
if let _ = reader.readInt32() {
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.SearchResultsCalendarPeriod.self)
}
var _7: [Api.Message]?
if let _ = reader.readInt32() {
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Message.self)
}
var _8: [Api.Chat]?
if let _ = reader.readInt32() {
_8 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Chat.self)
}
var _9: [Api.User]?
if let _ = reader.readInt32() {
_9 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 1) == 0) || _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
let _c8 = _8 != nil
let _c9 = _9 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.messages.SearchResultsCalendar.searchResultsCalendar(flags: _1!, count: _2!, minDate: _3!, minMsgId: _4!, offsetIdOffset: _5, periods: _6!, messages: _7!, chats: _8!, users: _9!)
}
else {
return nil
}
}
}
public enum AffectedMessages: TypeConstructorDescription {
case affectedMessages(pts: Int32, ptsCount: Int32)

View File

@ -160,14 +160,14 @@ public extension Api {
}
public enum ChatFull: TypeConstructorDescription {
case chatFull(flags: Int32, id: Int64, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?)
case channelFull(flags: Int32, id: Int64, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int64?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int64?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?)
case chatFull(flags: Int32, id: Int64, about: String, participants: Api.ChatParticipants, chatPhoto: Api.Photo?, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo]?, pinnedMsgId: Int32?, folderId: Int32?, call: Api.InputGroupCall?, ttlPeriod: Int32?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?)
case channelFull(flags: Int32, id: Int64, about: String, participantsCount: Int32?, adminsCount: Int32?, kickedCount: Int32?, bannedCount: Int32?, onlineCount: Int32?, readInboxMaxId: Int32, readOutboxMaxId: Int32, unreadCount: Int32, chatPhoto: Api.Photo, notifySettings: Api.PeerNotifySettings, exportedInvite: Api.ExportedChatInvite?, botInfo: [Api.BotInfo], migratedFromChatId: Int64?, migratedFromMaxId: Int32?, pinnedMsgId: Int32?, stickerset: Api.StickerSet?, availableMinId: Int32?, folderId: Int32?, linkedChatId: Int64?, location: Api.ChannelLocation?, slowmodeSeconds: Int32?, slowmodeNextSendDate: Int32?, statsDc: Int32?, pts: Int32, call: Api.InputGroupCall?, ttlPeriod: Int32?, pendingSuggestions: [String]?, groupcallDefaultJoinAs: Api.Peer?, themeEmoticon: String?, requestsPending: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon):
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending):
if boxed {
buffer.appendInt32(1304281241)
buffer.appendInt32(-2097579871)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
@ -187,10 +187,11 @@ public extension Api {
if Int(flags) & Int(1 << 14) != 0 {serializeInt32(ttlPeriod!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 15) != 0 {groupcallDefaultJoinAs!.serialize(buffer, true)}
if Int(flags) & Int(1 << 16) != 0 {serializeString(themeEmoticon!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 17) != 0 {serializeInt32(requestsPending!, buffer: buffer, boxed: false)}
break
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon):
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending):
if boxed {
buffer.appendInt32(-374179305)
buffer.appendInt32(1084166537)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(id, buffer: buffer, boxed: false)
@ -232,16 +233,17 @@ public extension Api {
}}
if Int(flags) & Int(1 << 26) != 0 {groupcallDefaultJoinAs!.serialize(buffer, true)}
if Int(flags) & Int(1 << 27) != 0 {serializeString(themeEmoticon!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 28) != 0 {serializeInt32(requestsPending!, buffer: buffer, boxed: false)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttlPeriod", ttlPeriod), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs), ("themeEmoticon", themeEmoticon)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call), ("ttlPeriod", ttlPeriod), ("pendingSuggestions", pendingSuggestions), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs), ("themeEmoticon", themeEmoticon)])
case .chatFull(let flags, let id, let about, let participants, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let pinnedMsgId, let folderId, let call, let ttlPeriod, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending):
return ("chatFull", [("flags", flags), ("id", id), ("about", about), ("participants", participants), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("pinnedMsgId", pinnedMsgId), ("folderId", folderId), ("call", call), ("ttlPeriod", ttlPeriod), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs), ("themeEmoticon", themeEmoticon), ("requestsPending", requestsPending)])
case .channelFull(let flags, let id, let about, let participantsCount, let adminsCount, let kickedCount, let bannedCount, let onlineCount, let readInboxMaxId, let readOutboxMaxId, let unreadCount, let chatPhoto, let notifySettings, let exportedInvite, let botInfo, let migratedFromChatId, let migratedFromMaxId, let pinnedMsgId, let stickerset, let availableMinId, let folderId, let linkedChatId, let location, let slowmodeSeconds, let slowmodeNextSendDate, let statsDc, let pts, let call, let ttlPeriod, let pendingSuggestions, let groupcallDefaultJoinAs, let themeEmoticon, let requestsPending):
return ("channelFull", [("flags", flags), ("id", id), ("about", about), ("participantsCount", participantsCount), ("adminsCount", adminsCount), ("kickedCount", kickedCount), ("bannedCount", bannedCount), ("onlineCount", onlineCount), ("readInboxMaxId", readInboxMaxId), ("readOutboxMaxId", readOutboxMaxId), ("unreadCount", unreadCount), ("chatPhoto", chatPhoto), ("notifySettings", notifySettings), ("exportedInvite", exportedInvite), ("botInfo", botInfo), ("migratedFromChatId", migratedFromChatId), ("migratedFromMaxId", migratedFromMaxId), ("pinnedMsgId", pinnedMsgId), ("stickerset", stickerset), ("availableMinId", availableMinId), ("folderId", folderId), ("linkedChatId", linkedChatId), ("location", location), ("slowmodeSeconds", slowmodeSeconds), ("slowmodeNextSendDate", slowmodeNextSendDate), ("statsDc", statsDc), ("pts", pts), ("call", call), ("ttlPeriod", ttlPeriod), ("pendingSuggestions", pendingSuggestions), ("groupcallDefaultJoinAs", groupcallDefaultJoinAs), ("themeEmoticon", themeEmoticon), ("requestsPending", requestsPending)])
}
}
@ -288,6 +290,8 @@ public extension Api {
} }
var _14: String?
if Int(_1!) & Int(1 << 16) != 0 {_14 = parseString(reader) }
var _15: Int32?
if Int(_1!) & Int(1 << 17) != 0 {_15 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -302,8 +306,9 @@ public extension Api {
let _c12 = (Int(_1!) & Int(1 << 14) == 0) || _12 != nil
let _c13 = (Int(_1!) & Int(1 << 15) == 0) || _13 != nil
let _c14 = (Int(_1!) & Int(1 << 16) == 0) || _14 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 {
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttlPeriod: _12, groupcallDefaultJoinAs: _13, themeEmoticon: _14)
let _c15 = (Int(_1!) & Int(1 << 17) == 0) || _15 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 {
return Api.ChatFull.chatFull(flags: _1!, id: _2!, about: _3!, participants: _4!, chatPhoto: _5, notifySettings: _6!, exportedInvite: _7, botInfo: _8, pinnedMsgId: _9, folderId: _10, call: _11, ttlPeriod: _12, groupcallDefaultJoinAs: _13, themeEmoticon: _14, requestsPending: _15)
}
else {
return nil
@ -392,6 +397,8 @@ public extension Api {
} }
var _32: String?
if Int(_1!) & Int(1 << 27) != 0 {_32 = parseString(reader) }
var _33: Int32?
if Int(_1!) & Int(1 << 28) != 0 {_33 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -424,8 +431,9 @@ public extension Api {
let _c30 = (Int(_1!) & Int(1 << 25) == 0) || _30 != nil
let _c31 = (Int(_1!) & Int(1 << 26) == 0) || _31 != nil
let _c32 = (Int(_1!) & Int(1 << 27) == 0) || _32 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!, call: _28, ttlPeriod: _29, pendingSuggestions: _30, groupcallDefaultJoinAs: _31, themeEmoticon: _32)
let _c33 = (Int(_1!) & Int(1 << 28) == 0) || _33 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 && _c10 && _c11 && _c12 && _c13 && _c14 && _c15 && _c16 && _c17 && _c18 && _c19 && _c20 && _c21 && _c22 && _c23 && _c24 && _c25 && _c26 && _c27 && _c28 && _c29 && _c30 && _c31 && _c32 && _c33 {
return Api.ChatFull.channelFull(flags: _1!, id: _2!, about: _3!, participantsCount: _4, adminsCount: _5, kickedCount: _6, bannedCount: _7, onlineCount: _8, readInboxMaxId: _9!, readOutboxMaxId: _10!, unreadCount: _11!, chatPhoto: _12!, notifySettings: _13!, exportedInvite: _14, botInfo: _15!, migratedFromChatId: _16, migratedFromMaxId: _17, pinnedMsgId: _18, stickerset: _19, availableMinId: _20, folderId: _21, linkedChatId: _22, location: _23, slowmodeSeconds: _24, slowmodeNextSendDate: _25, statsDc: _26, pts: _27!, call: _28, ttlPeriod: _29, pendingSuggestions: _30, groupcallDefaultJoinAs: _31, themeEmoticon: _32, requestsPending: _33)
}
else {
return nil
@ -2023,7 +2031,7 @@ public extension Api {
}
public enum ChatInvite: TypeConstructorDescription {
case chatInviteAlready(chat: Api.Chat)
case chatInvite(flags: Int32, title: String, photo: Api.Photo, participantsCount: Int32, participants: [Api.User]?)
case chatInvite(flags: Int32, title: String, about: String?, photo: Api.Photo, participantsCount: Int32, participants: [Api.User]?)
case chatInvitePeek(chat: Api.Chat, expires: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
@ -2034,12 +2042,13 @@ public extension Api {
}
chat.serialize(buffer, true)
break
case .chatInvite(let flags, let title, let photo, let participantsCount, let participants):
case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants):
if boxed {
buffer.appendInt32(-540871282)
buffer.appendInt32(806110401)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 5) != 0 {serializeString(about!, buffer: buffer, boxed: false)}
photo.serialize(buffer, true)
serializeInt32(participantsCount, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 4) != 0 {buffer.appendInt32(481674261)
@ -2062,8 +2071,8 @@ public extension Api {
switch self {
case .chatInviteAlready(let chat):
return ("chatInviteAlready", [("chat", chat)])
case .chatInvite(let flags, let title, let photo, let participantsCount, let participants):
return ("chatInvite", [("flags", flags), ("title", title), ("photo", photo), ("participantsCount", participantsCount), ("participants", participants)])
case .chatInvite(let flags, let title, let about, let photo, let participantsCount, let participants):
return ("chatInvite", [("flags", flags), ("title", title), ("about", about), ("photo", photo), ("participantsCount", participantsCount), ("participants", participants)])
case .chatInvitePeek(let chat, let expires):
return ("chatInvitePeek", [("chat", chat), ("expires", expires)])
}
@ -2087,23 +2096,26 @@ public extension Api {
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: Api.Photo?
var _3: String?
if Int(_1!) & Int(1 << 5) != 0 {_3 = parseString(reader) }
var _4: Api.Photo?
if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.Photo
_4 = Api.parse(reader, signature: signature) as? Api.Photo
}
var _4: Int32?
_4 = reader.readInt32()
var _5: [Api.User]?
var _5: Int32?
_5 = reader.readInt32()
var _6: [Api.User]?
if Int(_1!) & Int(1 << 4) != 0 {if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c3 = (Int(_1!) & Int(1 << 5) == 0) || _3 != nil
let _c4 = _4 != nil
let _c5 = (Int(_1!) & Int(1 << 4) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.ChatInvite.chatInvite(flags: _1!, title: _2!, photo: _3!, participantsCount: _4!, participants: _5)
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 4) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.ChatInvite.chatInvite(flags: _1!, title: _2!, about: _3, photo: _4!, participantsCount: _5!, participants: _6)
}
else {
return nil
@ -4080,6 +4092,52 @@ public extension Api {
return Api.UserStatus.userStatusLastMonth
}
}
public enum SearchResultsCalendarPeriod: TypeConstructorDescription {
case searchResultsCalendarPeriod(date: Int32, minMsgId: Int32, maxMsgId: Int32, count: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count):
if boxed {
buffer.appendInt32(-911191137)
}
serializeInt32(date, buffer: buffer, boxed: false)
serializeInt32(minMsgId, buffer: buffer, boxed: false)
serializeInt32(maxMsgId, buffer: buffer, boxed: false)
serializeInt32(count, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .searchResultsCalendarPeriod(let date, let minMsgId, let maxMsgId, let count):
return ("searchResultsCalendarPeriod", [("date", date), ("minMsgId", minMsgId), ("maxMsgId", maxMsgId), ("count", count)])
}
}
public static func parse_searchResultsCalendarPeriod(_ reader: BufferReader) -> SearchResultsCalendarPeriod? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.SearchResultsCalendarPeriod.searchResultsCalendarPeriod(date: _1!, minMsgId: _2!, maxMsgId: _3!, count: _4!)
}
else {
return nil
}
}
}
public enum Folder: TypeConstructorDescription {
case folder(flags: Int32, id: Int32, title: String, photo: Api.ChatPhoto?)
@ -11366,7 +11424,6 @@ public extension Api {
case channelAdminLogEventActionExportedInviteEdit(prevInvite: Api.ExportedChatInvite, newInvite: Api.ExportedChatInvite)
case channelAdminLogEventActionParticipantVolume(participant: Api.GroupCallParticipant)
case channelAdminLogEventActionChangeHistoryTTL(prevValue: Int32, newValue: Int32)
case channelAdminLogEventActionChangeTheme(prevValue: String, newValue: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
@ -11576,13 +11633,6 @@ public extension Api {
serializeInt32(prevValue, buffer: buffer, boxed: false)
serializeInt32(newValue, buffer: buffer, boxed: false)
break
case .channelAdminLogEventActionChangeTheme(let prevValue, let newValue):
if boxed {
buffer.appendInt32(-26672755)
}
serializeString(prevValue, buffer: buffer, boxed: false)
serializeString(newValue, buffer: buffer, boxed: false)
break
}
}
@ -11652,8 +11702,6 @@ public extension Api {
return ("channelAdminLogEventActionParticipantVolume", [("participant", participant)])
case .channelAdminLogEventActionChangeHistoryTTL(let prevValue, let newValue):
return ("channelAdminLogEventActionChangeHistoryTTL", [("prevValue", prevValue), ("newValue", newValue)])
case .channelAdminLogEventActionChangeTheme(let prevValue, let newValue):
return ("channelAdminLogEventActionChangeTheme", [("prevValue", prevValue), ("newValue", newValue)])
}
}
@ -12099,20 +12147,6 @@ public extension Api {
return nil
}
}
public static func parse_channelAdminLogEventActionChangeTheme(_ reader: BufferReader) -> ChannelAdminLogEventAction? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.ChannelAdminLogEventAction.channelAdminLogEventActionChangeTheme(prevValue: _1!, newValue: _2!)
}
else {
return nil
}
}
}
public enum SecurePlainData: TypeConstructorDescription {
@ -14786,36 +14820,44 @@ public extension Api {
}
public enum ChatInviteImporter: TypeConstructorDescription {
case chatInviteImporter(userId: Int64, date: Int32)
case chatInviteImporter(flags: Int32, userId: Int64, date: Int32, approvedBy: Int64?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .chatInviteImporter(let userId, let date):
case .chatInviteImporter(let flags, let userId, let date, let approvedBy):
if boxed {
buffer.appendInt32(190633460)
buffer.appendInt32(-1574303204)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(userId, buffer: buffer, boxed: false)
serializeInt32(date, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {serializeInt64(approvedBy!, buffer: buffer, boxed: false)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .chatInviteImporter(let userId, let date):
return ("chatInviteImporter", [("userId", userId), ("date", date)])
case .chatInviteImporter(let flags, let userId, let date, let approvedBy):
return ("chatInviteImporter", [("flags", flags), ("userId", userId), ("date", date), ("approvedBy", approvedBy)])
}
}
public static func parse_chatInviteImporter(_ reader: BufferReader) -> ChatInviteImporter? {
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: Int32?
_3 = reader.readInt32()
var _4: Int64?
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt64() }
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.ChatInviteImporter.chatInviteImporter(userId: _1!, date: _2!)
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.ChatInviteImporter.chatInviteImporter(flags: _1!, userId: _2!, date: _3!, approvedBy: _4)
}
else {
return nil
@ -20074,13 +20116,13 @@ public extension Api {
}
public enum ExportedChatInvite: TypeConstructorDescription {
case chatInviteExported(flags: Int32, link: String, adminId: Int64, date: Int32, startDate: Int32?, expireDate: Int32?, usageLimit: Int32?, usage: Int32?)
case chatInviteExported(flags: Int32, link: String, adminId: Int64, date: Int32, startDate: Int32?, expireDate: Int32?, usageLimit: Int32?, usage: Int32?, approved: Int32?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage):
case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let approved):
if boxed {
buffer.appendInt32(-1316944408)
buffer.appendInt32(19682803)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(link, buffer: buffer, boxed: false)
@ -20090,14 +20132,15 @@ public extension Api {
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(usage!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 7) != 0 {serializeInt32(approved!, buffer: buffer, boxed: false)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage):
return ("chatInviteExported", [("flags", flags), ("link", link), ("adminId", adminId), ("date", date), ("startDate", startDate), ("expireDate", expireDate), ("usageLimit", usageLimit), ("usage", usage)])
case .chatInviteExported(let flags, let link, let adminId, let date, let startDate, let expireDate, let usageLimit, let usage, let approved):
return ("chatInviteExported", [("flags", flags), ("link", link), ("adminId", adminId), ("date", date), ("startDate", startDate), ("expireDate", expireDate), ("usageLimit", usageLimit), ("usage", usage), ("approved", approved)])
}
}
@ -20118,6 +20161,8 @@ public extension Api {
if Int(_1!) & Int(1 << 2) != 0 {_7 = reader.readInt32() }
var _8: Int32?
if Int(_1!) & Int(1 << 3) != 0 {_8 = reader.readInt32() }
var _9: Int32?
if Int(_1!) & Int(1 << 7) != 0 {_9 = reader.readInt32() }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
@ -20126,8 +20171,9 @@ public extension Api {
let _c6 = (Int(_1!) & Int(1 << 1) == 0) || _6 != nil
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
let _c8 = (Int(_1!) & Int(1 << 3) == 0) || _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
return Api.ExportedChatInvite.chatInviteExported(flags: _1!, link: _2!, adminId: _3!, date: _4!, startDate: _5, expireDate: _6, usageLimit: _7, usage: _8)
let _c9 = (Int(_1!) & Int(1 << 7) == 0) || _9 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 && _c9 {
return Api.ExportedChatInvite.chatInviteExported(flags: _1!, link: _2!, adminId: _3!, date: _4!, startDate: _5, expireDate: _6, usageLimit: _7, usage: _8, approved: _9)
}
else {
return nil

View File

@ -3769,22 +3769,6 @@ public extension Api {
})
}
public static func getStatsURL(flags: Int32, peer: Api.InputPeer, params: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.StatsURL>) {
let buffer = Buffer()
buffer.appendInt32(-2127811866)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeString(params, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getStatsURL", parameters: [("flags", flags), ("peer", peer), ("params", params)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.StatsURL? in
let reader = BufferReader(buffer)
var result: Api.StatsURL?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.StatsURL
}
return result
})
}
public static func editChatAbout(peer: Api.InputPeer, about: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(-554301545)
@ -4324,15 +4308,16 @@ public extension Api {
})
}
public static func editExportedChatInvite(flags: Int32, peer: Api.InputPeer, link: String, expireDate: Int32?, usageLimit: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ExportedChatInvite>) {
public static func editExportedChatInvite(flags: Int32, peer: Api.InputPeer, link: String, expireDate: Int32?, usageLimit: Int32?, requestNeeded: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ExportedChatInvite>) {
let buffer = Buffer()
buffer.appendInt32(48562110)
buffer.appendInt32(1557932235)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeString(link, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(expireDate!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(usageLimit!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.editExportedChatInvite", parameters: [("flags", flags), ("peer", peer), ("link", link), ("expireDate", expireDate), ("usageLimit", usageLimit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in
if Int(flags) & Int(1 << 3) != 0 {requestNeeded!.serialize(buffer, true)}
return (FunctionDescription(name: "messages.editExportedChatInvite", parameters: [("flags", flags), ("peer", peer), ("link", link), ("expireDate", expireDate), ("usageLimit", usageLimit), ("requestNeeded", requestNeeded)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ExportedChatInvite? in
let reader = BufferReader(buffer)
var result: Api.messages.ExportedChatInvite?
if let signature = reader.readInt32() {
@ -4386,15 +4371,16 @@ public extension Api {
})
}
public static func getChatInviteImporters(peer: Api.InputPeer, link: String, offsetDate: Int32, offsetUser: Api.InputUser, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ChatInviteImporters>) {
public static func getChatInviteImporters(flags: Int32, peer: Api.InputPeer, link: String?, offsetDate: Int32, offsetUser: Api.InputUser, limit: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.ChatInviteImporters>) {
let buffer = Buffer()
buffer.appendInt32(654013065)
buffer.appendInt32(-1742901790)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
serializeString(link, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {serializeString(link!, buffer: buffer, boxed: false)}
serializeInt32(offsetDate, buffer: buffer, boxed: false)
offsetUser.serialize(buffer, true)
serializeInt32(limit, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getChatInviteImporters", parameters: [("peer", peer), ("link", link), ("offsetDate", offsetDate), ("offsetUser", offsetUser), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatInviteImporters? in
return (FunctionDescription(name: "messages.getChatInviteImporters", parameters: [("flags", flags), ("peer", peer), ("link", link), ("offsetDate", offsetDate), ("offsetUser", offsetUser), ("limit", limit)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.ChatInviteImporters? in
let reader = BufferReader(buffer)
var result: Api.messages.ChatInviteImporters?
if let signature = reader.readInt32() {
@ -4462,6 +4448,57 @@ public extension Api {
return result
})
}
public static func getSearchResultsCalendar(flags: Int32, peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, offsetDate: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.SearchResultsCalendar>) {
let buffer = Buffer()
buffer.appendInt32(-1991714845)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
filter.serialize(buffer, true)
serializeInt32(offsetId, buffer: buffer, boxed: false)
serializeInt32(offsetDate, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getSearchResultsCalendar", parameters: [("flags", flags), ("peer", peer), ("filter", filter), ("offsetId", offsetId), ("offsetDate", offsetDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsCalendar? in
let reader = BufferReader(buffer)
var result: Api.messages.SearchResultsCalendar?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.messages.SearchResultsCalendar
}
return result
})
}
public static func getSearchResultsRawMessages(peer: Api.InputPeer, filter: Api.MessagesFilter, offsetId: Int32, offsetDate: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.messages.SearchResultsRawMessages>) {
let buffer = Buffer()
buffer.appendInt32(1258852762)
peer.serialize(buffer, true)
filter.serialize(buffer, true)
serializeInt32(offsetId, buffer: buffer, boxed: false)
serializeInt32(offsetDate, buffer: buffer, boxed: false)
return (FunctionDescription(name: "messages.getSearchResultsRawMessages", parameters: [("peer", peer), ("filter", filter), ("offsetId", offsetId), ("offsetDate", offsetDate)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.messages.SearchResultsRawMessages? in
let reader = BufferReader(buffer)
var result: Api.messages.SearchResultsRawMessages?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.messages.SearchResultsRawMessages
}
return result
})
}
public static func hideChatJoinRequest(flags: Int32, peer: Api.InputPeer, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
let buffer = Buffer()
buffer.appendInt32(2145904661)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
userId.serialize(buffer, true)
return (FunctionDescription(name: "messages.hideChatJoinRequest", parameters: [("flags", flags), ("peer", peer), ("userId", userId)]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
let reader = BufferReader(buffer)
var result: Api.Updates?
if let signature = reader.readInt32() {
result = Api.parse(reader, signature: signature) as? Api.Updates
}
return result
})
}
}
public struct channels {
public static func readHistory(channel: Api.InputChannel, maxId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {

View File

@ -6,7 +6,7 @@ import TelegramApi
extension ExportedInvitation {
init(apiExportedInvite: Api.ExportedChatInvite) {
switch apiExportedInvite {
case let .chatInviteExported(flags, link, adminId, date, startDate, expireDate, usageLimit, usage):
case let .chatInviteExported(flags, link, adminId, date, startDate, expireDate, usageLimit, usage, _):
self = ExportedInvitation(link: link, isPermanent: (flags & (1 << 5)) != 0, isRevoked: (flags & (1 << 0)) != 0, adminId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(adminId)), date: date, startDate: startDate, expireDate: expireDate, usageLimit: usageLimit, count: usage)
}
}

View File

@ -0,0 +1,359 @@
import SwiftSignalKit
import Postbox
import TelegramApi
public final class SparseMessageList {
private final class Impl {
private let queue: Queue
private let account: Account
private let peerId: PeerId
private let messageTag: MessageTags
private struct TopSection: Equatable {
var messages: [Message]
static func ==(lhs: TopSection, rhs: TopSection) -> Bool {
if lhs.messages.count != rhs.messages.count {
return false
}
for i in 0 ..< lhs.messages.count {
if lhs.messages[i].id != rhs.messages[i].id {
return false
}
if lhs.messages[i].stableVersion != rhs.messages[i].stableVersion {
return false
}
}
return true
}
}
private struct ItemIndices: Equatable {
var ids: [MessageId]
var timestamps: [Int32]
}
private var topSectionItemRequestCount: Int = 100
private var topSection: TopSection?
private var topItemsDisposable: Disposable?
private var messageIndices: ItemIndices?
private var messageIndicesDisposable: Disposable?
private var loadingPlaceholders: [MessageId: Disposable] = [:]
private var loadedPlaceholders: [MessageId: Message] = [:]
let statePromise = Promise<SparseMessageList.State>()
init(queue: Queue, account: Account, peerId: PeerId, messageTag: MessageTags) {
self.queue = queue
self.account = account
self.peerId = peerId
self.messageTag = messageTag
self.resetTopSection()
self.messageIndicesDisposable = (self.account.postbox.transaction { transaction -> Api.InputPeer? in
return transaction.getPeer(peerId).flatMap(apiInputPeer)
}
|> mapToSignal { inputPeer -> Signal<ItemIndices, NoError> in
guard let inputPeer = inputPeer else {
return .single(ItemIndices(ids: [], timestamps: []))
}
return self.account.network.request(Api.functions.messages.getSearchResultsRawMessages(peer: inputPeer, filter: .inputMessagesFilterPhotoVideo, offsetId: 0, offsetDate: 0))
|> map { result -> ItemIndices in
switch result {
case let .searchResultsRawMessages(msgIds, msgDates):
return ItemIndices(ids: msgIds.map { id in
return MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: id)
}, timestamps: msgDates)
}
}
|> `catch` { _ -> Signal<ItemIndices, NoError> in
return .single(ItemIndices(ids: [], timestamps: []))
}
}
|> deliverOnMainQueue).start(next: { [weak self] indices in
guard let strongSelf = self else {
return
}
strongSelf.messageIndices = indices
if strongSelf.topSection != nil {
strongSelf.updateState()
}
})
}
deinit {
self.topItemsDisposable?.dispose()
self.messageIndicesDisposable?.dispose()
}
private func resetTopSection() {
self.topItemsDisposable = (self.account.postbox.aroundMessageHistoryViewForLocation(.peer(peerId), anchor: .upperBound, count: 10, fixedCombinedReadStates: nil, topTaggedMessageIdNamespaces: Set(), tagMask: self.messageTag, appendMessagesFromTheSameGroup: false, namespaces: .not(Set(Namespaces.Message.allScheduled)), orderStatistics: [])
|> deliverOn(self.queue)).start(next: { [weak self] view, updateType, _ in
guard let strongSelf = self else {
return
}
switch updateType {
case .FillHole:
strongSelf.resetTopSection()
default:
strongSelf.updateTopSection(view: view)
}
})
}
func loadMoreFromTopSection() {
self.topSectionItemRequestCount += 100
self.resetTopSection()
}
func loadPlaceholders(ids: [MessageId]) {
var loadGlobalIds: [MessageId] = []
var loadChannelIds: [PeerId: [MessageId]] = [:]
for id in ids {
if self.loadingPlaceholders[id] != nil {
continue
}
self.loadingPlaceholders[id] = MetaDisposable()
if id.peerId.namespace == Namespaces.Peer.CloudUser || id.peerId.namespace == Namespaces.Peer.CloudGroup {
loadGlobalIds.append(id)
} else if id.peerId.namespace == Namespaces.Peer.CloudChannel {
if loadChannelIds[id.peerId] == nil {
loadChannelIds[id.peerId] = []
}
loadChannelIds[id.peerId]!.append(id)
}
}
var loadSignals: [Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError>] = []
let account = self.account
if !loadGlobalIds.isEmpty {
loadSignals.append(self.account.postbox.transaction { transaction -> [Api.InputMessage] in
var result: [Api.InputMessage] = []
for id in loadGlobalIds {
let inputMessage: Api.InputMessage = .inputMessageID(id: id.id)
result.append(inputMessage)
}
return result
}
|> mapToSignal { inputMessages -> Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError> in
return account.network.request(Api.functions.messages.getMessages(id: inputMessages))
|> map { result -> (messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) in
switch result {
case let .messages(messages, chats, users):
return (messages, chats, users)
case let .messagesSlice(_, _, _, _, messages, chats, users):
return (messages, chats, users)
case let .channelMessages(_, _, _, _, messages, chats, users):
return (messages, chats, users)
case .messagesNotModified:
return ([], [], [])
}
}
|> `catch` { _ -> Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError> in
return .single(([], [], []))
}
})
}
if !loadChannelIds.isEmpty {
for (channelId, ids) in loadChannelIds {
loadSignals.append(self.account.postbox.transaction { transaction -> Api.InputChannel? in
return transaction.getPeer(channelId).flatMap(apiInputChannel)
}
|> mapToSignal { inputChannel -> Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError> in
guard let inputChannel = inputChannel else {
return .single(([], [], []))
}
return account.network.request(Api.functions.channels.getMessages(channel: inputChannel, id: ids.map { Api.InputMessage.inputMessageID(id: $0.id) }))
|> map { result -> (messages: [Api.Message], chats: [Api.Chat], users: [Api.User]) in
switch result {
case let .messages(messages, chats, users):
return (messages, chats, users)
case let .messagesSlice(_, _, _, _, messages, chats, users):
return (messages, chats, users)
case let .channelMessages(_, _, _, _, messages, chats, users):
return (messages, chats, users)
case .messagesNotModified:
return ([], [], [])
}
}
|> `catch` { _ -> Signal<(messages: [Api.Message], chats: [Api.Chat], users: [Api.User]), NoError> in
return .single(([], [], []))
}
})
}
}
let _ = (combineLatest(queue: self.queue, loadSignals)
|> mapToSignal { messageLists -> Signal<[Message], NoError> in
return account.postbox.transaction { transaction -> [Message] in
var parsedMessages: [StoreMessage] = []
var peers: [Peer] = []
var peerPresences: [PeerId: PeerPresence] = [:]
for (messages, chats, users) in messageLists {
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
}
}
for message in messages {
if let parsedMessage = StoreMessage(apiMessage: message) {
parsedMessages.append(parsedMessage)
}
}
}
let _ = transaction.addMessages(parsedMessages, location: .Random)
var result: [Message] = []
for parsedMessage in parsedMessages {
switch parsedMessage.id {
case let .Id(id):
if let message = transaction.getMessage(id) {
result.append(message)
}
case .Partial:
break
}
}
return result
}
}
|> deliverOn(self.queue)).start(next: { [weak self] messages in
guard let strongSelf = self else {
return
}
for message in messages {
strongSelf.loadedPlaceholders[message.id] = message
}
strongSelf.updateState()
})
}
private func updateTopSection(view: MessageHistoryView) {
var topSection: TopSection?
if view.isLoading {
topSection = nil
} else {
topSection = TopSection(messages: view.entries.map { entry in
return entry.message
})
}
if self.topSection != topSection {
self.topSection = topSection
}
self.updateState()
}
private func updateState() {
var items: [SparseMessageList.State.Item] = []
var minMessageId: MessageId?
if let topSection = self.topSection {
for i in 0 ..< topSection.messages.count {
let message = topSection.messages[i]
items.append(SparseMessageList.State.Item(index: items.count, content: .message(message)))
if let minMessageIdValue = minMessageId {
if message.id < minMessageIdValue {
minMessageId = message.id
}
} else {
minMessageId = message.id
}
}
}
var totalCount = items.count
if let minMessageId = minMessageId, let messageIndices = self.messageIndices {
for i in 0 ..< messageIndices.ids.count {
if messageIndices.ids[i] < minMessageId {
if let message = self.loadedPlaceholders[messageIndices.ids[i]] {
items.append(SparseMessageList.State.Item(index: items.count, content: .message(message)))
} else {
items.append(SparseMessageList.State.Item(index: items.count, content: .placeholder(id: messageIndices.ids[i], timestamp: messageIndices.timestamps[i])))
}
totalCount += 1
}
}
}
self.statePromise.set(.single(SparseMessageList.State(
items: items,
totalCount: items.count,
isLoading: self.topSection == nil
)))
}
}
private let queue: Queue
private let impl: QueueLocalObject<Impl>
public struct State {
public final class Item {
public enum Content {
case message(Message)
case placeholder(id: MessageId, timestamp: Int32)
}
public let index: Int
public let content: Content
init(index: Int, content: Content) {
self.index = index
self.content = content
}
}
public var items: [Item]
public var totalCount: Int
public var isLoading: Bool
}
public var state: Signal<State, NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
disposable.set(impl.statePromise.get().start(next: subscriber.putNext))
}
return disposable
}
}
init(account: Account, peerId: PeerId, messageTag: MessageTags) {
self.queue = .mainQueue()
let queue = self.queue
self.impl = QueueLocalObject(queue: queue, generate: {
return Impl(queue: queue, account: account, peerId: peerId, messageTag: messageTag)
})
}
public func loadMoreFromTopSection() {
self.impl.with { impl in
impl.loadMoreFromTopSection()
}
}
public func loadPlaceholders(ids: [MessageId]) {
self.impl.with { impl in
impl.loadPlaceholders(ids: ids)
}
}
}

View File

@ -241,5 +241,9 @@ public extension TelegramEngine {
}
}
}
public func sparseMessageList(peerId: EnginePeer.Id, tag: EngineMessage.Tags) -> SparseMessageList {
return SparseMessageList(account: self.account, peerId: peerId, messageTag: tag)
}
}
}

View File

@ -250,8 +250,8 @@ func channelAdminLogEvents(postbox: Postbox, network: Network, peerId: PeerId, m
action = .groupCallUpdateParticipantVolume(peerId: parsedParticipant.peerId, volume: parsedParticipant.volume ?? 10000)
case let .channelAdminLogEventActionChangeHistoryTTL(prevValue, newValue):
action = .changeHistoryTTL(previousValue: prevValue, updatedValue: newValue)
case let .channelAdminLogEventActionChangeTheme(prevValue, newValue):
action = .changeTheme(previous: prevValue, updated: newValue)
/*case let .channelAdminLogEventActionChangeTheme(prevValue, newValue):
action = .changeTheme(previous: prevValue, updated: newValue)*/
}
let peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
if let action = action {

View File

@ -92,7 +92,7 @@ func _internal_editPeerExportedInvitation(account: Account, peerId: PeerId, link
if let _ = usageLimit {
flags |= (1 << 1)
}
return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: expireDate, usageLimit: usageLimit))
return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: expireDate, usageLimit: usageLimit, requestNeeded: .boolFalse))
|> mapError { _ in return EditPeerExportedInvitationError.generic }
|> mapToSignal { result -> Signal<ExportedInvitation?, EditPeerExportedInvitationError> in
return account.postbox.transaction { transaction in
@ -132,7 +132,7 @@ func _internal_revokePeerExportedInvitation(account: Account, peerId: PeerId, li
return account.postbox.transaction { transaction -> Signal<RevokeExportedInvitationResult?, RevokePeerExportedInvitationError> in
if let peer = transaction.getPeer(peerId), let inputPeer = apiInputPeer(peer) {
let flags: Int32 = (1 << 2)
return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: nil, usageLimit: nil))
return account.network.request(Api.functions.messages.editExportedChatInvite(flags: flags, peer: inputPeer, link: link, expireDate: nil, usageLimit: nil, requestNeeded: .boolFalse))
|> mapError { _ in return RevokePeerExportedInvitationError.generic }
|> mapToSignal { result -> Signal<RevokeExportedInvitationResult?, RevokePeerExportedInvitationError> in
return account.postbox.transaction { transaction in
@ -775,7 +775,7 @@ private final class PeerInvitationImportersContextImpl {
if let inputPeer = inputPeer {
let offsetUser = lastResult?.peer.peer.flatMap { apiInputUser($0) } ?? .inputUserEmpty
let offsetDate = lastResult?.date ?? 0
let signal = account.network.request(Api.functions.messages.getChatInviteImporters(peer: inputPeer, link: link, offsetDate: offsetDate, offsetUser: offsetUser, limit: lastResult == nil ? 10 : 50))
let signal = account.network.request(Api.functions.messages.getChatInviteImporters(flags: 0, peer: inputPeer, link: link, offsetDate: offsetDate, offsetUser: offsetUser, limit: lastResult == nil ? 10 : 50))
|> map(Optional.init)
|> `catch` { _ -> Signal<Api.messages.ChatInviteImporters?, NoError> in
return .single(nil)
@ -799,7 +799,7 @@ private final class PeerInvitationImportersContextImpl {
let peerId: PeerId
let date: Int32
switch importer {
case let .chatInviteImporter(userId, dateValue):
case let .chatInviteImporter(_, userId, dateValue, _):
peerId = PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(userId))
date = dateValue
}

View File

@ -67,7 +67,7 @@ func _internal_joinLinkInformation(_ hash: String, account: Account) -> Signal<E
|> mapToSignal { (result) -> Signal<ExternalJoiningChatState, NoError> in
if let result = result {
switch result {
case let .chatInvite(_, title, invitePhoto, participantsCount, participants):
case let .chatInvite(_, title, _, invitePhoto, participantsCount, participants):
let photo = telegramMediaImageFromApiPhoto(invitePhoto).flatMap({ smallestImageRepresentation($0.representations) })
return .single(.invite(title: title, photoRepresentation: photo, participantsCount: participantsCount, participants: participants?.map({TelegramUser(user: $0)})))
case let .chatInviteAlready(chat):

View File

@ -222,14 +222,14 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
switch result {
case let .chatFull(fullChat, chats, users):
switch fullChat {
case let .chatFull(_, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _):
case let .chatFull(_, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _):
transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)])
case .channelFull:
break
}
switch fullChat {
case let .chatFull(chatFullFlags, _, chatFullAbout, chatFullParticipants, chatFullChatPhoto, _, chatFullExportedInvite, chatFullBotInfo, chatFullPinnedMsgId, _, chatFullCall, _, chatFullGroupcallDefaultJoinAs, chatFullThemeEmoticon):
case let .chatFull(chatFullFlags, _, chatFullAbout, chatFullParticipants, chatFullChatPhoto, _, chatFullExportedInvite, chatFullBotInfo, chatFullPinnedMsgId, _, chatFullCall, _, chatFullGroupcallDefaultJoinAs, chatFullThemeEmoticon, _):
var botInfos: [CachedPeerBotInfo] = []
for botInfo in chatFullBotInfo ?? [] {
switch botInfo {
@ -350,14 +350,14 @@ func _internal_fetchAndUpdateCachedPeerData(accountPeerId: PeerId, peerId rawPee
switch result {
case let .chatFull(fullChat, chats, users):
switch fullChat {
case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
case let .channelFull(_, _, _, _, _, _, _, _, _, _, _, _, notifySettings, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
transaction.updateCurrentPeerNotificationSettings([peerId: TelegramPeerNotificationSettings(apiSettings: notifySettings)])
case .chatFull:
break
}
switch fullChat {
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, _, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, _, inputCall, ttl, pendingSuggestions, groupcallDefaultJoinAs, themeEmoticon):
case let .channelFull(flags, _, about, participantsCount, adminsCount, kickedCount, bannedCount, _, _, _, _, chatPhoto, _, apiExportedInvite, apiBotInfos, migratedFromChatId, migratedFromMaxId, pinnedMsgId, stickerSet, minAvailableMsgId, _, linkedChatId, location, slowmodeSeconds, slowmodeNextSendDate, statsDc, _, inputCall, ttl, pendingSuggestions, groupcallDefaultJoinAs, themeEmoticon, _):
var channelFlags = CachedChannelFlags()
if (flags & (1 << 3)) != 0 {
channelFlags.insert(.canDisplayParticipants)

View File

@ -241,6 +241,7 @@ swift_library(
"//submodules/WallpaperBackgroundNode:WallpaperBackgroundNode",
"//submodules/ComponentFlow:ComponentFlow",
"//submodules/AdUI:AdUI",
"//submodules/SparseItemGrid:SparseItemGrid",
] + select({
"@build_bazel_rules_apple//apple:ios_armv7": [],
"@build_bazel_rules_apple//apple:ios_arm64": appcenter_targets,

View File

@ -13,6 +13,7 @@ import GridMessageSelectionNode
import UniversalMediaPlayer
import ListMessageItem
import ChatMessageInteractiveMediaBadge
import SparseItemGrid
private final class FrameSequenceThumbnailNode: ASDisplayNode {
private let context: AccountContext
@ -178,10 +179,10 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.containerNode.addSubnode(self.mediaBadgeNode)
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let item = strongSelf.item else {
guard let strongSelf = self, let item = strongSelf.item, let message = item.0.message else {
return
}
strongSelf.interaction.openMessageContextActions(item.0.message, strongSelf.containerNode, strongSelf.containerNode.bounds, gesture)
strongSelf.interaction.openMessageContextActions(message, strongSelf.containerNode, strongSelf.containerNode.bounds, gesture)
}
}
@ -208,9 +209,9 @@ private final class VisualMediaItemNode: ASDisplayNode {
if case .ended = recognizer.state {
if let (gesture, _) = recognizer.lastRecognizedGestureAndLocation {
if case .tap = gesture {
if let (item, _, _, _) = self.item {
if let (item, _, _, _) = self.item, let message = item.message {
var media: Media?
for value in item.message.media {
for value in message.media {
if let image = value as? TelegramMediaImage {
media = image
break
@ -222,13 +223,13 @@ private final class VisualMediaItemNode: ASDisplayNode {
if let media = media {
if let file = media as? TelegramMediaFile {
if isMediaStreamable(message: item.message, media: file) {
self.interaction.openMessage(item.message)
if isMediaStreamable(message: message, media: file) {
self.interaction.openMessage(message)
} else {
self.progressPressed()
}
} else {
self.interaction.openMessage(item.message)
self.interaction.openMessage(message)
}
}
}
@ -275,17 +276,19 @@ private final class VisualMediaItemNode: ASDisplayNode {
}
self.theme = theme
var media: Media?
for value in item.message.media {
if let image = value as? TelegramMediaImage {
media = image
break
} else if let file = value as? TelegramMediaFile {
media = file
break
if let message = item.message {
for value in message.media {
if let image = value as? TelegramMediaImage {
media = image
break
} else if let file = value as? TelegramMediaFile {
media = file
break
}
}
}
if let file = media as? TelegramMediaFile, file.isAnimated {
if let message = item.message, let file = media as? TelegramMediaFile, file.isAnimated {
if self.videoLayerFrameManager == nil {
let sampleBufferLayer: SampleBufferLayer
if let current = self.sampleBufferLayer {
@ -296,7 +299,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.imageNode.layer.addSublayer(sampleBufferLayer.layer)
}
self.videoLayerFrameManager = SoftwareVideoLayerFrameManager(account: self.context.account, fileReference: FileMediaReference.message(message: MessageReference(item.message), media: file), layerHolder: sampleBufferLayer)
self.videoLayerFrameManager = SoftwareVideoLayerFrameManager(account: self.context.account, fileReference: FileMediaReference.message(message: MessageReference(message), media: file), layerHolder: sampleBufferLayer)
self.videoLayerFrameManager?.start()
}
} else {
@ -307,12 +310,12 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.videoLayerFrameManager = nil
}
if let media = media, (self.item?.1 == nil || !media.isEqual(to: self.item!.1!)) {
if let message = item.message, let media = media, (self.item?.1 == nil || !media.isEqual(to: self.item!.1!)) {
var mediaDimensions: CGSize?
if let image = media as? TelegramMediaImage, let largestSize = largestImageRepresentation(image.representations)?.dimensions {
mediaDimensions = largestSize.cgSize
self.imageNode.setSignal(mediaGridMessagePhoto(account: context.account, photoReference: .message(message: MessageReference(item.message), media: image), fullRepresentationSize: CGSize(width: 300.0, height: 300.0), synchronousLoad: synchronousLoad), attemptSynchronously: synchronousLoad, dispatchOnDisplayLink: true)
self.imageNode.setSignal(mediaGridMessagePhoto(account: context.account, photoReference: .message(message: MessageReference(message), media: image), fullRepresentationSize: CGSize(width: 300.0, height: 300.0), synchronousLoad: synchronousLoad), attemptSynchronously: synchronousLoad, dispatchOnDisplayLink: true)
self.fetchStatusDisposable.set(nil)
self.statusNode.transitionToState(.none, completion: { [weak self] in
@ -322,7 +325,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.resourceStatus = nil
} else if let file = media as? TelegramMediaFile, file.isVideo {
mediaDimensions = file.dimensions?.cgSize
self.imageNode.setSignal(mediaGridMessageVideo(postbox: context.account.postbox, videoReference: .message(message: MessageReference(item.message), media: file), synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: true), attemptSynchronously: synchronousLoad)
self.imageNode.setSignal(mediaGridMessageVideo(postbox: context.account.postbox, videoReference: .message(message: MessageReference(message), media: file), synchronousLoad: synchronousLoad, autoFetchFullSizeThumbnail: true), attemptSynchronously: synchronousLoad)
self.mediaBadgeNode.isHidden = file.isAnimated
@ -330,12 +333,12 @@ private final class VisualMediaItemNode: ASDisplayNode {
self.item = (item, media, size, mediaDimensions)
self.fetchStatusDisposable.set((messageMediaFileStatus(context: context, messageId: item.message.id, file: file)
self.fetchStatusDisposable.set((messageMediaFileStatus(context: context, messageId: message.id, file: file)
|> deliverOnMainQueue).start(next: { [weak self] status in
if let strongSelf = self, let (item, _, _, _) = strongSelf.item {
if let strongSelf = self, let (item, _, _, _) = strongSelf.item, let message = item.message {
strongSelf.resourceStatus = status
let isStreamable = isMediaStreamable(message: item.message, media: file)
let isStreamable = isMediaStreamable(message: message, media: file)
var statusState: RadialStatusNodeState = .none
if isStreamable || file.isAnimated {
@ -484,18 +487,18 @@ private final class VisualMediaItemNode: ASDisplayNode {
}
func updateSelectionState(animated: Bool) {
if let (item, _, _, _) = self.item, let theme = self.theme {
if let (item, _, _, _) = self.item, let message = item.message, let theme = self.theme {
self.containerNode.isGestureEnabled = self.interaction.selectedMessageIds == nil
if let selectedIds = self.interaction.selectedMessageIds {
let selected = selectedIds.contains(item.message.id)
let selected = selectedIds.contains(message.id)
if let selectionNode = self.selectionNode {
selectionNode.updateSelected(selected, animated: animated)
selectionNode.frame = CGRect(origin: CGPoint(), size: self.bounds.size)
} else {
let selectionNode = GridMessageSelectionNode(theme: theme, toggle: { [weak self] value in
if let strongSelf = self, let messageId = strongSelf.item?.0.message.id {
if let strongSelf = self, let messageId = strongSelf.item?.0.message?.id {
var toggledValue = true
if let selectedMessageIds = strongSelf.interaction.selectedMessageIds, selectedMessageIds.contains(messageId) {
toggledValue = false
@ -549,7 +552,7 @@ private final class VisualMediaItemNode: ASDisplayNode {
func updateHiddenMedia() {
if let (item, _, _, _) = self.item {
if let _ = self.interaction.hiddenMedia[item.message.id] {
if let _ = self.interaction.hiddenMedia[item.id] {
self.isHidden = true
} else {
self.isHidden = false
@ -562,29 +565,37 @@ private final class VisualMediaItemNode: ASDisplayNode {
}
private final class VisualMediaItem {
let message: Message
let dimensions: CGSize
let aspectRatio: CGFloat
let id: MessageId
let timestamp: Int32
let message: Message?
enum StableId: Hashable {
case message(UInt32)
case placeholder(MessageId)
}
var stableId: StableId {
if let message = self.message {
return .message(message.stableId)
} else {
return .placeholder(self.id)
}
}
init(message: Message) {
self.message = message
var aspectRatio: CGFloat = 1.0
var dimensions = CGSize(width: 100.0, height: 100.0)
for media in message.media {
if let file = media as? TelegramMediaFile {
if let dimensionsValue = file.dimensions, dimensions.height > 1 {
dimensions = dimensionsValue.cgSize
aspectRatio = CGFloat(dimensionsValue.width) / CGFloat(dimensionsValue.height)
}
}
}
self.aspectRatio = aspectRatio
self.dimensions = dimensions
self.id = message.id
self.timestamp = message.timestamp
}
init(id: MessageId, timestamp: Int32) {
self.id = id
self.timestamp = timestamp
self.message = nil
}
}
private final class FloatingHeaderNode: ASDisplayNode {
/*private final class FloatingHeaderNode: ASDisplayNode {
private let backgroundNode: ASImageNode
private let labelNode: ImmediateTextNode
@ -631,7 +642,7 @@ private final class FloatingHeaderNode: ASDisplayNode {
let size = CGSize(width: labelSize.width + sideInset * 2.0, height: 27.0)
return size
}
}
}*/
private func tagMaskForType(_ type: PeerInfoVisualMediaPaneNode.ContentType) -> MessageTags {
switch type {
@ -684,52 +695,12 @@ private enum ItemsLayout {
}
}
final class Balanced {
let frames: [CGRect]
let contentHeight: CGFloat
init(containerWidth: CGFloat, items: [VisualMediaItem], bottomInset: CGFloat) {
self.frames = calculateItemFrames(items: items, containerWidth: containerWidth)
if let last = self.frames.last {
self.contentHeight = last.maxY + bottomInset
} else {
self.contentHeight = bottomInset
}
}
func visibleRange(rect: CGRect) -> (Int, Int) {
for i in 0 ..< self.frames.count {
if self.frames[i].maxY >= rect.minY {
for j in i ..< self.frames.count {
if self.frames[j].minY >= rect.maxY {
return (i, j - 1)
}
}
return (i, self.frames.count - 1)
}
}
return (0, -1)
}
func frame(forItemAt index: Int, sideInset: CGFloat) -> CGRect {
if index >= 0 && index < self.frames.count {
return self.frames[index]
} else {
assertionFailure()
return CGRect(origin: CGPoint(), size: CGSize(width: 100.0, height: 100.0))
}
}
}
case grid(Grid)
case balanced(Balanced)
var contentHeight: CGFloat {
switch self {
case let .grid(grid):
return grid.contentHeight
case let .balanced(balanced):
return balanced.contentHeight
}
}
@ -737,8 +708,6 @@ private enum ItemsLayout {
switch self {
case let .grid(grid):
return grid.visibleRange(rect: rect)
case let .balanced(balanced):
return balanced.visibleRange(rect: rect)
}
}
@ -746,8 +715,6 @@ private enum ItemsLayout {
switch self {
case let .grid(grid):
return grid.frame(forItemAt: index, sideInset: sideInset)
case let .balanced(balanced):
return balanced.frame(forItemAt: index, sideInset: sideInset)
}
}
}
@ -764,10 +731,10 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
private let contentType: ContentType
weak var parentController: ViewController?
private let scrollingArea: SparseItemGridScrollingArea
private let scrollNode: ASScrollNode
private let floatingHeaderNode: FloatingHeaderNode
private var flashHeaderDelayTimer: Foundation.Timer?
private var isDeceleratingAfterTracking = false
private var _itemInteraction: VisualMediaItemInteraction?
@ -787,28 +754,39 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
private var hiddenMediaDisposable: Disposable?
private var mediaItems: [VisualMediaItem] = []
private var itemsLayout: ItemsLayout?
private var visibleMediaItems: [UInt32: VisualMediaItemNode] = [:]
private var visibleMediaItems: [VisualMediaItem.StableId: VisualMediaItemNode] = [:]
private var numberOfItemsToRequest: Int = 50
private var currentView: MessageHistoryView?
//private var currentView: MessageHistoryView?
private var isRequestingView: Bool = false
private var isFirstHistoryView: Bool = true
private var decelerationAnimator: ConstantDisplayLinkAnimator?
private var animationTimer: SwiftSignalKit.Timer?
private let listSource: SparseMessageList
private var requestedPlaceholderIds = Set<MessageId>()
init(context: AccountContext, chatControllerInteraction: ChatControllerInteraction, peerId: PeerId, contentType: ContentType) {
self.context = context
self.peerId = peerId
self.chatControllerInteraction = chatControllerInteraction
self.contentType = contentType
self.scrollingArea = SparseItemGridScrollingArea()
self.scrollNode = ASScrollNode()
self.floatingHeaderNode = FloatingHeaderNode()
self.floatingHeaderNode.alpha = 0.0
self.listSource = self.context.engine.messages.sparseMessageList(peerId: self.peerId, tag: tagMaskForType(self.contentType))
super.init()
self.scrollingArea.beginScrolling = { [weak self] in
guard let strongSelf = self else {
return nil
}
return strongSelf.scrollNode.view
}
self._itemInteraction = VisualMediaItemInteraction(
openMessage: { [weak self] message in
@ -833,7 +811,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
self.scrollNode.view.delegate = self
self.addSubnode(self.scrollNode)
self.addSubnode(self.floatingHeaderNode)
self.addSubnode(self.scrollingArea)
self.requestHistoryAroundVisiblePosition()
@ -874,8 +852,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
func ensureMessageIsVisible(id: MessageId) {
let activeRect = self.scrollNode.bounds
for item in self.mediaItems {
if item.message.id == id {
if let itemNode = self.visibleMediaItems[item.message.stableId] {
if let message = item.message, message.id == id {
if let itemNode = self.visibleMediaItems[item.stableId] {
if !activeRect.contains(itemNode.frame) {
let targetContentOffset = CGPoint(x: 0.0, y: max(-self.scrollNode.view.contentInset.top, itemNode.frame.minY - (self.scrollNode.frame.height - itemNode.frame.height) / 2.0))
self.scrollNode.view.setContentOffset(targetContentOffset, animated: false)
@ -891,38 +869,38 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
return
}
self.isRequestingView = true
self.listDisposable.set((self.context.account.viewTracker.aroundMessageHistoryViewForLocation(.peer(self.peerId), index: .upperBound, anchorIndex: .upperBound, count: self.numberOfItemsToRequest, fixedCombinedReadStates: nil, tagMask: tagMaskForType(self.contentType))
|> deliverOnMainQueue).start(next: { [weak self] (view, updateType, _) in
self.listDisposable.set((self.listSource.state
|> deliverOnMainQueue).start(next: { [weak self] list in
guard let strongSelf = self else {
return
}
strongSelf.updateHistory(view: view, updateType: updateType)
strongSelf.updateHistory(list: list)
strongSelf.isRequestingView = false
}))
}
private func updateHistory(view: MessageHistoryView, updateType: ViewUpdateType) {
self.currentView = view
switch updateType {
case .FillHole:
self.requestHistoryAroundVisiblePosition()
default:
self.mediaItems.removeAll()
for entry in view.entries.reversed() {
self.mediaItems.append(VisualMediaItem(message: entry.message))
private func updateHistory(list: SparseMessageList.State) {
//self.currentView = view
self.mediaItems.removeAll()
for item in list.items {
switch item.content {
case let .message(message):
self.mediaItems.append(VisualMediaItem(message: message))
case let .placeholder(id, timestamp):
self.mediaItems.append(VisualMediaItem(id: id, timestamp: timestamp))
}
self.itemsLayout = nil
let wasFirstHistoryView = self.isFirstHistoryView
self.isFirstHistoryView = false
if let (size, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams {
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: wasFirstHistoryView, transition: .immediate)
if !self.didSetReady {
self.didSetReady = true
self.ready.set(.single(true))
}
}
self.itemsLayout = nil
let wasFirstHistoryView = self.isFirstHistoryView
self.isFirstHistoryView = false
if let (size, sideInset, bottomInset, visibleHeight, isScrollingLockedAtTop, expandProgress, presentationData) = self.currentParams {
self.update(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, isScrollingLockedAtTop: isScrollingLockedAtTop, expandProgress: expandProgress, presentationData: presentationData, synchronous: wasFirstHistoryView, transition: .immediate)
if !self.didSetReady {
self.didSetReady = true
self.ready.set(.single(true))
}
}
}
@ -938,7 +916,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
func findLoadedMessage(id: MessageId) -> Message? {
for item in self.mediaItems {
if item.message.id == id {
if let message = item.message, message.id == id {
return item.message
}
}
@ -1003,8 +981,8 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
func transitionNodeForGallery(messageId: MessageId, media: Media) -> (ASDisplayNode, CGRect, () -> (UIView?, UIView?))? {
for item in self.mediaItems {
if item.message.id == messageId {
if let itemNode = self.visibleMediaItems[item.message.stableId] {
if let message = item.message, message.id == messageId {
if let itemNode = self.visibleMediaItems[item.stableId] {
return itemNode.transitionNode()
}
break
@ -1039,14 +1017,15 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
switch self.contentType {
case .photoOrVideo, .gifs:
itemsLayout = .grid(ItemsLayout.Grid(containerWidth: availableWidth, itemCount: self.mediaItems.count, bottomInset: bottomInset))
/*case .gifs:
itemsLayout = .balanced(ItemsLayout.Balanced(containerWidth: availableWidth, items: self.mediaItems, bottomInset: bottomInset))*/
}
self.itemsLayout = itemsLayout
}
self.scrollNode.view.contentSize = CGSize(width: size.width, height: itemsLayout.contentHeight)
self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, strings: presentationData.strings, synchronousLoad: synchronous)
transition.updateFrame(node: self.scrollingArea, frame: CGRect(origin: CGPoint(), size: size))
self.updateScrollingArea(transition: transition)
if isScrollingLockedAtTop {
if self.scrollNode.view.contentOffset.y > .ulpOfOne {
@ -1073,12 +1052,14 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
if let (size, sideInset, bottomInset, visibleHeight, _, _, presentationData) = self.currentParams {
self.updateVisibleItems(size: size, sideInset: sideInset, bottomInset: bottomInset, visibleHeight: visibleHeight, theme: presentationData.theme, strings: presentationData.strings, synchronousLoad: false)
if scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.bounds.height * 2.0, let currentView = self.currentView, currentView.earlierId != nil {
/*if scrollView.contentOffset.y >= scrollView.contentSize.height - scrollView.bounds.height * 2.0, let currentView = self.currentView, currentView.earlierId != nil {
if !self.isRequestingView {
self.numberOfItemsToRequest += 50
self.requestHistoryAroundVisiblePosition()
}
}
}*/
self.updateScrollingArea(transition: .immediate)
}
}
@ -1091,12 +1072,61 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
self.resetHeaderFlashTimer(start: true)
self.updateHeaderFlashing(animated: true)
}
if !decelerate {
self.updateScrollingArea(transition: .animated(duration: 0.3, curve: .easeInOut))
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.isDeceleratingAfterTracking = false
self.resetHeaderFlashTimer(start: true)
self.updateHeaderFlashing(animated: true)
self.updateScrollingArea(transition: .animated(duration: 0.3, curve: .easeInOut))
}
private func updateScrollingArea(transition: ContainedViewLayoutTransition) {
guard let currentParams = self.currentParams, let itemsLayout = self.itemsLayout else {
return
}
let headerItemMinY = self.scrollNode.view.bounds.minY + 20.0
let activeRect = self.scrollNode.view.bounds
let visibleRect = activeRect.insetBy(dx: 0.0, dy: -400.0)
let (minVisibleIndex, maxVisibleIndex) = itemsLayout.visibleRange(rect: visibleRect)
var headerItem: Int32?
if minVisibleIndex <= maxVisibleIndex {
for i in minVisibleIndex ... maxVisibleIndex {
let itemFrame = itemsLayout.frame(forItemAt: i, sideInset: currentParams.sideInset)
if headerItem == nil && itemFrame.maxY > headerItemMinY {
headerItem = self.mediaItems[i].timestamp
break
}
}
}
var dateString: String = ""
if let headerItem = headerItem {
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
let (year, month) = listMessageDateHeaderInfo(timestamp: headerItem)
dateString = stringForMonth(strings: presentationData.strings, month: month, ofYear: year)
}
self.scrollingArea.update(
containerSize: currentParams.size,
containerInsets: UIEdgeInsets(top: 0.0, left: currentParams.sideInset, bottom: currentParams.bottomInset, right: currentParams.sideInset),
contentHeight: itemsLayout.contentHeight,
contentOffset: self.scrollNode.view.contentOffset.y,
isScrolling: self.scrollNode.view.isDragging || self.scrollNode.view.isDecelerating,
dateString: dateString,
transition: transition
)
}
private func updateVisibleItems(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, theme: PresentationTheme, strings: PresentationStrings, synchronousLoad: Bool) {
@ -1104,19 +1134,24 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
return
}
let headerItemMinY = self.scrollNode.view.bounds.minY + 20.0
let activeRect = self.scrollNode.view.bounds
let visibleRect = activeRect.insetBy(dx: 0.0, dy: -400.0)
let (minActuallyVisibleIndex, maxActuallyVisibleIndex) = itemsLayout.visibleRange(rect: activeRect)
let (minVisibleIndex, maxVisibleIndex) = itemsLayout.visibleRange(rect: visibleRect)
var requestPlaceholderIds: [MessageId] = []
var headerItem: Message?
var validIds = Set<UInt32>()
var validIds = Set<VisualMediaItem.StableId>()
if minVisibleIndex <= maxVisibleIndex {
for i in minVisibleIndex ... maxVisibleIndex {
let stableId = self.mediaItems[i].message.stableId
let stableId = self.mediaItems[i].stableId
validIds.insert(stableId)
if self.mediaItems[i].message == nil && !self.requestedPlaceholderIds.contains(self.mediaItems[i].id) {
requestPlaceholderIds.append(self.mediaItems[i].id)
self.requestedPlaceholderIds.insert(self.mediaItems[i].id)
}
let itemFrame = itemsLayout.frame(forItemAt: i, sideInset: sideInset)
@ -1129,18 +1164,16 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
self.scrollNode.addSubnode(itemNode)
}
itemNode.frame = itemFrame
if headerItem == nil && itemFrame.maxY > headerItemMinY {
headerItem = self.mediaItems[i].message
}
var itemSynchronousLoad = false
if itemFrame.maxY <= visibleHeight {
if i >= minActuallyVisibleIndex && i <= maxActuallyVisibleIndex {
itemSynchronousLoad = synchronousLoad
}
itemNode.update(size: itemFrame.size, item: self.mediaItems[i], theme: theme, synchronousLoad: itemSynchronousLoad)
itemNode.updateIsVisible(itemFrame.intersects(activeRect))
}
}
var removeKeys: [UInt32] = []
var removeKeys: [VisualMediaItem.StableId] = []
for (id, _) in self.visibleMediaItems {
if !validIds.contains(id) {
removeKeys.append(id)
@ -1152,18 +1185,13 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
}
}
if let headerItem = headerItem {
let (year, month) = listMessageDateHeaderInfo(timestamp: headerItem.timestamp)
let headerSize = self.floatingHeaderNode.update(constrainedWidth: size.width, year: year, month: month, theme: theme, strings: strings)
self.floatingHeaderNode.frame = CGRect(origin: CGPoint(x: floor((size.width - headerSize.width) / 2.0), y: 7.0), size: headerSize)
self.floatingHeaderNode.isHidden = false
} else {
self.floatingHeaderNode.isHidden = true
if !requestPlaceholderIds.isEmpty {
self.listSource.loadPlaceholders(ids: requestPlaceholderIds)
}
}
private func resetHeaderFlashTimer(start: Bool, duration: Double = 0.3) {
if let flashHeaderDelayTimer = self.flashHeaderDelayTimer {
/*if let flashHeaderDelayTimer = self.flashHeaderDelayTimer {
flashHeaderDelayTimer.invalidate()
self.flashHeaderDelayTimer = nil
}
@ -1194,15 +1222,16 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
self.flashHeaderDelayTimer = timer
RunLoop.main.add(timer, forMode: RunLoop.Mode.common)
self.updateHeaderFlashing(animated: true)
}
}*/
}
private func headerIsFlashing() -> Bool {
return self.scrollNode.view.isDragging || self.isDeceleratingAfterTracking || self.flashHeaderDelayTimer != nil
return false
//return self.scrollNode.view.isDragging || self.isDeceleratingAfterTracking || self.flashHeaderDelayTimer != nil
}
private func updateHeaderFlashing(animated: Bool) {
let flashing = self.headerIsFlashing()
/*let flashing = self.headerIsFlashing()
let alpha: CGFloat = flashing ? 1.0 : 0.0
let previousAlpha = self.floatingHeaderNode.alpha
@ -1212,7 +1241,7 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
let duration: Double = flashing ? 0.3 : 0.4
self.floatingHeaderNode.layer.animateAlpha(from: previousAlpha, to: alpha, duration: duration)
}
}
}*/
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
@ -1228,107 +1257,3 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
return result
}
}
private func calculateItemFrames(items: [VisualMediaItem], containerWidth: CGFloat) -> [CGRect] {
var frames: [CGRect] = []
var rowsCount = 0
var firstRowMax = 0;
let viewPortAvailableSize = containerWidth
let preferredRowSize: CGFloat = 100.0
let itemsCount = items.count
let spanCount: CGFloat = 100.0
var spanLeft = spanCount
var currentItemsInRow = 0
var currentItemsSpanAmount: CGFloat = 0.0
var itemSpans: [Int: CGFloat] = [:]
var itemsToRow: [Int: Int] = [:]
for a in 0 ..< itemsCount {
var size: CGSize = items[a].dimensions
if size.width <= 0.0 {
size.width = 100.0
}
if size.height <= 0.0 {
size.height = 100.0
}
let aspect: CGFloat = size.width / size.height
if aspect > 4.0 || aspect < 0.2 {
size.width = max(size.width, size.height)
size.height = size.width
}
var requiredSpan = min(spanCount, floor(spanCount * (size.width / size.height * preferredRowSize / viewPortAvailableSize)))
let moveToNewRow = spanLeft < requiredSpan || requiredSpan > 33.0 && spanLeft < requiredSpan - 15.0
if moveToNewRow {
if spanLeft > 0 {
let spanPerItem = floor(spanLeft / CGFloat(currentItemsInRow))
let start = a - currentItemsInRow
var b = start
while b < start + currentItemsInRow {
if (b == start + currentItemsInRow - 1) {
itemSpans[b] = itemSpans[b]! + spanLeft
} else {
itemSpans[b] = itemSpans[b]! + spanPerItem
}
spanLeft -= spanPerItem;
b += 1
}
itemsToRow[a - 1] = rowsCount
}
rowsCount += 1
currentItemsSpanAmount = 0
currentItemsInRow = 0
spanLeft = spanCount
} else {
if spanLeft < requiredSpan {
requiredSpan = spanLeft
}
}
if rowsCount == 0 {
firstRowMax = max(firstRowMax, a)
}
if a == itemsCount - 1 {
itemsToRow[a] = rowsCount
}
currentItemsSpanAmount += requiredSpan
currentItemsInRow += 1
spanLeft -= requiredSpan
spanLeft = max(0, spanLeft)
itemSpans[a] = requiredSpan
}
if itemsCount != 0 {
rowsCount += 1
}
var verticalOffset: CGFloat = 1.0
var currentRowHorizontalOffset: CGFloat = 0.0
for index in 0 ..< items.count {
guard let width = itemSpans[index] else {
continue
}
let itemWidth = floor(width * containerWidth / 100.0) - 1
var itemSize = CGSize(width: itemWidth, height: preferredRowSize)
if itemsToRow[index] != nil && currentRowHorizontalOffset + itemSize.width >= containerWidth - 10.0 {
itemSize.width = max(itemSize.width, containerWidth - currentRowHorizontalOffset)
}
frames.append(CGRect(origin: CGPoint(x: currentRowHorizontalOffset, y: verticalOffset), size: itemSize))
currentRowHorizontalOffset += itemSize.width + 1.0
if itemsToRow[index] != nil {
verticalOffset += preferredRowSize + 1.0
currentRowHorizontalOffset = 0.0
}
}
return frames
}