Monoforums

This commit is contained in:
Isaac 2025-05-23 00:37:21 +08:00
parent c2052b559a
commit c264378b6a
9 changed files with 283 additions and 223 deletions

View File

@ -881,16 +881,6 @@ open class NavigationBar: ASDisplayNode {
if needsLeftButton { if needsLeftButton {
if animated { if animated {
if self.leftButtonNode.view.superview != nil {
if let snapshotView = self.leftButtonNode.view.snapshotContentTree() {
snapshotView.frame = self.leftButtonNode.frame
self.leftButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.leftButtonNode.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
if self.backButtonNode.view.superview != nil { if self.backButtonNode.view.superview != nil {
if let snapshotView = self.backButtonNode.view.snapshotContentTree() { if let snapshotView = self.backButtonNode.view.snapshotContentTree() {
snapshotView.frame = self.backButtonNode.frame snapshotView.frame = self.backButtonNode.frame
@ -927,9 +917,11 @@ open class NavigationBar: ASDisplayNode {
self.badgeNode.removeFromSupernode() self.badgeNode.removeFromSupernode()
if let leftBarButtonItem = item.leftBarButtonItem { if let leftBarButtonItem = item.leftBarButtonItem {
self.leftButtonNode.updateItems([leftBarButtonItem]) self.leftButtonNode.updateItems([], animated: animated)
self.leftButtonNode.updateItems([leftBarButtonItem], animated: animated)
} else { } else {
self.leftButtonNode.updateItems([UIBarButtonItem(title: self.presentationData.strings.close, style: .plain, target: nil, action: nil)]) self.leftButtonNode.updateItems([], animated: animated)
self.leftButtonNode.updateItems([UIBarButtonItem(title: self.presentationData.strings.close, style: .plain, target: nil, action: nil)], animated: animated)
} }
if self.leftButtonNode.supernode == nil { if self.leftButtonNode.supernode == nil {
@ -994,9 +986,6 @@ open class NavigationBar: ASDisplayNode {
} }
self.updateAccessibilityElements() self.updateAccessibilityElements()
if animated {
self.hintAnimateTitleNodeOnNextLayout = true
}
} }
private func updateRightButton(animated: Bool) { private func updateRightButton(animated: Bool) {
@ -1008,23 +997,14 @@ open class NavigationBar: ASDisplayNode {
items = [rightBarButtonItem] items = [rightBarButtonItem]
} }
self.rightButtonNodeUpdated = true
if !items.isEmpty { if !items.isEmpty {
if animated, self.rightButtonNode.view.superview != nil { self.rightButtonNode.updateItems([], animated: animated)
if let snapshotView = self.rightButtonNode.view.snapshotContentTree() { self.rightButtonNode.updateItems(items, animated: animated)
snapshotView.frame = self.rightButtonNode.frame
self.rightButtonNode.view.superview?.insertSubview(snapshotView, aboveSubview: self.rightButtonNode.view)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview()
})
}
}
self.rightButtonNode.updateItems(items)
if self.rightButtonNode.supernode == nil { if self.rightButtonNode.supernode == nil {
self.buttonsContainerNode.addSubnode(self.rightButtonNode) self.buttonsContainerNode.addSubnode(self.rightButtonNode)
} }
if animated {
self.rightButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.15)
}
} else { } else {
if animated, self.rightButtonNode.view.superview != nil { if animated, self.rightButtonNode.view.superview != nil {
if let snapshotView = self.rightButtonNode.view.snapshotContentTree() { if let snapshotView = self.rightButtonNode.view.snapshotContentTree() {
@ -1050,9 +1030,6 @@ open class NavigationBar: ASDisplayNode {
self.rightButtonNode.removeFromSupernode() self.rightButtonNode.removeFromSupernode()
} }
if animated {
self.hintAnimateTitleNodeOnNextLayout = true
}
self.updateAccessibilityElements() self.updateAccessibilityElements()
} }
@ -1062,6 +1039,7 @@ open class NavigationBar: ASDisplayNode {
public let backButtonArrow: ASImageNode public let backButtonArrow: ASImageNode
public let leftButtonNode: NavigationButtonNode public let leftButtonNode: NavigationButtonNode
public let rightButtonNode: NavigationButtonNode public let rightButtonNode: NavigationButtonNode
private var rightButtonNodeUpdated: Bool = false
public let additionalContentNode: SparseNode public let additionalContentNode: SparseNode
private let navigationBackgroundCutoutView: NavigationBackgroundCutoutView private let navigationBackgroundCutoutView: NavigationBackgroundCutoutView
@ -1359,7 +1337,7 @@ open class NavigationBar: ASDisplayNode {
var leftTitleInset: CGFloat = leftInset + 1.0 var leftTitleInset: CGFloat = leftInset + 1.0
var rightTitleInset: CGFloat = rightInset + 1.0 var rightTitleInset: CGFloat = rightInset + 1.0
if self.backButtonNode.supernode != nil { if self.backButtonNode.supernode != nil {
let backButtonSize = self.backButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape) let backButtonSize = self.backButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true)
leftTitleInset = backButtonSize.width + backButtonInset + 1.0 leftTitleInset = backButtonSize.width + backButtonInset + 1.0
let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5 let topHitTestSlop = (nominalHeight - backButtonSize.height) * 0.5
@ -1411,7 +1389,7 @@ open class NavigationBar: ASDisplayNode {
self.badgeNode.alpha = 1.0 self.badgeNode.alpha = 1.0
} }
} else if self.leftButtonNode.supernode != nil { } else if self.leftButtonNode.supernode != nil {
let leftButtonSize = self.leftButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape) let leftButtonSize = self.leftButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true)
leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0 leftTitleInset = leftButtonSize.width + leftButtonInset + 1.0
var transition = transition var transition = transition
@ -1428,16 +1406,18 @@ open class NavigationBar: ASDisplayNode {
transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 16.0, dy: 2.0), size: badgeSize)) transition.updateFrame(node: self.badgeNode, frame: CGRect(origin: backButtonArrowFrame.origin.offsetBy(dx: 16.0, dy: 2.0), size: badgeSize))
if self.rightButtonNode.supernode != nil { if self.rightButtonNode.supernode != nil {
let rightButtonSize = self.rightButtonNode.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight)), isLandscape: isLandscape) let rightButtonSize = self.rightButtonNode.updateLayout(constrainedSize: (CGSize(width: size.width, height: nominalHeight)), isLandscape: isLandscape, isLeftAligned: false)
rightTitleInset = rightButtonSize.width + leftButtonInset + 1.0 rightTitleInset = rightButtonSize.width + leftButtonInset + 1.0
self.rightButtonNode.alpha = 1.0 self.rightButtonNode.alpha = 1.0
var transition = transition var transition = transition
if self.rightButtonNode.frame.width.isZero { if self.rightButtonNode.frame.width.isZero || self.rightButtonNodeUpdated {
transition = .immediate transition = .immediate
} }
transition.updateFrame(node: self.rightButtonNode, frame: CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize)) transition.updateFrame(node: self.rightButtonNode, frame: CGRect(origin: CGPoint(x: size.width - leftButtonInset - rightButtonSize.width, y: contentVerticalOrigin + floor((nominalHeight - rightButtonSize.height) / 2.0)), size: rightButtonSize))
} }
self.rightButtonNodeUpdated = false
if let transitionState = self.transitionState { if let transitionState = self.transitionState {
let progress = transitionState.progress let progress = transitionState.progress
@ -1447,7 +1427,7 @@ open class NavigationBar: ASDisplayNode {
break break
case .bottom: case .bottom:
if let transitionBackButtonNode = self.transitionBackButtonNode { if let transitionBackButtonNode = self.transitionBackButtonNode {
let transitionBackButtonSize = transitionBackButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape) let transitionBackButtonSize = transitionBackButtonNode.updateLayout(constrainedSize: CGSize(width: size.width, height: nominalHeight), isLandscape: isLandscape, isLeftAligned: true)
let initialX: CGFloat = backButtonInset + size.width * 0.3 let initialX: CGFloat = backButtonInset + size.width * 0.3
let finalX: CGFloat = floor((size.width - transitionBackButtonSize.width) / 2.0) let finalX: CGFloat = floor((size.width - transitionBackButtonSize.width) / 2.0)
@ -1592,7 +1572,7 @@ open class NavigationBar: ASDisplayNode {
node.updateManualText(self.backButtonNode.manualText) node.updateManualText(self.backButtonNode.manualText)
node.color = accentColor node.color = accentColor
if let validLayout = self.validLayout { if let validLayout = self.validLayout {
let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape) let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape, isLeftAligned: true)
node.frame = self.backButtonNode.frame node.frame = self.backButtonNode.frame
} }
return node return node
@ -1608,7 +1588,7 @@ open class NavigationBar: ASDisplayNode {
node.updateManualText(self.backButtonNode.manualText) node.updateManualText(self.backButtonNode.manualText)
node.color = accentColor node.color = accentColor
if let validLayout = self.validLayout { if let validLayout = self.validLayout {
let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape) let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape, isLeftAligned: true)
node.frame = self.backButtonNode.frame node.frame = self.backButtonNode.frame
} }
return node.view return node.view
@ -1628,10 +1608,10 @@ open class NavigationBar: ASDisplayNode {
items = [rightBarButtonItem] items = [rightBarButtonItem]
} }
} }
node.updateItems(items) node.updateItems(items, animated: false)
node.color = accentColor node.color = accentColor
if let validLayout = self.validLayout { if let validLayout = self.validLayout {
let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape) let _ = node.updateLayout(constrainedSize: CGSize(width: validLayout.size.width, height: validLayout.defaultHeight), isLandscape: validLayout.isLandscape, isLeftAligned: false)
node.frame = self.backButtonNode.frame node.frame = self.backButtonNode.frame
} }
return node return node

View File

@ -331,6 +331,8 @@ private final class NavigationButtonItemNode: ImmediateTextNode {
public final class NavigationButtonNode: ContextControllerSourceNode { public final class NavigationButtonNode: ContextControllerSourceNode {
private var nodes: [NavigationButtonItemNode] = [] private var nodes: [NavigationButtonItemNode] = []
private var disappearingNodes: [(frame: CGRect, size: CGSize, node: NavigationButtonItemNode)] = []
public var singleCustomNode: ASDisplayNode? { public var singleCustomNode: ASDisplayNode? {
for node in self.nodes { for node in self.nodes {
return node.node return node.node
@ -452,7 +454,7 @@ public final class NavigationButtonNode: ContextControllerSourceNode {
} }
} }
func updateItems(_ items: [UIBarButtonItem]) { func updateItems(_ items: [UIBarButtonItem], animated: Bool) {
for i in 0 ..< items.count { for i in 0 ..< items.count {
let node: NavigationButtonItemNode let node: NavigationButtonItemNode
if self.nodes.count > i { if self.nodes.count > i {
@ -486,16 +488,41 @@ public final class NavigationButtonNode: ContextControllerSourceNode {
node.bold = items[i].style == .done node.bold = items[i].style == .done
node.isEnabled = items[i].isEnabled node.isEnabled = items[i].isEnabled
node.node = items[i].customDisplayNode node.node = items[i].customDisplayNode
if animated {
node.layer.animateAlpha(from: 0.0, to: self.manualAlpha, duration: 0.16)
node.layer.animateScale(from: 0.001, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring)
}
} }
if items.count < self.nodes.count { if items.count < self.nodes.count {
for i in items.count ..< self.nodes.count { for i in items.count ..< self.nodes.count {
self.nodes[i].removeFromSupernode() let itemNode = self.nodes[i]
if animated {
disappearingNodes.append((itemNode.frame, self.bounds.size, itemNode))
itemNode.layer.animateAlpha(from: self.manualAlpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self, weak itemNode] _ in
guard let itemNode else {
return
}
itemNode.removeFromSupernode()
guard let self else {
return
}
if let index = self.disappearingNodes.firstIndex(where: { $0.node === itemNode }) {
self.disappearingNodes.remove(at: index)
}
})
itemNode.layer.animateScale(from: 1.0, to: 0.001, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
} else {
itemNode.removeFromSupernode()
}
} }
self.nodes.removeSubrange(items.count...) self.nodes.removeSubrange(items.count...)
} }
} }
public func updateLayout(constrainedSize: CGSize, isLandscape: Bool) -> CGSize { public func updateLayout(constrainedSize: CGSize, isLandscape: Bool, isLeftAligned: Bool) -> CGSize {
var nodeOrigin = CGPoint() var nodeOrigin = CGPoint()
var totalHeight: CGFloat = 0.0 var totalHeight: CGFloat = 0.0
for i in 0 ..< self.nodes.count { for i in 0 ..< self.nodes.count {
@ -520,6 +547,13 @@ public final class NavigationButtonNode: ContextControllerSourceNode {
nodeOrigin.x -= 5.0 nodeOrigin.x -= 5.0
} }
} }
if !isLeftAligned {
for disappearingNode in self.disappearingNodes {
disappearingNode.node.frame = disappearingNode.frame.offsetBy(dx: nodeOrigin.x - disappearingNode.size.width, dy: (totalHeight - disappearingNode.size.height) * 0.5)
}
}
return CGSize(width: nodeOrigin.x, height: totalHeight) return CGSize(width: nodeOrigin.x, height: totalHeight)
} }

View File

@ -265,31 +265,47 @@ public final class ChatAvatarNavigationNode: ASDisplayNode {
public final class SnapshotState { public final class SnapshotState {
fileprivate let snapshotView: UIView? fileprivate let snapshotView: UIView?
fileprivate let snapshotStatusView: UIView?
fileprivate init(snapshotView: UIView?) { fileprivate init(snapshotView: UIView?, snapshotStatusView: UIView?) {
self.snapshotView = snapshotView self.snapshotView = snapshotView
self.snapshotStatusView = snapshotStatusView
} }
} }
public func prepareSnapshotState() -> SnapshotState { public func prepareSnapshotState() -> SnapshotState {
let snapshotView = self.avatarNode.view.snapshotView(afterScreenUpdates: false) let snapshotView = self.avatarNode.view.snapshotView(afterScreenUpdates: false)
let snapshotStatusView = self.statusView.view?.snapshotView(afterScreenUpdates: false)
return SnapshotState( return SnapshotState(
snapshotView: snapshotView snapshotView: snapshotView,
snapshotStatusView: snapshotStatusView
) )
} }
public func animateFromSnapshot(_ snapshotState: SnapshotState) { public func animateFromSnapshot(_ snapshotState: SnapshotState) {
self.avatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) self.avatarNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.16)
self.avatarNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true) self.avatarNode.layer.animateScale(from: 0.1, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true)
self.statusView.view?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.16)
self.statusView.view?.layer.animateScale(from: 0.1, to: 1.0, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: true)
if let snapshotView = snapshotState.snapshotView { if let snapshotView = snapshotState.snapshotView {
snapshotView.frame = self.frame snapshotView.frame = self.frame
self.containerNode.view.addSubview(snapshotView) self.containerNode.view.insertSubview(snapshotView, at: 0)
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { [weak snapshotView] _ in snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
snapshotView?.removeFromSuperview() snapshotView?.removeFromSuperview()
}) })
snapshotView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false) snapshotView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
}
if let snapshotStatusView = snapshotState.snapshotStatusView {
snapshotStatusView.frame = CGRect(origin: CGPoint(x: floor((self.containerNode.bounds.width - snapshotStatusView.bounds.width) / 2.0), y: floor((self.containerNode.bounds.height - snapshotStatusView.bounds.height) / 2.0)), size: snapshotStatusView.bounds.size)
self.containerNode.view.insertSubview(snapshotStatusView, at: 0)
snapshotStatusView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotStatusView] _ in
snapshotStatusView?.removeFromSuperview()
})
snapshotStatusView.layer.animateScale(from: 1.0, to: 0.1, duration: 0.4, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
} }
} }

View File

@ -1021,16 +1021,16 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
if activitySize.width < size.width { if activitySize.width < size.width {
activityFrame.origin.x = -clearBounds.minX + floor((size.width - activityFrame.width) / 2.0) activityFrame.origin.x = -clearBounds.minX + floor((size.width - activityFrame.width) / 2.0)
} }
self.activityNode.frame = activityFrame titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: activityFrame)
} }
if let image = self.titleLeftIconNode.image { if let image = self.titleLeftIconNode.image {
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size) titleTransition.updateFrame(node: self.titleLeftIconNode, frame: CGRect(origin: CGPoint(x: -image.size.width - 3.0 - UIScreenPixel, y: 4.0), size: image.size))
} }
var nextIconX: CGFloat = titleFrame.width var nextIconX: CGFloat = titleFrame.width
self.titleVerifiedIconView.frame = CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize) titleTransition.updateFrame(view: self.titleVerifiedIconView, frame: CGRect(origin: CGPoint(x: 0.0, y: floor((titleFrame.height - titleVerifiedSize.height) / 2.0)), size: titleVerifiedSize))
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
nextIconX -= titleCredibilitySize.width nextIconX -= titleCredibilitySize.width
@ -1056,7 +1056,7 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
titleTransition.updateFrameAdditiveToCenter(view: self.titleContainerView, frame: titleFrame) titleTransition.updateFrameAdditiveToCenter(view: self.titleContainerView, frame: titleFrame)
titleTransition.updateFrameAdditiveToCenter(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size)) titleTransition.updateFrameAdditiveToCenter(node: self.titleTextNode, frame: CGRect(origin: CGPoint(), size: titleFrame.size))
self.activityNode.frame = CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + statusIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize) titleTransition.updateFrameAdditiveToCenter(node: self.activityNode, frame: CGRect(origin: CGPoint(x: floor((clearBounds.width - combinedWidth) / 2.0 + titleSize.width + leftIconWidth + credibilityIconWidth + verifiedIconWidth + statusIconWidth + rightIconWidth + titleInfoSpacing), y: floor((size.height - activitySize.height) / 2.0)), size: activitySize))
if let image = self.titleLeftIconNode.image { if let image = self.titleLeftIconNode.image {
self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.minY + 4.0), size: image.size) self.titleLeftIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.minX, y: titleFrame.minY + 4.0), size: image.size)
@ -1069,11 +1069,11 @@ public final class ChatTitleView: UIView, NavigationBarTitleView {
self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize) self.titleCredibilityIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleCredibilitySize.width, y: floor((titleFrame.height - titleCredibilitySize.height) / 2.0)), size: titleCredibilitySize)
nextIconX -= titleCredibilitySize.width nextIconX -= titleCredibilitySize.width
self.titleStatusIconView.frame = CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize) titleTransition.updateFrame(view: self.titleStatusIconView, frame: CGRect(origin: CGPoint(x: nextIconX - titleStatusSize.width, y: floor((titleFrame.height - titleStatusSize.height) / 2.0)), size: titleStatusSize))
nextIconX -= titleStatusSize.width nextIconX -= titleStatusSize.width
if let image = self.titleRightIconNode.image { if let image = self.titleRightIconNode.image {
self.titleRightIconNode.frame = CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: titleFrame.minY + 6.0), size: image.size) titleTransition.updateFrame(node: self.titleRightIconNode, frame: CGRect(origin: CGPoint(x: titleFrame.maxX - image.size.width, y: titleFrame.minY + 6.0), size: image.size))
} }
} }

View File

@ -153,7 +153,8 @@ extension ChatControllerImpl {
currentChatListFilter: self.currentChatListFilter, currentChatListFilter: self.currentChatListFilter,
customChatNavigationStack: self.customChatNavigationStack, customChatNavigationStack: self.customChatNavigationStack,
presentationData: self.presentationData, presentationData: self.presentationData,
historyNode: historyNode historyNode: historyNode,
inviteRequestsContext: self.contentData?.inviteRequestsContext
) )
self.pendingContentData = (contentData, historyNode) self.pendingContentData = (contentData, historyNode)
self.contentDataDisposable = (contentData.isReady.get() self.contentDataDisposable = (contentData.isReady.get()
@ -248,7 +249,16 @@ extension ChatControllerImpl {
self.chatDisplayNode.overlayTitle = contentData.overlayTitle self.chatDisplayNode.overlayTitle = contentData.overlayTitle
self.chatDisplayNode.historyNode.nextChannelToRead = contentData.state.nextChannelToRead self.chatDisplayNode.historyNode.nextChannelToRead = contentData.state.nextChannelToRead.flatMap { nextChannelToRead -> (peer: EnginePeer, threadData: (id: Int64, data: MessageHistoryThreadData)?, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation)? in
return (
nextChannelToRead.peer,
nextChannelToRead.threadData.flatMap { threadData -> (id: Int64, data: MessageHistoryThreadData) in
return (threadData.id, threadData.data)
},
nextChannelToRead.unreadCount,
nextChannelToRead.location
)
}
self.chatDisplayNode.historyNode.nextChannelToReadDisplayName = contentData.state.nextChannelToReadDisplayName self.chatDisplayNode.historyNode.nextChannelToReadDisplayName = contentData.state.nextChannelToReadDisplayName
self.updateNextChannelToReadVisibility() self.updateNextChannelToReadVisibility()
@ -286,6 +296,18 @@ extension ChatControllerImpl {
didDisplayActionsPanel = true didDisplayActionsPanel = true
} }
var previousInvitationPeers: [EnginePeer] = []
if let requestsState = previousState.requestsState {
previousInvitationPeers = Array(requestsState.importers.compactMap({ $0.peer.peer.flatMap({ EnginePeer($0) }) }).prefix(3))
}
var previousInvitationRequestsPeersDismissed = false
if let dismissedInvitationRequests = previousState.dismissedInvitationRequests, Set(previousInvitationPeers.map({ $0.id.toInt64() })) == Set(dismissedInvitationRequests) {
previousInvitationRequestsPeersDismissed = true
}
if let requestsState = previousState.requestsState, requestsState.count > 0 && !previousInvitationRequestsPeersDismissed {
didDisplayActionsPanel = true
}
var displayActionsPanel = false var displayActionsPanel = false
if let contactStatus = contentData.state.contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings { if let contactStatus = contentData.state.contactStatus, !contactStatus.isEmpty, let peerStatusSettings = contactStatus.peerStatusSettings {
if !peerStatusSettings.flags.isEmpty { if !peerStatusSettings.flags.isEmpty {
@ -307,6 +329,18 @@ extension ChatControllerImpl {
displayActionsPanel = true displayActionsPanel = true
} }
var invitationPeers: [EnginePeer] = []
if let requestsState = contentData.state.requestsState {
invitationPeers = Array(requestsState.importers.compactMap({ $0.peer.peer.flatMap({ EnginePeer($0) }) }).prefix(3))
}
var invitationRequestsPeersDismissed = false
if let dismissedInvitationRequests = contentData.state.dismissedInvitationRequests, Set(invitationPeers.map({ $0.id.toInt64() })) == Set(dismissedInvitationRequests) {
invitationRequestsPeersDismissed = true
}
if let requestsState = contentData.state.requestsState, requestsState.count > 0 && !invitationRequestsPeersDismissed {
displayActionsPanel = true
}
if displayActionsPanel != didDisplayActionsPanel { if displayActionsPanel != didDisplayActionsPanel {
animated = true animated = true
} }
@ -411,7 +445,6 @@ extension ChatControllerImpl {
if let dismissedInvitationRequests = contentData.state.dismissedInvitationRequests, Set(peers.map({ $0.id.toInt64() })) == Set(dismissedInvitationRequests) { if let dismissedInvitationRequests = contentData.state.dismissedInvitationRequests, Set(peers.map({ $0.id.toInt64() })) == Set(dismissedInvitationRequests) {
peersDismissed = true peersDismissed = true
} }
if let requestsState = contentData.state.requestsState, requestsState.count > 0 && !peersDismissed { if let requestsState = contentData.state.requestsState, requestsState.count > 0 && !peersDismissed {
if !context.contains(where: { if !context.contains(where: {
switch $0 { switch $0 {

View File

@ -434,14 +434,6 @@ func updateChatPresentationInterfaceStateImpl(
selfController.presentationInterfaceState = updatedChatPresentationInterfaceState selfController.presentationInterfaceState = updatedChatPresentationInterfaceState
/*if selfController.chatDisplayNode.chatLocation != selfController.presentationInterfaceState.chatLocation {
let defaultDirection: ChatControllerAnimateInnerChatSwitchDirection? = selfController.chatDisplayNode.chatLocationTabSwitchDirection(from: selfController.chatLocation.threadId, to: selfController.presentationInterfaceState.chatLocation.threadId).flatMap { direction -> ChatControllerAnimateInnerChatSwitchDirection in
return direction ? .right : .left
}
let tabSwitchDirection = selfController.currentChatSwitchDirection ?? defaultDirection
selfController.chatDisplayNode.updateChatLocation(chatLocation: selfController.presentationInterfaceState.chatLocation, transition: transition, tabSwitchDirection: tabSwitchDirection)
}*/
selfController.updateSlowmodeStatus() selfController.updateSlowmodeStatus()
switch updatedChatPresentationInterfaceState.inputMode { switch updatedChatPresentationInterfaceState.inputMode {
@ -498,20 +490,11 @@ func updateChatPresentationInterfaceStateImpl(
} }
var buttonsAnimated = transition.isAnimated var buttonsAnimated = transition.isAnimated
if selfController.currentChatSwitchDirection != nil {
buttonsAnimated = false
}
if let button = rightNavigationButtonForChatInterfaceState(context: selfController.context, presentationInterfaceState: updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.rightNavigationButton, target: selfController, selector: #selector(selfController.rightNavigationButtonAction), chatInfoNavigationButton: selfController.chatInfoNavigationButton, moreInfoNavigationButton: selfController.moreInfoNavigationButton) { if let button = rightNavigationButtonForChatInterfaceState(context: selfController.context, presentationInterfaceState: updatedChatPresentationInterfaceState, strings: updatedChatPresentationInterfaceState.strings, currentButton: selfController.rightNavigationButton, target: selfController, selector: #selector(selfController.rightNavigationButtonAction), chatInfoNavigationButton: selfController.chatInfoNavigationButton, moreInfoNavigationButton: selfController.moreInfoNavigationButton) {
if selfController.rightNavigationButton != button { if selfController.rightNavigationButton != button {
if let currentButton = selfController.rightNavigationButton?.action, currentButton == button.action { if let currentButton = selfController.rightNavigationButton?.action, currentButton == button.action {
buttonsAnimated = false buttonsAnimated = false
} }
if case .replyThread = selfController.chatLocation {
buttonsAnimated = false
}
if let channel = updatedChatPresentationInterfaceState.renderedPeer?.peer as? TelegramChannel, channel.isMonoForum {
buttonsAnimated = false
}
selfController.rightNavigationButton = button selfController.rightNavigationButton = button
} }
} else if let _ = selfController.rightNavigationButton { } else if let _ = selfController.rightNavigationButton {

View File

@ -9751,6 +9751,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
let navigationSnapshot = self.chatTitleView?.prepareSnapshotState() let navigationSnapshot = self.chatTitleView?.prepareSnapshotState()
let avatarSnapshot = self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil ? (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.prepareSnapshotState() : nil
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil) let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
let historyNode = self.chatDisplayNode.createHistoryNodeForChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder) let historyNode = self.chatDisplayNode.createHistoryNodeForChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder)
@ -9780,108 +9781,15 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
} }
self.chatTitleView?.animateFromSnapshot(navigationSnapshot, direction: mappedAnimationDirection) self.chatTitleView?.animateFromSnapshot(navigationSnapshot, direction: mappedAnimationDirection)
/*if let rightBarButtonItems = self.navigationItem.rightBarButtonItems { }
for i in 0 ..< rightBarButtonItems.count {
let item = rightBarButtonItems[i] if let avatarSnapshot, self.chatInfoNavigationButton?.buttonItem.customDisplayNode?.view.window != nil {
if let customDisplayNode = item.customDisplayNode { (self.chatInfoNavigationButton?.buttonItem.customDisplayNode as? ChatAvatarNavigationNode)?.animateFromSnapshot(avatarSnapshot)
customDisplayNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
//customDisplayNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
let _ = rightBarButtonItemSnapshots
/*if i < rightBarButtonItemSnapshots.count {
let (snapshotItem, snapshotFrame) = rightBarButtonItemSnapshots[i]
if let targetSuperview = customDisplayNode.view.superview {
snapshotItem.frame = targetSuperview.convert(snapshotFrame, from: self.view)
targetSuperview.addSubview(snapshotItem)
snapshotItem.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, completion: { [weak snapshotItem] _ in
snapshotItem?.removeFromSuperview()
})
snapshotItem.layer.animateScale(from: 1.0, to: 0.1, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
}
}*/
}
}
}*/
} }
self.currentChatSwitchDirection = nil self.currentChatSwitchDirection = nil
self.isUpdatingChatLocationThread = false self.isUpdatingChatLocationThread = false
}) })
/*
//
let rightBarButtonItemSnapshots: [(UIView, CGRect)] = (self.navigationItem.rightBarButtonItems ?? []).compactMap { item -> (UIView, CGRect)? in
guard let view = item.customDisplayNode?.view, let snapshotView = view.snapshotView(afterScreenUpdates: false) else {
return nil
}
return (snapshotView, view.convert(view.bounds, to: self.view))
}
let isReady = Promise<Bool>()
let chatLocationContextHolder = Atomic<ChatLocationContextHolder?>(value: nil)
self.reloadChatLocation(chatLocation: updatedChatLocation, chatLocationContextHolder: chatLocationContextHolder, historyNode: historyNode, isReady: isReady)
self.isUpdatingChatLocationThread = true
self.updateChatLocationThreadDisposable?.dispose()
self.updateChatLocationThreadDisposable = (isReady.get()
|> filter { $0 }
|> take(1)
|> deliverOnMainQueue).startStrict(next: { [weak self] _ in
guard let self else {
return
}
self.isUpdatingChatLocationThread = false
self.currentChatSwitchDirection = animationDirection
self.updateChatPresentationInterfaceState(animated: animationDirection != nil, interactive: false, { presentationInterfaceState in
return presentationInterfaceState.updatedChatLocation(updatedChatLocation)
})
if let navigationSnapshot, let animationDirection {
let mappedAnimationDirection: ChatTitleView.AnimateFromSnapshotDirection
switch animationDirection {
case .up:
mappedAnimationDirection = .up
case .down:
mappedAnimationDirection = .down
case .left:
mappedAnimationDirection = .left
case .right:
mappedAnimationDirection = .right
}
self.chatTitleView?.animateFromSnapshot(navigationSnapshot, direction: mappedAnimationDirection)
if let rightBarButtonItems = self.navigationItem.rightBarButtonItems {
for i in 0 ..< rightBarButtonItems.count {
let item = rightBarButtonItems[i]
if let customDisplayNode = item.customDisplayNode {
customDisplayNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
//customDisplayNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
let _ = rightBarButtonItemSnapshots
/*if i < rightBarButtonItemSnapshots.count {
let (snapshotItem, snapshotFrame) = rightBarButtonItemSnapshots[i]
if let targetSuperview = customDisplayNode.view.superview {
snapshotItem.frame = targetSuperview.convert(snapshotFrame, from: self.view)
targetSuperview.addSubview(snapshotItem)
snapshotItem.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.18, completion: { [weak snapshotItem] _ in
snapshotItem?.removeFromSuperview()
})
snapshotItem.layer.animateScale(from: 1.0, to: 0.1, duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring)
}
}*/
}
}
}
}
self.currentChatSwitchDirection = nil
})*/
} }
public var contentContainerNode: ASDisplayNode { public var contentContainerNode: ASDisplayNode {

View File

@ -57,6 +57,30 @@ extension ChatControllerImpl {
case dismiss case dismiss
} }
struct NextChannelToRead: Equatable {
struct ThreadData: Equatable {
let id: Int64
let data: MessageHistoryThreadData
init(id: Int64, data: MessageHistoryThreadData) {
self.id = id
self.data = data
}
}
let peer: EnginePeer
let threadData: ThreadData?
let unreadCount: Int
let location: TelegramEngine.NextUnreadChannelLocation
init(peer: EnginePeer, threadData: ThreadData?, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation) {
self.peer = peer
self.threadData = threadData
self.unreadCount = unreadCount
self.location = location
}
}
struct State { struct State {
var peerView: PeerView? var peerView: PeerView?
var threadInfo: EngineMessageHistoryThread.Info? var threadInfo: EngineMessageHistoryThread.Info?
@ -72,7 +96,7 @@ extension ChatControllerImpl {
var contactStatus: ChatContactStatus? var contactStatus: ChatContactStatus?
var adMessage: Message? var adMessage: Message?
var offerNextChannelToRead: Bool = false var offerNextChannelToRead: Bool = false
var nextChannelToRead: (peer: EnginePeer, threadData: (id: Int64, data: MessageHistoryThreadData)?, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation)? var nextChannelToRead: NextChannelToRead?
var nextChannelToReadDisplayName: Bool = false var nextChannelToReadDisplayName: Bool = false
var isNotAccessible: Bool = false var isNotAccessible: Bool = false
var hasBots: Bool = false var hasBots: Bool = false
@ -178,8 +202,8 @@ extension ChatControllerImpl {
let chatThemeEmoticonPromise = Promise<String?>() let chatThemeEmoticonPromise = Promise<String?>()
let chatWallpaperPromise = Promise<TelegramWallpaper?>() let chatWallpaperPromise = Promise<TelegramWallpaper?>()
var inviteRequestsContext: PeerInvitationImportersContext? private(set) var inviteRequestsContext: PeerInvitationImportersContext?
private var inviteRequestsDisposable = MetaDisposable() private var inviteRequestsDisposable: Disposable?
init( init(
context: AccountContext, context: AccountContext,
@ -192,10 +216,14 @@ extension ChatControllerImpl {
currentChatListFilter: Int32?, currentChatListFilter: Int32?,
customChatNavigationStack: [EnginePeer.Id]?, customChatNavigationStack: [EnginePeer.Id]?,
presentationData: PresentationData, presentationData: PresentationData,
historyNode: ChatHistoryListNodeImpl historyNode: ChatHistoryListNodeImpl,
inviteRequestsContext: PeerInvitationImportersContext?
) { ) {
self.chatLocation = chatLocation self.chatLocation = chatLocation
self.presentationData = presentationData self.presentationData = presentationData
self.inviteRequestsContext = inviteRequestsContext
let strings = self.presentationData.strings let strings = self.presentationData.strings
let chatLocationPeerId: PeerId? = chatLocation.peerId let chatLocationPeerId: PeerId? = chatLocation.peerId
@ -972,11 +1000,23 @@ extension ChatControllerImpl {
let previousState = strongSelf.state let previousState = strongSelf.state
strongSelf.state.offerNextChannelToRead = true var isUpdated = false
strongSelf.state.nextChannelToRead = nextPeer.flatMap { nextPeer -> (peer: EnginePeer, threadData: (id: Int64, data: MessageHistoryThreadData)?, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation) in
return (peer: nextPeer, threadData: nil, unreadCount: 0, location: .same) if !strongSelf.state.offerNextChannelToRead {
strongSelf.state.offerNextChannelToRead = true
isUpdated = true
}
let nextChannelToRead = nextPeer.flatMap { nextPeer -> NextChannelToRead in
return NextChannelToRead(peer: nextPeer, threadData: nil, unreadCount: 0, location: .same)
}
if strongSelf.state.nextChannelToRead != nextChannelToRead {
strongSelf.state.nextChannelToRead = nextChannelToRead
isUpdated = true
}
if strongSelf.state.nextChannelToReadDisplayName != (nextChatSuggestionTip >= 3) {
strongSelf.state.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3
isUpdated = true
} }
strongSelf.state.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3
let nextPeerId = nextPeer?.id let nextPeerId = nextPeer?.id
@ -992,7 +1032,9 @@ extension ChatControllerImpl {
} }
} }
strongSelf.onUpdated?(previousState) if isUpdated {
strongSelf.onUpdated?(previousState)
}
}) })
} }
} else if isRegularChat, strongSelf.nextChannelToReadDisposable == nil { } else if isRegularChat, strongSelf.nextChannelToReadDisposable == nil {
@ -1012,11 +1054,23 @@ extension ChatControllerImpl {
let previousState = strongSelf.state let previousState = strongSelf.state
strongSelf.state.offerNextChannelToRead = true var isUpdated = false
strongSelf.state.nextChannelToRead = nextPeer.flatMap { nextPeer -> (peer: EnginePeer, threadData: (id: Int64, data: MessageHistoryThreadData)?, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation) in
return (peer: nextPeer.peer, threadData: nil, unreadCount: nextPeer.unreadCount, location: nextPeer.location) if !strongSelf.state.offerNextChannelToRead {
strongSelf.state.offerNextChannelToRead = true
isUpdated = true
}
let nextChannelToRead = nextPeer.flatMap { nextPeer -> NextChannelToRead in
return NextChannelToRead(peer: nextPeer.peer, threadData: nil, unreadCount: nextPeer.unreadCount, location: nextPeer.location)
}
if strongSelf.state.nextChannelToRead != nextChannelToRead {
strongSelf.state.nextChannelToRead = nextChannelToRead
isUpdated = true
}
if strongSelf.state.nextChannelToReadDisplayName != (nextChatSuggestionTip >= 3) {
strongSelf.state.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3
isUpdated = true
} }
strongSelf.state.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3
let nextPeerId = nextPeer?.peer.id let nextPeerId = nextPeer?.peer.id
@ -1032,7 +1086,9 @@ extension ChatControllerImpl {
} }
} }
strongSelf.onUpdated?(previousState) if isUpdated {
strongSelf.onUpdated?(previousState)
}
}) })
} }
} }
@ -1570,13 +1626,27 @@ extension ChatControllerImpl {
let previousState = strongSelf.state let previousState = strongSelf.state
strongSelf.state.offerNextChannelToRead = true var isUpdated = false
strongSelf.state.nextChannelToRead = nextThreadData.flatMap { nextThreadData -> (peer: EnginePeer, threadData: (id: Int64, data: MessageHistoryThreadData)?, unreadCount: Int, location: TelegramEngine.NextUnreadChannelLocation) in
return (peer: EnginePeer(channel), threadData: nextThreadData, unreadCount: Int(nextThreadData.data.incomingUnreadCount), location: .same)
}
strongSelf.state.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3
strongSelf.onUpdated?(previousState) if !strongSelf.state.offerNextChannelToRead {
strongSelf.state.offerNextChannelToRead = true
isUpdated = true
}
let nextChannelToRead = nextThreadData.flatMap { nextThreadData -> NextChannelToRead in
return NextChannelToRead(peer: EnginePeer(channel), threadData: NextChannelToRead.ThreadData(id: nextThreadData.id, data: nextThreadData.data), unreadCount: Int(nextThreadData.data.incomingUnreadCount), location: .same)
}
if strongSelf.state.nextChannelToRead != nextChannelToRead {
strongSelf.state.nextChannelToRead = nextChannelToRead
isUpdated = true
}
if strongSelf.state.nextChannelToReadDisplayName != (nextChatSuggestionTip >= 3) {
strongSelf.state.nextChannelToReadDisplayName = nextChatSuggestionTip >= 3
isUpdated = true
}
if isUpdated {
strongSelf.onUpdated?(previousState)
}
}) })
} }
} }
@ -2129,19 +2199,6 @@ extension ChatControllerImpl {
if strongSelf.inviteRequestsContext == nil { if strongSelf.inviteRequestsContext == nil {
let inviteRequestsContext = context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil)) let inviteRequestsContext = context.engine.peers.peerInvitationImporters(peerId: peerId, subject: .requests(query: nil))
strongSelf.inviteRequestsContext = inviteRequestsContext strongSelf.inviteRequestsContext = inviteRequestsContext
strongSelf.inviteRequestsDisposable.set((combineLatest(queue: Queue.mainQueue(), inviteRequestsContext.state, ApplicationSpecificNotice.dismissedInvitationRequests(accountManager: context.sharedContext.accountManager, peerId: peerId))).startStrict(next: { [weak strongSelf] requestsState, dismissedInvitationRequests in
guard let strongSelf else {
return
}
let previousState = strongSelf.state
strongSelf.state.requestsState = requestsState
strongSelf.state.dismissedInvitationRequests = dismissedInvitationRequests
strongSelf.onUpdated?(previousState)
}))
} else if let inviteRequestsContext = strongSelf.inviteRequestsContext { } else if let inviteRequestsContext = strongSelf.inviteRequestsContext {
let _ = (inviteRequestsContext.state let _ = (inviteRequestsContext.state
|> take(1) |> take(1)
@ -2151,6 +2208,30 @@ extension ChatControllerImpl {
} }
}) })
} }
if chatLocation.threadId == nil {
if strongSelf.inviteRequestsDisposable == nil, let inviteRequestsContext = strongSelf.inviteRequestsContext {
strongSelf.inviteRequestsDisposable = combineLatest(queue: Queue.mainQueue(), inviteRequestsContext.state, ApplicationSpecificNotice.dismissedInvitationRequests(accountManager: context.sharedContext.accountManager, peerId: peerId)).startStrict(next: { [weak strongSelf] requestsState, dismissedInvitationRequests in
guard let strongSelf else {
return
}
let previousState = strongSelf.state
strongSelf.state.requestsState = requestsState
strongSelf.state.dismissedInvitationRequests = dismissedInvitationRequests
strongSelf.onUpdated?(previousState)
})
}
} else {
strongSelf.state.requestsState = nil
strongSelf.state.dismissedInvitationRequests = []
}
} else {
strongSelf.inviteRequestsContext = nil
strongSelf.state.requestsState = nil
strongSelf.state.dismissedInvitationRequests = []
} }
var isUpdated = false var isUpdated = false
@ -2196,7 +2277,7 @@ extension ChatControllerImpl {
self.cachedDataDisposable?.dispose() self.cachedDataDisposable?.dispose()
self.premiumGiftSuggestionDisposable?.dispose() self.premiumGiftSuggestionDisposable?.dispose()
self.translationStateDisposable?.dispose() self.translationStateDisposable?.dispose()
self.inviteRequestsDisposable.dispose() self.inviteRequestsDisposable?.dispose()
} }
} }
} }

View File

@ -108,12 +108,27 @@ private final class ChatInfoTitlePanelPeerNearbyInfoNode: ASDisplayNode {
} }
final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode { final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode {
private final class Params {
let width: CGFloat
let leftInset: CGFloat
let rightInset: CGFloat
let interfaceState: ChatPresentationInterfaceState
init(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, interfaceState: ChatPresentationInterfaceState) {
self.width = width
self.leftInset = leftInset
self.rightInset = rightInset
self.interfaceState = interfaceState
}
}
private let context: AccountContext private let context: AccountContext
private let separatorNode: ASDisplayNode private let separatorNode: ASDisplayNode
private let closeButton: HighlightableButtonNode private let closeButton: HighlightableButtonNode
private var button: UIButton? private let button: HighlightableButtonNode
private let buttonTitle: ImmediateTextNode
private let avatarsContext: AnimatedAvatarSetContext private let avatarsContext: AnimatedAvatarSetContext
private var avatarsContent: AnimatedAvatarSetContext.Content? private var avatarsContent: AnimatedAvatarSetContext.Content?
@ -127,6 +142,8 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode {
private var peers: [EnginePeer] = [] private var peers: [EnginePeer] = []
private var count: Int32 = 0 private var count: Int32 = 0
private var params: Params?
init(context: AccountContext) { init(context: AccountContext) {
self.context = context self.context = context
@ -137,6 +154,10 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode {
self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0) self.closeButton.hitTestSlop = UIEdgeInsets(top: -8.0, left: -8.0, bottom: -8.0, right: -8.0)
self.closeButton.displaysAsynchronously = false self.closeButton.displaysAsynchronously = false
self.button = HighlightableButtonNode()
self.buttonTitle = ImmediateTextNode()
self.buttonTitle.anchorPoint = CGPoint()
self.avatarsContext = AnimatedAvatarSetContext() self.avatarsContext = AnimatedAvatarSetContext()
self.avatarsNode = AnimatedAvatarSetNode() self.avatarsNode = AnimatedAvatarSetNode()
@ -150,6 +171,12 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode {
self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside]) self.closeButton.addTarget(self, action: #selector(self.closePressed), forControlEvents: [.touchUpInside])
self.addSubnode(self.closeButton) self.addSubnode(self.closeButton)
self.button.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
self.addSubnode(self.button)
self.buttonTitle.isUserInteractionEnabled = false
self.button.addSubnode(self.buttonTitle)
self.addSubnode(self.avatarsNode) self.addSubnode(self.avatarsNode)
self.addSubnode(self.activateAreaNode) self.addSubnode(self.activateAreaNode)
@ -161,12 +188,16 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode {
self.peers = peers self.peers = peers
self.count = count self.count = count
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
self.avatarsContent = self.avatarsContext.update(peers: peers, animated: false) self.avatarsContent = self.avatarsContext.update(peers: peers, animated: false)
self.button?.setTitle(presentationData.strings.Conversation_RequestsToJoin(count), for: [])
if let params = self.params {
let _ = self.updateLayout(width: params.width, leftInset: params.leftInset, rightInset: params.rightInset, transition: .immediate, interfaceState: params.interfaceState)
}
} }
override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult { override func updateLayout(width: CGFloat, leftInset: CGFloat, rightInset: CGFloat, transition: ContainedViewLayoutTransition, interfaceState: ChatPresentationInterfaceState) -> LayoutResult {
self.params = Params(width: width, leftInset: leftInset, rightInset: rightInset, interfaceState: interfaceState)
if interfaceState.theme !== self.theme { if interfaceState.theme !== self.theme {
self.theme = interfaceState.theme self.theme = interfaceState.theme
@ -181,21 +212,15 @@ final class ChatInviteRequestsTitlePanelNode: ChatTitleAccessoryPanelNode {
let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0)) let closeButtonSize = self.closeButton.measure(CGSize(width: 100.0, height: 100.0))
transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: width - contentRightInset - closeButtonSize.width, y: floorToScreenPixels((panelHeight - closeButtonSize.height) / 2.0)), size: closeButtonSize)) transition.updateFrame(node: self.closeButton, frame: CGRect(origin: CGPoint(x: width - contentRightInset - closeButtonSize.width, y: floorToScreenPixels((panelHeight - closeButtonSize.height) / 2.0)), size: closeButtonSize))
if self.button == nil { self.buttonTitle.attributedText = NSAttributedString(string: interfaceState.strings.Conversation_RequestsToJoin(self.count), font: Font.regular(16.0), textColor: interfaceState.theme.rootController.navigationBar.accentTextColor)
let view = UIButton()
view.titleLabel?.font = Font.regular(16.0)
view.setTitleColor(interfaceState.theme.rootController.navigationBar.accentTextColor, for: [])
view.setTitleColor(interfaceState.theme.rootController.navigationBar.accentTextColor.withAlphaComponent(0.7), for: [.highlighted])
view.addTarget(self, action: #selector(self.buttonPressed), for: [.touchUpInside])
self.view.addSubview(view)
self.button = view
}
self.button?.setTitle(interfaceState.strings.Conversation_RequestsToJoin(self.count), for: []) transition.updateFrame(node: self.button, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: panelHeight)))
let maxInset = max(contentRightInset, leftInset) let titleSize = self.buttonTitle.updateLayout(CGSize(width: width - leftInset - 90.0 - contentRightInset, height: 100.0))
let buttonWidth = floor(width - maxInset * 2.0) var buttonTitleFrame = CGRect(origin: CGPoint(x: leftInset + floor((width - leftInset - titleSize.width) * 0.5), y: floor((panelHeight - titleSize.height) * 0.5)), size: titleSize)
self.button?.frame = CGRect(origin: CGPoint(x: maxInset, y: 0.0), size: CGSize(width: buttonWidth, height: panelHeight)) buttonTitleFrame.origin.x = max(buttonTitleFrame.minX, leftInset + 90.0)
transition.updatePosition(node: self.buttonTitle, position: buttonTitleFrame.origin)
self.buttonTitle.bounds = CGRect(origin: CGPoint(), size: buttonTitleFrame.size)
let initialPanelHeight = panelHeight let initialPanelHeight = panelHeight
transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel))) transition.updateFrame(node: self.separatorNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: width, height: UIScreenPixel)))