Rework how messages and events are handled

- No more translatable, content_type, show_name_line attrs for
  TimelineEventReceived.
  Since they are UI concerns, they are handled directly in QML.

- Refactor the EventDelegate and get rid of errors when new items
  are added to the timeline

- Messages, events and emotes all combine correctly.

- No more 28px wide avatars for events, to make them uniform with
  messages.
This commit is contained in:
miruka
2019-07-19 23:07:26 -04:00
parent ecc2c099f1
commit cea586120e
9 changed files with 148 additions and 160 deletions

View File

@@ -11,15 +11,22 @@ Row {
spacing: theme.spacing / 2
// layoutDirection: onRight ? Qt.RightToLeft : Qt.LeftToRight
HUserAvatar {
id: avatar
userId: model.senderId
width: onRight ? 0 : model.showNameLine ? 48 : 28
height: onRight ? 0 : combine ? 1 : model.showNameLine ? 48 : 28
opacity: combine ? 0 : 1
Item {
width: hideAvatar ? 0 : 48
height: hideAvatar ? 0 : collapseAvatar ? 1 : smallAvatar ? 28 : 48
opacity: hideAvatar || collapseAvatar ? 0 : 1
visible: width > 0
// Don't animate w/h of avatar itself! It might affect the sourceSize
Behavior on width { HNumberAnimation {} }
Behavior on height { HNumberAnimation {} }
HUserAvatar {
id: avatar
userId: model.senderId
width: hideAvatar ? 0 : 48
height: hideAvatar ? 0 : collapseAvatar ? 1 : 48
}
}
Rectangle {
@@ -37,34 +44,34 @@ Row {
)
)
height: nameLabel.height + contentLabel.implicitHeight
y: parent.height / 2 - height / 2
Column {
spacing: 0
anchors.fill: parent
HLabel {
width: parent.width
height: model.showNameLine && ! onRight && ! combine ?
implicitHeight : 0
Behavior on height { HNumberAnimation {} }
visible: height > 0
id: nameLabel
visible: height > 0
width: parent.width
height: hideNameLine ? 0 : implicitHeight
Behavior on height { HNumberAnimation {} }
text: senderInfo.displayName || model.senderId
color: Utils.nameColor(avatar.name)
elide: Text.ElideRight
horizontalAlignment: onRight ? Text.AlignRight : Text.AlignLeft
leftPadding: horizontalPadding
rightPadding: horizontalPadding
topPadding: verticalPadding
leftPadding: theme.spacing
rightPadding: leftPadding
topPadding: theme.spacing / 2
}
HRichLabel {
id: contentLabel
width: parent.width
id: contentLabel
text: Utils.translatedEventContent(model) +
text: Utils.processedEventText(model) +
// time
"&nbsp;&nbsp;<font size=" + theme.fontSize.small +
"px color=" + theme.chat.message.date + ">" +
@@ -78,10 +85,10 @@ Row {
color: theme.chat.message.body
wrapMode: Text.Wrap
leftPadding: horizontalPadding
rightPadding: horizontalPadding
topPadding: nameLabel.visible ? 0 : verticalPadding
bottomPadding: verticalPadding
leftPadding: theme.spacing
rightPadding: leftPadding
topPadding: nameLabel.visible ? 0 : bottomPadding
bottomPadding: theme.spacing / 2
}
}
}

View File

@@ -4,84 +4,54 @@
import QtQuick 2.12
import QtQuick.Layouts 1.12
import "../../Base"
import "../../utils.js" as Utils
Column {
id: roomEventDelegate
id: eventDelegate
function minsBetween(date1, date2) {
return Math.round((((date2 - date1) % 86400000) % 3600000) / 60000)
// Remember timeline goes from newest message at index 0 to oldest
property var previousItem: eventList.model.get(model.index + 1)
property var nextItem: eventList.model.get(model.index - 1)
property int modelCount: eventList.model.count
onModelCountChanged: {
previousItem = eventList.model.get(model.index + 1)
nextItem = eventList.model.get(model.index - 1)
}
function getPreviousItem(nth=1) {
// Remember, index 0 = newest bottomest message
return eventList.model.count - 1 > model.index + nth ?
eventList.model.get(model.index + nth) : null
}
property var senderInfo: senderInfo = users.find(model.senderId)
property var previousItem: getPreviousItem()
signal reloadPreviousItem()
onReloadPreviousItem: previousItem = getPreviousItem()
property bool isOwn: chatPage.userId === model.senderId
property bool onRight: eventList.ownEventsOnRight && isOwn
property bool combine: eventList.canCombine(previousItem, model)
property bool talkBreak: eventList.canTalkBreak(previousItem, model)
property bool dayBreak: eventList.canDayBreak(previousItem, model)
property var senderInfo: null
Component.onCompleted: senderInfo = users.find(model.senderId)
readonly property bool smallAvatar:
eventList.canCombine(model, nextItem) &&
(model.eventType == "RoomMessageEmote" ||
! model.eventType.startsWith("RoomMessage"))
readonly property bool isOwn: chatPage.userId === model.senderId
readonly property bool onRight: eventList.ownEventsOnRight && isOwn
readonly property bool collapseAvatar: combine
readonly property bool hideAvatar: onRight
readonly property bool isFirstEvent: model.eventType == "RoomCreateEvent"
// Item roles may not be loaded yet, reason for all these checks
readonly property bool combine: Boolean(
model.date &&
previousItem && previousItem.eventType && previousItem.date &&
Utils.eventIsMessage(previousItem) == Utils.eventIsMessage(model) &&
// RoomMessageEmote are shown inline-style
! (previousItem.eventType == "RoomMessageEmote" &&
model.eventType != "RoomMessageEmote") &&
! (previousItem.eventType != "RoomMessageEmote" &&
model.eventType == "RoomMessageEmote") &&
! talkBreak &&
! dayBreak &&
previousItem.senderId === model.senderId &&
minsBetween(previousItem.date, model.date) <= 5
)
readonly property bool dayBreak: Boolean(
isFirstEvent ||
model.date && previousItem && previousItem.date &&
model.date.getDate() != previousItem.date.getDate()
)
readonly property bool talkBreak: Boolean(
model.date && previousItem && previousItem.date &&
! dayBreak &&
minsBetween(previousItem.date, model.date) >= 20
)
readonly property int horizontalPadding: theme.spacing
readonly property int verticalPadding: theme.spacing / 2
ListView.onAdd: {
let nextDelegate = eventList.contentItem.children[index]
if (nextDelegate) { nextDelegate.reloadPreviousItem() }
}
readonly property bool hideNameLine:
model.eventType == "RoomMessageEmote" ||
! model.eventType.startsWith("RoomMessage") ||
onRight ||
combine
width: eventList.width
topPadding:
isFirstEvent ? 0 :
dayBreak ? theme.spacing * 4 :
model.eventType == "RoomCreateEvent" ? 0 :
dayBreak ? theme.spacing * 4 :
talkBreak ? theme.spacing * 6 :
combine ? theme.spacing / 2 :
combine ? theme.spacing / 2 :
theme.spacing * 2
Loader {
source: dayBreak ? "Daybreak.qml" : ""
width: roomEventDelegate.width
width: eventDelegate.width
}
EventContent {

View File

@@ -4,6 +4,7 @@
import QtQuick 2.12
import SortFilterProxyModel 0.2
import "../../Base"
import "../../utils.js" as Utils
HRectangle {
property alias listView: eventList
@@ -14,6 +15,37 @@ HRectangle {
id: eventList
clip: true
function canCombine(item, itemAfter) {
if (! item || ! itemAfter) { return false }
return Boolean(
! canTalkBreak(item, itemAfter) &&
! canDayBreak(item, itemAfter) &&
item.senderId === itemAfter.senderId &&
Utils.minutesBetween(item.date, itemAfter.date) <= 5
)
}
function canTalkBreak(item, itemAfter) {
if (! item || ! itemAfter) { return false }
return Boolean(
! canDayBreak(item, itemAfter) &&
Utils.minutesBetween(item.date, itemAfter.date) >= 20
)
}
function canDayBreak(item, itemAfter) {
if (! item || ! itemAfter || ! item.date || ! itemAfter.date) {
return false
}
return Boolean(
itemAfter.eventType == "RoomCreateEvent" ||
item.date.getDate() != itemAfter.date.getDate()
)
}
model: HListModel {
sourceModel: timelines