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:
@@ -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
|
||||
" <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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
||||
|
Reference in New Issue
Block a user