// SPDX-FileCopyrightText: 2020 Black Hat // SPDX-License-Identifier: GPL-3.0-only pragma ComponentBehavior: Bound import QtQuick import QtQuick.Controls as QQC2 import QtQuick.Layouts import org.kde.kirigami as Kirigami import org.kde.kirigamiaddons.components as KirigamiComponents import org.kde.neochat import org.kde.neochat.libneochat as LibNeoChat /** * @brief The base delegate for all messages in the timeline. * * This supports a message bubble plus sender avatar for each message * and read markers. A date section can be show for when the message is on a different * day to the previous one. * * The component is designed for all messages, positioning them in the timeline with * variable padding depending on the window width. Local user messages are highlighted * and can also be aligned to the right if configured. * * This component also supports a compact mode where the padding is adjusted, the * background is hidden and the delegate spans the full width of the timeline. */ MessageDelegateBase { id: root /** * @brief The NeoChatRoom the delegate is being displayed in. */ required property NeoChatRoom room /** * @brief The index of the delegate in the model. */ required property var index /** * @brief The matrix ID of the message event. */ required property string eventId /** * @brief The model to visualise the content of the message. */ required property MessageContentModel contentModel /** * @brief The timestamp of the event as a neoChatDateTime. */ required property neoChatDateTime dateTime /** * @brief A model with the first 5 other user read markers for this message. */ required property var readMarkers /** * @brief The Matrix ID of the root message in the thread, if any. */ required property string threadRoot /** * @brief Whether the message in a poll. */ required property bool isPoll /** * @brief Whether this message has a local user mention. */ required property bool isHighlighted /** * @brief Whether an event is waiting to be accepted by the server. */ required property bool isPending /** * @brief Whether the event can be edited by the local user. */ required property bool isEditable /** * @brief Whether an encrypted message is sent in a verified session. */ required property bool verified /** * @brief Open the any message media externally. */ signal openExternally /** * @brief The main delegate content item to show in the bubble. */ property var bubbleContent /** * @brief Whether the bubble background is enabled. */ property bool cardBackground: true /** * @brief Whether the message should be highlighted. */ property bool showHighlight: root.isHighlighted || isTemporaryHighlighted /** * @brief Whether the message is selected. */ property bool selected: root.room.selectedMessageCount > 0 && room.isMessageSelected(eventId) /** * @brief Whether to show selection controls for this message. */ property bool showSelectionControl: false Message.room: root.room Message.timeline: root.ListView.view Message.contentModel: root.contentModel Message.index: root.index Message.maxContentWidth: maxContentWidth - bubble.leftPadding - bubble.rightPadding width: parent?.width leftPadding: NeoChatConfig.compactLayout ? Kirigami.Units.largeSpacing * 2 : Kirigami.Units.largeSpacing rightPadding: NeoChatConfig.compactLayout ? Kirigami.Units.largeSpacing * 2 : Kirigami.Units.largeSpacing enableAvatars: NeoChatConfig?.showAvatarInTimeline ?? false compactMode: NeoChatConfig?.compactLayout ?? false showLocalMessagesOnRight: NeoChatConfig.showLocalMessagesOnRight showSelection: root.showSelectionControl && room.selectedMessageCount > 0 contentItem: Bubble { id: bubble topPadding: NeoChatConfig.compactLayout ? 0 : Kirigami.Units.largeSpacing bottomPadding: NeoChatConfig.compactLayout ? 0 : Kirigami.Units.largeSpacing leftPadding: NeoChatConfig.compactLayout ? Kirigami.Units.mediumSpacing : Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing rightPadding: Kirigami.Units.largeSpacing + Kirigami.Units.smallSpacing author: root.author showAuthor: root.showAuthor isThreaded: root.isThreaded contentModel: root.contentModel showHighlight: root.showHighlight isPending: root.isPending onSelectedTextChanged: selectedText => { root.Message.selectedText = selectedText; } onHoveredLinkChanged: hoveredLink => { root.Message.hoveredLink = hoveredLink; } showBackground: root.cardBackground && !NeoChatConfig.compactLayout TapHandler { acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus acceptedButtons: Qt.RightButton onTapped: _private.showMessageMenu() } TapHandler { acceptedDevices: PointerDevice.TouchScreen acceptedButtons: Qt.LeftButton onLongPressed: _private.showMessageMenu() } } avatarComponent: KirigamiComponents.Avatar { id: avatar implicitWidth: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2 implicitHeight: width name: root.author.displayName source: root.author.avatarUrl color: root.author.color asynchronous: true QQC2.ToolTip.text: root.author.htmlSafeDisambiguatedName function openUserMenu(): void { const menu = Qt.createComponent("org.kde.neochat", "UserMenu").createObject(root, { window: QQC2.ApplicationWindow.window as Kirigami.ApplicationWindow, author: root.author, }) as UserMenu; console.info(Qt.createComponent("org.kde.neochat", "UserMenu").errorString()); menu.popup(root.QQC2.Overlay.overlay); } HoverHandler { cursorShape: Qt.PointingHandCursor } // tapping to open profile TapHandler { onTapped: RoomManager.resolveResource(root.author.uri) } // right-clicking/long-press for context menu TapHandler { acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad | PointerDevice.Stylus acceptedButtons: Qt.RightButton onTapped: avatar.openUserMenu() } TapHandler { acceptedDevices: PointerDevice.TouchScreen onTapped: avatar.openUserMenu() } } sectionComponent: Kirigami.ListSectionHeader { horizontalPadding: 0 text: root.dateTime.relativeDate } readMarkerComponent: AvatarFlow { model: root.readMarkers TapHandler { onTapped: { const dialog = Qt.createComponent("org.kde.neochat", "SeenByDialog").createObject(root, { model: root.readMarkers }) as SeenByDialog; dialog.open(); } } } compactBackgroundComponent: Rectangle { color: Kirigami.ColorUtils.tintWithAlpha(Kirigami.Theme.backgroundColor, Kirigami.Theme.highlightColor, 0.15) radius: Kirigami.Units.cornerRadius } quickActionComponent: QuickActions { room: root.room eventId: root.eventId author: root.author } selectionComponent: RowLayout { spacing: Kirigami.Units.smallSpacing implicitHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2 QQC2.CheckBox { checked: root.selected onClicked: root.room.toggleMessageSelection(root.eventId) } Kirigami.Separator { Layout.fillHeight: true } } QtObject { id: _private function showMessageMenu(): void { let event = root.ListView.view.model.findEvent(root.eventId); RoomManager.viewEventMenu(root.QQC2.Overlay.overlay, event, root.room, root.Message.selectedText, root.Message.hoveredLink); } } }