mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
[WIP] Inline forums
This commit is contained in:
parent
f2b0d699d9
commit
152fd01567
@ -4,7 +4,7 @@ import Postbox
|
||||
import Display
|
||||
import TelegramCore
|
||||
|
||||
public enum ChatListControllerLocation {
|
||||
public enum ChatListControllerLocation: Equatable {
|
||||
case chatList(groupId: EngineChatList.Group)
|
||||
case forum(peerId: PeerId)
|
||||
}
|
||||
|
@ -233,14 +233,21 @@ public final class AvatarNode: ASDisplayNode {
|
||||
} set(value) {
|
||||
let updateImage = !value.size.equalTo(super.frame.size)
|
||||
super.frame = value
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: value.size)
|
||||
|
||||
if updateImage {
|
||||
self.updateSize(size: value.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func updateSize(size: CGSize) {
|
||||
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
|
||||
self.editOverlayNode?.frame = self.imageNode.frame
|
||||
if updateImage && !self.displaySuspended {
|
||||
if !self.displaySuspended {
|
||||
self.setNeedsDisplay()
|
||||
self.editOverlayNode?.setNeedsDisplay()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public func playArchiveAnimation() {
|
||||
guard let theme = self.theme else {
|
||||
|
@ -52,7 +52,7 @@ private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBa
|
||||
} else {
|
||||
offset = 0.0
|
||||
}
|
||||
let _ = listNode.scrollToOffsetFromTop(offset)
|
||||
let _ = listNode.scrollToOffsetFromTop(offset, animated: true)
|
||||
return true
|
||||
} else if searchNode.expansionProgress == 1.0 {
|
||||
var sortItemNode: ListViewItemNode?
|
||||
@ -341,6 +341,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
private let moreBarButtonItem: UIBarButtonItem
|
||||
private var forumChannelTracker: ForumChannelTopics?
|
||||
|
||||
private var backNavigationItem: UIBarButtonItem?
|
||||
|
||||
private let selectAddMemberDisposable = MetaDisposable()
|
||||
private let addMemberDisposable = MetaDisposable()
|
||||
private let joinForumDisposable = MetaDisposable()
|
||||
@ -392,6 +394,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
|
||||
super.init(context: context, navigationBarPresentationData: NavigationBarPresentationData(presentationData: self.presentationData), mediaAccessoryPanelVisibility: .always, locationBroadcastPanelSource: .summary, groupCallPanelSource: groupCallPanelSource)
|
||||
|
||||
self.backNavigationItem = UIBarButtonItem(backButtonAppearanceWithTitle: self.presentationData.strings.Common_Back, target: self, action: #selector(self.navigationBackPressed))
|
||||
|
||||
self.tabBarItemContextActionType = .always
|
||||
self.automaticallyControlPresentationContextLayout = false
|
||||
|
||||
@ -742,6 +746,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
if strongSelf.navigationItem.leftBarButtonItem?.accessibilityLabel != leftBarButtonItem.accessibilityLabel {
|
||||
strongSelf.navigationItem.setLeftBarButton(leftBarButtonItem, animated: true)
|
||||
}
|
||||
} else if strongSelf.chatListDisplayNode.inlineStackContainerNode != nil {
|
||||
} else {
|
||||
let editItem: UIBarButtonItem
|
||||
if stateAndFilterId.state.editing {
|
||||
@ -1160,10 +1165,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
if force {
|
||||
strongSelf.tabContainerNode.cancelAnimations()
|
||||
strongSelf.chatListDisplayNode.inlineTabContainerNode.cancelAnimations()
|
||||
}
|
||||
strongSelf.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: strongSelf.isPremium, filtersLimit: tabContainerData.2, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition)
|
||||
strongSelf.chatListDisplayNode.inlineTabContainerNode.update(size: CGSize(width: layout.size.width, height: 40.0), sideInset: layout.safeInsets.left, filters: tabContainerData.0, selectedFilter: filter, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: false, transitionFraction: fraction, presentationData: strongSelf.presentationData, transition: transition)
|
||||
}
|
||||
self.reloadFilters()
|
||||
}
|
||||
@ -1277,7 +1280,9 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
editItem = self.moreBarButtonItem
|
||||
}
|
||||
}
|
||||
if case .chatList(.root) = self.location {
|
||||
if self.chatListDisplayNode.inlineStackContainerNode != nil {
|
||||
self.backNavigationItem?.title = self.presentationData.strings.Common_Back
|
||||
} else if case .chatList(.root) = self.location {
|
||||
self.navigationItem.leftBarButtonItem = editItem
|
||||
let rightBarButtonItem = UIBarButtonItem(image: PresentationResourcesRootController.navigationComposeIcon(self.presentationData.theme), style: .plain, target: self, action: #selector(self.composePressed))
|
||||
rightBarButtonItem.accessibilityLabel = self.presentationData.strings.VoiceOver_Navigation_Compose
|
||||
@ -1296,7 +1301,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
|
||||
if let layout = self.validLayout {
|
||||
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .immediate)
|
||||
self.chatListDisplayNode.inlineTabContainerNode.update(size: CGSize(width: layout.size.width, height: 40.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: false, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .immediate)
|
||||
}
|
||||
|
||||
if self.isNodeLoaded {
|
||||
@ -1383,11 +1387,21 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
|
||||
if case let .channel(channel) = peer, channel.flags.contains(.isForum), threadId == nil {
|
||||
strongSelf.chatListDisplayNode.clearHighlightAnimated(true)
|
||||
|
||||
if strongSelf.context.sharedContext.immediateExperimentalUISettings.inlineForums {
|
||||
strongSelf.chatListDisplayNode.setInlineChatList(location: .forum(peerId: channel.id))
|
||||
|
||||
if strongSelf.navigationItem.leftBarButtonItem !== strongSelf.backNavigationItem {
|
||||
strongSelf.navigationItem.setLeftBarButton(strongSelf.backNavigationItem, animated: true)
|
||||
}
|
||||
} else {
|
||||
strongSelf.context.sharedContext.navigateToForumChannel(context: strongSelf.context, peerId: channel.id, navigationController: navigationController)
|
||||
}
|
||||
} else {
|
||||
if let threadId = threadId {
|
||||
let _ = strongSelf.context.sharedContext.navigateToForumThread(context: strongSelf.context, peerId: peer.id, threadId: threadId, messageId: nil, navigationController: navigationController, activateInput: nil, keepStack: .never).start()
|
||||
strongSelf.chatListDisplayNode.containerNode.currentItemNode.clearHighlightAnimated(true)
|
||||
strongSelf.chatListDisplayNode.clearHighlightAnimated(true)
|
||||
} else {
|
||||
var navigationAnimationOptions: NavigationAnimationOptions = []
|
||||
var groupId: EngineChatList.Group = .root
|
||||
@ -1581,7 +1595,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
navigationController.filterController(strongSelf, animated: true)
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.containerNode.contentOffsetChanged = { [weak self] offset in
|
||||
self.chatListDisplayNode.contentOffsetChanged = { [weak self] offset in
|
||||
if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode, let validLayout = strongSelf.validLayout {
|
||||
var offset = offset
|
||||
if validLayout.inVoiceOver {
|
||||
@ -1591,7 +1605,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.containerNode.contentScrollingEnded = { [weak self] listView in
|
||||
self.chatListDisplayNode.contentScrollingEnded = { [weak self] listView in
|
||||
if let strongSelf = self, let searchContentNode = strongSelf.searchContentNode {
|
||||
return fixListNodeScrolling(listView, searchNode: searchContentNode)
|
||||
} else {
|
||||
@ -1885,20 +1899,12 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
strongSelf.selectTab(id: id)
|
||||
}
|
||||
}
|
||||
self.chatListDisplayNode.inlineTabContainerNode.tabSelected = { [weak self] id in
|
||||
self?.selectTab(id: id)
|
||||
}
|
||||
|
||||
self.tabContainerNode.tabRequestedDeletion = { [weak self] id in
|
||||
if case let .filter(id) = id {
|
||||
self?.askForFilterRemoval(id: id)
|
||||
}
|
||||
}
|
||||
self.chatListDisplayNode.inlineTabContainerNode.tabRequestedDeletion = { [weak self] id in
|
||||
if case let .filter(id) = id {
|
||||
self?.askForFilterRemoval(id: id)
|
||||
}
|
||||
}
|
||||
self.tabContainerNode.presentPremiumTip = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.present(UndoOverlayController(presentationData: strongSelf.presentationData, content: .universal(animation: "anim_reorder", scale: 0.05, colors: [:], title: nil, text: strongSelf.presentationData.strings.ChatListFolderSettings_SubscribeToMoveAll, customUndoText: strongSelf.presentationData.strings.ChatListFolderSettings_SubscribeToMoveAllAction), elevatedLayout: false, position: .top, animateInAsReplacement: false, action: { action in
|
||||
@ -2108,9 +2114,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self.tabContainerNode.contextGesture = { id, sourceNode, gesture, isDisabled in
|
||||
tabContextGesture(id, sourceNode, gesture, false, isDisabled)
|
||||
}
|
||||
self.chatListDisplayNode.inlineTabContainerNode.contextGesture = { id, sourceNode, gesture, isDisabled in
|
||||
tabContextGesture(id, sourceNode, gesture, true, isDisabled)
|
||||
}
|
||||
|
||||
if case .chatList(.root) = self.location {
|
||||
self.ready.set(.never())
|
||||
@ -2499,12 +2502,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
|
||||
transition.updateFrame(node: self.tabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: navigationBarHeight - self.additionalNavigationBarHeight - 46.0 + tabContainerOffset), size: CGSize(width: layout.size.width, height: 46.0)))
|
||||
self.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: self.isPremium, filtersLimit: self.tabContainerData?.2, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
if let tabContainerData = self.tabContainerData {
|
||||
self.chatListDisplayNode.inlineTabContainerNode.isHidden = !tabContainerData.1 || tabContainerData.0.count <= 1
|
||||
} else {
|
||||
self.chatListDisplayNode.inlineTabContainerNode.isHidden = true
|
||||
}
|
||||
self.chatListDisplayNode.inlineTabContainerNode.update(size: CGSize(width: layout.size.width, height: 40.0), sideInset: layout.safeInsets.left, filters: self.tabContainerData?.0 ?? [], selectedFilter: self.chatListDisplayNode.containerNode.currentItemFilter, isReordering: self.chatListDisplayNode.isReorderingFilters || (self.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !self.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: false, transitionFraction: self.chatListDisplayNode.containerNode.transitionFraction, presentationData: self.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
|
||||
self.chatListDisplayNode.containerLayoutUpdated(layout, navigationBarHeight: self.cleanNavigationHeight, visualNavigationHeight: navigationBarHeight, cleanNavigationBarHeight: self.cleanNavigationHeight, transition: transition)
|
||||
}
|
||||
@ -2559,22 +2556,8 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
}
|
||||
|
||||
@objc private func reorderingDonePressed() {
|
||||
guard let defaultFilters = self.tabContainerData else {
|
||||
return
|
||||
}
|
||||
let defaultFilterIds = defaultFilters.0.compactMap { entry -> Int32? in
|
||||
switch entry {
|
||||
case .all:
|
||||
return 0
|
||||
case let .filter(id, _, _):
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
var reorderedFilterIdsValue: [Int32]?
|
||||
if let reorderedFilterIds = self.chatListDisplayNode.inlineTabContainerNode.reorderedFilterIds, reorderedFilterIds != defaultFilterIds {
|
||||
reorderedFilterIdsValue = reorderedFilterIds
|
||||
} else if let reorderedFilterIds = self.tabContainerNode.reorderedFilterIds {
|
||||
if let reorderedFilterIds = self.tabContainerNode.reorderedFilterIds {
|
||||
reorderedFilterIdsValue = reorderedFilterIds
|
||||
}
|
||||
|
||||
@ -2620,6 +2603,17 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
self.moreBarButton.contextAction?(self.moreBarButton.containerNode, nil)
|
||||
}
|
||||
|
||||
@objc private func navigationBackPressed() {
|
||||
self.chatListDisplayNode.setInlineChatList(location: nil)
|
||||
|
||||
if self.navigationItem.leftBarButtonItem === self.backNavigationItem {
|
||||
let editItem = UIBarButtonItem(title: self.presentationData.strings.Common_Edit, style: .plain, target: self, action: #selector(self.editPressed))
|
||||
editItem.accessibilityLabel = self.presentationData.strings.Common_Edit
|
||||
|
||||
self.navigationItem.setLeftBarButton(editItem, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
public static func openMoreMenu(context: AccountContext, peerId: EnginePeer.Id, sourceController: ViewController, isViewingAsTopics: Bool, sourceView: UIView, gesture: ContextGesture?) {
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|
||||
|> deliverOnMainQueue).start(next: { peer in
|
||||
@ -2872,7 +2866,6 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController
|
||||
(strongSelf.parent as? TabBarController)?.updateLayout(transition: transition)
|
||||
} else {
|
||||
strongSelf.tabContainerNode.update(size: CGSize(width: layout.size.width, height: 46.0), sideInset: layout.safeInsets.left, filters: resolvedItems, selectedFilter: selectedEntryId, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing, canReorderAllChats: strongSelf.isPremium, filtersLimit: filtersLimit, transitionFraction: strongSelf.chatListDisplayNode.containerNode.transitionFraction, presentationData: strongSelf.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
strongSelf.chatListDisplayNode.inlineTabContainerNode.update(size: CGSize(width: layout.size.width, height: 40.0), sideInset: layout.safeInsets.left, filters: resolvedItems, selectedFilter: selectedEntryId, isReordering: strongSelf.chatListDisplayNode.isReorderingFilters || (strongSelf.chatListDisplayNode.containerNode.currentItemNode.currentState.editing && !strongSelf.chatListDisplayNode.didBeginSelectingChatsWhileEditing), isEditing: false, transitionFraction: strongSelf.chatListDisplayNode.containerNode.transitionFraction, presentationData: strongSelf.presentationData, transition: .animated(duration: 0.4, curve: .spring))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
||||
self.addSubnode(self.maskNode)
|
||||
}
|
||||
|
||||
func update(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, size: CGSize, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
func update(context: AccountContext, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, size: CGSize, isInlineMode: Bool, presentationData: PresentationData, transition: ContainedViewLayoutTransition) {
|
||||
if self.currentParams?.size != size || self.currentParams?.presentationData !== presentationData {
|
||||
self.currentParams = (size, presentationData)
|
||||
|
||||
@ -186,6 +186,7 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
||||
}, messageSelected: { _, _, _, _ in}, groupSelected: { _ in }, addContact: { _ in }, setPeerIdWithRevealedOptions: { _, _ in }, setItemPinned: { _, _ in }, setPeerMuted: { _, _ in }, setPeerThreadMuted: { _, _, _ in }, deletePeer: { _, _ in }, deletePeerThread: { _, _ in }, setPeerThreadStopped: { _, _, _ in }, setPeerThreadPinned: { _, _, _ in }, updatePeerGrouping: { _, _ in }, togglePeerMarkedUnread: { _, _ in}, toggleArchivedFolderHiddenByDefault: {}, toggleThreadsSelection: { _, _ in }, hidePsa: { _ in }, activateChatPreview: { _, _, _, gesture, _ in
|
||||
gesture?.cancel()
|
||||
}, present: { _ in }, openForumThread: { _, _ in })
|
||||
interaction.isInlineMode = isInlineMode
|
||||
|
||||
let items = (0 ..< 2).map { _ -> ChatListItem in
|
||||
let message = EngineMessage(
|
||||
@ -251,14 +252,25 @@ private final class ChatListShimmerNode: ASDisplayNode {
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
|
||||
if !isInlineMode {
|
||||
if itemNodes[sampleIndex].avatarNode.isHidden {
|
||||
context.fillEllipse(in: itemNodes[sampleIndex].avatarNode.frame.offsetBy(dx: 0.0, dy: currentY))
|
||||
}
|
||||
}
|
||||
|
||||
let titleFrame = itemNodes[sampleIndex].titleNode.frame.offsetBy(dx: 0.0, dy: currentY)
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX, y: floor(titleFrame.midY - fakeLabelPlaceholderHeight / 2.0)), width: 60.0)
|
||||
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX, y: currentY + itemHeight - floor(itemNodes[sampleIndex].titleNode.frame.midY - fakeLabelPlaceholderHeight / 2.0) - fakeLabelPlaceholderHeight), width: 60.0)
|
||||
let textFrame = itemNodes[sampleIndex].textNode.textNode.frame.offsetBy(dx: 0.0, dy: currentY)
|
||||
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 120.0)
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: titleFrame.minX + 120.0 + 10.0, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 60.0)
|
||||
if isInlineMode {
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(x: textFrame.minX, y: titleFrame.minY + 2.0), size: CGSize(width: 16.0, height: 16.0)))
|
||||
}
|
||||
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: textFrame.minX, y: currentY + itemHeight - floor(itemNodes[sampleIndex].titleNode.frame.midY - fakeLabelPlaceholderHeight / 2.0) - fakeLabelPlaceholderHeight), width: 60.0)
|
||||
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: textFrame.minX, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 120.0)
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: textFrame.minX + 120.0 + 10.0, y: currentY + floor((itemHeight - fakeLabelPlaceholderHeight) / 2.0)), width: 60.0)
|
||||
|
||||
let dateFrame = itemNodes[sampleIndex].dateNode.frame.offsetBy(dx: 0.0, dy: currentY)
|
||||
fillLabelPlaceholderRect(origin: CGPoint(x: dateFrame.maxX - 30.0, y: dateFrame.minY), width: 30.0)
|
||||
@ -288,6 +300,7 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
||||
private let becameEmpty: (ChatListFilter?) -> Void
|
||||
private let emptyAction: (ChatListFilter?) -> Void
|
||||
private let secondaryEmptyAction: () -> Void
|
||||
private let isInlineMode: Bool
|
||||
|
||||
private var floatingHeaderOffset: CGFloat?
|
||||
|
||||
@ -296,9 +309,9 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
||||
private var shimmerNodeOffset: CGFloat = 0.0
|
||||
let listNode: ChatListNode
|
||||
|
||||
private var validLayout: (CGSize, UIEdgeInsets, CGFloat)?
|
||||
private var validLayout: (CGSize, UIEdgeInsets, CGFloat, ChatListControllerLocation?)?
|
||||
|
||||
init(context: AccountContext, location: ChatListControllerLocation, filter: ChatListFilter?, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, becameEmpty: @escaping (ChatListFilter?) -> Void, emptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) {
|
||||
init(context: AccountContext, location: ChatListControllerLocation, filter: ChatListFilter?, previewing: Bool, isInlineMode: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, becameEmpty: @escaping (ChatListFilter?) -> Void, emptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.animationCache = animationCache
|
||||
self.animationRenderer = animationRenderer
|
||||
@ -306,8 +319,9 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
||||
self.becameEmpty = becameEmpty
|
||||
self.emptyAction = emptyAction
|
||||
self.secondaryEmptyAction = secondaryEmptyAction
|
||||
self.isInlineMode = isInlineMode
|
||||
|
||||
self.listNode = ChatListNode(context: context, location: location, chatListFilter: filter, previewing: previewing, fillPreloadItems: controlsHistoryPreload, mode: .chatList, theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: animationCache, animationRenderer: animationRenderer, disableAnimations: true)
|
||||
self.listNode = ChatListNode(context: context, location: location, chatListFilter: filter, previewing: previewing, fillPreloadItems: controlsHistoryPreload, mode: .chatList, theme: presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: animationCache, animationRenderer: animationRenderer, disableAnimations: true, isInlineMode: isInlineMode)
|
||||
|
||||
super.init()
|
||||
|
||||
@ -361,7 +375,7 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
||||
})
|
||||
strongSelf.emptyNode = emptyNode
|
||||
strongSelf.addSubnode(emptyNode)
|
||||
if let (size, insets, _) = strongSelf.validLayout {
|
||||
if let (size, insets, _, _) = strongSelf.validLayout {
|
||||
let emptyNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
||||
emptyNode.frame = emptyNodeFrame
|
||||
emptyNode.updateLayout(size: emptyNodeFrame.size, transition: .immediate)
|
||||
@ -387,7 +401,7 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
||||
let emptyShimmerEffectNode = ChatListShimmerNode()
|
||||
strongSelf.emptyShimmerEffectNode = emptyShimmerEffectNode
|
||||
strongSelf.insertSubnode(emptyShimmerEffectNode, belowSubnode: strongSelf.listNode)
|
||||
if let (size, insets, _) = strongSelf.validLayout, let offset = strongSelf.floatingHeaderOffset {
|
||||
if let (size, insets, _, _) = strongSelf.validLayout, let offset = strongSelf.floatingHeaderOffset {
|
||||
strongSelf.layoutEmptyShimmerEffectNode(node: emptyShimmerEffectNode, size: size, insets: insets, verticalOffset: offset + strongSelf.shimmerNodeOffset, transition: .immediate)
|
||||
}
|
||||
}
|
||||
@ -407,14 +421,14 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
||||
return
|
||||
}
|
||||
strongSelf.floatingHeaderOffset = offset
|
||||
if let (size, insets, _) = strongSelf.validLayout, let emptyShimmerEffectNode = strongSelf.emptyShimmerEffectNode {
|
||||
if let (size, insets, _, _) = strongSelf.validLayout, let emptyShimmerEffectNode = strongSelf.emptyShimmerEffectNode {
|
||||
strongSelf.layoutEmptyShimmerEffectNode(node: emptyShimmerEffectNode, size: size, insets: insets, verticalOffset: offset + strongSelf.shimmerNodeOffset, transition: transition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func layoutEmptyShimmerEffectNode(node: ChatListShimmerNode, size: CGSize, insets: UIEdgeInsets, verticalOffset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
node.update(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, size: size, presentationData: self.presentationData, transition: .immediate)
|
||||
node.update(context: self.context, animationCache: self.animationCache, animationRenderer: self.animationRenderer, size: size, isInlineMode: self.isInlineMode, presentationData: self.presentationData, transition: .immediate)
|
||||
transition.updateFrameAdditive(node: node, frame: CGRect(origin: CGPoint(x: 0.0, y: verticalOffset), size: size))
|
||||
}
|
||||
|
||||
@ -430,15 +444,14 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
||||
self.emptyNode?.updateThemeAndStrings(theme: presentationData.theme, strings: presentationData.strings)
|
||||
}
|
||||
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, visualNavigationHeight: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets, visualNavigationHeight)
|
||||
func updateLayout(size: CGSize, insets: UIEdgeInsets, visualNavigationHeight: CGFloat, inlineNavigationLocation: ChatListControllerLocation?, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (size, insets, visualNavigationHeight, inlineNavigationLocation)
|
||||
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: size, insets: insets, duration: duration, curve: curve)
|
||||
|
||||
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: size))
|
||||
self.listNode.visualInsets = UIEdgeInsets(top: visualNavigationHeight, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
self.listNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
|
||||
self.listNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: visualNavigationHeight, inlineNavigationLocation: inlineNavigationLocation)
|
||||
|
||||
if let emptyNode = self.emptyNode {
|
||||
let emptyNodeFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: size.width, height: size.height - insets.top - insets.bottom))
|
||||
@ -450,8 +463,9 @@ private final class ChatListContainerItemNode: ASDisplayNode {
|
||||
|
||||
final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
private let context: AccountContext
|
||||
private let location: ChatListControllerLocation
|
||||
let location: ChatListControllerLocation
|
||||
private let previewing: Bool
|
||||
private let isInlineMode: Bool
|
||||
private let controlsHistoryPreload: Bool
|
||||
private let filterBecameEmpty: (ChatListFilter?) -> Void
|
||||
private let filterEmptyAction: (ChatListFilter?) -> Void
|
||||
@ -473,12 +487,14 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
private(set) var transitionFraction: CGFloat = 0.0
|
||||
private var transitionFractionOffset: CGFloat = 0.0
|
||||
private var disableItemNodeOperationsWhileAnimating: Bool = false
|
||||
private var validLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, insets: UIEdgeInsets, isReorderingFilters: Bool, isEditing: Bool)?
|
||||
private var validLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, insets: UIEdgeInsets, isReorderingFilters: Bool, isEditing: Bool, inlineNavigationLocation: ChatListControllerLocation?)?
|
||||
|
||||
private var enableAdjacentFilterLoading: Bool = false
|
||||
|
||||
private var panRecognizer: InteractiveTransitionGestureRecognizer?
|
||||
|
||||
let leftSeparatorLayer: SimpleLayer
|
||||
|
||||
private let _ready = Promise<Bool>()
|
||||
var ready: Signal<Bool, NoError> {
|
||||
return _ready.get()
|
||||
@ -624,17 +640,18 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
var peerSelected: ((EnginePeer, Int64?, Bool, Bool, ChatListNodeEntryPromoInfo?) -> Void)?
|
||||
var groupSelected: ((EngineChatList.Group) -> Void)?
|
||||
var updatePeerGrouping: ((EnginePeer.Id, Bool) -> Void)?
|
||||
var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
|
||||
var contentScrollingEnded: ((ListView) -> Bool)?
|
||||
fileprivate var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
|
||||
fileprivate var contentScrollingEnded: ((ListView) -> Bool)?
|
||||
var activateChatPreview: ((ChatListItem, Int64?, ASDisplayNode, ContextGesture?, CGPoint?) -> Void)?
|
||||
var addedVisibleChatsWithPeerIds: (([EnginePeer.Id]) -> Void)?
|
||||
var didBeginSelectingChats: (() -> Void)?
|
||||
var displayFilterLimit: (() -> Void)?
|
||||
|
||||
init(context: AccountContext, location: ChatListControllerLocation, previewing: Bool, controlsHistoryPreload: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) {
|
||||
init(context: AccountContext, location: ChatListControllerLocation, previewing: Bool, controlsHistoryPreload: Bool, isInlineMode: Bool, presentationData: PresentationData, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, filterBecameEmpty: @escaping (ChatListFilter?) -> Void, filterEmptyAction: @escaping (ChatListFilter?) -> Void, secondaryEmptyAction: @escaping () -> Void) {
|
||||
self.context = context
|
||||
self.location = location
|
||||
self.previewing = previewing
|
||||
self.isInlineMode = isInlineMode
|
||||
self.filterBecameEmpty = filterBecameEmpty
|
||||
self.filterEmptyAction = filterEmptyAction
|
||||
self.secondaryEmptyAction = secondaryEmptyAction
|
||||
@ -646,9 +663,15 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
|
||||
self.selectedId = .all
|
||||
|
||||
self.leftSeparatorLayer = SimpleLayer()
|
||||
self.leftSeparatorLayer.isHidden = true
|
||||
self.leftSeparatorLayer.backgroundColor = presentationData.theme.rootController.navigationBar.separatorColor.cgColor
|
||||
|
||||
super.init()
|
||||
|
||||
let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: nil, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in
|
||||
self.backgroundColor = presentationData.theme.chatList.backgroundColor
|
||||
|
||||
let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: nil, previewing: self.previewing, isInlineMode: self.isInlineMode, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in
|
||||
self?.filterBecameEmpty(filter)
|
||||
}, emptyAction: { [weak self] filter in
|
||||
self?.filterEmptyAction(filter)
|
||||
@ -685,6 +708,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
panRecognizer.cancelsTouchesInView = true
|
||||
self.panRecognizer = panRecognizer
|
||||
self.view.addGestureRecognizer(panRecognizer)
|
||||
|
||||
self.view.layer.addSublayer(self.leftSeparatorLayer)
|
||||
}
|
||||
|
||||
deinit {
|
||||
@ -714,7 +739,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
self.onFilterSwitch?()
|
||||
|
||||
self.transitionFractionOffset = 0.0
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing) = self.validLayout, let itemNode = self.itemNodes[self.selectedId] {
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation) = self.validLayout, let itemNode = self.itemNodes[self.selectedId] {
|
||||
for (id, itemNode) in self.itemNodes {
|
||||
if id != selectedId {
|
||||
itemNode.emptyNode?.restartAnimation()
|
||||
@ -727,13 +752,13 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
for (_, itemNode) in self.itemNodes {
|
||||
itemNode.layer.removeAllAnimations()
|
||||
}
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, transition: .immediate)
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, transition: .immediate)
|
||||
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, .immediate, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
case .changed:
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing) = self.validLayout, let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation) = self.validLayout, let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
var transitionFraction = translation.x / layout.size.width
|
||||
|
||||
@ -770,11 +795,11 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
}
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, transition: .immediate)
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, transition: .immediate)
|
||||
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, .immediate, false)
|
||||
}
|
||||
case .cancelled, .ended:
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing) = self.validLayout, let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation) = self.validLayout, let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
|
||||
let translation = recognizer.translation(in: self.view)
|
||||
let velocity = recognizer.velocity(in: self.view)
|
||||
var directionIsToRight: Bool?
|
||||
@ -813,12 +838,12 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
self.transitionFraction = 0.0
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.45, curve: .spring)
|
||||
self.disableItemNodeOperationsWhileAnimating = true
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, transition: transition)
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, transition: transition)
|
||||
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false)
|
||||
DispatchQueue.main.async {
|
||||
self.disableItemNodeOperationsWhileAnimating = false
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing) = self.validLayout {
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, transition: .immediate)
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation) = self.validLayout {
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -827,9 +852,17 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func fixContentOffset(offset: CGFloat) {
|
||||
self.currentItemNode.fixContentOffset(offset: offset)
|
||||
}
|
||||
|
||||
func updatePresentationData(_ presentationData: PresentationData) {
|
||||
self.presentationData = presentationData
|
||||
|
||||
self.backgroundColor = self.presentationData.theme.chatList.backgroundColor
|
||||
|
||||
self.leftSeparatorLayer.backgroundColor = self.presentationData.theme.rootController.navigationBar.separatorColor.cgColor
|
||||
|
||||
for (_, itemNode) in self.itemNodes {
|
||||
itemNode.updatePresentationData(presentationData)
|
||||
}
|
||||
@ -884,8 +917,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
}
|
||||
strongSelf.availableFilters = availableFilters
|
||||
strongSelf.filtersLimit = limit
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing) = strongSelf.validLayout {
|
||||
strongSelf.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, transition: .immediate)
|
||||
if let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation) = strongSelf.validLayout {
|
||||
strongSelf.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, transition: .immediate)
|
||||
}
|
||||
}
|
||||
if !availableFilters.contains(where: { $0.id == self.selectedId }) {
|
||||
@ -902,8 +935,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
if value != self.enableAdjacentFilterLoading {
|
||||
self.enableAdjacentFilterLoading = value
|
||||
|
||||
if self.enableAdjacentFilterLoading, let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing) = self.validLayout {
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, transition: .immediate)
|
||||
if self.enableAdjacentFilterLoading, let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation) = self.validLayout {
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, transition: .immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -912,7 +945,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
self.onFilterSwitch?()
|
||||
if id != self.selectedId, let index = self.availableFilters.firstIndex(where: { $0.id == id }) {
|
||||
if let itemNode = self.itemNodes[id] {
|
||||
guard let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing) = self.validLayout else {
|
||||
guard let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation) = self.validLayout else {
|
||||
return
|
||||
}
|
||||
self.selectedId = id
|
||||
@ -921,12 +954,12 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
}
|
||||
self.applyItemNodeAsCurrent(id: id, itemNode: itemNode)
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.35, curve: .spring)
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, transition: transition)
|
||||
self.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, transition: transition)
|
||||
self.currentItemFilterUpdated?(self.currentItemFilter, self.transitionFraction, transition, false)
|
||||
itemNode.emptyNode?.restartAnimation()
|
||||
completion?()
|
||||
} else if self.pendingItemNode == nil {
|
||||
let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: self.availableFilters[index].filter, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in
|
||||
let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: self.availableFilters[index].filter, previewing: self.previewing, isInlineMode: self.isInlineMode, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in
|
||||
self?.filterBecameEmpty(filter)
|
||||
}, emptyAction: { [weak self] filter in
|
||||
self?.filterEmptyAction(filter)
|
||||
@ -951,7 +984,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
|
||||
strongSelf.pendingItemNode = nil
|
||||
|
||||
guard let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing) = strongSelf.validLayout else {
|
||||
guard let (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation) = strongSelf.validLayout else {
|
||||
strongSelf.itemNodes[id] = itemNode
|
||||
strongSelf.addSubnode(itemNode)
|
||||
|
||||
@ -1000,7 +1033,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
|
||||
transition.animatePositionAdditive(node: itemNode, offset: CGPoint(x: -offset, y: 0.0))
|
||||
|
||||
itemNode.updateLayout(size: layout.size, insets: insets, visualNavigationHeight: visualNavigationHeight, transition: .immediate)
|
||||
itemNode.updateLayout(size: layout.size, insets: insets, visualNavigationHeight: visualNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, transition: .immediate)
|
||||
|
||||
strongSelf.selectedId = id
|
||||
if let currentItemNode = strongSelf.currentItemNodeValue {
|
||||
@ -1008,7 +1041,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
}
|
||||
strongSelf.applyItemNodeAsCurrent(id: id, itemNode: itemNode)
|
||||
|
||||
strongSelf.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, transition: .immediate)
|
||||
strongSelf.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: isReorderingFilters, isEditing: isEditing, inlineNavigationLocation: inlineNavigationLocation, transition: .immediate)
|
||||
|
||||
strongSelf.currentItemFilterUpdated?(strongSelf.currentItemFilter, strongSelf.transitionFraction, transition, false)
|
||||
}
|
||||
@ -1019,8 +1052,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
func update(layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, insets: UIEdgeInsets, isReorderingFilters: Bool, isEditing: Bool, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing)
|
||||
func update(layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat, insets: UIEdgeInsets, isReorderingFilters: Bool, isEditing: Bool, inlineNavigationLocation: ChatListControllerLocation?, transition: ContainedViewLayoutTransition) {
|
||||
self.validLayout = (layout, navigationBarHeight, visualNavigationHeight, cleanNavigationBarHeight, insets, isReorderingFilters, isEditing, inlineNavigationLocation)
|
||||
|
||||
self._validLayoutReady.set(.single(true))
|
||||
|
||||
@ -1029,6 +1062,8 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
|
||||
self.panRecognizer?.isEnabled = !isEditing
|
||||
|
||||
transition.updateFrame(layer: self.leftSeparatorLayer, frame: CGRect(origin: CGPoint(x: -UIScreenPixel, y: 0.0), size: CGSize(width: UIScreenPixel, height: layout.size.height)))
|
||||
|
||||
if let selectedIndex = self.availableFilters.firstIndex(where: { $0.id == self.selectedId }) {
|
||||
var validNodeIds: [ChatListFilterTabEntryId] = []
|
||||
for i in max(0, selectedIndex - 1) ... min(self.availableFilters.count - 1, selectedIndex + 1) {
|
||||
@ -1036,7 +1071,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
validNodeIds.append(id)
|
||||
|
||||
if self.itemNodes[id] == nil && self.enableAdjacentFilterLoading && !self.disableItemNodeOperationsWhileAnimating {
|
||||
let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: self.availableFilters[i].filter, previewing: self.previewing, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in
|
||||
let itemNode = ChatListContainerItemNode(context: self.context, location: self.location, filter: self.availableFilters[i].filter, previewing: self.previewing, isInlineMode: self.isInlineMode, controlsHistoryPreload: self.controlsHistoryPreload, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, becameEmpty: { [weak self] filter in
|
||||
self?.filterBecameEmpty(filter)
|
||||
}, emptyAction: { [weak self] filter in
|
||||
self?.filterEmptyAction(filter)
|
||||
@ -1073,7 +1108,7 @@ final class ChatListContainerNode: ASDisplayNode, UIGestureRecognizerDelegate {
|
||||
nodeTransition.updateFrame(node: itemNode, frame: itemFrame, completion: { _ in
|
||||
})
|
||||
|
||||
itemNode.updateLayout(size: layout.size, insets: insets, visualNavigationHeight: visualNavigationHeight, transition: nodeTransition)
|
||||
itemNode.updateLayout(size: layout.size, insets: insets, visualNavigationHeight: visualNavigationHeight, inlineNavigationLocation: inlineNavigationLocation, transition: nodeTransition)
|
||||
|
||||
if wasAdded, case .animated = transition {
|
||||
animateSlidingIds.append(id)
|
||||
@ -1106,7 +1141,7 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
private let animationRenderer: MultiAnimationRenderer
|
||||
|
||||
let containerNode: ChatListContainerNode
|
||||
let inlineTabContainerNode: ChatListFilterTabInlineContainerNode
|
||||
private(set) var inlineStackContainerNode: ChatListContainerNode?
|
||||
private var tapRecognizer: UITapGestureRecognizer?
|
||||
var navigationBar: NavigationBar?
|
||||
weak var controller: ChatListControllerImpl?
|
||||
@ -1121,7 +1156,10 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
var didBeginSelectingChatsWhileEditing: Bool = false
|
||||
var isEditing: Bool = false
|
||||
|
||||
private var containerLayout: (ContainerViewLayout, CGFloat, CGFloat, CGFloat)?
|
||||
private var containerLayout: (layout: ContainerViewLayout, navigationBarHeight: CGFloat, visualNavigationHeight: CGFloat, cleanNavigationBarHeight: CGFloat)?
|
||||
|
||||
var contentOffsetChanged: ((ListViewVisibleContentOffset) -> Void)?
|
||||
var contentScrollingEnded: ((ListView) -> Bool)?
|
||||
|
||||
var requestDeactivateSearch: (() -> Void)?
|
||||
var requestOpenPeerFromSearch: ((EnginePeer, Int64?, Bool) -> Void)?
|
||||
@ -1146,7 +1184,7 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
var filterBecameEmpty: ((ChatListFilter?) -> Void)?
|
||||
var filterEmptyAction: ((ChatListFilter?) -> Void)?
|
||||
var secondaryEmptyAction: (() -> Void)?
|
||||
self.containerNode = ChatListContainerNode(context: context, location: location, previewing: previewing, controlsHistoryPreload: controlsHistoryPreload, presentationData: presentationData, animationCache: animationCache, animationRenderer: animationRenderer, filterBecameEmpty: { filter in
|
||||
self.containerNode = ChatListContainerNode(context: context, location: location, previewing: previewing, controlsHistoryPreload: controlsHistoryPreload, isInlineMode: false, presentationData: presentationData, animationCache: animationCache, animationRenderer: animationRenderer, filterBecameEmpty: { filter in
|
||||
filterBecameEmpty?(filter)
|
||||
}, filterEmptyAction: { filter in
|
||||
filterEmptyAction?(filter)
|
||||
@ -1154,8 +1192,6 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
secondaryEmptyAction?()
|
||||
})
|
||||
|
||||
self.inlineTabContainerNode = ChatListFilterTabInlineContainerNode()
|
||||
|
||||
self.controller = controller
|
||||
|
||||
super.init()
|
||||
@ -1167,7 +1203,13 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
self.backgroundColor = presentationData.theme.chatList.backgroundColor
|
||||
|
||||
self.addSubnode(self.containerNode)
|
||||
self.addSubnode(self.inlineTabContainerNode)
|
||||
|
||||
self.containerNode.contentOffsetChanged = { [weak self] offset in
|
||||
self?.contentOffsetChanged(offset: offset, isPrimary: true)
|
||||
}
|
||||
self.containerNode.contentScrollingEnded = { [weak self] listView in
|
||||
return self?.contentScrollingEnded(listView: listView, isPrimary: true) ?? false
|
||||
}
|
||||
|
||||
self.addSubnode(self.debugListView)
|
||||
|
||||
@ -1292,9 +1334,42 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
self.controller?.presentationContext.containerLayoutUpdated(childrenLayout, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: layout.size))
|
||||
self.containerNode.update(layout: layout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: insets, isReorderingFilters: self.isReorderingFilters, isEditing: self.isEditing, transition: transition)
|
||||
var mainNavigationBarHeight = navigationBarHeight
|
||||
var cleanMainNavigationBarHeight = cleanNavigationBarHeight
|
||||
var mainInsets = insets
|
||||
if self.inlineStackContainerNode != nil {
|
||||
mainNavigationBarHeight = visualNavigationHeight
|
||||
cleanMainNavigationBarHeight = visualNavigationHeight
|
||||
mainInsets.top = visualNavigationHeight
|
||||
}
|
||||
self.containerNode.update(layout: layout, navigationBarHeight: mainNavigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanMainNavigationBarHeight, insets: mainInsets, isReorderingFilters: self.isReorderingFilters, isEditing: self.isEditing, inlineNavigationLocation: self.inlineStackContainerNode?.location, transition: transition)
|
||||
|
||||
transition.updateFrame(node: self.inlineTabContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - layout.intrinsicInsets.bottom - 8.0 - 40.0), size: CGSize(width: layout.size.width, height: 40.0)))
|
||||
if let inlineStackContainerNode = self.inlineStackContainerNode {
|
||||
var inlineStackContainerNodeTransition = transition
|
||||
var animateIn = false
|
||||
if inlineStackContainerNode.supernode == nil {
|
||||
self.insertSubnode(inlineStackContainerNode, aboveSubnode: self.containerNode)
|
||||
inlineStackContainerNodeTransition = .immediate
|
||||
animateIn = true
|
||||
}
|
||||
|
||||
let inlineSideInset: CGFloat = layout.safeInsets.left + 72.0
|
||||
inlineStackContainerNodeTransition.updateFrame(node: inlineStackContainerNode, frame: CGRect(origin: CGPoint(x: inlineSideInset, y: 0.0), size: layout.size))
|
||||
var inlineLayout = layout
|
||||
inlineLayout.size.width -= inlineSideInset
|
||||
inlineLayout.safeInsets.left = 0.0
|
||||
inlineLayout.intrinsicInsets.left = 0.0
|
||||
inlineLayout.additionalInsets.left = 0.0
|
||||
|
||||
var inlineInsets = insets
|
||||
inlineInsets.left = 0.0
|
||||
|
||||
inlineStackContainerNode.update(layout: inlineLayout, navigationBarHeight: navigationBarHeight, visualNavigationHeight: visualNavigationHeight, cleanNavigationBarHeight: cleanNavigationBarHeight, insets: inlineInsets, isReorderingFilters: self.isReorderingFilters, isEditing: self.isEditing, inlineNavigationLocation: nil, transition: inlineStackContainerNodeTransition)
|
||||
|
||||
if animateIn {
|
||||
transition.animatePosition(node: inlineStackContainerNode, from: CGPoint(x: inlineStackContainerNode.position.x + inlineStackContainerNode.bounds.width + UIScreenPixel, y: inlineStackContainerNode.position.y))
|
||||
}
|
||||
}
|
||||
|
||||
self.tapRecognizer?.isEnabled = self.isReorderingFilters
|
||||
|
||||
@ -1372,6 +1447,150 @@ final class ChatListControllerNode: ASDisplayNode {
|
||||
}
|
||||
}
|
||||
|
||||
func clearHighlightAnimated(_ animated: Bool) {
|
||||
self.containerNode.currentItemNode.clearHighlightAnimated(true)
|
||||
self.inlineStackContainerNode?.currentItemNode.clearHighlightAnimated(true)
|
||||
}
|
||||
|
||||
private var contentOffsetSyncLockedIn: Bool = false
|
||||
|
||||
private func contentOffsetChanged(offset: ListViewVisibleContentOffset, isPrimary: Bool) {
|
||||
guard let inlineStackContainerNode = self.inlineStackContainerNode else {
|
||||
self.contentOffsetChanged?(offset)
|
||||
return
|
||||
}
|
||||
guard let containerLayout = self.containerLayout else {
|
||||
return
|
||||
}
|
||||
|
||||
if !isPrimary {
|
||||
self.contentOffsetChanged?(offset)
|
||||
if "".isEmpty {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if "".isEmpty {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let targetNode: ChatListContainerNode
|
||||
if isPrimary {
|
||||
targetNode = inlineStackContainerNode
|
||||
} else {
|
||||
targetNode = self.containerNode
|
||||
}
|
||||
|
||||
switch offset {
|
||||
case let .known(value) where (value <= containerLayout.navigationBarHeight - 76.0 - 46.0 - 8.0 + UIScreenPixel || self.contentOffsetSyncLockedIn):
|
||||
if case let .known(otherValue) = targetNode.currentItemNode.visibleContentOffset(), abs(otherValue - value) <= UIScreenPixel {
|
||||
self.contentOffsetSyncLockedIn = true
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
switch offset {
|
||||
case let .known(value) where self.contentOffsetSyncLockedIn:
|
||||
var targetValue = value
|
||||
if targetValue > containerLayout.navigationBarHeight - 76.0 - 46.0 - 8.0 {
|
||||
targetValue = containerLayout.navigationBarHeight - 76.0 - 46.0 - 8.0
|
||||
}
|
||||
|
||||
targetNode.fixContentOffset(offset: targetValue)
|
||||
|
||||
self.contentOffsetChanged?(offset)
|
||||
default:
|
||||
if !isPrimary {
|
||||
self.contentOffsetChanged?(offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func contentScrollingEnded(listView: ListView, isPrimary: Bool) -> Bool {
|
||||
guard let inlineStackContainerNode = self.inlineStackContainerNode else {
|
||||
return self.contentScrollingEnded?(listView) ?? false
|
||||
}
|
||||
|
||||
self.contentOffsetSyncLockedIn = false
|
||||
|
||||
if isPrimary {
|
||||
return false
|
||||
}
|
||||
|
||||
let _ = inlineStackContainerNode
|
||||
|
||||
return self.contentScrollingEnded?(listView) ?? false
|
||||
}
|
||||
|
||||
func setInlineChatList(location: ChatListControllerLocation?) {
|
||||
if let location = location {
|
||||
if self.inlineStackContainerNode?.location != location {
|
||||
let inlineStackContainerNode = ChatListContainerNode(context: self.context, location: location, previewing: false, controlsHistoryPreload: false, isInlineMode: true, presentationData: self.presentationData, animationCache: self.animationCache, animationRenderer: self.animationRenderer, filterBecameEmpty: { _ in }, filterEmptyAction: { _ in }, secondaryEmptyAction: {})
|
||||
|
||||
inlineStackContainerNode.leftSeparatorLayer.isHidden = false
|
||||
|
||||
inlineStackContainerNode.presentAlert = self.containerNode.presentAlert
|
||||
inlineStackContainerNode.present = self.containerNode.present
|
||||
inlineStackContainerNode.push = self.containerNode.push
|
||||
inlineStackContainerNode.deletePeerChat = self.containerNode.deletePeerChat
|
||||
inlineStackContainerNode.deletePeerThread = self.containerNode.deletePeerThread
|
||||
inlineStackContainerNode.setPeerThreadStopped = self.containerNode.setPeerThreadStopped
|
||||
inlineStackContainerNode.setPeerThreadPinned = self.containerNode.setPeerThreadPinned
|
||||
inlineStackContainerNode.peerSelected = self.containerNode.peerSelected
|
||||
inlineStackContainerNode.groupSelected = self.containerNode.groupSelected
|
||||
inlineStackContainerNode.updatePeerGrouping = self.containerNode.updatePeerGrouping
|
||||
|
||||
inlineStackContainerNode.contentOffsetChanged = { [weak self] offset in
|
||||
self?.contentOffsetChanged(offset: offset, isPrimary: false)
|
||||
}
|
||||
inlineStackContainerNode.contentScrollingEnded = { [weak self] listView in
|
||||
return self?.contentScrollingEnded(listView: listView, isPrimary: false) ?? false
|
||||
}
|
||||
|
||||
inlineStackContainerNode.activateChatPreview = self.containerNode.activateChatPreview
|
||||
inlineStackContainerNode.addedVisibleChatsWithPeerIds = self.containerNode.addedVisibleChatsWithPeerIds
|
||||
inlineStackContainerNode.didBeginSelectingChats = nil
|
||||
inlineStackContainerNode.displayFilterLimit = nil
|
||||
|
||||
let previousInlineStackContainerNode = self.inlineStackContainerNode
|
||||
|
||||
self.inlineStackContainerNode = inlineStackContainerNode
|
||||
|
||||
if let containerLayout = self.containerLayout {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
|
||||
|
||||
if let previousInlineStackContainerNode {
|
||||
transition.updatePosition(node: previousInlineStackContainerNode, position: CGPoint(x: previousInlineStackContainerNode.position.x + previousInlineStackContainerNode.bounds.width + UIScreenPixel, y: previousInlineStackContainerNode.position.y), completion: { [weak previousInlineStackContainerNode] _ in
|
||||
previousInlineStackContainerNode?.removeFromSupernode()
|
||||
})
|
||||
}
|
||||
|
||||
self.containerLayoutUpdated(containerLayout.layout, navigationBarHeight: containerLayout.navigationBarHeight, visualNavigationHeight: containerLayout.visualNavigationHeight, cleanNavigationBarHeight: containerLayout.cleanNavigationBarHeight, transition: transition)
|
||||
} else {
|
||||
previousInlineStackContainerNode?.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if let inlineStackContainerNode = self.inlineStackContainerNode {
|
||||
self.inlineStackContainerNode = nil
|
||||
|
||||
self.containerNode.contentScrollingEnded = self.contentScrollingEnded
|
||||
|
||||
if let containerLayout = self.containerLayout {
|
||||
let transition: ContainedViewLayoutTransition = .animated(duration: 0.4, curve: .spring)
|
||||
self.containerLayoutUpdated(containerLayout.layout, navigationBarHeight: containerLayout.navigationBarHeight, visualNavigationHeight: containerLayout.visualNavigationHeight, cleanNavigationBarHeight: containerLayout.cleanNavigationBarHeight, transition: transition)
|
||||
|
||||
transition.updatePosition(node: inlineStackContainerNode, position: CGPoint(x: inlineStackContainerNode.position.x + inlineStackContainerNode.bounds.width + UIScreenPixel, y: inlineStackContainerNode.position.y), completion: { [weak inlineStackContainerNode] _ in
|
||||
inlineStackContainerNode?.removeFromSupernode()
|
||||
})
|
||||
} else {
|
||||
inlineStackContainerNode.removeFromSupernode()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func playArchiveAnimation() {
|
||||
self.containerNode.playArchiveAnimation()
|
||||
}
|
||||
|
@ -775,6 +775,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
|
||||
let contextContainer: ContextControllerSourceNode
|
||||
let mainContentContainerNode: ASDisplayNode
|
||||
|
||||
let avatarNode: AvatarNode
|
||||
var avatarIconView: ComponentHostView<Empty>?
|
||||
@ -784,6 +785,8 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
private let playbackStartDisposable = MetaDisposable()
|
||||
private var videoLoopCount = 0
|
||||
|
||||
private var inlineNavigationMarkLayer: SimpleLayer?
|
||||
|
||||
let titleNode: TextNode
|
||||
let authorNode: AuthorNode
|
||||
private var compoundHighlightingNode: LinkHighlightingNode?
|
||||
@ -1026,6 +1029,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
self.contextContainer = ContextControllerSourceNode()
|
||||
|
||||
self.mainContentContainerNode = ASDisplayNode()
|
||||
self.mainContentContainerNode.clipsToBounds = true
|
||||
|
||||
self.measureNode = TextNode()
|
||||
|
||||
self.titleNode = TextNode()
|
||||
@ -1073,19 +1079,20 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.addSubnode(self.separatorNode)
|
||||
|
||||
self.addSubnode(self.contextContainer)
|
||||
self.contextContainer.addSubnode(self.mainContentContainerNode)
|
||||
|
||||
self.contextContainer.addSubnode(self.avatarNode)
|
||||
self.contextContainer.addSubnode(self.onlineNode)
|
||||
|
||||
self.contextContainer.addSubnode(self.titleNode)
|
||||
self.contextContainer.addSubnode(self.authorNode)
|
||||
self.contextContainer.addSubnode(self.textNode.textNode)
|
||||
self.contextContainer.addSubnode(self.dateNode)
|
||||
self.contextContainer.addSubnode(self.statusNode)
|
||||
self.contextContainer.addSubnode(self.pinnedIconNode)
|
||||
self.contextContainer.addSubnode(self.badgeNode)
|
||||
self.contextContainer.addSubnode(self.mentionBadgeNode)
|
||||
self.contextContainer.addSubnode(self.mutedIconNode)
|
||||
self.mainContentContainerNode.addSubnode(self.titleNode)
|
||||
self.mainContentContainerNode.addSubnode(self.authorNode)
|
||||
self.mainContentContainerNode.addSubnode(self.textNode.textNode)
|
||||
self.mainContentContainerNode.addSubnode(self.dateNode)
|
||||
self.mainContentContainerNode.addSubnode(self.statusNode)
|
||||
self.mainContentContainerNode.addSubnode(self.pinnedIconNode)
|
||||
self.mainContentContainerNode.addSubnode(self.badgeNode)
|
||||
self.mainContentContainerNode.addSubnode(self.mentionBadgeNode)
|
||||
self.mainContentContainerNode.addSubnode(self.mutedIconNode)
|
||||
|
||||
self.peerPresenceManager = PeerPresenceStatusManager(update: { [weak self] in
|
||||
if let strongSelf = self, let layoutParams = strongSelf.layoutParams {
|
||||
@ -1094,6 +1101,19 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
})
|
||||
|
||||
self.contextContainer.shouldBegin = { [weak self] location in
|
||||
guard let strongSelf = self else {
|
||||
return false
|
||||
}
|
||||
if let value = strongSelf.hitTest(location, with: nil), value === strongSelf.compoundTextButtonNode?.view {
|
||||
strongSelf.contextContainer.targetNodeForActivationProgress = strongSelf.compoundHighlightingNode
|
||||
} else {
|
||||
strongSelf.contextContainer.targetNodeForActivationProgress = nil
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
self.contextContainer.activated = { [weak self] gesture, location in
|
||||
guard let strongSelf = self, let item = strongSelf.item else {
|
||||
return
|
||||
@ -1505,7 +1525,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let avatarDiameter = min(60.0, floor(item.presentationData.fontSize.baseDisplaySize * 60.0 / 17.0))
|
||||
|
||||
let avatarLeftInset: CGFloat
|
||||
if case .forum = item.index {
|
||||
if item.interaction.isInlineMode {
|
||||
avatarLeftInset = 12.0
|
||||
} else if case .forum = item.index {
|
||||
avatarLeftInset = 50.0
|
||||
} else {
|
||||
avatarLeftInset = 18.0 + avatarDiameter
|
||||
@ -2291,8 +2313,6 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.cachedChatListSearchResult = chatListSearchResult
|
||||
strongSelf.onlineIsVoiceChat = onlineIsVoiceChat
|
||||
|
||||
strongSelf.contextContainer.frame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
|
||||
if case .groupReference = item.content {
|
||||
strongSelf.layer.sublayerTransform = CATransform3DMakeTranslation(0.0, layout.contentSize.height - itemHeight, 0.0)
|
||||
}
|
||||
@ -2301,7 +2321,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
strongSelf.separatorNode.backgroundColor = item.presentationData.theme.chatList.itemSeparatorColor
|
||||
}
|
||||
|
||||
let revealOffset = strongSelf.revealOffset
|
||||
let revealOffset = 0.0//strongSelf.revealOffset
|
||||
|
||||
let transition: ContainedViewLayoutTransition
|
||||
if animated {
|
||||
@ -2310,6 +2330,30 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
transition = .immediate
|
||||
}
|
||||
|
||||
let contextContainerFrame = CGRect(origin: CGPoint(), size: layout.contentSize)
|
||||
strongSelf.contextContainer.position = contextContainerFrame.center
|
||||
transition.updateBounds(node: strongSelf.contextContainer, bounds: contextContainerFrame.offsetBy(dx: -strongSelf.revealOffset, dy: 0.0))
|
||||
|
||||
if item.interaction.inlineNavigationLocation != nil {
|
||||
let mainContentFrame = CGRect(origin: CGPoint(x: params.leftInset + 72.0, y: 0.0), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height))
|
||||
transition.updatePosition(node: strongSelf.mainContentContainerNode, position: mainContentFrame.center)
|
||||
|
||||
transition.updateBounds(node: strongSelf.mainContentContainerNode, bounds: CGRect(origin: CGPoint(x: mainContentFrame.size.width, y: 0.0), size: mainContentFrame.size))
|
||||
transition.updateAlpha(node: strongSelf.mainContentContainerNode, alpha: 0.0)
|
||||
} else if case .chatList = item.chatListLocation {
|
||||
let mainContentFrame = CGRect(origin: CGPoint(x: params.leftInset + 72.0, y: 0.0), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height))
|
||||
transition.updatePosition(node: strongSelf.mainContentContainerNode, position: mainContentFrame.center)
|
||||
|
||||
transition.updateBounds(node: strongSelf.mainContentContainerNode, bounds: CGRect(origin: CGPoint(x: mainContentFrame.origin.x, y: 0.0), size: mainContentFrame.size))
|
||||
transition.updateAlpha(node: strongSelf.mainContentContainerNode, alpha: 1.0)
|
||||
} else {
|
||||
let mainContentFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.contentSize.width, height: layout.contentSize.height))
|
||||
transition.updatePosition(node: strongSelf.mainContentContainerNode, position: mainContentFrame.center)
|
||||
|
||||
transition.updateBounds(node: strongSelf.mainContentContainerNode, bounds: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: mainContentFrame.size))
|
||||
transition.updateAlpha(node: strongSelf.mainContentContainerNode, alpha: 1.0)
|
||||
}
|
||||
|
||||
var crossfadeContent = false
|
||||
if let selectableControlSizeAndApply = selectableControlSizeAndApply {
|
||||
let selectableControlSize = CGSize(width: selectableControlSizeAndApply.0, height: layout.contentSize.height)
|
||||
@ -2380,9 +2424,51 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let contentRect = rawContentRect.offsetBy(dx: editingOffset + leftInset + revealOffset, dy: 0.0)
|
||||
|
||||
let avatarFrame = CGRect(origin: CGPoint(x: leftInset - avatarLeftInset + editingOffset + 10.0 + revealOffset, y: floor((itemHeight - avatarDiameter) / 2.0)), size: CGSize(width: avatarDiameter, height: avatarDiameter))
|
||||
transition.updateFrame(node: strongSelf.avatarNode, frame: avatarFrame)
|
||||
var avatarScaleOffset: CGFloat = 0.0
|
||||
var avatarScale: CGFloat = 1.0
|
||||
if item.interaction.inlineNavigationLocation != nil {
|
||||
avatarScale = 54.0 / avatarFrame.width
|
||||
avatarScaleOffset = -(avatarFrame.width - avatarFrame.width * avatarScale) * 0.5
|
||||
}
|
||||
transition.updatePosition(node: strongSelf.avatarNode, position: avatarFrame.center.offsetBy(dx: avatarScaleOffset, dy: 0.0))
|
||||
transition.updateBounds(node: strongSelf.avatarNode, bounds: CGRect(origin: CGPoint(), size: avatarFrame.size))
|
||||
transition.updateTransformScale(node: strongSelf.avatarNode, scale: avatarScale)
|
||||
strongSelf.avatarNode.updateSize(size: avatarFrame.size)
|
||||
strongSelf.updateVideoVisibility()
|
||||
|
||||
var itemPeerId: EnginePeer.Id?
|
||||
if case let .chatList(index) = item.index {
|
||||
itemPeerId = index.messageIndex.id.peerId
|
||||
}
|
||||
|
||||
if let itemPeerId = itemPeerId, let inlineNavigationLocation = item.interaction.inlineNavigationLocation, inlineNavigationLocation.location.peerId == itemPeerId {
|
||||
let inlineNavigationMarkLayer: SimpleLayer
|
||||
var animateIn = false
|
||||
if let current = strongSelf.inlineNavigationMarkLayer {
|
||||
inlineNavigationMarkLayer = current
|
||||
} else {
|
||||
inlineNavigationMarkLayer = SimpleLayer()
|
||||
strongSelf.inlineNavigationMarkLayer = inlineNavigationMarkLayer
|
||||
inlineNavigationMarkLayer.cornerRadius = 4.0
|
||||
animateIn = true
|
||||
strongSelf.layer.addSublayer(inlineNavigationMarkLayer)
|
||||
}
|
||||
inlineNavigationMarkLayer.backgroundColor = item.presentationData.theme.list.itemAccentColor.cgColor
|
||||
let markHeight: CGFloat = 50.0
|
||||
let markFrame = CGRect(origin: CGPoint(x: -4.0, y: avatarFrame.midY - markHeight * 0.5), size: CGSize(width: 8.0, height: markHeight))
|
||||
if animateIn {
|
||||
inlineNavigationMarkLayer.frame = markFrame
|
||||
transition.animatePositionAdditive(layer: inlineNavigationMarkLayer, offset: CGPoint(x: -markFrame.width * 0.5, y: 0.0))
|
||||
} else {
|
||||
transition.updateFrame(layer: inlineNavigationMarkLayer, frame: markFrame)
|
||||
}
|
||||
} else {
|
||||
if let inlineNavigationMarkLayer = strongSelf.inlineNavigationMarkLayer {
|
||||
strongSelf.inlineNavigationMarkLayer = nil
|
||||
transition.updatePosition(layer: inlineNavigationMarkLayer, position: CGPoint(x: -inlineNavigationMarkLayer.bounds.width * 0.5, y: avatarFrame.midY))
|
||||
}
|
||||
}
|
||||
|
||||
if let threadInfo = threadInfo {
|
||||
let avatarIconView: ComponentHostView<Empty>
|
||||
if let current = strongSelf.avatarIconView {
|
||||
@ -2390,7 +2476,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
avatarIconView = ComponentHostView<Empty>()
|
||||
strongSelf.avatarIconView = avatarIconView
|
||||
strongSelf.contextContainer.view.addSubview(avatarIconView)
|
||||
strongSelf.mainContentContainerNode.view.addSubview(avatarIconView)
|
||||
}
|
||||
|
||||
let avatarIconContent: EmojiStatusComponent.Content
|
||||
@ -2414,9 +2500,16 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
transition: .immediate,
|
||||
component: AnyComponent(avatarIconComponent),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 32.0, height: 32.0)
|
||||
containerSize: item.interaction.isInlineMode ? CGSize(width: 18.0, height: 18.0) : CGSize(width: 32.0, height: 32.0)
|
||||
)
|
||||
transition.updateFrame(view: avatarIconView, frame: CGRect(origin: CGPoint(x: editingOffset + params.leftInset + floor((leftInset - params.leftInset - iconSize.width) / 2.0) + revealOffset, y: contentRect.origin.y + 2.0), size: iconSize))
|
||||
|
||||
let avatarIconFrame: CGRect
|
||||
if item.interaction.isInlineMode {
|
||||
avatarIconFrame = CGRect(origin: CGPoint(x: contentRect.origin.x, y: contentRect.origin.y + 1.0), size: iconSize)
|
||||
} else {
|
||||
avatarIconFrame = CGRect(origin: CGPoint(x: editingOffset + params.leftInset + floor((leftInset - params.leftInset - iconSize.width) / 2.0) + revealOffset, y: contentRect.origin.y + 2.0), size: iconSize)
|
||||
}
|
||||
transition.updateFrame(view: avatarIconView, frame: avatarIconFrame)
|
||||
} else if let avatarIconView = strongSelf.avatarIconView {
|
||||
strongSelf.avatarIconView = nil
|
||||
avatarIconView.removeFromSuperview()
|
||||
@ -2479,7 +2572,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
dateStatusIconNode = ASImageNode()
|
||||
strongSelf.dateStatusIconNode = dateStatusIconNode
|
||||
strongSelf.contextContainer.addSubnode(dateStatusIconNode)
|
||||
strongSelf.mainContentContainerNode.addSubnode(dateStatusIconNode)
|
||||
}
|
||||
dateStatusIconNode.image = dateIconImage
|
||||
|
||||
@ -2539,6 +2632,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
var titleOffset: CGFloat = 0.0
|
||||
if item.interaction.isInlineMode {
|
||||
titleOffset += 22.0
|
||||
}
|
||||
if let currentSecretIconImage = currentSecretIconImage {
|
||||
let iconNode: ASImageNode
|
||||
if let current = strongSelf.secretIconNode {
|
||||
@ -2548,7 +2644,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
iconNode.isLayerBacked = true
|
||||
iconNode.displaysAsynchronously = false
|
||||
iconNode.displayWithoutProcessing = true
|
||||
strongSelf.contextContainer.addSubnode(iconNode)
|
||||
strongSelf.mainContentContainerNode.addSubnode(iconNode)
|
||||
strongSelf.secretIconNode = iconNode
|
||||
}
|
||||
iconNode.image = currentSecretIconImage
|
||||
@ -2576,7 +2672,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
compoundHighlightingNode.alpha = strongSelf.authorNode.alpha
|
||||
compoundHighlightingNode.useModernPathCalculation = true
|
||||
strongSelf.compoundHighlightingNode = compoundHighlightingNode
|
||||
strongSelf.contextContainer.insertSubnode(compoundHighlightingNode, at: 0)
|
||||
strongSelf.mainContentContainerNode.insertSubnode(compoundHighlightingNode, at: 0)
|
||||
}
|
||||
|
||||
let compoundTextButtonNode: HighlightTrackingButtonNode
|
||||
@ -2585,7 +2681,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
compoundTextButtonNode = HighlightTrackingButtonNode()
|
||||
strongSelf.compoundTextButtonNode = compoundTextButtonNode
|
||||
strongSelf.contextContainer.addSubnode(compoundTextButtonNode)
|
||||
strongSelf.mainContentContainerNode.addSubnode(compoundTextButtonNode)
|
||||
compoundTextButtonNode.addTarget(strongSelf, action: #selector(strongSelf.compoundTextButtonPressed), forControlEvents: .touchUpInside)
|
||||
compoundTextButtonNode.highligthedChanged = { highlighted in
|
||||
guard let strongSelf = self, let compoundHighlightingNode = strongSelf.compoundHighlightingNode else {
|
||||
@ -2644,7 +2740,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
textArrowNode = ASImageNode()
|
||||
strongSelf.textArrowNode = textArrowNode
|
||||
compoundTextButtonNode.addSubnode(textArrowNode)
|
||||
compoundHighlightingNode.addSubnode(textArrowNode)
|
||||
}
|
||||
textArrowNode.image = textArrowImage
|
||||
let arrowScale: CGFloat = 0.75
|
||||
@ -2677,7 +2773,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
dustNode = InvisibleInkDustNode(textNode: nil)
|
||||
dustNode.isUserInteractionEnabled = false
|
||||
strongSelf.dustNode = dustNode
|
||||
strongSelf.contextContainer.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode.textNode)
|
||||
strongSelf.mainContentContainerNode.insertSubnode(dustNode, aboveSubnode: strongSelf.textNode.textNode)
|
||||
}
|
||||
dustNode.update(size: textNodeFrame.size, color: theme.messageTextColor, textColor: theme.messageTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 0.0, dy: 1.0) })
|
||||
dustNode.frame = textNodeFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)
|
||||
@ -2699,7 +2795,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
|
||||
if let inputActivities = inputActivities, !inputActivities.isEmpty {
|
||||
if strongSelf.inputActivitiesNode.supernode == nil {
|
||||
strongSelf.contextContainer.addSubnode(strongSelf.inputActivitiesNode)
|
||||
strongSelf.mainContentContainerNode.addSubnode(strongSelf.inputActivitiesNode)
|
||||
} else {
|
||||
animateInputActivitiesFrame = true
|
||||
}
|
||||
@ -2768,7 +2864,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
previewNodeAlphaTransition = .immediate
|
||||
previewNode = ChatListMediaPreviewNode(context: item.context, message: message, media: media)
|
||||
strongSelf.mediaPreviewNodes[mediaId] = previewNode
|
||||
strongSelf.contextContainer.addSubnode(previewNode)
|
||||
strongSelf.mainContentContainerNode.addSubnode(previewNode)
|
||||
}
|
||||
previewNode.updateLayout(size: mediaSize, synchronousLoads: synchronousLoads)
|
||||
previewNodeAlphaTransition.updateAlpha(node: previewNode, alpha: strongSelf.inputActivitiesNode.alpha.isZero ? 1.0 : 0.0)
|
||||
@ -2820,7 +2916,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
} else {
|
||||
credibilityIconView = ComponentHostView<Empty>()
|
||||
strongSelf.credibilityIconView = credibilityIconView
|
||||
strongSelf.contextContainer.view.addSubview(credibilityIconView)
|
||||
strongSelf.mainContentContainerNode.view.addSubview(credibilityIconView)
|
||||
}
|
||||
|
||||
let credibilityIconComponent = EmojiStatusComponent(
|
||||
@ -2866,11 +2962,15 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
}
|
||||
|
||||
transition.updateFrame(node: strongSelf.separatorNode, frame: CGRect(origin: CGPoint(x: separatorInset, y: layoutOffset + itemHeight - separatorHeight), size: CGSize(width: params.width - separatorInset, height: separatorHeight)))
|
||||
transition.updateAlpha(node: strongSelf.separatorNode, alpha: item.interaction.inlineNavigationLocation != nil ? 0.0 : 1.0)
|
||||
|
||||
transition.updateFrame(node: strongSelf.backgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.contentSize.width, height: itemHeight)))
|
||||
let backgroundColor: UIColor
|
||||
let highlightedBackgroundColor: UIColor
|
||||
if item.selected {
|
||||
if item.interaction.inlineNavigationLocation != nil {
|
||||
backgroundColor = theme.pinnedItemBackgroundColor
|
||||
highlightedBackgroundColor = theme.itemHighlightedBackgroundColor
|
||||
} else if item.selected {
|
||||
backgroundColor = theme.itemSelectedBackgroundColor
|
||||
highlightedBackgroundColor = theme.itemHighlightedBackgroundColor
|
||||
} else if isPinned {
|
||||
@ -3011,7 +3111,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
self.playbackStartDisposable.set(nil)
|
||||
videoNode.isHidden = false
|
||||
}
|
||||
videoNode.layer.cornerRadius = self.avatarNode.frame.size.width / 2.0
|
||||
videoNode.layer.cornerRadius = self.avatarNode.bounds.size.width / 2.0
|
||||
if #available(iOS 13.0, *) {
|
||||
videoNode.layer.cornerCurve = .circular
|
||||
}
|
||||
@ -3019,7 +3119,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
videoNode.canAttachContent = true
|
||||
videoNode.play()
|
||||
|
||||
// self.contextContainer.insertSubnode(videoNode, aboveSubnode: self.avatarNode)
|
||||
// self.mainContentContainerNode.insertSubnode(videoNode, aboveSubnode: self.avatarNode)
|
||||
self.avatarNode.addSubnode(videoNode)
|
||||
self.videoNode = videoNode
|
||||
}
|
||||
@ -3037,7 +3137,9 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
override func updateRevealOffset(offset: CGFloat, transition: ContainedViewLayoutTransition) {
|
||||
super.updateRevealOffset(offset: offset, transition: transition)
|
||||
|
||||
if let item = self.item, let params = self.layoutParams?.5, let currentItemHeight = self.currentItemHeight, let countersSize = self.layoutParams?.6 {
|
||||
transition.updateBounds(node: self.contextContainer, bounds: self.contextContainer.frame.offsetBy(dx: -offset, dy: 0.0))
|
||||
|
||||
/*if let item = self.item, let params = self.layoutParams?.5, let currentItemHeight = self.currentItemHeight, let countersSize = self.layoutParams?.6 {
|
||||
let editingOffset: CGFloat
|
||||
if let selectableControlNode = self.selectableControlNode {
|
||||
editingOffset = selectableControlNode.bounds.size.width
|
||||
@ -3188,7 +3290,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode {
|
||||
let badgeBackgroundFrame = CGRect(x: badgeOffset, y: self.pinnedIconNode.frame.origin.y, width: badgeBackgroundWidth, height: pinnedIconSize.height)
|
||||
transition.updateFrame(node: self.pinnedIconNode, frame: badgeBackgroundFrame)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
override func touchesToOtherItemsPrevented() {
|
||||
|
@ -35,7 +35,7 @@ struct ChatListNodeListViewTransition {
|
||||
let animateCrossfade: Bool
|
||||
}
|
||||
|
||||
final class ChatListHighlightedLocation {
|
||||
final class ChatListHighlightedLocation: Equatable {
|
||||
let location: ChatLocation
|
||||
let progress: CGFloat
|
||||
|
||||
@ -47,6 +47,16 @@ final class ChatListHighlightedLocation {
|
||||
func withUpdatedProgress(_ progress: CGFloat) -> ChatListHighlightedLocation {
|
||||
return ChatListHighlightedLocation(location: location, progress: progress)
|
||||
}
|
||||
|
||||
static func ==(lhs: ChatListHighlightedLocation, rhs: ChatListHighlightedLocation) -> Bool {
|
||||
if lhs.location != rhs.location {
|
||||
return false
|
||||
}
|
||||
if lhs.progress != rhs.progress {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public final class ChatListNodeInteraction {
|
||||
@ -84,6 +94,9 @@ public final class ChatListNodeInteraction {
|
||||
public var searchTextHighightState: String?
|
||||
var highlightedChatLocation: ChatListHighlightedLocation?
|
||||
|
||||
var isInlineMode: Bool = false
|
||||
var inlineNavigationLocation: ChatListHighlightedLocation?
|
||||
|
||||
let animationCache: AnimationCache
|
||||
let animationRenderer: MultiAnimationRenderer
|
||||
|
||||
@ -871,7 +884,9 @@ public final class ChatListNode: ListView {
|
||||
public var selectionLimit: Int32 = 100
|
||||
public var reachedSelectionLimit: ((Int32) -> Void)?
|
||||
|
||||
public init(context: AccountContext, location: ChatListControllerLocation, chatListFilter: ChatListFilter? = nil, previewing: Bool, fillPreloadItems: Bool, mode: ChatListNodeMode, theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, disableAnimations: Bool) {
|
||||
private var visibleTopInset: CGFloat?
|
||||
|
||||
public init(context: AccountContext, location: ChatListControllerLocation, chatListFilter: ChatListFilter? = nil, previewing: Bool, fillPreloadItems: Bool, mode: ChatListNodeMode, theme: PresentationTheme, fontSize: PresentationFontSize, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, nameSortOrder: PresentationPersonNameOrder, nameDisplayOrder: PresentationPersonNameOrder, animationCache: AnimationCache, animationRenderer: MultiAnimationRenderer, disableAnimations: Bool, isInlineMode: Bool) {
|
||||
self.context = context
|
||||
self.location = location
|
||||
self.chatListFilter = chatListFilter
|
||||
@ -1156,6 +1171,7 @@ public final class ChatListNode: ListView {
|
||||
self.peerSelected?(peer, threadId, true, true, nil)
|
||||
})
|
||||
})
|
||||
nodeInteraction.isInlineMode = isInlineMode
|
||||
|
||||
let viewProcessingQueue = self.viewProcessingQueue
|
||||
|
||||
@ -1929,6 +1945,9 @@ public final class ChatListNode: ListView {
|
||||
guard let strongSelf = self else {
|
||||
return
|
||||
}
|
||||
if !strongSelf.dequeuedInitialTransitionOnLayout {
|
||||
return
|
||||
}
|
||||
let atTop: Bool
|
||||
var revealHiddenItems: Bool = false
|
||||
switch offset {
|
||||
@ -2255,12 +2274,16 @@ public final class ChatListNode: ListView {
|
||||
var scrollToItem = transition.scrollToItem
|
||||
if transition.adjustScrollToFirstItem {
|
||||
var offset: CGFloat = 0.0
|
||||
if let visibleTopInset = self.visibleTopInset {
|
||||
offset = visibleTopInset - self.insets.top
|
||||
} else {
|
||||
switch self.visibleContentOffset() {
|
||||
case let .known(value) where abs(value) < .ulpOfOne:
|
||||
offset = 0.0
|
||||
default:
|
||||
offset = -navigationBarSearchContentHeight
|
||||
}
|
||||
}
|
||||
scrollToItem = ListViewScrollToItem(index: 0, position: .top(offset), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
|
||||
}
|
||||
|
||||
@ -2316,8 +2339,34 @@ public final class ChatListNode: ListView {
|
||||
}
|
||||
}
|
||||
|
||||
public func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets) {
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
public func fixContentOffset(offset: CGFloat) {
|
||||
let _ = self.scrollToOffsetFromTop(offset, animated: false)
|
||||
|
||||
/*let scrollToItem: ListViewScrollToItem = ListViewScrollToItem(index: 0, position: .top(-offset), animated: false, curve: .Default(duration: 0.0), directionHint: .Up)
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: scrollToItem, updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })*/
|
||||
}
|
||||
|
||||
public func updateLayout(transition: ContainedViewLayoutTransition, updateSizeAndInsets: ListViewUpdateSizeAndInsets, visibleTopInset: CGFloat, inlineNavigationLocation: ChatListControllerLocation?) {
|
||||
self.visibleTopInset = visibleTopInset
|
||||
|
||||
self.visualInsets = UIEdgeInsets(top: visibleTopInset, left: 0.0, bottom: 0.0, right: 0.0)
|
||||
|
||||
var highlightedLocation: ChatListHighlightedLocation?
|
||||
if case let .forum(peerId) = inlineNavigationLocation {
|
||||
highlightedLocation = ChatListHighlightedLocation(location: .peer(id: peerId), progress: 1.0)
|
||||
}
|
||||
var navigationLocationUpdated = false
|
||||
if self.interaction?.inlineNavigationLocation != highlightedLocation {
|
||||
self.interaction?.inlineNavigationLocation = highlightedLocation
|
||||
navigationLocationUpdated = true
|
||||
}
|
||||
|
||||
var options: ListViewDeleteAndInsertOptions = [.Synchronous, .LowLatency]
|
||||
if navigationLocationUpdated {
|
||||
options.insert(.ForceUpdate)
|
||||
options.insert(.AnimateInsertion)
|
||||
}
|
||||
self.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: options, scrollToItem: nil, updateSizeAndInsets: updateSizeAndInsets, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
|
||||
if !self.dequeuedInitialTransitionOnLayout {
|
||||
self.dequeuedInitialTransitionOnLayout = true
|
||||
|
@ -210,7 +210,7 @@ func preparedChatListNodeViewTransition(from fromView: ChatListNodeView?, to toV
|
||||
}
|
||||
|
||||
var adjustScrollToFirstItem = false
|
||||
if !previewing && !searchMode && fromEmptyView && scrollToItem == nil && toView.filteredEntries.count >= 2 {
|
||||
if !previewing && !searchMode && fromEmptyView && scrollToItem == nil && toView.filteredEntries.count >= 1 {
|
||||
adjustScrollToFirstItem = true
|
||||
}
|
||||
|
||||
|
@ -105,7 +105,7 @@ private func fixListNodeScrolling(_ listNode: ListView, searchNode: NavigationBa
|
||||
} else {
|
||||
offset = 0.0
|
||||
}
|
||||
let _ = listNode.scrollToOffsetFromTop(offset)
|
||||
let _ = listNode.scrollToOffsetFromTop(offset, animated: true)
|
||||
return true
|
||||
} else if searchNode.expansionProgress == 1.0 {
|
||||
var sortItemNode: ListViewItemNode?
|
||||
|
@ -86,7 +86,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
case enableDebugDataDisplay(Bool)
|
||||
case acceleratedStickers(Bool)
|
||||
case experimentalBackground(Bool)
|
||||
case inlineStickers(Bool)
|
||||
case inlineForums(Bool)
|
||||
case localTranscription(Bool)
|
||||
case enableReactionOverrides(Bool)
|
||||
case playerEmbedding(Bool)
|
||||
@ -111,7 +111,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return DebugControllerSection.logging.rawValue
|
||||
case .enableRaiseToSpeak, .keepChatNavigationStack, .skipReadHistory, .crashOnSlowQueries:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .inlineStickers, .localTranscription, . enableReactionOverrides, .restorePurchases:
|
||||
case .clearTips, .crash, .resetData, .resetDatabase, .resetDatabaseAndCache, .resetHoles, .reindexUnread, .resetBiometricsData, .resetWebViewCache, .optimizeDatabase, .photoPreview, .knockoutWallpaper, .playerEmbedding, .playlistPlayback, .voiceConference, .experimentalCompatibility, .enableDebugDataDisplay, .acceleratedStickers, .experimentalBackground, .inlineForums, .localTranscription, . enableReactionOverrides, .restorePurchases:
|
||||
return DebugControllerSection.experiments.rawValue
|
||||
case .preferredVideoCodec:
|
||||
return DebugControllerSection.videoExperiments.rawValue
|
||||
@ -188,7 +188,7 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
return 30
|
||||
case .experimentalBackground:
|
||||
return 31
|
||||
case .inlineStickers:
|
||||
case .inlineForums:
|
||||
return 32
|
||||
case .localTranscription:
|
||||
return 33
|
||||
@ -1053,12 +1053,12 @@ private enum DebugControllerEntry: ItemListNodeEntry {
|
||||
})
|
||||
}).start()
|
||||
})
|
||||
case let .inlineStickers(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Inline Stickers", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
case let .inlineForums(value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, title: "Inline Forums", value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
let _ = arguments.sharedContext.accountManager.transaction ({ transaction in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings, { settings in
|
||||
var settings = settings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
settings.inlineStickers = value
|
||||
settings.inlineForums = value
|
||||
return PreferencesEntry(settings)
|
||||
})
|
||||
}).start()
|
||||
@ -1219,7 +1219,7 @@ private func debugControllerEntries(sharedContext: SharedAccountContext, present
|
||||
entries.append(.enableDebugDataDisplay(experimentalSettings.enableDebugDataDisplay))
|
||||
entries.append(.acceleratedStickers(experimentalSettings.acceleratedStickers))
|
||||
entries.append(.experimentalBackground(experimentalSettings.experimentalBackground))
|
||||
entries.append(.inlineStickers(experimentalSettings.inlineStickers))
|
||||
entries.append(.inlineForums(experimentalSettings.inlineForums))
|
||||
entries.append(.localTranscription(experimentalSettings.localTranscription))
|
||||
if case .internal = sharedContext.applicationBindings.appBuildType {
|
||||
entries.append(.enableReactionOverrides(experimentalSettings.enableReactionOverrides))
|
||||
|
@ -1144,6 +1144,8 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
|
||||
if bottomItemFound {
|
||||
bottomItemEdge = self.itemNodes[self.itemNodes.count - 1].apparentFrame.maxY
|
||||
} else {
|
||||
bottomItemEdge = self.visibleSize.height
|
||||
}
|
||||
|
||||
if topItemFound && bottomItemFound {
|
||||
@ -1734,7 +1736,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
|
||||
private func deleteAndInsertItemsTransaction(deleteIndices: [ListViewDeleteItem], insertIndicesAndItems: [ListViewInsertItem], updateIndicesAndItems: [ListViewUpdateItem], options: ListViewDeleteAndInsertOptions, scrollToItem: ListViewScrollToItem?, additionalScrollDistance: CGFloat, updateSizeAndInsets: ListViewUpdateSizeAndInsets?, stationaryItemRange: (Int, Int)?, updateOpaqueState: Any?, completion: @escaping () -> Void) {
|
||||
if deleteIndices.isEmpty && insertIndicesAndItems.isEmpty && updateIndicesAndItems.isEmpty && scrollToItem == nil {
|
||||
if let updateSizeAndInsets = updateSizeAndInsets, (self.items.count == 0 || (updateSizeAndInsets.size == self.visibleSize && updateSizeAndInsets.insets == self.insets)) {
|
||||
if let updateSizeAndInsets = updateSizeAndInsets, (self.items.count == 0 || (updateSizeAndInsets.size == self.visibleSize && updateSizeAndInsets.insets == self.insets && !options.contains(.ForceUpdate))) {
|
||||
self.visibleSize = updateSizeAndInsets.size
|
||||
self.insets = updateSizeAndInsets.insets
|
||||
self.headerInsets = updateSizeAndInsets.headerInsets ?? self.insets
|
||||
@ -1769,7 +1771,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
|
||||
let widthUpdated: Bool
|
||||
if let updateSizeAndInsets = updateSizeAndInsets {
|
||||
widthUpdated = abs(state.visibleSize.width - updateSizeAndInsets.size.width) > CGFloat.ulpOfOne
|
||||
widthUpdated = abs(state.visibleSize.width - updateSizeAndInsets.size.width) > CGFloat.ulpOfOne || options.contains(.ForceUpdate)
|
||||
|
||||
state.visibleSize = updateSizeAndInsets.size
|
||||
state.insets = updateSizeAndInsets.insets
|
||||
@ -2820,7 +2822,7 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
case let .bottom(additionalOffset):
|
||||
offset = (self.visibleSize.height - insets.bottom) - itemNode.apparentFrame.maxY + itemNode.scrollPositioningInsets.bottom + additionalOffset
|
||||
case let .top(additionalOffset):
|
||||
offset = insets.top - itemNode.apparentFrame.minY - itemNode.scrollPositioningInsets.top + additionalOffset
|
||||
offset = (insets.top + additionalOffset + itemNode.scrollPositioningInsets.top) - itemNode.apparentFrame.minY
|
||||
case let .center(overflow):
|
||||
let contentAreaHeight = self.visibleSize.height - insets.bottom - insets.top
|
||||
if itemNode.apparentFrame.size.height <= contentAreaHeight + CGFloat.ulpOfOne {
|
||||
@ -2911,9 +2913,13 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
offsetFix = 0.0
|
||||
} else if self.snapToBottomInsetUntilFirstInteraction {
|
||||
offsetFix = -updateSizeAndInsets.insets.bottom + self.insets.bottom
|
||||
} else {
|
||||
if let visualInsets = self.visualInsets, animated, (visualInsets.top == updateSizeAndInsets.insets.top || visualInsets.top == self.insets.top) {
|
||||
offsetFix = 0.0
|
||||
} else {
|
||||
offsetFix = updateSizeAndInsets.insets.top - self.insets.top
|
||||
}
|
||||
}
|
||||
|
||||
offsetFix += additionalScrollDistance
|
||||
|
||||
@ -4783,10 +4789,14 @@ open class ListView: ASDisplayNode, UIScrollViewAccessibilityDelegate, UIGesture
|
||||
}
|
||||
}
|
||||
|
||||
public func scrollToOffsetFromTop(_ offset: CGFloat) -> Bool {
|
||||
public func scrollToOffsetFromTop(_ offset: CGFloat, animated: Bool) -> Bool {
|
||||
for itemNode in self.itemNodes {
|
||||
if itemNode.index == 0 {
|
||||
self.scroller.setContentOffset(CGPoint(x: 0.0, y: offset), animated: true)
|
||||
if animated {
|
||||
self.scroller.setContentOffset(CGPoint(x: 0.0, y: offset), animated: animated)
|
||||
} else {
|
||||
self.scroller.contentOffset = CGPoint(x: 0.0, y: offset)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +104,7 @@ public struct ListViewDeleteAndInsertOptions: OptionSet {
|
||||
public static let PreferSynchronousDrawing = ListViewDeleteAndInsertOptions(rawValue: 64)
|
||||
public static let PreferSynchronousResourceLoading = ListViewDeleteAndInsertOptions(rawValue: 128)
|
||||
public static let AnimateCrossfade = ListViewDeleteAndInsertOptions(rawValue: 256)
|
||||
public static let ForceUpdate = ListViewDeleteAndInsertOptions(rawValue: 512)
|
||||
}
|
||||
|
||||
public struct ListViewUpdateSizeAndInsets {
|
||||
|
@ -206,6 +206,17 @@ final class ViewTracker {
|
||||
let record = (view, ValuePipe<CombinedView>())
|
||||
let index = self.combinedViews.add(record)
|
||||
|
||||
if view.views.keys.contains(where: { key in
|
||||
switch key {
|
||||
case .messageHistoryThreadIndex:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}) {
|
||||
self.updateTrackedForumTopicListHoles()
|
||||
}
|
||||
|
||||
return (index, record.1.signal())
|
||||
}
|
||||
|
||||
|
@ -456,7 +456,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
||||
dict[-1434950843] = { return Api.MessageAction.parse_messageActionSetChatTheme($0) }
|
||||
dict[-1441072131] = { return Api.MessageAction.parse_messageActionSetMessagesTTL($0) }
|
||||
dict[228168278] = { return Api.MessageAction.parse_messageActionTopicCreate($0) }
|
||||
dict[-1316338916] = { return Api.MessageAction.parse_messageActionTopicEdit($0) }
|
||||
dict[-1064024032] = { return Api.MessageAction.parse_messageActionTopicEdit($0) }
|
||||
dict[-1262252875] = { return Api.MessageAction.parse_messageActionWebViewDataSent($0) }
|
||||
dict[1205698681] = { return Api.MessageAction.parse_messageActionWebViewDataSentMe($0) }
|
||||
dict[546203849] = { return Api.MessageEntity.parse_inputMessageEntityMentionName($0) }
|
||||
|
@ -1020,7 +1020,7 @@ public extension Api {
|
||||
case messageActionSetChatTheme(emoticon: String)
|
||||
case messageActionSetMessagesTTL(period: Int32)
|
||||
case messageActionTopicCreate(flags: Int32, title: String, iconColor: Int32, iconEmojiId: Int64?)
|
||||
case messageActionTopicEdit(flags: Int32, title: String?, iconEmojiId: Int64?, closed: Api.Bool?)
|
||||
case messageActionTopicEdit(flags: Int32, title: String?, iconEmojiId: Int64?, closed: Api.Bool?, hidden: Api.Bool?)
|
||||
case messageActionWebViewDataSent(text: String)
|
||||
case messageActionWebViewDataSentMe(text: String, data: String)
|
||||
|
||||
@ -1265,14 +1265,15 @@ public extension Api {
|
||||
serializeInt32(iconColor, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)}
|
||||
break
|
||||
case .messageActionTopicEdit(let flags, let title, let iconEmojiId, let closed):
|
||||
case .messageActionTopicEdit(let flags, let title, let iconEmojiId, let closed, let hidden):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1316338916)
|
||||
buffer.appendInt32(-1064024032)
|
||||
}
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {closed!.serialize(buffer, true)}
|
||||
if Int(flags) & Int(1 << 3) != 0 {hidden!.serialize(buffer, true)}
|
||||
break
|
||||
case .messageActionWebViewDataSent(let text):
|
||||
if boxed {
|
||||
@ -1356,8 +1357,8 @@ public extension Api {
|
||||
return ("messageActionSetMessagesTTL", [("period", String(describing: period))])
|
||||
case .messageActionTopicCreate(let flags, let title, let iconColor, let iconEmojiId):
|
||||
return ("messageActionTopicCreate", [("flags", String(describing: flags)), ("title", String(describing: title)), ("iconColor", String(describing: iconColor)), ("iconEmojiId", String(describing: iconEmojiId))])
|
||||
case .messageActionTopicEdit(let flags, let title, let iconEmojiId, let closed):
|
||||
return ("messageActionTopicEdit", [("flags", String(describing: flags)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("closed", String(describing: closed))])
|
||||
case .messageActionTopicEdit(let flags, let title, let iconEmojiId, let closed, let hidden):
|
||||
return ("messageActionTopicEdit", [("flags", String(describing: flags)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("closed", String(describing: closed)), ("hidden", String(describing: hidden))])
|
||||
case .messageActionWebViewDataSent(let text):
|
||||
return ("messageActionWebViewDataSent", [("text", String(describing: text))])
|
||||
case .messageActionWebViewDataSentMe(let text, let data):
|
||||
@ -1783,12 +1784,17 @@ public extension Api {
|
||||
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
|
||||
_4 = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
} }
|
||||
var _5: Api.Bool?
|
||||
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
|
||||
_5 = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
} }
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
|
||||
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
|
||||
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.MessageAction.messageActionTopicEdit(flags: _1!, title: _2, iconEmojiId: _3, closed: _4)
|
||||
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 && _c5 {
|
||||
return Api.MessageAction.messageActionTopicEdit(flags: _1!, title: _2, iconEmojiId: _3, closed: _4, hidden: _5)
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
|
@ -1524,6 +1524,23 @@ public extension Api.functions.auth {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.auth {
|
||||
static func importWebTokenAuthorization(apiId: Int32, apiHash: String, webAuthToken: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.Authorization>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(767062953)
|
||||
serializeInt32(apiId, buffer: buffer, boxed: false)
|
||||
serializeString(apiHash, buffer: buffer, boxed: false)
|
||||
serializeString(webAuthToken, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "auth.importWebTokenAuthorization", parameters: [("apiId", String(describing: apiId)), ("apiHash", String(describing: apiHash)), ("webAuthToken", String(describing: webAuthToken))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.auth.Authorization? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.auth.Authorization?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.auth.Authorization
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.auth {
|
||||
static func logOut() -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.auth.LoggedOut>) {
|
||||
let buffer = Buffer()
|
||||
@ -2026,16 +2043,17 @@ public extension Api.functions.channels {
|
||||
}
|
||||
}
|
||||
public extension Api.functions.channels {
|
||||
static func editForumTopic(flags: Int32, channel: Api.InputChannel, topicId: Int32, title: String?, iconEmojiId: Int64?, closed: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
static func editForumTopic(flags: Int32, channel: Api.InputChannel, topicId: Int32, title: String?, iconEmojiId: Int64?, closed: Api.Bool?, hidden: Api.Bool?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1820868141)
|
||||
buffer.appendInt32(-186670715)
|
||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||
channel.serialize(buffer, true)
|
||||
serializeInt32(topicId, buffer: buffer, boxed: false)
|
||||
if Int(flags) & Int(1 << 0) != 0 {serializeString(title!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 1) != 0 {serializeInt64(iconEmojiId!, buffer: buffer, boxed: false)}
|
||||
if Int(flags) & Int(1 << 2) != 0 {closed!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "channels.editForumTopic", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("topicId", String(describing: topicId)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("closed", String(describing: closed))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
if Int(flags) & Int(1 << 3) != 0 {hidden!.serialize(buffer, true)}
|
||||
return (FunctionDescription(name: "channels.editForumTopic", parameters: [("flags", String(describing: flags)), ("channel", String(describing: channel)), ("topicId", String(describing: topicId)), ("title", String(describing: title)), ("iconEmojiId", String(describing: iconEmojiId)), ("closed", String(describing: closed)), ("hidden", String(describing: hidden))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
@ -2484,6 +2502,22 @@ public extension Api.functions.channels {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.channels {
|
||||
static func reportAntiSpamFalsePositive(channel: Api.InputChannel, msgId: Int32) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(-1471109485)
|
||||
channel.serialize(buffer, true)
|
||||
serializeInt32(msgId, buffer: buffer, boxed: false)
|
||||
return (FunctionDescription(name: "channels.reportAntiSpamFalsePositive", parameters: [("channel", String(describing: channel)), ("msgId", String(describing: msgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Bool?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Bool
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.channels {
|
||||
static func reportSpam(channel: Api.InputChannel, participant: Api.InputPeer, id: [Int32]) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||
let buffer = Buffer()
|
||||
@ -2537,6 +2571,22 @@ public extension Api.functions.channels {
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.channels {
|
||||
static func toggleAntiSpam(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
buffer.appendInt32(1760814315)
|
||||
channel.serialize(buffer, true)
|
||||
enabled.serialize(buffer, true)
|
||||
return (FunctionDescription(name: "channels.toggleAntiSpam", parameters: [("channel", String(describing: channel)), ("enabled", String(describing: enabled))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Updates? in
|
||||
let reader = BufferReader(buffer)
|
||||
var result: Api.Updates?
|
||||
if let signature = reader.readInt32() {
|
||||
result = Api.parse(reader, signature: signature) as? Api.Updates
|
||||
}
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
public extension Api.functions.channels {
|
||||
static func toggleForum(channel: Api.InputChannel, enabled: Api.Bool) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Updates>) {
|
||||
let buffer = Buffer()
|
||||
|
@ -89,7 +89,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
||||
return TelegramMediaAction(action: .giftPremium(currency: currency, amount: amount, months: months))
|
||||
case let .messageActionTopicCreate(_, title, iconColor, iconEmojiId):
|
||||
return TelegramMediaAction(action: .topicCreated(title: title, iconColor: iconColor, iconFileId: iconEmojiId))
|
||||
case let .messageActionTopicEdit(flags, title, iconEmojiId, closed):
|
||||
case let .messageActionTopicEdit(flags, title, iconEmojiId, closed, _):
|
||||
var components: [TelegramMediaActionType.ForumTopicEditComponent] = []
|
||||
if let title = title {
|
||||
components.append(.title(title))
|
||||
|
@ -293,7 +293,8 @@ func _internal_editForumChannelTopic(account: Account, peerId: PeerId, threadId:
|
||||
topicId: Int32(clamping: threadId),
|
||||
title: title,
|
||||
iconEmojiId: iconFileId ?? 0,
|
||||
closed: nil
|
||||
closed: nil,
|
||||
hidden: nil
|
||||
))
|
||||
|> mapError { _ -> EditForumChannelTopicError in
|
||||
return .generic
|
||||
@ -338,7 +339,8 @@ func _internal_setForumChannelTopicClosed(account: Account, id: EnginePeer.Id, t
|
||||
topicId: Int32(clamping: threadId),
|
||||
title: nil,
|
||||
iconEmojiId: nil,
|
||||
closed: isClosed ? .boolTrue : .boolFalse
|
||||
closed: isClosed ? .boolTrue : .boolFalse,
|
||||
hidden: nil
|
||||
))
|
||||
|> mapError { _ -> EditForumChannelTopicError in
|
||||
return .generic
|
||||
|
@ -96,7 +96,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
||||
|
||||
if case let .chatSelection(_, selectedChats, additionalCategories, chatListFilters) = mode {
|
||||
placeholder = self.presentationData.strings.ChatListFilter_AddChatsTitle
|
||||
let chatListNode = ChatListNode(context: context, location: .chatList(groupId: .root), previewing: false, fillPreloadItems: false, mode: .peers(filter: [.excludeSecretChats], isSelecting: true, additionalCategories: additionalCategories?.categories ?? [], chatListFilters: chatListFilters), theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true)
|
||||
let chatListNode = ChatListNode(context: context, location: .chatList(groupId: .root), previewing: false, fillPreloadItems: false, mode: .peers(filter: [.excludeSecretChats], isSelecting: true, additionalCategories: additionalCategories?.categories ?? [], chatListFilters: chatListFilters), theme: self.presentationData.theme, fontSize: self.presentationData.listsFontSize, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, nameSortOrder: self.presentationData.nameSortOrder, nameDisplayOrder: self.presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false)
|
||||
if let limit = limit {
|
||||
chatListNode.selectionLimit = limit
|
||||
chatListNode.reachedSelectionLimit = reachedSelectionLimit
|
||||
@ -279,7 +279,7 @@ final class ContactMultiselectionControllerNode: ASDisplayNode {
|
||||
combinedInsets.right += layout.safeInsets.right
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: combinedInsets, headerInsets: headerInsets, duration: duration, curve: curve)
|
||||
chatsNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
|
||||
chatsNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: updateSizeAndInsets.insets.top, inlineNavigationLocation: nil)
|
||||
}
|
||||
self.contentNode.node.frame = CGRect(origin: CGPoint(), size: layout.size)
|
||||
|
||||
|
@ -144,7 +144,7 @@ final class PeerInfoGroupsInCommonPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
||||
}
|
||||
|
||||
func scrollToTop() -> Bool {
|
||||
if !self.listNode.scrollToOffsetFromTop(0.0) {
|
||||
if !self.listNode.scrollToOffsetFromTop(0.0, animated: true) {
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
return true
|
||||
} else {
|
||||
|
@ -224,7 +224,7 @@ final class PeerInfoMembersPaneNode: ASDisplayNode, PeerInfoPaneNode {
|
||||
}
|
||||
|
||||
func scrollToTop() -> Bool {
|
||||
if !self.listNode.scrollToOffsetFromTop(0.0) {
|
||||
if !self.listNode.scrollToOffsetFromTop(0.0, animated: true) {
|
||||
self.listNode.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous, .LowLatency], scrollToItem: ListViewScrollToItem(index: 0, position: .top(0.0), animated: true, curve: .Default(duration: nil), directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
return true
|
||||
} else {
|
||||
|
@ -137,7 +137,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
chatListLocation = .chatList(groupId: .root)
|
||||
}
|
||||
|
||||
self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: .peers(filter: filter, isSelecting: false, additionalCategories: chatListCategories, chatListFilters: nil), theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true)
|
||||
self.chatListNode = ChatListNode(context: context, location: chatListLocation, previewing: false, fillPreloadItems: false, mode: .peers(filter: filter, isSelecting: false, additionalCategories: chatListCategories, chatListFilters: nil), theme: self.presentationData.theme, fontSize: presentationData.listsFontSize, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, nameSortOrder: presentationData.nameSortOrder, nameDisplayOrder: presentationData.nameDisplayOrder, animationCache: self.animationCache, animationRenderer: self.animationRenderer, disableAnimations: true, isInlineMode: false)
|
||||
|
||||
super.init()
|
||||
|
||||
@ -576,7 +576,7 @@ final class PeerSelectionControllerNode: ASDisplayNode {
|
||||
let (duration, curve) = listViewAnimationDurationAndCurve(transition: transition)
|
||||
let updateSizeAndInsets = ListViewUpdateSizeAndInsets(size: layout.size, insets: insets, headerInsets: headerInsets, duration: duration, curve: curve)
|
||||
|
||||
self.chatListNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets)
|
||||
self.chatListNode.updateLayout(transition: transition, updateSizeAndInsets: updateSizeAndInsets, visibleTopInset: updateSizeAndInsets.insets.top, inlineNavigationLocation: nil)
|
||||
|
||||
if let contactListNode = self.contactListNode {
|
||||
contactListNode.bounds = CGRect(x: 0.0, y: 0.0, width: layout.size.width, height: layout.size.height)
|
||||
|
@ -45,6 +45,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
public var inlineStickers: Bool
|
||||
public var localTranscription: Bool
|
||||
public var enableReactionOverrides: Bool
|
||||
public var inlineForums: Bool
|
||||
public var accountReactionEffectOverrides: [AccountReactionOverrides]
|
||||
public var accountStickerEffectOverrides: [AccountReactionOverrides]
|
||||
|
||||
@ -69,6 +70,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
inlineStickers: false,
|
||||
localTranscription: false,
|
||||
enableReactionOverrides: false,
|
||||
inlineForums: false,
|
||||
accountReactionEffectOverrides: [],
|
||||
accountStickerEffectOverrides: []
|
||||
)
|
||||
@ -94,6 +96,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
inlineStickers: Bool,
|
||||
localTranscription: Bool,
|
||||
enableReactionOverrides: Bool,
|
||||
inlineForums: Bool,
|
||||
accountReactionEffectOverrides: [AccountReactionOverrides],
|
||||
accountStickerEffectOverrides: [AccountReactionOverrides]
|
||||
) {
|
||||
@ -116,6 +119,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.inlineStickers = inlineStickers
|
||||
self.localTranscription = localTranscription
|
||||
self.enableReactionOverrides = enableReactionOverrides
|
||||
self.inlineForums = inlineForums
|
||||
self.accountReactionEffectOverrides = accountReactionEffectOverrides
|
||||
self.accountStickerEffectOverrides = accountStickerEffectOverrides
|
||||
}
|
||||
@ -142,6 +146,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
self.inlineStickers = (try container.decodeIfPresent(Int32.self, forKey: "inlineStickers") ?? 0) != 0
|
||||
self.localTranscription = (try container.decodeIfPresent(Int32.self, forKey: "localTranscription") ?? 0) != 0
|
||||
self.enableReactionOverrides = try container.decodeIfPresent(Bool.self, forKey: "enableReactionOverrides") ?? false
|
||||
self.inlineForums = try container.decodeIfPresent(Bool.self, forKey: "inlineForums") ?? false
|
||||
self.accountReactionEffectOverrides = (try? container.decodeIfPresent([AccountReactionOverrides].self, forKey: "accountReactionEffectOverrides")) ?? []
|
||||
self.accountStickerEffectOverrides = (try? container.decodeIfPresent([AccountReactionOverrides].self, forKey: "accountStickerEffectOverrides")) ?? []
|
||||
}
|
||||
@ -168,6 +173,7 @@ public struct ExperimentalUISettings: Codable, Equatable {
|
||||
try container.encode((self.inlineStickers ? 1 : 0) as Int32, forKey: "inlineStickers")
|
||||
try container.encode((self.localTranscription ? 1 : 0) as Int32, forKey: "localTranscription")
|
||||
try container.encode(self.enableReactionOverrides, forKey: "enableReactionOverrides")
|
||||
try container.encode(self.inlineForums, forKey: "inlineForums")
|
||||
try container.encode(self.accountReactionEffectOverrides, forKey: "accountReactionEffectOverrides")
|
||||
try container.encode(self.accountStickerEffectOverrides, forKey: "accountStickerEffectOverrides")
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user