Merge pull request #1906 from scvalex/later-bubble

Add "N hours later" bubble in conversation when there is a +1 hour gap
This commit is contained in:
DeepBlueV7.X 2025-06-20 21:02:50 +00:00 committed by GitHub
commit d214763f21
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 29 additions and 6 deletions

View file

@ -21,6 +21,7 @@ TimelineEvent {
required property bool isSender
required property int index
property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day)
property var previousMessageTimestamp: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Timestamp)
property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent)
property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId)
@ -46,6 +47,9 @@ TimelineEvent {
property alias hovered: messageHover.hovered
property int oneHour: 60 * 60 * 1000
property bool showSection: wrapper.previousMessageDay !== wrapper.day || wrapper.timestamp - wrapper.previousMessageTimestamp > oneHour
mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0) + 4
replyInset: mainInset + 4 + Nheko.paddingSmall
@ -57,7 +61,7 @@ TimelineEvent {
Loader {
id: section
active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.previousMessageDay !== wrapper.day || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent
active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.showSection || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent
//asynchronous: true
sourceComponent: TimelineSectionHeader {
day: wrapper.day
@ -65,6 +69,7 @@ TimelineEvent {
isStateEvent: wrapper.isStateEvent
parentWidth: wrapper.width
previousMessageDay: wrapper.previousMessageDay
previousMessageTimestamp: wrapper.previousMessageTimestamp
previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent
previousMessageUserId: wrapper.previousMessageUserId
timestamp: wrapper.timestamp

View file

@ -21,6 +21,7 @@ TimelineEvent {
required property bool isSender
required property int index
property var previousMessageDay: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Day)
property var previousMessageTimestamp: (index + 1) >= chat.count ? 0 : chat.model.dataByIndex(index + 1, Room.Timestamp)
property bool previousMessageIsStateEvent: (index + 1) >= chat.count ? true : chat.model.dataByIndex(index + 1, Room.IsStateEvent)
property string previousMessageUserId: (index + 1) >= chat.count ? "" : chat.model.dataByIndex(index + 1, Room.UserId)
@ -46,6 +47,9 @@ TimelineEvent {
property alias hovered: messageHover.hovered
property int oneHour: 60 * 60 * 1000
property bool showSection: wrapper.previousMessageDay !== wrapper.day || wrapper.timestamp - wrapper.previousMessageTimestamp > oneHour
mainInset: (threadId ? (4 + Nheko.paddingSmall) : 0)
replyInset: mainInset + 4 + Nheko.paddingSmall
@ -55,7 +59,7 @@ TimelineEvent {
Loader {
id: section
active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.previousMessageDay !== wrapper.day || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent
active: wrapper.previousMessageUserId !== wrapper.userId || wrapper.showSection || wrapper.previousMessageIsStateEvent !== wrapper.isStateEvent
//asynchronous: true
sourceComponent: TimelineSectionHeader {
day: wrapper.day
@ -63,6 +67,7 @@ TimelineEvent {
isStateEvent: wrapper.isStateEvent
parentWidth: wrapper.width
previousMessageDay: wrapper.previousMessageDay
previousMessageTimestamp: wrapper.previousMessageTimestamp
previousMessageIsStateEvent: wrapper.previousMessageIsStateEvent
previousMessageUserId: wrapper.previousMessageUserId
timestamp: wrapper.timestamp

View file

@ -16,6 +16,7 @@ Column {
required property bool isStateEvent
required property int parentWidth
required property var previousMessageDay
required property var previousMessageTimestamp
required property bool previousMessageIsStateEvent
required property string previousMessageUserId
required property date timestamp
@ -23,10 +24,14 @@ Column {
required property string userName
required property string userPowerlevel
bottomPadding: Settings.bubbles ? (isSender && previousMessageDay == day ? 0 : 2) : 3
property int oneHour: 60 * 60 * 1000
property bool dayChanged: previousMessageDay !== day
property bool showLabel: dayChanged || timestamp - previousMessageTimestamp > oneHour
bottomPadding: Settings.bubbles ? (isSender && !showLabel ? 0 : 2) : 3
spacing: 8
topPadding: userName_.visible ? 4 : 0
visible: (previousMessageUserId !== userId || previousMessageDay !== day || isStateEvent !== previousMessageIsStateEvent)
visible: (previousMessageUserId !== userId || showLabel || isStateEvent !== previousMessageIsStateEvent)
width: parentWidth
Label {
@ -36,9 +41,9 @@ Column {
color: palette.text
height: Math.round(fontMetrics.height * 1.4)
horizontalAlignment: Text.AlignHCenter
text: room ? room.formatDateSeparator(timestamp) : ""
text: room ? (dayChanged ? room.formatDateSeparator(timestamp) : room.formatLaterSeparator(previousMessageTimestamp, timestamp)) : ""
verticalAlignment: Text.AlignVCenter
visible: room && previousMessageDay !== day
visible: room && showLabel
width: contentWidth * 1.2
background: Rectangle {

View file

@ -1441,6 +1441,13 @@ TimelineModel::formatDateSeparator(QDate date) const
return date.toString(fmt);
}
QString
TimelineModel::formatLaterSeparator(QDateTime prevDate, QDateTime date) const
{
auto deltaHours = prevDate.secsTo(date) / 60 / 60;
return tr("%n hour(s) later", "", deltaHours);
}
void
TimelineModel::viewRawMessage(const QString &id)
{

View file

@ -305,6 +305,7 @@ public:
Q_INVOKABLE QString displayName(const QString &id) const;
Q_INVOKABLE QString avatarUrl(const QString &id) const;
Q_INVOKABLE QString formatDateSeparator(QDate date) const;
Q_INVOKABLE QString formatLaterSeparator(QDateTime prevDate, QDateTime date) const;
Q_INVOKABLE QString formatTypingUsers(const QStringList &users, const QColor &bg);
Q_INVOKABLE bool showAcceptKnockButton(const QString &id);
Q_INVOKABLE void acceptKnock(const QString &id);