2020-06-17 19:18:31 +00:00
import QtQuick 2.13
import QtQuick . Controls 2.13
import QtQuick . Layouts 1.13
2020-11-30 21:24:01 +00:00
import QtQuick . Dialogs 1.3
2021-10-27 21:27:49 +00:00
2022-07-14 11:03:36 +00:00
import StatusQ . Core 0.1
2022-09-19 08:40:02 +00:00
import StatusQ . Core . Theme 0.1
2022-07-14 11:03:36 +00:00
import StatusQ . Controls 0.1
import StatusQ . Components 0.1
2021-10-27 21:27:49 +00:00
import utils 1.0
import shared 1.0
2021-10-28 20:23:30 +00:00
import shared . views 1.0
2021-10-27 21:27:49 +00:00
import shared . panels 1.0
import shared . popups 1.0
import shared . status 1.0
import shared . controls 1.0
2021-10-28 20:23:30 +00:00
import shared . views . chat 1.0
2021-09-28 15:04:06 +00:00
2021-10-01 15:58:36 +00:00
import "../controls"
2021-07-22 14:53:19 +00:00
Item {
2021-08-02 13:38:03 +00:00
id: root
2021-07-22 14:53:19 +00:00
2022-07-05 10:12:27 +00:00
property var chatContentModule
property var rootStore
2021-12-09 12:53:40 +00:00
property var messageStore
2022-02-08 12:08:02 +00:00
property var usersStore
2022-01-04 12:06:05 +00:00
property var contactsStore
2022-03-14 19:32:52 +00:00
property string channelEmoji
2021-10-21 22:39:53 +00:00
2022-04-13 09:59:16 +00:00
property var emojiPopup
2022-11-14 20:21:00 +00:00
property var stickersPopup
2022-04-13 09:59:16 +00:00
2021-12-08 21:20:43 +00:00
property bool stickersLoaded: false
2020-11-19 18:30:09 +00:00
property alias chatLogView: chatLogView
2022-02-24 12:15:02 +00:00
property bool isChatBlocked: false
2022-06-23 15:47:30 +00:00
property bool isActiveChannel: false
2021-07-08 15:20:03 +00:00
2022-07-05 10:12:27 +00:00
property var messageContextMenu
2021-08-05 21:05:47 +00:00
2021-12-08 21:20:43 +00:00
signal openStickerPackPopup ( string stickerPackId )
2022-01-12 12:55:26 +00:00
signal showReplyArea ( string messageId , string author )
2023-01-05 03:14:12 +00:00
signal editModeChanged ( bool editModeOn )
2021-12-08 21:20:43 +00:00
2022-12-05 07:52:41 +00:00
QtObject {
id: d
readonly property real scrollY: chatLogView . visibleArea . yPosition * chatLogView . contentHeight
readonly property bool isMostRecentMessageInViewport: chatLogView . visibleArea . yPosition >= 0.999 - chatLogView . visibleArea . heightRatio
2022-12-18 20:29:18 +00:00
readonly property var chatDetails: chatContentModule . chatDetails || null
2022-12-05 07:52:41 +00:00
2023-01-18 07:58:24 +00:00
readonly property var loadMoreMessagesIfScrollBelowThreshold: Backpressure . oneInTime ( root , 100 , function ( ) {
2023-01-10 11:40:01 +00:00
if ( scrollY < 1000 ) messageStore . loadMoreMessages ( )
2022-12-20 10:58:50 +00:00
} )
2022-12-05 07:52:41 +00:00
function markAllMessagesReadIfMostRecentMessageIsInViewport ( ) {
2023-01-06 09:17:39 +00:00
if ( ! isMostRecentMessageInViewport || ! chatLogView . visible ) {
2022-12-05 07:52:41 +00:00
return
}
2022-12-26 16:25:16 +00:00
if ( chatDetails && chatDetails . active && chatDetails . hasUnreadMessages && ! messageStore . messageSearchOngoing ) {
2022-12-05 07:52:41 +00:00
chatContentModule . markAllMessagesRead ( )
}
}
onIsMostRecentMessageInViewportChanged: markAllMessagesReadIfMostRecentMessageIsInViewport ( )
}
2022-01-27 11:28:27 +00:00
Connections {
target: root . messageStore . messageModule
2022-09-22 22:01:29 +00:00
function onMessageSuccessfullySent ( ) {
2022-12-05 07:52:41 +00:00
chatLogView . positionViewAtBeginning ( )
2022-01-27 11:28:27 +00:00
}
2022-09-22 22:01:29 +00:00
function onSendingMessageFailed ( ) {
sendingMsgFailedPopup . open ( )
2022-01-27 11:28:27 +00:00
}
2022-12-05 07:52:41 +00:00
function onScrollToMessage ( messageIndex ) {
chatLogView . positionViewAtIndex ( messageIndex , ListView . Center )
chatLogView . itemAtIndex ( messageIndex ) . startMessageFoundAnimation ( )
2022-01-27 12:44:33 +00:00
}
2022-12-05 07:52:41 +00:00
function onMessageSearchOngoingChanged ( ) {
d . markAllMessagesReadIfMostRecentMessageIsInViewport ( )
2022-01-27 11:28:27 +00:00
}
2022-12-05 07:52:41 +00:00
}
2022-01-27 11:28:27 +00:00
2022-12-05 07:52:41 +00:00
Connections {
2022-12-20 15:23:49 +00:00
target: ! ! d . chatDetails ? d.chatDetails : null
2022-12-05 07:52:41 +00:00
function onActiveChanged ( ) {
d . markAllMessagesReadIfMostRecentMessageIsInViewport ( )
2022-12-20 10:58:50 +00:00
d . loadMoreMessagesIfScrollBelowThreshold ( )
2022-12-05 07:52:41 +00:00
}
function onHasUnreadMessagesChanged ( ) {
2023-01-06 09:17:39 +00:00
if ( ! d . chatDetails . hasUnreadMessages ) {
return
}
// HACK: we call `addNewMessagesMarker` later because messages model
// may not be yet propagated with unread messages when this signal is emitted
if ( chatLogView . visible ) {
if ( ! d . isMostRecentMessageInViewport ) {
Qt . callLater ( ( ) = > messageStore . addNewMessagesMarker ( ) )
}
} else {
2022-12-05 07:52:41 +00:00
Qt . callLater ( ( ) = > messageStore . addNewMessagesMarker ( ) )
}
}
2022-01-27 11:28:27 +00:00
}
2022-12-20 10:58:50 +00:00
Connections {
target: root . rootStore
enabled: d . chatDetails && d . chatDetails . active
function onLoadingHistoryMessagesInProgressChanged ( ) {
if ( ! root . rootStore . loadingHistoryMessagesInProgress ) {
d . loadMoreMessagesIfScrollBelowThreshold ( )
}
}
}
2021-12-22 12:00:44 +00:00
Item {
id: loadingMessagesIndicator
2022-07-05 10:12:27 +00:00
visible: root . rootStore . loadingHistoryMessagesInProgress
2021-12-22 12:00:44 +00:00
anchors.top: parent . top
anchors.left: parent . left
height: visible ? 20 : 0
width: parent . width
Loader {
2022-07-05 10:12:27 +00:00
active: root . rootStore . loadingHistoryMessagesInProgress
2021-12-22 12:00:44 +00:00
anchors.horizontalCenter: parent . horizontalCenter
anchors.verticalCenter: parent . verticalCenter
sourceComponent: Component {
LoadingAnimation {
width: 18
height: 18
}
}
}
}
2022-07-14 11:03:36 +00:00
StatusListView {
2021-08-02 13:38:03 +00:00
id: chatLogView
2022-07-20 12:14:50 +00:00
objectName: "chatLogView"
2021-12-22 12:00:44 +00:00
anchors.top: loadingMessagesIndicator . bottom
anchors.bottom: parent . bottom
anchors.left: parent . left
anchors.right: parent . right
2021-12-10 11:29:33 +00:00
spacing: 0
2021-08-02 13:38:03 +00:00
verticalLayoutDirection: ListView . BottomToTop
function checkHeaderHeight ( ) {
if ( ! chatLogView . headerItem ) {
return
2020-07-23 20:22:45 +00:00
}
2020-09-24 15:05:17 +00:00
2021-08-02 13:38:03 +00:00
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
2020-07-23 20:22:45 +00:00
}
2021-08-02 13:38:03 +00:00
}
2020-09-24 15:05:17 +00:00
2022-07-05 10:12:27 +00:00
model: messageStore . messagesModel
onContentYChanged: {
2022-12-05 07:52:41 +00:00
scrollDownButton . visible = contentHeight - ( d . scrollY + height ) > 400
2022-12-20 10:58:50 +00:00
d . loadMoreMessagesIfScrollBelowThreshold ( )
2022-07-05 10:12:27 +00:00
}
2022-12-05 07:52:41 +00:00
onCountChanged: d . markAllMessagesReadIfMostRecentMessageIsInViewport ( )
2023-01-06 09:17:39 +00:00
onVisibleChanged: d . markAllMessagesReadIfMostRecentMessageIsInViewport ( )
2022-07-14 11:03:36 +00:00
ScrollBar.vertical: StatusScrollBar {
2021-12-22 12:00:44 +00:00
visible: chatLogView . visibleArea . heightRatio < 1
}
2021-12-09 12:53:40 +00:00
2022-07-05 10:12:27 +00:00
// 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
// 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 {
height: 0
width: chatLogView . width
}
2021-08-02 13:38:03 +00:00
Timer {
id: timer
}
2021-06-23 17:30:57 +00:00
2021-12-22 12:00:44 +00:00
Button {
2022-07-05 10:12:27 +00:00
id: scrollDownButton
2021-12-22 12:00:44 +00:00
readonly property int buttonPadding: 5
anchors.bottom: parent . bottom
anchors.right: parent . right
anchors.rightMargin: Style . current . padding
2022-12-05 07:52:41 +00:00
visible: false
height: 32
width: arrowImage . width + 2 * Style . current . halfPadding
2021-12-22 12:00:44 +00:00
background: Rectangle {
color: Style . current . buttonSecondaryColor
border.width: 0
radius: 16
}
2022-07-05 10:12:27 +00:00
2021-12-22 12:00:44 +00:00
onClicked: {
scrollDownButton . visible = false
2022-12-05 07:52:41 +00:00
chatLogView . positionViewAtBeginning ( )
2021-12-22 12:00:44 +00:00
}
2021-12-09 12:53:40 +00:00
2022-10-03 12:16:44 +00:00
StatusIcon {
2021-12-22 12:00:44 +00:00
id: arrowImage
2022-12-05 07:52:41 +00:00
anchors.centerIn: parent
2021-12-22 12:00:44 +00:00
width: 24
height: 24
2022-10-03 12:16:44 +00:00
icon: "arrow-down"
color: Style . current . pillButtonTextColor
2021-12-22 12:00:44 +00:00
}
2021-12-09 12:53:40 +00:00
2021-12-22 12:00:44 +00:00
MouseArea {
cursorShape: Qt . PointingHandCursor
anchors.fill: parent
2023-01-06 15:43:54 +00:00
acceptedButtons: Qt . NoButton
2021-12-22 12:00:44 +00:00
}
}
2020-11-30 21:24:01 +00:00
2021-10-01 15:58:36 +00:00
delegate: MessageView {
2021-08-02 13:38:03 +00:00
id: msgDelegate
2022-07-05 10:12:27 +00:00
width: ListView . view . width
2022-09-30 15:15:22 +00:00
height: implicitHeight
2022-07-05 10:12:27 +00:00
2022-07-21 16:16:25 +00:00
objectName: "chatMessageViewDelegate"
2022-07-05 10:12:27 +00:00
rootStore: root . rootStore
2021-12-09 12:53:40 +00:00
messageStore: root . messageStore
2022-02-08 12:08:02 +00:00
usersStore: root . usersStore
2022-01-04 12:06:05 +00:00
contactsStore: root . contactsStore
2022-03-14 19:32:52 +00:00
channelEmoji: root . channelEmoji
2022-04-13 09:59:16 +00:00
emojiPopup: root . emojiPopup
2022-11-14 20:21:00 +00:00
stickersPopup: root . stickersPopup
2022-07-25 12:43:05 +00:00
chatLogView: ListView . view
2022-01-04 12:06:05 +00:00
2022-06-29 16:50:10 +00:00
isActiveChannel: root . isActiveChannel
2022-02-24 12:15:02 +00:00
isChatBlocked: root . isChatBlocked
2022-07-05 10:12:27 +00:00
messageContextMenu: root . messageContextMenu
2021-12-09 12:53:40 +00:00
messageId: model . id
2022-02-24 15:04:59 +00:00
communityId: model . communityId
2021-12-09 12:53:40 +00:00
responseToMessageWithId: model . responseToMessageWithId
senderId: model . senderId
senderDisplayName: model . senderDisplayName
2022-09-14 07:35:26 +00:00
senderOptionalName: model . senderOptionalName
senderIsEnsVerified: model . senderEnsVerified
2021-12-09 12:53:40 +00:00
senderIcon: model . senderIcon
2023-01-10 11:29:24 +00:00
senderColorHash: model . senderColorHash
2022-03-04 21:33:48 +00:00
senderIsAdded: model . senderIsAdded
2022-09-30 14:36:49 +00:00
senderTrustStatus: model . senderTrustStatus
2021-12-09 12:53:40 +00:00
amISender: model . amISender
2022-07-05 10:12:27 +00:00
messageText: model . messageText
2023-01-06 20:19:27 +00:00
unparsedText: model . unparsedText
2021-12-09 12:53:40 +00:00
messageImage: model . messageImage
messageTimestamp: model . timestamp
messageOutgoingStatus: model . outgoingStatus
2022-12-08 21:01:08 +00:00
resendError: model . resendError
2021-12-09 12:53:40 +00:00
messageContentType: model . contentType
pinnedMessage: model . pinned
2022-01-05 15:50:03 +00:00
messagePinnedBy: model . pinnedBy
2021-12-20 14:21:35 +00:00
reactionsModel: model . reactions
2022-01-13 19:25:38 +00:00
sticker: model . sticker
stickerPack: model . stickerPack
2022-01-17 18:46:46 +00:00
editModeOn: model . editMode
2023-01-05 03:14:12 +00:00
onEditModeOnChanged: root . editModeChanged ( editModeOn )
2022-01-17 18:46:46 +00:00
isEdited: model . isEdited
2022-01-25 12:56:53 +00:00
linkUrls: model . links
2022-09-15 07:31:38 +00:00
messageAttachments: model . messageAttachments
2022-02-09 00:04:49 +00:00
transactionParams: model . transactionParameters
2022-12-19 14:55:44 +00:00
hasMention: model . mentioned
2023-01-12 19:23:26 +00:00
quotedMessageText: model . quotedMessageParsedText
quotedMessageFrom: model . quotedMessageFrom
quotedMessageContentType: model . quotedMessageContentType
2023-01-11 21:16:35 +00:00
quotedMessageDeleted: model . quotedMessageDeleted
2023-01-10 11:29:24 +00:00
quotedMessageAuthorDetailsName: model . quotedMessageAuthorName
quotedMessageAuthorDetailsDisplayName: model . quotedMessageAuthorDisplayName
quotedMessageAuthorDetailsThumbnailImage: model . quotedMessageAuthorThumbnailImage
quotedMessageAuthorDetailsEnsVerified: model . quotedMessageAuthorEnsVerified
quotedMessageAuthorDetailsIsContact: model . quotedMessageAuthorIsContact
quotedMessageAuthorDetailsColorHash: model . quotedMessageAuthorColorHash
2022-02-25 10:42:32 +00:00
gapFrom: model . gapFrom
gapTo: model . gapTo
2023-01-10 11:29:24 +00:00
// This is possible since we have all data loaded before we load qml.
// When we fetch messages to fulfill a gap we have to set them at once.
// Also one important thing here is that messages are set in descending order
// in terms of `timestamp` of a message, that means a message with the most
// recent time is added at index 0.
prevMessageIndex: prevMsgIndex
prevMessageTimestamp: prevMsgTimestamp
prevMessageSenderId: prevMsgSenderId
prevMessageContentType: prevMsgContentType
nextMessageIndex: nextMsgIndex
nextMessageTimestamp: nextMsgTimestamp
2022-07-05 10:12:27 +00:00
2021-12-08 21:20:43 +00:00
onOpenStickerPackPopup: {
root . openStickerPackPopup ( stickerPackId ) ;
}
2022-01-05 15:50:03 +00:00
2022-01-12 12:55:26 +00:00
onShowReplyArea: {
root . showReplyArea ( messageId , author )
}
2022-07-05 10:12:27 +00:00
onImageClicked: Global . openImagePopup ( image , messageContextMenu )
2022-01-18 21:02:47 +00:00
2022-01-05 15:50:03 +00:00
stickersLoaded: root . stickersLoaded
2022-01-17 18:46:46 +00:00
onVisibleChanged: {
if ( ! visible && model . editMode )
messageStore . setEditModeOff ( model . id )
}
2021-08-02 13:38:03 +00:00
}
2021-06-30 18:46:26 +00:00
}
2021-12-10 16:11:18 +00:00
2021-12-22 12:00:44 +00:00
MessageDialog {
id: sendingMsgFailedPopup
standardButtons: StandardButton . Ok
2022-04-04 11:26:30 +00:00
text: qsTr ( "Failed to send message." )
2021-12-22 12:00:44 +00:00
icon: StandardIcon . Critical
}
2020-05-27 22:59:17 +00:00
}