Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85805532
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
16 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/contents/ui/Bubble.qml b/src/contents/ui/Bubble.qml
index 539735e..5aac8c4 100644
--- a/src/contents/ui/Bubble.qml
+++ b/src/contents/ui/Bubble.qml
@@ -1,190 +1,202 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import org.kde.kirigami 2.13 as Kirigami
import '.' as Kazv
ItemDelegate {
id: upper
default property var children
property var currentEvent: event
property var menuContent: []
readonly property var bubbleSpacing: leftPadding + rightPadding
Layout.fillWidth: true
Layout.preferredHeight: bubbleRootLayout.implicitHeight
property var backgroundItem: Rectangle {
anchors.fill: parent
color: hovered ? Kirigami.Theme.activeBackgroundColor : Kirigami.Theme.backgroundColor
}
background: compactMode ? null : backgroundItem
property var eventSourcePopup: Kirigami.OverlaySheet {
title: l10n.get('event-source-popup-title')
Kazv.EventSourceView {
event: upper.currentEvent
}
}
property var reactionPopup: Kazv.ReactToEventPopup {
onAccepted: eventView.reactWith(text)
}
property var menu: Menu {
objectName: 'bubbleContextMenu'
property list<QtObject> staticItems: [
Kirigami.Action {
objectName: 'replyToMenuItem'
text: l10n.get('event-reply-action')
onTriggered: setDraftReplyTo(currentEvent.eventId)
enabled: event && !event.redacted
},
Kirigami.Action {
objectName: 'deleteMenuItem'
text: l10n.get('event-delete')
onTriggered: eventView.redactSelf()
enabled: event && !event.redacted
},
Kirigami.Action {
objectName: 'reactMenuItem'
text: l10n.get('event-react-action')
onTriggered: reactionPopup.open()
enabled: event && !event.redacted
},
MenuSeparator {},
Kirigami.Action {
text: l10n.get('event-view-source')
onTriggered: eventSourcePopup.open()
}
]
contentData: Array.from(staticItems).concat(upper.menuContent)
}
function shouldPopupMenu() {
return !compactMode
}
function maybePopupMenu() {
if (shouldPopupMenu()) {
menu.popup(parent)
}
}
RowLayout {
id: bubbleRootLayout
anchors.left: parent.left
anchors.right: parent.right
property var replyToOrAnnotatedEventId: currentEvent.replyingToEventId || (currentEvent.relationType === 'm.annotation' ? currentEvent.relatedEventId : '')
ColumnLayout {
Layout.fillWidth: true
- property var inReplyTo: RowLayout {
+ property var inReplyTo: Item {
Layout.fillWidth: true
Layout.margins: 0
+ Layout.minimumHeight: Math.min(inReplyToLayout.implicitHeight, Kirigami.Units.gridUnit * 5)
+ Layout.maximumHeight: Kirigami.Units.gridUnit * 5
+ clip: true
+
+ RowLayout {
+ id: inReplyToLayout
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ }
+ Rectangle {
+ Layout.fillHeight: true
+ Layout.preferredWidth: Kirigami.Units.smallSpacing
+ color: Kirigami.Theme.activeTextColor
+ clip: true
+ }
- Rectangle {
- Layout.fillHeight: true
- Layout.preferredWidth: Kirigami.Units.smallSpacing
- color: Kirigami.Theme.activeTextColor
- }
-
- Loader {
- id: inReplyToLoader
- source: 'qrc:/EventViewCompact.qml'
- active: bubbleRootLayout.replyToOrAnnotatedEventId && !compactMode
- asynchronous: true
- Layout.minimumHeight: inReplyToLoader.item ? inReplyToLoader.item.height : 0
- Layout.fillWidth: true
-
- property var event: room.messageById(bubbleRootLayout.replyToOrAnnotatedEventId)
- property var props: ({
- event,
- sender: room.member(event.sender || matrixSdk.userId),
- stateKeyUser: event.stateKey ? room.member(event.stateKey) : {},
- isGapped: timeline.gaps.includes(event.eventId),
- })
+ Loader {
+ id: inReplyToLoader
+ clip: true
+ source: 'qrc:/EventViewCompact.qml'
+ active: bubbleRootLayout.replyToOrAnnotatedEventId && !compactMode
+ asynchronous: true
+ Layout.minimumHeight: inReplyToLoader.item ? inReplyToLoader.item.height : 0
+ Layout.fillWidth: true
+
+ property var event: room.messageById(bubbleRootLayout.replyToOrAnnotatedEventId)
+ property var props: ({
+ event,
+ sender: room.member(event.sender || matrixSdk.userId),
+ stateKeyUser: event.stateKey ? room.member(event.stateKey) : {},
+ isGapped: timeline.gaps.includes(event.eventId),
+ })
+ }
}
}
-
children: [
...(bubbleRootLayout.replyToOrAnnotatedEventId && !compactMode ? [inReplyTo] : []),
...(Array.isArray(upper.children) ? upper.children :
upper.children ? [upper.children] : []),
]
}
property var popupAction: Kirigami.Action {
id: popupAction
text: l10n.get('event-popup-action')
iconName: 'view-more-horizontal-symbolic'
onTriggered: maybePopupMenu()
}
RowLayout {
Layout.alignment: Qt.AlignBottom
Kirigami.Icon {
source: 'emblem-encrypted-locked'
visible: !compactMode && event && event.encrypted
Layout.preferredHeight: inlineBadgeSize
Layout.preferredWidth: inlineBadgeSize
Accessible.role: Accessible.StaticText
Accessible.name: l10n.get('event-encrypted')
ToolTip.text: l10n.get('event-encrypted')
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hoverHandlerEncIcon.hovered
HoverHandler { id: hoverHandlerEncIcon }
}
Kirigami.Icon {
objectName: 'localEchoIndicator'
visible: !!event.isLocalEcho
source: event.isSending ? 'state-sync' : 'state-warning'
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Accessible.role: event.isSending ? Accessible.StaticText : Accessible.Button
Accessible.name: event.isSending ? l10n.get('event-sending') : l10n.get('event-resend')
ToolTip.text: event.isSending ? l10n.get('event-sending') : l10n.get('event-send-failed')
ToolTip.delay: 1000
ToolTip.timeout: 5000
ToolTip.visible: hoverHandler.hovered
HoverHandler { id: hoverHandler }
TapHandler {
objectName: 'resendEventButton'
onTapped: {
room.resendMessage(event.txnId);
}
enabled: event.isFailed
}
}
Label {
objectName: 'timeIndicator'
visible: !compactMode && !event.isLocalEcho
text: event.formattedTime
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8
}
ToolButton {
id: moreButton
visible: !compactMode
action: popupAction
display: AbstractButton.IconOnly
}
}
}
}
diff --git a/src/contents/ui/EventView.qml b/src/contents/ui/EventView.qml
index aeddd45..1b744fc 100644
--- a/src/contents/ui/EventView.qml
+++ b/src/contents/ui/EventView.qml
@@ -1,266 +1,266 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.2
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import Qt.labs.qmlmodels 1.0
import org.kde.kirigami 2.13 as Kirigami
import 'event-types' as Types
Item {
id: eventView
property var event
property var sender
property var stateKeyUser
property var messageType: getMessageType(event)
property var iconSize: Kirigami.Units.iconSizes.large
property var inlineBadgeSize: Kirigami.Units.iconSizes.smallMedium
property var senderNameSize: Kirigami.Units.gridUnit * 1.2
property var redactPromise: null
property var minHeight: Kirigami.Units.gridUnit * 2
property var isGapped: false
property var compactMode: false
property var calculatedHeight: (gappedLabel.visible ? gappedLabel.height : 0) + (loader.item ? loader.item.implicitHeight : 0)
height: calculatedHeight
implicitHeight: calculatedHeight
Layout.minimumHeight: calculatedHeight
Layout.preferredHeight: calculatedHeight
Component {
id: textEV
Types.Text {
event: eventView.event
sender: eventView.sender
}
}
Component {
id: emoteEV
Types.Emote {
event: eventView.event
sender: eventView.sender
}
}
Component {
id: noticeEV
Types.Notice {
event: eventView.event
sender: eventView.sender
}
}
Component {
id: stateEV
Types.State {
event: eventView.event
sender: eventView.sender
stateKeyUser: eventView.stateKeyUser
}
}
Component {
id: imageEV
Types.Image {
event: eventView.event
sender: eventView.sender
}
}
Component {
id: fileEV
Types.File {
event: eventView.event
sender: eventView.sender
}
}
Component {
id: videoEV
Types.Video {
event: eventView.event
sender: eventView.sender
}
}
Component {
id: audioEV
Types.Audio {
event: eventView.event
sender: eventView.sender
}
}
Component {
id: unknownEV
Label {
text: 'unknown event'
}
}
Component {
id: redactedEV
Types.Redacted {
event: eventView.event
sender: eventView.sender
}
}
Component {
id: reactionEV
Types.Reaction {
event: eventView.event
sender: eventView.sender
}
}
Component {
id: ignoreEV
Item {
}
}
ToolButton {
id: gappedLabel
- visible: eventView.isGapped
+ visible: eventView.isGapped && !eventView.compactMode
icon.name: 'content-loading-symbolic'
text: l10n.get('room-timeline-load-more-action')
anchors.top: eventView.top
anchors.bottom: loader.top
anchors.left: eventView.left
anchors.right: eventView.right
onClicked: eventView.paginateBack()
}
Loader {
sourceComponent: getSource(messageType)
id: loader
anchors.left: eventView.left
anchors.right: eventView.right
anchors.bottom: eventView.bottom
}
function getSource(t) {
switch (t) {
case 'text':
return textEV;
case 'emote':
return emoteEV;
case 'notice':
return noticeEV;
case 'state':
return stateEV;
case 'image':
return imageEV;
case 'file':
return fileEV;
case 'video':
return videoEV;
case 'audio':
return audioEV;
case 'redacted':
return redactedEV;
case 'reaction':
return reactionEV;
case 'ignore':
return ignoreEV;
default:
return unknownEV;
}
}
function getMessageType(e) {
if (e.redacted) {
return 'redacted';
}
if (e.isState) {
return 'state';
}
switch (e.type) {
case 'm.room.message':
switch (e.content.msgtype) {
case 'm.text':
return 'text';
case 'm.emote':
return 'emote';
case 'm.notice':
return 'notice';
case 'm.image':
return 'image';
case 'm.file':
return 'file';
case 'm.audio':
return 'audio';
case 'm.video':
return 'video';
case 'm.location':
return 'location';
default:
console.log('msg type=unknown');
return 'unknown';
}
case 'm.room.redaction':
return 'ignore';
case 'm.reaction':
return 'reaction';
default:
return 'unknown';
}
}
function redactSelf(reason) {
eventView.redactPromise = room.redactEvent(eventView.event.eventId, reason);
}
Connections {
target: eventView.redactPromise
function onResolved(success, data) {
if (!success) {
showPassiveNotification(l10n.get('event-delete-failed', { errorMsg: data.error, errorCode: data.errorCode }));
}
eventView.redactPromise = null;
}
}
function paginateBack() {
room.paginateBackFrom(event.eventId);
}
function reactWith(reactionText) {
room.sendReaction(reactionText, event.eventId);
}
}
diff --git a/src/contents/ui/SendMessageBox.qml b/src/contents/ui/SendMessageBox.qml
index a4bb429..954da10 100644
--- a/src/contents/ui/SendMessageBox.qml
+++ b/src/contents/ui/SendMessageBox.qml
@@ -1,132 +1,143 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import QtQuick.Dialogs 1.3
import org.kde.kirigami 2.13 as Kirigami
import moe.kazv.mxc.kazv 0.0 as MK
import '.' as Kazv
ColumnLayout {
id: sendMessageBox
property var room
property var draftReplyTo: ''
property var timeline: room.timeline()
onRoomChanged: {
textArea.changeText(room.localDraft, true);
}
ColumnLayout {
visible: !!sendMessageBox.draftReplyTo
RowLayout {
Label {
Layout.fillWidth: true
text: l10n.get('send-message-box-reply-to')
}
ToolButton {
action: removeReplyToAction
display: AbstractButton.IconOnly
}
}
- Kazv.EventViewWrapper {
- Layout.minimumHeight: implicitHeight
+ Item {
+ Layout.minimumHeight: Math.min(replyToEventView.implicitHeight, Kirigami.Units.gridUnit * 5)
+ Layout.maximumHeight: Kirigami.Units.gridUnit * 5
Layout.fillWidth: true
- event: room.messageById(sendMessageBox.draftReplyTo)
- compactMode: true
+ clip: true
+
+ Kazv.EventViewWrapper {
+ id: replyToEventView
+ anchors {
+ top: parent.top
+ left: parent.left
+ right: parent.right
+ }
+ event: room.messageById(sendMessageBox.draftReplyTo)
+ compactMode: true
+ }
}
}
RowLayout {
TextArea {
id: textArea
objectName: 'draftMessage'
property var shortcutList: ["Ctrl+Return", "Ctrl+Enter"]
property var inhibitTyping: false
placeholderText: l10n.get('send-message-box-input-placeholder')
Layout.fillWidth: true
wrapMode: TextEdit.Wrap
onTextChanged: {
room.setLocalDraft(text);
if (!inhibitTyping) {
room.setTyping(true);
}
inhibitTyping = false;
}
function changeText(newText, inhibitTyping) {
textArea.inhibitTyping = inhibitTyping;
textArea.text = newText;
}
onVisibleChanged: {
if (!visible) {
room.updateLocalDraftNow();
}
}
// Shortcut keys for sending messages (TODO: Shortcut keys customized by the user)
Shortcut {
sequences: textArea.shortcutList
onActivated: {
sendAction.trigger()
}
}
}
ToolButton {
icon.name: "document-send-symbolic"
onClicked: sendMediaFileAction.trigger()
}
ToolButton {
icon.name: "document-send"
onClicked: sendAction.trigger()
}
}
Kirigami.Action {
id: removeReplyToAction
iconName: 'window-close-symbolic'
text: l10n.get('send-message-box-remove-reply-to-action')
onTriggered: {
sendMessageBox.draftReplyTo = '';
}
}
Kirigami.Action {
id: sendAction
iconName: "document-send"
text: l10n.get("send-message-box-send")
onTriggered: {
room.setTyping(false);
room.sendTextMessage(textArea.text, draftReplyTo);
textArea.changeText("", true);
draftReplyTo = '';
}
}
Kirigami.Action {
id:sendMediaFileAction
iconName: "mail-attachment-symbolic"
text: l10n.get("send-message-box-send-file")
onTriggered: {
fileDialog.open()
}
}
property var fileDialog: FileDialog {
onAccepted: {
kazvIOManager.startNewUploadJob(matrixSdk.serverUrl, fileUrl, matrixSdk.token,
room.roomId, sdkVars.roomList, room.encrypted)
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Oct 11, 1:42 PM (1 d, 15 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1785833
Default Alt Text
(16 KB)
Attached To
Mode
rK kazv
Attached
Detach File
Event Timeline
Log In to Comment