fix(@desktop/chat): support to mentions in message edit mode
This commit is contained in:
parent
0ef1dee5ce
commit
544b0aafc7
|
@ -15,7 +15,7 @@ import "../ContactsColumn"
|
||||||
import "../CommunityComponents"
|
import "../CommunityComponents"
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: svRoot
|
id: root
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
property alias chatLogView: chatLogView
|
property alias chatLogView: chatLogView
|
||||||
|
@ -26,317 +26,307 @@ Item {
|
||||||
property bool loadingMessages: false
|
property bool loadingMessages: false
|
||||||
property real scrollY: chatLogView.visibleArea.yPosition * chatLogView.contentHeight
|
property real scrollY: chatLogView.visibleArea.yPosition * chatLogView.contentHeight
|
||||||
property int newMessages: 0
|
property int newMessages: 0
|
||||||
|
property int countOnStartUp: 0
|
||||||
|
|
||||||
ScrollView {
|
ListView {
|
||||||
id: root
|
id: chatLogView
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
contentHeight: childrenRect.height
|
anchors.bottomMargin: Style.current.bigPadding
|
||||||
contentItem: chatLogView
|
spacing: appSettings.useCompactMode ? 0 : 4
|
||||||
ScrollBar.vertical.policy: chatLogView.contentHeight > chatLogView.height ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
boundsBehavior: Flickable.StopAtBounds
|
||||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
clip: true
|
||||||
|
flickDeceleration: {
|
||||||
property int countOnStartUp: 0
|
if (utilsModel.getOs() === Constants.windows) {
|
||||||
|
return 5000
|
||||||
ListView {
|
|
||||||
id: chatLogView
|
|
||||||
anchors.fill: parent
|
|
||||||
anchors.bottomMargin: Style.current.bigPadding
|
|
||||||
spacing: appSettings.useCompactMode ? 0 : 4
|
|
||||||
boundsBehavior: Flickable.StopAtBounds
|
|
||||||
flickDeceleration: {
|
|
||||||
if (utilsModel.getOs() === Constants.windows) {
|
|
||||||
return 5000
|
|
||||||
}
|
|
||||||
return 10000
|
|
||||||
}
|
}
|
||||||
verticalLayoutDirection: ListView.BottomToTop
|
return 10000
|
||||||
|
}
|
||||||
|
verticalLayoutDirection: ListView.BottomToTop
|
||||||
|
|
||||||
// This header and Connections is to create an invisible padding so that the chat identifier is at the top
|
// This header and Connections is to create an invisible padding so that the chat identifier is at the top
|
||||||
// The Connections is necessary, because doing the check inside the header created a binding loop (the contentHeight includes the header height
|
// The Connections is necessary, because doing the check inside the header created a binding loop (the contentHeight includes the header height
|
||||||
// If the content height is smaller than the full height, we "show" the padding so that the chat identifier is at the top, otherwise we disable the Connections
|
// If the content height is smaller than the full height, we "show" the padding so that the chat identifier is at the top, otherwise we disable the Connections
|
||||||
header: Item {
|
header: Item {
|
||||||
height: 0
|
height: 0
|
||||||
width: chatLogView.width
|
width: chatLogView.width
|
||||||
}
|
|
||||||
function checkHeaderHeight() {
|
|
||||||
if (!chatLogView.headerItem) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (chatLogView.contentItem.height - chatLogView.headerItem.height < chatLogView.height) {
|
|
||||||
chatLogView.headerItem.height = chatLogView.height - (chatLogView.contentItem.height - chatLogView.headerItem.height) - 36
|
|
||||||
} else {
|
|
||||||
chatLogView.headerItem.height = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property var scrollToMessage: function (messageId) {
|
|
||||||
let item
|
|
||||||
for (let i = 0; i < messageListDelegate.count; i++) {
|
|
||||||
item = messageListDelegate.items.get(i)
|
|
||||||
if (item.model.messageId === messageId) {
|
|
||||||
chatLogView.positionViewAtIndex(i, ListView.Center)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
id: contentHeightConnection
|
|
||||||
enabled: true
|
|
||||||
target: chatLogView
|
|
||||||
onContentHeightChanged: {
|
|
||||||
chatLogView.checkHeaderHeight()
|
|
||||||
}
|
|
||||||
onHeightChanged: {
|
|
||||||
chatLogView.checkHeaderHeight()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: timer
|
|
||||||
}
|
|
||||||
|
|
||||||
Button {
|
|
||||||
readonly property int buttonPadding: 5
|
|
||||||
|
|
||||||
id: scrollDownButton
|
|
||||||
visible: false
|
|
||||||
height: 32
|
|
||||||
width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0)
|
|
||||||
anchors.bottom: parent.bottom
|
|
||||||
anchors.right: parent.right
|
|
||||||
anchors.rightMargin: Style.current.padding
|
|
||||||
background: Rectangle {
|
|
||||||
color: Style.current.buttonSecondaryColor
|
|
||||||
border.width: 0
|
|
||||||
radius: 16
|
|
||||||
}
|
|
||||||
onClicked: {
|
|
||||||
newMessages = 0
|
|
||||||
scrollDownButton.visible = false
|
|
||||||
chatLogView.scrollToBottom(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
StyledText {
|
|
||||||
id: nbMessages
|
|
||||||
visible: newMessages > 0
|
|
||||||
width: visible ? implicitWidth : 0
|
|
||||||
text: newMessages
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: parent.left
|
|
||||||
color: Style.current.pillButtonTextColor
|
|
||||||
font.pixelSize: 15
|
|
||||||
anchors.leftMargin: Style.current.halfPadding
|
|
||||||
}
|
|
||||||
|
|
||||||
SVGImage {
|
|
||||||
id: arrowImage
|
|
||||||
width: 24
|
|
||||||
height: 24
|
|
||||||
anchors.verticalCenter: parent.verticalCenter
|
|
||||||
anchors.left: nbMessages.right
|
|
||||||
source: "../../../img/leave_chat.svg"
|
|
||||||
anchors.leftMargin: nbMessages.visible ? scrollDownButton.buttonPadding : 0
|
|
||||||
rotation: -90
|
|
||||||
|
|
||||||
ColorOverlay {
|
|
||||||
anchors.fill: parent
|
|
||||||
source: parent
|
|
||||||
color: Style.current.pillButtonTextColor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MouseArea {
|
|
||||||
cursorShape: Qt.PointingHandCursor
|
|
||||||
anchors.fill: parent
|
|
||||||
onPressed: mouse.accepted = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function scrollToBottom(force, caller) {
|
|
||||||
if (!force && !chatLogView.atYEnd) {
|
|
||||||
// User has scrolled up, we don't want to scroll back
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
|
|
||||||
// If we have a caller, only accept its request if it's the last message
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
// 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
|
|
||||||
// Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
|
|
||||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
|
||||||
timer.setTimeout(function() {
|
|
||||||
Qt.callLater(chatLogView.positionViewAtBeginning)
|
|
||||||
}, 100);
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: chatsModel
|
|
||||||
|
|
||||||
onAppReady: {
|
|
||||||
chatLogView.scrollToBottom(true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: chatsModel.messageView
|
|
||||||
onMessagesLoaded: {
|
|
||||||
loadingMessages = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
onSendingMessage: {
|
|
||||||
chatLogView.scrollToBottom(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
onSendingMessageFailed: {
|
|
||||||
sendingMsgFailedPopup.open();
|
|
||||||
}
|
|
||||||
|
|
||||||
onNewMessagePushed: {
|
|
||||||
if (!chatLogView.scrollToBottom()) {
|
|
||||||
newMessages++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
|
||||||
target: chatsModel.communities
|
|
||||||
|
|
||||||
onMembershipRequestChanged: function (communityId, communityName, accepted) {
|
|
||||||
chatColumnLayout.currentNotificationChatId = null
|
|
||||||
chatColumnLayout.currentNotificationCommunityId = communityId
|
|
||||||
systemTray.showMessage("Status",
|
|
||||||
//% "You have been accepted into the ‘%1’ community"
|
|
||||||
accepted ? qsTrId("you-have-been-accepted-into-the---1--community").arg(communityName) :
|
|
||||||
//% "Your request to join the ‘%1’ community was declined"
|
|
||||||
qsTrId("your-request-to-join-the---1--community-was-declined").arg(communityName),
|
|
||||||
SystemTrayIcon.NoIcon,
|
|
||||||
Constants.notificationPopupTTL)
|
|
||||||
}
|
|
||||||
|
|
||||||
onMembershipRequestPushed: function (communityId, communityName, pubKey) {
|
|
||||||
chatColumnLayout.currentNotificationChatId = null
|
|
||||||
chatColumnLayout.currentNotificationCommunityId = communityId
|
|
||||||
//% "New membership request"
|
|
||||||
systemTray.showMessage(qsTrId("new-membership-request"),
|
|
||||||
//% "%1 asks to join ‘%2’"
|
|
||||||
qsTrId("-1-asks-to-join---2-").arg(Utils.getDisplayName(pubKey)).arg(communityName),
|
|
||||||
SystemTrayIcon.NoIcon,
|
|
||||||
Constants.notificationPopupTTL)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
property var loadMsgs : Backpressure.oneInTime(chatLogView, 500, function() {
|
|
||||||
if(loadingMessages) return;
|
|
||||||
loadingMessages = true;
|
|
||||||
chatsModel.messageView.loadMoreMessages();
|
|
||||||
});
|
|
||||||
|
|
||||||
onContentYChanged: {
|
|
||||||
scrollDownButton.visible = (contentHeight - (scrollY + height) > 400)
|
|
||||||
if(scrollY < 500){
|
|
||||||
loadMsgs();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
model: messageListDelegate
|
|
||||||
section.property: "sectionIdentifier"
|
|
||||||
section.criteria: ViewSection.FullString
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageDialog {
|
function checkHeaderHeight() {
|
||||||
id: sendingMsgFailedPopup
|
if (!chatLogView.headerItem) {
|
||||||
standardButtons: StandardButton.Ok
|
return
|
||||||
//% "Failed to send message."
|
}
|
||||||
text: qsTrId("failed-to-send-message-")
|
|
||||||
icon: StandardIcon.Critical
|
if (chatLogView.contentItem.height - chatLogView.headerItem.height < chatLogView.height) {
|
||||||
|
chatLogView.headerItem.height = chatLogView.height - (chatLogView.contentItem.height - chatLogView.headerItem.height) - 36
|
||||||
|
} else {
|
||||||
|
chatLogView.headerItem.height = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property var scrollToMessage: function (messageId) {
|
||||||
|
let item
|
||||||
|
for (let i = 0; i < messageListDelegate.count; i++) {
|
||||||
|
item = messageListDelegate.items.get(i)
|
||||||
|
if (item.model.messageId === messageId) {
|
||||||
|
chatLogView.positionViewAtIndex(i, ListView.Center)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScrollBar.vertical: ScrollBar {
|
||||||
|
visible: chatLogView.visibleArea.heightRatio < 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
id: contentHeightConnection
|
||||||
|
enabled: true
|
||||||
|
target: chatLogView
|
||||||
|
onContentHeightChanged: {
|
||||||
|
chatLogView.checkHeaderHeight()
|
||||||
|
}
|
||||||
|
onHeightChanged: {
|
||||||
|
chatLogView.checkHeaderHeight()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Timer {
|
Timer {
|
||||||
id: modelLoadingDelayTimer
|
id: timer
|
||||||
interval: 1000
|
}
|
||||||
onTriggered: {
|
|
||||||
root.countOnStartUp = messageListDelegate.count;
|
Button {
|
||||||
|
readonly property int buttonPadding: 5
|
||||||
|
|
||||||
|
id: scrollDownButton
|
||||||
|
visible: false
|
||||||
|
height: 32
|
||||||
|
width: nbMessages.width + arrowImage.width + 2 * Style.current.halfPadding + (nbMessages.visible ? scrollDownButton.buttonPadding : 0)
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.rightMargin: Style.current.padding
|
||||||
|
background: Rectangle {
|
||||||
|
color: Style.current.buttonSecondaryColor
|
||||||
|
border.width: 0
|
||||||
|
radius: 16
|
||||||
|
}
|
||||||
|
onClicked: {
|
||||||
|
newMessages = 0
|
||||||
|
scrollDownButton.visible = false
|
||||||
|
chatLogView.scrollToBottom(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
id: nbMessages
|
||||||
|
visible: newMessages > 0
|
||||||
|
width: visible ? implicitWidth : 0
|
||||||
|
text: newMessages
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: parent.left
|
||||||
|
color: Style.current.pillButtonTextColor
|
||||||
|
font.pixelSize: 15
|
||||||
|
anchors.leftMargin: Style.current.halfPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
SVGImage {
|
||||||
|
id: arrowImage
|
||||||
|
width: 24
|
||||||
|
height: 24
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
anchors.left: nbMessages.right
|
||||||
|
source: "../../../img/leave_chat.svg"
|
||||||
|
anchors.leftMargin: nbMessages.visible ? scrollDownButton.buttonPadding : 0
|
||||||
|
rotation: -90
|
||||||
|
|
||||||
|
ColorOverlay {
|
||||||
|
anchors.fill: parent
|
||||||
|
source: parent
|
||||||
|
color: Style.current.pillButtonTextColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
cursorShape: Qt.PointingHandCursor
|
||||||
|
anchors.fill: parent
|
||||||
|
onPressed: mouse.accepted = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DelegateModelGeneralized {
|
function scrollToBottom(force, caller) {
|
||||||
id: messageListDelegate
|
if (!force && !chatLogView.atYEnd) {
|
||||||
lessThan: [
|
// User has scrolled up, we don't want to scroll back
|
||||||
function(left, right) { return left.clock > right.clock }
|
return false
|
||||||
]
|
|
||||||
|
|
||||||
model: messageList
|
|
||||||
|
|
||||||
delegate: Message {
|
|
||||||
id: msgDelegate
|
|
||||||
fromAuthor: model.fromAuthor
|
|
||||||
chatId: model.chatId
|
|
||||||
userName: model.userName
|
|
||||||
alias: model.alias
|
|
||||||
localName: model.localName
|
|
||||||
message: model.message
|
|
||||||
plainText: model.plainText
|
|
||||||
identicon: model.identicon
|
|
||||||
isCurrentUser: model.isCurrentUser
|
|
||||||
timestamp: model.timestamp
|
|
||||||
sticker: model.sticker
|
|
||||||
contentType: model.contentType
|
|
||||||
replaces: model.replaces
|
|
||||||
isEdited: model.isEdited
|
|
||||||
outgoingStatus: model.outgoingStatus
|
|
||||||
responseTo: model.responseTo
|
|
||||||
authorCurrentMsg: msgDelegate.ListView.section
|
|
||||||
// The previous message is actually the nextSection since we reversed the list order
|
|
||||||
authorPrevMsg: msgDelegate.ListView.nextSection
|
|
||||||
imageClick: imagePopup.openPopup.bind(imagePopup)
|
|
||||||
messageId: model.messageId
|
|
||||||
emojiReactions: model.emojiReactions
|
|
||||||
linkUrls: model.linkUrls
|
|
||||||
communityId: model.communityId
|
|
||||||
hasMention: model.hasMention
|
|
||||||
stickerPackId: model.stickerPackId
|
|
||||||
pinnedMessage: model.isPinned
|
|
||||||
pinnedBy: model.pinnedBy
|
|
||||||
gapFrom: model.gapFrom
|
|
||||||
gapTo: model.gapTo
|
|
||||||
Component.onCompleted: {
|
|
||||||
if ((root.countOnStartUp > 0) && (root.countOnStartUp - 1) < index) {
|
|
||||||
//new message, increment z order
|
|
||||||
z = index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
messageContextMenu: svRoot.messageContextMenuInst
|
|
||||||
|
|
||||||
prevMessageIndex: {
|
|
||||||
// This is used in order to have access to the previous message and determine the timestamp
|
|
||||||
// we can't rely on the index because the sequence of messages is not ordered on the nim side
|
|
||||||
if (msgDelegate.DelegateModel.itemsIndex < messageListDelegate.items.count - 1) {
|
|
||||||
return messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex + 1).model.index
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
nextMessageIndex: {
|
|
||||||
if (msgDelegate.DelegateModel.itemsIndex < 1) {
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
return messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex - 1).model.index
|
|
||||||
}
|
|
||||||
scrollToBottom: chatLogView.scrollToBottom
|
|
||||||
timeout: model.timeout
|
|
||||||
}
|
}
|
||||||
|
if (caller && caller !== chatLogView.itemAtIndex(chatLogView.count - 1)) {
|
||||||
|
// If we have a caller, only accept its request if it's the last message
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
// Add a small delay because images, even though they say they say they are loaed, they aren't shown yet
|
||||||
|
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||||
|
timer.setTimeout(function() {
|
||||||
|
Qt.callLater(chatLogView.positionViewAtBeginning)
|
||||||
|
}, 100);
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: chatsModel
|
||||||
|
|
||||||
|
onAppReady: {
|
||||||
|
chatLogView.scrollToBottom(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: chatsModel.messageView
|
||||||
|
onMessagesLoaded: {
|
||||||
|
loadingMessages = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onSendingMessage: {
|
||||||
|
chatLogView.scrollToBottom(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
onSendingMessageFailed: {
|
||||||
|
sendingMsgFailedPopup.open();
|
||||||
|
}
|
||||||
|
|
||||||
|
onNewMessagePushed: {
|
||||||
|
if (!chatLogView.scrollToBottom()) {
|
||||||
|
newMessages++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: chatsModel.communities
|
||||||
|
|
||||||
|
onMembershipRequestChanged: function (communityId, communityName, accepted) {
|
||||||
|
chatColumnLayout.currentNotificationChatId = null
|
||||||
|
chatColumnLayout.currentNotificationCommunityId = communityId
|
||||||
|
systemTray.showMessage("Status",
|
||||||
|
//% "You have been accepted into the ‘%1’ community"
|
||||||
|
accepted ? qsTrId("you-have-been-accepted-into-the---1--community").arg(communityName) :
|
||||||
|
//% "Your request to join the ‘%1’ community was declined"
|
||||||
|
qsTrId("your-request-to-join-the---1--community-was-declined").arg(communityName),
|
||||||
|
SystemTrayIcon.NoIcon,
|
||||||
|
Constants.notificationPopupTTL)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMembershipRequestPushed: function (communityId, communityName, pubKey) {
|
||||||
|
chatColumnLayout.currentNotificationChatId = null
|
||||||
|
chatColumnLayout.currentNotificationCommunityId = communityId
|
||||||
|
//% "New membership request"
|
||||||
|
systemTray.showMessage(qsTrId("new-membership-request"),
|
||||||
|
//% "%1 asks to join ‘%2’"
|
||||||
|
qsTrId("-1-asks-to-join---2-").arg(Utils.getDisplayName(pubKey)).arg(communityName),
|
||||||
|
SystemTrayIcon.NoIcon,
|
||||||
|
Constants.notificationPopupTTL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
property var loadMsgs : Backpressure.oneInTime(chatLogView, 500, function() {
|
||||||
|
if(loadingMessages) return;
|
||||||
|
loadingMessages = true;
|
||||||
|
chatsModel.messageView.loadMoreMessages();
|
||||||
|
});
|
||||||
|
|
||||||
|
onContentYChanged: {
|
||||||
|
scrollDownButton.visible = (contentHeight - (scrollY + height) > 400)
|
||||||
|
if(scrollY < 500){
|
||||||
|
loadMsgs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
model: messageListDelegate
|
||||||
|
section.property: "sectionIdentifier"
|
||||||
|
section.criteria: ViewSection.FullString
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDialog {
|
||||||
|
id: sendingMsgFailedPopup
|
||||||
|
standardButtons: StandardButton.Ok
|
||||||
|
//% "Failed to send message."
|
||||||
|
text: qsTrId("failed-to-send-message-")
|
||||||
|
icon: StandardIcon.Critical
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: modelLoadingDelayTimer
|
||||||
|
interval: 1000
|
||||||
|
onTriggered: {
|
||||||
|
root.countOnStartUp = messageListDelegate.count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateModelGeneralized {
|
||||||
|
id: messageListDelegate
|
||||||
|
lessThan: [
|
||||||
|
function(left, right) { return left.clock > right.clock }
|
||||||
|
]
|
||||||
|
|
||||||
|
model: messageList
|
||||||
|
|
||||||
|
delegate: Message {
|
||||||
|
id: msgDelegate
|
||||||
|
fromAuthor: model.fromAuthor
|
||||||
|
chatId: model.chatId
|
||||||
|
userName: model.userName
|
||||||
|
alias: model.alias
|
||||||
|
localName: model.localName
|
||||||
|
message: model.message
|
||||||
|
plainText: model.plainText
|
||||||
|
identicon: model.identicon
|
||||||
|
isCurrentUser: model.isCurrentUser
|
||||||
|
timestamp: model.timestamp
|
||||||
|
sticker: model.sticker
|
||||||
|
contentType: model.contentType
|
||||||
|
replaces: model.replaces
|
||||||
|
isEdited: model.isEdited
|
||||||
|
outgoingStatus: model.outgoingStatus
|
||||||
|
responseTo: model.responseTo
|
||||||
|
authorCurrentMsg: msgDelegate.ListView.section
|
||||||
|
// The previous message is actually the nextSection since we reversed the list order
|
||||||
|
authorPrevMsg: msgDelegate.ListView.nextSection
|
||||||
|
imageClick: imagePopup.openPopup.bind(imagePopup)
|
||||||
|
messageId: model.messageId
|
||||||
|
emojiReactions: model.emojiReactions
|
||||||
|
linkUrls: model.linkUrls
|
||||||
|
communityId: model.communityId
|
||||||
|
hasMention: model.hasMention
|
||||||
|
stickerPackId: model.stickerPackId
|
||||||
|
pinnedMessage: model.isPinned
|
||||||
|
pinnedBy: model.pinnedBy
|
||||||
|
gapFrom: model.gapFrom
|
||||||
|
gapTo: model.gapTo
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
modelLoadingDelayTimer.start();
|
if ((root.countOnStartUp > 0) && (root.countOnStartUp - 1) < index) {
|
||||||
|
//new message, increment z order
|
||||||
|
z = index;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
messageContextMenu: root.messageContextMenuInst
|
||||||
|
|
||||||
|
prevMessageIndex: {
|
||||||
|
// This is used in order to have access to the previous message and determine the timestamp
|
||||||
|
// we can't rely on the index because the sequence of messages is not ordered on the nim side
|
||||||
|
if (msgDelegate.DelegateModel.itemsIndex < messageListDelegate.items.count - 1) {
|
||||||
|
return messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex + 1).model.index
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
nextMessageIndex: {
|
||||||
|
if (msgDelegate.DelegateModel.itemsIndex < 1) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return messageListDelegate.items.get(msgDelegate.DelegateModel.itemsIndex - 1).model.index
|
||||||
|
}
|
||||||
|
scrollToBottom: chatLogView.scrollToBottom
|
||||||
|
timeout: model.timeout
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
modelLoadingDelayTimer.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*##^##
|
|
||||||
Designer {
|
|
||||||
D{i:0;autoSize:true;height:480;width:640}
|
|
||||||
}
|
|
||||||
##^##*/
|
|
||||||
|
|
|
@ -24,6 +24,14 @@ Item {
|
||||||
height: messageContainer.height + messageContainer.anchors.topMargin
|
height: messageContainer.height + messageContainer.anchors.topMargin
|
||||||
+ (dateGroupLbl.visible ? dateGroupLbl.height + dateGroupLbl.anchors.topMargin : 0)
|
+ (dateGroupLbl.visible ? dateGroupLbl.height + dateGroupLbl.anchors.topMargin : 0)
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: ensureMessageFullyVisibleTimer
|
||||||
|
interval: 1
|
||||||
|
onTriggered: {
|
||||||
|
chatLogView.positionViewAtIndex(ListView.currentIndex, ListView.Contain)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MouseArea {
|
MouseArea {
|
||||||
enabled: !placeholderMessage
|
enabled: !placeholderMessage
|
||||||
anchors.fill: messageContainer
|
anchors.fill: messageContainer
|
||||||
|
@ -195,16 +203,38 @@ Item {
|
||||||
sourceComponent: Item {
|
sourceComponent: Item {
|
||||||
id: editText
|
id: editText
|
||||||
height: childrenRect.height
|
height: childrenRect.height
|
||||||
|
|
||||||
|
property bool suggestionsOpened: false
|
||||||
|
Keys.onEscapePressed: {
|
||||||
|
if (!suggestionsOpened) {
|
||||||
|
cancelBtn.clicked()
|
||||||
|
}
|
||||||
|
suggestionsOpened = false
|
||||||
|
}
|
||||||
|
|
||||||
StatusChatInput {
|
StatusChatInput {
|
||||||
|
id: editTextInput
|
||||||
|
readonly property string originalText: Utils.getMessageWithStyle(Emoji.parse(message))
|
||||||
Component.onCompleted: {
|
Component.onCompleted: {
|
||||||
textInput.forceActiveFocus();
|
suggestionsList.clear()
|
||||||
|
for (let i = 0; i < chatInput.suggestionsList.count; i++) {
|
||||||
|
suggestionsList.append(chatInput.suggestionsList.get(i))
|
||||||
|
}
|
||||||
|
textInput.forceActiveFocus()
|
||||||
textInput.cursorPosition = textInput.length
|
textInput.cursorPosition = textInput.length
|
||||||
}
|
}
|
||||||
id: editTextInput
|
|
||||||
chatInputPlaceholder: qsTrId("type-a-message-")
|
chatInputPlaceholder: qsTrId("type-a-message-")
|
||||||
chatType: chatsModel.channelView.activeChannel.chatType
|
chatType: chatsModel.channelView.activeChannel.chatType
|
||||||
isEdit: true
|
isEdit: true
|
||||||
textInput.text: Utils.getMessageWithStyle(Emoji.parse(message.replace(/(<a href="\/\/0x[0-9A-Fa-f]+" class="mention">)/g, "$1@")))
|
textInput.text: originalText
|
||||||
|
onSendMessage: {
|
||||||
|
saveBtn.clicked()
|
||||||
|
}
|
||||||
|
suggestions.onVisibleChanged: {
|
||||||
|
if (suggestions.visible) {
|
||||||
|
editText.suggestionsOpened = true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusButton {
|
StatusButton {
|
||||||
|
@ -218,6 +248,7 @@ Item {
|
||||||
onClicked: {
|
onClicked: {
|
||||||
isEdit = false
|
isEdit = false
|
||||||
editTextInput.textInput.text = Emoji.parse(message)
|
editTextInput.textInput.text = Emoji.parse(message)
|
||||||
|
ensureMessageFullyVisibleTimer.start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +259,7 @@ Item {
|
||||||
anchors.top: editTextInput.bottom
|
anchors.top: editTextInput.bottom
|
||||||
//% "Save"
|
//% "Save"
|
||||||
text: qsTrId("save")
|
text: qsTrId("save")
|
||||||
|
enabled: editTextInput.textInput.text.trim().length > 0
|
||||||
onClicked: {
|
onClicked: {
|
||||||
let msg = chatsModel.plainText(Emoji.deparse(editTextInput.textInput.text))
|
let msg = chatsModel.plainText(Emoji.deparse(editTextInput.textInput.text))
|
||||||
if (msg.length > 0){
|
if (msg.length > 0){
|
||||||
|
@ -251,7 +283,7 @@ Item {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
anchors.rightMargin: root.chatHorizontalPadding
|
anchors.rightMargin: root.chatHorizontalPadding
|
||||||
visible: !isEdit
|
visible: !isEdit
|
||||||
|
|
||||||
ChatText {
|
ChatText {
|
||||||
readonly property int leftPadding: chatImage.anchors.leftMargin + chatImage.width + root.chatHorizontalPadding
|
readonly property int leftPadding: chatImage.anchors.leftMargin + chatImage.width + root.chatHorizontalPadding
|
||||||
id: chatText
|
id: chatText
|
||||||
|
|
|
@ -143,7 +143,9 @@ ModalPopup {
|
||||||
reactionModel: EmojiReactions { }
|
reactionModel: EmojiReactions { }
|
||||||
|
|
||||||
onCloseParentPopup: {
|
onCloseParentPopup: {
|
||||||
messageItem.view.closePopup()
|
if (messageItem.view) {
|
||||||
|
messageItem.view.closePopup()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue