mirror of
https://github.com/status-im/status-desktop.git
synced 2025-03-04 08:20:58 +00:00
feat:(@desktop/channels): loading state when switching channels/chats
This commit is contained in:
parent
5479880cde
commit
b580f0a810
@ -212,6 +212,12 @@ proc init*(self: Controller) =
|
|||||||
if (community.id == self.sectionId):
|
if (community.id == self.sectionId):
|
||||||
self.delegate.updateCommunityDetails(community)
|
self.delegate.updateCommunityDetails(community)
|
||||||
|
|
||||||
|
self.events.on(SIGNAL_MESSAGE_FIRST_UNSEEN) do(e: Args):
|
||||||
|
let args = MessageFirstUnseen(e)
|
||||||
|
if (args.chatId != self.chatId):
|
||||||
|
return
|
||||||
|
self.delegate.onFirstUnseenMessageId(args.messageId)
|
||||||
|
|
||||||
proc getMySectionId*(self: Controller): string =
|
proc getMySectionId*(self: Controller): string =
|
||||||
return self.sectionId
|
return self.sectionId
|
||||||
|
|
||||||
@ -280,8 +286,8 @@ proc setSearchedMessageId*(self: Controller, searchedMessageId: string) =
|
|||||||
proc clearSearchedMessageId*(self: Controller) =
|
proc clearSearchedMessageId*(self: Controller) =
|
||||||
self.setSearchedMessageId("")
|
self.setSearchedMessageId("")
|
||||||
|
|
||||||
proc getFirstUnseenMessageId*(self: Controller): string =
|
proc getAsyncFirstUnseenMessageId*(self: Controller) =
|
||||||
self.messageService.getFirstUnseenMessageIdFor(self.chatId)
|
self.messageService.getAsyncFirstUnseenMessageId(self.chatId)
|
||||||
|
|
||||||
proc getLoadingMessagesPerPageFactor*(self: Controller): int =
|
proc getLoadingMessagesPerPageFactor*(self: Controller): int =
|
||||||
return self.loadingMessagesPerPageFactor
|
return self.loadingMessagesPerPageFactor
|
||||||
|
@ -153,11 +153,11 @@ 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 scrollToNewMessagesMarker*(self: AccessInterface) =
|
|
||||||
raise newException(ValueError, "No implementation available")
|
|
||||||
|
|
||||||
method markAllMessagesRead*(self: AccessInterface) =
|
method markAllMessagesRead*(self: AccessInterface) =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method updateCommunityDetails*(self: AccessInterface, community: CommunityDto) =
|
method updateCommunityDetails*(self: AccessInterface, community: CommunityDto) =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method onFirstUnseenMessageId*(self: AccessInterface, messageId: string) =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
@ -681,16 +681,12 @@ 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.view.model().setFirstUnseenMessageId(self.controller.getFirstUnseenMessageId())
|
self.controller.getAsyncFirstUnseenMessageId()
|
||||||
self.view.model().resetNewMessagesMarker()
|
|
||||||
|
|
||||||
method removeNewMessagesMarker*(self: Module) =
|
method removeNewMessagesMarker*(self: Module) =
|
||||||
self.view.model().setFirstUnseenMessageId("")
|
self.view.model().setFirstUnseenMessageId("")
|
||||||
self.view.model().resetNewMessagesMarker()
|
self.view.model().resetNewMessagesMarker()
|
||||||
|
|
||||||
method scrollToNewMessagesMarker*(self: Module) =
|
|
||||||
self.scrollToMessage(self.view.model().getFirstUnseenMessageId())
|
|
||||||
|
|
||||||
method markAllMessagesRead*(self: Module) =
|
method markAllMessagesRead*(self: Module) =
|
||||||
self.view.model().markAllAsSeen()
|
self.view.model().markAllAsSeen()
|
||||||
|
|
||||||
@ -721,3 +717,11 @@ proc updateItemsByAlbum(self: Module, items: var seq[Item], message: MessageDto)
|
|||||||
items[i] = item
|
items[i] = item
|
||||||
return true
|
return true
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
method onFirstUnseenMessageId*(self: Module, messageId: string) =
|
||||||
|
self.view.model().setFirstUnseenMessageId(messageId)
|
||||||
|
self.view.model().resetNewMessagesMarker()
|
||||||
|
let index = self.view.model().findIndexForMessageId(messageId)
|
||||||
|
if (index != -1):
|
||||||
|
self.view.emitScrollToFirstUnreadMessageSignal(index)
|
||||||
|
self.view.setFirstUnseenMessageLoaded(true)
|
||||||
|
@ -17,6 +17,7 @@ QtObject:
|
|||||||
chatColor: string
|
chatColor: string
|
||||||
chatIcon: string
|
chatIcon: string
|
||||||
chatType: int
|
chatType: int
|
||||||
|
firstUnseenMessageLoaded: bool
|
||||||
|
|
||||||
proc delete*(self: View) =
|
proc delete*(self: View) =
|
||||||
self.model.delete
|
self.model.delete
|
||||||
@ -233,4 +234,19 @@ QtObject:
|
|||||||
|
|
||||||
proc setChatType*(self: View, value: int) =
|
proc setChatType*(self: View, value: int) =
|
||||||
self.chatType = value
|
self.chatType = value
|
||||||
self.chatTypeChanged()
|
self.chatTypeChanged()
|
||||||
|
|
||||||
|
proc firstUnseenMessageLoadedChanged*(self: View) {.signal.}
|
||||||
|
proc getFirstUnseenMessageLoaded*(self: View): bool {.slot.} =
|
||||||
|
return self.firstUnseenMessageLoaded
|
||||||
|
proc setFirstUnseenMessageLoaded*(self: View, value: bool) =
|
||||||
|
self.firstUnseenMessageLoaded = value
|
||||||
|
self.firstUnseenMessageLoadedChanged()
|
||||||
|
|
||||||
|
QtProperty[bool] firstUnseenMessageLoaded:
|
||||||
|
read = getFirstUnseenMessageLoaded
|
||||||
|
notify = firstUnseenMessageLoadedChanged
|
||||||
|
|
||||||
|
proc scrollToFirstUnreadMessage(self: View, messageIndex: int) {.signal.}
|
||||||
|
proc emitScrollToFirstUnreadMessageSignal*(self: View, messageIndex: int) =
|
||||||
|
self.scrollToFirstUnreadMessage(messageIndex)
|
@ -387,7 +387,6 @@ method contactTrustStatusChanged*(self: Module, publicKey: string, isUntrustwort
|
|||||||
|
|
||||||
method onMadeActive*(self: Module) =
|
method onMadeActive*(self: Module) =
|
||||||
self.messagesModule.resetNewMessagesMarker()
|
self.messagesModule.resetNewMessagesMarker()
|
||||||
self.messagesModule.scrollToNewMessagesMarker()
|
|
||||||
self.view.setActive()
|
self.view.setActive()
|
||||||
|
|
||||||
method onMadeInactive*(self: Module) =
|
method onMadeInactive*(self: Module) =
|
||||||
|
@ -233,4 +233,36 @@ const asyncGetLinkPreviewDataTask: Task = proc(argEncoded: string) {.gcsafe, nim
|
|||||||
previewData["links"].add(responseJson)
|
previewData["links"].add(responseJson)
|
||||||
|
|
||||||
let tpl: tuple[previewData: JsonNode, uuid: string] = (previewData, arg.uuid)
|
let tpl: tuple[previewData: JsonNode, uuid: string] = (previewData, arg.uuid)
|
||||||
arg.finish(tpl)
|
arg.finish(tpl)
|
||||||
|
|
||||||
|
#################################################
|
||||||
|
# Async get first unseen message id
|
||||||
|
#################################################
|
||||||
|
type
|
||||||
|
AsyncGetFirstUnseenMessageIdForTaskArg = ref object of QObjectTaskArg
|
||||||
|
chatId: string
|
||||||
|
|
||||||
|
const asyncGetFirstUnseenMessageIdForTaskArg: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||||
|
let arg = decode[AsyncGetFirstUnseenMessageIdForTaskArg](argEncoded)
|
||||||
|
|
||||||
|
let responseJson = %*{
|
||||||
|
"messageId": "",
|
||||||
|
"chatId": arg.chatId,
|
||||||
|
"error": ""
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
let response = status_go.firstUnseenMessageID(arg.chatId)
|
||||||
|
|
||||||
|
if(not response.error.isNil):
|
||||||
|
error "error getFirstUnseenMessageIdFor: ", errDescription = response.error.message
|
||||||
|
responseJson["error"] = %response.error.message
|
||||||
|
else:
|
||||||
|
responseJson["messageId"] = %response.result.getStr()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error "error: ", procName = "getFirstUnseenMessageIdFor", errName = e.name,
|
||||||
|
errDesription = e.msg, chatId=arg.chatId
|
||||||
|
responseJson["error"] = %e.msg
|
||||||
|
|
||||||
|
arg.finish(responseJson)
|
@ -40,6 +40,7 @@ const WEEK_AS_MILLISECONDS = initDuration(seconds = 60*60*24*7).inMilliSeconds
|
|||||||
|
|
||||||
# Signals which may be emitted by this service:
|
# Signals which may be emitted by this service:
|
||||||
const SIGNAL_MESSAGES_LOADED* = "messagesLoaded"
|
const SIGNAL_MESSAGES_LOADED* = "messagesLoaded"
|
||||||
|
const SIGNAL_MESSAGE_FIRST_UNSEEN* = "messageFirstUnseen"
|
||||||
const SIGNAL_NEW_MESSAGE_RECEIVED* = "newMessageReceived"
|
const SIGNAL_NEW_MESSAGE_RECEIVED* = "newMessageReceived"
|
||||||
const SIGNAL_MESSAGE_PINNED* = "messagePinned"
|
const SIGNAL_MESSAGE_PINNED* = "messagePinned"
|
||||||
const SIGNAL_MESSAGE_UNPINNED* = "messageUnpinned"
|
const SIGNAL_MESSAGE_UNPINNED* = "messageUnpinned"
|
||||||
@ -117,6 +118,10 @@ type
|
|||||||
ReloadMessagesArgs* = ref object of Args
|
ReloadMessagesArgs* = ref object of Args
|
||||||
communityId*: string
|
communityId*: string
|
||||||
|
|
||||||
|
MessageFirstUnseen* = ref object of Args
|
||||||
|
chatId*: string
|
||||||
|
messageId*: string
|
||||||
|
|
||||||
QtObject:
|
QtObject:
|
||||||
type Service* = ref object of QObject
|
type Service* = ref object of QObject
|
||||||
events: EventEmitter
|
events: EventEmitter
|
||||||
@ -188,9 +193,6 @@ QtObject:
|
|||||||
let pinnedMsgCursorValue = if (pinnedMsgCursor.isFetchable()): pinnedMsgCursor.getValue() else: CURSOR_VALUE_IGNORE
|
let pinnedMsgCursorValue = if (pinnedMsgCursor.isFetchable()): pinnedMsgCursor.getValue() else: CURSOR_VALUE_IGNORE
|
||||||
|
|
||||||
if(msgCursorValue == CURSOR_VALUE_IGNORE and pinnedMsgCursorValue == CURSOR_VALUE_IGNORE):
|
if(msgCursorValue == CURSOR_VALUE_IGNORE and pinnedMsgCursorValue == CURSOR_VALUE_IGNORE):
|
||||||
# it's important to emit signal in case we are not fetching messages, so we can update the view appropriatelly.
|
|
||||||
let data = MessagesLoadedArgs(chatId: chatId)
|
|
||||||
self.events.emit(SIGNAL_MESSAGES_LOADED, data)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
if(msgCursorValue != CURSOR_VALUE_IGNORE):
|
if(msgCursorValue != CURSOR_VALUE_IGNORE):
|
||||||
@ -386,7 +388,6 @@ QtObject:
|
|||||||
let responseObj = response.parseJson
|
let responseObj = response.parseJson
|
||||||
if (responseObj.kind != JObject):
|
if (responseObj.kind != JObject):
|
||||||
info "load more messages response is not a json object"
|
info "load more messages response is not a json object"
|
||||||
|
|
||||||
# notify view, this is important
|
# notify view, this is important
|
||||||
self.events.emit(SIGNAL_MESSAGES_LOADED, MessagesLoadedArgs())
|
self.events.emit(SIGNAL_MESSAGES_LOADED, MessagesLoadedArgs())
|
||||||
return
|
return
|
||||||
@ -654,18 +655,37 @@ QtObject:
|
|||||||
|
|
||||||
self.threadpool.start(arg)
|
self.threadpool.start(arg)
|
||||||
|
|
||||||
proc getFirstUnseenMessageIdFor*(self: Service, chatId: string): string =
|
proc getAsyncFirstUnseenMessageId*(self: Service, chatId: string) =
|
||||||
|
let arg = AsyncGetFirstUnseenMessageIdForTaskArg(
|
||||||
|
tptr: cast[ByteAddress](asyncGetFirstUnseenMessageIdForTaskArg),
|
||||||
|
vptr: cast[ByteAddress](self.vptr),
|
||||||
|
slot: "onGetFirstUnseenMessageIdFor",
|
||||||
|
chatId: chatId,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.threadpool.start(arg)
|
||||||
|
|
||||||
|
proc onGetFirstUnseenMessageIdFor*(self: Service, response: string) {.slot.} =
|
||||||
try:
|
try:
|
||||||
let response = status_go.firstUnseenMessageID(chatId)
|
let responseObj = response.parseJson
|
||||||
|
|
||||||
if(not response.error.isNil):
|
var error: string
|
||||||
error "error getFirstUnseenMessageIdFor: ", errDescription = response.error.message
|
discard responseObj.getProp("error", error)
|
||||||
|
|
||||||
result = response.result.getStr()
|
var chatId: string
|
||||||
|
discard responseObj.getProp("chatId", chatId)
|
||||||
|
|
||||||
|
var messageId = ""
|
||||||
|
|
||||||
|
if(error.len > 0):
|
||||||
|
error "error: ", procName="onGetFirstUnseenMessageIdFor", errDescription=error
|
||||||
|
else:
|
||||||
|
discard responseObj.getProp("messageId", messageId)
|
||||||
|
|
||||||
|
self.events.emit(SIGNAL_MESSAGE_FIRST_UNSEEN, MessageFirstUnseen(chatId: chatId, messageId: messageId))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "error: ", procName = "getFirstUnseenMessageIdFor", errName = e.name,
|
error "error: ", procName="onGetFirstUnseenMessageIdFor", errName = e.name, errDesription = e.msg
|
||||||
errDesription = e.msg
|
|
||||||
|
|
||||||
proc onAsyncGetLinkPreviewData*(self: Service, response: string) {.slot.} =
|
proc onAsyncGetLinkPreviewData*(self: Service, response: string) {.slot.} =
|
||||||
let responseObj = response.parseJson
|
let responseObj = response.parseJson
|
||||||
|
@ -83,6 +83,13 @@ Item {
|
|||||||
chatLogView.itemAtIndex(messageIndex).startMessageFoundAnimation()
|
chatLogView.itemAtIndex(messageIndex).startMessageFoundAnimation()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onScrollToFirstUnreadMessage(messageIndex) {
|
||||||
|
if (d.isMostRecentMessageInViewport) {
|
||||||
|
chatLogView.positionViewAtIndex(messageIndex, ListView.Center)
|
||||||
|
chatLogView.itemAtIndex(messageIndex).startMessageFoundAnimation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function onMessageSearchOngoingChanged() {
|
function onMessageSearchOngoingChanged() {
|
||||||
d.markAllMessagesReadIfMostRecentMessageIsInViewport()
|
d.markAllMessagesReadIfMostRecentMessageIsInViewport()
|
||||||
}
|
}
|
||||||
@ -145,8 +152,27 @@ Item {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loader {
|
||||||
|
id: loadingMessagesView
|
||||||
|
|
||||||
|
readonly property bool show: !messageStore.messageModule.firstUnseenMessageLoaded ||
|
||||||
|
!messageStore.messageModule.initialMessagesLoaded
|
||||||
|
active: show
|
||||||
|
visible: show
|
||||||
|
anchors.top: loadingMessagesIndicator.bottom
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.right: parent.right
|
||||||
|
sourceComponent:
|
||||||
|
MessagesLoadingView {
|
||||||
|
anchors.margins: 16
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StatusListView {
|
StatusListView {
|
||||||
id: chatLogView
|
id: chatLogView
|
||||||
|
visible: !loadingMessagesView.visible
|
||||||
objectName: "chatLogView"
|
objectName: "chatLogView"
|
||||||
anchors.top: loadingMessagesIndicator.bottom
|
anchors.top: loadingMessagesIndicator.bottom
|
||||||
anchors.bottom: parent.bottom
|
anchors.bottom: parent.bottom
|
||||||
@ -270,7 +296,7 @@ Item {
|
|||||||
quotedMessageAuthorDetailsEnsVerified: model.quotedMessageAuthorEnsVerified
|
quotedMessageAuthorDetailsEnsVerified: model.quotedMessageAuthorEnsVerified
|
||||||
quotedMessageAuthorDetailsIsContact: model.quotedMessageAuthorIsContact
|
quotedMessageAuthorDetailsIsContact: model.quotedMessageAuthorIsContact
|
||||||
quotedMessageAuthorDetailsColorHash: model.quotedMessageAuthorColorHash
|
quotedMessageAuthorDetailsColorHash: model.quotedMessageAuthorColorHash
|
||||||
|
|
||||||
gapFrom: model.gapFrom
|
gapFrom: model.gapFrom
|
||||||
gapTo: model.gapTo
|
gapTo: model.gapTo
|
||||||
|
|
||||||
|
65
ui/app/AppLayouts/Chat/views/MessagesLoadingView.qml
Normal file
65
ui/app/AppLayouts/Chat/views/MessagesLoadingView.qml
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
spacing: 20
|
||||||
|
interactive: false
|
||||||
|
clip: true
|
||||||
|
|
||||||
|
model: ListModel {
|
||||||
|
Component.onCompleted: {
|
||||||
|
var numElements = 20
|
||||||
|
for (var i = 1; i < numElements; ++i) {
|
||||||
|
if (i % 5 === 0)
|
||||||
|
append({ "isImage": true, "thirdLine": false })
|
||||||
|
else if (i % 3 === 0)
|
||||||
|
append({ "isImage": false, "thirdLine": true })
|
||||||
|
else
|
||||||
|
append({ "isImage": false, "thirdLine": false })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
|
||||||
|
implicitHeight: layoutContent.implicitHeight
|
||||||
|
implicitWidth: layoutContent.implicitWidth
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
id: layoutContent
|
||||||
|
anchors.fill: parent
|
||||||
|
spacing: 8
|
||||||
|
|
||||||
|
LoadingComponent {
|
||||||
|
Layout.alignment: Qt.AlignTop
|
||||||
|
radius: width / 2
|
||||||
|
height: 44
|
||||||
|
width: 44
|
||||||
|
}
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 4
|
||||||
|
LoadingComponent {
|
||||||
|
radius: 4
|
||||||
|
height: 20
|
||||||
|
width: 124
|
||||||
|
}
|
||||||
|
LoadingComponent {
|
||||||
|
radius: 16
|
||||||
|
height: model.isImage ? 194 : 18
|
||||||
|
width: model.isImage ? 147 : 335
|
||||||
|
}
|
||||||
|
LoadingComponent {
|
||||||
|
visible: thirdLine
|
||||||
|
radius: 4
|
||||||
|
height: 18
|
||||||
|
width: 215
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user