refactor(@desktop/chat-messages): load more messages

- load more messages on scroll up for chat/channel added
- sending messages improved in terms of adding new messages to
appropriate position
- scroll to message added on the qml side
- qml connected to the sending message success/failed signals
This commit is contained in:
Sale Djenic 2021-12-22 13:00:44 +01:00 committed by Sale Djenic
parent 0eb40287fa
commit 16a33f8fa7
14 changed files with 348 additions and 174 deletions

View File

@ -56,7 +56,13 @@ method init*(self: Controller) =
let args = MessageSendingSuccess(e) let args = MessageSendingSuccess(e)
if(self.chatId != args.chat.id): if(self.chatId != args.chat.id):
return return
self.delegate.newMessagesLoaded(@[args.message], @[], @[]) self.delegate.onSendingMessageSuccess(args.message)
self.events.on(SIGNAL_SENDING_FAILED) do(e:Args):
let args = ChatArgs(e)
if(self.chatId != args.chatId):
return
self.delegate.onSendingMessageError()
self.events.on(SIGNAL_MESSAGE_PINNED) do(e:Args): self.events.on(SIGNAL_MESSAGE_PINNED) do(e:Args):
let args = MessagePinUnpinArgs(e) let args = MessagePinUnpinArgs(e)
@ -100,6 +106,9 @@ method getOneToOneChatNameAndImage*(self: Controller): tuple[name: string, image
method belongsToCommunity*(self: Controller): bool = method belongsToCommunity*(self: Controller): bool =
return self.belongsToCommunity return self.belongsToCommunity
method loadMoreMessages*(self: Controller) =
self.messageService.asyncLoadMoreMessagesForChat(self.chatId)
method addReaction*(self: Controller, messageId: string, emojiId: int) = method addReaction*(self: Controller, messageId: string, emojiId: int) =
self.messageService.addReaction(self.chatId, messageId, emojiId) self.messageService.addReaction(self.chatId, messageId, emojiId)

View File

@ -31,6 +31,9 @@ method getOneToOneChatNameAndImage*(self: AccessInterface): tuple[name: string,
method belongsToCommunity*(self: AccessInterface): bool {.base.} = method belongsToCommunity*(self: AccessInterface): bool {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method loadMoreMessages*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method addReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} = method addReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")

View File

@ -81,6 +81,7 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
pinnedMessages: seq[PinnedMessageDto]) = pinnedMessages: seq[PinnedMessageDto]) =
var viewItems: seq[Item] var viewItems: seq[Item]
if(messages.len > 0):
for m in messages: for m in messages:
let sender = self.controller.getContactById(m.`from`) let sender = self.controller.getContactById(m.`from`)
let senderDisplayName = sender.userNameOrAlias() let senderDisplayName = sender.userNameOrAlias()
@ -118,7 +119,35 @@ method newMessagesLoaded*(self: Module, messages: seq[MessageDto], reactions: se
# Delete the old ChatIdentifier message first # Delete the old ChatIdentifier message first
self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID) self.view.model().removeItem(CHAT_IDENTIFIER_MESSAGE_ID)
# Add new loaded messages # Add new loaded messages
self.view.model().prependItems(viewItems) self.view.model().appendItems(viewItems)
if(not self.view.getInitialMessagesLoaded()):
self.view.initialMessagesAreLoaded()
self.view.setLoadingHistoryMessagesInProgress(false)
method onSendingMessageSuccess*(self: Module, message: MessageDto) =
let sender = self.controller.getContactById(message.`from`)
let senderDisplayName = sender.userNameOrAlias()
let amISender = message.`from` == singletonInstance.userProfile.getPubKey()
var senderIcon = sender.identicon
var isSenderIconIdenticon = sender.identicon.len > 0
if(sender.image.thumbnail.len > 0):
senderIcon = sender.image.thumbnail
isSenderIconIdenticon = false
var item = initItem(message.id, message.responseTo, message.`from`, senderDisplayName, sender.localNickname,
senderIcon, isSenderIconIdenticon, amISender, message.outgoingStatus, message.text, message.image, message.seen,
message.timestamp, message.contentType.ContentType, message.messageType)
self.view.model().prependItem(item)
self.view.emitSendingMessageSuccessSignal()
method onSendingMessageError*(self: Module) =
self.view.emitSendingMessageErrorSignal()
method loadMoreMessages*(self: Module) =
self.controller.loadMoreMessages()
method toggleReaction*(self: Module, messageId: string, emojiId: int) = method toggleReaction*(self: Module, messageId: string, emojiId: int) =
var emojiIdAsEnum: EmojiId var emojiIdAsEnum: EmojiId

View File

@ -12,3 +12,9 @@ method onReactionRemoved*(self: AccessInterface, messageId: string, emojiId: int
method onPinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} = method onPinUnpinMessage*(self: AccessInterface, messageId: string, pin: bool) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method onSendingMessageSuccess*(self: AccessInterface, message: MessageDto) {.base.} =
raise newException(ValueError, "No implementation available")
method onSendingMessageError*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -1,6 +1,9 @@
method viewDidLoad*(self: AccessInterface) {.base.} = method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method loadMoreMessages*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available")
method toggleReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} = method toggleReaction*(self: AccessInterface, messageId: string, emojiId: int) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")

View File

@ -8,6 +8,8 @@ QtObject:
delegate: io_interface.AccessInterface delegate: io_interface.AccessInterface
model: Model model: Model
modelVariant: QVariant modelVariant: QVariant
initialMessagesLoaded: bool
loadingHistoryMessagesInProgress: bool
proc delete*(self: View) = proc delete*(self: View) =
self.model.delete self.model.delete
@ -20,6 +22,8 @@ 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.loadingHistoryMessagesInProgress = false
proc load*(self: View) = proc load*(self: View) =
self.delegate.viewDidLoad() self.delegate.viewDidLoad()
@ -29,7 +33,6 @@ QtObject:
proc getModel(self: View): QVariant {.slot.} = proc getModel(self: View): QVariant {.slot.} =
return self.modelVariant return self.modelVariant
QtProperty[QVariant] model: QtProperty[QVariant] model:
read = getModel read = getModel
@ -65,3 +68,41 @@ 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 loadingHistoryMessagesInProgressChanged*(self: View) {.signal.}
proc getLoadingHistoryMessagesInProgress*(self: View): bool {.slot.} =
return self.loadingHistoryMessagesInProgress
QtProperty[bool] loadingHistoryMessagesInProgress:
read = getLoadingHistoryMessagesInProgress
notify = loadingHistoryMessagesInProgressChanged
proc setLoadingHistoryMessagesInProgress*(self: View, value: bool) = # this is not a slot
if (value == self.loadingHistoryMessagesInProgress):
return
self.loadingHistoryMessagesInProgress = value
self.loadingHistoryMessagesInProgressChanged()
proc loadMoreMessages*(self: View) {.slot.} =
self.setLoadingHistoryMessagesInProgress(true)
self.delegate.loadMoreMessages()
proc messageSuccessfullySent*(self: View) {.signal.}
proc emitSendingMessageSuccessSignal*(self: View) =
self.messageSuccessfullySent()
proc sendingMessageFailed*(self: View) {.signal.}
proc emitSendingMessageErrorSignal*(self: View) =
self.sendingMessageFailed()

View File

@ -173,6 +173,8 @@ method newPinnedMessagesLoaded*(self: Module, pinnedMessages: seq[PinnedMessageD
viewItems = item & viewItems # messages are sorted from the most recent to the least recent one viewItems = item & viewItems # messages are sorted from the most recent to the least recent one
if(viewItems.len == 0):
return
self.view.pinnedModel().prependItems(viewItems) self.view.pinnedModel().prependItems(viewItems)
method unpinMessage*(self: Module, messageId: string) = method unpinMessage*(self: Module, messageId: string) =

View File

@ -1,4 +1,4 @@
import Tables, json, strformat import json, strformat
import ../../../app_service/common/types import ../../../app_service/common/types
export types.ContentType export types.ContentType

View File

@ -158,6 +158,20 @@ QtObject:
self.endInsertRows() self.endInsertRows()
self.countChanged() self.countChanged()
proc appendItems*(self: Model, items: seq[Item]) =
if(items.len == 0):
return
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
let first = self.items.len
let last = first + items.len - 1
self.beginInsertRows(parentModelIndex, first, last)
self.items.add(items)
self.endInsertRows()
self.countChanged()
proc appendItem*(self: Model, item: Item) = proc appendItem*(self: Model, item: Item) =
let parentModelIndex = newQModelIndex() let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete defer: parentModelIndex.delete
@ -167,6 +181,15 @@ QtObject:
self.endInsertRows() self.endInsertRows()
self.countChanged() self.countChanged()
proc prependItem*(self: Model, item: Item) =
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginInsertRows(parentModelIndex, 0, 0)
self.items.insert(item, 0)
self.endInsertRows()
self.countChanged()
proc removeItem*(self: Model, messageId: string) = proc removeItem*(self: Model, messageId: string) =
let ind = self.findIndexForMessageId(messageId) let ind = self.findIndexForMessageId(messageId)
if(ind == -1): if(ind == -1):

View File

@ -111,8 +111,8 @@ QtObject:
proc processMessageUpdateAfterSend*(self: Service, response: RpcResponse[JsonNode]): (seq[ChatDto], seq[MessageDto]) = proc processMessageUpdateAfterSend*(self: Service, response: RpcResponse[JsonNode]): (seq[ChatDto], seq[MessageDto]) =
result = self.parseChatResponse(response) result = self.parseChatResponse(response)
var (chats, messages) = result var (chats, messages) = result
if chats.len == 0 and messages.len == 0: if chats.len == 0 or messages.len == 0:
self.events.emit(SIGNAL_SENDING_FAILED, Args()) error "no chats or messages in the parsed response"
return return
# This fixes issue#3490 # This fixes issue#3490
@ -251,7 +251,9 @@ QtObject:
preferredUsername, preferredUsername,
communityId) communityId)
discard self.processMessageUpdateAfterSend(response) let (chats, messages) = self.processMessageUpdateAfterSend(response)
if chats.len == 0 or messages.len == 0:
self.events.emit(SIGNAL_SENDING_FAILED, ChatArgs(chatId: chatId))
except Exception as e: except Exception as e:
error "Error sending message", msg = e.msg error "Error sending message", msg = e.msg

View File

@ -14,34 +14,37 @@ type
const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} = const asyncFetchChatMessagesTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
let arg = decode[AsyncFetchChatMessagesTaskArg](argEncoded) let arg = decode[AsyncFetchChatMessagesTaskArg](argEncoded)
var responseJson = %*{
"chatId": arg.chatId
}
# handle messages # handle messages
if(arg.msgCursor != CURSOR_VALUE_IGNORE):
var messagesArr: JsonNode var messagesArr: JsonNode
var messagesCursor: string var messagesCursor: JsonNode
let msgsResponse = status_go.fetchMessages(arg.chatId, arg.msgCursor, arg.limit) let msgsResponse = status_go.fetchMessages(arg.chatId, arg.msgCursor, arg.limit)
discard msgsResponse.result.getProp("cursor", messagesCursor) discard msgsResponse.result.getProp("cursor", messagesCursor)
discard msgsResponse.result.getProp("messages", messagesArr) discard msgsResponse.result.getProp("messages", messagesArr)
responseJson["messages"] = messagesArr
responseJson["messagesCursor"] = messagesCursor
# handle pinned messages # handle pinned messages
if(arg.pinnedMsgCursor != CURSOR_VALUE_IGNORE):
var pinnedMsgArr: JsonNode var pinnedMsgArr: JsonNode
var pinnedMsgCursor: string var pinnedMsgCursor: JsonNode
let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.pinnedMsgCursor, arg.limit) let pinnedMsgsResponse = status_go.fetchPinnedMessages(arg.chatId, arg.pinnedMsgCursor, arg.limit)
discard pinnedMsgsResponse.result.getProp("cursor", pinnedMsgCursor) discard pinnedMsgsResponse.result.getProp("cursor", pinnedMsgCursor)
discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr) discard pinnedMsgsResponse.result.getProp("pinnedMessages", pinnedMsgArr)
responseJson["pinnedMessages"] = pinnedMsgArr
responseJson["pinnedMessagesCursor"] = pinnedMsgCursor
# handle reactions # handle reactions
var reactionsArr: JsonNode if(arg.msgCursor != CURSOR_VALUE_IGNORE):
# messages and reactions are using the same cursor # messages and reactions are using the same cursor
var reactionsArr: JsonNode
let rResponse = status_go.fetchReactions(arg.chatId, arg.msgCursor, arg.limit) let rResponse = status_go.fetchReactions(arg.chatId, arg.msgCursor, arg.limit)
reactionsArr = rResponse.result reactionsArr = rResponse.result
responseJson["pinnedMessages"] = reactionsArr
let responseJson = %*{
"chatId": arg.chatId,
"messages": messagesArr,
"messagesCursor": messagesCursor,
"pinnedMessages": pinnedMsgArr,
"pinnedMessagesCursor": pinnedMsgCursor,
"reactions": reactionsArr
}
arg.finish(responseJson) arg.finish(responseJson)

View File

@ -13,12 +13,11 @@ export message_dto
export pinned_msg_dto export pinned_msg_dto
export reaction_dto export reaction_dto
include async_tasks
logScope: logScope:
topics = "messages-service" topics = "messages-service"
const MESSAGES_PER_PAGE = 20 const MESSAGES_PER_PAGE = 20
const CURSOR_VALUE_IGNORE = "ignore"
# Signals which may be emitted by this service: # Signals which may be emitted by this service:
const SIGNAL_MESSAGES_LOADED* = "new-messagesLoaded" #Once we are done with refactoring we should remove "new-" from all signals const SIGNAL_MESSAGES_LOADED* = "new-messagesLoaded" #Once we are done with refactoring we should remove "new-" from all signals
@ -29,6 +28,8 @@ const SIGNAL_MESSAGES_MARKED_AS_READ* = "new-messagesMarkedAsRead"
const SIGNAL_MESSAGE_REACTION_ADDED* = "new-messageReactionAdded" const SIGNAL_MESSAGE_REACTION_ADDED* = "new-messageReactionAdded"
const SIGNAL_MESSAGE_REACTION_REMOVED* = "new-messageReactionRemoved" const SIGNAL_MESSAGE_REACTION_REMOVED* = "new-messageReactionRemoved"
include async_tasks
type type
SearchMessagesLoadedArgs* = ref object of Args SearchMessagesLoadedArgs* = ref object of Args
messages*: seq[MessageDto] messages*: seq[MessageDto]
@ -59,7 +60,9 @@ QtObject:
events: EventEmitter events: EventEmitter
threadpool: ThreadPool threadpool: ThreadPool
msgCursor: Table[string, string] msgCursor: Table[string, string]
lastUsedMsgCursor: Table[string, string]
pinnedMsgCursor: Table[string, string] pinnedMsgCursor: Table[string, string]
lastUsedPinnedMsgCursor: Table[string, string]
numOfPinnedMessagesPerChat: Table[string, int] # [chat_id, num_of_pinned_messages] numOfPinnedMessagesPerChat: Table[string, int] # [chat_id, num_of_pinned_messages]
proc delete*(self: Service) = proc delete*(self: Service) =
@ -71,7 +74,9 @@ QtObject:
result.events = events result.events = events
result.threadpool = threadpool result.threadpool = threadpool
result.msgCursor = initTable[string, string]() result.msgCursor = initTable[string, string]()
result.lastUsedMsgCursor = initTable[string, string]()
result.pinnedMsgCursor = initTable[string, string]() result.pinnedMsgCursor = initTable[string, string]()
result.lastUsedPinnedMsgCursor = initTable[string, string]()
proc initialMessagesFetched(self: Service, chatId: string): bool = proc initialMessagesFetched(self: Service, chatId: string): bool =
return self.msgCursor.hasKey(chatId) return self.msgCursor.hasKey(chatId)
@ -100,10 +105,17 @@ QtObject:
var chatId: string var chatId: string
discard responseObj.getProp("chatId", chatId) discard responseObj.getProp("chatId", chatId)
# this is important case we don't want to fetch the same messages multiple times.
self.lastUsedMsgCursor[chatId] = self.msgCursor[chatId]
self.lastUsedPinnedMsgCursor[chatId] = self.pinnedMsgCursor[chatId]
# handling messages # handling messages
var msgCursor: string var msgCursor: string
if(responseObj.getProp("messagesCursor", msgCursor)): if(responseObj.getProp("messagesCursor", msgCursor)):
if(msgCursor.len > 0):
self.msgCursor[chatId] = msgCursor self.msgCursor[chatId] = msgCursor
else:
self.msgCursor[chatId] = self.lastUsedMsgCursor[chatId]
var messagesArr: JsonNode var messagesArr: JsonNode
var messages: seq[MessageDto] var messages: seq[MessageDto]
@ -113,7 +125,10 @@ QtObject:
# handling pinned messages # handling pinned messages
var pinnedMsgCursor: string var pinnedMsgCursor: string
if(responseObj.getProp("pinnedMessagesCursor", pinnedMsgCursor)): if(responseObj.getProp("pinnedMessagesCursor", pinnedMsgCursor)):
if(pinnedMsgCursor.len > 0):
self.pinnedMsgCursor[chatId] = pinnedMsgCursor self.pinnedMsgCursor[chatId] = pinnedMsgCursor
else:
self.pinnedMsgCursor[chatId] = self.lastUsedPinnedMsgCursor[chatId]
var pinnedMsgArr: JsonNode var pinnedMsgArr: JsonNode
var pinnedMessages: seq[PinnedMessageDto] var pinnedMessages: seq[PinnedMessageDto]
@ -142,13 +157,27 @@ QtObject:
error "empty chat id", methodName="asyncLoadMoreMessagesForChat" error "empty chat id", methodName="asyncLoadMoreMessagesForChat"
return return
var msgCursor = self.getCurrentMessageCursor(chatId)
if(self.lastUsedMsgCursor.hasKey(chatId) and msgCursor == self.lastUsedMsgCursor[chatId]):
msgCursor = CURSOR_VALUE_IGNORE
var pinnedMsgCursor = self.getCurrentPinnedMessageCursor(chatId)
if(self.lastUsedPinnedMsgCursor.hasKey(chatId) and pinnedMsgCursor == self.lastUsedPinnedMsgCursor[chatId]):
pinnedMsgCursor = CURSOR_VALUE_IGNORE
if(msgCursor == CURSOR_VALUE_IGNORE and pinnedMsgCursor == 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
let arg = AsyncFetchChatMessagesTaskArg( let arg = AsyncFetchChatMessagesTaskArg(
tptr: cast[ByteAddress](asyncFetchChatMessagesTask), tptr: cast[ByteAddress](asyncFetchChatMessagesTask),
vptr: cast[ByteAddress](self.vptr), vptr: cast[ByteAddress](self.vptr),
slot: "onAsyncLoadMoreMessagesForChat", slot: "onAsyncLoadMoreMessagesForChat",
chatId: chatId, chatId: chatId,
msgCursor: self.getCurrentMessageCursor(chatId), msgCursor: msgCursor,
pinnedMsgCursor: self.getCurrentPinnedMessageCursor(chatId), pinnedMsgCursor: pinnedMsgCursor,
limit: MESSAGES_PER_PAGE limit: MESSAGES_PER_PAGE
) )

View File

@ -5,6 +5,16 @@ QtObject {
id: root id: root
property var messageModule property var messageModule
property var messagesModel: messageModule.model
function loadMoreMessages () {
if(!messageModule)
return
if(!messageModule.initialMessagesLoaded || messageModule.loadingHistoryMessagesInProgress)
return
messageModule.loadMoreMessages()
}
function getMessageByIdAsJson (id) { function getMessageByIdAsJson (id) {
if(!messageModule) if(!messageModule)

View File

@ -36,9 +36,33 @@ Item {
property int countOnStartUp: 0 property int countOnStartUp: 0
signal openStickerPackPopup(string stickerPackId) signal openStickerPackPopup(string stickerPackId)
Item {
id: loadingMessagesIndicator
visible: messageStore.messageModule.loadingHistoryMessagesInProgress
anchors.top: parent.top
anchors.left: parent.left
height: visible? 20 : 0
width: parent.width
Loader {
active: messageStore.messageModule.loadingHistoryMessagesInProgress
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
sourceComponent: Component {
LoadingAnimation {
width: 18
height: 18
}
}
}
}
ListView { ListView {
id: chatLogView id: chatLogView
anchors.fill: parent anchors.top: loadingMessagesIndicator.bottom
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.right: parent.right
spacing: 0 spacing: 0
boundsBehavior: Flickable.StopAtBounds boundsBehavior: Flickable.StopAtBounds
clip: true clip: true
@ -92,9 +116,9 @@ Item {
// } // }
// } // }
// ScrollBar.vertical: ScrollBar { ScrollBar.vertical: ScrollBar {
// visible: chatLogView.visibleArea.heightRatio < 1 visible: chatLogView.visibleArea.heightRatio < 1
// } }
// Connections { // Connections {
// id: contentHeightConnection // id: contentHeightConnection
@ -112,80 +136,79 @@ Item {
id: timer id: timer
} }
// Button { Button {
// readonly property int buttonPadding: 5 readonly property int buttonPadding: 5
// id: scrollDownButton id: scrollDownButton
// visible: false visible: false
// height: 32 height: 32
// width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0) width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0)
// anchors.bottom: parent.bottom anchors.bottom: parent.bottom
// anchors.right: parent.right anchors.right: parent.right
// anchors.rightMargin: Style.current.padding anchors.rightMargin: Style.current.padding
// background: Rectangle { background: Rectangle {
// color: Style.current.buttonSecondaryColor color: Style.current.buttonSecondaryColor
// border.width: 0 border.width: 0
// radius: 16 radius: 16
// } }
// onClicked: { onClicked: {
// newMessages = 0 newMessages = 0
// scrollDownButton.visible = false scrollDownButton.visible = false
// chatLogView.scrollToBottom(true) chatLogView.scrollToBottom(true)
// } }
// StyledText { StyledText {
// id: nbMessages id: nbMessages
// visible: newMessages > 0 visible: newMessages > 0
// width: visible ? implicitWidth : 0 width: visible ? implicitWidth : 0
// text: newMessages text: newMessages
// anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// anchors.left: parent.left anchors.left: parent.left
// color: Style.current.pillButtonTextColor color: Style.current.pillButtonTextColor
// font.pixelSize: 15 font.pixelSize: 15
// anchors.leftMargin: Style.current.halfPadding anchors.leftMargin: Style.current.halfPadding
// } }
// SVGImage { SVGImage {
// id: arrowImage id: arrowImage
// width: 24 width: 24
// height: 24 height: 24
// anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
// anchors.left: nbMessages.right anchors.left: nbMessages.right
// source: Style.svg("leave_chat") source: Style.svg("leave_chat")
// anchors.leftMargin: nbMessages.visible ? scrollDownButton.buttonPadding : 0 anchors.leftMargin: nbMessages.visible ? scrollDownButton.buttonPadding : 0
// rotation: -90 rotation: -90
// ColorOverlay { ColorOverlay {
// anchors.fill: parent anchors.fill: parent
// source: parent source: parent
// color: Style.current.pillButtonTextColor color: Style.current.pillButtonTextColor
// } }
// } }
// MouseArea { MouseArea {
// cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
// anchors.fill: parent anchors.fill: parent
// onPressed: mouse.accepted = false onPressed: mouse.accepted = false
// } }
// } }
function scrollToBottom(force, caller) { function scrollToBottom(force, caller) {
// Not Refactored Yet if (!force && !chatLogView.atYEnd) {
// if (!force && !chatLogView.atYEnd) { // User has scrolled up, we don't want to scroll back
// // User has scrolled up, we don't want to scroll back return false
// return false }
// } if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
// if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) { // If we have a caller, only accept its request if it's the last message
// // If we have a caller, only accept its request if it's the last message return false
// return false }
// } // Call this twice and with a timer since the first scroll to bottom might have happened before some stuff loads
// // Call this twice and with a timer since the first scroll to bottom might have happened before some stuff loads // meaning that the scroll will not actually be at the bottom on switch
// // meaning that the scroll will not actually be at the bottom on switch // Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
// // Add a small delay because images, even though they say they say they are loaed, they aren't shown yet Qt.callLater(chatLogView.positionViewAtBeginning)
// Qt.callLater(chatLogView.positionViewAtBeginning) timer.setTimeout(function() {
// timer.setTimeout(function() { Qt.callLater(chatLogView.positionViewAtBeginning)
// Qt.callLater(chatLogView.positionViewAtBeginning) }, 100);
// }, 100);
return true return true
} }
@ -198,24 +221,24 @@ Item {
// } // }
// } // }
// Connections { Connections {
target: messageStore.messageModule
onMessageSuccessfullySent: {
chatLogView.scrollToBottom(true)
}
onSendingMessageFailed: {
sendingMsgFailedPopup.open();
}
// Not Refactored Yet // Not Refactored Yet
// target: root.store.chatsModelInst.messageView
// onSendingMessageSuccess: {
// chatLogView.scrollToBottom(true)
// }
// onSendingMessageFailed: {
// sendingMsgFailedPopup.open();
// }
// onNewMessagePushed: { // onNewMessagePushed: {
// if (!chatLogView.scrollToBottom()) { // if (!chatLogView.scrollToBottom()) {
// newMessages++ // newMessages++
// } // }
// } // }
// } }
// Connections { // Connections {
// Not Refactored Yet // Not Refactored Yet
@ -274,24 +297,15 @@ Item {
// } // }
// } // }
// Not Refactored Yet onContentYChanged: {
// property var loadMsgs : Backpressure.oneInTime(chatLogView, 500, function() { scrollDownButton.visible = contentHeight - (scrollY + height) > 400
// if(!messages.initialMessagesLoaded || messages.loadingHistoryMessages) let loadMore = scrollDownButton.visible && scrollY < 500
// return if(loadMore){
messageStore.loadMoreMessages()
}
}
// root.store.chatsModelInst.messageView.loadMoreMessages(chatId); model: messageStore.messagesModel
// });
// onContentYChanged: {
// scrollDownButton.visible = (contentHeight - (scrollY + height) > 400)
// if(scrollDownButton.visible && scrollY < 500){
// loadMsgs();
// }
// }
model: messageStore.messageModule.model
section.property: "sectionIdentifier"
section.criteria: ViewSection.FullString
// Not Refactored Yet // Not Refactored Yet
//Component.onCompleted: scrollToBottom(true) //Component.onCompleted: scrollToBottom(true)
@ -330,11 +344,11 @@ Item {
} }
} }
// MessageDialog { MessageDialog {
// id: sendingMsgFailedPopup id: sendingMsgFailedPopup
// standardButtons: StandardButton.Ok standardButtons: StandardButton.Ok
// //% "Failed to send message." //% "Failed to send message."
// text: qsTrId("failed-to-send-message-") text: qsTrId("failed-to-send-message-")
// icon: StandardIcon.Critical icon: StandardIcon.Critical
// } }
} }