moment/src/gui/Pages/Chat/Timeline/EventDelegate.qml
miruka 39f159f0a3 Fix the one-line events combining
When there's a one line (emote or non-message) event with its avatar
shown and combinable events follow it, that first event delegate's height
is supposed to be one line with the avatar overflowing into
the following event's blank space.

This behavior hasn't been working for a long time. This commit restores
it, along with improving the related code's quality.
2020-09-01 13:48:15 -04:00

288 lines
8.5 KiB
QML

// SPDX-License-Identifier: LGPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Layouts 1.12
import Clipboard 0.1
import "../../.."
import "../../../Base"
import "../../../PythonBridge"
HColumnLayout {
id: eventDelegate
property var hoveredMediaTypeUrl: [] // [] or [mediaType, url, title]
property var fetchProfilesFuture: null
// Remember timeline goes from newest message at index 0 to oldest
readonly property var previousModel: eventList.model.get(model.index + 1)
readonly property var nextModel: eventList.model.get(model.index - 1)
readonly property QtObject currentModel: model
readonly property bool compact: window.settings.compactMode
readonly property bool checked: model.id in eventList.checked
readonly property bool isOwn: chat.userId === model.sender_id
readonly property bool isRedacted: model.event_type === "RedactedEvent"
readonly property bool onRight: ! eventList.ownEventsOnLeft && isOwn
readonly property bool combine: eventList.canCombine(previousModel, model)
readonly property bool asOneLine: eventList.renderEventAsOneLine(model)
readonly property bool talkBreak:
eventList.canTalkBreak(previousModel, model)
readonly property bool dayBreak:
eventList.canDayBreak(previousModel, model)
readonly property int cursorShape:
eventContent.hoveredLink || hoveredMediaTypeUrl.length === 3 ?
Qt.PointingHandCursor :
eventContent.hoveredSelectable ? Qt.IBeamCursor :
Qt.ArrowCursor
readonly property int separationSpacing:
dayBreak ? theme.spacing * 4 :
talkBreak ? theme.spacing * 6 :
combine ? theme.spacing / (compact ? 4 : 2) :
theme.spacing * (compact ? 1 : 2)
readonly property alias eventContent: eventContent
function json() {
let event = ModelStore.get(chat.userId, chat.roomId, "events")
.get(model.index)
event = JSON.parse(JSON.stringify(event))
event.source = JSON.parse(event.source)
return JSON.stringify(event, null, 4)
}
function openContextMenu() {
contextMenu.media = eventDelegate.hoveredMediaTypeUrl
contextMenu.link = eventContent.hoveredLink
contextMenu.popup()
}
function toggleChecked() {
eventList.toggleCheck(model.index)
}
width: eventList.width - eventList.leftMargin - eventList.rightMargin
// Needed because of eventList's MouseArea which steals the
// HSelectableLabel's MouseArea hover events
onCursorShapeChanged: eventList.cursorShape = cursorShape
Component.onCompleted: if (model.fetch_profile)
fetchProfilesFuture = py.callClientCoro(
chat.userId,
"get_event_profiles",
[chat.roomId, model.id],
() => { fetchProfilesFuture = null }
)
Component.onDestruction:
if (fetchProfilesFuture) fetchProfilesFuture.cancel()
ListView.onRemove: eventList.uncheck(model.id)
Item {
Layout.fillWidth: true
visible: model.event_type !== "RoomCreateEvent"
Layout.preferredHeight: separationSpacing
}
Daybreak {
visible: dayBreak
Layout.fillWidth: true
Layout.minimumWidth: parent.width
Layout.bottomMargin: separationSpacing
}
EventContent {
id: eventContent
Layout.fillWidth: true
Behavior on x { HNumberAnimation {} }
}
TapHandler {
acceptedButtons: Qt.LeftButton
acceptedModifiers: Qt.NoModifier
onTapped: toggleChecked()
}
TapHandler {
acceptedButtons: Qt.LeftButton
acceptedModifiers: Qt.ShiftModifier
onTapped: eventList.checkFromLastToHere(model.index)
}
TapHandler {
acceptedButtons: Qt.RightButton
acceptedPointerTypes: PointerDevice.GenericPointer | PointerDevice.Pen
onTapped: openContextMenu()
}
TapHandler {
acceptedPointerTypes: PointerDevice.Finger | PointerDevice.Pen
onLongPressed: openContextMenu()
}
HMenu {
id: contextMenu
property var media: []
property string link: ""
readonly property bool isEncryptedMedia:
Object.keys(JSON.parse(model.media_crypt_dict)).length > 0
onClosed: {
media = []
link = ""
}
HMenuItem {
icon.name: "toggle-select-message"
text: eventDelegate.checked ? qsTr("Deselect") : qsTr("Select")
onTriggered: eventDelegate.toggleChecked()
}
HMenuItem {
visible: eventList.selectedCount >= 2
icon.name: "deselect-all-messages"
text: qsTr("Deselect all")
onTriggered: eventList.checked = {}
}
HMenuItem {
visible: model.index !== 0
icon.name: "select-until-here"
text: qsTr("Select until here")
onTriggered: eventList.checkFromLastToHere(model.index)
}
HMenuItem {
icon.name: "open-externally"
text: qsTr("Open externally")
visible: Boolean(model.media_url)
onTriggered: eventList.openMediaExternally(model)
}
HMenuItem {
icon.name: "copy-local-path"
text: qsTr("Copy local path")
visible: Boolean(model.media_local_path)
onTriggered:
Clipboard.text =
model.media_local_path.replace(/^file:\/\//, "")
}
HMenuItem {
id: copyMedia
icon.name: "copy-link"
text:
contextMenu.media.length === 0 ||
contextMenu.isEncryptedMedia ?
"" :
contextMenu.media[0] === Utils.Media.File ?
qsTr("Copy file address") :
contextMenu.media[0] === Utils.Media.Image ?
qsTr("Copy image address") :
contextMenu.media[0] === Utils.Media.Video ?
qsTr("Copy video address") :
qsTr("Copy audio address")
visible: Boolean(text)
onTriggered: Clipboard.text = model.media_http_url
}
HMenuItem {
icon.name: "copy-link"
text: qsTr("Copy link address")
visible: Boolean(contextMenu.link)
onTriggered: Clipboard.text = contextMenu.link
}
HMenuItem {
icon.name: "copy-text"
text:
eventList.selectedCount ? qsTr("Copy selection") :
contextMenu.media.length > 0 ? qsTr("Copy filename") :
qsTr("Copy text")
onTriggered: {
if (! eventList.selectedCount){
Clipboard.text = JSON.parse(model.source).body
return
}
eventList.copySelectedDelegates()
}
}
HMenuItem {
icon.name: "reply-to"
text: qsTr("Reply")
onTriggered: {
chat.replyToEventId = model.id
chat.replyToUserId = model.sender_id
chat.replyToDisplayName = model.sender_name
}
}
HMenuItemPopupSpawner {
readonly property var events: {
eventList.selectedCount ?
eventList.redactableCheckedEvents :
eventList.canRedact(currentModel) ?
[model] :
[]
}
icon.name: "remove-message"
text: qsTr("Remove")
enabled: properties.eventSenderAndIds.length
popup: "Popups/RedactPopup.qml"
properties: ({
preferUserId: chat.userId,
roomId: chat.roomId,
eventSenderAndIds: events.map(ev => [ev.sender_id, ev.id]),
onlyOwnMessageWarning:
! chat.roomInfo.can_redact_all &&
events.length < eventList.selectedCount
})
}
HMenuItem {
icon.name: "debug"
text: qsTr("Debug this event")
onTriggered:
mainUI.debugConsole.toggle(eventContent, "t.parent.json()")
}
HMenuItemPopupSpawner {
icon.name: "clear-messages"
text: qsTr("Clear messages")
popup: "Popups/ClearMessagesPopup.qml"
properties: ({
userId: chat.userId,
roomId: chat.roomId,
preClearCallback: eventList.uncheckAll,
})
}
}
}