mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-22 22:25:57 +00:00
no message
This commit is contained in:
@@ -4,201 +4,13 @@ import SwiftSignalKit
|
||||
import Display
|
||||
import TelegramCore
|
||||
|
||||
enum ChatListMessageViewPosition: Equatable {
|
||||
case Tail(count: Int)
|
||||
case Around(index: MessageIndex, anchorIndex: MessageIndex, scrollPosition: ListViewScrollPosition?)
|
||||
}
|
||||
|
||||
func ==(lhs: ChatListMessageViewPosition, rhs: ChatListMessageViewPosition) -> Bool {
|
||||
switch lhs {
|
||||
case let .Tail(lhsCount):
|
||||
switch rhs {
|
||||
case let .Tail(rhsCount) where lhsCount == rhsCount:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case let .Around(lhsId, lhsAnchorIndex, lhsScrollPosition):
|
||||
switch rhs {
|
||||
case let .Around(rhsId, rhsAnchorIndex, rhsScrollPosition) where lhsId == rhsId && lhsAnchorIndex == rhsAnchorIndex && lhsScrollPosition == rhsScrollPosition:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum ChatListControllerEntryId: Hashable, CustomStringConvertible {
|
||||
case Search
|
||||
case Hole(Int64)
|
||||
case PeerId(Int64)
|
||||
|
||||
var hashValue: Int {
|
||||
switch self {
|
||||
case .Search:
|
||||
return 0
|
||||
case let .Hole(peerId):
|
||||
return peerId.hashValue
|
||||
case let .PeerId(peerId):
|
||||
return peerId.hashValue
|
||||
}
|
||||
}
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .Search:
|
||||
return "search"
|
||||
case let .Hole(value):
|
||||
return "hole(\(value))"
|
||||
case let .PeerId(value):
|
||||
return "peerId(\(value))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func <(lhs: ChatListControllerEntryId, rhs: ChatListControllerEntryId) -> Bool {
|
||||
return lhs.hashValue < rhs.hashValue
|
||||
}
|
||||
|
||||
private func ==(lhs: ChatListControllerEntryId, rhs: ChatListControllerEntryId) -> Bool {
|
||||
switch lhs {
|
||||
case .Search:
|
||||
switch rhs {
|
||||
case .Search:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case let .Hole(lhsId):
|
||||
switch rhs {
|
||||
case .Hole(lhsId):
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case let .PeerId(lhsId):
|
||||
switch rhs {
|
||||
case let .PeerId(rhsId):
|
||||
return lhsId == rhsId
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum ChatListControllerEntry: Comparable, Identifiable {
|
||||
case SearchEntry
|
||||
case MessageEntry(Message, CombinedPeerReadState?, PeerNotificationSettings?)
|
||||
case HoleEntry(ChatListHole)
|
||||
case Nothing(MessageIndex)
|
||||
|
||||
var index: MessageIndex {
|
||||
switch self {
|
||||
case .SearchEntry:
|
||||
return MessageIndex.absoluteUpperBound()
|
||||
case let .MessageEntry(message, _, _):
|
||||
return MessageIndex(message)
|
||||
case let .HoleEntry(hole):
|
||||
return hole.index
|
||||
case let .Nothing(index):
|
||||
return index
|
||||
}
|
||||
}
|
||||
|
||||
var stableId: ChatListControllerEntryId {
|
||||
switch self {
|
||||
case .SearchEntry:
|
||||
return .Search
|
||||
case let .MessageEntry(message, _, _):
|
||||
return .PeerId(message.id.peerId.toInt64())
|
||||
case let .HoleEntry(hole):
|
||||
return .Hole(Int64(hole.index.id.id))
|
||||
case let .Nothing(index):
|
||||
return .PeerId(index.id.peerId.toInt64())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func <(lhs: ChatListControllerEntry, rhs: ChatListControllerEntry) -> Bool {
|
||||
return lhs.index < rhs.index
|
||||
}
|
||||
|
||||
private func ==(lhs: ChatListControllerEntry, rhs: ChatListControllerEntry) -> Bool {
|
||||
switch lhs {
|
||||
case .SearchEntry:
|
||||
switch rhs {
|
||||
case .SearchEntry:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case let .MessageEntry(lhsMessage, lhsUnreadCount, lhsNotificationSettings):
|
||||
switch rhs {
|
||||
case let .MessageEntry(rhsMessage, rhsUnreadCount, rhsNotificationSettings):
|
||||
if lhsMessage.id != rhsMessage.id || lhsMessage.flags != rhsMessage.flags || lhsUnreadCount != rhsUnreadCount {
|
||||
return false
|
||||
}
|
||||
if let lhsNotificationSettings = lhsNotificationSettings, let rhsNotificationSettings = rhsNotificationSettings {
|
||||
if !lhsNotificationSettings.isEqual(to: rhsNotificationSettings) {
|
||||
return false
|
||||
}
|
||||
} else if (lhsNotificationSettings != nil) != (rhsNotificationSettings != nil) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
default:
|
||||
break
|
||||
}
|
||||
case let .HoleEntry(lhsHole):
|
||||
switch rhs {
|
||||
case let .HoleEntry(rhsHole):
|
||||
return lhsHole == rhsHole
|
||||
default:
|
||||
return false
|
||||
}
|
||||
case let .Nothing(lhsIndex):
|
||||
switch rhs {
|
||||
case let .Nothing(rhsIndex):
|
||||
return lhsIndex == rhsIndex
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
extension ChatListEntry: Identifiable {
|
||||
public var stableId: Int64 {
|
||||
return self.index.id.peerId.toInt64()
|
||||
}
|
||||
}
|
||||
|
||||
private final class ChatListOpaqueTransactionState {
|
||||
let chatListViewAndEntries: (ChatListView, [ChatListControllerEntry])
|
||||
|
||||
init(chatListViewAndEntries: (ChatListView, [ChatListControllerEntry])) {
|
||||
self.chatListViewAndEntries = chatListViewAndEntries
|
||||
}
|
||||
}
|
||||
|
||||
public class ChatListController: ViewController {
|
||||
let account: Account
|
||||
|
||||
private var chatListViewAndEntries: (ChatListView, [ChatListControllerEntry])?
|
||||
|
||||
var chatListPosition: ChatListMessageViewPosition?
|
||||
let chatListDisposable: MetaDisposable = MetaDisposable()
|
||||
|
||||
let messageViewQueue = Queue()
|
||||
let messageViewTransactionQueue = ListViewTransactionQueue()
|
||||
var settingView = false
|
||||
private let account: Account
|
||||
|
||||
let openMessageFromSearchDisposable: MetaDisposable = MetaDisposable()
|
||||
|
||||
var chatListDisplayNode: ChatListControllerNode {
|
||||
get {
|
||||
return super.displayNode as! ChatListControllerNode
|
||||
}
|
||||
private var chatListDisplayNode: ChatListControllerNode {
|
||||
return super.displayNode as! ChatListControllerNode
|
||||
}
|
||||
|
||||
public init(account: Account) {
|
||||
@@ -216,15 +28,9 @@ public class ChatListController: ViewController {
|
||||
|
||||
self.scrollToTop = { [weak self] in
|
||||
if let strongSelf = self {
|
||||
if let (view, _) = strongSelf.chatListViewAndEntries, view.laterIndex == nil {
|
||||
strongSelf.chatListDisplayNode.listView.transaction(deleteIndices: [], insertIndicesAndItems: [], updateIndicesAndItems: [], options: [.Synchronous], scrollToItem: ListViewScrollToItem(index: 0, position: .Top, animated: true, curve: .Default, directionHint: .Up), updateSizeAndInsets: nil, stationaryItemRange: nil, updateOpaqueState: nil, completion: { _ in })
|
||||
} else {
|
||||
strongSelf.setMessageViewPosition(.Around(index: MessageIndex.absoluteUpperBound(), anchorIndex: MessageIndex.absoluteUpperBound(), scrollPosition: .Top), hint: "later", force: true)
|
||||
}
|
||||
strongSelf.chatListDisplayNode.chatListNode.scrollToLatest()
|
||||
}
|
||||
}
|
||||
|
||||
self.setMessageViewPosition(.Tail(count: 50), hint: "initial", force: false)
|
||||
}
|
||||
|
||||
required public init(coder aDecoder: NSCoder) {
|
||||
@@ -232,31 +38,29 @@ public class ChatListController: ViewController {
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.chatListDisposable.dispose()
|
||||
self.openMessageFromSearchDisposable.dispose()
|
||||
}
|
||||
|
||||
override public func loadDisplayNode() {
|
||||
self.displayNode = ChatListControllerNode(account: self.account)
|
||||
|
||||
self.chatListDisplayNode.listView.displayedItemRangeChanged = { [weak self] range, transactionOpaqueState in
|
||||
if let strongSelf = self, !strongSelf.settingView {
|
||||
if let range = range.loadedRange, let (view, _) = (transactionOpaqueState as? ChatListOpaqueTransactionState)?.chatListViewAndEntries {
|
||||
if range.firstIndex < 5 && view.laterIndex != nil {
|
||||
strongSelf.setMessageViewPosition(.Around(index: view.entries[view.entries.count - 1].index, anchorIndex: MessageIndex.absoluteUpperBound(), scrollPosition: nil), hint: "later", force: false)
|
||||
} else if range.firstIndex >= 5 && range.lastIndex >= view.entries.count - 5 && view.earlierIndex != nil {
|
||||
strongSelf.setMessageViewPosition(.Around(index: view.entries[0].index, anchorIndex: MessageIndex.absoluteUpperBound(), scrollPosition: nil), hint: "earlier", force: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.navigationBar = self.navigationBar
|
||||
|
||||
self.chatListDisplayNode.requestDeactivateSearch = { [weak self] in
|
||||
self?.deactivateSearch()
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.chatListNode.activateSearch = { [weak self] in
|
||||
self?.activateSearch()
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.chatListNode.peerSelected = { [weak self] peerId in
|
||||
if let strongSelf = self {
|
||||
(strongSelf.navigationController as? NavigationController)?.pushViewController(ChatController(account: strongSelf.account, peerId: peerId))
|
||||
strongSelf.chatListDisplayNode.chatListNode.clearHighlightAnimated(true)
|
||||
}
|
||||
}
|
||||
|
||||
self.chatListDisplayNode.requestOpenMessageFromSearch = { [weak self] peer, messageId in
|
||||
if let strongSelf = self {
|
||||
let storedPeer = strongSelf.account.postbox.modify { modifier -> Void in
|
||||
@@ -283,56 +87,6 @@ public class ChatListController: ViewController {
|
||||
self.displayNodeDidLoad()
|
||||
}
|
||||
|
||||
private func setMessageViewPosition(_ position: ChatListMessageViewPosition, hint: String, force: Bool) {
|
||||
if self.chatListPosition == nil || self.chatListPosition! != position || force {
|
||||
let signal: Signal<(ChatListView, ViewUpdateType), NoError>
|
||||
self.chatListPosition = position
|
||||
var scrollPosition: (MessageIndex, ListViewScrollPosition, ListViewScrollToItemDirectionHint)?
|
||||
switch position {
|
||||
case let .Tail(count):
|
||||
signal = self.account.postbox.tailChatListView(count)
|
||||
case let .Around(index, _, position):
|
||||
trace("request around \(index.id.id) \(hint)")
|
||||
signal = self.account.postbox.aroundChatListView(index, count: 80)
|
||||
if let position = position {
|
||||
var directionHint: ListViewScrollToItemDirectionHint = .Up
|
||||
if let visibleItemRange = self.chatListDisplayNode.listView.displayedItemRange.loadedRange, let (_, entries) = self.chatListViewAndEntries {
|
||||
if visibleItemRange.firstIndex >= 0 && visibleItemRange.firstIndex < entries.count {
|
||||
if entries[visibleItemRange.firstIndex].index < index {
|
||||
directionHint = .Up
|
||||
} else {
|
||||
directionHint = .Down
|
||||
}
|
||||
}
|
||||
}
|
||||
scrollPosition = (index, position, directionHint)
|
||||
}
|
||||
}
|
||||
|
||||
var firstTime = true
|
||||
chatListDisposable.set((
|
||||
signal |> deliverOnMainQueue
|
||||
).start(next: {[weak self] (view, updateType) in
|
||||
if let strongSelf = self {
|
||||
let animated: Bool
|
||||
switch updateType {
|
||||
case .Generic:
|
||||
animated = !firstTime
|
||||
case .FillHole:
|
||||
animated = false
|
||||
case .InitialUnread:
|
||||
animated = false
|
||||
case .UpdateVisible:
|
||||
animated = false
|
||||
}
|
||||
|
||||
strongSelf.setPeerView(view, firstTime: strongSelf.chatListViewAndEntries == nil, scrollPosition: firstTime ? scrollPosition : nil, animated: animated)
|
||||
firstTime = false
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
override public func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(animated)
|
||||
}
|
||||
@@ -341,179 +95,6 @@ public class ChatListController: ViewController {
|
||||
super.viewDidDisappear(animated)
|
||||
}
|
||||
|
||||
private func chatListControllerEntries(_ view: ChatListView) -> [ChatListControllerEntry] {
|
||||
var result: [ChatListControllerEntry] = []
|
||||
for entry in view.entries {
|
||||
switch entry {
|
||||
case let .MessageEntry(message, combinedReadState, notificationSettings):
|
||||
result.append(.MessageEntry(message, combinedReadState, notificationSettings))
|
||||
case let .HoleEntry(hole):
|
||||
result.append(.HoleEntry(hole))
|
||||
case let .Nothing(index):
|
||||
result.append(.Nothing(index))
|
||||
}
|
||||
}
|
||||
if view.laterIndex == nil {
|
||||
result.append(.SearchEntry)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private func setPeerView(_ view: ChatListView, firstTime: Bool, scrollPosition: (MessageIndex, ListViewScrollPosition, ListViewScrollToItemDirectionHint)?, animated: Bool) {
|
||||
self.messageViewTransactionQueue.addTransaction { [weak self] completed in
|
||||
if let strongSelf = self {
|
||||
strongSelf.settingView = true
|
||||
let currentEntries = strongSelf.chatListViewAndEntries?.1 ?? []
|
||||
let viewEntries = strongSelf.chatListControllerEntries(view)
|
||||
|
||||
strongSelf.messageViewQueue.async {
|
||||
let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: currentEntries, rightList: viewEntries)
|
||||
//let (deleteIndices, indicesAndItems) = mergeListsStable(leftList: currentEntries, rightList: viewEntries)
|
||||
//let updateIndices: [(Int, ChatListControllerEntry)] = []
|
||||
|
||||
Queue.mainQueue().async {
|
||||
var adjustedDeleteIndices: [ListViewDeleteItem] = []
|
||||
let previousCount = currentEntries.count
|
||||
if deleteIndices.count != 0 {
|
||||
for index in deleteIndices {
|
||||
adjustedDeleteIndices.append(ListViewDeleteItem(index: previousCount - 1 - index, directionHint: nil))
|
||||
}
|
||||
}
|
||||
|
||||
let updatedCount = viewEntries.count
|
||||
|
||||
var maxAnimatedInsertionIndex = -1
|
||||
if animated {
|
||||
for (index, _, _) in indicesAndItems.sorted(by: { $0.0 > $1.0 }) {
|
||||
let adjustedIndex = updatedCount - 1 - index
|
||||
if adjustedIndex == maxAnimatedInsertionIndex + 1 {
|
||||
maxAnimatedInsertionIndex += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var adjustedIndicesAndItems: [ListViewInsertItem] = []
|
||||
for (index, entry, previousIndex) in indicesAndItems {
|
||||
let adjustedIndex = updatedCount - 1 - index
|
||||
|
||||
var adjustedPreviousIndex: Int?
|
||||
if let previousIndex = previousIndex {
|
||||
adjustedPreviousIndex = previousCount - 1 - previousIndex
|
||||
}
|
||||
|
||||
var directionHint: ListViewItemOperationDirectionHint?
|
||||
if maxAnimatedInsertionIndex >= 0 && adjustedIndex <= maxAnimatedInsertionIndex {
|
||||
directionHint = .Down
|
||||
}
|
||||
|
||||
switch entry {
|
||||
case .SearchEntry:
|
||||
adjustedIndicesAndItems.append(ListViewInsertItem(index: updatedCount - 1 - index, previousIndex: adjustedPreviousIndex, item: ChatListSearchItem(placeholder: "Search for messages or users", activate: { [weak self] in
|
||||
self?.activateSearch()
|
||||
}), directionHint: directionHint))
|
||||
case let .MessageEntry(message, combinedReadState, notificationSettings):
|
||||
adjustedIndicesAndItems.append(ListViewInsertItem(index: adjustedIndex, previousIndex: adjustedPreviousIndex, item: ChatListItem(account: strongSelf.account, message: message, combinedReadState: combinedReadState, notificationSettings: notificationSettings, action: { [weak self] message in
|
||||
if let strongSelf = self {
|
||||
strongSelf.entrySelected(entry)
|
||||
strongSelf.chatListDisplayNode.listView.clearHighlightAnimated(true)
|
||||
}
|
||||
}), directionHint: directionHint))
|
||||
case .HoleEntry:
|
||||
adjustedIndicesAndItems.append(ListViewInsertItem(index: updatedCount - 1 - index, previousIndex: adjustedPreviousIndex, item: ChatListHoleItem(), directionHint: directionHint))
|
||||
case .Nothing:
|
||||
adjustedIndicesAndItems.append(ListViewInsertItem(index: updatedCount - 1 - index, previousIndex: adjustedPreviousIndex, item: ChatListEmptyItem(), directionHint: directionHint))
|
||||
}
|
||||
}
|
||||
|
||||
var adjustedUpdateItems: [ListViewUpdateItem] = []
|
||||
for (index, entry, previousIndex) in updateIndices {
|
||||
let adjustedIndex = updatedCount - 1 - index
|
||||
let adjustedPreviousIndex = previousCount - 1 - previousIndex
|
||||
|
||||
let directionHint: ListViewItemOperationDirectionHint? = nil
|
||||
|
||||
switch entry {
|
||||
case .SearchEntry:
|
||||
adjustedUpdateItems.append(ListViewUpdateItem(index: adjustedIndex, previousIndex: adjustedPreviousIndex, item: ChatListSearchItem(placeholder: "Search for messages or users", activate: { [weak self] in
|
||||
self?.activateSearch()
|
||||
}), directionHint: directionHint))
|
||||
case let .MessageEntry(message, combinedReadState, notificationSettings):
|
||||
adjustedUpdateItems.append(ListViewUpdateItem(index: adjustedIndex, previousIndex: adjustedPreviousIndex, item: ChatListItem(account: strongSelf.account, message: message, combinedReadState: combinedReadState, notificationSettings: notificationSettings, action: { [weak self] message in
|
||||
if let strongSelf = self {
|
||||
strongSelf.entrySelected(entry)
|
||||
strongSelf.chatListDisplayNode.listView.clearHighlightAnimated(true)
|
||||
}
|
||||
}), directionHint: directionHint))
|
||||
case .HoleEntry:
|
||||
adjustedUpdateItems.append(ListViewUpdateItem(index: adjustedIndex, previousIndex: adjustedPreviousIndex, item: ChatListHoleItem(), directionHint: directionHint))
|
||||
case .Nothing:
|
||||
adjustedUpdateItems.append(ListViewUpdateItem(index: adjustedIndex, previousIndex: adjustedPreviousIndex, item: ChatListEmptyItem(), directionHint: directionHint))
|
||||
}
|
||||
}
|
||||
|
||||
if !adjustedDeleteIndices.isEmpty || !adjustedIndicesAndItems.isEmpty || !adjustedUpdateItems.isEmpty || scrollPosition != nil {
|
||||
var options: ListViewDeleteAndInsertOptions = []
|
||||
if firstTime {
|
||||
} else {
|
||||
let _ = options.insert(.AnimateAlpha)
|
||||
|
||||
if animated {
|
||||
let _ = options.insert(.AnimateInsertion)
|
||||
}
|
||||
}
|
||||
|
||||
var scrollToItem: ListViewScrollToItem?
|
||||
if let (itemIndex, itemPosition, directionHint) = scrollPosition {
|
||||
var index = viewEntries.count - 1
|
||||
for entry in viewEntries {
|
||||
if entry.index >= itemIndex {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: itemPosition, animated: true, curve: .Default, directionHint: directionHint)
|
||||
break
|
||||
}
|
||||
index -= 1
|
||||
}
|
||||
|
||||
if scrollToItem == nil {
|
||||
var index = 0
|
||||
for entry in viewEntries.reversed() {
|
||||
if entry.index < itemIndex {
|
||||
scrollToItem = ListViewScrollToItem(index: index, position: itemPosition, animated: true, curve: .Default, directionHint: directionHint)
|
||||
break
|
||||
}
|
||||
index += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
strongSelf.chatListDisplayNode.listView.transaction(deleteIndices: adjustedDeleteIndices, insertIndicesAndItems: adjustedIndicesAndItems, updateIndicesAndItems: adjustedUpdateItems, options: options, scrollToItem: scrollToItem, updateOpaqueState: ChatListOpaqueTransactionState(chatListViewAndEntries: (view, viewEntries)), completion: { [weak self] _ in
|
||||
if let strongSelf = self {
|
||||
strongSelf.ready.set(single(true, NoError.self))
|
||||
strongSelf.settingView = false
|
||||
completed()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
strongSelf.ready.set(single(true, NoError.self))
|
||||
strongSelf.settingView = false
|
||||
completed()
|
||||
}
|
||||
|
||||
strongSelf.chatListViewAndEntries = (view, viewEntries)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func entrySelected(_ entry: ChatListControllerEntry) {
|
||||
if case let .MessageEntry(message, _, _) = entry {
|
||||
//(self.navigationController as? NavigationController)?.pushViewController(PeerMediaCollectionController(account: self.account, peerId: message.id.peerId))
|
||||
(self.navigationController as? NavigationController)?.pushViewController(ChatController(account: self.account, peerId: message.id.peerId))
|
||||
}
|
||||
}
|
||||
|
||||
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||
super.containerLayoutUpdated(layout, transition: transition)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user