fix: prevent forever chat loading animation (#13929)

* prevent animation if loading not started

* prevent scrolling to verification/contact request messages

* fix ac click behaviour

* remove CURSOR_VALUE_IGNORE. cleanup logs
This commit is contained in:
Igor Sirotin 2024-03-12 14:24:55 +00:00 committed by GitHub
parent 666ba77051
commit a6f5f0bc94
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 109 additions and 72 deletions

View File

@ -265,9 +265,9 @@ proc getOneToOneChatNameAndImage*(self: Controller):
proc belongsToCommunity*(self: Controller): bool =
return self.belongsToCommunity
proc loadMoreMessages*(self: Controller) =
proc loadMoreMessages*(self: Controller): bool =
let limit = self.loadingMessagesPerPageFactor * MESSAGES_PER_PAGE
self.messageService.asyncLoadMoreMessagesForChat(self.chatId, limit)
return self.messageService.asyncLoadMoreMessagesForChat(self.chatId, limit)
proc addReaction*(self: Controller, messageId: string, emojiId: int) =
self.messageService.addReaction(self.chatId, messageId, emojiId)

View File

@ -1,4 +1,4 @@
import NimQml, chronicles, sequtils, uuids, sets, times, tables, system
import NimQml, chronicles, sequtils, uuids, sets, times, tables, system, sugar
import io_interface
import ../io_interface as delegate_interface
import view, controller
@ -354,19 +354,27 @@ proc createChatIdentifierItem(self: Module): Item =
bridgeMessage = BridgeMessage(),
)
proc checkIfMessageLoadedAndScrollToItIfItIs(self: Module) =
proc checkIfMessageLoadedAndScroll(self: Module) =
let searchedMessageId = self.controller.getSearchedMessageId()
if(searchedMessageId.len > 0):
let index = self.view.model().findIndexForMessageId(searchedMessageId)
if(index != -1):
self.controller.clearSearchedMessageId()
self.controller.resetLoadingMessagesPerPageFactor()
self.view.emitScrollToMessageSignal(index)
self.view.setMessageSearchOngoing(false)
self.reevaluateViewLoadingState()
else:
self.controller.increaseLoadingMessagesPerPageFactor()
self.loadMoreMessages()
if searchedMessageId.len == 0:
return
let index = self.view.model().findIndexForMessageId(searchedMessageId)
if index == -1:
self.controller.increaseLoadingMessagesPerPageFactor()
if self.controller.loadMoreMessages():
warn "failed to start loading more messages"
return
# If failed to `loadMoreMessages`, then the most recent message is already loaded.
# Then message is not found.
self.controller.clearSearchedMessageId()
self.controller.resetLoadingMessagesPerPageFactor()
if index != -1:
self.view.emitScrollToMessageSignal(index)
self.view.setMessageSearchOngoing(false)
self.reevaluateViewLoadingState()
proc currentUserWalletContainsAddress(self: Module, address: string): bool =
if (address.len == 0):
@ -378,13 +386,14 @@ proc currentUserWalletContainsAddress(self: Module, address: string): bool =
return false
method reevaluateViewLoadingState*(self: Module) =
self.view.setLoading(not self.initialMessagesLoaded or
not self.firstUnseenMessageState.initialized or
self.firstUnseenMessageState.fetching or
self.view.getMessageSearchOngoing())
let loading = not self.initialMessagesLoaded or
not self.firstUnseenMessageState.initialized or
self.firstUnseenMessageState.fetching or
self.view.getMessageSearchOngoing()
self.view.setLoading(loading)
method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: seq[ReactionDto]) =
if(messages.len > 0):
if messages.len > 0:
var viewItems = self.createMessageItemsFromMessageDtos(messages, reactions)
if self.controller.getChatDetails().hasMoreMessagesToRequest():
@ -397,7 +406,7 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
self.view.model().resetNewMessagesMarker()
# check if this loading was caused by the click on a messages from the app search result
self.checkIfMessageLoadedAndScrollToItIfItIs()
self.checkIfMessageLoadedAndScroll()
self.initialMessagesLoaded = true
self.reevaluateViewLoadingState()
@ -433,7 +442,7 @@ method onMessageDelivered*(self: Module, messageId: string) =
self.view.model().itemDelivered(messageId)
method loadMoreMessages*(self: Module) =
self.controller.loadMoreMessages()
discard self.controller.loadMoreMessages()
method toggleReaction*(self: Module, messageId: string, emojiId: int) =
var emojiIdAsEnum: EmojiId
@ -627,7 +636,7 @@ method updateChatFetchMoreMessages*(self: Module) =
proc switchToMessage*(self: Module, messageId: string) =
let index = self.view.model().findIndexForMessageId(messageId)
if(index != -1):
if index != -1:
self.controller.clearSearchedMessageId()
self.view.emitSwitchToMessageSignal(index)
else:
@ -641,10 +650,10 @@ method scrollToMessage*(self: Module, messageId: string) =
return
self.getMessageRequestId = self.controller.asyncGetMessageById(messageId)
self.controller.setSearchedMessageId(messageId)
self.view.setMessageSearchOngoing(true)
method onGetMessageById*(self: Module, requestId: UUID, messageId: string, message: MessageDto, errorMessage: string) =
if self.getMessageRequestId != requestId:
return
@ -653,8 +662,13 @@ method onGetMessageById*(self: Module, requestId: UUID, messageId: string, messa
self.view.setMessageSearchOngoing(false)
return
self.controller.setSearchedMessageId(messageId)
self.checkIfMessageLoadedAndScrollToItIfItIs()
if message.contentType == ContentType.ContactIdentityVerification or
message.contentType == ContentType.ContactRequest:
warn "attempted to scroll to a non-displayed message", messageId, contentType = $message.contentType
self.view.setMessageSearchOngoing(false)
return
self.checkIfMessageLoadedAndScroll()
self.reevaluateViewLoadingState()
method requestMoreMessages*(self: Module) =

View File

@ -33,22 +33,19 @@ const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimc
}
# handle messages
if(arg.msgCursor != CURSOR_VALUE_IGNORE):
var messagesArr: JsonNode
var messagesCursor: JsonNode
let msgsResponse = status_go.fetchMessages(arg.chatId, arg.msgCursor, arg.limit)
discard msgsResponse.result.getProp("cursor", messagesCursor)
discard msgsResponse.result.getProp("messages", messagesArr)
responseJson["messages"] = messagesArr
responseJson["messagesCursor"] = messagesCursor
var messagesArr: JsonNode
var messagesCursor: JsonNode
let msgsResponse = status_go.fetchMessages(arg.chatId, arg.msgCursor, arg.limit)
discard msgsResponse.result.getProp("cursor", messagesCursor)
discard msgsResponse.result.getProp("messages", messagesArr)
responseJson["messages"] = messagesArr
responseJson["messagesCursor"] = messagesCursor
# handle reactions
if(arg.msgCursor != CURSOR_VALUE_IGNORE):
# messages and reactions are using the same cursor
var reactionsArr: JsonNode
let rResponse = status_go.fetchReactions(arg.chatId, arg.msgCursor, arg.limit)
reactionsArr = rResponse.result
responseJson["reactions"] = reactionsArr
var reactionsArr: JsonNode
let rResponse = status_go.fetchReactions(arg.chatId, arg.msgCursor, arg.limit)
reactionsArr = rResponse.result
responseJson["reactions"] = reactionsArr
arg.finish(responseJson)
@ -61,15 +58,14 @@ const asyncFetchPinnedChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe
var responseJson = %*{
"chatId": arg.chatId
}
#handle pinned messages
if(arg.msgCursor != CURSOR_VALUE_IGNORE):
var pinnedMsgArr: JsonNode
var msgCursor: JsonNode
let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.msgCursor, arg.limit)
discard pinnedMsgsResponse.result.getProp("cursor", msgCursor)
discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr)
responseJson["pinnedMessages"] = pinnedMsgArr
responseJson["pinnedMessagesCursor"] = msgCursor
# handle pinned messages
var pinnedMsgArr: JsonNode
var msgCursor: JsonNode
let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.msgCursor, arg.limit)
discard pinnedMsgsResponse.result.getProp("cursor", msgCursor)
discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr)
responseJson["pinnedMessages"] = pinnedMsgArr
responseJson["pinnedMessagesCursor"] = msgCursor
arg.finish(responseJson)
@ -341,4 +337,4 @@ const asyncMarkMessageAsUnreadTask: Task = proc(argEncoded: string) {.gcsafe, ni
error "asyncMarkMessageAsUnreadTask failed", message = e.msg
responseJson["error"] = %e.msg
arg.finish(responseJson)
arg.finish(responseJson)

View File

@ -28,6 +28,12 @@ proc setPending*(self: MessageCursor) =
proc isFetchable*(self: MessageCursor): bool =
return not (self.pending or self.mostRecent)
proc isPending*(self: MessageCursor): bool =
return self.pending
proc isMostRecent*(self: MessageCursor): bool =
return self.mostRecent
proc isEmpty*(self: MessageCursor): bool =
return self.value == ""

View File

@ -39,7 +39,6 @@ logScope:
let NEW_LINE = re"\n|\r" #must be defined as let, not const
const MESSAGES_PER_PAGE* = 20
const MESSAGES_PER_PAGE_MAX* = 40
const CURSOR_VALUE_IGNORE = "ignore"
const WEEK_AS_MILLISECONDS = initDuration(seconds = 60*60*24*7).inMilliSeconds
# Signals which may be emitted by this service:
@ -213,19 +212,21 @@ QtObject:
return self.pinnedMsgCursor[chatId]
proc asyncLoadMoreMessagesForChat*(self: Service, chatId: string, limit = MESSAGES_PER_PAGE) =
proc asyncLoadMoreMessagesForChat*(self: Service, chatId: string, limit = MESSAGES_PER_PAGE): bool =
if (chatId.len == 0):
error "empty chat id", procName="asyncLoadMoreMessagesForChat"
return
return false
let msgCursor = self.initOrGetMessageCursor(chatId)
let msgCursorValue = if (msgCursor.isFetchable()): msgCursor.getValue() else: CURSOR_VALUE_IGNORE
if(msgCursorValue == CURSOR_VALUE_IGNORE):
return
if msgCursor.isPending():
return true
if(msgCursorValue != CURSOR_VALUE_IGNORE):
msgCursor.setPending()
if msgCursor.isMostRecent():
return false
let msgCursorValue = msgCursor.getValue()
msgCursor.setPending()
let arg = AsyncFetchChatMessagesTaskArg(
tptr: cast[ByteAddress](asyncFetchChatMessagesTask),
@ -237,6 +238,7 @@ QtObject:
)
self.threadpool.start(arg)
return true
proc asyncLoadPinnedMessagesForChat*(self: Service, chatId: string) =
if (chatId.len == 0):
@ -244,11 +246,10 @@ QtObject:
return
let pinnedMsgCursor = self.initOrGetPinnedMessageCursor(chatId)
let pinnedMsgCursorValue = if (pinnedMsgCursor.isFetchable()): pinnedMsgCursor.getValue() else: CURSOR_VALUE_IGNORE
if(pinnedMsgCursorValue == CURSOR_VALUE_IGNORE):
if not pinnedMsgCursor.isFetchable():
return
let pinnedMsgCursorValue = pinnedMsgCursor.getValue()
pinnedMsgCursor.setPending()
let arg = AsyncFetchChatMessagesTaskArg(
@ -263,7 +264,7 @@ QtObject:
self.threadpool.start(arg)
proc asyncLoadInitialMessagesForChat*(self: Service, chatId: string) =
if(self.isChatCursorInitialized(chatId)):
if self.isChatCursorInitialized(chatId):
let data = MessagesLoadedArgs(chatId: chatId,
messages: @[],
reactions: @[])
@ -271,7 +272,7 @@ QtObject:
self.events.emit(SIGNAL_MESSAGES_LOADED, data)
return
self.asyncLoadMoreMessagesForChat(chatId)
discard self.asyncLoadMoreMessagesForChat(chatId)
proc handleMessagesUpdate(self: Service, chats: var seq[ChatDto], messages: var seq[MessageDto]) =
# We included `chats` in this condition cause that's the form how `status-go` sends updates.
@ -445,7 +446,7 @@ QtObject:
self.events.on(SignalType.DiscordChannelImportFinished.event) do(e: Args):
var receivedData = DiscordChannelImportFinishedSignal(e)
self.resetMessageCursor(receivedData.channelId)
self.asyncLoadMoreMessagesForChat(receivedData.channelId)
discard self.asyncLoadMoreMessagesForChat(receivedData.channelId)
self.events.on(SIGNAL_CHAT_LEFT) do(e: Args):
var chatArg = ChatArgs(e)

View File

@ -66,6 +66,10 @@ ActivityNotificationMessage {
}
}
onMessageClicked: {
root.openProfilePopup()
}
Component {
id: reviewContactRequestPopupComponent

View File

@ -52,6 +52,10 @@ ActivityNotificationMessage {
ctaComponent: isOutgoingMessage ? outgoingContactVerificationCta : incomingContactVerificationCta
onMessageClicked: {
root.openProfilePopup()
}
Component {
id: outgoingContactVerificationCta

View File

@ -84,8 +84,7 @@ ActivityNotificationBase {
hoverEnabled: root.messageBadgeComponent
cursorShape: Qt.PointingHandCursor
onClicked: {
root.activityCenterStore.switchTo(notification)
root.closeActivityCenter()
root.messageClicked()
}
SimplifiedMessageView {

View File

@ -125,13 +125,26 @@ StatusMenu {
}
}
StatusAction {
objectName: "chatFetchMessagesMenuItem"
text: qsTr("Fetch messages")
icon.name: "download"
StatusMenu {
title: qsTr("Debug actions")
enabled: root.showDebugOptions
onTriggered: {
root.requestMoreMessages(root.chatId)
StatusAction {
text: root.isCommunityChat ? qsTr("Copy channel ID") : qsTr("Copy chat ID")
icon.name: "copy"
onTriggered: {
Utils.copyToClipboard(root.chatId)
}
}
StatusAction {
objectName: "chatFetchMessagesMenuItem"
text: qsTr("Fetch messages")
icon.name: "download"
onTriggered: {
root.requestMoreMessages(root.chatId)
}
}
}