moment/src/gui/Pages/Chat/Timeline/EventDelegate.qml

297 lines
8.8 KiB
QML
Raw Normal View History

// SPDX-License-Identifier: LGPL-3.0-or-later
import QtQuick 2.12
import QtQuick.Layouts 1.12
2019-12-27 09:06:42 -04:00
import Clipboard 0.1
import "../../.."
2019-12-18 04:53:08 -04:00
import "../../../Base"
import "../../../PythonBridge"
2019-03-21 23:28:14 -04:00
HColumnLayout {
id: eventDelegate
2019-03-21 23:28:14 -04:00
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
property bool checked: model.id in eventList.checked
property bool compact: window.settings.compactMode
2019-12-09 05:25:31 -04:00
property bool isOwn: chat.userId === model.sender_id
property bool onRight: ! eventList.ownEventsOnLeft && isOwn
property bool combine: eventList.canCombine(previousModel, model)
property bool talkBreak: eventList.canTalkBreak(previousModel, model)
property bool dayBreak: eventList.canDayBreak(previousModel, model)
2019-07-19 20:55:52 -04:00
readonly property bool smallAvatar: compact
readonly property bool collapseAvatar: combine
readonly property bool hideAvatar: onRight
readonly property bool isRedacted: model.event_type === "RedactedEvent"
2019-03-21 23:28:14 -04:00
readonly property bool hideNameLine:
model.event_type === "RoomMessageEmote" ||
! (
model.event_type.startsWith("RoomMessage") ||
model.event_type.startsWith("RoomEncrypted")
) ||
onRight ||
combine
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)
}
2019-09-14 18:52:43 -04:00
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) py.callClientCoro(
chat.userId, "get_event_profiles", [chat.roomId, model.id],
)
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
2019-07-20 02:27:17 -04:00
}
2019-07-02 22:29:09 -04:00
EventContent {
id: eventContent
Layout.fillWidth: true
2019-12-16 04:42:41 -04:00
Behavior on x { HNumberAnimation {} }
2019-04-28 14:48:59 -04:00
}
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
2019-09-14 18:52:43 -04:00
onTapped: openContextMenu()
}
TapHandler {
acceptedPointerTypes: PointerDevice.Finger | PointerDevice.Pen
onLongPressed: openContextMenu()
}
HMenu {
id: contextMenu
property var media: []
property string link: ""
property var localPath: null
property Future getLocalFuture: null
readonly property bool isEncryptedMedia:
Object.keys(JSON.parse(model.media_crypt_dict)).length > 0
onClosed: {
if (getLocalFuture) getLocalFuture.cancel()
media = []
link = ""
}
onOpened: if (media.length === 3 && media[1].startsWith("mxc://")) {
getLocalFuture = py.callCoro(
"media_cache.get_local_media",
[media[1], media[2]],
path => { localPath = path; getLocalFuture = null },
)
}
HMenuItem {
icon.name: "toggle-select-message"
2020-03-27 07:06:38 -04:00
text: eventDelegate.checked ? qsTr("Deselect") : qsTr("Select")
onTriggered: eventDelegate.toggleChecked()
}
HMenuItem {
visible: eventList.selectedCount >= 2
2020-03-27 07:06:38 -04:00
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: "copy-local-path"
text: qsTr("Copy local path")
visible: Boolean(contextMenu.localPath)
onTriggered:
Clipboard.text =
contextMenu.localPath.replace(/^file:\/\//, "")
}
HMenuItem {
id: copyMedia
icon.name: "copy-link"
text:
contextMenu.media.length === 0 || 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 ?
2019-09-17 23:23:47 -04:00
qsTr("Copy video address") :
qsTr("Copy audio address")
visible: Boolean(text)
onTriggered: Clipboard.text = contextMenu.media[1]
}
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 && ! eventList.currentItem){
Clipboard.text = JSON.parse(model.source).body
return
}
eventList.copySelectedDelegates()
}
}
2020-05-20 05:19:06 -04:00
HMenuItem {
icon.name: "reply-to"
text: qsTr("Reply")
onTriggered: {
2020-05-20 06:17:14 -04:00
chat.replyToEventId = model.id
2020-05-20 05:19:06 -04:00
chat.replyToUserId = model.sender_id
chat.replyToDisplayName = model.sender_name
}
}
2020-03-26 17:31:57 -03:00
HMenuItemPopupSpawner {
readonly property var events: {
eventList.selectedCount ?
eventList.redactableCheckedEvents :
eventList.canRedact(currentModel) ?
[model] :
[]
}
2020-03-26 17:31:57 -03:00
icon.name: "remove-message"
text: qsTr("Remove")
enabled: properties.eventSenderAndIds.length
2020-03-26 17:31:57 -03:00
popup: "Popups/RedactPopup.qml"
2020-03-26 17:31:57 -03:00
properties: ({
preferUserId: chat.userId,
2020-03-26 17:31:57 -03:00
roomId: chat.roomId,
eventSenderAndIds: events.map(ev => [ev.sender_id, ev.id]),
onlyOwnMessageWarning:
! chat.roomInfo.can_redact_all &&
2020-04-02 10:19:43 -04:00
events.length < eventList.selectedCount
2020-03-26 17:31:57 -03:00
})
}
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,
})
}
}
2019-03-21 23:28:14 -04:00
}