Merge branch 'master' of gitlab.com:peter-iakovlev/telegram-ios

This commit is contained in:
Ilya Laktyushin 2021-07-14 01:07:17 +03:00
commit 4a89668a10
43 changed files with 467 additions and 152 deletions

View File

@ -650,9 +650,9 @@ class CallListCallItemNode: ItemListRevealOptionsItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.3, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let (item, _, _, _, _) = self.layoutParams {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -467,9 +467,9 @@ class CallListGroupCallItemNode: ItemListRevealOptionsItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.3, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let (item, _, _, _, _) = self.layoutParams {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -151,9 +151,10 @@ private enum ChatListSearchItemHeaderId: Int32 {
}
public final class ChatListSearchItemHeader: ListViewItemHeader {
public let id: Int64
public let id: ListViewItemNode.HeaderId
public let type: ChatListSearchItemHeaderType
public let stickDirection: ListViewItemHeaderStickDirection = .top
public let stickOverInsets: Bool = true
public let theme: PresentationTheme
public let strings: PresentationStrings
public let actionTitle: String?
@ -163,14 +164,14 @@ public final class ChatListSearchItemHeader: ListViewItemHeader {
public init(type: ChatListSearchItemHeaderType, theme: PresentationTheme, strings: PresentationStrings, actionTitle: String? = nil, action: (() -> Void)? = nil) {
self.type = type
self.id = Int64(self.type.id.rawValue)
self.id = ListViewItemNode.HeaderId(space: 0, id: Int64(self.type.id.rawValue))
self.theme = theme
self.strings = strings
self.actionTitle = actionTitle
self.action = action
}
public func node() -> ListViewItemHeaderNode {
public func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ChatListSearchItemHeaderNode(type: self.type, theme: self.theme, strings: self.strings, actionTitle: self.actionTitle, action: self.action)
}

View File

@ -363,9 +363,9 @@ public class ChatListAdditionalCategoryItemNode: ItemListRevealOptionsItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -465,7 +465,7 @@ class ChatListFilterPresetCategoryItemNode: ItemListRevealOptionsItemNode, ItemL
}
}
override func header() -> ListViewItemHeader? {
override func headers() -> [ListViewItemHeader]? {
return nil
}
}

View File

@ -154,9 +154,9 @@ class ChatListRecentPeersListItemNode: ListViewItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -1893,9 +1893,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
}
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.layoutParams?.0 {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -245,9 +245,9 @@ class ContactsAddItemNode: ListViewItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let (item, _, _, _, _) = self.layoutParams {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -325,9 +325,9 @@ class ContactListActionItemNode: ListViewItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -5,20 +5,21 @@ import TelegramPresentationData
import ListSectionHeaderNode
final class ContactListNameIndexHeader: Equatable, ListViewItemHeader {
let id: Int64
let id: ListViewItemNode.HeaderId
let theme: PresentationTheme
let letter: unichar
let stickDirection: ListViewItemHeaderStickDirection = .top
public let stickOverInsets: Bool = true
let height: CGFloat = 29.0
init(theme: PresentationTheme, letter: unichar) {
self.theme = theme
self.letter = letter
self.id = Int64(letter)
self.id = ListViewItemNode.HeaderId(space: 0, id: Int64(letter))
}
func node() -> ListViewItemHeaderNode {
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ContactListNameIndexHeaderNode(theme: self.theme, letter: self.letter)
}

View File

@ -1182,9 +1182,9 @@ public class ContactsPeerItemNode: ItemListRevealOptionsItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let (item, _, _, _, _, _) = self.layoutParams {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -41,6 +41,8 @@ private func cancelOtherGestures(gesture: ContextGesture, view: UIView) {
for recognizer in gestureRecognizers {
if let recognizer = recognizer as? ContextGesture, recognizer !== gesture {
recognizer.cancel()
} else if let recognizer = recognizer as? ListViewTapGestureRecognizer {
recognizer.cancel()
}
}
}

View File

@ -6,6 +6,11 @@ import UIKitRuntimeUtils
private let infiniteScrollSize: CGFloat = 10000.0
private let insertionAnimationDuration: Double = 0.4
private struct VisibleHeaderNodeId: Hashable {
var id: ListViewItemNode.HeaderId
var affinity: Int
}
private final class ListViewBackingLayer: CALayer {
override func setNeedsLayout() {
}
@ -275,7 +280,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
private final var items: [ListViewItem] = []
private final var itemNodes: [ListViewItemNode] = []
private final var itemHeaderNodes: [Int64: ListViewItemHeaderNode] = [:]
private final var itemHeaderNodes: [VisibleHeaderNodeId: ListViewItemHeaderNode] = [:]
public final var itemHeaderNodesAlpha: CGFloat = 1.0
@ -846,7 +851,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
self.updateScroller(transition: .immediate)
self.updateItemHeaders(leftInset: self.insets.left, rightInset: self.insets.right)
self.updateItemHeaders(leftInset: self.insets.left, rightInset: self.insets.right, synchronousLoad: false)
for (_, headerNode) in self.itemHeaderNodes {
if self.dynamicBounceEnabled && headerNode.wantsScrollDynamics {
@ -1366,13 +1371,6 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
} else if let itemHighlightOverlayBackground = self.itemHighlightOverlayBackground {
self.itemHighlightOverlayBackground = nil
for (_, _) in self.itemHeaderNodes {
//self.view.bringSubview(toFront: headerNode.view)
}
//self.view.bringSubview(toFront: itemHighlightOverlayBackground.view)
for _ in self.itemNodes {
//self.view.bringSubview(toFront: itemNode.view)
}
transition.updateAlpha(node: itemHighlightOverlayBackground, alpha: 0.0, completion: { [weak itemHighlightOverlayBackground] _ in
itemHighlightOverlayBackground?.removeFromSupernode()
})
@ -1869,7 +1867,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
let beginReplay = { [weak self] in
if let strongSelf = self {
strongSelf.replayOperations(animated: animated, animateAlpha: options.contains(.AnimateAlpha), animateCrossfade: options.contains(.AnimateCrossfade), synchronous: options.contains(.Synchronous), animateTopItemVerticalOrigin: options.contains(.AnimateTopItemPosition), operations: updatedOperations, requestItemInsertionAnimationsIndices: options.contains(.RequestItemInsertionAnimations) ? insertedIndexSet : Set(), scrollToItem: scrollToItem, additionalScrollDistance: additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemIndex: stationaryItemIndex, updateOpaqueState: updateOpaqueState, completion: {
strongSelf.replayOperations(animated: animated, animateAlpha: options.contains(.AnimateAlpha), animateCrossfade: options.contains(.AnimateCrossfade), synchronous: options.contains(.Synchronous), synchronousLoads: options.contains(.PreferSynchronousResourceLoading), animateTopItemVerticalOrigin: options.contains(.AnimateTopItemPosition), operations: updatedOperations, requestItemInsertionAnimationsIndices: options.contains(.RequestItemInsertionAnimations) ? insertedIndexSet : Set(), scrollToItem: scrollToItem, additionalScrollDistance: additionalScrollDistance, updateSizeAndInsets: updateSizeAndInsets, stationaryItemIndex: stationaryItemIndex, updateOpaqueState: updateOpaqueState, completion: {
if options.contains(.PreferSynchronousDrawing) {
self?.recursivelyEnsureDisplaySynchronously(true)
}
@ -2318,7 +2316,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
}
}
private func replayOperations(animated: Bool, animateAlpha: Bool, animateCrossfade: Bool, synchronous: Bool, animateTopItemVerticalOrigin: Bool, operations: [ListViewStateOperation], requestItemInsertionAnimationsIndices: Set<Int>, scrollToItem originalScrollToItem: ListViewScrollToItem?, additionalScrollDistance: CGFloat, updateSizeAndInsets: ListViewUpdateSizeAndInsets?, stationaryItemIndex: Int?, updateOpaqueState: Any?, completion: () -> Void) {
private func replayOperations(animated: Bool, animateAlpha: Bool, animateCrossfade: Bool, synchronous: Bool, synchronousLoads: Bool, animateTopItemVerticalOrigin: Bool, operations: [ListViewStateOperation], requestItemInsertionAnimationsIndices: Set<Int>, scrollToItem originalScrollToItem: ListViewScrollToItem?, additionalScrollDistance: CGFloat, updateSizeAndInsets: ListViewUpdateSizeAndInsets?, stationaryItemIndex: Int?, updateOpaqueState: Any?, completion: () -> Void) {
var scrollToItem: ListViewScrollToItem?
var isExperimentalSnapToScrollToItem = false
if let originalScrollToItem = originalScrollToItem {
@ -3039,7 +3037,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
previousItemHeaderNodes.append(headerNode)
}
self.updateItemHeaders(leftInset: listInsets.left, rightInset: listInsets.right, transition: headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
self.updateItemHeaders(leftInset: listInsets.left, rightInset: listInsets.right, synchronousLoad: synchronousLoads, transition: headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
if let offset = offset, !offset.isZero {
//self.didScrollWithOffset?(-offset, headerNodesTransition.0, nil)
@ -3215,7 +3213,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
completion()
} else {
self.updateItemHeaders(leftInset: listInsets.left, rightInset: listInsets.right, transition: headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
self.updateItemHeaders(leftInset: listInsets.left, rightInset: listInsets.right, synchronousLoad: synchronousLoads, transition: headerNodesTransition, animateInsertion: animated || !requestItemInsertionAnimationsIndices.isEmpty)
self.updateItemNodesVisibilities(onlyPositive: deferredUpdateVisible)
if animated {
@ -3262,15 +3260,88 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
node.headerAccessoryItemNode?.removeFromSupernode()
node.headerAccessoryItemNode = nil
}
private var nextHeaderSpaceAffinity: Int = 0
private func assignHeaderSpaceAffinities() {
var nextTempAffinity = 0
var existingAffinityIdByAffinity: [Int: Int] = [:]
for i in 0 ..< self.itemNodes.count {
let currentItemNode = self.itemNodes[i]
if let currentItemHeaders = currentItemNode.headers() {
currentHeadersLoop: for currentHeader in currentItemHeaders {
let currentId = currentHeader.id
if let currentAffinity = currentItemNode.tempHeaderSpaceAffinities[currentId] {
if let existingAffinity = currentItemNode.headerSpaceAffinities[currentId] {
existingAffinityIdByAffinity[currentAffinity] = existingAffinity
}
continue currentHeadersLoop
}
let currentAffinity = nextTempAffinity
nextTempAffinity += 1
currentItemNode.tempHeaderSpaceAffinities[currentId] = currentAffinity
if let existingAffinity = currentItemNode.headerSpaceAffinities[currentId] {
existingAffinityIdByAffinity[currentAffinity] = existingAffinity
}
groupSearch: for nextIndex in (i + 1) ..< self.itemNodes.count {
let nextItemNode = self.itemNodes[nextIndex]
var containsSameHeader = false
if let nextHeaders = nextItemNode.headers() {
nextHeaderSearch: for nextHeader in nextHeaders {
if nextHeader.id == currentId {
containsSameHeader = true
break nextHeaderSearch
}
}
}
if containsSameHeader {
nextItemNode.tempHeaderSpaceAffinities[currentId] = currentAffinity
} else {
break groupSearch
}
}
}
}
}
for i in 0 ..< self.itemNodes.count {
let itemNode = self.itemNodes[i]
for (headerId, tempAffinity) in itemNode.tempHeaderSpaceAffinities {
let affinity: Int
if let existing = existingAffinityIdByAffinity[tempAffinity] {
affinity = existing
} else {
affinity = self.nextHeaderSpaceAffinity
existingAffinityIdByAffinity[tempAffinity] = affinity
self.nextHeaderSpaceAffinity += 1
}
itemNode.headerSpaceAffinities[headerId] = affinity
itemNode.tempHeaderSpaceAffinities = [:]
}
}
}
private func updateItemHeaders(leftInset: CGFloat, rightInset: CGFloat, transition: (ContainedViewLayoutTransition, Bool, CGFloat) = (.immediate, false, 0.0), animateInsertion: Bool = false) {
private func updateItemHeaders(leftInset: CGFloat, rightInset: CGFloat, synchronousLoad: Bool, transition: (ContainedViewLayoutTransition, Bool, CGFloat) = (.immediate, false, 0.0), animateInsertion: Bool = false) {
self.assignHeaderSpaceAffinities()
let upperDisplayBound = self.headerInsets.top
let lowerDisplayBound = self.visibleSize.height - self.insets.bottom
var visibleHeaderNodes = Set<Int64>()
var visibleHeaderNodes = Set<VisibleHeaderNodeId>()
let flashing = self.headerItemsAreFlashing()
func addHeader(id: Int64, upperBound: CGFloat, upperBoundEdge: CGFloat, lowerBound: CGFloat, item: ListViewItemHeader, hasValidNodes: Bool) {
func addHeader(id: VisibleHeaderNodeId, upperBound: CGFloat, upperBoundEdge: CGFloat, lowerBound: CGFloat, item: ListViewItemHeader, hasValidNodes: Bool) {
let itemHeaderHeight: CGFloat = item.height
let headerFrame: CGRect
@ -3334,13 +3405,12 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
} else if hasValidNodes && headerNode.alpha.isZero {
headerNode.alpha = initialHeaderNodeAlpha
if animateInsertion {
headerNode.layer.animateAlpha(from: 0.0, to: initialHeaderNodeAlpha, duration: 0.2)
headerNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.2)
headerNode.animateAdded(duration: 0.2)
}
}
headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, transition: transition.0)
} else {
let headerNode = item.node()
let headerNode = item.node(synchronousLoad: synchronousLoad)
headerNode.alpha = initialHeaderNodeAlpha
if headerNode.item !== item {
item.updateNode(headerNode, previous: nil, next: nil)
@ -3357,39 +3427,70 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
self.addSubnode(headerNode)
}
if animateInsertion {
headerNode.layer.animateAlpha(from: 0.0, to: initialHeaderNodeAlpha, duration: 0.3)
headerNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.3)
headerNode.alpha = initialHeaderNodeAlpha
headerNode.animateAdded(duration: 0.2)
}
headerNode.updateStickDistanceFactor(stickLocationDistanceFactor, transition: .immediate)
}
}
var previousHeaderBySpace: [AnyHashable: (id: VisibleHeaderNodeId, upperBound: CGFloat, upperBoundEdge: CGFloat, lowerBound: CGFloat, item: ListViewItemHeader, hasValidNodes: Bool)] = [:]
var previousHeader: (id: Int64, upperBound: CGFloat, upperBoundEdge: CGFloat, lowerBound: CGFloat, item: ListViewItemHeader, hasValidNodes: Bool)?
for itemNode in self.itemNodes {
let itemFrame = itemNode.apparentFrame
let itemTopInset = itemNode.insets.top
if let itemHeader = itemNode.header() {
if let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader {
if previousHeaderId == itemHeader.id {
previousHeader = (previousHeaderId, previousUpperBound, previousUpperBoundEdge, itemFrame.maxY, previousHeaderItem, hasValidNodes || itemNode.index != nil)
} else {
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes)
previousHeader = (itemHeader.id, itemFrame.minY, itemFrame.minY + itemTopInset, itemFrame.maxY, itemHeader, itemNode.index != nil)
var validItemHeaderSpaces: [AnyHashable] = []
if let itemHeaders = itemNode.headers() {
for itemHeader in itemHeaders {
guard let affinity = itemNode.headerSpaceAffinities[itemHeader.id] else {
assertionFailure()
continue
}
let headerId = VisibleHeaderNodeId(id: itemHeader.id, affinity: affinity)
validItemHeaderSpaces.append(itemHeader.id.space)
let itemMaxY: CGFloat
if itemHeader.stickOverInsets {
itemMaxY = itemFrame.maxY
} else {
itemMaxY = itemFrame.maxY - (self.rotated ? itemNode.insets.top : itemNode.insets.bottom)
}
if let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeaderBySpace[itemHeader.id.space] {
if previousHeaderId == headerId {
previousHeaderBySpace[itemHeader.id.space] = (previousHeaderId, previousUpperBound, previousUpperBoundEdge, itemMaxY, previousHeaderItem, hasValidNodes || itemNode.index != nil)
} else {
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes)
previousHeaderBySpace[itemHeader.id.space] = (headerId, itemFrame.minY, itemFrame.minY + itemTopInset, itemMaxY, itemHeader, itemNode.index != nil)
}
} else {
previousHeaderBySpace[itemHeader.id.space] = (headerId, itemFrame.minY, itemFrame.minY + itemTopInset, itemMaxY, itemHeader, itemNode.index != nil)
}
} else {
previousHeader = (itemHeader.id, itemFrame.minY, itemFrame.minY + itemTopInset, itemFrame.maxY, itemHeader, itemNode.index != nil)
}
} else {
if let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader {
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes)
}
for (space, previousHeader) in previousHeaderBySpace {
if validItemHeaderSpaces.contains(space) {
continue
}
previousHeader = nil
let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes)
previousHeaderBySpace.removeValue(forKey: space)
}
}
if let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader {
for (space, previousHeader) in previousHeaderBySpace {
let (previousHeaderId, previousUpperBound, previousUpperBoundEdge, previousLowerBound, previousHeaderItem, hasValidNodes) = previousHeader
addHeader(id: previousHeaderId, upperBound: previousUpperBound, upperBoundEdge: previousUpperBoundEdge, lowerBound: previousLowerBound, item: previousHeaderItem, hasValidNodes: hasValidNodes)
previousHeaderBySpace.removeValue(forKey: space)
}
let currentIds = Set(self.itemHeaderNodes.keys)
@ -3709,10 +3810,10 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
var updatedOperations = operations
updatedState.removeInvisibleNodes(&updatedOperations)
if synchronous {
self.replayOperations(animated: false, animateAlpha: false, animateCrossfade: false, synchronous: false, animateTopItemVerticalOrigin: false, operations: updatedOperations, requestItemInsertionAnimationsIndices: Set(), scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemIndex: nil, updateOpaqueState: nil, completion: completion)
self.replayOperations(animated: false, animateAlpha: false, animateCrossfade: false, synchronous: false, synchronousLoads: false, animateTopItemVerticalOrigin: false, operations: updatedOperations, requestItemInsertionAnimationsIndices: Set(), scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemIndex: nil, updateOpaqueState: nil, completion: completion)
} else {
self.dispatchOnVSync {
self.replayOperations(animated: false, animateAlpha: false, animateCrossfade: false, synchronous: false, animateTopItemVerticalOrigin: false, operations: updatedOperations, requestItemInsertionAnimationsIndices: Set(), scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemIndex: nil, updateOpaqueState: nil, completion: completion)
self.replayOperations(animated: false, animateAlpha: false, animateCrossfade: false, synchronous: false, synchronousLoads: false, animateTopItemVerticalOrigin: false, operations: updatedOperations, requestItemInsertionAnimationsIndices: Set(), scrollToItem: nil, additionalScrollDistance: 0.0, updateSizeAndInsets: nil, stationaryItemIndex: nil, updateOpaqueState: nil, completion: completion)
}
}
}

View File

@ -8,14 +8,13 @@ public enum ListViewItemHeaderStickDirection {
case bottom
}
public typealias ListViewItemHeaderId = Int64
public protocol ListViewItemHeader: class {
var id: ListViewItemHeaderId { get }
public protocol ListViewItemHeader: AnyObject {
var id: ListViewItemNode.HeaderId { get }
var stickDirection: ListViewItemHeaderStickDirection { get }
var height: CGFloat { get }
var stickOverInsets: Bool { get }
func node() -> ListViewItemHeaderNode
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?)
}
@ -114,6 +113,11 @@ open class ListViewItemHeaderNode: ASDisplayNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
self.layer.animateScale(from: 1.0, to: 0.2, duration: duration, removeOnCompletion: false)
}
open func animateAdded(duration: Double) {
self.layer.animateAlpha(from: 0.0, to: self.alpha, duration: 0.2)
self.layer.animateScale(from: 0.2, to: 1.0, duration: 0.2)
}
private var cachedLayout: (CGSize, CGFloat, CGFloat)?

View File

@ -84,6 +84,16 @@ public struct ListViewItemLayoutParams {
}
open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
public struct HeaderId: Hashable {
public var space: AnyHashable
public var id: AnyHashable
public init(space: AnyHashable, id: AnyHashable) {
self.space = space
self.id = id
}
}
let rotated: Bool
final var index: Int?
@ -116,6 +126,9 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
private final var spring: ListViewItemSpring?
private final var animations: [(String, ListViewAnimation)] = []
final var tempHeaderSpaceAffinities: [ListViewItemNode.HeaderId: Int] = [:]
final var headerSpaceAffinities: [ListViewItemNode.HeaderId: Int] = [:]
final let wantsScrollDynamics: Bool
@ -533,7 +546,7 @@ open class ListViewItemNode: ASDisplayNode, AccessibilityFocusableNode {
return false
}
open func header() -> ListViewItemHeader? {
open func headers() -> [ListViewItemHeader]? {
return nil
}

View File

@ -2,5 +2,7 @@ import Foundation
import UIKit
public final class ListViewTapGestureRecognizer: UITapGestureRecognizer {
public func cancel() {
self.state = .failed
}
}

View File

@ -1328,8 +1328,12 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
}
}
override public func header() -> ListViewItemHeader? {
return self.layoutParams?.0.header
override public func headers() -> [ListViewItemHeader]? {
if let item = self.layoutParams?.0 {
return item.header.flatMap { [$0] }
} else {
return nil
}
}
override public func updateAbsoluteRect(_ rect: CGRect, within containerSize: CGSize) {
@ -1350,10 +1354,11 @@ public class ItemListPeerItemNode: ItemListRevealOptionsItemNode, ItemListItemNo
}
public final class ItemListPeerItemHeader: ListViewItemHeader {
public let id: Int64
public let id: ListViewItemNode.HeaderId
public let text: String
public let additionalText: String
public let stickDirection: ListViewItemHeaderStickDirection = .topEdge
public let stickOverInsets: Bool = true
public let theme: PresentationTheme
public let strings: PresentationStrings
public let actionTitle: String?
@ -1364,14 +1369,14 @@ public final class ItemListPeerItemHeader: ListViewItemHeader {
public init(theme: PresentationTheme, strings: PresentationStrings, text: String, additionalText: String, actionTitle: String? = nil, id: Int64, action: (() -> Void)? = nil) {
self.text = text
self.additionalText = additionalText
self.id = id
self.id = ListViewItemNode.HeaderId(space: 0, id: id)
self.theme = theme
self.strings = strings
self.actionTitle = actionTitle
self.action = action
}
public func node() -> ListViewItemHeaderNode {
public func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ItemListPeerItemHeaderNode(theme: self.theme, strings: self.strings, text: self.text, additionalText: self.additionalText, actionTitle: self.actionTitle, action: self.action)
}

View File

@ -457,7 +457,11 @@ public class ItemListVenueItemNode: ListViewItemNode, ItemListItemNode {
self.item?.infoAction?()
}
override public func header() -> ListViewItemHeader? {
return self.item?.header
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header.flatMap { [$0] }
} else {
return nil
}
}
}

View File

@ -42,7 +42,7 @@ final class ListMessageDateHeader: ListViewItemHeader {
private let month: Int32
private let year: Int32
let id: Int64
let id: ListViewItemNode.HeaderId
let theme: PresentationTheme
let strings: PresentationStrings
let fontSize: PresentationFontSize
@ -61,14 +61,15 @@ final class ListMessageDateHeader: ListViewItemHeader {
self.month = timeinfo.tm_mon
self.year = timeinfo.tm_year
self.id = Int64(self.roundedTimestamp)
self.id = ListViewItemNode.HeaderId(space: 0, id: Int64(self.roundedTimestamp))
}
let stickDirection: ListViewItemHeaderStickDirection = .top
let stickOverInsets: Bool = true
let height: CGFloat = 28.0
func node() -> ListViewItemHeaderNode {
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ListMessageDateHeaderNode(theme: self.theme, strings: self.strings, fontSize: self.fontSize, roundedTimestamp: self.roundedTimestamp, month: self.month, year: self.year)
}

View File

@ -1118,8 +1118,8 @@ public final class ListMessageFileItemNode: ListMessageNode {
}
}
override public func header() -> ListViewItemHeader? {
return self.item?.header
override public func headers() -> [ListViewItemHeader]? {
return self.item?.header.flatMap { [$0] }
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

View File

@ -759,8 +759,12 @@ public final class ListMessageSnippetItemNode: ListMessageNode {
}
}
override public func header() -> ListViewItemHeader? {
return self.item?.header
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header.flatMap { [$0] }
} else {
return nil
}
}
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {

View File

@ -2055,7 +2055,7 @@ static NSString *dumpHexString(NSData *data, int maxLength) {
[self getAuthKeyForCurrentScheme:scheme createIfNeeded:false authInfoSelector:&authInfoSelector];
if (MTLogEnabled()) {
MTLog(@"[MTProto#%p@%p missing key %lld selector %d]", self, _context, _validAuthInfo.authInfo.authKeyId, authInfoSelector);
MTLog(@"[MTProto#%p@%p missing key %lld selector %d useExplicitAuthKey: %lld, canResetAuthData: %s]", self, _context, _validAuthInfo.authInfo.authKeyId, authInfoSelector, _useExplicitAuthKey.authKeyId, _canResetAuthData ? "true" : "false");
}
if (_useExplicitAuthKey != nil) {

View File

@ -247,7 +247,7 @@ private final class BubbleSettingsControllerNode: ASDisplayNode, UIScrollViewDel
dateHeaderNode = currentDateHeaderNode
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
} else {
dateHeaderNode = headerItem.node()
dateHeaderNode = headerItem.node(synchronousLoad: true)
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
self.messagesContainerNode.addSubnode(dateHeaderNode)
self.dateHeaderNode = dateHeaderNode

View File

@ -243,9 +243,9 @@ class SettingsSearchRecentItemNode: ItemListRevealOptionsItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -386,7 +386,7 @@ private final class TextSizeSelectionControllerNode: ASDisplayNode, UIScrollView
dateHeaderNode = currentDateHeaderNode
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
} else {
dateHeaderNode = headerItem.node()
dateHeaderNode = headerItem.node(synchronousLoad: true)
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
self.messagesContainerNode.addSubnode(dateHeaderNode)
self.dateHeaderNode = dateHeaderNode

View File

@ -970,7 +970,7 @@ final class ThemeAccentColorControllerNode: ASDisplayNode, UIScrollViewDelegate
dateHeaderNode = currentDateHeaderNode
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
} else {
dateHeaderNode = headerItem.node()
dateHeaderNode = headerItem.node(synchronousLoad: true)
//dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
self.messagesContainerNode.addSubnode(dateHeaderNode)
self.dateHeaderNode = dateHeaderNode

View File

@ -249,9 +249,9 @@ class ThemeGridSearchColorsItemNode: ListViewItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}

View File

@ -550,7 +550,7 @@ final class ThemePreviewControllerNode: ASDisplayNode, UIScrollViewDelegate {
dateHeaderNode = currentDateHeaderNode
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
} else {
dateHeaderNode = headerItem.node()
dateHeaderNode = headerItem.node(synchronousLoad: true)
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
self.messagesContainerNode.addSubnode(dateHeaderNode)
self.dateHeaderNode = dateHeaderNode

View File

@ -1082,7 +1082,7 @@ final class MessageStoryRenderer {
dateHeaderNode = currentDateHeaderNode
headerItem.updateNode(dateHeaderNode, previous: nil, next: headerItem)
} else {
dateHeaderNode = headerItem.node()
dateHeaderNode = headerItem.node(synchronousLoad: true)
dateHeaderNode.subnodeTransform = CATransform3DMakeScale(-1.0, 1.0, 1.0)
self.messagesContainerNode.addSubnode(dateHeaderNode)
self.dateHeaderNode = dateHeaderNode

View File

@ -281,7 +281,7 @@ class VoiceChatActionItemNode: ListViewItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
return nil
}
}

View File

@ -990,7 +990,7 @@ class VoiceChatFullscreenParticipantItemNode: ItemListRevealOptionsItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
}
override func header() -> ListViewItemHeader? {
override func headers() -> [ListViewItemHeader]? {
return nil
}

View File

@ -1273,7 +1273,7 @@ class VoiceChatParticipantItemNode: ItemListRevealOptionsItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
}
override func header() -> ListViewItemHeader? {
override func headers() -> [ListViewItemHeader]? {
return nil
}

View File

@ -7927,6 +7927,12 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
itemNode.updateSelectionState(animated: animated)
}
}
self.chatDisplayNode.historyNode.forEachItemHeaderNode{ itemHeaderNode in
if let avatarNode = itemHeaderNode as? ChatMessageAvatarHeaderNode {
avatarNode.updateSelectionState(animated: animated)
}
}
}
private func updatePollTooltipMessageState(animated: Bool) {

View File

@ -1086,6 +1086,8 @@ public final class ChatHistoryListNode: ListView, ChatHistoryNode {
strongSelf.forEachItemHeaderNode { itemHeaderNode in
if let dateNode = itemHeaderNode as? ChatMessageDateHeaderNode {
dateNode.updatePresentationData(chatPresentationData, context: context)
} else if let avatarNode = itemHeaderNode as? ChatMessageAvatarHeaderNode {
avatarNode.updatePresentationData(chatPresentationData, context: context)
} else if let dateNode = itemHeaderNode as? ListMessageDateHeaderNode {
dateNode.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings)
}

View File

@ -108,14 +108,6 @@ class ChatHoleItemNode: ListViewItemNode {
}
}
/*override public func header() -> ListViewItemHeader? {
if let item = self.item {
return item.header
} else {
return nil
}
}*/
override public func animateAdded(_ currentTimestamp: Double, duration: Double) {
self.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}

View File

@ -150,7 +150,7 @@ class ChatMessageShareButton: HighlightableButtonNode {
textNode.removeFromSupernode()
}
self.backgroundNode.frame = CGRect(origin: CGPoint(), size: size)
self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: self.backgroundNode.bounds.height / 2.0, transition: .immediate)
self.backgroundNode.update(size: self.backgroundNode.bounds.size, cornerRadius: min(self.backgroundNode.bounds.width, self.backgroundNode.bounds.height) / 2.0, transition: .immediate)
if let image = self.iconNode.image {
self.iconNode.frame = CGRect(origin: CGPoint(x: floor((size.width - image.size.width) / 2.0) + self.iconOffset.x, y: floor((size.width - image.size.width) / 2.0) - (offsetIcon ? 1.0 : 0.0) + self.iconOffset.y), size: image.size)
}

View File

@ -6,6 +6,7 @@ import TelegramPresentationData
import Postbox
import SyncCore
import AccountContext
import AvatarNode
private let timezoneOffset: Int32 = {
let nowTimestamp = Int32(CFAbsoluteTimeGetCurrent() + NSTimeIntervalSince1970)
@ -22,7 +23,7 @@ final class ChatMessageDateHeader: ListViewItemHeader {
private let roundedTimestamp: Int32
private let scheduled: Bool
let id: Int64
let id: ListViewItemNode.HeaderId
let presentationData: ChatPresentationData
let context: AccountContext
let action: ((Int32) -> Void)?
@ -33,21 +34,16 @@ final class ChatMessageDateHeader: ListViewItemHeader {
self.presentationData = presentationData
self.context = context
self.action = action
if timestamp == scheduleWhenOnlineTimestamp {
self.roundedTimestamp = scheduleWhenOnlineTimestamp
} else if timestamp == Int32.max {
self.roundedTimestamp = timestamp / (granularity) * (granularity)
} else {
self.roundedTimestamp = ((timestamp + timezoneOffset) / (granularity)) * (granularity)
}
self.id = Int64(self.roundedTimestamp)
self.roundedTimestamp = dateHeaderTimestampId(timestamp: timestamp)
self.id = ListViewItemNode.HeaderId(space: 0, id: Int64(self.roundedTimestamp))
}
let stickDirection: ListViewItemHeaderStickDirection = .bottom
let stickOverInsets: Bool = true
let height: CGFloat = 34.0
func node() -> ListViewItemHeaderNode {
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ChatMessageDateHeaderNode(localTimestamp: self.roundedTimestamp, scheduled: self.scheduled, presentationData: self.presentationData, context: self.context, action: self.action)
}
@ -90,6 +86,16 @@ private func monthAtIndex(_ index: Int, strings: PresentationStrings) -> String
}
}
private func dateHeaderTimestampId(timestamp: Int32) -> Int32 {
if timestamp == scheduleWhenOnlineTimestamp {
return timestamp
} else if timestamp == Int32.max {
return timestamp / (granularity) * (granularity)
} else {
return ((timestamp + timezoneOffset) / (granularity)) * (granularity)
}
}
final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
let labelNode: TextNode
let backgroundNode: NavigationBackgroundNode
@ -164,12 +170,11 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
let graphics = PresentationResourcesChat.principalGraphics(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
//self.backgroundNode.image = graphics.dateStaticBackground
self.backgroundNode.updateColor(color: selectDateFillStaticColor(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper), transition: .immediate)
self.stickBackgroundNode.image = graphics.dateFloatingBackground
self.stickBackgroundNode.alpha = 0.0
//self.backgroundNode.addSubnode(self.stickBackgroundNode)
self.addSubnode(self.backgroundNode)
self.addSubnode(self.labelNode)
@ -198,8 +203,7 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
self.presentationData = presentationData
let graphics = PresentationResourcesChat.principalGraphics(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper, bubbleCorners: presentationData.chatBubbleCorners)
//self.backgroundNode.image = graphics.dateStaticBackground
self.backgroundNode.updateColor(color: selectDateFillStaticColor(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper), enableBlur: dateFillNeedsBlur(theme: presentationData.theme.theme, wallpaper: presentationData.theme.wallpaper), transition: .immediate)
self.stickBackgroundNode.image = graphics.dateFloatingBackground
@ -220,12 +224,6 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
func updateBackgroundColor(color: UIColor, enableBlur: Bool) {
self.backgroundNode.updateColor(color: color, enableBlur: enableBlur, transition: .immediate)
/*let chatDateSize: CGFloat = 20.0
self.backgroundNode.image = generateImage(CGSize(width: chatDateSize, height: chatDateSize), contextGenerator: { size, context -> Void in
context.clear(CGRect(origin: CGPoint(), size: size))
context.setFillColor(color.cgColor)
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
})!.stretchableImage(withLeftCapWidth: Int(chatDateSize) / 2, topCapHeight: Int(chatDateSize) / 2)*/
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) {
@ -307,3 +305,173 @@ final class ChatMessageDateHeaderNode: ListViewItemHeaderNode {
}
}
}
final class ChatMessageAvatarHeader: ListViewItemHeader {
struct Id: Hashable {
var peerId: PeerId
var timestampId: Int32
}
let id: ListViewItemNode.HeaderId
let peerId: PeerId
let peer: Peer?
let messageReference: MessageReference?
let presentationData: ChatPresentationData
let context: AccountContext
let controllerInteraction: ChatControllerInteraction
init(timestamp: Int32, peerId: PeerId, peer: Peer?, messageReference: MessageReference?, presentationData: ChatPresentationData, context: AccountContext, controllerInteraction: ChatControllerInteraction) {
self.peerId = peerId
self.peer = peer
self.messageReference = messageReference
self.presentationData = presentationData
self.context = context
self.controllerInteraction = controllerInteraction
self.id = ListViewItemNode.HeaderId(space: 1, id: Id(peerId: peerId, timestampId: dateHeaderTimestampId(timestamp: timestamp)))
}
let stickDirection: ListViewItemHeaderStickDirection = .top
let stickOverInsets: Bool = false
let height: CGFloat = 38.0
func node(synchronousLoad: Bool) -> ListViewItemHeaderNode {
return ChatMessageAvatarHeaderNode(peerId: self.peerId, peer: self.peer, messageReference: self.messageReference, presentationData: self.presentationData, context: self.context, controllerInteraction: self.controllerInteraction, synchronousLoad: synchronousLoad)
}
func updateNode(_ node: ListViewItemHeaderNode, previous: ListViewItemHeader?, next: ListViewItemHeader?) {
guard let node = node as? ChatMessageAvatarHeaderNode, let next = next as? ChatMessageAvatarHeader else {
return
}
node.updatePresentationData(next.presentationData, context: next.context)
}
}
private let avatarFont = avatarPlaceholderFont(size: 16.0)
final class ChatMessageAvatarHeaderNode: ListViewItemHeaderNode {
private let peerId: PeerId
private let messageReference: MessageReference?
private let peer: Peer?
private let containerNode: ContextControllerSourceNode
private let avatarNode: AvatarNode
private var presentationData: ChatPresentationData
private let context: AccountContext
private let controllerInteraction: ChatControllerInteraction
init(peerId: PeerId, peer: Peer?, messageReference: MessageReference?, presentationData: ChatPresentationData, context: AccountContext, controllerInteraction: ChatControllerInteraction, synchronousLoad: Bool) {
self.peerId = peerId
self.peer = peer
self.messageReference = messageReference
self.presentationData = presentationData
self.context = context
self.controllerInteraction = controllerInteraction
self.containerNode = ContextControllerSourceNode()
self.avatarNode = AvatarNode(font: avatarFont)
super.init(layerBacked: false, dynamicBounce: true, isRotated: true, seeThrough: false)
self.transform = CATransform3DMakeRotation(CGFloat.pi, 0.0, 0.0, 1.0)
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.avatarNode)
if let peer = peer {
self.setPeer(context: context, theme: presentationData.theme.theme, synchronousLoad: synchronousLoad, peer: peer, authorOfMessage: messageReference, emptyColor: .black)
}
self.containerNode.activated = { [weak self] gesture, _ in
guard let strongSelf = self, let peer = strongSelf.peer else {
return
}
var messageId: MessageId?
if let messageReference = messageReference, case let .message(m) = messageReference.content {
messageId = m.id
}
strongSelf.controllerInteraction.openPeerContextMenu(peer, messageId, strongSelf.containerNode, strongSelf.containerNode.bounds, gesture)
}
self.updateSelectionState(animated: false)
}
func setCustomLetters(context: AccountContext, theme: PresentationTheme, synchronousLoad: Bool, letters: [String], emptyColor: UIColor) {
self.containerNode.isGestureEnabled = false
self.avatarNode.setCustomLetters(letters, icon: !letters.isEmpty ? nil : .phone)
}
func setPeer(context: AccountContext, theme: PresentationTheme, synchronousLoad: Bool, peer: Peer, authorOfMessage: MessageReference?, emptyColor: UIColor) {
self.containerNode.isGestureEnabled = peer.smallProfileImage != nil
var overrideImage: AvatarNodeImageOverride?
if peer.isDeleted {
overrideImage = .deletedIcon
}
self.avatarNode.setPeer(context: context, theme: theme, peer: peer, authorOfMessage: authorOfMessage, overrideImage: overrideImage, emptyColor: emptyColor, synchronousLoad: synchronousLoad, displayDimensions: CGSize(width: 38.0, height: 38.0))
}
override func didLoad() {
super.didLoad()
self.avatarNode.view.addGestureRecognizer(ListViewTapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:))))
}
func updatePresentationData(_ presentationData: ChatPresentationData, context: AccountContext) {
self.presentationData = presentationData
self.setNeedsLayout()
}
override func updateLayout(size: CGSize, leftInset: CGFloat, rightInset: CGFloat) {
self.containerNode.frame = CGRect(origin: CGPoint(x: leftInset + 3.0, y: 0.0), size: CGSize(width: 38.0, height: 38.0))
self.avatarNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 38.0, height: 38.0))
}
override func animateRemoved(duration: Double) {
self.alpha = 0.0
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false)
self.avatarNode.layer.animateScale(from: 1.0, to: 0.2, duration: duration, removeOnCompletion: false)
}
override func animateAdded(duration: Double) {
self.layer.animateAlpha(from: 0.0, to: self.alpha, duration: 0.2)
self.avatarNode.layer.animateScale(from: 0.2, to: 1.0, duration: 0.2)
}
override func updateStickDistanceFactor(_ factor: CGFloat, transition: ContainedViewLayoutTransition) {
}
override func updateFlashingOnScrolling(_ isFlashingOnScrolling: Bool, animated: Bool) {
}
func updateSelectionState(animated: Bool) {
let offset: CGFloat = self.controllerInteraction.selectionState != nil ? 42.0 : 0.0
let previousSubnodeTransform = self.subnodeTransform
self.subnodeTransform = CATransform3DMakeTranslation(offset, 0.0, 0.0);
if animated {
self.layer.animate(from: NSValue(caTransform3D: previousSubnodeTransform), to: NSValue(caTransform3D: self.subnodeTransform), keyPath: "sublayerTransform", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2)
}
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if !self.bounds.contains(point) {
return nil
}
let result = self.containerNode.view.hitTest(self.view.convert(point, to: self.containerNode.view), with: event)
return result
}
override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
super.touchesCancelled(touches, with: event)
}
@objc func tapGesture(_ recognizer: ListViewTapGestureRecognizer) {
if case .ended = recognizer.state {
self.controllerInteraction.openPeer(self.peerId, .info, nil)
}
}
}

View File

@ -192,9 +192,8 @@ func chatItemsHaveCommonDateHeader(_ lhs: ListViewItem, _ rhs: ListViewItem?) -
let lhsHeader: ChatMessageDateHeader?
let rhsHeader: ChatMessageDateHeader?
if let lhs = lhs as? ChatMessageItem {
lhsHeader = lhs.header
lhsHeader = lhs.dateHeader
} else if let _ = lhs as? ChatHoleItem {
//lhsHeader = lhs.header
lhsHeader = nil
} else if let lhs = lhs as? ChatUnreadItem {
lhsHeader = lhs.header
@ -205,7 +204,7 @@ func chatItemsHaveCommonDateHeader(_ lhs: ListViewItem, _ rhs: ListViewItem?) -
}
if let rhs = rhs {
if let rhs = rhs as? ChatMessageItem {
rhsHeader = rhs.header
rhsHeader = rhs.dateHeader
} else if let _ = rhs as? ChatHoleItem {
//rhsHeader = rhs.header
rhsHeader = nil
@ -257,8 +256,11 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
let effectiveAuthorId: PeerId?
let additionalContent: ChatMessageItemAdditionalContent?
public let accessoryItem: ListViewAccessoryItem?
let header: ChatMessageDateHeader
//public let accessoryItem: ListViewAccessoryItem?
let dateHeader: ChatMessageDateHeader
let avatarHeader: ChatMessageAvatarHeader?
let headers: [ListViewItemHeader]
var message: Message {
switch self.content {
@ -288,7 +290,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
self.disableDate = disableDate
self.additionalContent = additionalContent
var accessoryItem: ListViewAccessoryItem?
var avatarHeader: ChatMessageAvatarHeader?
let incoming = content.effectivelyIncoming(self.context.account.peerId)
var effectiveAuthor: Peer?
@ -325,7 +327,7 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
isScheduledMessages = true
}
self.header = ChatMessageDateHeader(timestamp: content.index.timestamp, scheduled: isScheduledMessages, presentationData: presentationData, context: context, action: { timestamp in
self.dateHeader = ChatMessageDateHeader(timestamp: content.index.timestamp, scheduled: isScheduledMessages, presentationData: presentationData, context: context, action: { timestamp in
var calendar = NSCalendar.current
calendar.timeZone = TimeZone(abbreviation: "UTC")!
let date = Date(timeIntervalSince1970: TimeInterval(timestamp))
@ -355,11 +357,18 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
}
if !hasActionMedia && !isBroadcastChannel {
if let effectiveAuthor = effectiveAuthor {
accessoryItem = ChatMessageAvatarAccessoryItem(context: context, peerId: effectiveAuthor.id, peer: effectiveAuthor, messageReference: MessageReference(message), messageTimestamp: content.index.timestamp, forwardInfo: message.forwardInfo, emptyColor: presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper.fill, controllerInteraction: controllerInteraction)
//accessoryItem = ChatMessageAvatarAccessoryItem(context: context, peerId: effectiveAuthor.id, peer: effectiveAuthor, messageReference: MessageReference(message), messageTimestamp: content.index.timestamp, forwardInfo: message.forwardInfo, emptyColor: presentationData.theme.theme.chat.message.incoming.bubble.withoutWallpaper.fill, controllerInteraction: controllerInteraction)
avatarHeader = ChatMessageAvatarHeader(timestamp: content.index.timestamp, peerId: effectiveAuthor.id, peer: effectiveAuthor, messageReference: MessageReference(message), presentationData: presentationData, context: context, controllerInteraction: controllerInteraction)
}
}
}
self.accessoryItem = accessoryItem
self.avatarHeader = avatarHeader
var headers: [ListViewItemHeader] = [self.dateHeader]
if let avatarHeader = self.avatarHeader {
headers.append(avatarHeader)
}
self.headers = headers
}
public func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal<Void, NoError>?, (ListViewItemApply) -> Void)) -> Void) {
@ -467,25 +476,25 @@ public final class ChatMessageItem: ListViewItem, CustomStringConvertible {
var mergedBottom: ChatMessageMerge = .none
var dateAtBottom = false
if let top = top as? ChatMessageItem {
if top.header.id != self.header.id {
if top.dateHeader.id != self.dateHeader.id {
mergedBottom = .none
} else {
mergedBottom = messagesShouldBeMerged(accountPeerId: self.context.account.peerId, message, top.message)
}
}
if let bottom = bottom as? ChatMessageItem {
if bottom.header.id != self.header.id {
if bottom.dateHeader.id != self.dateHeader.id {
mergedTop = .none
dateAtBottom = true
} else {
mergedTop = messagesShouldBeMerged(accountPeerId: self.context.account.peerId, bottom.message, message)
}
} else if let bottom = bottom as? ChatUnreadItem {
if bottom.header.id != self.header.id {
if bottom.header.id != self.dateHeader.id {
dateAtBottom = true
}
} else if let bottom = bottom as? ChatReplyCountItem {
if bottom.header.id != self.header.id {
if bottom.header.id != self.dateHeader.id {
dateAtBottom = true
}
} else if let _ = bottom as? ChatHoleItem {

View File

@ -801,9 +801,9 @@ public class ChatMessageItemView: ListViewItemNode {
return nil
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header
return item.headers
} else {
return nil
}

View File

@ -211,9 +211,9 @@ class ChatReplyCountItemNode: ListViewItemNode {
}
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header
return [item.header]
} else {
return nil
}

View File

@ -147,9 +147,9 @@ class ChatUnreadItemNode: ListViewItemNode {
}
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header
return [item.header]
} else {
return nil
}

View File

@ -347,7 +347,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
let selectionPanel: ChatMessageSelectionInputPanelNode
let separatorNode: ASDisplayNode
let backgroundNode: ASDisplayNode
let backgroundNode: NavigationBackgroundNode
init(context: AccountContext, peerId: PeerId, deleteMessages: @escaping () -> Void, shareMessages: @escaping () -> Void, forwardMessages: @escaping () -> Void, reportMessages: @escaping () -> Void) {
self.context = context
@ -360,11 +360,10 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.separatorNode = ASDisplayNode()
self.backgroundNode = ASDisplayNode()
self.backgroundNode = NavigationBackgroundNode(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor)
self.selectionPanel = ChatMessageSelectionInputPanelNode(theme: presentationData.theme, strings: presentationData.strings, peerMedia: true)
self.selectionPanel.context = context
self.selectionPanel.backgroundColor = presentationData.theme.chat.inputPanel.panelBackgroundColor
let interfaceInteraction = ChatPanelInterfaceInteraction(setupReplyMessage: { _, _ in
}, setupEditMessage: { _, _ in
@ -466,7 +465,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
}
func update(layout: ContainerViewLayout, presentationData: PresentationData, transition: ContainedViewLayoutTransition) -> CGFloat {
self.backgroundNode.backgroundColor = presentationData.theme.rootController.navigationBar.blurredBackgroundColor
self.backgroundNode.updateColor(color: presentationData.theme.rootController.navigationBar.blurredBackgroundColor, transition: .immediate)
self.separatorNode.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor
let interfaceState = ChatPresentationInterfaceState(chatWallpaper: .color(0), theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameDisplayOrder: presentationData.nameDisplayOrder, limitsConfiguration: .defaultValue, fontSize: .regular, bubbleCorners: PresentationChatBubbleCorners(mainRadius: 16.0, auxiliaryRadius: 8.0, mergeBubbleCorners: true), accountPeerId: self.context.account.peerId, mode: .standard(previewing: false), chatLocation: .peer(self.peerId), subject: nil, peerNearbyData: nil, greetingData: nil, pendingUnpinnedAllMessages: false, activeGroupCallInfo: nil, hasActiveGroupCall: false, importState: nil)
@ -477,6 +476,7 @@ final class PeerInfoSelectionPanelNode: ASDisplayNode {
let panelHeightWithInset = panelHeight + layout.intrinsicInsets.bottom
transition.updateFrame(node: self.backgroundNode, frame: CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: panelHeightWithInset)))
self.backgroundNode.update(size: self.backgroundNode.bounds.size, transition: transition)
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: -UIScreenPixel), size: CGSize(width: layout.size.width, height: UIScreenPixel)))
return panelHeightWithInset

View File

@ -200,9 +200,9 @@ class WebSearchRecentQueryItemNode: ItemListRevealOptionsItemNode {
self.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration * 0.5, removeOnCompletion: false)
}
override public func header() -> ListViewItemHeader? {
override public func headers() -> [ListViewItemHeader]? {
if let item = self.item {
return item.header
return item.header.flatMap { [$0] }
} else {
return nil
}