mirror of
https://github.com/status-im/status-desktop.git
synced 2025-02-22 19:48:52 +00:00
fix(chat): better integrate new messages marker with loading state
- new messages marker is reevaluated only if chat has unviewed messages - loading state is reevaluated only when chat is made active, this fixes case described here: https://github.com/status-im/status-desktop/pull/10151#discussion_r1158702638 fixes: #10275
This commit is contained in:
parent
1e2e7075d5
commit
89efb1cd71
@ -153,6 +153,9 @@ method resendChatMessage*(self: AccessInterface, messageId: string): string =
|
|||||||
method resetNewMessagesMarker*(self: AccessInterface) =
|
method resetNewMessagesMarker*(self: AccessInterface) =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method removeNewMessagesMarker*(self: AccessInterface) =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method resetAndScrollToNewMessagesMarker*(self: AccessInterface) =
|
method resetAndScrollToNewMessagesMarker*(self: AccessInterface) =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
@ -164,3 +167,9 @@ method updateCommunityDetails*(self: AccessInterface, community: CommunityDto) =
|
|||||||
|
|
||||||
method onFirstUnseenMessageLoaded*(self: AccessInterface, messageId: string) =
|
method onFirstUnseenMessageLoaded*(self: AccessInterface, messageId: string) =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method isFirstUnseenMessageInitialized*(self: AccessInterface): bool =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method reevaluateViewLoadingState*(self: AccessInterface) =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
@ -25,6 +25,12 @@ const CHAT_IDENTIFIER_CLOCK = -2
|
|||||||
const FETCH_MORE_MESSAGES_MESSAGE_ID = "fetch-more_messages-message-id"
|
const FETCH_MORE_MESSAGES_MESSAGE_ID = "fetch-more_messages-message-id"
|
||||||
const FETCH_MORE_MESSAGES_CLOCK = -1
|
const FETCH_MORE_MESSAGES_CLOCK = -1
|
||||||
|
|
||||||
|
type
|
||||||
|
FirstUnseenMessageState = tuple
|
||||||
|
initialized: bool
|
||||||
|
fetching: bool
|
||||||
|
scrollToWhenFetched: bool
|
||||||
|
|
||||||
type
|
type
|
||||||
Module* = ref object of io_interface.AccessInterface
|
Module* = ref object of io_interface.AccessInterface
|
||||||
delegate: delegate_interface.AccessInterface
|
delegate: delegate_interface.AccessInterface
|
||||||
@ -32,7 +38,8 @@ type
|
|||||||
viewVariant: QVariant
|
viewVariant: QVariant
|
||||||
controller: Controller
|
controller: Controller
|
||||||
moduleLoaded: bool
|
moduleLoaded: bool
|
||||||
scrollToFirstUnseenMessageWhenLoaded: bool
|
initialMessagesLoaded: bool
|
||||||
|
firstUnseenMessageState: FirstUnseenMessageState
|
||||||
|
|
||||||
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
|
proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitter, sectionId: string, chatId: string,
|
||||||
belongsToCommunity: bool, contactService: contact_service.Service, communityService: community_service.Service,
|
belongsToCommunity: bool, contactService: contact_service.Service, communityService: community_service.Service,
|
||||||
@ -45,7 +52,8 @@ proc newModule*(delegate: delegate_interface.AccessInterface, events: EventEmitt
|
|||||||
result.controller = controller.newController(result, events, sectionId, chatId, belongsToCommunity, contactService,
|
result.controller = controller.newController(result, events, sectionId, chatId, belongsToCommunity, contactService,
|
||||||
communityService, chatService, messageService, mailserversService)
|
communityService, chatService, messageService, mailserversService)
|
||||||
result.moduleLoaded = false
|
result.moduleLoaded = false
|
||||||
result.scrollToFirstUnseenMessageWhenLoaded = true
|
result.initialMessagesLoaded = false
|
||||||
|
result.firstUnseenMessageState = (false, false, false)
|
||||||
|
|
||||||
# Forward declaration
|
# Forward declaration
|
||||||
proc createChatIdentifierItem(self: Module): Item
|
proc createChatIdentifierItem(self: Module): Item
|
||||||
@ -208,6 +216,11 @@ method currentUserWalletContainsAddress(self: Module, address: string): bool =
|
|||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
method reevaluateViewLoadingState*(self: Module) =
|
||||||
|
self.view.setLoading(not self.initialMessagesLoaded or
|
||||||
|
not self.firstUnseenMessageState.initialized or
|
||||||
|
self.firstUnseenMessageState.fetching)
|
||||||
|
|
||||||
method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto],
|
method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto],
|
||||||
pinnedMessages: seq[PinnedMessageDto]) =
|
pinnedMessages: seq[PinnedMessageDto]) =
|
||||||
var viewItems: seq[Item]
|
var viewItems: seq[Item]
|
||||||
@ -341,7 +354,8 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
|
|||||||
# check if this loading was caused by the click on a messages from the app search result
|
# check if this loading was caused by the click on a messages from the app search result
|
||||||
self.checkIfMessageLoadedAndScrollToItIfItIs()
|
self.checkIfMessageLoadedAndScrollToItIfItIs()
|
||||||
|
|
||||||
self.view.initialMessagesAreLoaded()
|
self.initialMessagesLoaded = true
|
||||||
|
self.reevaluateViewLoadingState()
|
||||||
|
|
||||||
method messagesAdded*(self: Module, messages: seq[MessageDto]) =
|
method messagesAdded*(self: Module, messages: seq[MessageDto]) =
|
||||||
var items: seq[Item]
|
var items: seq[Item]
|
||||||
@ -684,13 +698,13 @@ method resendChatMessage*(self: Module, messageId: string): string =
|
|||||||
return self.controller.resendChatMessage(messageId)
|
return self.controller.resendChatMessage(messageId)
|
||||||
|
|
||||||
method resetNewMessagesMarker*(self: Module) =
|
method resetNewMessagesMarker*(self: Module) =
|
||||||
self.scrollToFirstUnseenMessageWhenLoaded = false
|
self.firstUnseenMessageState.fetching = true
|
||||||
self.view.setFirstUnseenMessageLoaded(false)
|
self.firstUnseenMessageState.scrollToWhenFetched = false
|
||||||
self.controller.getAsyncFirstUnseenMessageId()
|
self.controller.getAsyncFirstUnseenMessageId()
|
||||||
|
|
||||||
method resetAndScrollToNewMessagesMarker*(self: Module) =
|
method resetAndScrollToNewMessagesMarker*(self: Module) =
|
||||||
self.scrollToFirstUnseenMessageWhenLoaded = true
|
self.firstUnseenMessageState.fetching = true
|
||||||
self.view.setFirstUnseenMessageLoaded(false)
|
self.firstUnseenMessageState.scrollToWhenFetched = true
|
||||||
self.controller.getAsyncFirstUnseenMessageId()
|
self.controller.getAsyncFirstUnseenMessageId()
|
||||||
|
|
||||||
method removeNewMessagesMarker*(self: Module) =
|
method removeNewMessagesMarker*(self: Module) =
|
||||||
@ -728,9 +742,14 @@ proc updateItemsByAlbum(self: Module, items: var seq[Item], message: MessageDto)
|
|||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
method isFirstUnseenMessageInitialized*(self: Module): bool =
|
||||||
|
return self.firstUnseenMessageState.initialized
|
||||||
|
|
||||||
method onFirstUnseenMessageLoaded*(self: Module, messageId: string) =
|
method onFirstUnseenMessageLoaded*(self: Module, messageId: string) =
|
||||||
self.view.model().setFirstUnseenMessageId(messageId)
|
self.view.model().setFirstUnseenMessageId(messageId)
|
||||||
self.view.model().resetNewMessagesMarker()
|
self.view.model().resetNewMessagesMarker()
|
||||||
if self.scrollToFirstUnseenMessageWhenLoaded:
|
if self.firstUnseenMessageState.scrollToWhenFetched:
|
||||||
self.scrollToMessage(messageId)
|
self.scrollToMessage(messageId)
|
||||||
self.view.setFirstUnseenMessageLoaded(true)
|
self.firstUnseenMessageState.initialized = true
|
||||||
|
self.firstUnseenMessageState.fetching = false
|
||||||
|
self.reevaluateViewLoadingState()
|
||||||
|
@ -10,14 +10,13 @@ QtObject:
|
|||||||
delegate: io_interface.AccessInterface
|
delegate: io_interface.AccessInterface
|
||||||
model: Model
|
model: Model
|
||||||
modelVariant: QVariant
|
modelVariant: QVariant
|
||||||
initialMessagesLoaded: bool
|
|
||||||
messageSearchOngoing: bool
|
messageSearchOngoing: bool
|
||||||
amIChatAdmin: bool
|
amIChatAdmin: bool
|
||||||
isPinMessageAllowedForMembers: bool
|
isPinMessageAllowedForMembers: bool
|
||||||
chatColor: string
|
chatColor: string
|
||||||
chatIcon: string
|
chatIcon: string
|
||||||
chatType: int
|
chatType: int
|
||||||
firstUnseenMessageLoaded: bool
|
loading: bool
|
||||||
|
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.model.delete
|
self.model.delete
|
||||||
@ -30,14 +29,13 @@ QtObject:
|
|||||||
result.delegate = delegate
|
result.delegate = delegate
|
||||||
result.model = newModel()
|
result.model = newModel()
|
||||||
result.modelVariant = newQVariant(result.model)
|
result.modelVariant = newQVariant(result.model)
|
||||||
result.initialMessagesLoaded = false
|
|
||||||
result.messageSearchOngoing = false
|
result.messageSearchOngoing = false
|
||||||
result.amIChatAdmin = false
|
result.amIChatAdmin = false
|
||||||
result.isPinMessageAllowedForMembers = false
|
result.isPinMessageAllowedForMembers = false
|
||||||
result.chatColor = ""
|
result.chatColor = ""
|
||||||
result.chatIcon = ""
|
result.chatIcon = ""
|
||||||
result.chatType = ChatType.Unknown.int
|
result.chatType = ChatType.Unknown.int
|
||||||
|
result.loading = false
|
||||||
|
|
||||||
proc load*(self: View) =
|
proc load*(self: View) =
|
||||||
self.delegate.viewDidLoad()
|
self.delegate.viewDidLoad()
|
||||||
@ -80,21 +78,6 @@ QtObject:
|
|||||||
proc getNumberOfPinnedMessages*(self: View): int {.slot.} =
|
proc getNumberOfPinnedMessages*(self: View): int {.slot.} =
|
||||||
return self.delegate.getNumberOfPinnedMessages()
|
return self.delegate.getNumberOfPinnedMessages()
|
||||||
|
|
||||||
proc initialMessagesLoadedChanged*(self: View) {.signal.}
|
|
||||||
|
|
||||||
proc getInitialMessagesLoaded(self: View): bool {.slot.} =
|
|
||||||
return self.initialMessagesLoaded
|
|
||||||
|
|
||||||
QtProperty[bool] initialMessagesLoaded:
|
|
||||||
read = getInitialMessagesLoaded
|
|
||||||
notify = initialMessagesLoadedChanged
|
|
||||||
|
|
||||||
proc initialMessagesAreLoaded*(self: View) = # this is not a slot
|
|
||||||
if (self.initialMessagesLoaded):
|
|
||||||
return
|
|
||||||
self.initialMessagesLoaded = true
|
|
||||||
self.initialMessagesLoadedChanged()
|
|
||||||
|
|
||||||
proc loadMoreMessages*(self: View) {.slot.} =
|
proc loadMoreMessages*(self: View) {.slot.} =
|
||||||
self.delegate.loadMoreMessages()
|
self.delegate.loadMoreMessages()
|
||||||
|
|
||||||
@ -236,13 +219,13 @@ QtObject:
|
|||||||
self.chatType = value
|
self.chatType = value
|
||||||
self.chatTypeChanged()
|
self.chatTypeChanged()
|
||||||
|
|
||||||
proc firstUnseenMessageLoadedChanged*(self: View) {.signal.}
|
proc loadingChanged*(self: View) {.signal.}
|
||||||
proc getFirstUnseenMessageLoaded*(self: View): bool {.slot.} =
|
proc isLoading*(self: View): bool {.slot.} =
|
||||||
return self.firstUnseenMessageLoaded
|
return self.loading
|
||||||
proc setFirstUnseenMessageLoaded*(self: View, value: bool) =
|
proc setLoading*(self: View, value: bool) =
|
||||||
self.firstUnseenMessageLoaded = value
|
self.loading = value
|
||||||
self.firstUnseenMessageLoadedChanged()
|
self.loadingChanged()
|
||||||
|
|
||||||
QtProperty[bool] firstUnseenMessageLoaded:
|
QtProperty[bool] loading:
|
||||||
read = getFirstUnseenMessageLoaded
|
read = isLoading
|
||||||
notify = firstUnseenMessageLoadedChanged
|
notify = loadingChanged
|
||||||
|
@ -387,8 +387,16 @@ method contactTrustStatusChanged*(self: Module, publicKey: string, isUntrustwort
|
|||||||
self.view.updateTrustStatus(isUntrustworthy)
|
self.view.updateTrustStatus(isUntrustworthy)
|
||||||
|
|
||||||
method onMadeActive*(self: Module) =
|
method onMadeActive*(self: Module) =
|
||||||
self.messagesModule.resetAndScrollToNewMessagesMarker()
|
# The new messages marker is reset each time the chat is made active,
|
||||||
|
# as messages may arrive out of order and relying on the previous
|
||||||
|
# new messages marker could yield incorrect results.
|
||||||
|
if not self.messagesModule.isFirstUnseenMessageInitialized() or
|
||||||
|
self.controller.getChatDetails().unviewedMessagesCount > 0:
|
||||||
|
self.messagesModule.resetAndScrollToNewMessagesMarker()
|
||||||
|
self.messagesModule.reevaluateViewLoadingState()
|
||||||
self.view.setActive()
|
self.view.setActive()
|
||||||
|
|
||||||
method onMadeInactive*(self: Module) =
|
method onMadeInactive*(self: Module) =
|
||||||
|
if self.controller.getChatDetails().unviewedMessagesCount == 0:
|
||||||
|
self.messagesModule.removeNewMessagesMarker()
|
||||||
self.view.setInactive()
|
self.view.setInactive()
|
||||||
|
@ -11,8 +11,7 @@ QtObject {
|
|||||||
readonly property bool loadingHistoryMessagesInProgress: root.chatSectionModule? root.chatSectionModule.loadingHistoryMessagesInProgress : false
|
readonly property bool loadingHistoryMessagesInProgress: root.chatSectionModule? root.chatSectionModule.loadingHistoryMessagesInProgress : false
|
||||||
readonly property int newMessagesCount: messagesModel ? messagesModel.newMessagesCount : 0
|
readonly property int newMessagesCount: messagesModel ? messagesModel.newMessagesCount : 0
|
||||||
readonly property bool messageSearchOngoing: messageModule ? messageModule.messageSearchOngoing : false
|
readonly property bool messageSearchOngoing: messageModule ? messageModule.messageSearchOngoing : false
|
||||||
readonly property bool initialMessagesLoaded: messageModule ? messageModule.initialMessagesLoaded : false
|
readonly property bool loading: messageModule ? messageModule.loading : false
|
||||||
readonly property bool firstUnseenMessageLoaded: messageModule ? messageModule.firstUnseenMessageLoaded : false
|
|
||||||
|
|
||||||
readonly property bool amIChatAdmin: messageModule ? messageModule.amIChatAdmin : false
|
readonly property bool amIChatAdmin: messageModule ? messageModule.amIChatAdmin : false
|
||||||
readonly property bool isPinMessageAllowedForMembers: messageModule ? messageModule.isPinMessageAllowedForMembers : false
|
readonly property bool isPinMessageAllowedForMembers: messageModule ? messageModule.isPinMessageAllowedForMembers : false
|
||||||
@ -31,7 +30,7 @@ QtObject {
|
|||||||
if(!messageModule)
|
if(!messageModule)
|
||||||
return
|
return
|
||||||
|
|
||||||
if(!messageModule.initialMessagesLoaded)
|
if(root.loading)
|
||||||
return
|
return
|
||||||
|
|
||||||
messageModule.loadMoreMessages()
|
messageModule.loadMoreMessages()
|
||||||
|
@ -59,8 +59,7 @@ Item {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (chatDetails && chatDetails.active && chatDetails.hasUnreadMessages &&
|
if (chatDetails && chatDetails.active && chatDetails.hasUnreadMessages && !messageStore.loading) {
|
||||||
!messageStore.messageSearchOngoing && messageStore.firstUnseenMessageLoaded) {
|
|
||||||
chatContentModule.markAllMessagesRead()
|
chatContentModule.markAllMessagesRead()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,7 +95,7 @@ Item {
|
|||||||
d.markAllMessagesReadIfMostRecentMessageIsInViewport()
|
d.markAllMessagesReadIfMostRecentMessageIsInViewport()
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFirstUnseenMessageLoadedChanged() {
|
function onLoadingChanged() {
|
||||||
d.markAllMessagesReadIfMostRecentMessageIsInViewport()
|
d.markAllMessagesReadIfMostRecentMessageIsInViewport()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,11 +115,7 @@ Item {
|
|||||||
|
|
||||||
// HACK: we call `addNewMessagesMarker` later because messages model
|
// HACK: we call `addNewMessagesMarker` later because messages model
|
||||||
// may not be yet propagated with unread messages when this signal is emitted
|
// may not be yet propagated with unread messages when this signal is emitted
|
||||||
if (chatLogView.visible) {
|
if (chatLogView.visible && !d.isMostRecentMessageInViewport) {
|
||||||
if (!d.isMostRecentMessageInViewport) {
|
|
||||||
Qt.callLater(() => messageStore.addNewMessagesMarker())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Qt.callLater(() => messageStore.addNewMessagesMarker())
|
Qt.callLater(() => messageStore.addNewMessagesMarker())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -161,19 +156,17 @@ Item {
|
|||||||
Loader {
|
Loader {
|
||||||
id: loadingMessagesView
|
id: loadingMessagesView
|
||||||
|
|
||||||
readonly property bool show: !messageStore.firstUnseenMessageLoaded ||
|
|
||||||
!messageStore.initialMessagesLoaded
|
|
||||||
active: show
|
|
||||||
visible: show
|
|
||||||
anchors.top: loadingMessagesIndicator.bottom
|
anchors.top: loadingMessagesIndicator.bottom
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
sourceComponent:
|
|
||||||
MessagesLoadingView {
|
active: messageStore.loading
|
||||||
|
visible: active
|
||||||
|
sourceComponent: MessagesLoadingView {
|
||||||
anchors.margins: 16
|
anchors.margins: 16
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusListView {
|
StatusListView {
|
||||||
@ -205,7 +198,7 @@ Item {
|
|||||||
|
|
||||||
// after inilial messages are loaded
|
// after inilial messages are loaded
|
||||||
// load as much messages as the view requires
|
// load as much messages as the view requires
|
||||||
if (messageStore.initialMessagesLoaded) {
|
if (!messageStore.loading) {
|
||||||
d.loadMoreMessagesIfScrollBelowThreshold()
|
d.loadMoreMessagesIfScrollBelowThreshold()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user