Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,19 @@
import Foundation

class MessagesPreloaderHelper {

class var sharedInstance : MessagesPreloaderHelper {
struct Static {
static let instance = MessagesPreloaderHelper()
}
return Static.instance
}

struct ScrollState {
var firstRowId: Int
var difference: CGFloat
var isAtBottom: Bool

init(
firstRowId: Int,
difference: CGFloat,
Expand All @@ -32,11 +32,11 @@ class MessagesPreloaderHelper {
self.isAtBottom = isAtBottom
}
}

struct PreloadedMessagesState {
var messageCellStates: [MessageTableCellState]
var resultsControllerCount: Int

init(
messageCellStates: [MessageTableCellState],
resultsControllerCount: Int
Expand All @@ -45,37 +45,46 @@ class MessagesPreloaderHelper {
self.resultsControllerCount = resultsControllerCount
}
}


private let maxPreloadedChats = 5
private var preloadedChatOrder: [Int] = []

func releaseMemory() {
tribesData = [:]
linksData = [:]
chatMessages = [:]
}

var chatMessages: [Int: PreloadedMessagesState] = [:]
var chatScrollState: [Int: ScrollState] = [:]

var tribesData: [String: MessageTableCellState.TribeData] = [:]
var linksData: [String: MessageTableCellState.LinkData] = [:]

func add(
messageStateArray: [MessageTableCellState],
resultsControllerCount: Int,
for chatId: Int
) {
guard !messageStateArray.isEmpty else {
return
}

self.chatMessages[chatId] = PreloadedMessagesState(
messageCellStates: messageStateArray,
resultsControllerCount: resultsControllerCount
)

markChatAsRecentlyPreloaded(chatId)
trimPreloadedChatsIfNeeded()
}

func getPreloadedMessagesState(for chatId: Int) -> PreloadedMessagesState? {
if let preloadedMessagesState = chatMessages[chatId], preloadedMessagesState.messageCellStates.count > 0 {
return preloadedMessagesState
}
return nil
}

func save(
firstRowId: Int,
difference: CGFloat,
Expand All @@ -88,13 +97,15 @@ class MessagesPreloaderHelper {
isAtBottom: isAtBottom
)
}

func reset(
for chatId: Int
) {
self.chatScrollState.removeValue(forKey: chatId)
self.chatMessages.removeValue(forKey: chatId)
self.preloadedChatOrder.removeAll(where: { $0 == chatId })
}

func getScrollState(
for chatId: Int,
pinnedMessageId: Int? = nil
Expand All @@ -111,4 +122,17 @@ class MessagesPreloaderHelper {
}
return nil
}

private func markChatAsRecentlyPreloaded(_ chatId: Int) {
preloadedChatOrder.removeAll(where: { $0 == chatId })
preloadedChatOrder.append(chatId)
}

private func trimPreloadedChatsIfNeeded() {
while preloadedChatOrder.count > maxPreloadedChats {
let oldestChatId = preloadedChatOrder.removeFirst()
chatMessages.removeValue(forKey: oldestChatId)
chatScrollState.removeValue(forKey: oldestChatId)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,97 +9,63 @@
import Foundation

extension NewChatTableDataSource {
// func preloadDataForItems() {
// for index in stride(from: messageTableCellStateArray.count - 1, through: 0, by: -1) {
// let item = messageTableCellStateArray[index]
//
// if let messageId = item.message?.id {
// DispatchQueue.global(qos: .userInteractive).async {
// self.preloadDataFor(
// rowIndex: index,
// messageId: messageId
// )
// }
// }
// }
// }
//
// func preloadDataFor(
// rowIndex: Int,
// messageId: Int
// ) {
// if let tableCellState = getTableCellStateFor(
// messageId: messageId,
// and: rowIndex
// ) {
// var mutableCellState = tableCellState
//
// if let link = mutableCellState.1.webLink?.link {
// let linkData = preloaderHelper.linksData[link]
//
// if linkData == nil {
//// loadLinkDataFor(
//// messageId: messageId,
//// and: rowIndex
//// )
// }
// }
// }
// }


private static let maxPreloadedRows = 200

@objc func restorePreloadedOrLoadMessages() {
// guard let chat = chat else {
// return
// }
//
// if let preloadedMessagesState = preloaderHelper.getPreloadedMessagesState(for: chat.id) {
// messageTableCellStateArray = preloadedMessagesState.messageCellStates
// updatePreloadedSnapshot()
//
// DelayPerformedHelper.performAfterDelay(seconds: 0.5, completion: { [weak self] in
// guard let self = self else { return }
// self.configureResultsController(
// items: max(self.dataSource.snapshot().numberOfItems, preloadedMessagesState.resultsControllerCount)
// )
// fetchMoreItems()
// })
// } else {
guard let chat = chat else {
return
}

if let preloadedMessagesState = preloaderHelper.getPreloadedMessagesState(for: chat.id) {
messageTableCellStateArray = preloadedMessagesState.messageCellStates
updatePreloadedSnapshot()

DelayPerformedHelper.performAfterDelay(seconds: 0.2, completion: { [weak self] in
guard let self = self else { return }
self.configureResultsController(
items: max(self.dataSource.snapshot().numberOfItems, preloadedMessagesState.resultsControllerCount)
)
})
} else {
configureResultsController(items: max(dataSource.snapshot().numberOfItems, 100))
// }
}
}

@objc func saveMessagesToPreloader() {
// let collectionViewOffsetY = collectionViewScroll.documentYOffset + collectionViewScroll.contentInsets.top
// let firstVisibleItem = collectionView.indexPathForItem(at: NSPoint(x: 0, y: collectionViewOffsetY))?.item ?? 0
//
// guard let chat = chat, collectionView.numberOfSections > 0 && firstVisibleItem > 0 else {
// return
// }
//
// let numberOfItems = collectionView.numberOfItems(inSection: 0)
//
// preloaderHelper.add(
// messageStateArray: messageTableCellStateArray.endSubarray(size: (numberOfItems - firstVisibleItem) + 10),
// resultsControllerCount: messagesCount,
// for: chat.id
// )
let collectionViewOffsetY = collectionViewScroll.documentYOffset + collectionViewScroll.contentInsets.top
let firstVisibleItem = collectionView.indexPathForItem(at: NSPoint(x: 0, y: collectionViewOffsetY))?.item ?? 0

guard let chat = chat, collectionView.numberOfSections > 0 else {
return
}

let numberOfItems = collectionView.numberOfItems(inSection: 0)
let rowsFromVisibleItem = max(numberOfItems - firstVisibleItem, 0) + 10
let rowsToPreload = min(rowsFromVisibleItem, Self.maxPreloadedRows)

preloaderHelper.add(
messageStateArray: messageTableCellStateArray.endSubarray(size: rowsToPreload),
resultsControllerCount: messagesCount,
for: chat.id
)
}

@objc func saveSnapshotCurrentState() {
saveScrollPosition()
// saveMessagesToPreloader()
saveMessagesToPreloader()
}

func deleteSnapshotCurrentState() {
guard let chatId = chat?.id else {
return
}

self.preloaderHelper.reset(
for: chatId
)
}

@objc func restoreScrollLastPosition() {
guard let chatId = chat?.id else { return }

Expand All @@ -110,7 +76,7 @@ extension NewChatTableDataSource {

///Find index of stored first visible item
if let index = messageTableCellStateArray.firstIndex(where: { $0.getUniqueIdentifier() == scrollState.firstRowId}) {

let numberOfItems = collectionView.numberOfItems(inSection: 0)
guard index < numberOfItems else {
return
Expand All @@ -133,47 +99,47 @@ extension NewChatTableDataSource {
return
}
}

///Scroll to bottom if it didn't scroll to spefici position
let collectionViewContentSize = collectionView.collectionViewLayout?.collectionViewContentSize.height ?? 0
let offset = collectionViewContentSize - collectionViewScroll.frame.height + collectionViewScroll.contentInsets.top
scrollViewDesiredOffset = offset
collectionViewScroll.documentYOffset = offset

if scrolledAtBottom {
return
}

scrolledAtBottom = true

delegate?.didScrollToBottom()
scrollViewDidScroll()
}

func saveScrollPosition() {
guard let _ = collectionView.enclosingScrollView else { return }
if collectionView.alphaValue == 0 { return }

guard let chatId = chat?.id else {
return
}

let collectionViewOffsetY = collectionViewScroll.documentYOffset + collectionViewScroll.contentInsets.top

///Find first visible item
if let firstVisibleRow = collectionView.indexPathForItem(at: NSPoint(x: 0, y: collectionViewOffsetY))?.item {
///Find first visible item y position
if var firstVisibleRowY = collectionView.item(at: firstVisibleRow)?.view.frame.origin.y {
///Find unique identifier for first visible item
var firstVisibleItem = dataSource.snapshot().itemIdentifiers[firstVisibleRow]
var firstRowId = dataSource.snapshot().itemIdentifiers[firstVisibleRow].getUniqueIdentifier()

///If first visible row is date separator, then take next since date separator can change its position based on number of items displayed
if !firstVisibleItem.isMessageRow && dataSource.snapshot().itemIdentifiers.count > firstVisibleRow + 1 {
firstRowId = dataSource.snapshot().itemIdentifiers[firstVisibleRow + 1].getUniqueIdentifier()
firstVisibleRowY = collectionView.item(at: firstVisibleRow + 1)?.view.frame.origin.y ?? 0
}

///Save scroll position based on visible item and offset
self.preloaderHelper.save(
firstRowId: firstRowId,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class NewChatViewController: DashboardSplittedViewController {
override func viewWillDisappear() {
super.viewWillDisappear()

chatTableDataSource?.deleteSnapshotCurrentState()
chatTableDataSource?.saveSnapshotCurrentState()
chatTableDataSource?.releaseMemory()

closeThreadAndResetEscapeMonitor()
Expand Down