2023-02-22 01:48:49 +03:00
// SPDX-FileCopyrightText: Nheko Contributors
2021-03-07 07:57:56 +03:00
//
2021-03-05 02:35:15 +03:00
// SPDX-License-Identifier: GPL-3.0-or-later
2021-06-10 02:01:49 +03:00
import "./ui"
2022-03-06 16:46:52 +03:00
import "./dialogs"
2021-07-12 01:24:33 +03:00
import QtQuick 2.15
import QtQuick . Controls 2.15
2021-01-12 17:03:39 +03:00
import QtQuick . Layouts 1.2
2021-08-04 03:27:50 +03:00
import QtQuick . Window 2.13
2020-10-26 16:57:54 +03:00
import im . nheko 1.0
2022-02-19 04:49:58 +03:00
Item {
id: chatRoot
property int availableWidth: width
2023-06-02 02:45:24 +03:00
property int padding: Nheko . paddingMedium
2022-10-06 22:59:59 +03:00
property string searchString: ""
2023-06-22 20:54:17 +03:00
property Room roommodel: room
2022-10-06 22:59:59 +03:00
2023-01-23 15:15:43 +03:00
// HACK: https://bugreports.qt.io/browse/QTBUG-83972, qtwayland cannot auto hide menu
Connections {
function onHideMenu ( ) {
2023-09-20 03:17:20 +03:00
messageContextMenuC . close ( ) ;
2023-10-10 01:00:17 +03:00
replyContextMenuC . close ( ) ;
2023-01-23 15:15:43 +03:00
}
2023-06-02 02:45:24 +03:00
2023-01-23 15:15:43 +03:00
target: MainWindow
}
2024-04-05 03:52:45 +03:00
Connections {
function onScrollToIndex ( index ) {
chat . positionViewAtIndex ( index , ListView . Center ) ;
chat . updateLastScroll ( ) ;
}
target: room
}
2022-02-19 04:49:58 +03:00
ScrollBar {
id: scrollbar
2023-06-02 02:45:24 +03:00
2022-02-19 04:49:58 +03:00
anchors.bottom: parent . bottom
2023-06-02 02:45:24 +03:00
anchors.right: parent . right
anchors.top: parent . top
parent: chat . parent
2022-02-19 04:49:58 +03:00
}
2022-02-20 12:09:22 +03:00
ListView {
id: chat
2021-03-05 00:59:10 +03:00
2023-06-02 02:45:24 +03:00
property int delegateMaxWidth: ( ( Settings . timelineMaxWidth > 100 && Settings . timelineMaxWidth < chatRoot . availableWidth ) ? Settings.timelineMaxWidth : chatRoot . availableWidth ) - chatRoot . padding * 2 - ( scrollbar . interactive ? scrollbar.width : 0 )
2022-12-21 00:34:55 +03:00
readonly property alias filteringInProgress: filteredTimeline . filteringInProgress
2023-06-02 02:45:24 +03:00
ScrollBar.vertical: scrollbar
anchors.fill: parent
anchors.rightMargin: scrollbar . interactive ? scrollbar.width : 0
2022-02-20 12:09:22 +03:00
// reuseItems still has a few bugs, see https://bugreports.qt.io/browse/QTBUG-95105 https://bugreports.qt.io/browse/QTBUG-95107
//onModelChanged: if (room) room.sendReset()
2024-01-08 05:47:36 +03:00
//reuseItems: true
2022-02-20 12:09:22 +03:00
boundsBehavior: Flickable . StopAtBounds
2023-10-08 21:14:13 +03:00
displayMarginBeginning: height / 4
displayMarginEnd: height / 4
2023-06-02 02:45:24 +03:00
model: ( filteredTimeline . filterByThread || filteredTimeline . filterByContent ) ? filteredTimeline : room
2022-02-20 12:09:22 +03:00
//pixelAligned: true
spacing: 2
verticalLayoutDirection: ListView . BottomToTop
2023-06-02 02:45:24 +03:00
2023-10-23 02:56:53 +03:00
property int lastScrollPos: 0
// Fixup the scroll position when the height changes. Without this, the view is kept around the center of the currently visible content, while we usually want to stick to the bottom.
2024-04-05 03:52:45 +03:00
function updateLastScroll ( ) {
lastScrollPos = ( contentY + height ) ;
}
onMovementEnded: updateLastScroll ( )
onModelChanged: updateLastScroll ( )
2023-10-23 02:56:53 +03:00
onHeightChanged: contentY = ( lastScrollPos - height )
2023-10-09 04:49:45 +03:00
Component {
id: defaultMessageStyle
TimelineDefaultMessageStyle {
messageActions: messageActionsC
messageContextMenu: messageContextMenuC
2023-10-10 01:00:17 +03:00
replyContextMenu: replyContextMenuC
2023-10-09 04:49:45 +03:00
scrolledToThis: eventId === room . scrollTarget && ( y + height > chat . y + chat . contentY && y < chat . y + chat . height + chat . contentY )
2024-05-27 22:57:26 +03:00
data: [
Connections {
function onMovementEnded ( ) {
if ( y + height + 2 * chat . spacing > chat . contentY + chat . height && y < chat . contentY + chat . height ) {
room . currentIndex = index ;
}
}
target: chat
}
]
2023-10-09 04:49:45 +03:00
}
2023-06-02 02:45:24 +03:00
}
2023-10-09 22:28:39 +03:00
Component {
id: bubbleMessageStyle
TimelineBubbleMessageStyle {
messageActions: messageActionsC
messageContextMenu: messageContextMenuC
2023-10-10 01:00:17 +03:00
replyContextMenu: replyContextMenuC
2023-10-09 22:28:39 +03:00
scrolledToThis: eventId === room . scrollTarget && ( y + height > chat . y + chat . contentY && y < chat . y + chat . height + chat . contentY )
2024-05-27 22:57:26 +03:00
data: [
Connections {
function onMovementEnded ( ) {
if ( y + height + 2 * chat . spacing > chat . contentY + chat . height && y < chat . contentY + chat . height ) {
room . currentIndex = index ;
}
}
target: chat
}
]
2023-10-09 22:28:39 +03:00
}
}
2023-10-09 04:49:45 +03:00
2023-10-09 22:28:39 +03:00
delegate: Settings . bubbles ? bubbleMessageStyle : defaultMessageStyle
2023-06-02 02:45:24 +03:00
footer: Item {
2024-03-02 03:04:24 +03:00
width: chat . delegateMaxWidth
2023-06-02 02:45:24 +03:00
// hacky, but works
height: loadingSpinner . height + 2 * Nheko . paddingLarge
visible: ( room && room . paginationInProgress ) || chat . filteringInProgress
Spinner {
id: loadingSpinner
anchors.centerIn: parent
anchors.margins: Nheko . paddingLarge
foreground: palette . mid
running: ( room && room . paginationInProgress ) || chat . filteringInProgress
z: 3
}
}
Window.onActiveChanged: readTimer . running = Window . active
2022-02-20 12:09:22 +03:00
onCountChanged: {
// Mark timeline as read
2023-06-02 02:45:24 +03:00
if ( atYEnd && room )
model . currentIndex = 0 ;
2022-02-20 12:09:22 +03:00
}
2021-03-05 00:59:10 +03:00
2023-06-02 02:45:24 +03:00
TimelineFilter {
id: filteredTimeline
2021-12-13 02:43:05 +03:00
2023-06-02 02:45:24 +03:00
filterByContent: chatRoot . searchString
filterByThread: room ? room.thread : ""
source: room
}
2022-03-08 01:16:18 +03:00
Control {
2023-09-20 03:17:20 +03:00
id: messageActionsC
2021-12-13 02:43:05 +03:00
2022-02-20 12:09:22 +03:00
property Item attached: null
// use comma to update on scroll
2023-06-02 02:45:24 +03:00
property alias model: row . model
2021-12-13 02:43:05 +03:00
2022-03-22 21:59:22 +03:00
hoverEnabled: true
2023-06-02 02:45:24 +03:00
padding: Nheko . paddingSmall
2022-03-08 01:16:18 +03:00
visible: Settings . buttonsInTimeline && ! ! attached && ( attached . hovered || hovered )
2022-02-20 12:09:22 +03:00
z: 10
2023-09-08 02:41:57 +03:00
parent: chat . contentItem
anchors.bottom: attached ? . top
anchors.right: attached ? . right
2022-03-08 01:16:18 +03:00
background: Rectangle {
2023-06-02 02:29:05 +03:00
border.color: palette . buttonText
2022-03-08 01:19:56 +03:00
border.width: 1
2023-06-02 02:45:24 +03:00
color: palette . window
2022-03-08 01:19:56 +03:00
radius: padding
2022-02-20 12:09:22 +03:00
}
2022-03-09 21:39:25 +03:00
contentItem: RowLayout {
2022-02-20 12:09:22 +03:00
id: row
2022-02-19 04:49:58 +03:00
2022-02-20 12:09:22 +03:00
property var model
2022-02-19 04:49:58 +03:00
2023-09-20 03:17:20 +03:00
spacing: messageActionsC . padding
2022-02-19 04:49:58 +03:00
2022-02-20 12:09:22 +03:00
Repeater {
model: Settings . recentReactions
2023-05-31 04:59:07 +03:00
visible: room ? room . permissions . canSend ( MtxEvent . Reaction ) : false
delegate: AbstractButton {
id: button
2022-02-19 04:49:58 +03:00
2023-06-02 02:29:05 +03:00
property color buttonTextColor: palette . buttonText
2023-06-02 02:45:24 +03:00
property color highlightColor: palette . highlight
required property string modelData
2023-05-31 04:59:07 +03:00
property bool showImage: modelData . startsWith ( "mxc://" )
//Layout.preferredHeight: fontMetrics.height
Layout.alignment: Qt . AlignBottom
focusPolicy: Qt . NoFocus
height: showImage ? 16 : buttonText . implicitHeight
implicitHeight: showImage ? 16 : buttonText . implicitHeight
2023-06-02 02:45:24 +03:00
implicitWidth: showImage ? 16 : buttonText . implicitWidth
width: showImage ? 16 : buttonText . implicitWidth
onClicked: {
room . input . reaction ( row . model . eventId , modelData ) ;
TimelineManager . focusMessageInput ( ) ;
}
2023-05-31 04:59:07 +03:00
Label {
id: buttonText
anchors.centerIn: parent
color: button . hovered ? button.highlightColor : button . buttonTextColor
font.family: Settings . emojiFont
horizontalAlignment: Text . AlignHCenter
2023-06-02 02:45:24 +03:00
padding: 0
text: button . modelData
verticalAlignment: Text . AlignVCenter
visible: ! button . showImage
2023-05-31 04:59:07 +03:00
}
Image {
// Workaround, can't get icon.source working for now...
anchors.fill: parent
2023-06-02 02:45:24 +03:00
fillMode: Image . PreserveAspectFit
2023-05-31 04:59:07 +03:00
source: button . showImage ? ( button . modelData . replace ( "mxc://" , "image://MxcImage/" ) + "?scale" ) : ""
sourceSize.height: button . height
sourceSize.width: button . width
}
2023-06-19 02:38:40 +03:00
NhekoCursorShape {
2023-05-31 04:59:07 +03:00
anchors.fill: parent
cursorShape: Qt . PointingHandCursor
}
Ripple {
color: Qt . rgba ( buttonTextColor . r , buttonTextColor . g , buttonTextColor . b , 0.5 )
}
2021-03-05 00:59:10 +03:00
}
2022-02-20 12:09:22 +03:00
}
ImageButton {
2023-06-02 02:45:24 +03:00
ToolTip.delay: Nheko . tooltipDelay
ToolTip.text: qsTr ( "Edit" )
ToolTip.visible: hovered
2023-06-02 02:29:05 +03:00
buttonTextColor: palette . buttonText
2022-02-20 12:09:22 +03:00
hoverEnabled: true
image: ":/icons/icons/ui/edit.svg"
2023-06-02 02:45:24 +03:00
visible: ! ! row . model && row . model . isEditable
2023-10-26 17:43:09 +03:00
Layout.preferredWidth: 16
2023-06-02 02:45:24 +03:00
2022-02-20 12:09:22 +03:00
onClicked: {
2023-06-02 02:45:24 +03:00
if ( row . model . isEditable )
room . edit = row . model . eventId ;
2022-02-19 04:49:58 +03:00
}
2022-02-20 12:09:22 +03:00
}
ImageButton {
id: reactButton
ToolTip.delay: Nheko . tooltipDelay
ToolTip.text: qsTr ( "React" )
2023-06-02 02:45:24 +03:00
ToolTip.visible: hovered
hoverEnabled: true
image: ":/icons/icons/ui/smile-add.svg"
visible: room ? room . permissions . canSend ( MtxEvent . Reaction ) : false
2023-10-26 17:43:09 +03:00
Layout.preferredWidth: 16
2022-02-19 04:49:58 +03:00
2023-06-02 02:45:24 +03:00
onClicked: emojiPopup . visible ? emojiPopup . close ( ) : emojiPopup . show ( reactButton , room . roomId , function ( plaintext , markdown ) {
var event_id = row . model ? row.model.eventId : "" ;
room . input . reaction ( event_id , plaintext ) ;
TimelineManager . focusMessageInput ( ) ;
} )
}
2022-09-30 04:27:05 +03:00
ImageButton {
ToolTip.delay: Nheko . tooltipDelay
2022-10-01 02:53:12 +03:00
ToolTip.text: ( row . model && row . model . threadId ) ? qsTr ( "Reply in thread" ) : qsTr ( "New thread" )
2023-06-02 02:45:24 +03:00
ToolTip.visible: hovered
hoverEnabled: true
image: ( row . model && row . model . threadId ) ? ":/icons/icons/ui/thread.svg" : ":/icons/icons/ui/new-thread.svg"
visible: room ? room . permissions . canSend ( MtxEvent . TextMessage ) : false
2023-10-26 17:43:09 +03:00
Layout.preferredWidth: 16
2023-06-02 02:45:24 +03:00
2022-10-01 02:53:12 +03:00
onClicked: room . thread = ( row . model . threadId || row . model . eventId )
2022-09-30 04:27:05 +03:00
}
2022-02-20 12:09:22 +03:00
ImageButton {
ToolTip.delay: Nheko . tooltipDelay
ToolTip.text: qsTr ( "Reply" )
2023-06-02 02:45:24 +03:00
ToolTip.visible: hovered
hoverEnabled: true
image: ":/icons/icons/ui/reply.svg"
visible: room ? room . permissions . canSend ( MtxEvent . TextMessage ) : false
2023-10-26 17:43:09 +03:00
Layout.preferredWidth: 16
2023-06-02 02:45:24 +03:00
2022-10-01 02:53:12 +03:00
onClicked: room . reply = row . model . eventId
2022-02-20 12:09:22 +03:00
}
2023-05-30 15:21:44 +03:00
ImageButton {
2023-06-02 02:45:24 +03:00
ToolTip.delay: Nheko . tooltipDelay
ToolTip.text: qsTr ( "Go to message" )
ToolTip.visible: hovered
2023-06-02 02:29:05 +03:00
buttonTextColor: palette . buttonText
2023-05-30 15:21:44 +03:00
hoverEnabled: true
image: ":/icons/icons/ui/go-to.svg"
2023-06-02 02:45:24 +03:00
visible: ! ! row . model && filteredTimeline . filterByContent
2023-10-26 17:43:09 +03:00
Layout.preferredWidth: 16
2023-06-02 02:45:24 +03:00
2023-05-30 15:21:44 +03:00
onClicked: {
topBar . searchString = "" ;
room . showEvent ( row . model . eventId ) ;
}
}
2022-02-20 12:09:22 +03:00
ImageButton {
id: optionsButton
2021-03-05 00:59:10 +03:00
2022-02-20 12:09:22 +03:00
ToolTip.delay: Nheko . tooltipDelay
ToolTip.text: qsTr ( "Options" )
2023-06-02 02:45:24 +03:00
ToolTip.visible: hovered
hoverEnabled: true
image: ":/icons/icons/ui/options.svg"
2023-10-26 17:43:09 +03:00
Layout.preferredWidth: 16
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onClicked: messageContextMenuC . show ( row . model . eventId , row . model . threadId , row . model . type , row . model . isSender , row . model . isEncrypted , row . model . isEditable , "" , row . model . body , optionsButton )
2021-03-05 00:59:10 +03:00
}
}
2022-02-20 12:09:22 +03:00
}
Shortcut {
2024-01-10 04:53:16 +03:00
sequences: [ StandardKey . MoveToPreviousPage ]
2023-06-02 02:45:24 +03:00
2022-02-20 12:09:22 +03:00
onActivated: {
2023-03-02 21:35:14 +03:00
chat . contentY = chat . contentY - chat . height * 0.9 ;
2022-02-20 12:09:22 +03:00
chat . returnToBounds ( ) ;
2021-02-14 01:52:45 +03:00
}
2022-02-20 12:09:22 +03:00
}
Shortcut {
2024-01-10 04:53:16 +03:00
sequences: [ StandardKey . MoveToNextPage ]
2023-06-02 02:45:24 +03:00
2022-02-20 12:09:22 +03:00
onActivated: {
2023-03-02 21:35:14 +03:00
chat . contentY = chat . contentY + chat . height * 0.9 ;
2022-02-20 12:09:22 +03:00
chat . returnToBounds ( ) ;
2021-02-14 01:52:45 +03:00
}
2022-02-20 12:09:22 +03:00
}
Shortcut {
2024-01-10 04:53:16 +03:00
sequences: [ StandardKey . Cancel ]
2023-06-02 02:45:24 +03:00
2022-02-20 12:09:22 +03:00
onActivated: {
2023-06-02 02:45:24 +03:00
if ( room . input . uploads . length > 0 )
2022-06-17 23:08:29 +03:00
room . input . declineUploads ( ) ;
2023-06-02 02:45:24 +03:00
else if ( room . reply )
2022-10-01 02:53:12 +03:00
room . reply = undefined ;
else if ( room . edit )
room . edit = undefined ;
2022-09-30 04:27:05 +03:00
else
2023-06-02 02:45:24 +03:00
room . thread = undefined ;
2022-08-19 10:41:22 +03:00
TimelineManager . focusMessageInput ( ) ;
2021-02-14 01:52:45 +03:00
}
2022-02-20 12:09:22 +03:00
}
2020-10-26 16:57:54 +03:00
2022-10-06 02:39:30 +03:00
// These shortcuts use the room timeline because switching to threads and out is annoying otherwise.
// Better solution welcome.
2022-02-20 12:09:22 +03:00
Shortcut {
sequence: "Alt+Up"
2023-06-02 02:45:24 +03:00
2022-10-06 02:39:30 +03:00
onActivated: room . reply = room . indexToId ( room . reply ? room . idToIndex ( room . reply ) + 1 : 0 )
2022-02-20 12:09:22 +03:00
}
Shortcut {
sequence: "Alt+Down"
2023-06-02 02:45:24 +03:00
2022-02-20 12:09:22 +03:00
onActivated: {
2022-10-06 02:39:30 +03:00
var idx = room . reply ? room . idToIndex ( room . reply ) - 1 : - 1 ;
room . reply = idx >= 0 ? room . indexToId ( idx ) : null ;
2021-02-14 01:52:45 +03:00
}
2022-02-20 12:09:22 +03:00
}
Shortcut {
sequence: "Alt+F"
2023-06-02 02:45:24 +03:00
2022-02-20 12:09:22 +03:00
onActivated: {
2022-10-01 02:53:12 +03:00
if ( room . reply ) {
2022-02-20 12:09:22 +03:00
var forwardMess = forwardCompleterComponent . createObject ( timelineRoot ) ;
2022-10-01 02:53:12 +03:00
forwardMess . setMessageEventId ( room . reply ) ;
2022-02-20 12:09:22 +03:00
forwardMess . open ( ) ;
2022-10-01 02:53:12 +03:00
room . reply = null ;
2022-02-21 07:01:01 +03:00
timelineRoot . destroyOnClose ( forwardMess ) ;
2022-02-19 04:49:58 +03:00
}
2021-02-14 01:52:45 +03:00
}
2022-02-20 12:09:22 +03:00
}
Shortcut {
sequence: "Ctrl+E"
2023-06-02 02:45:24 +03:00
2022-02-20 12:09:22 +03:00
onActivated: {
2022-10-01 02:53:12 +03:00
room . edit = room . reply ;
2021-07-27 23:35:38 +03:00
}
2022-02-20 12:09:22 +03:00
}
Timer {
id: readTimer
2021-02-13 03:41:09 +03:00
2023-06-02 02:45:24 +03:00
interval: 1000
2022-02-20 12:09:22 +03:00
// force current read index to update
onTriggered: {
2022-10-01 02:53:12 +03:00
if ( room )
2023-06-02 02:45:24 +03:00
room . setCurrentIndex ( room . currentIndex ) ;
2021-07-27 23:35:38 +03:00
}
2022-02-20 12:09:22 +03:00
}
2020-10-26 16:57:54 +03:00
}
2024-10-09 00:04:41 +03:00
Menu {
2023-09-20 03:17:20 +03:00
id: messageContextMenuC
2021-05-28 23:14:59 +03:00
property string eventId
property int eventType
property bool isEditable
2023-06-02 02:45:24 +03:00
property bool isEncrypted
2021-05-28 23:14:59 +03:00
property bool isSender
2023-06-02 02:45:24 +03:00
property string link
property string text
property string threadId
2021-05-28 23:14:59 +03:00
2022-09-30 04:27:05 +03:00
function show ( eventId_ , threadId_ , eventType_ , isSender_ , isEncrypted_ , isEditable_ , link_ , text_ , showAt_ ) {
2021-05-28 23:14:59 +03:00
eventId = eventId_ ;
2022-09-30 04:27:05 +03:00
threadId = threadId_ ;
2021-05-28 23:14:59 +03:00
eventType = eventType_ ;
isEncrypted = isEncrypted_ ;
isEditable = isEditable_ ;
isSender = isSender_ ;
if ( text_ )
2023-06-02 02:45:24 +03:00
text = text_ ;
2021-05-28 23:14:59 +03:00
else
2023-06-02 02:45:24 +03:00
text = "" ;
2021-05-28 23:14:59 +03:00
if ( link_ )
2023-06-02 02:45:24 +03:00
link = link_ ;
2021-05-28 23:14:59 +03:00
else
2023-06-02 02:45:24 +03:00
link = "" ;
2021-05-28 23:14:59 +03:00
if ( showAt_ )
2024-10-09 00:35:13 +03:00
popup ( showAt_ ) ;
2021-05-28 23:14:59 +03:00
else
2024-10-09 00:35:13 +03:00
popup ( ) ;
2021-05-28 23:14:59 +03:00
}
2022-03-06 16:46:52 +03:00
Component {
id: removeReason
2023-06-02 02:45:24 +03:00
2022-03-06 16:46:52 +03:00
InputDialog {
id: removeReasonDialog
property string eventId
prompt: qsTr ( "Enter reason for removal or hit enter for no reason:" )
2023-06-02 02:45:24 +03:00
title: qsTr ( "Reason for removal" )
onAccepted: function ( text ) {
2022-03-06 16:46:52 +03:00
room . redactEvent ( eventId , text ) ;
}
}
}
2023-07-25 02:07:45 +03:00
Component {
id: reportDialog
ReportMessage { }
}
2024-10-09 00:04:41 +03:00
MenuItem {
2023-06-02 02:45:24 +03:00
enabled: visible
text: qsTr ( "Go to &message" )
visible: filteredTimeline . filterByContent
onTriggered: function ( ) {
2023-05-29 18:10:00 +03:00
topBar . searchString = "" ;
2023-09-20 03:17:20 +03:00
room . showEvent ( messageContextMenuC . eventId ) ;
2023-05-29 18:10:00 +03:00
}
2023-06-02 02:45:24 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-05-28 23:14:59 +03:00
enabled: visible
2021-06-12 16:05:07 +03:00
text: qsTr ( "&Copy" )
2023-09-20 03:17:20 +03:00
visible: messageContextMenuC . text
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onTriggered: Clipboard . text = messageContextMenuC . text
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-05-28 23:14:59 +03:00
enabled: visible
2021-06-12 16:05:07 +03:00
text: qsTr ( "Copy &link location" )
2023-09-20 03:17:20 +03:00
visible: messageContextMenuC . link
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onTriggered: Clipboard . text = messageContextMenuC . link
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-05-28 23:14:59 +03:00
id: reactionOption
2021-06-12 16:05:07 +03:00
text: qsTr ( "Re&act" )
2023-06-02 02:45:24 +03:00
visible: room ? room . permissions . canSend ( MtxEvent . Reaction ) : false
2021-05-28 23:14:59 +03:00
2023-06-02 02:45:24 +03:00
onTriggered: emojiPopup . visible ? emojiPopup . close ( ) : emojiPopup . show ( null , room . roomId , function ( plaintext , markdown ) {
2023-09-20 03:17:20 +03:00
room . input . reaction ( messageContextMenuC . eventId , plaintext ) ;
2023-06-02 02:45:24 +03:00
TimelineManager . focusMessageInput ( ) ;
} )
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-06-12 16:05:07 +03:00
text: qsTr ( "Repl&y" )
2023-06-02 02:45:24 +03:00
visible: room ? room . permissions . canSend ( MtxEvent . TextMessage ) : false
2023-09-20 03:17:20 +03:00
onTriggered: room . reply = ( messageContextMenuC . eventId )
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-05-28 23:14:59 +03:00
enabled: visible
2021-06-12 16:05:07 +03:00
text: qsTr ( "&Edit" )
2023-09-20 03:17:20 +03:00
visible: messageContextMenuC . isEditable && ( room ? room . permissions . canSend ( MtxEvent . TextMessage ) : false )
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onTriggered: room . edit = ( messageContextMenuC . eventId )
2022-09-30 04:27:05 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2022-09-30 04:27:05 +03:00
enabled: visible
text: qsTr ( "&Thread" )
2023-06-02 02:45:24 +03:00
visible: ( room ? room . permissions . canSend ( MtxEvent . TextMessage ) : false )
2023-09-20 03:17:20 +03:00
onTriggered: room . thread = ( messageContextMenuC . threadId || messageContextMenuC . eventId )
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-12-11 08:10:41 +03:00
enabled: visible
2023-09-20 03:17:20 +03:00
text: visible && room . pinnedMessages . includes ( messageContextMenuC . eventId ) ? qsTr ( "Un&pin" ) : qsTr ( "&Pin" )
2023-06-02 02:45:24 +03:00
visible: ( room ? room . permissions . canChange ( MtxEvent . PinnedEvents ) : false )
2023-09-20 03:17:20 +03:00
onTriggered: visible && room . pinnedMessages . includes ( messageContextMenuC . eventId ) ? room . unpin ( messageContextMenuC . eventId ) : room . pin ( messageContextMenuC . eventId )
2021-12-11 08:10:41 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2022-09-30 04:27:05 +03:00
text: qsTr ( "&Read receipts" )
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onTriggered: room . showReadReceipts ( messageContextMenuC . eventId )
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-06-12 16:05:07 +03:00
text: qsTr ( "&Forward" )
2023-09-20 03:17:20 +03:00
visible: messageContextMenuC . eventType == MtxEvent . ImageMessage || messageContextMenuC . eventType == MtxEvent . VideoMessage || messageContextMenuC . eventType == MtxEvent . AudioMessage || messageContextMenuC . eventType == MtxEvent . FileMessage || messageContextMenuC . eventType == MtxEvent . Sticker || messageContextMenuC . eventType == MtxEvent . TextMessage || messageContextMenuC . eventType == MtxEvent . LocationMessage || messageContextMenuC . eventType == MtxEvent . EmoteMessage || messageContextMenuC . eventType == MtxEvent . NoticeMessage
2023-06-02 02:45:24 +03:00
2021-05-28 23:14:59 +03:00
onTriggered: {
var forwardMess = forwardCompleterComponent . createObject ( timelineRoot ) ;
2023-09-20 03:17:20 +03:00
forwardMess . setMessageEventId ( messageContextMenuC . eventId ) ;
2021-05-28 23:14:59 +03:00
forwardMess . open ( ) ;
2022-02-21 07:01:01 +03:00
timelineRoot . destroyOnClose ( forwardMess ) ;
2021-05-28 23:14:59 +03:00
}
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-06-12 16:05:07 +03:00
text: qsTr ( "&Mark as read" )
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-05-28 23:14:59 +03:00
text: qsTr ( "View raw message" )
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onTriggered: room . viewRawMessage ( messageContextMenuC . eventId )
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-05-28 23:14:59 +03:00
enabled: visible
text: qsTr ( "View decrypted raw message" )
2023-06-02 02:45:24 +03:00
// TODO(Nico): Fix this still being iterated over, when using keyboard to select options
2023-09-20 03:17:20 +03:00
visible: messageContextMenuC . isEncrypted
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onTriggered: room . viewDecryptedRawMessage ( messageContextMenuC . eventId )
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-06-12 16:05:07 +03:00
text: qsTr ( "Remo&ve message" )
2023-09-20 03:17:20 +03:00
visible: ( room ? room . permissions . canRedact ( ) : false ) || messageContextMenuC . isSender
2023-06-02 02:45:24 +03:00
onTriggered: function ( ) {
2022-03-06 16:46:52 +03:00
var dialog = removeReason . createObject ( timelineRoot ) ;
2023-09-20 03:17:20 +03:00
dialog . eventId = messageContextMenuC . eventId ;
2022-03-06 16:46:52 +03:00
dialog . show ( ) ;
dialog . forceActiveFocus ( ) ;
timelineRoot . destroyOnClose ( dialog ) ;
}
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2023-07-25 02:07:45 +03:00
text: qsTr ( "Report message" )
enabled: visible
onTriggered: function ( ) {
2024-01-29 03:02:55 +03:00
var dialog = reportDialog . createObject ( timelineRoot , { "eventId" : messageContextMenuC . eventId } ) ;
2023-07-25 02:07:45 +03:00
dialog . show ( ) ;
dialog . forceActiveFocus ( ) ;
timelineRoot . destroyOnClose ( dialog ) ;
}
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-05-28 23:14:59 +03:00
enabled: visible
2021-06-12 16:05:07 +03:00
text: qsTr ( "&Save as" )
2023-09-20 03:17:20 +03:00
visible: messageContextMenuC . eventType == MtxEvent . ImageMessage || messageContextMenuC . eventType == MtxEvent . VideoMessage || messageContextMenuC . eventType == MtxEvent . AudioMessage || messageContextMenuC . eventType == MtxEvent . FileMessage || messageContextMenuC . eventType == MtxEvent . Sticker
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onTriggered: room . saveMedia ( messageContextMenuC . eventId )
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-05-28 23:14:59 +03:00
enabled: visible
2021-06-12 16:05:07 +03:00
text: qsTr ( "&Open in external program" )
2023-09-20 03:17:20 +03:00
visible: messageContextMenuC . eventType == MtxEvent . ImageMessage || messageContextMenuC . eventType == MtxEvent . VideoMessage || messageContextMenuC . eventType == MtxEvent . AudioMessage || messageContextMenuC . eventType == MtxEvent . FileMessage || messageContextMenuC . eventType == MtxEvent . Sticker
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onTriggered: room . openMedia ( messageContextMenuC . eventId )
2021-05-28 23:14:59 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-05-28 23:14:59 +03:00
enabled: visible
2021-06-12 16:05:07 +03:00
text: qsTr ( "Copy link to eve&nt" )
2023-09-20 03:17:20 +03:00
visible: messageContextMenuC . eventId
2023-06-02 02:45:24 +03:00
2023-09-20 03:17:20 +03:00
onTriggered: room . copyLinkToEvent ( messageContextMenuC . eventId )
2021-05-28 23:14:59 +03:00
}
}
Component {
id: forwardCompleterComponent
ForwardCompleter {
}
}
2024-10-09 00:04:41 +03:00
Menu {
2023-10-10 01:00:17 +03:00
id: replyContextMenuC
2021-08-25 17:10:55 +03:00
2022-03-24 03:35:42 +03:00
property string eventId
2023-06-02 02:45:24 +03:00
property string link
property string text
2021-08-25 17:10:55 +03:00
2022-03-24 03:35:42 +03:00
function show ( text_ , link_ , eventId_ ) {
2021-08-25 17:10:55 +03:00
text = text_ ;
link = link_ ;
2022-03-24 03:35:42 +03:00
eventId = eventId_ ;
2021-08-25 17:10:55 +03:00
open ( ) ;
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-08-25 17:10:55 +03:00
enabled: visible
text: qsTr ( "&Copy" )
2023-10-10 01:00:17 +03:00
visible: replyContextMenuC . text
2023-06-02 02:45:24 +03:00
2023-10-10 01:00:17 +03:00
onTriggered: Clipboard . text = replyContextMenuC . text
2021-08-25 17:10:55 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-08-25 17:10:55 +03:00
enabled: visible
text: qsTr ( "Copy &link location" )
2023-10-10 01:00:17 +03:00
visible: replyContextMenuC . link
2023-06-02 02:45:24 +03:00
2023-10-10 01:00:17 +03:00
onTriggered: Clipboard . text = replyContextMenuC . link
2021-08-25 17:10:55 +03:00
}
2024-10-09 00:04:41 +03:00
MenuItem {
2021-08-25 17:10:55 +03:00
enabled: visible
2021-09-14 00:17:03 +03:00
text: qsTr ( "&Go to quoted message" )
2023-06-02 02:45:24 +03:00
visible: true
2023-10-10 01:00:17 +03:00
onTriggered: room . showEvent ( replyContextMenuC . eventId )
2021-08-25 17:10:55 +03:00
}
}
2022-03-30 00:11:25 +03:00
RoundButton {
id: toEndButton
2023-06-02 02:45:24 +03:00
2022-03-30 00:11:25 +03:00
property int fullWidth: 40
2023-06-02 02:45:24 +03:00
2022-03-30 00:11:25 +03:00
flat: true
2023-06-02 02:45:24 +03:00
height: width
2022-03-30 02:23:23 +03:00
hoverEnabled: true
2023-06-02 02:45:24 +03:00
radius: width / 2
width: 0
2022-03-30 02:23:23 +03:00
background: Rectangle {
2023-06-02 02:29:05 +03:00
border.color: toEndButton . hovered ? palette.highlight : palette . buttonText
2022-03-30 02:23:23 +03:00
border.width: 1
2023-06-02 02:45:24 +03:00
color: toEndButton . down ? palette.highlight : palette . button
opacity: enabled ? 1 : 0.3
2022-03-30 02:23:23 +03:00
radius: toEndButton . radius
}
2022-03-30 20:19:39 +03:00
states: [
State {
name: ""
2023-06-02 02:45:24 +03:00
PropertyChanges {
2023-10-26 17:43:09 +03:00
toEndButton.width: 0
2023-06-02 02:45:24 +03:00
}
2022-03-30 20:19:39 +03:00
} ,
State {
name: "shown"
when: ! chat . atYEnd
2023-06-02 02:45:24 +03:00
PropertyChanges {
2023-10-26 17:43:09 +03:00
toEndButton.width: toEndButton . fullWidth
2023-06-02 02:45:24 +03:00
}
2022-03-30 20:19:39 +03:00
}
]
transitions: Transition {
from: ""
reversible: true
2023-06-02 02:45:24 +03:00
to: "shown"
2024-06-24 04:24:39 +03:00
enabled: ! Settings . reducedMotion
2022-03-30 00:11:25 +03:00
2022-03-30 20:19:39 +03:00
SequentialAnimation {
2023-06-02 02:45:24 +03:00
PauseAnimation {
duration: 500
}
2022-03-30 00:11:25 +03:00
PropertyAnimation {
duration: 200
2023-06-02 02:45:24 +03:00
easing.type: Easing . InOutQuad
properties: "width"
target: toEndButton
2022-03-30 00:11:25 +03:00
}
}
2022-03-30 20:19:39 +03:00
}
2023-06-02 02:45:24 +03:00
onClicked: function ( ) {
chat . positionViewAtBeginning ( ) ;
TimelineManager . focusMessageInput ( ) ;
2024-04-05 03:52:45 +03:00
chat . updateLastScroll ( ) ;
2023-06-02 02:45:24 +03:00
}
anchors {
bottom: parent . bottom
bottomMargin: Nheko . paddingMedium + ( fullWidth - width ) / 2
right: scrollbar . left
rightMargin: Nheko . paddingMedium + ( fullWidth - width ) / 2
}
Image {
anchors.fill: parent
anchors.margins: Nheko . paddingMedium
fillMode: Image . PreserveAspectFit
source: "image://colorimage/:/icons/icons/ui/download.svg?" + ( toEndButton . down ? palette.highlightedText : palette . buttonText )
}
2022-03-30 00:11:25 +03:00
}
2020-10-26 16:57:54 +03:00
}