Skip to content
Snippets Groups Projects
Commit 44fdbb83 authored by Ming Rui Zhang's avatar Ming Rui Zhang
Browse files

messages: divide up MessageDelegate and use DelegateChooser

1. GeneratedMessageDelegate, TextMessageDelegate and
   DataTransferMessageDelegate
2. DelegateChooser in MessageListView

Change-Id: I5a3718f59e74b3499afc4abfa2826bab6cf442c8
parent e85d4506
No related branches found
No related tags found
No related merge requests found
...@@ -161,10 +161,12 @@ ...@@ -161,10 +161,12 @@
<file>src/commoncomponents/BackButton.qml</file> <file>src/commoncomponents/BackButton.qml</file>
<file>src/commoncomponents/JamiSwitch.qml</file> <file>src/commoncomponents/JamiSwitch.qml</file>
<file>src/mainview/components/ReadOnlyFooter.qml</file> <file>src/mainview/components/ReadOnlyFooter.qml</file>
<file>src/commoncomponents/MessageDelegate.qml</file> <file>src/commoncomponents/TextMessageDelegate.qml</file>
<file>src/mainview/components/MessageListView.qml</file> <file>src/mainview/components/MessageListView.qml</file>
<file>src/commoncomponents/MessageBubble.qml</file> <file>src/commoncomponents/MessageBubble.qml</file>
<file>src/constant/MsgSeq.qml</file> <file>src/constant/MsgSeq.qml</file>
<file>src/commoncomponents/SBSMessageBase.qml</file> <file>src/commoncomponents/SBSMessageBase.qml</file>
<file>src/commoncomponents/GeneratedMessageDelegate.qml</file>
<file>src/commoncomponents/DataTransferMessageDelegate.qml</file>
</qresource> </qresource>
</RCC> </RCC>
/* /*
* Copyright (C) 2021 by Savoir-faire Linux * Copyright (C) 2021 by Savoir-faire Linux
* Author: Trevor Tabah <trevor.tabah@savoirfairelinux.com> * Author: Trevor Tabah <trevor.tabah@savoirfairelinux.com>
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com> * Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
...@@ -24,272 +25,48 @@ import QtGraphicalEffects 1.15 ...@@ -24,272 +25,48 @@ import QtGraphicalEffects 1.15
import QtWebEngine 1.10 import QtWebEngine 1.10
import net.jami.Models 1.1 import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1 import net.jami.Constants 1.1
import net.jami.Adapters 1.1
Control { Loader {
id: root id: root
readonly property ListView listView: ListView.view
readonly property bool isGenerated: Type === Interaction.Type.CALL ||
Type === Interaction.Type.CONTACT
readonly property string author: Author
readonly property var body: Body
readonly property var timestamp: Timestamp
readonly property bool isOutgoing: model.Author === ""
readonly property var formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
readonly property var linkInfo: LinkPreviewInfo
property var mediaInfo property var mediaInfo
readonly property real senderMargin: 64
readonly property real avatarSize: 32
readonly property real msgRadius: 18
readonly property real hMargin: 12
property bool showTime: false property bool showTime: false
property int seq: MsgSeq.single property int seq: MsgSeq.single
width: parent ? parent.width : 0 width: ListView.view ? ListView.view.width : 0
height: loader.height
// message interaction
property string hoveredLink
MouseArea {
id: itemMouseArea
anchors.fill: parent
acceptedButtons: Qt.LeftButton
onClicked: {
if (root.hoveredLink)
Qt.openUrlExternally(root.hoveredLink)
}
}
Loader {
id: loader
width: root.width
height: sourceComponent.height
sourceComponent: { sourceComponent: {
switch (Type) {
case Interaction.Type.TEXT: return textMsgComp
case Interaction.Type.CALL:
case Interaction.Type.CONTACT: return generatedMsgComp
case Interaction.Type.DATA_TRANSFER:
if (Status === Interaction.Status.TRANSFER_FINISHED) { if (Status === Interaction.Status.TRANSFER_FINISHED) {
mediaInfo = MessagesAdapter.getMediaInfo(Body) mediaInfo = MessagesAdapter.getMediaInfo(Body)
if (Object.keys(mediaInfo).length !== 0) if (Object.keys(mediaInfo).length !== 0)
return localMediaMsgComp return localMediaMsgComp
} }
return dataTransferMsgComp return dataTransferMsgComp
default:
// if this happens, adjust FilteredMsgListModel
console.warn("Invalid message type has not been filtered.")
return null
}
}
}
Component {
id: textMsgComp
SBSMessageBase {
property real maxMsgWidth: root.width - senderMargin - 2 * hMargin - avatarBlockWidth
property bool isRemoteImage
isOutgoing: root.isOutgoing
showTime: root.showTime
seq: root.seq
author: root.author
formattedTime: root.formattedTime
extraHeight: extraContent.active && !isRemoteImage ? msgRadius : -isRemoteImage
innerContent.children: [
TextEdit {
padding: 10
anchors.right: isOutgoing ? parent.right : undefined
text: '<span style="white-space: pre-wrap">' + body + '</span>'
width: {
if (extraContent.active)
Math.max(extraContent.width,
Math.min(implicitWidth - avatarBlockWidth,
extraContent.minSize) - senderMargin)
else
Math.min(implicitWidth, innerContent.width - senderMargin)
}
height: implicitHeight
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
selectByMouse: true
font.pointSize: 11
font.hintingPreference: Font.PreferNoHinting
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
onLinkHovered: root.hoveredLink = hoveredLink
onLinkActivated: Qt.openUrlExternally(hoveredLink)
readOnly: true
color: isOutgoing ?
JamiTheme.messageOutTxtColor :
JamiTheme.messageInTxtColor
},
Loader {
id: extraContent
width: sourceComponent.width
height: sourceComponent.height
anchors.right: isOutgoing ? parent.right : undefined
property real minSize: 192
property real maxSize: 320
active: linkInfo.url !== undefined
sourceComponent: ColumnLayout {
id: previewContent
spacing: 12
Component.onCompleted: {
isRemoteImage = MessagesAdapter.isRemoteImage(linkInfo.url)
}
HoverHandler {
target: previewContent
onHoveredChanged: {
root.hoveredLink = hovered ? linkInfo.url : ""
}
cursorShape: Qt.PointingHandCursor
}
AnimatedImage {
id: img
cache: true
source: isRemoteImage ?
linkInfo.url :
(hasImage ? linkInfo.image : "")
fillMode: Image.PreserveAspectCrop
mipmap: true
antialiasing: true
autoTransform: true
asynchronous: true
readonly property bool hasImage: linkInfo.image !== null
property real aspectRatio: implicitWidth / implicitHeight
property real adjustedWidth: Math.min(extraContent.maxSize,
Math.max(extraContent.minSize,
maxMsgWidth))
Layout.preferredWidth: adjustedWidth
Layout.preferredHeight: Math.ceil(adjustedWidth / aspectRatio)
Rectangle {
color: JamiTheme.previewImageBackgroundColor
z: -1
anchors.fill: parent
}
layer.enabled: isRemoteImage
layer.effect: OpacityMask {
maskSource: MessageBubble {
Rectangle { height: msgRadius; width: parent.width }
out: isOutgoing
type: seq
width: img.width
height: img.height
radius: msgRadius
}
}
}
Column {
opacity: img.status !== Image.Loading
visible: !isRemoteImage
Layout.preferredWidth: img.width - 2 * hMargin
Layout.leftMargin: hMargin
Layout.rightMargin: hMargin
spacing: 6
Label {
width: parent.width
font.pointSize: 10
font.hintingPreference: Font.PreferNoHinting
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
color: JamiTheme.previewTitleColor
visible: linkInfo.title !== null
text: linkInfo.title
}
Label {
width: parent.width
font.pointSize: 11
font.hintingPreference: Font.PreferNoHinting
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
color: JamiTheme.previewSubtitleColor
visible: linkInfo.description !== null
text: '<a href=" " style="text-decoration: ' +
( hoveredLink ? 'underline' : 'none') + ';"' +
'>' + linkInfo.description + '</a>'
}
Label {
width: parent.width
font.pointSize: 10
font.hintingPreference: Font.PreferNoHinting
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
color: JamiTheme.previewSubtitleColor
text: linkInfo.domain
}
} }
}
}
]
Component.onCompleted: {
if (!Linkified) {
MessagesAdapter.parseMessageUrls(Id, Body)
}
}
}
}
Component {
id: generatedMsgComp
Column {
width: root.width
spacing: 2
topPadding: 12
bottomPadding: 12
Label {
width: parent.width
text: body
horizontalAlignment: Qt.AlignHCenter
font.pointSize: 12
color: JamiTheme.chatviewTextColor
}
Item {
id: infoCell
width: parent.width opacity: 0
height: childrenRect.height Behavior on opacity { NumberAnimation { duration: 100 } }
onLoaded: opacity = 1
Label {
text: formattedTime
color: JamiTheme.timestampColor
visible: showTime || seq === MsgSeq.last
height: visible * implicitHeight
font.pointSize: 9
anchors.horizontalCenter: parent.horizontalCenter
}
}
}
}
Component { Component {
id: dataTransferMsgComp id: dataTransferMsgComp
SBSMessageBase { SBSMessageBase {
id: dataTransferItem id: dataTransferItem
property var transferStats: MessagesAdapter.getTransferStats(Id, Status) property var transferStats: MessagesAdapter.getTransferStats(Id, Status)
property bool canOpen: Status === Interaction.Status.TRANSFER_FINISHED || isOutgoing property bool canOpen: Status === Interaction.Status.TRANSFER_FINISHED || isOutgoing
property real maxMsgWidth: root.width - senderMargin - property real maxMsgWidth: root.width - senderMargin -
2 * hMargin - avatarBlockWidth 2 * hPadding - avatarBlockWidth
- buttonsLoader.width - 24 - 6 - 24 - buttonsLoader.width - 24 - 6 - 24
isOutgoing: root.isOutgoing
isOutgoing: Author === ""
showTime: root.showTime showTime: root.showTime
seq: root.seq seq: root.seq
author: root.author author: Author
formattedTime: root.formattedTime formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
extraHeight: progressBar.visible ? 18 : 0 extraHeight: progressBar.visible ? 18 : 0
innerContent.children: [ innerContent.children: [
RowLayout { RowLayout {
...@@ -300,9 +77,8 @@ Control { ...@@ -300,9 +77,8 @@ Control {
target: parent target: parent
enabled: canOpen enabled: canOpen
onHoveredChanged: { onHoveredChanged: {
root.hoveredLink = enabled && hovered ? dataTransferItem.hoveredLink = enabled && hovered ?
("file:///" + body) : ("file:///" + Body) : ""
""
} }
cursorShape: enabled ? cursorShape: enabled ?
Qt.PointingHandCursor : Qt.PointingHandCursor :
...@@ -393,7 +169,7 @@ Control { ...@@ -393,7 +169,7 @@ Control {
topPadding: 10 topPadding: 10
text: CurrentConversation.isSwarm ? text: CurrentConversation.isSwarm ?
TransferName : TransferName :
body Body
wrapMode: Label.WrapAtWordBoundaryOrAnywhere wrapMode: Label.WrapAtWordBoundaryOrAnywhere
font.weight: Font.DemiBold font.weight: Font.DemiBold
font.pointSize: 11 font.pointSize: 11
...@@ -404,10 +180,10 @@ Control { ...@@ -404,10 +180,10 @@ Control {
JamiTheme.messageInTxtColor JamiTheme.messageInTxtColor
MouseArea { MouseArea {
anchors.fill: parent anchors.fill: parent
propagateComposedEvents: true
cursorShape: canOpen ? cursorShape: canOpen ?
Qt.PointingHandCursor : Qt.PointingHandCursor :
Qt.ArrowCursor Qt.ArrowCursor
onClicked: if(canOpen) itemMouseArea.clicked(mouse)
} }
} }
Label { Label {
...@@ -450,11 +226,13 @@ Control { ...@@ -450,11 +226,13 @@ Control {
id: localMediaMsgComp id: localMediaMsgComp
SBSMessageBase { SBSMessageBase {
isOutgoing: root.isOutgoing id: localMediaMsgItem
isOutgoing: Author === ""
showTime: root.showTime showTime: root.showTime
seq: root.seq seq: root.seq
author: root.author author: Author
formattedTime: root.formattedTime formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
bubble.visible: false bubble.visible: false
innerContent.children: [ innerContent.children: [
Loader { Loader {
...@@ -522,7 +300,7 @@ Control { ...@@ -522,7 +300,7 @@ Control {
antialiasing: true antialiasing: true
autoTransform: false autoTransform: false
asynchronous: true asynchronous: true
source: "file:///" + body source: "file:///" + Body
property real aspectRatio: implicitWidth / implicitHeight property real aspectRatio: implicitWidth / implicitHeight
property real adjustedWidth: Math.min(maxSize, property real adjustedWidth: Math.min(maxSize,
Math.max(minSize, Math.max(minSize,
...@@ -547,7 +325,7 @@ Control { ...@@ -547,7 +325,7 @@ Control {
HoverHandler { HoverHandler {
target : parent target : parent
onHoveredChanged: { onHoveredChanged: {
root.hoveredLink = hovered ? img.source : "" localMediaMsgItem.hoveredLink = hovered ? img.source : ""
} }
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
} }
...@@ -557,8 +335,4 @@ Control { ...@@ -557,8 +335,4 @@ Control {
] ]
} }
} }
opacity: 0
Behavior on opacity { NumberAnimation { duration: 40 } }
Component.onCompleted: opacity = 1
} }
/*
* Copyright (C) 2021 by Savoir-faire Linux
* Author: Trevor Tabah <trevor.tabah@savoirfairelinux.com>
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
* Author: Mingrui Zhang <mingrui.zhang@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
Column {
id: root
property bool showTime: false
property int seq: MsgSeq.single
width: ListView.view ? ListView.view.width : 0
spacing: 2
topPadding: 12
bottomPadding: 12
Label {
width: parent.width
text: Body
horizontalAlignment: Qt.AlignHCenter
font.pointSize: 12
color: JamiTheme.chatviewTextColor
}
Item {
id: infoCell
width: parent.width
height: childrenRect.height
Label {
text: MessagesAdapter.getFormattedTime(Timestamp)
color: JamiTheme.timestampColor
visible: showTime || seq === MsgSeq.last
height: visible * implicitHeight
font.pointSize: 9
anchors.horizontalCenter: parent.horizontalCenter
}
}
opacity: 0
Behavior on opacity { NumberAnimation { duration: 100 } }
Component.onCompleted: opacity = 1
}
...@@ -25,7 +25,7 @@ import net.jami.Models 1.1 ...@@ -25,7 +25,7 @@ import net.jami.Models 1.1
import net.jami.Adapters 1.1 import net.jami.Adapters 1.1
import net.jami.Constants 1.1 import net.jami.Constants 1.1
ColumnLayout { Control {
id: root id: root
property alias avatarBlockWidth: avatarBlock.width property alias avatarBlockWidth: avatarBlock.width
...@@ -39,16 +39,26 @@ ColumnLayout { ...@@ -39,16 +39,26 @@ ColumnLayout {
property int seq property int seq
property string author property string author
property string formattedTime property string formattedTime
property string hoveredLink
readonly property real senderMargin: 64 readonly property real senderMargin: 64
readonly property real avatarSize: 32 readonly property real avatarSize: 32
readonly property real msgRadius: 18 readonly property real msgRadius: 18
readonly property real hMargin: 12 readonly property real hPadding: 12
width: ListView.view ? ListView.view.width : 0
height: mainColumnLayout.implicitHeight
rightPadding: hPadding
leftPadding: hPadding
contentItem: ColumnLayout {
id: mainColumnLayout
anchors.centerIn: parent
width: parent.width
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: hMargin
anchors.rightMargin: hMargin
spacing: 2 spacing: 2
RowLayout { RowLayout {
...@@ -57,7 +67,7 @@ ColumnLayout { ...@@ -57,7 +67,7 @@ ColumnLayout {
spacing: 0 spacing: 0
Item { Item {
id: avatarBlock id: avatarBlock
Layout.preferredWidth: isOutgoing ? 0 : avatar.width + hMargin Layout.preferredWidth: isOutgoing ? 0 : avatar.width + hPadding
Layout.preferredHeight: isOutgoing ? 0 : bubble.height Layout.preferredHeight: isOutgoing ? 0 : bubble.height
Avatar { Avatar {
id: avatar id: avatar
...@@ -96,7 +106,7 @@ ColumnLayout { ...@@ -96,7 +106,7 @@ ColumnLayout {
Item { Item {
id: infoCell id: infoCell
Layout.preferredWidth: parent.width Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height Layout.preferredHeight: childrenRect.height
Label { Label {
...@@ -113,3 +123,15 @@ ColumnLayout { ...@@ -113,3 +123,15 @@ ColumnLayout {
} }
} }
} }
MouseArea {
id: itemMouseArea
anchors.fill: parent
z: -1
acceptedButtons: Qt.LeftButton
onClicked: {
if (root.hoveredLink)
Qt.openUrlExternally(root.hoveredLink)
}
}
}
/*
* Copyright (C) 2021 by Savoir-faire Linux
* Author: Trevor Tabah <trevor.tabah@savoirfairelinux.com>
* Author: Andreas Traczyk <andreas.traczyk@savoirfairelinux.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtGraphicalEffects 1.15
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
SBSMessageBase {
id : root
property bool isRemoteImage
property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth
isOutgoing: Author === ""
author: Author
formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
extraHeight: extraContent.active && !isRemoteImage ? msgRadius : -isRemoteImage
innerContent.children: [
TextEdit {
padding: 10
anchors.right: isOutgoing ? parent.right : undefined
text: '<span style="white-space: pre-wrap">' + Body + '</span>'
width: {
if (extraContent.active)
Math.max(extraContent.width,
Math.min(implicitWidth - avatarBlockWidth,
extraContent.minSize) - senderMargin)
else
Math.min(implicitWidth, innerContent.width - senderMargin)
}
height: implicitHeight
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
selectByMouse: true
font.pointSize: 11
font.hintingPreference: Font.PreferNoHinting
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
onLinkHovered: root.hoveredLink = hoveredLink
onLinkActivated: Qt.openUrlExternally(hoveredLink)
readOnly: true
color: isOutgoing ?
JamiTheme.messageOutTxtColor :
JamiTheme.messageInTxtColor
},
Loader {
id: extraContent
width: sourceComponent.width
height: sourceComponent.height
anchors.right: isOutgoing ? parent.right : undefined
property real minSize: 192
property real maxSize: 320
active: LinkPreviewInfo.url !== undefined
sourceComponent: ColumnLayout {
id: previewContent
spacing: 12
Component.onCompleted: {
isRemoteImage = MessagesAdapter.isRemoteImage(LinkPreviewInfo.url)
}
HoverHandler {
target: previewContent
onHoveredChanged: {
root.hoveredLink = hovered ? LinkPreviewInfo.url : ""
}
cursorShape: Qt.PointingHandCursor
}
AnimatedImage {
id: img
cache: true
source: isRemoteImage ?
LinkPreviewInfo.url :
(hasImage ? LinkPreviewInfo.image : "")
fillMode: Image.PreserveAspectCrop
mipmap: true
antialiasing: true
autoTransform: true
asynchronous: true
readonly property bool hasImage: LinkPreviewInfo.image !== null
property real aspectRatio: implicitWidth / implicitHeight
property real adjustedWidth: Math.min(extraContent.maxSize,
Math.max(extraContent.minSize,
maxMsgWidth))
Layout.preferredWidth: adjustedWidth
Layout.preferredHeight: Math.ceil(adjustedWidth / aspectRatio)
Rectangle {
color: JamiTheme.previewImageBackgroundColor
z: -1
anchors.fill: parent
}
layer.enabled: isRemoteImage
layer.effect: OpacityMask {
maskSource: MessageBubble {
Rectangle { height: msgRadius; width: parent.width }
out: isOutgoing
type: seq
width: img.width
height: img.height
radius: msgRadius
}
}
}
Column {
opacity: img.status !== Image.Loading
visible: !isRemoteImage
Layout.preferredWidth: img.width - 2 * hPadding
Layout.leftMargin: hPadding
Layout.rightMargin: hPadding
spacing: 6
Label {
width: parent.width
font.pointSize: 10
font.hintingPreference: Font.PreferNoHinting
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
color: JamiTheme.previewTitleColor
visible: LinkPreviewInfo.title !== null
text: LinkPreviewInfo.title
}
Label {
width: parent.width
font.pointSize: 11
font.hintingPreference: Font.PreferNoHinting
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
color: JamiTheme.previewSubtitleColor
visible: LinkPreviewInfo.description !== null
text: '<a href=" " style="text-decoration: ' +
( hoveredLink ? 'underline' : 'none') + ';"' +
'>' + LinkPreviewInfo.description + '</a>'
}
Label {
width: parent.width
font.pointSize: 10
font.hintingPreference: Font.PreferNoHinting
wrapMode: Label.WrapAtWordBoundaryOrAnywhere
renderType: Text.NativeRendering
textFormat: TextEdit.RichText
color: JamiTheme.previewSubtitleColor
text: LinkPreviewInfo.domain
}
}
}
}
]
opacity: 0
Behavior on opacity { NumberAnimation { duration: 100 } }
Component.onCompleted: {
if (!Linkified) {
MessagesAdapter.parseMessageUrls(Id, Body)
}
opacity = 1
}
}
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15 import QtQuick.Layouts 1.15
import Qt.labs.qmlmodels 1.0
import net.jami.Models 1.1 import net.jami.Models 1.1
import net.jami.Adapters 1.1 import net.jami.Adapters 1.1
...@@ -30,90 +31,53 @@ import "../../commoncomponents" ...@@ -30,90 +31,53 @@ import "../../commoncomponents"
ListView { ListView {
id: root id: root
// fade-in mechanism function getDistanceToBottom() {
Component.onCompleted: fadeAnimation.start() const scrollDiff = ScrollBar.vertical.position -
Rectangle { (1.0 - ScrollBar.vertical.size)
id: overlay return Math.abs(scrollDiff) * contentHeight
anchors.fill: parent
color: JamiTheme.chatviewBgColor
visible: opacity !== 0
SequentialAnimation {
id: fadeAnimation
NumberAnimation {
target: overlay; property: "opacity"
to: 1; duration: 0
}
NumberAnimation {
target: overlay; property: "opacity"
to: 0; duration: 240
}
}
}
Connections {
target: CurrentConversation
function onIdChanged() { fadeAnimation.start() }
} }
topMargin: 12 function loadMoreMsgsIfNeeded() {
bottomMargin: 6 if (atYBeginning && !CurrentConversation.allMessagesLoaded)
spacing: 2 MessagesAdapter.loadMoreMessages()
anchors.centerIn: parent }
height: parent.height
width: parent.width
// this offscreen caching is pretty huge
// displayMarginEnd may be removed
displayMarginBeginning: 4096
displayMarginEnd: 4096
maximumFlickVelocity: 2048
verticalLayoutDirection: ListView.BottomToTop
clip: true
boundsBehavior: Flickable.StopAtBounds
currentIndex: -1
ScrollBar.vertical: ScrollBar {}
model: MessagesAdapter.messageListModel
delegate: MessageDelegate {
// sequencing/timestamps (2-sided style) // sequencing/timestamps (2-sided style)
function computeTimestampVisibility() { function computeTimestampVisibility(item, itemIndex) {
if (listView === undefined) if (root === undefined)
return return
var nItem = listView.itemAtIndex(index - 1) var nItem = root.itemAtIndex(itemIndex - 1)
if (nItem && index !== listView.count - 1) { if (nItem && itemIndex !== root.count - 1) {
showTime = (nItem.timestamp - timestamp) > 60 && item.showTime = (nItem.timestamp - item.timestamp) > 60 &&
nItem.formattedTime !== formattedTime nItem.formattedTime !== item.formattedTime
} else { } else {
showTime = true item.showTime = true
var pItem = listView.itemAtIndex(index + 1) var pItem = root.itemAtIndex(itemIndex + 1)
if (pItem) { if (pItem) {
pItem.showTime = (timestamp - pItem.timestamp) > 60 && pItem.showTime = (item.timestamp - pItem.timestamp) > 60 &&
pItem.formattedTime !== formattedTime pItem.formattedTime !== item.formattedTime
} }
} }
} }
function computeSequencing() { function computeSequencing(computeItem, computeItemIndex) {
if (listView === undefined) if (root === undefined)
return return
var cItem = { var cItem = {
'author': author, 'author': computeItem.author,
'isGenerated': isGenerated, 'showTime': computeItem.showTime
'showTime': showTime
} }
var pItem = listView.itemAtIndex(index + 1) var pItem = root.itemAtIndex(computeItemIndex + 1)
var nItem = listView.itemAtIndex(index - 1) var nItem = root.itemAtIndex(computeItemIndex - 1)
let isSeq = (item0, item1) => let isSeq = (item0, item1) =>
item0.author === item1.author && item0.author === item1.author && !item0.showTime
!(item0.isGenerated || item1.isGenerated) &&
!item0.showTime
let setSeq = function (newSeq, item) { let setSeq = function (newSeq, item) {
if (item === undefined) if (item === undefined)
seq = isGenerated ? MsgSeq.single : newSeq computeItem.seq = newSeq
else else
item.seq = item.isGenerated ? MsgSeq.single : newSeq item.seq = newSeq
} }
let rAdjustSeq = function (item) { let rAdjustSeq = function (item) {
...@@ -132,26 +96,26 @@ ListView { ...@@ -132,26 +96,26 @@ ListView {
if (pItem && !nItem) { if (pItem && !nItem) {
if (!isSeq(pItem, cItem)) { if (!isSeq(pItem, cItem)) {
seq = MsgSeq.single computeItem.seq = MsgSeq.single
} else { } else {
seq = MsgSeq.last computeItem.seq = MsgSeq.last
rAdjustSeq(pItem) rAdjustSeq(pItem)
} }
} else if (nItem && !pItem) { } else if (nItem && !pItem) {
if (!isSeq(cItem, nItem)) { if (!isSeq(cItem, nItem)) {
seq = MsgSeq.single computeItem.seq = MsgSeq.single
} else { } else {
setSeq(MsgSeq.first) setSeq(MsgSeq.first)
adjustSeq(nItem) adjustSeq(nItem)
} }
} else if (!nItem && !pItem) { } else if (!nItem && !pItem) {
seq = MsgSeq.single computeItem.seq = MsgSeq.single
} else { } else {
if (isSeq(pItem, nItem)) { if (isSeq(pItem, nItem)) {
if (isSeq(pItem, cItem)) { if (isSeq(pItem, cItem)) {
seq = MsgSeq.middle computeItem.seq = MsgSeq.middle
} else { } else {
seq = MsgSeq.single computeItem.seq = MsgSeq.single
if (pItem.seq === MsgSeq.first) if (pItem.seq === MsgSeq.first)
pItem.seq = MsgSeq.single pItem.seq = MsgSeq.single
...@@ -165,44 +129,122 @@ ListView { ...@@ -165,44 +129,122 @@ ListView {
} }
} else { } else {
if (!isSeq(pItem, cItem)) { if (!isSeq(pItem, cItem)) {
seq = MsgSeq.first computeItem.seq = MsgSeq.first
adjustSeq(pItem) adjustSeq(pItem)
} else { } else {
seq = MsgSeq.last computeItem.seq = MsgSeq.last
rAdjustSeq(nItem) rAdjustSeq(nItem)
} }
} }
} }
if (seq === MsgSeq.last) { if (computeItem.seq === MsgSeq.last) {
showTime = true computeItem.showTime = true
}
}
// fade-in mechanism
Component.onCompleted: fadeAnimation.start()
Rectangle {
id: overlay
anchors.fill: parent
color: JamiTheme.chatviewBgColor
visible: opacity !== 0
SequentialAnimation {
id: fadeAnimation
NumberAnimation {
target: overlay; property: "opacity"
to: 1; duration: 0
}
NumberAnimation {
target: overlay; property: "opacity"
to: 0; duration: 240
}
} }
} }
Connections {
target: CurrentConversation
function onIdChanged() { fadeAnimation.start() }
}
topMargin: 12
bottomMargin: 6
spacing: 2
anchors.centerIn: parent
height: parent.height
width: parent.width
// this offscreen caching is pretty huge
// displayMarginEnd may be removed
displayMarginBeginning: 4096
displayMarginEnd: 4096
maximumFlickVelocity: 2048
verticalLayoutDirection: ListView.BottomToTop
clip: true
boundsBehavior: Flickable.StopAtBounds
currentIndex: -1
ScrollBar.vertical: ScrollBar {}
model: MessagesAdapter.messageListModel
delegate: DelegateChooser {
id: delegateChooser
role: "Type"
DelegateChoice {
roleValue: Interaction.Type.TEXT
TextMessageDelegate {
Component.onCompleted: { Component.onCompleted: {
if (index) { if (index) {
computeTimestampVisibility() computeTimestampVisibility(this, index)
computeSequencing() computeSequencing(this, index)
} else { } else {
Qt.callLater(computeTimestampVisibility) Qt.callLater(computeTimestampVisibility, this, index)
Qt.callLater(computeSequencing) Qt.callLater(computeSequencing, this, index)
}
}
}
}
DelegateChoice {
roleValue: Interaction.Type.CALL
GeneratedMessageDelegate {
Component.onCompleted: {
if (index)
computeTimestampVisibility(this, index)
else
Qt.callLater(computeTimestampVisibility, this, index)
}
}
}
DelegateChoice {
roleValue: Interaction.Type.CONTACT
GeneratedMessageDelegate {
Component.onCompleted: {
if (index)
computeTimestampVisibility(this, index)
else
Qt.callLater(computeTimestampVisibility, this, index)
}
}
}
DelegateChoice {
roleValue: Interaction.Type.DATA_TRANSFER
DataTransferMessageDelegate {
Component.onCompleted: {
if (index) {
computeTimestampVisibility(this, index)
computeSequencing(this, index)
} else {
Qt.callLater(computeTimestampVisibility, this, index)
Qt.callLater(computeSequencing, this, index)
}
} }
} }
} }
function getDistanceToBottom() {
const scrollDiff = ScrollBar.vertical.position -
(1.0 - ScrollBar.vertical.size)
return Math.abs(scrollDiff) * contentHeight
} }
onAtYBeginningChanged: loadMoreMsgsIfNeeded() onAtYBeginningChanged: loadMoreMsgsIfNeeded()
function loadMoreMsgsIfNeeded() {
if (atYBeginning && !CurrentConversation.allMessagesLoaded)
MessagesAdapter.loadMoreMessages()
}
Connections { Connections {
target: MessagesAdapter target: MessagesAdapter
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment