Skip to content
This repository has been archived by the owner on Jun 7, 2020. It is now read-only.

[BUG] Fixed problem with unread marker wrongly positioned #2399

Merged
merged 5 commits into from
Dec 5, 2018
Merged
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
8 changes: 6 additions & 2 deletions Rocket.Chat/Controllers/Chat/MessagesViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,15 @@ final class MessagesViewController: RocketChatViewController {
var emptyStateImageView: UIImageView?
var documentController: UIDocumentInteractionController?

var unmanagedSubscription: UnmanagedSubscription?
var subscription: Subscription! {
didSet {
let sub: Subscription? = subscription
let unmanaged = sub?.unmanaged

viewModel.subscription = sub
viewSubscriptionModel.subscription = sub?.unmanaged
viewSubscriptionModel.subscription = unmanaged
unmanagedSubscription = unmanaged

recoverDraftMessage()
updateEmptyState()
Expand Down Expand Up @@ -161,6 +164,7 @@ final class MessagesViewController: RocketChatViewController {

// Update dataset with the new data normalized
self.updateData(with: self.viewModel.dataNormalized)
self.markAsRead()
}

viewSubscriptionModel.onDataChanged = { [weak self] in
Expand Down Expand Up @@ -389,7 +393,7 @@ final class MessagesViewController: RocketChatViewController {
// MARK: Reading Status

private func markAsRead() {
guard let subscription = viewModel.subscription?.validated()?.unmanaged else { return }
guard let subscription = unmanagedSubscription else { return }
API.current()?.client(SubscriptionsClient.self).markAsRead(subscription: subscription)
}

Expand Down
42 changes: 32 additions & 10 deletions Rocket.Chat/Controllers/Chat/MessagesViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ final class MessagesViewModel {
internal var subscription: Subscription? {
didSet {
guard let subscription = subscription?.validated() else { return }
lastSeen = subscription.lastSeen ?? Date()
lastSeen = lastSeen == nil ? subscription.lastSeen : lastSeen
subscribe(for: subscription)
messagesQuery = subscription.fetchMessagesQueryResults()
messagesQueryToken = messagesQuery?.observe(handleDataUpdates)
Expand All @@ -59,8 +59,9 @@ final class MessagesViewModel {
/**
Last time user read the messages from the Subscription from this view model.
*/
internal var lastSeen = Date()
internal var lastSeen: Date?
internal var hasUnreadMarker = false
internal var unreadMarkerObjectIdentifier: String?

/**
If the view model is requesting new data from the API.
Expand Down Expand Up @@ -391,6 +392,7 @@ final class MessagesViewModel {
self?.cacheDataSorted()
}

self?.markUnreadMarkerIfNeeded()
self?.normalizeDataSorted()

if shouldUpdateUI {
Expand All @@ -415,23 +417,47 @@ final class MessagesViewModel {
}
}

/**
This method will mark the unread marker position
for this subscription state and won't change until
the user leaves the room.
*/
internal func markUnreadMarkerIfNeeded() {
// Unread marker will remain on the same message
// all the time until user closes the screen.
if unreadMarkerObjectIdentifier == nil {
if let lastSeen = lastSeen {
for object in dataSorted.reversed() {
guard let messageSection1 = object.object.base as? MessageSectionModel else { continue }

let message = messageSection1.message
let unreadMarker = !hasUnreadMarker && message.createdAt > lastSeen

if unreadMarker {
unreadMarkerObjectIdentifier = message.identifier
hasUnreadMarker = true
break
}
}
}
}
}

/**
Anything related to the section that refers to sequential messages, day separators
and unread marks is done on this method. A loop in the whole list of messages
is executed on every update to make sure that there's no duplicated separators
and everything looks good to the user on the final result.
*/
internal func normalizeDataSorted() {
hasUnreadMarker = false

let dataSortedMaxIndex = dataSorted.count - 1

for (idx, object) in dataSorted.enumerated() {
guard let messageSection1 = object.object.base as? MessageSectionModel else { continue }

let message = messageSection1.message
let collpsibleItemsState = (object.base as? MessageSection)?.collapsibleItemsState ?? [:]
let unreadMarker = !hasUnreadMarker && message.createdAt > lastSeen

var separator: Date?
var sequential = false
var loader = false
Expand All @@ -449,7 +475,7 @@ final class MessagesViewModel {
message: message,
daySeparator: separator,
sequential: sequential,
unreadIndicator: unreadMarker,
unreadIndicator: unreadMarkerObjectIdentifier == message.identifier,
loader: loader,
header: header
)
Expand All @@ -469,10 +495,6 @@ final class MessagesViewModel {
// Cache the processed result of the message text
// on this loop to avoid doing that in the main thread.
MessageTextCacheManager.shared.message(for: message, with: currentTheme)

if unreadMarker {
hasUnreadMarker = true
}
}

dataNormalized = dataSorted.map({ ArraySection(model: $0, elements: $0.viewModels()) })
Expand Down
11 changes: 9 additions & 2 deletions Rocket.ChatTests/Controllers/MessagesViewModelSpec.swift
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,15 @@ final class MessagesViewModelSpec: XCTestCase {
func testUnreadMarkerPresenceTrue() {
let model = MessagesViewModel()

let first = Message.testInstance()
first.identifier = "message-identifier-1"

let second = Message.testInstance()
second.identifier = "message-identifier-2"

guard
let messageFirst = Message.testInstance().validated()?.unmanaged,
var messageSecond = Message.testInstance().validated()?.unmanaged
let messageFirst = first.validated()?.unmanaged,
var messageSecond = second.validated()?.unmanaged
else {
return XCTFail("messages must be created")
}
Expand All @@ -128,6 +134,7 @@ final class MessagesViewModelSpec: XCTestCase {
model.lastSeen = Date().addingTimeInterval(-500)
model.data = [section1, section2]
model.cacheDataSorted()
model.markUnreadMarkerIfNeeded()
model.normalizeDataSorted()
}

Expand Down