Refactor delegates
This commit is contained in:
@@ -15,47 +15,62 @@ import NeoChat.Component 1.0
|
||||
import NeoChat.Dialog 1.0
|
||||
import NeoChat.Menu.Timeline 1.0
|
||||
|
||||
Control {
|
||||
id: root
|
||||
TimelineContainer {
|
||||
id: audioDelegate
|
||||
|
||||
Layout.fillWidth: true
|
||||
width: ListView.view.width
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
hoverComponent: hoverActions
|
||||
innerObject: Control {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: audioDelegate.bubbleMaxWidth
|
||||
|
||||
Audio {
|
||||
id: audio
|
||||
source: currentRoom.urlToMxcUrl(content.url)
|
||||
autoLoad: false
|
||||
}
|
||||
Audio {
|
||||
id: audio
|
||||
source: currentRoom.urlToMxcUrl(content.url)
|
||||
autoLoad: false
|
||||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
RowLayout {
|
||||
ToolButton {
|
||||
icon.name: audio.playbackState == Audio.PlayingState ? "media-playback-pause" : "media-playback-start"
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openFileContext(model, parent)
|
||||
}
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onLongPressed: openFileContext(model, parent)
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
if (audio.playbackState == Audio.PlayingState) {
|
||||
audio.pause()
|
||||
} else {
|
||||
audio.play()
|
||||
contentItem: ColumnLayout {
|
||||
RowLayout {
|
||||
ToolButton {
|
||||
icon.name: audio.playbackState == Audio.PlayingState ? "media-playback-pause" : "media-playback-start"
|
||||
|
||||
onClicked: {
|
||||
if (audio.playbackState == Audio.PlayingState) {
|
||||
audio.pause()
|
||||
} else {
|
||||
audio.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: model.display
|
||||
}
|
||||
}
|
||||
Label {
|
||||
text: model.display
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
visible: audio.hasAudio
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
// Server doesn't support seeking, so use ProgressBar instead of Slider :(
|
||||
ProgressBar {
|
||||
from: 0
|
||||
to: audio.duration
|
||||
value: audio.position
|
||||
}
|
||||
RowLayout {
|
||||
visible: audio.hasAudio
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
// Server doesn't support seeking, so use ProgressBar instead of Slider :(
|
||||
ProgressBar {
|
||||
from: 0
|
||||
to: audio.duration
|
||||
value: audio.position
|
||||
}
|
||||
|
||||
Label {
|
||||
text: Controller.formatDuration(audio.position) + "/" + Controller.formatDuration(audio.duration)
|
||||
Label {
|
||||
text: Controller.formatDuration(audio.position) + "/" + Controller.formatDuration(audio.duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,23 @@
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import org.kde.kirigami 2.15 as Kirigami
|
||||
import org.kde.neochat 1.0
|
||||
|
||||
TextEdit {
|
||||
text: i18n("This message is encrypted and the sender has not shared the key with this device.")
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
||||
selectByMouse: !Kirigami.Settings.isMobile
|
||||
readOnly: true
|
||||
wrapMode: Text.WordWrap
|
||||
textFormat: Text.RichText
|
||||
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.")
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize
|
||||
selectByMouse: !Kirigami.Settings.isMobile
|
||||
readOnly: true
|
||||
wrapMode: Text.WordWrap
|
||||
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,113 +14,131 @@ import NeoChat.Component 1.0
|
||||
import NeoChat.Dialog 1.0
|
||||
import NeoChat.Menu.Timeline 1.0
|
||||
|
||||
RowLayout {
|
||||
id: root
|
||||
property bool openOnFinished: false
|
||||
readonly property bool downloaded: progressInfo && progressInfo.completed
|
||||
TimelineContainer {
|
||||
id: fileDelegate
|
||||
width: ListView.view.width
|
||||
|
||||
Layout.margins: Kirigami.Units.largeSpacing
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
hoverComponent: hoverActions
|
||||
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
innerObject: RowLayout {
|
||||
property bool openOnFinished: false
|
||||
readonly property bool downloaded: progressInfo && progressInfo.completed
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "downloaded"
|
||||
when: progressInfo.completed
|
||||
|
||||
PropertyChanges {
|
||||
target: downloadButton
|
||||
|
||||
icon.name: "document-open"
|
||||
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to open its downloaded file with an appropriate application", "Open File")
|
||||
|
||||
onClicked: openSavedFile()
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "downloading"
|
||||
when: progressInfo.active
|
||||
|
||||
PropertyChanges {
|
||||
target: sizeLabel
|
||||
text: i18nc("file download progress", "%1 / %2", Controller.formatByteSize(progressInfo.progress), Controller.formatByteSize(progressInfo.total))
|
||||
}
|
||||
PropertyChanges {
|
||||
target: downloadButton
|
||||
icon.name: "media-playback-stop"
|
||||
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
|
||||
onClicked: currentRoom.cancelFileTransfer(eventId)
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "raw"
|
||||
when: true
|
||||
|
||||
PropertyChanges {
|
||||
target: downloadButton
|
||||
|
||||
onClicked: root.saveFileAs()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Kirigami.Icon {
|
||||
id: ikon
|
||||
source: model.fileMimetypeIcon
|
||||
fallback: "unknown"
|
||||
}
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: fileDelegate.bubbleMaxWidth
|
||||
Layout.margins: Kirigami.Units.largeSpacing
|
||||
|
||||
spacing: 0
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
QQC2.Label {
|
||||
text: model.display
|
||||
wrapMode: Text.Wrap
|
||||
states: [
|
||||
State {
|
||||
name: "downloaded"
|
||||
when: progressInfo.completed
|
||||
|
||||
Layout.fillWidth: true
|
||||
PropertyChanges {
|
||||
target: downloadButton
|
||||
|
||||
icon.name: "document-open"
|
||||
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to open its downloaded file with an appropriate application", "Open File")
|
||||
|
||||
onClicked: openSavedFile()
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "downloading"
|
||||
when: progressInfo.active
|
||||
|
||||
PropertyChanges {
|
||||
target: sizeLabel
|
||||
text: i18nc("file download progress", "%1 / %2", Controller.formatByteSize(progressInfo.progress), Controller.formatByteSize(progressInfo.total))
|
||||
}
|
||||
PropertyChanges {
|
||||
target: downloadButton
|
||||
icon.name: "media-playback-stop"
|
||||
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; stops downloading the message's file", "Stop Download")
|
||||
onClicked: currentRoom.cancelFileTransfer(eventId)
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "raw"
|
||||
when: true
|
||||
|
||||
PropertyChanges {
|
||||
target: downloadButton
|
||||
|
||||
onClicked: root.saveFileAs()
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
Kirigami.Icon {
|
||||
id: ikon
|
||||
source: model.fileMimetypeIcon
|
||||
fallback: "unknown"
|
||||
}
|
||||
QQC2.Label {
|
||||
id: sizeLabel
|
||||
|
||||
text: Controller.formatByteSize(content.info ? content.info.size : 0)
|
||||
opacity: 0.7
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
|
||||
QQC2.Button {
|
||||
id: downloadButton
|
||||
icon.name: "download"
|
||||
spacing: 0
|
||||
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to download its file", "Download")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
QQC2.Label {
|
||||
text: model.display
|
||||
wrapMode: Text.Wrap
|
||||
|
||||
Component {
|
||||
id: fileDialog
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
QQC2.Label {
|
||||
id: sizeLabel
|
||||
|
||||
FileDialog {
|
||||
fileMode: FileDialog.SaveFile
|
||||
folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation)
|
||||
onAccepted: {
|
||||
currentRoom.downloadFile(eventId, file)
|
||||
text: Controller.formatByteSize(content.info ? content.info.size : 0)
|
||||
opacity: 0.7
|
||||
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function saveFileAs() {
|
||||
var dialog = fileDialog.createObject(QQC2.ApplicationWindow.overlay)
|
||||
dialog.open()
|
||||
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(eventId)
|
||||
}
|
||||
QQC2.Button {
|
||||
id: downloadButton
|
||||
icon.name: "download"
|
||||
|
||||
function openSavedFile() {
|
||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
||||
QQC2.ToolTip.text: i18nc("tooltip for a button on a message; offers ability to download its file", "Download")
|
||||
QQC2.ToolTip.visible: hovered
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDialog
|
||||
|
||||
FileDialog {
|
||||
fileMode: FileDialog.SaveFile
|
||||
folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation)
|
||||
onAccepted: {
|
||||
currentRoom.downloadFile(eventId, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openFileContext(model, parent)
|
||||
}
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onLongPressed: openFileContext(model, parent)
|
||||
}
|
||||
|
||||
function saveFileAs() {
|
||||
var dialog = fileDialog.createObject(QQC2.ApplicationWindow.overlay)
|
||||
dialog.open()
|
||||
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(eventId)
|
||||
}
|
||||
|
||||
function openSavedFile() {
|
||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,88 +12,116 @@ import NeoChat.Component 1.0
|
||||
import NeoChat.Dialog 1.0
|
||||
import NeoChat.Menu.Timeline 1.0
|
||||
|
||||
Image {
|
||||
id: img
|
||||
|
||||
property var content: model.content
|
||||
readonly property bool isAnimated: contentType === "image/gif"
|
||||
TimelineContainer {
|
||||
id: imageDelegate
|
||||
|
||||
property bool openOnFinished: false
|
||||
readonly property bool downloaded: progressInfo && progressInfo.completed
|
||||
width: ListView.view.width
|
||||
|
||||
readonly property bool isThumbnail: !(content.info.thumbnail_info == null || content.thumbnailMediaId == null)
|
||||
// readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info
|
||||
readonly property var info: content.info
|
||||
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId
|
||||
property bool readonly: false
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
hoverComponent: hoverActions
|
||||
|
||||
source: "image://mxc/" + mediaId
|
||||
innerObject: Image {
|
||||
id: img
|
||||
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: content.info["xyz.amorgan.blurhash"] ? ("image://blurhash/" + content.info["xyz.amorgan.blurhash"]) : ""
|
||||
visible: parent.status !== Image.Ready
|
||||
}
|
||||
property var content: model.content
|
||||
readonly property bool isAnimated: contentType === "image/gif"
|
||||
|
||||
fillMode: Image.PreserveAspectFit
|
||||
property bool openOnFinished: false
|
||||
readonly property bool downloaded: progressInfo && progressInfo.completed
|
||||
|
||||
ToolTip.text: display
|
||||
ToolTip.visible: hoverHandler.hovered
|
||||
readonly property bool isThumbnail: !(content.info.thumbnail_info == null || content.thumbnailMediaId == null)
|
||||
// readonly property var info: isThumbnail ? content.info.thumbnail_info : content.info
|
||||
readonly property var info: content.info
|
||||
readonly property string mediaId: isThumbnail ? content.thumbnailMediaId : content.mediaId
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
enabled: img.readonly
|
||||
}
|
||||
Layout.maximumWidth: imageDelegate.bubbleMaxWidth
|
||||
source: "image://mxc/" + mediaId
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
visible: progressInfo.active && !downloaded
|
||||
|
||||
color: "#BB000000"
|
||||
|
||||
ProgressBar {
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: parent.width * 0.8
|
||||
|
||||
from: 0
|
||||
to: progressInfo.total
|
||||
value: progressInfo.progress
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
source: content.info["xyz.amorgan.blurhash"] ? ("image://blurhash/" + content.info["xyz.amorgan.blurhash"]) : ""
|
||||
visible: parent.status !== Image.Ready
|
||||
}
|
||||
}
|
||||
|
||||
function saveFileAs() {
|
||||
var dialog = fileDialog.createObject(ApplicationWindow.overlay)
|
||||
dialog.open()
|
||||
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(eventId)
|
||||
}
|
||||
fillMode: Image.PreserveAspectFit
|
||||
|
||||
Component {
|
||||
id: fileDialog
|
||||
ToolTip.text: display
|
||||
ToolTip.visible: hoverHandler.hovered
|
||||
|
||||
FileDialog {
|
||||
fileMode: FileDialog.SaveFile
|
||||
folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation)
|
||||
onAccepted: {
|
||||
currentRoom.downloadFile(eventId, file)
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
visible: progressInfo.active && !downloaded
|
||||
|
||||
color: "#BB000000"
|
||||
|
||||
ProgressBar {
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: parent.width * 0.8
|
||||
|
||||
from: 0
|
||||
to: progressInfo.total
|
||||
value: progressInfo.progress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function downloadAndOpen()
|
||||
{
|
||||
if (downloaded) openSavedFile()
|
||||
else
|
||||
function saveFileAs() {
|
||||
var dialog = fileDialog.createObject(ApplicationWindow.overlay)
|
||||
dialog.open()
|
||||
dialog.currentFile = dialog.folder + "/" + currentRoom.fileNameToDownload(eventId)
|
||||
}
|
||||
|
||||
Component {
|
||||
id: fileDialog
|
||||
|
||||
FileDialog {
|
||||
fileMode: FileDialog.SaveFile
|
||||
folder: StandardPaths.writableLocation(StandardPaths.DownloadLocation)
|
||||
onAccepted: {
|
||||
currentRoom.downloadFile(eventId, file)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
openOnFinished = true
|
||||
currentRoom.downloadFile(eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
||||
if (downloaded) openSavedFile()
|
||||
else
|
||||
{
|
||||
openOnFinished = true
|
||||
currentRoom.downloadFile(eventId, StandardPaths.writableLocation(StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
||||
}
|
||||
}
|
||||
|
||||
function openSavedFile()
|
||||
{
|
||||
if (Qt.openUrlExternally(progressInfo.localPath)) return;
|
||||
if (Qt.openUrlExternally(progressInfo.localDir)) return;
|
||||
}
|
||||
}
|
||||
|
||||
function openSavedFile()
|
||||
{
|
||||
if (Qt.openUrlExternally(progressInfo.localPath)) 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
|
||||
|
||||
RowLayout {
|
||||
id: row
|
||||
|
||||
x: Kirigami.Units.gridUnit * 1.5 + Kirigami.Units.smallSpacing
|
||||
height: label.contentHeight
|
||||
width: ListView.view.width - Kirigami.Units.largeSpacing - x
|
||||
|
||||
Kirigami.Avatar {
|
||||
id: icon
|
||||
|
||||
@@ -15,130 +15,147 @@ import NeoChat.Component 1.0
|
||||
import NeoChat.Dialog 1.0
|
||||
import NeoChat.Menu.Timeline 1.0
|
||||
|
||||
Video {
|
||||
id: vid
|
||||
TimelineContainer {
|
||||
id: videoDelegate
|
||||
|
||||
property bool playOnFinished: false
|
||||
readonly property bool downloaded: progressInfo && progressInfo.completed
|
||||
width: ListView.view.width
|
||||
|
||||
property bool supportStreaming: true
|
||||
onReplyClicked: ListView.view.goToEvent(eventID)
|
||||
hoverComponent: hoverActions
|
||||
|
||||
onDownloadedChanged: {
|
||||
if (downloaded) {
|
||||
vid.source = progressInfo.localPath
|
||||
innerObject: Video {
|
||||
id: vid
|
||||
|
||||
property bool playOnFinished: false
|
||||
readonly property bool downloaded: progressInfo && progressInfo.completed
|
||||
|
||||
property bool supportStreaming: true
|
||||
|
||||
Layout.maximumWidth: videoDelegate.bubbleMaxWidth
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumHeight: Kirigami.Units.gridUnit * 15
|
||||
Layout.minimumHeight: Kirigami.Units.gridUnit * 5
|
||||
|
||||
onDownloadedChanged: {
|
||||
if (downloaded) {
|
||||
vid.source = progressInfo.localPath
|
||||
}
|
||||
|
||||
if (downloaded && playOnFinished) {
|
||||
playSavedFile()
|
||||
playOnFinished = false
|
||||
}
|
||||
}
|
||||
|
||||
if (downloaded && playOnFinished) {
|
||||
playSavedFile()
|
||||
playOnFinished = false
|
||||
readonly property int maxWidth: 1000 // TODO messageListView.width
|
||||
|
||||
Layout.preferredWidth: content.info.w > maxWidth ? maxWidth : content.info.w
|
||||
Layout.preferredHeight: content.info.w > maxWidth ? (content.info.h / content.info.w * maxWidth) : content.info.h
|
||||
|
||||
loops: MediaPlayer.Infinite
|
||||
|
||||
fillMode: VideoOutput.PreserveAspectFit
|
||||
|
||||
Component.onCompleted: {
|
||||
if (downloaded) {
|
||||
source = progressInfo.localPath
|
||||
} else {
|
||||
source = currentRoom.urlToMxcUrl(content.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
readonly property int maxWidth: 1000 // TODO messageListView.width
|
||||
|
||||
Layout.preferredWidth: content.info.w > maxWidth ? maxWidth : content.info.w
|
||||
Layout.preferredHeight: content.info.w > maxWidth ? (content.info.h / content.info.w * maxWidth) : content.info.h
|
||||
|
||||
loops: MediaPlayer.Infinite
|
||||
|
||||
fillMode: VideoOutput.PreserveAspectFit
|
||||
|
||||
Component.onCompleted: {
|
||||
if (downloaded) {
|
||||
source = progressInfo.localPath
|
||||
} else {
|
||||
source = currentRoom.urlToMxcUrl(content.url)
|
||||
onDurationChanged: {
|
||||
if (!duration) {
|
||||
vid.supportStreaming = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onDurationChanged: {
|
||||
if (!duration) {
|
||||
supportStreaming = false;
|
||||
onErrorChanged: {
|
||||
if (error != MediaPlayer.NoError) {
|
||||
vid.supportStreaming = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onErrorChanged: {
|
||||
if (error != MediaPlayer.NoError) {
|
||||
supportStreaming = false;
|
||||
Image {
|
||||
anchors.fill: parent
|
||||
|
||||
visible: vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError
|
||||
|
||||
source: "image://mxc/" + content.thumbnailMediaId
|
||||
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
visible: isThumbnail && (vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError)
|
||||
|
||||
source: "image://mxc/" + (isThumbnail ? content.thumbnailMediaId : "")
|
||||
|
||||
sourceSize.width: info.w
|
||||
sourceSize.height: info.h
|
||||
|
||||
fillMode: Image.PreserveAspectFit
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
|
||||
visible: vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError
|
||||
color: "white"
|
||||
text: i18n("Video")
|
||||
font.pixelSize: 16
|
||||
|
||||
padding: 8
|
||||
|
||||
background: Rectangle {
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
color: "black"
|
||||
opacity: 0.3
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
visible: progressInfo.active && !downloaded
|
||||
|
||||
color: "#BB000000"
|
||||
|
||||
ProgressBar {
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: parent.width * 0.8
|
||||
visible: vid.playbackState == MediaPlayer.StoppedState || vid.error != MediaPlayer.NoError
|
||||
color: "white"
|
||||
text: i18n("Video")
|
||||
font.pixelSize: 16
|
||||
|
||||
from: 0
|
||||
to: progressInfo.total
|
||||
value: progressInfo.progress
|
||||
}
|
||||
}
|
||||
padding: 8
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onTapped: if (supportStreaming || progressInfo.completed) {
|
||||
if (vid.playbackState == MediaPlayer.PlayingState) {
|
||||
vid.pause()
|
||||
} else {
|
||||
vid.play()
|
||||
background: Rectangle {
|
||||
radius: Kirigami.Units.smallSpacing
|
||||
color: "black"
|
||||
opacity: 0.3
|
||||
}
|
||||
} else {
|
||||
downloadAndPlay()
|
||||
}
|
||||
}
|
||||
|
||||
function downloadAndPlay() {
|
||||
if (downloaded) {
|
||||
playSavedFile()
|
||||
} else {
|
||||
playOnFinished = true
|
||||
currentRoom.downloadFile(eventId, Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
|
||||
visible: progressInfo.active && !vid.downloaded
|
||||
|
||||
color: "#BB000000"
|
||||
|
||||
ProgressBar {
|
||||
anchors.centerIn: parent
|
||||
|
||||
width: parent.width * 0.8
|
||||
|
||||
from: 0
|
||||
to: progressInfo.total
|
||||
value: progressInfo.progress
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function playSavedFile() {
|
||||
vid.stop()
|
||||
vid.play()
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onTapped: if (vid.supportStreaming || progressInfo.completed) {
|
||||
if (vid.playbackState == MediaPlayer.PlayingState) {
|
||||
vid.pause()
|
||||
} else {
|
||||
vid.play()
|
||||
}
|
||||
} else {
|
||||
vid.downloadAndPlay()
|
||||
}
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.RightButton
|
||||
onTapped: openFileContext(model, parent)
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
acceptedButtons: Qt.LeftButton
|
||||
onLongPressed: openFileContext(model, parent)
|
||||
}
|
||||
|
||||
function downloadAndPlay() {
|
||||
if (vid.downloaded) {
|
||||
playSavedFile()
|
||||
} else {
|
||||
playOnFinished = true
|
||||
currentRoom.downloadFile(eventId, Platform.StandardPaths.writableLocation(Platform.StandardPaths.CacheLocation) + "/" + eventId.replace(":", "_").replace("/", "_").replace("+", "_") + currentRoom.fileNameToDownload(eventId))
|
||||
}
|
||||
}
|
||||
|
||||
function playSavedFile() {
|
||||
vid.stop()
|
||||
vid.play()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,4 +8,7 @@ FileDelegate 1.0 FileDelegate.qml
|
||||
VideoDelegate 1.0 VideoDelegate.qml
|
||||
ReactionDelegate 1.0 ReactionDelegate.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
|
||||
}
|
||||
|
||||
delegate: DelegateChooser {
|
||||
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 {}
|
||||
}
|
||||
}
|
||||
delegate: EventDelegate {}
|
||||
|
||||
QQC2.RoundButton {
|
||||
anchors.right: parent.right
|
||||
@@ -631,7 +356,7 @@ Kirigami.ScrollablePage {
|
||||
visible: currentRoom && currentRoom.hasUnreadMessages && currentRoom.readMarkerLoaded
|
||||
action: Kirigami.Action {
|
||||
onTriggered: {
|
||||
goToEvent(currentRoom.readMarkerEventId)
|
||||
messageListView.goToEvent(currentRoom.readMarkerEventId)
|
||||
}
|
||||
icon.name: "go-up"
|
||||
}
|
||||
@@ -739,6 +464,9 @@ Kirigami.ScrollablePage {
|
||||
}
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
|
||||
function goToEvent(eventID) {
|
||||
messageListView.positionViewAtIndex(eventToIndex(eventID), ListView.Contain)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -820,10 +548,6 @@ Kirigami.ScrollablePage {
|
||||
messageListView.positionViewAtIndex(0, ListView.End)
|
||||
}
|
||||
|
||||
function goToEvent(eventID) {
|
||||
messageListView.positionViewAtIndex(eventToIndex(eventID), ListView.Contain)
|
||||
}
|
||||
|
||||
function eventToIndex(eventID) {
|
||||
const index = messageEventModel.eventIDToIndex(eventID)
|
||||
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/ImageDelegate.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/LoginStep.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;
|
||||
return format.formatRelativeDateTime(eventDate, QLocale::ShortFormat);
|
||||
}
|
||||
case SpecialMarksRole:
|
||||
return EventStatus::Hidden;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user