Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85823193
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Authored By
Unknown
Size
39 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/contents/ui/AddStickerPopup.qml b/src/contents/ui/AddStickerPopup.qml
index e7ffa42..3e6e024 100644
--- a/src/contents/ui/AddStickerPopup.qml
+++ b/src/contents/ui/AddStickerPopup.qml
@@ -1,88 +1,88 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2024 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.20 as Kirigami
import moe.kazv.mxc.kazv 0.0 as MK
import '.' as Kazv
-Kirigami.OverlaySheet {
+Kazv.SelfDestroyableOverlaySheet {
id: addStickerPopup
property var stickerPackList
property var event
property var currentPack: stickerPackList.at(0)
property var stickerSize: Kirigami.Units.iconSizes.enormous
property var addingSticker: false
title: l10n.get('add-sticker-popup-title')
ColumnLayout {
Kirigami.FormLayout {
Layout.fillWidth: true
Kirigami.Icon {
Layout.preferredHeight: addStickerPopup.stickerSize
Layout.preferredWidth: addStickerPopup.stickerSize
height: addStickerPopup.stickerSize
width: addStickerPopup.stickerSize
source: matrixSdk.mxcUriToHttp(event.content.url)
}
TextField {
Layout.fillWidth: true
id: shortCodeInput
objectName: 'shortCodeInput'
readOnly: addStickerPopup.addingSticker
Kirigami.FormData.label: l10n.get('add-sticker-popup-short-code-prompt')
}
Label {
objectName: 'shortCodeExistsWarning'
visible: currentPack.hasShortCode(shortCodeInput.text)
text: l10n.get('add-sticker-popup-short-code-exists-warning')
}
}
RowLayout {
Layout.alignment: Qt.AlignRight
Button {
objectName: 'addStickerButton'
text: l10n.get('add-sticker-popup-add-sticker-button')
onClicked: addStickerPopup.updateStickerPack.call()
enabled: !addStickerPopup.addingSticker
}
Button {
objectName: 'cancelButton'
text: l10n.get('add-sticker-popup-cancel-button')
onClicked: addStickerPopup.close()
}
}
}
property var updateStickerPack: Kazv.AsyncHandler {
trigger: () => {
addStickerPopup.addingSticker = true;
return matrixSdk.updateStickerPack(
currentPack.addSticker(shortCodeInput.text, addStickerPopup.event)
);
}
onResolved: (success, data) => {
addStickerPopup.addingSticker = false;
if (!success) {
showPassiveNotification(l10n.get('add-sticker-popup-failed-prompt', { errorCode: data.errorCode, errorMsg: data.error }));
} else {
addStickerPopup.close();
}
}
}
}
diff --git a/src/contents/ui/Bubble.qml b/src/contents/ui/Bubble.qml
index a4e4353..248053f 100644
--- a/src/contents/ui/Bubble.qml
+++ b/src/contents/ui/Bubble.qml
@@ -1,234 +1,244 @@
/*
* 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
import 'matrix-helpers.js' as Helpers
ItemDelegate {
id: upper
+ objectName: 'bubble'
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 || isSelected) ? Kirigami.Theme.activeBackgroundColor : Kirigami.Theme.backgroundColor
}
background: compactMode ? null : backgroundItem
- property var eventSourcePopup: Kirigami.OverlaySheet {
- title: l10n.get('event-source-popup-title')
+ property var eventSourcePopupComp: Component {
+ Kazv.SelfDestroyableOverlaySheet {
+ shouldSelfDestroy: true
+ title: l10n.get('event-source-popup-title')
- Kazv.EventSourceView {
- event: upper.currentEvent
+ Kazv.EventSourceView {
+ event: upper.currentEvent
+ }
}
}
- property var reactionPopup: Kazv.ReactToEventPopup {
- onAccepted: eventView.reactWith(text)
+ property var reactionPopupComp: Component {
+ Kazv.ReactToEventPopup {
+ shouldSelfDestroy: true
+ onAccepted: eventView.reactWith(text)
+ }
}
function getIsEditable(event) {
return event.sender === matrixSdk.userId
&& event.type === 'm.room.message'
&& event.content.msgtype === 'm.text';
}
- property var menu: Menu {
- objectName: 'bubbleContextMenu'
- property list<QtObject> staticItems: [
- Kirigami.Action {
- objectName: 'replyToMenuItem'
- text: l10n.get('event-reply-action')
- onTriggered: setDraftRelation('m.in_reply_to', currentEvent.eventId)
- enabled: event && !event.redacted
- },
- Kirigami.Action {
- objectName: 'editMenuItem'
- text: l10n.get('event-edit-action')
- onTriggered: {
- setDraftRelation('m.replace', currentEvent.eventId);
- replaceDraftRequested(Helpers.getEventBodyForEditing(event));
+ property var menuComp: Component {
+ Menu {
+ objectName: 'bubbleContextMenu'
+ property list<QtObject> staticItems: [
+ Kirigami.Action {
+ objectName: 'replyToMenuItem'
+ text: l10n.get('event-reply-action')
+ onTriggered: setDraftRelation('m.in_reply_to', currentEvent.eventId)
+ enabled: event && !event.redacted
+ },
+ Kirigami.Action {
+ objectName: 'editMenuItem'
+ text: l10n.get('event-edit-action')
+ onTriggered: {
+ setDraftRelation('m.replace', currentEvent.eventId);
+ replaceDraftRequested(Helpers.getEventBodyForEditing(event));
+ }
+ enabled: event && !event.redacted && !event.isLocalEcho && getIsEditable(event)
+ },
+ 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: reactionPopupComp.createObject(applicationWindow().overlay).open()
+ enabled: event && !event.redacted
+ },
+ MenuSeparator {},
+ Kirigami.Action {
+ text: l10n.get('event-view-source')
+ onTriggered: eventSourcePopupComp.createObject(applicationWindow().overlay).open()
}
- enabled: event && !event.redacted && !event.isLocalEcho && getIsEditable(event)
- },
- 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)
+ contentData: Array.from(staticItems).concat(upper.menuContent)
+ onClosed: destroy()
+ }
}
function shouldPopupMenu() {
return !compactMode
}
function maybePopupMenu() {
if (shouldPopupMenu()) {
- menu.popup(parent)
+ menuComp.createObject(parent).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: MouseArea {
Layout.fillWidth: true
Layout.margins: 0
Layout.minimumHeight: Math.min(inReplyToLayout.implicitHeight, Kirigami.Units.gridUnit * 5)
Layout.maximumHeight: Kirigami.Units.gridUnit * 5
clip: true
onClicked: eventListView.goToEvent(bubbleRootLayout.replyToOrAnnotatedEventId)
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
}
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
}
Label {
objectName: 'editedIndicator'
visible: !compactMode && event.isEdited
text: l10n.get('event-edited-indicator')
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 0.8
}
Kazv.EventReadIndicator {
objectName: 'eventReadIndicator'
shouldShow: !compactMode && !event.isLocalEcho
model: event.readers()
}
ToolButton {
id: moreButton
objectName: 'moreButton'
visible: !compactMode
action: popupAction
display: AbstractButton.IconOnly
}
}
}
}
diff --git a/src/contents/ui/ReactToEventPopup.qml b/src/contents/ui/ReactToEventPopup.qml
index ad70036..a3e7a22 100644
--- a/src/contents/ui/ReactToEventPopup.qml
+++ b/src/contents/ui/ReactToEventPopup.qml
@@ -1,59 +1,59 @@
/*
* 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
-Kirigami.OverlaySheet {
+Kazv.SelfDestroyableOverlaySheet {
id: popup
signal accepted(string text)
title: l10n.get('event-react-popup-title')
property var acceptAction: Kirigami.Action {
text: l10n.get('event-react-accept-action')
onTriggered: {
accepted(reactionTextInput.text);
popup.close();
}
}
property var cancelAction: Kirigami.Action {
text: l10n.get('event-react-cancel-action')
onTriggered: {
popup.close();
}
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: l10n.get('event-react-with-prompt')
}
TextField {
id: reactionTextInput
Layout.fillWidth: true
}
RowLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Button {
action: acceptAction
}
Button {
action: cancelAction
}
}
}
}
diff --git a/src/contents/ui/SelfDestroyableOverlaySheet.qml b/src/contents/ui/SelfDestroyableOverlaySheet.qml
new file mode 100644
index 0000000..cfd4ed3
--- /dev/null
+++ b/src/contents/ui/SelfDestroyableOverlaySheet.qml
@@ -0,0 +1,21 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2024 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
+
+Kirigami.OverlaySheet {
+ property var shouldSelfDestroy: false
+
+ onSheetOpenChanged: {
+ if (!sheetOpen && shouldSelfDestroy) {
+ destroy();
+ }
+ }
+}
diff --git a/src/contents/ui/event-types/Image.qml b/src/contents/ui/event-types/Image.qml
index ca3c63d..dc9a30b 100644
--- a/src/contents/ui/event-types/Image.qml
+++ b/src/contents/ui/event-types/Image.qml
@@ -1,105 +1,108 @@
/*
* 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 moe.kazv.mxc.kazv 0.0 as MK
import org.kde.kirigami 2.13 as Kirigami
import '.' as Types
import '..' as Kazv
Simple {
id: upper
property var gender: 'neutral'
property var body: event.content.body
property var curEvent: event
property var thumbnailFile: imageInfo.thumbnail_file || {}
property var thumbnailInfo: imageInfo.thumbnail_info || {}
property var thumbnailWidth: thumbnailInfo.w
property var thumbnailHeight: thumbnailInfo.h
property var imageInfo: event.content.info || {}
property var imageFile: event.content.file || {}
property var imageWidth: imageInfo.w
property var imageHeight: imageInfo.h
property var imageMxcUri: fileHandler.encryptedFileMxcUri || fileHandler.unencryptedFileMxcUri
property var innerContentWidth: upper.contentMaxWidth - bubble.bubbleSpacing
property var jobManager: kazvIOManager
property var mtxSdk: matrixSdk
property var isSticker: event.type === 'm.sticker'
Kazv.FileHandler {
id: fileHandler
eventContent: event.content
autoCache: true
kazvIOManager: upper.jobManager
matrixSdk: upper.mtxSdk
}
Types.MediaBubble {
id: bubble
eventContent: event.content
property var label: Kazv.SelectableText {
Layout.fillWidth: true
wrapMode: Text.Wrap
text: isSticker ? '' : l10n.get('event-message-image-sent', { gender, body })
}
property var image: Image {
id: image
Layout.fillWidth: true
Layout.preferredWidth: calcImageWidth()
Layout.preferredHeight: width < calcImageWidth() ? calcImageHeight() / calcImageWidth() * width : calcImageHeight()
source: fileHandler.kazvIOJob ? "" : fileHandler.cachedFile
horizontalAlignment: Image.AlignLeft
fillMode: Image.PreserveAspectFit
}
property var canAddSticker: !!event.content.url
- property var addStickerPopup: Kazv.AddStickerPopup {
- stickerPackList: matrixSdk.stickerPackList()
- event: upper.curEvent
+ property var addStickerPopupComp: Component {
+ Kazv.AddStickerPopup {
+ shouldSelfDestroy: true
+ stickerPackList: matrixSdk.stickerPackList()
+ event: upper.curEvent
+ }
}
property var addStickerAction: Kirigami.Action {
text: l10n.get('media-file-menu-add-sticker-action')
- onTriggered: bubble.addStickerPopup.open()
+ onTriggered: bubble.addStickerPopupComp.createObject(applicationWindow().overlay).open()
}
additionalMenuContent: canAddSticker ? [addStickerAction] : []
ColumnLayout {
id: layout
Layout.fillWidth: true
data: [
bubble.mediaFileMenu,
bubble.label,
bubble.image,
bubble.progressBar
]
}
}
function calcImageWidth() {
const eventSpecWidth = thumbnailWidth || imageWidth
return eventSpecWidth || image.implicitWidth || 1
}
function calcImageHeight() {
const eventSpecHeight = thumbnailHeight || imageHeight
return eventSpecHeight || image.implicitHeight || 1
}
}
diff --git a/src/resources.qrc b/src/resources.qrc
index 5a0a2e1..e97c706 100644
--- a/src/resources.qrc
+++ b/src/resources.qrc
@@ -1,86 +1,87 @@
<RCC>
<qresource prefix="/">
<file alias="main.qml">contents/ui/main.qml</file>
<file alias="PageManager.qml">contents/ui/PageManager.qml</file>
<file alias="LoginPage.qml">contents/ui/LoginPage.qml</file>
<file alias="MainPage.qml">contents/ui/MainPage.qml</file>
<file alias="TabView.qml">contents/ui/TabView.qml</file>
<file alias="Tab.qml">contents/ui/Tab.qml</file>
<file alias="ClosableScrollablePage.qml">contents/ui/ClosableScrollablePage.qml</file>
+ <file alias="SelfDestroyableOverlaySheet.qml">contents/ui/SelfDestroyableOverlaySheet.qml</file>
<file alias="RoomListView.qml">contents/ui/RoomListView.qml</file>
<file alias="RoomListViewItemDelegate.qml">contents/ui/RoomListViewItemDelegate.qml</file>
<file alias="RoomPage.qml">contents/ui/RoomPage.qml</file>
<file alias="RoomTimelineView.qml">contents/ui/RoomTimelineView.qml</file>
<file alias="SendMessageBox.qml">contents/ui/SendMessageBox.qml</file>
<file alias="EventView.qml">contents/ui/EventView.qml</file>
<file alias="EventViewWrapper.qml">contents/ui/EventViewWrapper.qml</file>
<file alias="EventViewCompact.qml">contents/ui/EventViewCompact.qml</file>
<file alias="EventSourceView.qml">contents/ui/EventSourceView.qml</file>
<file alias="Bubble.qml">contents/ui/Bubble.qml</file>
<file alias="MediaFileMenu.qml">contents/ui/MediaFileMenu.qml</file>
<file alias="KazvIOMenu.qml">contents/ui/KazvIOMenu.qml</file>
<file alias="ConfirmUploadPopup.qml">contents/ui/ConfirmUploadPopup.qml</file>
<file alias="StickerPicker.qml">contents/ui/StickerPicker.qml</file>
<file alias="AddStickerPopup.qml">contents/ui/AddStickerPopup.qml</file>
<file alias="ConfirmationOverlay.qml">contents/ui/ConfirmationOverlay.qml</file>
<file alias="event-types/Simple.qml">contents/ui/event-types/Simple.qml</file>
<file alias="event-types/Text.qml">contents/ui/event-types/Text.qml</file>
<file alias="event-types/Emote.qml">contents/ui/event-types/Emote.qml</file>
<file alias="event-types/Notice.qml">contents/ui/event-types/Notice.qml</file>
<file alias="event-types/State.qml">contents/ui/event-types/State.qml</file>
<file alias="event-types/TextTemplate.qml">contents/ui/event-types/TextTemplate.qml</file>
<file alias="event-types/Image.qml">contents/ui/event-types/Image.qml</file>
<file alias="event-types/File.qml">contents/ui/event-types/File.qml</file>
<file alias="event-types/Video.qml">contents/ui/event-types/Video.qml</file>
<file alias="event-types/Audio.qml">contents/ui/event-types/Audio.qml</file>
<file alias="event-types/MediaBubble.qml">contents/ui/event-types/MediaBubble.qml</file>
<file alias="event-types/Redacted.qml">contents/ui/event-types/Redacted.qml</file>
<file alias="event-types/Reaction.qml">contents/ui/event-types/Reaction.qml</file>
<file alias="event-types/Fallback.qml">contents/ui/event-types/Fallback.qml</file>
<file alias="TypingIndicator.qml">contents/ui/TypingIndicator.qml</file>
<file alias="EventReadIndicator.qml">contents/ui/EventReadIndicator.qml</file>
<file alias="SelectableText.qml">contents/ui/SelectableText.qml</file>
<file alias="ReactToEventPopup.qml">contents/ui/ReactToEventPopup.qml</file>
<file alias="FileHandler.qml">contents/ui/FileHandler.qml</file>
<file alias="ActionSettingsPage.qml">contents/ui/ActionSettingsPage.qml</file>
<file alias="CreateRoomPage.qml">contents/ui/CreateRoomPage.qml</file>
<file alias="JoinRoomPage.qml">contents/ui/JoinRoomPage.qml</file>
<file alias="RoomMemberListView.qml">contents/ui/RoomMemberListView.qml</file>
<file alias="RoomMemberListViewItemDelegate.qml">contents/ui/RoomMemberListViewItemDelegate.qml</file>
<file alias="UserNameProvider.qml">contents/ui/UserNameProvider.qml</file>
<file alias="RoomNameProvider.qml">contents/ui/RoomNameProvider.qml</file>
<file alias="AsyncHandler.qml">contents/ui/AsyncHandler.qml</file>
<file alias="UploadFileHelper.qml">contents/ui/UploadFileHelper.qml</file>
<file alias="SettingsPage.qml">contents/ui/SettingsPage.qml</file>
<file alias="settings/ProfileSettings.qml">contents/ui/settings/ProfileSettings.qml</file>
<file alias="settings/CacheSettings.qml">contents/ui/settings/CacheSettings.qml</file>
<file alias="room-settings/RoomSettingsPage.qml">contents/ui/room-settings/RoomSettingsPage.qml</file>
<file alias="room-settings/RoomTagHandler.qml">contents/ui/room-settings/RoomTagHandler.qml</file>
<file alias="room-settings/RoomMemberListPage.qml">contents/ui/room-settings/RoomMemberListPage.qml</file>
<file alias="room-settings/RoomInvitePage.qml">contents/ui/room-settings/RoomInvitePage.qml</file>
<file alias="device-mgmt/Device.qml">contents/ui/device-mgmt/Device.qml</file>
<file alias="device-mgmt/DeviceList.qml">contents/ui/device-mgmt/DeviceList.qml</file>
<file alias="UserPage.qml">contents/ui/UserPage.qml</file>
<file alias="shortcuts/ActionCollection.qml">contents/ui/shortcuts/ActionCollection.qml</file>
<file alias="shortcuts/ActionItem.qml">contents/ui/shortcuts/ActionItem.qml</file>
<file alias="shortcuts/ActionSettings.qml">contents/ui/shortcuts/ActionSettings.qml</file>
<file alias="shortcuts/ShortcutInput.qml">contents/ui/shortcuts/ShortcutInput.qml</file>
<file alias="MessageNotification.qml">contents/ui/MessageNotification.qml</file>
<file alias="Notifier.qml">contents/ui/Notifier.qml</file>
<file alias="l10n.js">js/l10n.js</file>
<file alias="fluent-bundle.js">js/transformed-libs/fluent-bundle.js</file>
<file alias="fluent-sequence.js">js/transformed-libs/fluent-sequence.js</file>
<file alias="fluent-langneg.js">js/transformed-libs/fluent-langneg.js</file>
<file alias="bundled-deps.js">js/transformed-libs/bundled-deps.js</file>
<file alias="global-this.js">js/global-this.js</file>
<file alias="matrix-helpers.js">js/matrix-helpers.js</file>
</qresource>
</RCC>
diff --git a/src/tests/quick-tests/tst_EventView.qml b/src/tests/quick-tests/tst_EventView.qml
index 43bc517..a7c7eed 100644
--- a/src/tests/quick-tests/tst_EventView.qml
+++ b/src/tests/quick-tests/tst_EventView.qml
@@ -1,513 +1,529 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.3
import QtQuick.Layouts 1.15
import QtTest 1.0
import moe.kazv.mxc.kazv 0.0 as MK
import '../../contents/ui' as Kazv
import 'test-helpers.js' as Helpers
import 'test-helpers' as TestHelpers
Item {
id: item
width: 800
height: 600
property var promiseComp: Component {
TestHelpers.MatrixPromiseMock {
}
}
property var activateUserPageCalled: 0
function activateUserPage() {
++activateUserPageCalled;
}
property var timeline: ({
gaps: [],
})
property var room: ({
test_resendMessageCalled: 0,
test_resendMessageLastTxnId: '',
test_removeLocalEchoCalled: 0,
test_removeLocalEchoLastTxnId: '',
test_ensureStateEventCalled: 0,
test_ensureStateEventCalledArgs: [],
test_ensureStateEventPromise: null,
resendMessage: (txnId) => {
++item.room.test_resendMessageCalled;
item.room.test_resendMessageLastTxnId = txnId;
},
removeLocalEcho: (txnId) => {
++item.room.test_removeLocalEchoCalled;
item.room.test_removeLocalEchoLastTxnId = txnId;
},
ensureStateEvent: (type, stateKey) => {
++item.room.test_ensureStateEventCalled;
item.room.test_ensureStateEventCalledArgs = [type, stateKey];
item.room.test_ensureStateEventPromise = promiseComp.createObject(item);
return item.room.test_ensureStateEventPromise;
},
messageById: (_id) => item.textEvent,
member: (_id) => ({}),
})
property var l10n: Helpers.fluentMock
property var matrixSdk: TestHelpers.MatrixSdkMock {
property var userId: '@foo:tusooa.xyz'
}
property var sdkVars: QtObject {
property var userGivenNicknameMap: QtObject {
property var map: ({
'@foo:tusooa.xyz': 'something',
})
}
}
property var kazvIOManager: TestHelpers.KazvIOManagerMock {}
property var localEcho: ({
eventId: '',
sender: '',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'm.text',
body: 'some body',
},
encrypted: false,
isState: false,
unsignedData: {},
isLocalEcho: true,
isSending: true,
isFailed: false,
txnId: 'some-txn-id',
})
property var failedLocalEcho: Object.assign({}, item.localEcho, { isSending: false, isFailed: true, })
property var textEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'm.text',
body: 'some body',
},
formattedTime: '4:06 P.M.',
})
property var htmlEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'm.text',
body: '**some body**',
format: 'org.matrix.custom.html',
formatted_body: '<strong>some body</strong>',
},
formattedTime: '4:06 P.M.',
})
property var replyHtmlEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
replyingToEventId: '$xxx',
content: {
msgtype: 'm.text',
body: '> some quote\n**some body**',
format: 'org.matrix.custom.html',
formatted_body: '<mx-reply><blockquote>some quote</blockquote></mx-reply><strong>some body</strong>',
},
formattedTime: '4:06 P.M.',
})
property var replyNoQuoteHtmlEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
replyingToEventId: '$xxx',
content: {
msgtype: 'm.text',
body: '**some body**',
format: 'org.matrix.custom.html',
formatted_body: '<strong>some body</strong>',
},
formattedTime: '4:06 P.M.',
})
property var redactedEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
content: {},
redacted: true,
formattedTime: '4:06 P.M.',
})
property var imageEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'm.image',
body: 'some body',
},
formattedTime: '4:06 P.M.',
})
property var eventBySomeoneElse: ({
eventId: '',
sender: '@bar:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'm.image',
body: 'some body',
},
formattedTime: '4:06 P.M.',
})
property var eventTextBySomeoneElse: ({
eventId: '',
sender: '@bar:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'm.text',
body: 'some body',
},
formattedTime: '4:06 P.M.',
})
property var ignoredEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
relationType: 'm.replace',
stateKey: '',
content: {
msgtype: 'm.image',
body: 'some body',
},
formattedTime: '4:06 P.M.',
})
property var unknownTypeEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'moe.kazv.mxc.unknown.event',
stateKey: '',
content: {
msgtype: 'm.image',
body: 'some body',
},
})
property var unknownMsgtypeEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'moe.kazv.mxc.unknown.msgtype',
body: 'some body',
},
})
property var cannotDecryptEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'moe.kazv.mxc.cannot.decrypt',
body: 'some body',
},
})
property var sender: ({
membership: 'join',
userId: '@foo:tusooa.xyz',
name: 'foo',
avatarMxcUri: '',
})
property var senderOther: ({
membership: 'join',
userId: '@bar:tusooa.xyz',
name: 'bar',
avatarMxcUri: '',
})
ColumnLayout {
anchors.fill: parent
Kazv.EventView {
Layout.fillWidth: true
id: eventView
event: item.localEcho
sender: item.sender
}
Kazv.EventView {
Layout.fillWidth: true
id: eventViewFailed
event: item.failedLocalEcho
sender: item.sender
}
Kazv.EventView {
Layout.fillWidth: true
id: eventViewText
event: item.textEvent
sender: item.sender
}
Kazv.EventView {
Layout.fillWidth: true
id: eventViewHtml
event: item.htmlEvent
sender: item.sender
}
Kazv.EventView {
Layout.fillWidth: true
id: eventViewHtmlReply
event: item.replyHtmlEvent
sender: item.sender
}
Kazv.EventView {
Layout.fillWidth: true
id: eventViewHtmlReplyNoQuote
event: item.replyNoQuoteHtmlEvent
sender: item.sender
}
Kazv.EventView {
Layout.fillWidth: true
id: eventViewRedacted
event: item.redactedEvent
sender: item.sender
}
Kazv.EventView {
id: eventViewUnknownType
event: item.unknownTypeEvent
sender: item.sender
}
Kazv.EventView {
id: eventViewUnknownMsgtype
event: item.unknownMsgtypeEvent
sender: item.sender
}
Kazv.EventView {
id: eventViewCannotDecrypt
event: item.cannotDecryptEvent
sender: item.sender
}
Kazv.EventView {
id: eventViewCollapseSameSender
event: item.imageEvent
sender: item.sender
prevEvent: item.redactedEvent
}
Kazv.EventView {
id: eventViewCollapseLocalEcho
event: item.localEcho
sender: item.sender
prevEvent: item.redactedEvent
}
Kazv.EventView {
id: eventViewDontCollapseDifferentSender
event: item.imageEvent
sender: item.sender
prevEvent: item.eventBySomeoneElse
}
Kazv.EventView {
id: eventViewDontCollapseLocalEchoDifferentSender
event: item.localEcho
sender: item.sender
prevEvent: item.eventBySomeoneElse
}
Kazv.EventView {
id: eventViewDontCollapseIgnoredEvent
event: item.imageEvent
sender: item.sender
prevEvent: item.ignoredEvent
}
Kazv.EventView {
id: eventViewTextBySomeoneElse
event: item.eventTextBySomeoneElse
sender: item.senderOther
}
}
TestCase {
id: eventViewTest
name: 'EventViewTest'
when: windowShown
function init() {
eventView.event = item.localEcho;
item.room.test_resendMessageCalled = 0;
item.room.test_resendMessageLastTxnId = '';
item.room.test_removeLocalEchoCalled = 0;
item.room.test_removeLocalEchoLastTxnId = '';
item.room.test_ensureStateEventCalled = 0;
item.room.test_ensureStateEventCalledArgs = [];
item.room.test_ensureStateEventPromise = null;
item.activateUserPageCalled = 0;
}
function test_localEcho() {
const indicator = findChild(eventView, 'localEchoIndicator');
verify(indicator);
verify(indicator.source === 'state-sync');
verify(indicator.Accessible.name === l10n.get('event-sending'));
const resendButton = findChild(eventView, 'resendEventButton');
verify(resendButton);
verify(!resendButton.enabled);
verify(!findChild(eventView, 'timeIndicator').visible);
}
function test_localEchoFailedRemove() {
const indicator = findChild(eventViewFailed, 'localEchoIndicator');
verify(indicator);
verify(indicator.visible);
verify(indicator.source === 'state-warning');
verify(indicator.Accessible.name === l10n.get('event-resend'));
- const deleteAction = findChild(eventViewFailed, 'deleteMenuItem');
+ const menuComp = findChild(eventViewFailed, 'bubble').menuComp;
+ const menu = menuComp.createObject(menuComp.parent);
+ const deleteAction = findChild(menu, 'deleteMenuItem');
deleteAction.trigger();
tryVerify(() => item.room.test_removeLocalEchoCalled === 1);
tryVerify(() => item.room.test_removeLocalEchoLastTxnId === 'some-txn-id');
verify(item.room.test_resendMessageCalled === 0);
verify(item.room.test_resendMessageLastTxnId === '');
}
function test_sentMessage() {
const indicator = findChild(eventViewText, 'localEchoIndicator');
verify(indicator);
verify(!indicator.visible);
verify(findChild(eventViewText, 'timeIndicator').visible);
}
function test_htmlMessage() {
const text = findChild(eventViewHtml, 'textEventContent');
verify(text.textFormat === TextEdit.RichText);
verify(text.text.includes('some body'));
}
function test_htmlMessageReply() {
const text = findChild(eventViewHtmlReply, 'textEventContent');
verify(text.textFormat === TextEdit.RichText);
verify(!text.text.includes('<blockquote>'));
}
function test_htmlMessageReplyNoQuote() {
const text = findChild(eventViewHtmlReplyNoQuote, 'textEventContent');
verify(text.textFormat === TextEdit.RichText);
verify(!text.text.includes('<blockquote>'));
verify(text.text.includes('some body'));
}
function test_menuText() {
- const menu = findChild(eventViewText, 'bubbleContextMenu');
+ const menuComp = findChild(eventViewText, 'bubble').menuComp;
+ const menu = menuComp.createObject(menuComp.parent);
verify(menu);
tryVerify(() => findChild(menu, 'replyToMenuItem').enabled);
tryVerify(() => findChild(menu, 'editMenuItem').enabled);
tryVerify(() => findChild(menu, 'deleteMenuItem').enabled);
}
function test_menuRedacted() {
- const menu = findChild(eventViewRedacted, 'bubbleContextMenu');
+ const menuComp = findChild(eventViewRedacted, 'bubble').menuComp;
+ const menu = menuComp.createObject(menuComp.parent);
verify(menu);
tryVerify(() => !findChild(menu, 'replyToMenuItem').enabled);
tryVerify(() => !findChild(menu, 'deleteMenuItem').enabled);
}
function test_fallback() {
verify(findChild(eventViewUnknownType, 'fallbackIcon'));
verify(findChild(eventViewUnknownType, 'textEventContent').text ===
l10n.get('event-fallback-text', { type: unknownTypeEvent.type }));
verify(findChild(eventViewUnknownMsgtype, 'fallbackIcon'));
verify(findChild(eventViewUnknownMsgtype, 'textEventContent').text ===
l10n.get('event-msgtype-fallback-text', { msgtype: unknownMsgtypeEvent.content.msgtype }));
verify(findChild(eventViewCannotDecrypt, 'fallbackIcon'));
verify(findChild(eventViewCannotDecrypt, 'textEventContent').text ===
l10n.get('event-cannot-decrypt-text'));
}
function test_collapseSender() {
verify(!findChild(eventViewCollapseSameSender, 'senderAvatar').visible);
verify(findChild(eventViewCollapseSameSender, 'senderCollapsedPlaceholder').visible);
verify(!findChild(eventViewCollapseLocalEcho, 'senderAvatar').visible);
verify(findChild(eventViewCollapseLocalEcho, 'senderCollapsedPlaceholder').visible);
verify(findChild(eventViewDontCollapseDifferentSender, 'senderAvatar').visible);
verify(!findChild(eventViewDontCollapseDifferentSender, 'senderCollapsedPlaceholder').visible);
verify(findChild(eventViewDontCollapseLocalEchoDifferentSender, 'senderAvatar').visible);
verify(!findChild(eventViewDontCollapseLocalEchoDifferentSender, 'senderCollapsedPlaceholder').visible);
verify(findChild(eventViewDontCollapseIgnoredEvent, 'senderAvatar').visible);
verify(!findChild(eventViewDontCollapseIgnoredEvent, 'senderCollapsedPlaceholder').visible);
}
function test_eventByOther() {
- const menu = findChild(eventViewTextBySomeoneElse, 'bubbleContextMenu');
+ const menuComp = findChild(eventViewTextBySomeoneElse, 'bubble').menuComp;
+ const menu = menuComp.createObject(menuComp.parent);
verify(menu);
tryVerify(() => !findChild(menu, 'editMenuItem').enabled);
}
+ function test_menuSelfDestruct() {
+ const menuComp = findChild(eventViewTextBySomeoneElse, 'bubble').menuComp;
+ const menu = menuComp.createObject(menuComp.parent);
+ verify(menu);
+ menu.popup(item);
+ menu.dismiss();
+ const menu2 = menuComp.createObject(menuComp.parent);
+ menu2.popup(item);
+ menu2.dismiss();
+ }
+
function test_userLink() {
const text = findChild(eventViewText, 'textEventContent');
text.linkActivated('https://matrix.to/#/@mew:example.com');
tryVerify(() => item.room.test_ensureStateEventCalled);
verify(Helpers.deepEqual(
item.room.test_ensureStateEventCalledArgs,
['m.room.member', '@mew:example.com']));
item.room.test_ensureStateEventPromise.resolve(true, {});
tryVerify(() => item.activateUserPageCalled);
}
function test_senderNameOverrided() {
const name = findChild(eventViewText, 'userNicknameText');
compare(name.text, l10n.get(
'user-name-overrided',
{ overridedName: 'something', globalName: 'foo' }));
}
function test_senderNameNotOverrided() {
const name = findChild(eventViewTextBySomeoneElse, 'userNicknameText');
compare(name.text, 'bar');
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Oct 29, 2:34 AM (4 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1797427
Default Alt Text
(39 KB)
Attached To
Mode
rK kazv
Attached
Detach File
Event Timeline