Refactor delegates
This commit is contained in:
@@ -15,10 +15,15 @@ import NeoChat.Component 1.0
|
|||||||
import NeoChat.Dialog 1.0
|
import NeoChat.Dialog 1.0
|
||||||
import NeoChat.Menu.Timeline 1.0
|
import NeoChat.Menu.Timeline 1.0
|
||||||
|
|
||||||
Control {
|
TimelineContainer {
|
||||||
id: root
|
id: audioDelegate
|
||||||
|
|
||||||
|
width: ListView.view.width
|
||||||
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
|
hoverComponent: hoverActions
|
||||||
|
innerObject: Control {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: audioDelegate.bubbleMaxWidth
|
||||||
|
|
||||||
Audio {
|
Audio {
|
||||||
id: audio
|
id: audio
|
||||||
@@ -26,6 +31,15 @@ Control {
|
|||||||
autoLoad: false
|
autoLoad: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onTapped: openFileContext(model, parent)
|
||||||
|
}
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
onLongPressed: openFileContext(model, parent)
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
RowLayout {
|
RowLayout {
|
||||||
ToolButton {
|
ToolButton {
|
||||||
@@ -59,4 +73,5 @@ Control {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,16 @@
|
|||||||
|
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
import QtQuick.Controls 2.15
|
import QtQuick.Controls 2.15
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
import org.kde.kirigami 2.15 as Kirigami
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
import org.kde.neochat 1.0
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
TextEdit {
|
TimelineContainer {
|
||||||
|
id: encryptedDelegate
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
innerObject: TextEdit {
|
||||||
text: i18n("This message is encrypted and the sender has not shared the key with this device.")
|
text: i18n("This message is encrypted and the sender has not shared the key with this device.")
|
||||||
color: Kirigami.Theme.disabledTextColor
|
color: Kirigami.Theme.disabledTextColor
|
||||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
||||||
@@ -15,4 +20,6 @@ TextEdit {
|
|||||||
readOnly: true
|
readOnly: true
|
||||||
wrapMode: Text.WordWrap
|
wrapMode: Text.WordWrap
|
||||||
textFormat: Text.RichText
|
textFormat: Text.RichText
|
||||||
|
Layout.maximumWidth: encryptedDelegate.bubbleMaxWidth
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
79
imports/NeoChat/Component/Timeline/EventDelegate.qml
Normal file
79
imports/NeoChat/Component/Timeline/EventDelegate.qml
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import Qt.labs.qmlmodels 1.0
|
||||||
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
DelegateChooser {
|
||||||
|
role: "eventType"
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "state"
|
||||||
|
delegate: StateDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "emote"
|
||||||
|
delegate: MessageDelegate {
|
||||||
|
isEmote: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "message"
|
||||||
|
delegate: MessageDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "notice"
|
||||||
|
delegate: MessageDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "image"
|
||||||
|
delegate: ImageDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "sticker"
|
||||||
|
delegate: ImageDelegate {
|
||||||
|
cardBackground: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "audio"
|
||||||
|
delegate: AudioDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "video"
|
||||||
|
delegate: VideoDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "file"
|
||||||
|
delegate: FileDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "encrypted"
|
||||||
|
delegate: EncryptedDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "readMarker"
|
||||||
|
delegate: ReadMarkerDelegate {}
|
||||||
|
}
|
||||||
|
|
||||||
|
DelegateChoice {
|
||||||
|
roleValue: "other"
|
||||||
|
delegate: Item {}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,11 +14,19 @@ import NeoChat.Component 1.0
|
|||||||
import NeoChat.Dialog 1.0
|
import NeoChat.Dialog 1.0
|
||||||
import NeoChat.Menu.Timeline 1.0
|
import NeoChat.Menu.Timeline 1.0
|
||||||
|
|
||||||
RowLayout {
|
TimelineContainer {
|
||||||
id: root
|
id: fileDelegate
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
|
hoverComponent: hoverActions
|
||||||
|
|
||||||
|
innerObject: RowLayout {
|
||||||
property bool openOnFinished: false
|
property bool openOnFinished: false
|
||||||
readonly property bool downloaded: progressInfo && progressInfo.completed
|
readonly property bool downloaded: progressInfo && progressInfo.completed
|
||||||
|
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumWidth: fileDelegate.bubbleMaxWidth
|
||||||
Layout.margins: Kirigami.Units.largeSpacing
|
Layout.margins: Kirigami.Units.largeSpacing
|
||||||
|
|
||||||
spacing: Kirigami.Units.largeSpacing
|
spacing: Kirigami.Units.largeSpacing
|
||||||
@@ -113,6 +121,15 @@ RowLayout {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onTapped: openFileContext(model, parent)
|
||||||
|
}
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
onLongPressed: openFileContext(model, parent)
|
||||||
|
}
|
||||||
|
|
||||||
function saveFileAs() {
|
function saveFileAs() {
|
||||||
var dialog = fileDialog.createObject(QQC2.ApplicationWindow.overlay)
|
var dialog = fileDialog.createObject(QQC2.ApplicationWindow.overlay)
|
||||||
dialog.open()
|
dialog.open()
|
||||||
@@ -123,4 +140,5 @@ RowLayout {
|
|||||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
||||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,16 @@ import NeoChat.Component 1.0
|
|||||||
import NeoChat.Dialog 1.0
|
import NeoChat.Dialog 1.0
|
||||||
import NeoChat.Menu.Timeline 1.0
|
import NeoChat.Menu.Timeline 1.0
|
||||||
|
|
||||||
Image {
|
|
||||||
|
TimelineContainer {
|
||||||
|
id: imageDelegate
|
||||||
|
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
|
hoverComponent: hoverActions
|
||||||
|
|
||||||
|
innerObject: Image {
|
||||||
id: img
|
id: img
|
||||||
|
|
||||||
property var content: model.content
|
property var content: model.content
|
||||||
@@ -25,8 +34,8 @@ Image {
|
|||||||
// readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info
|
// readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info
|
||||||
readonly property var info: content.info
|
readonly property var info: content.info
|
||||||
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId
|
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId
|
||||||
property bool readonly: false
|
|
||||||
|
|
||||||
|
Layout.maximumWidth: imageDelegate.bubbleMaxWidth
|
||||||
source: "image://mxc/" + mediaId
|
source: "image://mxc/" + mediaId
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
@@ -42,7 +51,6 @@ Image {
|
|||||||
|
|
||||||
HoverHandler {
|
HoverHandler {
|
||||||
id: hoverHandler
|
id: hoverHandler
|
||||||
enabled: img.readonly
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
@@ -81,6 +89,25 @@ Image {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onTapped: openFileContext(model, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
onLongPressed: openFileContext(model, parent)
|
||||||
|
onTapped: {
|
||||||
|
fullScreenImage.createObject(parent, {
|
||||||
|
filename: eventId,
|
||||||
|
localPath: currentRoom.urlToDownload(eventId),
|
||||||
|
blurhash: model.content.info["xyz.amorgan.blurhash"],
|
||||||
|
imageWidth: content.info.w,
|
||||||
|
imageHeight: content.info.h
|
||||||
|
}).showFullScreen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function downloadAndOpen()
|
function downloadAndOpen()
|
||||||
{
|
{
|
||||||
if (downloaded) openSavedFile()
|
if (downloaded) openSavedFile()
|
||||||
@@ -96,4 +123,5 @@ Image {
|
|||||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
||||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
27
imports/NeoChat/Component/Timeline/MessageDelegate.qml
Normal file
27
imports/NeoChat/Component/Timeline/MessageDelegate.qml
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2021 Tobias Fella <fella@posteo.de>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import Qt.labs.qmlmodels 1.0
|
||||||
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
TimelineContainer {
|
||||||
|
id: messageDelegate
|
||||||
|
|
||||||
|
width: ListView.view.width
|
||||||
|
property bool isEmote: false
|
||||||
|
|
||||||
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
|
hoverComponent: hoverActions
|
||||||
|
|
||||||
|
innerObject: TextDelegate {
|
||||||
|
isEmote: messageDelegate.isEmote
|
||||||
|
Layout.maximumWidth: messageDelegate.bubbleMaxWidth
|
||||||
|
onRequestOpenMessageContext: openMessageContext(model, parent.selectedText)
|
||||||
|
}
|
||||||
|
}
|
||||||
78
imports/NeoChat/Component/Timeline/ReadMarkerDelegate.qml
Normal file
78
imports/NeoChat/Component/Timeline/ReadMarkerDelegate.qml
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Controls 2.15 as QQC2
|
||||||
|
import QtQuick.Layouts 1.15
|
||||||
|
|
||||||
|
import Qt.labs.qmlmodels 1.0
|
||||||
|
import org.kde.kirigami 2.15 as Kirigami
|
||||||
|
|
||||||
|
import org.kde.neochat 1.0
|
||||||
|
|
||||||
|
QQC2.ItemDelegate {
|
||||||
|
padding: Kirigami.Units.largeSpacing
|
||||||
|
topInset: Kirigami.Units.largeSpacing
|
||||||
|
topPadding: Kirigami.Units.largeSpacing * 2
|
||||||
|
width: ListView.view.width - Kirigami.Units.gridUnit
|
||||||
|
x: Kirigami.Units.gridUnit / 2
|
||||||
|
contentItem: QQC2.Label {
|
||||||
|
text: i18nc("Relative time since the room was last read", "Last read: %1", time)
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Kirigami.ShadowedRectangle {
|
||||||
|
color: Kirigami.Theme.backgroundColor
|
||||||
|
opacity: 0.6
|
||||||
|
radius: Kirigami.Units.smallSpacing
|
||||||
|
shadow.size: Kirigami.Units.smallSpacing
|
||||||
|
shadow.color: Qt.rgba(0.0, 0.0, 0.0, 0.10)
|
||||||
|
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: makeMeDisapearTimer
|
||||||
|
interval: Kirigami.Units.humanMoment * 2
|
||||||
|
onTriggered: if (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden) {
|
||||||
|
currentRoom.markAllMessagesAsRead();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ListView.onPooled: makeMeDisapearTimer.stop()
|
||||||
|
|
||||||
|
ListView.onAdd: {
|
||||||
|
const view = ListView.view;
|
||||||
|
if (view.atYEnd) {
|
||||||
|
makeMeDisapearTimer.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When the read marker is visible and we are at the end of the list,
|
||||||
|
// start the makeMeDisapearTimer
|
||||||
|
Connections {
|
||||||
|
target: ListView.view
|
||||||
|
function onAtYEndChanged() {
|
||||||
|
makeMeDisapearTimer.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ListView.onRemove: {
|
||||||
|
const view = ListView.view;
|
||||||
|
|
||||||
|
if (view.atYEnd) {
|
||||||
|
// easy case just mark everything as read
|
||||||
|
if (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden) {
|
||||||
|
currentRoom.markAllMessagesAsRead();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// mark the last visible index
|
||||||
|
const lastVisibleIdx = lastVisibleIndex();
|
||||||
|
|
||||||
|
if (lastVisibleIdx < index) {
|
||||||
|
currentRoom.readMarkerEventId = sortedMessageEventModel.data(sortedMessageEventModel.index(lastVisibleIdx, 0), MessageEventModel.EventIdRole)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,9 +11,9 @@ import NeoChat.Component 1.0
|
|||||||
import NeoChat.Dialog 1.0
|
import NeoChat.Dialog 1.0
|
||||||
|
|
||||||
RowLayout {
|
RowLayout {
|
||||||
id: row
|
x: Kirigami.Units.gridUnit * 1.5 + Kirigami.Units.smallSpacing
|
||||||
|
|
||||||
height: label.contentHeight
|
height: label.contentHeight
|
||||||
|
width: ListView.view.width - Kirigami.Units.largeSpacing - x
|
||||||
|
|
||||||
Kirigami.Avatar {
|
Kirigami.Avatar {
|
||||||
id: icon
|
id: icon
|
||||||
|
|||||||
@@ -15,7 +15,15 @@ import NeoChat.Component 1.0
|
|||||||
import NeoChat.Dialog 1.0
|
import NeoChat.Dialog 1.0
|
||||||
import NeoChat.Menu.Timeline 1.0
|
import NeoChat.Menu.Timeline 1.0
|
||||||
|
|
||||||
Video {
|
TimelineContainer {
|
||||||
|
id: videoDelegate
|
||||||
|
|
||||||
|
width: ListView.view.width
|
||||||
|
|
||||||
|
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||||
|
hoverComponent: hoverActions
|
||||||
|
|
||||||
|
innerObject: Video {
|
||||||
id: vid
|
id: vid
|
||||||
|
|
||||||
property bool playOnFinished: false
|
property bool playOnFinished: false
|
||||||
@@ -23,6 +31,11 @@ Video {
|
|||||||
|
|
||||||
property bool supportStreaming: true
|
property bool supportStreaming: true
|
||||||
|
|
||||||
|
Layout.maximumWidth: videoDelegate.bubbleMaxWidth
|
||||||
|
Layout.fillWidth: true
|
||||||
|
Layout.maximumHeight: Kirigami.Units.gridUnit * 15
|
||||||
|
Layout.minimumHeight: Kirigami.Units.gridUnit * 5
|
||||||
|
|
||||||
onDownloadedChanged: {
|
onDownloadedChanged: {
|
||||||
if (downloaded) {
|
if (downloaded) {
|
||||||
vid.source = progressInfo.localPath
|
vid.source = progressInfo.localPath
|
||||||
@@ -34,7 +47,6 @@ Video {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
readonly property int maxWidth: 1000 // TODO messageListView.width
|
readonly property int maxWidth: 1000 // TODO messageListView.width
|
||||||
|
|
||||||
Layout.preferredWidth: content.info.w > maxWidth ? maxWidth : content.info.w
|
Layout.preferredWidth: content.info.w > maxWidth ? maxWidth : content.info.w
|
||||||
@@ -54,28 +66,22 @@ Video {
|
|||||||
|
|
||||||
onDurationChanged: {
|
onDurationChanged: {
|
||||||
if (!duration) {
|
if (!duration) {
|
||||||
supportStreaming = false;
|
vid.supportStreaming = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onErrorChanged: {
|
onErrorChanged: {
|
||||||
if (error != MediaPlayer.NoError) {
|
if (error != MediaPlayer.NoError) {
|
||||||
supportStreaming = false;
|
vid.supportStreaming = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Image {
|
Image {
|
||||||
readonly property bool isThumbnail: content.info.thumbnail_info && content.thumbnailMediaId
|
|
||||||
readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info
|
|
||||||
|
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
visible: isThumbnail && (vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError)
|
visible: vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError
|
||||||
|
|
||||||
source: "image://mxc/" + (isThumbnail ? content.thumbnailMediaId : "")
|
source: "image://mxc/" + content.thumbnailMediaId
|
||||||
|
|
||||||
sourceSize.width: info.w
|
|
||||||
sourceSize.height: info.h
|
|
||||||
|
|
||||||
fillMode: Image.PreserveAspectFit
|
fillMode: Image.PreserveAspectFit
|
||||||
}
|
}
|
||||||
@@ -100,7 +106,7 @@ Video {
|
|||||||
Rectangle {
|
Rectangle {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
|
|
||||||
visible: progressInfo.active && !downloaded
|
visible: progressInfo.active && !vid.downloaded
|
||||||
|
|
||||||
color: "#BB000000"
|
color: "#BB000000"
|
||||||
|
|
||||||
@@ -117,19 +123,29 @@ Video {
|
|||||||
|
|
||||||
TapHandler {
|
TapHandler {
|
||||||
acceptedButtons: Qt.LeftButton
|
acceptedButtons: Qt.LeftButton
|
||||||
onTapped: if (supportStreaming || progressInfo.completed) {
|
onTapped: if (vid.supportStreaming || progressInfo.completed) {
|
||||||
if (vid.playbackState == MediaPlayer.PlayingState) {
|
if (vid.playbackState == MediaPlayer.PlayingState) {
|
||||||
vid.pause()
|
vid.pause()
|
||||||
} else {
|
} else {
|
||||||
vid.play()
|
vid.play()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
downloadAndPlay()
|
vid.downloadAndPlay()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.RightButton
|
||||||
|
onTapped: openFileContext(model, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
TapHandler {
|
||||||
|
acceptedButtons: Qt.LeftButton
|
||||||
|
onLongPressed: openFileContext(model, parent)
|
||||||
|
}
|
||||||
|
|
||||||
function downloadAndPlay() {
|
function downloadAndPlay() {
|
||||||
if (downloaded) {
|
if (vid.downloaded) {
|
||||||
playSavedFile()
|
playSavedFile()
|
||||||
} else {
|
} else {
|
||||||
playOnFinished = true
|
playOnFinished = true
|
||||||
@@ -141,4 +157,5 @@ Video {
|
|||||||
vid.stop()
|
vid.stop()
|
||||||
vid.play()
|
vid.play()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,3 +9,6 @@ VideoDelegate 1.0 VideoDelegate.qml
|
|||||||
ReactionDelegate 1.0 ReactionDelegate.qml
|
ReactionDelegate 1.0 ReactionDelegate.qml
|
||||||
AudioDelegate 1.0 AudioDelegate.qml
|
AudioDelegate 1.0 AudioDelegate.qml
|
||||||
EncryptedDelegate 1.0 EncryptedDelegate.qml
|
EncryptedDelegate 1.0 EncryptedDelegate.qml
|
||||||
|
EventDelegate 1.0 EventDelegate.qml
|
||||||
|
MessageDelegate 1.0 MessageDelegate.qml
|
||||||
|
ReadMarkerDelegate 1.0 ReadMarkerDelegate.qml
|
||||||
|
|||||||
@@ -341,282 +341,7 @@ Kirigami.ScrollablePage {
|
|||||||
sourceModel: messageEventModel
|
sourceModel: messageEventModel
|
||||||
}
|
}
|
||||||
|
|
||||||
delegate: DelegateChooser {
|
delegate: EventDelegate {}
|
||||||
id: timelineDelegateChooser
|
|
||||||
role: "eventType"
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "state"
|
|
||||||
delegate: QQC2.Control {
|
|
||||||
leftPadding: Kirigami.Units.gridUnit * 1.5 + Kirigami.Units.smallSpacing
|
|
||||||
topPadding: 0
|
|
||||||
bottomPadding: 0
|
|
||||||
height: contentItem.height
|
|
||||||
contentItem: StateDelegate { }
|
|
||||||
implicitWidth: messageListView.width - Kirigami.Units.largeSpacing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "emote"
|
|
||||||
delegate: TimelineContainer {
|
|
||||||
id: emoteContainer
|
|
||||||
width: messageListView.width
|
|
||||||
isEmote: true
|
|
||||||
onReplyClicked: goToEvent(eventID)
|
|
||||||
hoverComponent: hoverActions
|
|
||||||
|
|
||||||
innerObject: TextDelegate {
|
|
||||||
isEmote: true
|
|
||||||
Layout.maximumWidth: emoteContainer.bubbleMaxWidth
|
|
||||||
onRequestOpenMessageContext: openMessageContext(model, parent.selectedText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "message"
|
|
||||||
delegate: TimelineContainer {
|
|
||||||
id: messageContainer
|
|
||||||
width: messageListView.width
|
|
||||||
onReplyClicked: goToEvent(eventID)
|
|
||||||
hoverComponent: hoverActions
|
|
||||||
|
|
||||||
innerObject: TextDelegate {
|
|
||||||
Layout.maximumWidth: messageContainer.bubbleMaxWidth
|
|
||||||
onRequestOpenMessageContext: openMessageContext(model, parent.selectedText)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "notice"
|
|
||||||
delegate: TimelineContainer {
|
|
||||||
id: noticeContainer
|
|
||||||
width: messageListView.width
|
|
||||||
onReplyClicked: goToEvent(eventID)
|
|
||||||
|
|
||||||
innerObject: TextDelegate {
|
|
||||||
Layout.fillWidth: !Config.compactLayout
|
|
||||||
hasContextMenu: false
|
|
||||||
Layout.maximumWidth: noticeContainer.bubbleMaxWidth
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "image"
|
|
||||||
delegate: TimelineContainer {
|
|
||||||
id: imageContainer
|
|
||||||
width: messageListView.width
|
|
||||||
onReplyClicked: goToEvent(eventID)
|
|
||||||
hoverComponent: hoverActions
|
|
||||||
|
|
||||||
innerObject: ImageDelegate {
|
|
||||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 15
|
|
||||||
Layout.maximumWidth: imageContainer.bubbleMaxWidth
|
|
||||||
Layout.preferredHeight: info.h / info.w * width
|
|
||||||
Layout.maximumHeight: Kirigami.Units.gridUnit * 20
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onTapped: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onLongPressed: openFileContext(model, parent)
|
|
||||||
onTapped: {
|
|
||||||
fullScreenImage.createObject(parent, {
|
|
||||||
filename: eventId,
|
|
||||||
localPath: currentRoom.urlToDownload(eventId),
|
|
||||||
blurhash: model.content.info["xyz.amorgan.blurhash"],
|
|
||||||
imageWidth: content.info.w,
|
|
||||||
imageHeight: content.info.h
|
|
||||||
}).showFullScreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "sticker"
|
|
||||||
delegate: TimelineContainer {
|
|
||||||
width: messageListView.width
|
|
||||||
onReplyClicked: goToEvent(eventID)
|
|
||||||
hoverComponent: hoverActions
|
|
||||||
cardBackground: false
|
|
||||||
|
|
||||||
innerObject: ImageDelegate {
|
|
||||||
readonly: true
|
|
||||||
Layout.maximumWidth: Kirigami.Units.gridUnit * 10
|
|
||||||
Layout.minimumWidth: Kirigami.Units.gridUnit * 10
|
|
||||||
Layout.preferredHeight: info.h / info.w * width
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "audio"
|
|
||||||
delegate: TimelineContainer {
|
|
||||||
id: audioContainer
|
|
||||||
width: messageListView.width
|
|
||||||
onReplyClicked: goToEvent(eventID)
|
|
||||||
hoverComponent: hoverActions
|
|
||||||
|
|
||||||
innerObject: AudioDelegate {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.maximumWidth: audioContainer.bubbleMaxWidth
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onTapped: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onLongPressed: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "video"
|
|
||||||
delegate: TimelineContainer {
|
|
||||||
id: videoContainer
|
|
||||||
width: messageListView.width
|
|
||||||
onReplyClicked: goToEvent(eventID)
|
|
||||||
hoverComponent: hoverActions
|
|
||||||
|
|
||||||
innerObject: VideoDelegate {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.maximumWidth: videoContainer.bubbleMaxWidth
|
|
||||||
Layout.preferredHeight: content.info.h / content.info.w * width
|
|
||||||
Layout.maximumHeight: Kirigami.Units.gridUnit * 15
|
|
||||||
Layout.minimumHeight: Kirigami.Units.gridUnit * 5
|
|
||||||
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onTapped: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onLongPressed: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "file"
|
|
||||||
delegate: TimelineContainer {
|
|
||||||
id: fileContainer
|
|
||||||
width: messageListView.width
|
|
||||||
onReplyClicked: goToEvent(eventID)
|
|
||||||
|
|
||||||
innerObject: FileDelegate {
|
|
||||||
Layout.fillWidth: true
|
|
||||||
Layout.maximumWidth: fileContainer.bubbleMaxWidth
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.RightButton
|
|
||||||
onTapped: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
TapHandler {
|
|
||||||
acceptedButtons: Qt.LeftButton
|
|
||||||
onLongPressed: openFileContext(model, parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "encrypted"
|
|
||||||
delegate: TimelineContainer {
|
|
||||||
id: encryptedContainer
|
|
||||||
width: messageListView.width
|
|
||||||
|
|
||||||
innerObject: EncryptedDelegate {
|
|
||||||
Layout.fillWidth: Config.compactLayout
|
|
||||||
Layout.maximumWidth: encryptedContainer.bubbleMaxWidth
|
|
||||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
|
||||||
Layout.leftMargin: Config.showAvatarInTimeline ? Kirigami.Units.largeSpacing : 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "readMarker"
|
|
||||||
delegate: QQC2.ItemDelegate {
|
|
||||||
padding: Kirigami.Units.largeSpacing
|
|
||||||
topInset: Kirigami.Units.largeSpacing
|
|
||||||
topPadding: Kirigami.Units.largeSpacing * 2
|
|
||||||
width: ListView.view.width - Kirigami.Units.gridUnit
|
|
||||||
x: Kirigami.Units.gridUnit / 2
|
|
||||||
contentItem: QQC2.Label {
|
|
||||||
text: i18nc("Relative time since the room was last read", "Last read: %1", time)
|
|
||||||
}
|
|
||||||
|
|
||||||
background: Kirigami.ShadowedRectangle {
|
|
||||||
color: Kirigami.Theme.backgroundColor
|
|
||||||
opacity: 0.6
|
|
||||||
radius: Kirigami.Units.smallSpacing
|
|
||||||
shadow.size: Kirigami.Units.smallSpacing
|
|
||||||
shadow.color: Qt.rgba(0.0, 0.0, 0.0, 0.10)
|
|
||||||
border.color: Kirigami.ColorUtils.tintWithAlpha(color, Kirigami.Theme.textColor, 0.15)
|
|
||||||
border.width: 1
|
|
||||||
}
|
|
||||||
|
|
||||||
Timer {
|
|
||||||
id: makeMeDisapearTimer
|
|
||||||
interval: Kirigami.Units.humanMoment * 2
|
|
||||||
onTriggered: if (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden) {
|
|
||||||
currentRoom.markAllMessagesAsRead();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ListView.onPooled: makeMeDisapearTimer.stop()
|
|
||||||
|
|
||||||
ListView.onAdd: {
|
|
||||||
const view = ListView.view;
|
|
||||||
if (view.atYEnd) {
|
|
||||||
makeMeDisapearTimer.start()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When the read marker is visible and we are at the end of the list,
|
|
||||||
// start the makeMeDisapearTimer
|
|
||||||
Connections {
|
|
||||||
target: ListView.view
|
|
||||||
function onAtYEndChanged() {
|
|
||||||
makeMeDisapearTimer.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ListView.onRemove: {
|
|
||||||
const view = ListView.view;
|
|
||||||
|
|
||||||
if (view.atYEnd) {
|
|
||||||
// easy case just mark everything as read
|
|
||||||
if (QQC2.ApplicationWindow.window.visibility !== QQC2.ApplicationWindow.Hidden) {
|
|
||||||
currentRoom.markAllMessagesAsRead();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// mark the last visible index
|
|
||||||
const lastVisibleIdx = lastVisibleIndex();
|
|
||||||
|
|
||||||
if (lastVisibleIdx < index) {
|
|
||||||
currentRoom.readMarkerEventId = sortedMessageEventModel.data(sortedMessageEventModel.index(lastVisibleIdx, 0), MessageEventModel.EventIdRole)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DelegateChoice {
|
|
||||||
roleValue: "other"
|
|
||||||
delegate: Item {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QQC2.RoundButton {
|
QQC2.RoundButton {
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
@@ -631,7 +356,7 @@ Kirigami.ScrollablePage {
|
|||||||
visible: currentRoom && currentRoom.hasUnreadMessages && currentRoom.readMarkerLoaded
|
visible: currentRoom && currentRoom.hasUnreadMessages && currentRoom.readMarkerLoaded
|
||||||
action: Kirigami.Action {
|
action: Kirigami.Action {
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
goToEvent(currentRoom.readMarkerEventId)
|
messageListView.goToEvent(currentRoom.readMarkerEventId)
|
||||||
}
|
}
|
||||||
icon.name: "go-up"
|
icon.name: "go-up"
|
||||||
}
|
}
|
||||||
@@ -739,6 +464,9 @@ Kirigami.ScrollablePage {
|
|||||||
}
|
}
|
||||||
headerPositioning: ListView.OverlayHeader
|
headerPositioning: ListView.OverlayHeader
|
||||||
|
|
||||||
|
function goToEvent(eventID) {
|
||||||
|
messageListView.positionViewAtIndex(eventToIndex(eventID), ListView.Contain)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -820,10 +548,6 @@ Kirigami.ScrollablePage {
|
|||||||
messageListView.positionViewAtIndex(0, ListView.End)
|
messageListView.positionViewAtIndex(0, ListView.End)
|
||||||
}
|
}
|
||||||
|
|
||||||
function goToEvent(eventID) {
|
|
||||||
messageListView.positionViewAtIndex(eventToIndex(eventID), ListView.Contain)
|
|
||||||
}
|
|
||||||
|
|
||||||
function eventToIndex(eventID) {
|
function eventToIndex(eventID) {
|
||||||
const index = messageEventModel.eventIDToIndex(eventID)
|
const index = messageEventModel.eventIDToIndex(eventID)
|
||||||
if (index === -1)
|
if (index === -1)
|
||||||
|
|||||||
3
res.qrc
3
res.qrc
@@ -42,6 +42,9 @@
|
|||||||
<file>imports/NeoChat/Component/Timeline/FileDelegate.qml</file>
|
<file>imports/NeoChat/Component/Timeline/FileDelegate.qml</file>
|
||||||
<file>imports/NeoChat/Component/Timeline/ImageDelegate.qml</file>
|
<file>imports/NeoChat/Component/Timeline/ImageDelegate.qml</file>
|
||||||
<file>imports/NeoChat/Component/Timeline/EncryptedDelegate.qml</file>
|
<file>imports/NeoChat/Component/Timeline/EncryptedDelegate.qml</file>
|
||||||
|
<file>imports/NeoChat/Component/Timeline/EventDelegate.qml</file>
|
||||||
|
<file>imports/NeoChat/Component/Timeline/MessageDelegate.qml</file>
|
||||||
|
<file>imports/NeoChat/Component/Timeline/ReadMarkerDelegate.qml</file>
|
||||||
<file>imports/NeoChat/Component/Login/qmldir</file>
|
<file>imports/NeoChat/Component/Login/qmldir</file>
|
||||||
<file>imports/NeoChat/Component/Login/LoginStep.qml</file>
|
<file>imports/NeoChat/Component/Login/LoginStep.qml</file>
|
||||||
<file>imports/NeoChat/Component/Login/Login.qml</file>
|
<file>imports/NeoChat/Component/Login/Login.qml</file>
|
||||||
|
|||||||
@@ -423,8 +423,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
|
|||||||
const KFormat format;
|
const KFormat format;
|
||||||
return format.formatRelativeDateTime(eventDate, QLocale::ShortFormat);
|
return format.formatRelativeDateTime(eventDate, QLocale::ShortFormat);
|
||||||
}
|
}
|
||||||
case SpecialMarksRole:
|
|
||||||
return EventStatus::Hidden;
|
|
||||||
}
|
}
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user