1
0
Fork 0
mirror of https://git.jami.net/savoirfairelinux/jami-client-qt.git synced 2025-07-03 23:25:28 +02:00

chatview: timestamp improvements

New timestamp computation and sequencing ( by day and hour)

GitLab: #827
Change-Id: Ie170f31c075dc37f00d393272410329dc045f2d3
This commit is contained in:
Nicolas 2022-09-12 09:36:50 -04:00 committed by Nicolas Vengeon
parent c3e8e38e99
commit d6ed9adf32
14 changed files with 366 additions and 255 deletions

View file

@ -203,5 +203,6 @@
<file>src/app/mainview/components/CustomizeTipBox.qml</file> <file>src/app/mainview/components/CustomizeTipBox.qml</file>
<file>src/app/mainview/components/BackupTipBox.qml</file> <file>src/app/mainview/components/BackupTipBox.qml</file>
<file>src/app/mainview/components/InformativeTipBox.qml</file> <file>src/app/mainview/components/InformativeTipBox.qml</file>
<file>src/app/commoncomponents/TimestampInfo.qml</file>
</qresource> </qresource>
</RCC> </RCC>

View file

@ -27,24 +27,36 @@ Column {
id: root id: root
property bool showTime: false property bool showTime: false
property int seq: MsgSeq.single property bool showDay: false
property alias font: textLabel.font property int timestamp: Timestamp
property string formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
property string formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
property int seq: MsgSeq.single//a changer par textlabel
width: ListView.view ? ListView.view.width : 0 width: ListView.view ? ListView.view.width : 0
spacing: 2 spacing: 2
topPadding: 12 topPadding: 12
bottomPadding: 12 bottomPadding: 12
ColumnLayout {
anchors.horizontalCenter: parent.horizontalCenter
TimestampInfo {
id:timestampItem
showDay: root.showDay
showTime: root.showTime
formattedTime: root.formattedTime
formattedDay: root.formattedDay
Layout.alignment: Qt.AlignHCenter
}
Rectangle { Rectangle {
id: msg id: msg
anchors.horizontalCenter: parent.horizontalCenter
width: childrenRect.width width: childrenRect.width
height: JamiTheme.contactMessageAvatarSize + 12 height: JamiTheme.contactMessageAvatarSize + 12
radius: JamiTheme.contactMessageAvatarSize / 2 + 6 radius: JamiTheme.contactMessageAvatarSize / 2 + 6
Layout.alignment: Qt.AlignVCenter
color: "transparent" color: "transparent"
border.width: 1 border.width: 1
border.color: CurrentConversation.isCoreDialog ? JamiTheme.messageInBgColor : CurrentConversation.color border.color: CurrentConversation.isCoreDialog ? JamiTheme.messageInBgColor : CurrentConversation.color
@ -54,20 +66,18 @@ Column {
Avatar { Avatar {
Layout.leftMargin: 6 Layout.leftMargin: 6
width: JamiTheme.contactMessageAvatarSize width: JamiTheme.contactMessageAvatarSize
height: JamiTheme.contactMessageAvatarSize height: JamiTheme.contactMessageAvatarSize
visible: ActionUri !== "" visible: ActionUri !== ""
imageId: ActionUri !== CurrentAccount.uri ? ActionUri : CurrentAccount.id imageId: ActionUri !== CurrentAccount.uri ? ActionUri : CurrentAccount.id
showPresenceIndicator: false showPresenceIndicator: false
mode: ActionUri !== CurrentAccount.uri ? Avatar.Mode.Contact : Avatar.Mode.Account mode: ActionUri !== CurrentAccount.uri ? Avatar.Mode.Contact : Avatar.Mode.Account
} }
Label { Label {
Layout.rightMargin: 6
id: textLabel id: textLabel
Layout.rightMargin: 6
width: parent.width width: parent.width
text: Body text: Body
horizontalAlignment: Qt.AlignHCenter horizontalAlignment: Qt.AlignHCenter
@ -77,24 +87,7 @@ Column {
} }
} }
} }
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 opacity: 0
Behavior on opacity { NumberAnimation { duration: 100 } } Behavior on opacity { NumberAnimation { duration: 100 } }
Component.onCompleted: opacity = 1 Component.onCompleted: opacity = 1

View file

@ -31,7 +31,12 @@ Loader {
id: root id: root
property var mediaInfo property var mediaInfo
property bool showTime: false property bool showTime
property bool showDay
property int timestamp: Timestamp
property string formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
property string formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
property int seq: MsgSeq.single property int seq: MsgSeq.single
property string author: Author property string author: Author
@ -70,7 +75,9 @@ Loader {
transferName: TransferName transferName: TransferName
transferId: Id transferId: Id
readers: Readers readers: Readers
formattedTime: MessagesAdapter.getFormattedTime(Timestamp) timestamp: root.timestamp
formattedTime: root.formattedTime
formattedDay: root.formattedTime
extraHeight: progressBar.visible ? 18 : 0 extraHeight: progressBar.visible ? 18 : 0
innerContent.children: [ innerContent.children: [
RowLayout { RowLayout {
@ -247,6 +254,7 @@ Loader {
transferId: Id transferId: Id
readers: Readers readers: Readers
formattedTime: MessagesAdapter.getFormattedTime(Timestamp) formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
bubble.visible: false bubble.visible: false
innerContent.children: [ innerContent.children: [
Loader { Loader {
@ -332,7 +340,7 @@ Loader {
sourceSize.width: width sourceSize.width: width
sourceSize.height: height sourceSize.height: height
source: "file:///" + Body source: "file:///" + Body
property real aspectRatio: implicitWidth / implicitHeight property real aspectRatio: width / implicitHeight
property real adjustedWidth: Math.min(maxSize, property real adjustedWidth: Math.min(maxSize,
Math.max(minSize, Math.max(minSize,
innerContent.width - senderMargin)) innerContent.width - senderMargin))

View file

@ -29,39 +29,38 @@ Column {
id: root id: root
property bool showTime: false property bool showTime: false
property bool showDay: false
property int seq: MsgSeq.single property int seq: MsgSeq.single
property alias font: textLabel.font property alias font: textLabel.font
property int timestamp: Timestamp
property string formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
property string formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
width: ListView.view ? ListView.view.width : 0 width: ListView.view ? ListView.view.width : 0
spacing: 2 spacing: 2
topPadding: 12 topPadding: 12
bottomPadding: 12 bottomPadding: 12
ColumnLayout {
width: parent.width
TimestampInfo {
id:timestampItem
showDay: root.showDay
showTime: root.showTime
formattedTime: root.formattedTime
formattedDay: root.formattedDay
Layout.alignment: Qt.AlignHCenter
}
Label { Label {
id: textLabel id: textLabel
width: parent.width
text: Body text: Body
horizontalAlignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
font.pointSize: 12 font.pointSize: 12
color: JamiTheme.chatviewTextColor 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 opacity: 0

View file

@ -37,26 +37,26 @@ Control {
// these MUST be set but we won't use the 'required' keyword yet // these MUST be set but we won't use the 'required' keyword yet
property bool isOutgoing property bool isOutgoing
property bool showTime property bool showTime: false
property bool showDay: false
property int seq property int seq
property string author property string author
property string transferId property string transferId
property string registeredNameText property string registeredNameText
property string transferName property string transferName
property string formattedTime property string formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
property string formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
property string location property string location
property string id: Id property string id: Id
property string hoveredLink property string hoveredLink
property var readers: [] property var readers: []
property int timestamp: Timestamp
readonly property real senderMargin: 64 readonly property real senderMargin: 64
readonly property real avatarSize: 20 readonly property real avatarSize: 20
readonly property real msgRadius: 20 readonly property real msgRadius: 20
readonly property real hPadding: JamiTheme.sbsMessageBasePreferredPadding readonly property real hPadding: JamiTheme.sbsMessageBasePreferredPadding
width: ListView.view ? ListView.view.width : 0 width: ListView.view ? ListView.view.width : 0
height: mainColumnLayout.implicitHeight height: mainColumnLayout.implicitHeight
rightPadding: hPadding rightPadding: hPadding
leftPadding: hPadding leftPadding: hPadding
@ -64,11 +64,21 @@ Control {
id: mainColumnLayout id: mainColumnLayout
anchors.centerIn: parent anchors.centerIn: parent
width: parent.width - hPadding * 2 width: parent.width - hPadding * 2
spacing: 0 spacing: 0
TimestampInfo {
id: timestampItem
showDay: root.showDay
showTime: root.showTime
formattedTime: root.formattedTime
formattedDay: root.formattedDay
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.fillHeight: true
}
Item { Item {
id: usernameblock id: usernameblock
@ -107,13 +117,11 @@ Control {
} }
} }
MouseArea { MouseArea {
id: itemMouseArea id: itemMouseArea
Layout.fillWidth: true Layout.fillWidth: true
Layout.fillHeight: true Layout.fillHeight: true
acceptedButtons: Qt.LeftButton | Qt.RightButton acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: function (mouse) { onClicked: function (mouse) {
if (mouse.button === Qt.RightButton if (mouse.button === Qt.RightButton
@ -194,6 +202,7 @@ Control {
id: readsOne id: readsOne
visible: root.readers.length === 1 && CurrentAccount.sendReadReceipt visible: root.readers.length === 1 && CurrentAccount.sendReadReceipt
width: { width: {
if (root.readers.length === 0) if (root.readers.length === 0)
return 0 return 0
@ -216,28 +225,12 @@ Control {
orientation: ListView.Horizontal orientation: ListView.Horizontal
Layout.preferredHeight: { Layout.preferredHeight: {
if (showTime || seq === MsgSeq.last) if (showTime || seq === MsgSeq.last)
return contentHeight + formattedTimeLabel.contentHeight return contentHeight + timestampItem.contentHeight
else if (readsMultiple.visible) else if (readsMultiple.visible)
return JamiTheme.avatarReadReceiptSize return JamiTheme.avatarReadReceiptSize
return 0 return 0
} }
Label {
id: formattedTimeLabel
text: formattedTime
color: JamiTheme.timestampColor
visible: showTime || seq === MsgSeq.last
height: visible * implicitHeight
font.pointSize: 9
topPadding : 4
anchors.rightMargin: status.width
anchors.right: !isOutgoing ? undefined : readsMultiple.left
anchors.left: isOutgoing ? undefined : parent.left
anchors.leftMargin: avatarBlockWidth + 6
}
ReadStatus { ReadStatus {
id: readsMultiple id: readsMultiple
visible: root.readers.length > 1 && CurrentAccount.sendReadReceipt visible: root.readers.length > 1 && CurrentAccount.sendReadReceipt
@ -254,7 +247,6 @@ Control {
anchors.topMargin: 1 anchors.topMargin: 1
readers: root.readers readers: root.readers
} }
} }
} }

View file

@ -21,7 +21,6 @@ import QtQuick
import QtQuick.Controls import QtQuick.Controls
import QtQuick.Layouts import QtQuick.Layouts
import Qt5Compat.GraphicalEffects import Qt5Compat.GraphicalEffects
import net.jami.Models 1.1 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
@ -31,25 +30,21 @@ SBSMessageBase {
id : root id : root
property bool isRemoteImage property bool isRemoteImage
property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth property real maxMsgWidth: root.width - senderMargin - 2 * hPadding - avatarBlockWidth
isOutgoing: Author === "" isOutgoing: Author === ""
author: Author author: Author
readers: Readers readers: Readers
timestamp: Timestamp
formattedTime: MessagesAdapter.getFormattedTime(Timestamp) formattedTime: MessagesAdapter.getFormattedTime(Timestamp)
formattedDay: MessagesAdapter.getFormattedDay(Timestamp)
extraHeight: extraContent.active && !isRemoteImage ? msgRadius : -isRemoteImage extraHeight: extraContent.active && !isRemoteImage ? msgRadius : -isRemoteImage
innerContent.children: [ innerContent.children: [
TextEdit { TextEdit {
padding: JamiTheme.preferredMarginSize padding: JamiTheme.preferredMarginSize
anchors.right: isOutgoing ? parent.right : undefined anchors.right: isOutgoing ? parent.right : undefined
text: Body text: Body
horizontalAlignment: Text.AlignLeft horizontalAlignment: Text.AlignLeft
width: { width: {
if (extraContent.active) if (extraContent.active)
Math.max(extraContent.width, Math.max(extraContent.width,
@ -62,9 +57,7 @@ SBSMessageBase {
height: implicitHeight height: implicitHeight
wrapMode: Label.WrapAtWordBoundaryOrAnywhere wrapMode: Label.WrapAtWordBoundaryOrAnywhere
selectByMouse: true selectByMouse: true
font.pixelSize: JamiTheme.chatviewFontSize font.pixelSize: JamiTheme.chatviewFontSize
font.hintingPreference: Font.PreferNoHinting font.hintingPreference: Font.PreferNoHinting
renderType: Text.NativeRendering renderType: Text.NativeRendering
textFormat: Text.MarkdownText textFormat: Text.MarkdownText

View file

@ -0,0 +1,106 @@
/*
* Copyright (C) 2022 Savoir-faire Linux Inc.
* Author: Nicolas Vengeon <nicolas.vengeon@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 "../mainview/components/"
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import net.jami.Models 1.1
import net.jami.Adapters 1.1
import net.jami.Constants 1.1
ColumnLayout{
id: root
property bool showTime
property bool showDay
property string formattedTime
property string formattedDay
property real detailsOpacity: 0.6
Connections {
target: MessagesAdapter.messageListModel
function onTimestampUpdate() {
if (showTime || showDay) {
formattedTime = MessagesAdapter.getFormattedTime(Timestamp)
}
}
}
Item {
visible: showDay
Layout.alignment: Qt.AlignHCenter
Layout.fillHeight: true
Layout.fillWidth: true
Layout.topMargin: JamiTheme.dayTimestampTopMargin
Rectangle {
id: line
height: 1
opacity: detailsOpacity
color:JamiTheme.timestampColor
width: chatView.width - JamiTheme.timestampLinePadding
anchors.centerIn: parent
}
Rectangle {
id: dayRectangle
width: borderRectangle.width
height: borderRectangle.height
radius: 5
color: JamiTheme.chatviewBgColor
Layout.fillHeight: true
anchors.centerIn: parent
Rectangle {
id: borderRectangle
border { color: JamiTheme.timestampColor; width: 1}
opacity: detailsOpacity
width: formattedDayLabel.width + JamiTheme.dayTimestampVPadding
height: formattedDayLabel.height + JamiTheme.dayTimestampHPadding
radius: dayRectangle.radius
color: JamiTheme.transparentColor
}
Text {
id: formattedDayLabel
color: JamiTheme.chatviewTextColor
anchors { verticalCenter: parent.verticalCenter; horizontalCenter: parent.horizontalCenter}
text: formattedDay
font.pointSize: JamiTheme.timestampFont
}
}
}
Label {
id: formattedTimeLabel
text: formattedTime
Layout.bottomMargin: JamiTheme.timestampBottomMargin
Layout.topMargin: JamiTheme.timestampTopMargin
Layout.alignment: Qt.AlignHCenter
color: JamiTheme.timestampColor
visible: showTime
height: visible * implicitHeight
font.pointSize: JamiTheme.timestampFont
}
}

View file

@ -321,6 +321,16 @@ Item {
property real lineEditContextMenuItemsWidth: 100 property real lineEditContextMenuItemsWidth: 100
property real lineEditContextMenuSeparatorsHeight: 2 property real lineEditContextMenuSeparatorsHeight: 2
//TimestampInfo
property int timestampLinePadding: 40
property int dayTimestampTopMargin: 30
property int timestampBottomMargin: 42
property int timestampTopMargin: 20
property int dayTimestampHPadding: 16
property real dayTimestampVPadding: 32
property real timestampFont: calcSize(12)
property int timestampIntervalTime: 120
// Jami switch // Jami switch
property real switchIndicatorRadius: 30 property real switchIndicatorRadius: 30

View file

@ -44,105 +44,95 @@ JamiListView {
MessagesAdapter.loadMoreMessages() MessagesAdapter.loadMoreMessages()
} }
// sequencing/timestamps (2-sided style)
function computeTimestampVisibility(item, itemIndex) { function computeTimestampVisibility(item1, item1Index, item2, item2Index) {
if (root === undefined) if (item1 && item2) {
return if (item1Index < item2Index) {
var nItem = root.itemAtIndex(itemIndex - 1) item1.showTime = item1.timestamp - item2.timestamp > JamiTheme.timestampIntervalTime
if (nItem && itemIndex !== root.count - 1) { item1.showDay = item1.formattedDay !== item2.formattedDay
item.showTime = (nItem.timestamp - item.timestamp) > 60 &&
nItem.formattedTime !== item.formattedTime
}else { }else {
item.showTime = true item2.showTime = item2.timestamp - item1.timestamp > JamiTheme.timestampIntervalTime
var pItem = root.itemAtIndex(itemIndex + 1) item2.showDay = item2.formattedDay !== item1.formattedDay
if (pItem) {
pItem.showTime = (item.timestamp - pItem.timestamp) > 60 &&
pItem.formattedTime !== item.formattedTime
} }
return true
} }
return false
} }
function computeSequencing(computeItem, computeItemIndex) { function computeChatview(item,itemIndex) {
if (root === undefined) if (!root ) return
return var rootItem = root.itemAtIndex(0)
var cItem = { var pItem = root.itemAtIndex(itemIndex - 1)
'author': computeItem.author, var pItemIndex = itemIndex - 1
'showTime': computeItem.showTime var nItem = root.itemAtIndex(itemIndex + 1)
var nItemIndex = itemIndex + 1
//Middle insertion
if (pItem && nItem) {
computeTimestampVisibility(item, itemIndex, nItem, nItemIndex)
computeSequencing(nItemIndex, nItem, root.itemAtIndex(itemIndex + 2), item)
} }
var pItem = root.itemAtIndex(computeItemIndex + 1) //top buffer insertion = scroll up
var nItem = root.itemAtIndex(computeItemIndex - 1)
let isSeq = (item0, item1) =>
item0.author === item1.author && !item0.showTime
let setSeq = function (newSeq, item) {
if (item === undefined)
computeItem.seq = newSeq
else
item.seq = newSeq
}
let rAdjustSeq = function (item) {
if (item.seq === MsgSeq.last)
item.seq = MsgSeq.middle
else if (item.seq === MsgSeq.single)
setSeq(MsgSeq.first, item)
}
let adjustSeq = function (item) {
if (item.seq === MsgSeq.first)
item.seq = MsgSeq.middle
else if (item.seq === MsgSeq.single)
setSeq(MsgSeq.last, item)
}
if (pItem && !nItem) { if (pItem && !nItem) {
if (!isSeq(pItem, cItem)) { computeTimestampVisibility(item, itemIndex, pItem, pItemIndex)
computeItem.seq = MsgSeq.single computeSequencing(pItemIndex, pItem, item, root.itemAtIndex(itemIndex - 2))
} else {
computeItem.seq = MsgSeq.last
rAdjustSeq(pItem)
} }
} else if (nItem && !pItem) { //bottom buffer insertion = scroll down
if (!isSeq(cItem, nItem)) { if (!pItem && nItem) {
computeItem.seq = MsgSeq.single computeTimestampVisibility(item, itemIndex, nItem, nItemIndex)
} else { computeSequencing(nItemIndex, nItem, root.itemAtIndex(itemIndex + 2), item)
setSeq(MsgSeq.first)
adjustSeq(nItem)
} }
} else if (!nItem && !pItem) { //index 0 insertion = new message
computeItem.seq = MsgSeq.single if (itemIndex === 0) {
} else { Qt.callLater(computeSequencing, itemIndex, item, root.itemAtIndex(itemIndex + 1), null)
if (isSeq(pItem, nItem)) { if (! computeTimestampVisibility(item, itemIndex, nItem, nItemIndex)) {
if (isSeq(pItem, cItem)) { Qt.callLater(computeChatview, item, itemIndex)
computeItem.seq = MsgSeq.middle
} else {
computeItem.seq = MsgSeq.single
if (pItem.seq === MsgSeq.first)
pItem.seq = MsgSeq.single
else if (item.seq === MsgSeq.middle)
pItem.seq = MsgSeq.last
if (nItem.seq === MsgSeq.last)
nItem.seq = MsgSeq.single
else if (nItem.seq === MsgSeq.middle)
nItem.seq = MsgSeq.first
} }
} else {
if (!isSeq(pItem, cItem)) {
computeItem.seq = MsgSeq.first
adjustSeq(pItem)
} else {
computeItem.seq = MsgSeq.last
rAdjustSeq(nItem)
} }
//top element
if(itemIndex === root.count - 1 && CurrentConversation.allMessagesLoaded) {
item.showTime = true
item.showDay = true
} }
} }
if (computeItem.seq === MsgSeq.last) { function computeSequencing(index, item, nItem, pItem) {
computeItem.showTime = true if (root === undefined || !item)
return
function isFirst() {
if (!nItem) return true
else{
if (item.showTime) {
return true
} }
if (nItem.author !== item.author) {
return true
}
}
return false
}
function isLast() {
if (!pItem) return true
else{
if (pItem.showTime) {
return true
}
if (pItem.author !== item.author) {
return true
}
}
return false
}
if (isLast() && isFirst())
item.seq = MsgSeq.single
if (!isLast() && isFirst())
item.seq = MsgSeq.first
if (isLast() && !isFirst())
item.seq = MsgSeq.last
if (!isLast() && !isFirst())
item.seq = MsgSeq.middle
} }
// fade-in mechanism // fade-in mechanism
@ -189,6 +179,7 @@ JamiListView {
width: parent.width width: parent.width
// this offscreen caching is pretty huge // this offscreen caching is pretty huge
// displayMarginEnd may be removed // displayMarginEnd may be removed
displayMarginBeginning: 2048 displayMarginBeginning: 2048
displayMarginEnd: 2048 displayMarginEnd: 2048
maximumFlickVelocity: 2048 maximumFlickVelocity: 2048
@ -216,65 +207,55 @@ JamiListView {
id: delegateChooser id: delegateChooser
role: "Type" role: "Type"
DelegateChoice { DelegateChoice {
id: delegateChoice
roleValue: Interaction.Type.TEXT roleValue: Interaction.Type.TEXT
TextMessageDelegate { TextMessageDelegate {
Component.onCompleted: { Component.onCompleted: {
if (index) { computeChatview(this,index)
computeTimestampVisibility(this, index)
computeSequencing(this, index)
} else {
Qt.callLater(computeTimestampVisibility, this, index)
Qt.callLater(computeSequencing, this, index)
}
} }
} }
} }
DelegateChoice { DelegateChoice {
roleValue: Interaction.Type.CALL roleValue: Interaction.Type.CALL
GeneratedMessageDelegate { GeneratedMessageDelegate {
Component.onCompleted: { Component.onCompleted: {
if (index) computeChatview(this,index)
computeTimestampVisibility(this, index)
else
Qt.callLater(computeTimestampVisibility, this, index)
} }
} }
} }
DelegateChoice { DelegateChoice {
roleValue: Interaction.Type.CONTACT roleValue: Interaction.Type.CONTACT
ContactMessageDelegate { ContactMessageDelegate {
Component.onCompleted: { Component.onCompleted: {
if (index) computeChatview(this,index)
computeTimestampVisibility(this, index)
else
Qt.callLater(computeTimestampVisibility, this, index)
} }
} }
} }
DelegateChoice { DelegateChoice {
roleValue: Interaction.Type.INITIAL roleValue: Interaction.Type.INITIAL
GeneratedMessageDelegate { GeneratedMessageDelegate {
font.bold: true font.bold: true
Component.onCompleted: { Component.onCompleted: {
if (index) computeChatview(this,index)
computeTimestampVisibility(this, index)
else
Qt.callLater(computeTimestampVisibility, this, index)
} }
} }
} }
DelegateChoice { DelegateChoice {
roleValue: Interaction.Type.DATA_TRANSFER roleValue: Interaction.Type.DATA_TRANSFER
DataTransferMessageDelegate { DataTransferMessageDelegate {
Component.onCompleted: { Component.onCompleted: {
if (index) { computeChatview(this,index)
computeTimestampVisibility(this, index)
computeSequencing(this, index)
} else {
Qt.callLater(computeTimestampVisibility, this, index)
Qt.callLater(computeSequencing, this, index)
}
} }
} }
} }

View file

@ -581,21 +581,41 @@ MessagesAdapter::getFormattedTime(const quint64 timestamp)
{ {
const auto now = QDateTime::currentDateTime(); const auto now = QDateTime::currentDateTime();
const auto seconds = now.toSecsSinceEpoch() - timestamp; const auto seconds = now.toSecsSinceEpoch() - timestamp;
auto interval = qFloor(seconds / (3600 * 24)); auto interval = qFloor(seconds / 60);
if (interval > 5)
return QLocale::system().toString(QDateTime::fromSecsSinceEpoch(timestamp), if (interval > 1) {
QLocale::ShortFormat); auto curLang = settingsManager_->getValue(Settings::Key::LANG);
if (interval > 1) auto curLocal(QLocale(curLang.toString()));
return QObject::tr("%1 days ago").arg(interval); auto curTime = QDateTime::fromSecsSinceEpoch(timestamp).time();
if (interval == 1) QString timeLocale;
return QObject::tr("one day ago"); if (curLang == "SYSTEM")
interval = qFloor(seconds / 3600); timeLocale = QLocale::system().toString(curTime, QLocale::system().ShortFormat);
if (interval > 1) else
return QObject::tr("%1 hours ago").arg(interval); timeLocale = curLocal.toString(curTime, curLocal.ShortFormat);
if (interval == 1)
return QObject::tr("one hour ago"); return timeLocale;
interval = qFloor(seconds / 60); }
if (interval > 1)
return QObject::tr("%1 minutes ago").arg(interval);
return QObject::tr("just now"); return QObject::tr("just now");
} }
QString
MessagesAdapter::getFormattedDay(const quint64 timestamp)
{
auto now = QDate::currentDate();
auto before = QDateTime::fromSecsSinceEpoch(timestamp).date();
if (before == now)
return QObject::tr("Today");
if (before.daysTo(now) == 1)
return QObject::tr("Yesterday");
auto curLang = settingsManager_->getValue(Settings::Key::LANG);
auto curLocal(QLocale(curLang.toString()));
auto curDate = QDateTime::fromSecsSinceEpoch(timestamp).date();
QString dateLocale;
if (curLang == "SYSTEM")
dateLocale = QLocale::system().toString(curDate, QLocale::system().ShortFormat);
else
dateLocale = curLocal.toString(curDate, curLocal.ShortFormat);
return dateLocale;
}

View file

@ -79,6 +79,7 @@ protected:
Q_INVOKABLE QVariantMap isLocalImage(const QString& mimeName); Q_INVOKABLE QVariantMap isLocalImage(const QString& mimeName);
Q_INVOKABLE QVariantMap getMediaInfo(const QString& msg); Q_INVOKABLE QVariantMap getMediaInfo(const QString& msg);
Q_INVOKABLE bool isRemoteImage(const QString& msg); Q_INVOKABLE bool isRemoteImage(const QString& msg);
Q_INVOKABLE QString getFormattedDay(const quint64 timestamp);
Q_INVOKABLE QString getFormattedTime(const quint64 timestamp); Q_INVOKABLE QString getFormattedTime(const quint64 timestamp);
Q_INVOKABLE void parseMessageUrls(const QString& messageId, Q_INVOKABLE void parseMessageUrls(const QString& messageId,
const QString& msg, const QString& msg,

View file

@ -62,7 +62,6 @@ RowLayout {
Layout.preferredWidth: root.itemWidth Layout.preferredWidth: root.itemWidth
Layout.preferredHeight: JamiTheme.preferredFieldHeight Layout.preferredHeight: JamiTheme.preferredFieldHeight
Layout.alignment: Qt.AlignCenter Layout.alignment: Qt.AlignCenter
font.pointSize: JamiTheme.buttonFontSize font.pointSize: JamiTheme.buttonFontSize
font.kerning: true font.kerning: true
@ -91,7 +90,6 @@ RowLayout {
MaterialToolTip { MaterialToolTip {
id: toolTip id: toolTip
parent: textField parent: textField
visible: textField.hovered && (root.tooltipText.length > 0) visible: textField.hovered && (root.tooltipText.length > 0)
delay: Qt.styleHints.mousePressAndHoldInterval delay: Qt.styleHints.mousePressAndHoldInterval

View file

@ -36,7 +36,11 @@ using reverseIterator = MessageListModel::reverseIterator;
MessageListModel::MessageListModel(QObject* parent) MessageListModel::MessageListModel(QObject* parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)
{} , timestampTimer_(new QTimer(this))
{
connect(timestampTimer_, &QTimer::timeout, this, &MessageListModel::timestampUpdate);
timestampTimer_->start(1000);
}
QPair<iterator, bool> QPair<iterator, bool>
MessageListModel::emplace(const QString& msgId, interaction::Info message, bool beginning) MessageListModel::emplace(const QString& msgId, interaction::Info message, bool beginning)

View file

@ -22,6 +22,7 @@
#include "api/interaction.h" #include "api/interaction.h"
#include <QAbstractListModel> #include <QAbstractListModel>
#include <QTimer>
namespace lrc { namespace lrc {
namespace api { namespace api {
@ -125,6 +126,8 @@ public:
void emitDataChanged(iterator it, VectorInt roles = {}); void emitDataChanged(iterator it, VectorInt roles = {});
void emitDataChanged(const QString& msgId, VectorInt roles = {}); void emitDataChanged(const QString& msgId, VectorInt roles = {});
Q_SIGNAL void timestampUpdate();
protected: protected:
using Role = MessageList::Role; using Role = MessageList::Role;
@ -144,6 +147,8 @@ private:
iterator insertMessage(iterator it, item_t& message); iterator insertMessage(iterator it, item_t& message);
void removeMessage(int index, iterator it); void removeMessage(int index, iterator it);
void moveMessage(int from, int to); void moveMessage(int from, int to);
QTimer* timestampTimer_ {nullptr};
}; };
} // namespace api } // namespace api
} // namespace lrc } // namespace lrc