mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-10-09 03:20:48 +00:00
Sparse message API
This commit is contained in:
parent
38c7d6a38c
commit
d73db2811f
@ -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)
|
||||
|
21
submodules/SparseItemGrid/BUILD
Normal file
21
submodules/SparseItemGrid/BUILD
Normal 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",
|
||||
],
|
||||
)
|
8
submodules/SparseItemGrid/Sources/SparseItemGrid.swift
Normal file
8
submodules/SparseItemGrid/Sources/SparseItemGrid.swift
Normal file
@ -0,0 +1,8 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
|
||||
public final class SparseItemGrid: ASDisplayNode {
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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>) {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
|
@ -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,7 +276,8 @@ private final class VisualMediaItemNode: ASDisplayNode {
|
||||
}
|
||||
self.theme = theme
|
||||
var media: Media?
|
||||
for value in item.message.media {
|
||||
if let message = item.message {
|
||||
for value in message.media {
|
||||
if let image = value as? TelegramMediaImage {
|
||||
media = image
|
||||
break
|
||||
@ -284,8 +286,9 @@ private final class VisualMediaItemNode: ASDisplayNode {
|
||||
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
|
||||
self.id = message.id
|
||||
self.timestamp = message.timestamp
|
||||
}
|
||||
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -765,9 +732,9 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
|
||||
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,10 +754,10 @@ 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
|
||||
|
||||
@ -798,18 +765,29 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
|
||||
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
|
||||
let _ = self?.chatControllerInteraction.openMessage(message, .default)
|
||||
@ -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,26 +869,27 @@ 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
|
||||
private func updateHistory(list: SparseMessageList.State) {
|
||||
//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))
|
||||
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
|
||||
|
||||
@ -925,7 +904,6 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func scrollToTop() -> Bool {
|
||||
if self.scrollNode.view.contentOffset.y > 0.0 {
|
||||
@ -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,8 +1017,6 @@ 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
|
||||
}
|
||||
@ -1048,6 +1024,9 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
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 {
|
||||
transition.updateBounds(node: self.scrollNode, bounds: CGRect(origin: CGPoint(), size: self.scrollNode.bounds.size))
|
||||
@ -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,16 +1072,22 @@ 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 updateVisibleItems(size: CGSize, sideInset: CGFloat, bottomInset: CGFloat, visibleHeight: CGFloat, theme: PresentationTheme, strings: PresentationStrings, synchronousLoad: Bool) {
|
||||
guard let itemsLayout = self.itemsLayout else {
|
||||
private func updateScrollingArea(transition: ContainedViewLayoutTransition) {
|
||||
guard let currentParams = self.currentParams, let itemsLayout = self.itemsLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
@ -1110,14 +1097,62 @@ final class PeerInfoVisualMediaPaneNode: ASDisplayNode, PeerInfoPaneNode, UIScro
|
||||
|
||||
let (minVisibleIndex, maxVisibleIndex) = itemsLayout.visibleRange(rect: visibleRect)
|
||||
|
||||
var headerItem: Message?
|
||||
var headerItem: Int32?
|
||||
|
||||
var validIds = Set<UInt32>()
|
||||
if minVisibleIndex <= maxVisibleIndex {
|
||||
for i in minVisibleIndex ... maxVisibleIndex {
|
||||
let stableId = self.mediaItems[i].message.stableId
|
||||
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) {
|
||||
guard let itemsLayout = self.itemsLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
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 validIds = Set<VisualMediaItem.StableId>()
|
||||
if minVisibleIndex <= maxVisibleIndex {
|
||||
for i in minVisibleIndex ... maxVisibleIndex {
|
||||
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)
|
||||
|
||||
let itemNode: VisualMediaItemNode
|
||||
@ -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
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user