Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F85806495
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
75 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 7601d85..90e5530 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,135 +1,269 @@
+function(kazv_add_qml_files)
+ set(options "")
+ set(oneValueArgs BASE_DIRECTORY)
+ set(multiValueArgs SOURCES)
+ cmake_parse_arguments(PARSE_ARGV 0 kazv_add_qml_files "${options}" "${oneValueArgs}" "${multiValueArgs}")
+
+ set(target "${kazv_add_qml_files_UNPARSED_ARGUMENTS}")
+ foreach(source ${kazv_add_qml_files_SOURCES})
+ message(STATUS "Adding source: ${kazv_add_qml_files_BASE_DIRECTORY} / ${source}")
+ set_source_files_properties(
+ "${kazv_add_qml_files_BASE_DIRECTORY}/${source}"
+ PROPERTIES
+ QT_RESOURCE_ALIAS "${source}"
+ )
+ qt_target_qml_sources(
+ "${target}"
+ QML_FILES "${kazv_add_qml_files_BASE_DIRECTORY}/${source}"
+ )
+ endforeach()
+endfunction()
+
+
if(WIN32)
set(KAZV_IS_WINDOWS 1)
else()
set(KAZV_IS_WINDOWS 0)
endif()
configure_file(kazv-version.cpp.in kazv-version.cpp)
configure_file(kazv-platform.hpp.in kazv-platform.hpp)
configure_file(kazv-defs.hpp.in kazv-defs.hpp)
set(kazvqmlmodule_SRCS
qt-job-handler.cpp
qt-job.cpp
${CMAKE_CURRENT_BINARY_DIR}/kazv-version.cpp
matrix-sdk.cpp
kazv-session-lock-guard.cpp
matrix-room.cpp
matrix-room-list.cpp
matrix-room-timeline.cpp
matrix-room-pinned-events-timeline.cpp
matrix-room-member.cpp
matrix-room-member-list-model.cpp
matrix-event-reader.cpp
matrix-event-reader-list-model.cpp
matrix-event.cpp
matrix-event-list.cpp
matrix-link.cpp
meta-types.cpp
l10n-provider.cpp
qt-rand-adapter.cpp
helper.cpp
matrix-promise.cpp
kazv-util.cpp
matrix-sticker-pack.cpp
matrix-sticker.cpp
matrix-sticker-pack-list.cpp
matrix-sticker-pack-source.cpp
matrix-user-given-attrs-map.cpp
kazv-abstract-list-model.cpp
qt-promise-handler.cpp
device-mgmt/matrix-device.cpp
device-mgmt/matrix-device-list.cpp
kazv-config.cpp
kazv-path-config.cpp
kazv-io-manager.cpp
kazv-io-job.cpp
kazv-file.cpp
upload-job-model.cpp
kazv-markdown.cpp
kazv-clipboard.cpp
)
ecm_qt_declare_logging_category(kazvqmlmodule_SRCS
HEADER kazv-log.hpp
IDENTIFIER kazvLog
CATEGORY_NAME moe.kazv.mxc.kazv
)
qt_add_qml_module(kazvqmlmodule
STATIC
URI moe.kazv.mxc.kazv
VERSION 0.0
SOURCES ${kazvqmlmodule_SRCS}
)
target_link_libraries(kazvqmlmodule PUBLIC
libkazv::kazvall
Qt${QT_MAJOR_VERSION}::Core
Qt${QT_MAJOR_VERSION}::Network
Threads::Threads
Qt${QT_MAJOR_VERSION}::Gui
Qt${QT_MAJOR_VERSION}::Qml
Qt${QT_MAJOR_VERSION}::Quick
Qt${QT_MAJOR_VERSION}::QuickControls2
Qt${QT_MAJOR_VERSION}::Svg
Qt${QT_MAJOR_VERSION}::Concurrent
Qt${QT_MAJOR_VERSION}::Widgets
KF${KF_MAJOR_VERSION}::ConfigCore KF${KF_MAJOR_VERSION}::KIOCore
KF${KF_MAJOR_VERSION}::Notifications
${CMARK_TARGET_NAME}
)
target_include_directories(kazvqmlmodule PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(kazvqmlmodule PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/device-mgmt)
qt_add_qml_module(kazvshortcutsqmlmodule
STATIC
URI moe.kazv.mxc.kazvshortcuts
VERSION 0.0
SOURCES
shortcuts/shortcut-util.cpp
)
target_link_libraries(kazvshortcutsqmlmodule PUBLIC
libkazv::kazvall
Qt${QT_MAJOR_VERSION}::Core
Qt${QT_MAJOR_VERSION}::Gui
Qt${QT_MAJOR_VERSION}::Qml
)
target_include_directories(kazvshortcutsqmlmodule
PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/shortcuts
)
add_library(kazvprivlib INTERFACE)
target_link_libraries(kazvprivlib
INTERFACE
kazvqmlmodule
kazvqmlmoduleplugin
kazvshortcutsqmlmodule
kazvshortcutsqmlmoduleplugin
)
+qt_add_qml_module(kazvqmlqmlmodule
+ STATIC
+ URI moe.kazv.mxc.kazvqml
+ VERSION 0.0
+)
+kazv_add_qml_files(kazvqmlqmlmodule
+ BASE_DIRECTORY contents/ui
+ SOURCES
+ Main.qml
+ PageManager.qml
+ LoginPage.qml
+ MainPage.qml
+ TabView.qml
+ Tab.qml
+ ClosableScrollablePage.qml
+ SelfDestroyableOverlaySheet.qml
+ RoomListView.qml
+ RoomListViewItemDelegate.qml
+ RoomPage.qml
+ RoomPinnedEventsPage.qml
+ RoomTimelineView.qml
+ SendMessageBox.qml
+ EventView.qml
+ EventViewWrapper.qml
+ EventViewCompact.qml
+ EventSourceView.qml
+ EventHistoryView.qml
+ Bubble.qml
+ MediaFileMenu.qml
+ KazvIOMenu.qml
+ ConfirmUploadPopup.qml
+ StickerPicker.qml
+ AddStickerPopup.qml
+ ConfirmationOverlay.qml
+ event-types/Simple.qml
+ event-types/Text.qml
+ event-types/Emote.qml
+ event-types/Notice.qml
+ event-types/State.qml
+ event-types/TextTemplate.qml
+ event-types/Image.qml
+ event-types/File.qml
+ event-types/Video.qml
+ event-types/Audio.qml
+ event-types/MediaBubble.qml
+ event-types/Redacted.qml
+ event-types/Reaction.qml
+ event-types/Fallback.qml
+ TypingIndicator.qml
+ EventReadIndicator.qml
+ SelectableText.qml
+ ReactToEventPopup.qml
+ FileHandler.qml
+
+ ActionSettingsPage.qml
+ CreateRoomPage.qml
+ JoinRoomPage.qml
+ RoomMemberListView.qml
+ RoomMemberListViewItemDelegate.qml
+ UserNameProvider.qml
+ RoomNameProvider.qml
+ Completion.qml
+
+ AsyncHandler.qml
+ UploadFileHelper.qml
+ SettingsPage.qml
+ settings/ProfileSettings.qml
+ settings/CacheSettings.qml
+
+ room-settings/RoomSettingsPage.qml
+ room-settings/RoomTagHandler.qml
+ room-settings/RoomMemberListPage.qml
+ room-settings/RoomInvitePage.qml
+
+ device-mgmt/Device.qml
+ device-mgmt/DeviceList.qml
+
+ UserPage.qml
+
+ shortcuts/ActionCollection.qml
+ shortcuts/ActionItem.qml
+ shortcuts/ActionSettings.qml
+ shortcuts/ShortcutInput.qml
+
+ MessageNotification.qml
+ Notifier.qml
+ NotificationAction.qml
+ AvatarAdapter.qml
+ FileDialogAdapter.qml
+ FolderDialogAdapter.qml
+ ScrollablePageAdapter.qml
+)
+kazv_add_qml_files(kazvqmlqmlmodule
+ BASE_DIRECTORY js
+ SOURCES
+ l10n.js
+ global-this.js
+ matrix-helpers.js
+)
+
+kazv_add_qml_files(kazvqmlqmlmodule
+ BASE_DIRECTORY js/transformed-libs
+ SOURCES
+ fluent-bundle.js
+ fluent-sequence.js
+ fluent-langneg.js
+ bundled-deps.js
+)
+
+target_link_libraries(kazvqmlqmlmodule PUBLIC kazvprivlib)
+
set(kazv_SRCS
main.cpp
- resources.qrc
l10n/translations.qrc
)
add_executable(kazv ${kazv_SRCS})
target_link_libraries(kazv
PRIVATE
+ kazvqmlqmlmodule
+ kazvqmlqmlmoduleplugin
kazvprivlib
)
set(kazv_INSTALL_ARGS ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
if(NOT kazv_INSTALL_ARGS)
set(kazv_INSTALL_ARGS ${KF${KF_MAJOR_VERSION}_INSTALL_TARGETS_DEFAULT_ARGS})
endif()
install(TARGETS kazv ${kazv_INSTALL_ARGS})
add_subdirectory(tests)
diff --git a/src/contents/ui/Bubble.qml b/src/contents/ui/Bubble.qml
index 965327d..d399948 100644
--- a/src/contents/ui/Bubble.qml
+++ b/src/contents/ui/Bubble.qml
@@ -1,262 +1,262 @@
/*
* 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.Window 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: []
property var shouldDisplayTime: displayTime || (!compactMode && !event.isLocalEcho)
property var eventPinned: event.eventId && pinnedEvents.eventIds.includes(event.eventId)
topPadding: 0
bottomPadding: 0
readonly property var bubbleSpacing: leftPadding + rightPadding
readonly property double replyMaxHeight: Kirigami.Units.gridUnit * 7
Layout.fillWidth: true
property var backgroundItem: Rectangle {
anchors.fill: parent
color: (hovered || isSelected) ? Kirigami.Theme.activeBackgroundColor : Kirigami.Theme.backgroundColor
}
background: compactMode ? null : backgroundItem
property var eventSourcePopupComp: Component {
Kazv.SelfDestroyableOverlaySheet {
shouldSelfDestroy: true
title: l10n.get('event-source-popup-title')
Kazv.EventSourceView {
Layout.preferredWidth: Math.min(Kirigami.Units.gridUnit * 40, Window.width)
event: upper.currentEvent
}
}
}
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 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
},
Kirigami.Action {
objectName: 'pinUnpinMenuItem'
enabled: !event.isLocalEcho
text: eventPinned ? l10n.get('event-unpin-action') : l10n.get('event-pin-action')
onTriggered: eventPinned ? unpinEventRequested(event.eventId) : pinEventRequested(event.eventId)
},
MenuSeparator {},
Kirigami.Action {
text: l10n.get('event-view-source')
onTriggered: eventSourcePopupComp.createObject(applicationWindow().overlay).open()
},
Kirigami.Action {
objectName: 'viewHistoryMenuItem'
text: l10n.get('event-view-history')
enabled: event && !!event.isEdited
onTriggered: viewEventHistoryRequested(event.eventId)
}
]
contentData: Array.from(staticItems).concat(upper.menuContent)
onClosed: destroy()
}
}
function shouldPopupMenu() {
return !compactMode
}
function maybePopupMenu() {
if (shouldPopupMenu()) {
menuComp.createObject(parent).popup(parent)
}
}
contentItem: RowLayout {
id: bubbleRootLayout
property var replyToOrAnnotatedEventId: currentEvent.replyingToEventId || (currentEvent.relationType === 'm.annotation' ? currentEvent.relatedEventId : '')
ColumnLayout {
Layout.fillWidth: true
- property var inReplyTo: MouseArea {
+ MouseArea {
+ visible: bubbleRootLayout.replyToOrAnnotatedEventId && !compactMode
Layout.fillWidth: true
Layout.margins: 0
Layout.preferredHeight: inReplyToLayout.implicitHeight
Layout.maximumHeight: upper.replyMaxHeight
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'
+ source: Qt.resolvedUrl('EventViewCompact.qml')
active: bubbleRootLayout.replyToOrAnnotatedEventId && !compactMode
asynchronous: true
- Layout.preferredHeight: inReplyToLayout.item?.height
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] : []),
- ]
+
+ ColumnLayout {
+ id: children
+ data: upper.children
+ }
}
property var popupAction: Kirigami.Action {
id: popupAction
text: l10n.get('event-popup-action')
icon.name: '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: Kirigami.Units.toolTipDelay
ToolTip.timeout: Helpers.toolTipTimeout
ToolTip.visible: hoverHandlerEncIcon.hovered
HoverHandler { id: hoverHandlerEncIcon }
}
ToolButton {
objectName: 'localEchoIndicator'
visible: !!event.isLocalEcho
hoverEnabled: true
enabled: event.isFailed
icon.name: 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: Kirigami.Units.toolTipDelay
ToolTip.timeout: Helpers.toolTipTimeout
ToolTip.visible: hovered
onClicked: {
room.resendMessage(event.txnId);
}
}
Label {
objectName: 'timeIndicator'
visible: shouldDisplayTime
text: event.formattedTime
ToolTip.text: event.formattedDateTime
ToolTip.delay: Kirigami.Units.toolTipDelay
ToolTip.timeout: Helpers.toolTipTimeout
ToolTip.visible: hoverHandlerTime.hovered
HoverHandler { id: hoverHandlerTime }
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/main.qml b/src/contents/ui/Main.qml
similarity index 99%
rename from src/contents/ui/main.qml
rename to src/contents/ui/Main.qml
index cff94fc..19c74e4 100644
--- a/src/contents/ui/main.qml
+++ b/src/contents/ui/Main.qml
@@ -1,346 +1,346 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick
import org.kde.kirigami as Kirigami
import QtQuick.Layouts
import QtQuick.Controls
import moe.kazv.mxc.kazv as MK
import '.' as Kazv
import 'shortcuts' as KazvShortcuts
import 'l10n.js' as L10n
Kirigami.ApplicationWindow {
id: root
property var kazvIOManager: MK.KazvIOManager {
cacheDirectory: kazvConfig.cacheDirectory
}
property var matrixSdk: MK.MatrixSdk {
onLoginSuccessful: {
switchToMainPage();
recordLastSession();
}
onLoginFailed: {
console.log("Login Failed");
showPassiveNotification(l10n.get('login-page-request-failed-prompt', { errorCode, errorMsg }));
}
onDiscoverFailed: {
console.log("Discover Failed");
showPassiveNotification(l10n.get('login-page-discover-failed-prompt', { errorCode, errorMsg }));
}
onSessionChanged: {
console.log('session changed');
reloadSdkVariables();
}
onLogoutSuccessful: {
root.loggedIn = false;
root.mainPage = Qt.createComponent("MainPage.qml").createObject(root);
pageStack.clear();
pushLoginPage();
}
onLogoutFailed: {
console.warn('Logout failed');
showPassiveNotification(l10n.get('logout-failed-prompt', { errorCode, errorMsg }));
}
}
property var loggedIn: !!matrixSdk.token
property var sdkVars: QtObject {
property var roomList: matrixSdk.roomList()
property var userGivenNicknameMap: matrixSdk.userGivenNicknameMap()
property string currentRoomId: ''
}
property var l10nProvider: MK.L10nProvider {
}
property var kazvConfig: MK.KazvConfig {
}
function initializeL10n () {
const availableLocales = l10nProvider.availableLocaleCodes();
console.log('available locales:', availableLocales);
const desiredLanguages = [Qt.uiLanguage];
console.log('ui language is: ', desiredLanguages);
const defaultLocale = 'en';
const wantedLocales = L10n.negotiateLanguages(desiredLanguages, availableLocales, defaultLocale);
console.log('wanted locales:', wantedLocales);
const bundles = L10n.generateBundles(wantedLocales, l10nProvider.getFtlData(wantedLocales));
const provider = new L10n.FluentProvider(bundles);
return provider;
}
property var l10n: initializeL10n();
property var confirmLogoutPopup: Kirigami.OverlaySheet {
id: confirmLogoutPopup
title: l10n.get('confirm-logout-popup-title')
ColumnLayout {
Label {
Layout.fillWidth: true
text: l10n.get('confirm-logout-popup-prompt')
}
RowLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignCenter
Button {
text: l10n.get('confirm-logout-popup-accept-button')
onClicked: {
hardLogout();
confirmLogoutPopup.close();
}
}
Button {
text: l10n.get('confirm-logout-popup-cancel-button')
onClicked: confirmLogoutPopup.close();
}
}
}
}
title: l10n.get('app-title')
globalDrawer: Kirigami.GlobalDrawer {
title: l10n.get('global-drawer-title')
titleIcon: "applications-graphics"
property var actionGroupName: 'global-drawer-actions'
actions: [
Kirigami.Action {
objectName: 'switch-account'
text: l10n.get('global-drawer-action-switch-account')
icon.name: 'system-switch-user'
onTriggered: switchAccount()
},
Kirigami.Action {
objectName: 'hard-logout'
text: l10n.get('global-drawer-action-hard-logout')
icon.name: 'application-exit'
onTriggered: confirmLogoutPopup.open()
},
Kirigami.Action {
objectName: 'save'
text: l10n.get('global-drawer-action-save-session')
icon.name: 'document-save'
onTriggered: {
recordLastSession()
matrixSdk.serializeToFile()
}
},
Kirigami.Action {
objectName: 'configure-shortcuts'
text: l10n.get('global-drawer-action-configure-shortcuts')
icon.name: 'configure-shortcuts'
onTriggered: {
pushActionSettingsPage();
}
},
Kirigami.Action {
objectName: 'settings'
text: l10n.get('global-drawer-action-settings')
icon.name: 'configure'
onTriggered: {
pushSettingsPage();
}
},
Kirigami.Action {
objectName: 'create-room'
enabled: root.loggedIn
icon.name: 'contact-new'
text: l10n.get('global-drawer-action-create-room')
onTriggered: {
pushCreateRoomPage();
}
},
Kirigami.Action {
objectName: 'join-room'
enabled: root.loggedIn
text: l10n.get('global-drawer-action-join-room')
icon.name: 'list-add-user'
onTriggered: {
pushJoinRoomPage();
}
}
]
}
contextDrawer: Kirigami.ContextDrawer {
id: contextDrawer
}
property var actionCollection: KazvShortcuts.ActionCollection {
id: globalActionCollection
children: [
root.globalDrawer,
root.contextDrawer,
]
shortcutsConfig: kazvConfig
}
property var notifier: Kazv.Notifier {
}
pageStack.initialPage: Qt.resolvedUrl("LoginPage.qml")
property var mainPage: Kazv.MainPage {}
property var emptyPage: Kirigami.Page {
title: l10n.get('empty-room-page-title')
Label {
text: l10n.get('empty-room-page-description')
}
}
signal switchToRoomRequested(string roomId)
property var pageManager: Kazv.PageManager {
pageStack: root.pageStack
sdkVars: root.sdkVars
main: root
}
function activateRoomSettingsPage(room) {
pageStack.push(Qt.resolvedUrl("room-settings/RoomSettingsPage.qml"), {
room
});
}
function activateRoomPinnedEventsPage(room) {
pageStack.push(Qt.resolvedUrl('RoomPinnedEventsPage.qml'), {
room
});
}
function activateRoomInvitePage(room) {
pageStack.push(Qt.resolvedUrl("room-settings/RoomInvitePage.qml"), {
room
});
}
function activateUserPage(user, room, userId) {
- pageStack.push(Qt.resolvedUrl("qrc:///UserPage.qml"), {
+ pageStack.push(Qt.resolvedUrl("UserPage.qml"), {
userId: userId || user.userId,
user,
room
});
}
function switchToMainPage() {
pageStack.replace([root.mainPage])
pageStack.currentIndex = 0
}
function switchToLoginPage() {
pageStack.replace([Qt.resolvedUrl("LoginPage.qml")])
pageStack.currentIndex = 0
}
function pushLoginPage() {
pageStack.push(Qt.resolvedUrl("LoginPage.qml"), {
isSwitchingAccount: true,
});
}
function pushActionSettingsPage() {
pageStack.push(Qt.resolvedUrl("ActionSettingsPage.qml"), {
actionsToConfigure: globalActionCollection.allActions(),
});
}
function pushSettingsPage() {
pageStack.push(Qt.resolvedUrl("SettingsPage.qml"));
}
function pushCreateRoomPage() {
pageStack.push(Qt.resolvedUrl("CreateRoomPage.qml"), {});
}
function pushJoinRoomPage() {
pageStack.push(Qt.resolvedUrl("JoinRoomPage.qml"), {});
}
function sessionNameFor(userId, deviceId) {
return userId + '/' + deviceId;
}
function getSessionLoadError(res, sessionName) {
switch (res) {
case MK.MatrixSdk.SessionNotFound:
return l10n.get('session-load-failure-not-found', { sessionName });
case MK.MatrixSdk.SessionFormatUnknown:
return l10n.get('session-load-failure-format-unknown', { sessionName });
case MK.MatrixSdk.SessionCannotBackup:
return l10n.get('session-load-failure-cannot-backup', { sessionName });
case MK.MatrixSdk.SessionLockFailed:
return l10n.get('session-load-failure-lock-failed', { sessionName });
case MK.MatrixSdk.SessionCannotOpenFile:
return l10n.get('session-load-failure-cannot-open-file', { sessionName });
case MK.MatrixSdk.SessionDeserializeFailed:
return l10n.get('session-load-failure-deserialize-failed', { sessionName });
default:
console.error('loadSession: Encountered an unknown error code');
}
}
function loadSession(sessionName) {
const res = matrixSdk.loadSession(sessionName);
if (res === MK.MatrixSdk.SessionLoadSuccess) {
console.log('load session successful');
switchToMainPage();
recordLastSession();
} else {
showPassiveNotification(getSessionLoadError(res, sessionName));
}
return res;
}
function loadLastSession() {
console.log('last session is:', kazvConfig.lastSession);
return loadSession(kazvConfig.lastSession);
}
function recordLastSession() {
kazvConfig.lastSession = sessionNameFor(matrixSdk.userId, matrixSdk.deviceId);
}
function reloadSdkVariables() {
sdkVars.roomList = matrixSdk.roomList();
sdkVars.userGivenNicknameMap = matrixSdk.userGivenNicknameMap();
sdkVars.currentRoomId = '';
}
function switchAccount() {
pushLoginPage();
}
function hardLogout() {
matrixSdk.logout();
}
Component.onCompleted: {
actionCollection.setupShortcuts();
loadLastSession();
}
onClosing: {
kazvIOManager.clearJobs()
}
}
diff --git a/src/contents/ui/event-types/Simple.qml b/src/contents/ui/event-types/Simple.qml
index 756b262..c05d9e9 100644
--- a/src/contents/ui/event-types/Simple.qml
+++ b/src/contents/ui/event-types/Simple.qml
@@ -1,131 +1,131 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-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
import '..' as Kazv
ColumnLayout {
+ objectName: 'eventViewMainItem'
property var event
property var sender
- default property var children
+ default property alias children: container.data
property var isSelected
/// The item shown after the username, in compact mode
property var summaryItem: Item {}
property var nameProvider: Kazv.UserNameProvider {
user: sender
}
property var senderNickname: nameProvider.name
property var contentMaxWidth: {
(parent.width
// avatar size and margins
- iconSize
- 2 * Kirigami.Units.largeSpacing
- 2 * Kirigami.Units.largeSpacing
)
}
property var shouldCollapseSender: (
!compactMode
&& prevEventType
&& prevEventType !== 'ignore'
&& (
event.sender === prevEvent.sender
// local echo does not have a sender, will use the current user
|| (!!event.isLocalEcho && !!prevEvent.isLocalEcho)
|| (!!event.isLocalEcho && prevEvent.sender === matrixSdk.userId)
)
)
id: layout
RowLayout {
// width: parent.width
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.largeSpacing
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.topMargin: shouldCollapseSender ? 0 : Kirigami.Units.largeSpacing
Layout.bottomMargin: 0
Kazv.AvatarAdapter {
id: avatar
objectName: 'senderAvatar'
Layout.alignment: Qt.AlignTop
sourceSize.width: iconSize
sourceSize.height: iconSize
source: sender.avatarMxcUri ? matrixSdk.mxcUriToHttp(sender.avatarMxcUri) : ''
name: senderNickname
visible: !compactMode && !shouldCollapseSender
TapHandler {
onTapped: {
activateUserPage(sender, room, event.sender);
}
}
}
Item {
objectName: 'senderCollapsedPlaceholder'
visible: shouldCollapseSender
Layout.preferredWidth: iconSize
Layout.preferredHeight: 1
}
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: Kirigami.Units.largeSpacing
RowLayout {
visible: !shouldCollapseSender
Kazv.AvatarAdapter {
id: avatarCompact
Layout.preferredWidth: Kirigami.Units.iconSizes.sizeForLabels
Layout.preferredHeight: Kirigami.Units.iconSizes.sizeForLabels
sourceSize.width: Kirigami.Units.iconSizes.sizeForLabels
sourceSize.height: Kirigami.Units.iconSizes.sizeForLabels
source: sender.avatarMxcUri ? matrixSdk.mxcUriToHttp(sender.avatarMxcUri) : ''
name: senderNickname
visible: compactMode
}
Label {
id: userNicknameText
objectName: 'userNicknameText'
text: senderNickname
Layout.fillWidth: !compactMode
property var reasonableWidth: Math.max(contentWidth, Kirigami.Units.gridUnit)
TapHandler {
enabled: !compactMode
onTapped: {
if (point.position.x <= userNicknameText.reasonableWidth) {
mentionUserRequested(sender.userId);
}
}
}
}
RowLayout {
objectName: 'compactModeSummary'
visible: compactMode
data: [summaryItem]
}
}
RowLayout {
id: container
Layout.fillWidth: true
- data: layout.children
}
}
}
}
diff --git a/src/contents/ui/room-settings/RoomSettingsPage.qml b/src/contents/ui/room-settings/RoomSettingsPage.qml
index 95c8d84..b798902 100644
--- a/src/contents/ui/room-settings/RoomSettingsPage.qml
+++ b/src/contents/ui/room-settings/RoomSettingsPage.qml
@@ -1,362 +1,362 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 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.20 as Kirigami
import moe.kazv.mxc.kazv 0.0 as MK
import '../matrix-helpers.js' as Helpers
import '..' as Kazv
import '.' as RoomSettings
Kazv.ClosableScrollablePage {
id: roomSettingsPage
property var room
property var roomNameProvider: Kazv.RoomNameProvider {
room: roomSettingsPage.room
}
property var roomDisplayName: roomNameProvider.name
property var customTagIds: room.tagIds.filter(k => k.startsWith('u.'))
property var editingTopic: false
property var submittingTopic: false
property var submitTopic: Kazv.AsyncHandler {
trigger: () => {
roomSettingsPage.submittingTopic = true;
return roomSettingsPage.room.setTopic(roomTopicEdit.text);
}
onResolved: (success, data) => {
roomSettingsPage.submittingTopic = false;
if (success) {
roomSettingsPage.editingTopic = false;
} else {
showPassiveNotification(l10n.get('room-settings-set-topic-failed-prompt', {
room: room.roomId,
errorCode: data.errorCode,
errorMsg: data.error,
}));
}
}
}
title: l10n.get('room-settings-page-title', { room: roomDisplayName })
function tagIdToName(tagId) {
return tagId.slice(2);
}
property var encryptionPopup: Kirigami.PromptDialog {
parent: roomSettingsPage.overlay
objectName: 'encryptionPopup'
title: l10n.get('room-settings-enable-encryption-prompt-dialog-title')
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
ColumnLayout {
Label {
Layout.fillWidth: true
text: l10n.get('room-settings-enable-encryption-prompt-dialog-prompt')
wrapMode: Text.Wrap
}
}
onAccepted: enableEncryption.call()
}
property var enablingEncryption: false
property var enableEncryption: Kazv.AsyncHandler {
trigger: () => {
roomSettingsPage.enablingEncryption = true;
return room.sendStateEvent({
type: 'm.room.encryption',
state_key: '',
content: {
algorithm: 'm.megolm.v1.aes-sha2',
},
});
}
onResolved: (success, data) => {
roomSettingsPage.enablingEncryption = false;
if (success) {
showPassiveNotification(l10n.get('room-settings-encryption-enabled-notification'));
} else {
showPassiveNotification(l10n.get('room-settings-encryption-failed-to-enable-notification', { errorCode: data.errorCode, errorMsg: data.error }));
}
}
}
property var tagHandler: RoomSettings.RoomTagHandler {
room: roomSettingsPage.room
}
property var members: room.members()
property var avatarCount: 4
ColumnLayout {
RowLayout {
Layout.alignment: Qt.AlignHCenter
Repeater {
objectName: 'roomMembersAvatarsRepeater'
model: Math.min(members.count, roomSettingsPage.avatarCount)
Kazv.AvatarAdapter {
property var member: members.at(index)
property var nameProvider: Kazv.UserNameProvider {
user: member
}
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
source: member.avatarMxcUri ?
matrixSdk.mxcUriToHttp(member.avatarMxcUri) : ''
sourceSize.width: Kirigami.Units.iconSizes.huge
sourceSize.height: Kirigami.Units.iconSizes.huge
name: nameProvider.name
}
}
Kirigami.Icon {
objectName: 'roomMembersAvatarsMore'
Layout.preferredHeight: Kirigami.Units.iconSizes.huge
Layout.preferredWidth: Kirigami.Units.iconSizes.huge
source: 'go-next-skip'
visible: members.count > roomSettingsPage.avatarCount
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignHCenter
Label {
objectName: 'roomNameLabel'
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Kirigami.Units.gridUnit
text: !!room.name ? room.name : room.roomId
}
Label {
objectName: 'roomIdLabel'
Layout.alignment: Qt.AlignHCenter
font.pixelSize: Math.ceil(Kirigami.Units.gridUnit * 0.8)
text: room.roomId
visible: !!room.name
}
}
RowLayout {
visible: !roomSettingsPage.editingTopic
Kazv.SelectableText {
objectName: 'roomTopicLabel'
text: room.topic || l10n.get('room-settings-topic-missing')
font.italic: !room.topic
wrapMode: Text.Wrap
Kirigami.Theme.colorGroup: !!room.topic ? Kirigami.Theme.Normal : Kirigami.Theme.Inactive
Layout.fillWidth: true
}
Button {
Layout.alignment: Qt.AlignTop
objectName: 'editTopicButton'
text: l10n.get('room-settings-edit-topic-action')
onClicked: {
roomTopicEdit.text = room.topic;
roomSettingsPage.editingTopic = true;
}
}
}
RowLayout {
visible: roomSettingsPage.editingTopic
TextArea {
id: roomTopicEdit
objectName: 'roomTopicEdit'
Layout.fillWidth: true
wrapMode: Text.Wrap
}
Button {
Layout.alignment: Qt.AlignTop
objectName: 'saveTopicButton'
enabled: !roomSettingsPage.submittingTopic
text: l10n.get('room-settings-save-topic-action')
onClicked: roomSettingsPage.submitTopic.call()
}
Button {
Layout.alignment: Qt.AlignTop
objectName: 'discardTopicButton'
enabled: !roomSettingsPage.submittingTopic
text: l10n.get('room-settings-discard-topic-action')
onClicked: roomSettingsPage.editingTopic = false
}
}
Button {
text: l10n.get('room-settings-members-action')
icon.name: 'im-user'
Layout.fillWidth: true
- onClicked: pageStack.push(Qt.resolvedUrl('qrc:/room-settings/RoomMemberListPage.qml'), { room: room, members: room.members() })
+ onClicked: pageStack.push(Qt.resolvedUrl('RoomMemberListPage.qml'), { room: room, members: room.members() })
}
Button {
text: l10n.get('room-settings-banned-members-action')
icon.name: 'im-kick-user'
icon.color: Kirigami.Theme.negativeTextColor
Layout.fillWidth: true
- onClicked: pageStack.push(Qt.resolvedUrl('qrc:/room-settings/RoomMemberListPage.qml'), { room: room, members: room.bannedMembers() })
+ onClicked: pageStack.push(Qt.resolvedUrl('RoomMemberListPage.qml'), { room: room, members: room.bannedMembers() })
}
Label {
objectName: 'encryptionIndicator'
Layout.fillWidth: true
text: !!room.encrypted ? l10n.get('room-settings-encrypted') : l10n.get('room-settings-not-encrypted')
}
Button {
objectName: 'enableEncryptionButton'
Layout.fillWidth: true
visible: !room.encrypted
enabled: !roomSettingsPage.enablingEncryption
text: l10n.get('room-settings-enable-encryption-action')
onClicked: encryptionPopup.open()
}
RowLayout {
Layout.fillWidth: true
Label {
text: l10n.get('room-settings-tags')
Layout.fillWidth: true
}
CheckBox {
Layout.alignment: Qt.AlignRight
text: l10n.get('room-settings-favourited')
checkable: true
checked: tagHandler.hasTag('m.favourite')
enabled: tagHandler.available
onToggled: tagHandler.toggleTag('m.favourite')
}
}
ListView {
model: customTagIds.length
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: childrenRect.height
delegate: Kirigami.SwipeListItem {
required property var index
contentItem: RowLayout {
Label {
Layout.fillWidth: true
text: tagIdToName(customTagIds[index])
}
}
actions: [
Kirigami.Action {
enabled: tagHandler.available
icon.name: 'list-remove-symbolic'
text: l10n.get('room-settings-remove-tag')
onTriggered: tagHandler.toggleTag(customTagIds[index])
}
]
}
}
RowLayout {
TextField {
id: newTagName
enabled: tagHandler.available
text: ''
Layout.fillWidth: true
}
Button {
enabled: tagHandler.available
text: l10n.get('room-settings-add-tag')
icon.name: 'list-add'
onClicked: {
tagHandler.toggleTag('u.' + newTagName.text)
}
}
Connections {
target: tagHandler
function onTagAdded(tagId) {
if (tagId === 'u.' + newTagName.text) {
newTagName.text = '';
}
}
}
}
Button {
text: l10n.get('room-invite-action')
icon.name: 'list-add-user'
Layout.fillWidth: true
onClicked: {
activateRoomInvitePage(roomSettingsPage.room);
}
}
Button {
objectName: 'leaveRoomButton'
text: l10n.get('room-leave-action')
icon.name: 'im-ban-kick-user'
icon.color: Kirigami.Theme.negativeTextColor
Layout.fillWidth: true
onClicked: {
confirmLeaveOverlay.open();
}
}
Button {
objectName: 'forgetRoomButton'
text: l10n.get('room-forget-action')
Layout.fillWidth: true
visible: room.membership === MK.MatrixRoom.Leave
onClicked: {
confirmForgetOverlay.open();
}
}
}
property var confirmLeaveOverlay: Kazv.ConfirmationOverlay {
objectName: 'confirmLeaveRoomPopup'
parent: roomSettingsPage.overlay
title: l10n.get('room-leave-confirm-popup-title')
message: l10n.get('room-leave-confirm-popup-message')
confirmActionText: l10n.get('room-leave-confirm-popup-confirm-action')
cancelActionText: l10n.get('room-leave-confirm-popup-cancel-action')
onAccepted: leaveRoomHandler.call()
}
property var leaveRoomHandler: Kazv.AsyncHandler {
trigger: () => room.leaveRoom()
onResolved: {
if (success) {
showPassiveNotification(l10n.get('leave-room-success-prompt', { room: room.roomId }));
inviteOverlay.close();
} else {
showPassiveNotification(l10n.get('leave-room-failed-prompt', { room: room.roomId, errorCode: data.errorCode, errorMsg: data.error }));
}
}
}
property var confirmForgetOverlay: Kazv.ConfirmationOverlay {
objectName: 'confirmForgetRoomPopup'
parent: roomSettingsPage.overlay
title: l10n.get('room-forget-confirm-popup-title')
message: l10n.get('room-forget-confirm-popup-message')
confirmActionText: l10n.get('room-forget-confirm-popup-confirm-action')
cancelActionText: l10n.get('room-forget-confirm-popup-cancel-action')
onAccepted: forgetRoomHandler.call()
}
property var forgetRoomHandler: Kazv.AsyncHandler {
trigger: () => room.forgetRoom()
onResolved: (success, data) => {
if (success) {
showPassiveNotification(l10n.get('forget-room-success-prompt', { room: room.roomId }));
} else {
showPassiveNotification(l10n.get('forget-room-failed-prompt', { room: room.roomId, errorCode: data.errorCode, errorMsg: data.error }));
}
}
}
}
diff --git a/src/kazv-io-job.hpp b/src/kazv-io-job.hpp
index f83ef6d..568b82d 100644
--- a/src/kazv-io-job.hpp
+++ b/src/kazv-io-job.hpp
@@ -1,158 +1,160 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2022-2023 nannanko <nannanko@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <kazv-defs.hpp>
#include "matrix-room.hpp"
#include "matrix-room-list.hpp"
#include "kazv-file.hpp"
#include <aes-256-ctr.hpp>
#include <QObject>
#include <QPointer>
#include <QString>
#include <QUrl>
#include <KIO/TransferJob>
#include <memory>
#include <optional>
struct KazvIOBaseJobPrivate;
class KazvIOBaseJob : public QObject
{
Q_OBJECT
+ QML_ELEMENT
+ QML_UNCREATABLE("")
Q_PROPERTY(float progress READ progress NOTIFY progressChanged)
std::unique_ptr<KazvIOBaseJobPrivate> m_d;
public:
enum ErrorCode {
/**
* No Error or the signal result has not been emitted yet.
*/
NoError = 0,
/**
* The user actively canceled the job.
*/
UserCancel,
/**
* Open File Error.
*/
OpenFileError,
/**
* Write File Error
*/
WriteFileError,
/**
* Errors caused by KIO, such network error.
*/
KIOError,
/**
* Error caused by hash error
*/
HashError,
/**
* Error caused by invalid response.
* KIO reported no error, but got an invalid JSON response.
*/
ResponseError,
/**
* Errors caused by bad code, means the bug is somewhere.
*/
KazvError
};
Q_ENUM(ErrorCode)
KazvIOBaseJob(std::optional<Kazv::AES256CTRDesc> aes = std::nullopt, QObject *parent = 0);
~KazvIOBaseJob();
float progress();
Q_INVOKABLE void suspend();
Q_INVOKABLE void resume();
Q_INVOKABLE void cancel();
Q_INVOKABLE bool isSuspended();
Q_INVOKABLE bool isResulted();
/**
* Only use it after emitted the result signal
*/
Q_INVOKABLE ErrorCode error();
Q_SIGNALS:
void jobChanged();
void progressChanged();
/**
* @param data the mxcUri if this is an upload job.
*/
void result(ErrorCode ec, QString data);
protected Q_SLOTS:
virtual void connectJob();
protected:
QPointer<KIO::TransferJob> job();
void setJob(QPointer<KIO::TransferJob> job);
std::optional<Kazv::AES256CTRDesc> aes();
void setAes(Kazv::AES256CTRDesc);
void emitResult(ErrorCode ec, QString data = QString());
};
struct KazvIODownloadJobPrivate;
class KazvIODownloadJob : public KazvIOBaseJob
{
Q_OBJECT
std::unique_ptr<KazvIODownloadJobPrivate> m_d;
bool setFile(QString fileName);
public:
KazvIODownloadJob(const QString &hash, std::optional<Kazv::AES256CTRDesc> aes = std::nullopt, QObject *parent = 0);
KazvIODownloadJob(const QString &fileName, const QUrl &serverUrl, const QString &hash,
std::optional<Kazv::AES256CTRDesc> aes = std::nullopt, QObject *parent = 0);
~KazvIODownloadJob();
Q_INVOKABLE QString fileName();
private Q_SLOTS:
void writeFile(KJob *job, const QByteArray &data);
void closeFile(KJob *job);
protected Q_SLOTS:
void connectJob() override;
};
struct KazvIOUploadJobPrivate;
class KazvIOUploadJob : public KazvIOBaseJob
{
Q_OBJECT
std::unique_ptr<KazvIOUploadJobPrivate> m_d;
bool setFile(const QString fileName);
public:
KazvIOUploadJob(std::optional<Kazv::AES256CTRDesc> aes = std::nullopt, QObject *parent = 0);
KazvIOUploadJob(const QString fileName, const QUrl serverUrl, MatrixRoomList *roomList,
const QString &roomId, const QString token,
std::optional<Kazv::AES256CTRDesc> aes = std::nullopt, QObject *parent = 0);
~KazvIOUploadJob();
Q_INVOKABLE QString fileName();
private Q_SLOTS:
void handleResult(KJob *job);
protected Q_SLOTS:
void connectJob() override;
};
diff --git a/src/main.cpp b/src/main.cpp
index 371c312..856cd69 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,72 +1,72 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2021-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <kazv-defs.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <QApplication>
#include <QQmlApplicationEngine>
#include <QtQml>
#include <QUrl>
#include <QIcon>
#include <QCommandLineParser>
#include <QQuickStyle>
#include "meta-types.hpp"
#include "kazv-platform.hpp"
#include "kazv-path-config.hpp"
Q_DECL_EXPORT int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QApplication app(argc, argv);
QCoreApplication::setOrganizationName("project-kazv");
QCoreApplication::setOrganizationDomain("mxc.kazv.moe");
QCoreApplication::setApplicationName("kazv");
QGuiApplication::setDesktopFileName("moe.kazv.mxc.kazv.desktop");
#if KAZV_IS_WINDOWS
if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
QQuickStyle::setStyle(QStringLiteral("org.kde.desktop"));
}
#endif
QQmlApplicationEngine engine;
QString iconThemeToSet;
#if KAZV_IS_WINDOWS
iconThemeToSet = QStringLiteral("breeze");
#endif
QCommandLineParser parser;
QCommandLineOption iconThemeOption("i", "Icon theme", "theme");
parser.addOption(iconThemeOption);
parser.process(app);
if (!parser.value(iconThemeOption).isEmpty()) {
iconThemeToSet = parser.value(iconThemeOption);
}
KazvMetaTypeRegistration registration;
Q_UNUSED(registration);
#if KAZV_IS_WINDOWS
// On Windows the default search path is only in qrc
QStringList iconThemePaths;
iconThemePaths << (appDir() + QStringLiteral("/data/icons"));
QIcon::setThemeSearchPaths(iconThemePaths);
#endif
if (!iconThemeToSet.isEmpty()) {
QIcon::setThemeName(iconThemeToSet);
}
- engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
+ engine.loadFromModule("moe.kazv.mxc.kazvqml", "Main");
if (engine.rootObjects().isEmpty()) {
return -1;
}
return app.exec();
}
diff --git a/src/resources.qrc b/src/resources.qrc
deleted file mode 100644
index 6cd7bb9..0000000
--- a/src/resources.qrc
+++ /dev/null
@@ -1,94 +0,0 @@
-<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="RoomPinnedEventsPage.qml">contents/ui/RoomPinnedEventsPage.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="EventHistoryView.qml">contents/ui/EventHistoryView.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="Completion.qml">contents/ui/Completion.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="NotificationAction.qml">contents/ui/NotificationAction.qml</file>
- <file alias="AvatarAdapter.qml">contents/ui/AvatarAdapter.qml</file>
- <file alias="FileDialogAdapter.qml">contents/ui/FileDialogAdapter.qml</file>
- <file alias="FolderDialogAdapter.qml">contents/ui/FolderDialogAdapter.qml</file>
- <file alias="ScrollablePageAdapter.qml">contents/ui/ScrollablePageAdapter.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 13884c2..dce0a0d 100644
--- a/src/tests/quick-tests/tst_EventView.qml
+++ b/src/tests/quick-tests/tst_EventView.qml
@@ -1,714 +1,714 @@
/*
* 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 org.kde.kirigami 2.13 as Kirigami
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 mockHelper: TestHelpers.MockHelper {}
property var activateUserPage: mockHelper.noop()
property var paginateBackRequested: mockHelper.noop()
property var viewEventHistoryRequested: mockHelper.noop()
property var pinEventRequested: mockHelper.noop()
property var unpinEventRequested: mockHelper.noop()
property var timeline: ({
gaps: [],
})
property var pinnedEvents: ({
count: 2,
eventIds: ['$123', '$456'],
})
property var room: ({
resendMessage: mockHelper.promise(),
removeLocalEcho: mockHelper.promise(),
ensureStateEvent: mockHelper.promise(),
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',
info: {
h: Kirigami.Units.gridUnit * 10,
w: Kirigami.Units.gridUnit * 10,
}
},
formattedTime: '4:06 P.M.',
})
property var stickerEvent: ({
eventId: '',
sender: '@foo:tusooa.xyz',
type: 'm.sticker',
stateKey: '',
content: {
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 editedEvent: ({
eventId: '!some-event-id',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
stateKey: '',
content: {
msgtype: 'm.text',
body: 'some body',
},
formattedTime: '4:06 P.M.',
isEdited: true,
})
property var pinnedEvent: ({
eventId: '$123',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
content: {
msgtype: 'm.text',
body: 'some body',
},
formattedTime: '4:06 P.M.',
})
property var notPinnedEvent: ({
eventId: '$789',
sender: '@foo:tusooa.xyz',
type: 'm.room.message',
content: {
msgtype: 'm.text',
body: 'some body',
},
formattedTime: '4:06 P.M.',
})
property var sender: ({
membership: 'join',
userId: '@foo:tusooa.xyz',
name: 'foo',
avatarMxcUri: '',
})
property var senderOther: ({
membership: 'join',
userId: '@bar:tusooa.xyz',
name: 'bar',
avatarMxcUri: '',
})
property var eventViewComp: Component {
Kazv.EventView {
Layout.fillWidth: true
event: QtObject {
property var eventId: '$some-event-id'
function readers() { return 0; }
}
sender: item.sender
isGapped: true
}
}
property var eventComp: Component {
QtObject {
property var eventId: '$some-event-id'
function readers() { return 0; }
}
}
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
}
Kazv.EventView {
id: eventViewImage
event: item.imageEvent
sender: item.sender
}
Kazv.EventView {
id: eventViewImageCompact
compactMode: true
event: item.imageEvent
sender: item.sender
}
Kazv.EventView {
id: eventViewSticker
event: item.stickerEvent
sender: item.sender
}
Kazv.EventView {
id: eventViewStickerCompact
compactMode: true
event: item.stickerEvent
sender: item.sender
}
Kazv.EventView {
Layout.fillWidth: true
id: eventViewEdited
event: item.editedEvent
sender: item.sender
}
Kazv.EventView {
Layout.fillWidth: true
id: eventViewPinned
event: item.pinnedEvent
sender: item.sender
}
Kazv.EventView {
Layout.fillWidth: true
id: eventViewNotPinned
event: item.notPinnedEvent
sender: item.sender
}
}
TestCase {
id: eventViewTest
name: 'EventViewTest'
when: windowShown
function init() {
eventView.event = item.localEcho;
mockHelper.clearAll();
}
function test_localEcho() {
const indicator = findChild(eventView, 'localEchoIndicator');
verify(indicator);
verify(indicator.icon.name === 'state-sync');
verify(indicator.Accessible.name === l10n.get('event-sending'));
const resendButton = findChild(eventView, 'localEchoIndicator');
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.icon.name === 'state-warning');
verify(indicator.Accessible.name === l10n.get('event-resend'));
const menuComp = findChild(eventViewFailed, 'bubble').menuComp;
const menu = menuComp.createObject(menuComp.parent);
const deleteAction = findChild(menu, 'deleteMenuItem');
deleteAction.trigger();
tryVerify(() => item.room.removeLocalEcho.calledTimes() === 1);
tryVerify(() => item.room.removeLocalEcho.lastArgs()[0] === 'some-txn-id');
verify(item.room.resendMessage.calledTimes() === 0);
}
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');
+ const text = findChild(eventViewHtml, 'eventViewMainItem');
verify(text.textFormat === TextEdit.RichText);
verify(text.text.includes('some body'));
}
function test_htmlMessageReply() {
- const text = findChild(eventViewHtmlReply, 'textEventContent');
+ const text = findChild(eventViewHtmlReply, 'eventViewMainItem');
verify(text.textFormat === TextEdit.RichText);
verify(!text.text.includes('<blockquote>'));
}
function test_htmlMessageReplyNoQuote() {
- const text = findChild(eventViewHtmlReplyNoQuote, 'textEventContent');
+ const text = findChild(eventViewHtmlReplyNoQuote, 'eventViewMainItem');
verify(text.textFormat === TextEdit.RichText);
verify(!text.text.includes('<blockquote>'));
verify(text.text.includes('some body'));
}
function test_menuText() {
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 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 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.ensureStateEvent.calledTimes());
verify(Helpers.deepEqual(
item.room.ensureStateEvent.lastArgs(),
['m.room.member', '@mew:example.com']));
item.room.ensureStateEvent.lastRetVal().resolve(true, {});
tryVerify(() => item.activateUserPage.calledTimes());
}
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');
}
function test_paginateBack() {
const obj = item.eventViewComp.createObject(item);
tryVerify(() => item.paginateBackRequested.calledTimes());
verify(item.paginateBackRequested.lastArgs()[0] === '$some-event-id');
item.paginateBackRequested.clear();
obj.event.eventId = '$some-other-event-id';
tryVerify(() => item.paginateBackRequested.calledTimes() === 1);
verify(item.paginateBackRequested.lastArgs()[0] === '$some-other-event-id');
item.paginateBackRequested.clear();
const newEvent = eventComp.createObject(item);
obj.event = newEvent;
tryVerify(() => item.paginateBackRequested.calledTimes() === 1);
verify(item.paginateBackRequested.lastArgs()[0] === '$some-event-id');
item.paginateBackRequested.clear();
obj.isGapped = false;
verify(item.paginateBackRequested.calledTimes() === 0);
}
function test_avatarClick() {
const avatar = findChild(eventViewText, 'senderAvatar');
mouseClick(avatar);
tryVerify(() => item.activateUserPage.calledTimes() === 1);
// verify that the user id is passed
verify(item.activateUserPage.lastArgs()[2] === '@foo:tusooa.xyz');
}
function test_imageEvent() {
verify(!findChild(eventViewImage, 'summaryLabel').visible);
verify(findChild(eventViewImage, 'bodyLabel').visible);
verify(findChild(eventViewImageCompact, 'summaryLabel').visible);
verify(!findChild(eventViewImageCompact, 'bodyLabel').visible);
verify(!findChild(eventViewSticker, 'summaryLabel').visible);
verify(findChild(eventViewSticker, 'bodyLabel').visible);
verify(findChild(eventViewStickerCompact, 'summaryLabel').visible);
verify(!findChild(eventViewStickerCompact, 'bodyLabel').visible);
const image = findChild(eventViewImageCompact, 'mainImage');
verify(image.height <= Kirigami.Units.gridUnit * 5);
}
function test_history() {
{
const menuComp = findChild(eventViewText, 'bubble').menuComp;
const menu = menuComp.createObject(menuComp.parent);
const action = findChild(menu, 'viewHistoryMenuItem');
verify(!action.enabled);
}
{
const menuComp = findChild(eventViewEdited, 'bubble').menuComp;
const menu = menuComp.createObject(menuComp.parent);
const action = findChild(menu, 'viewHistoryMenuItem');
verify(action.enabled);
action.trigger();
compare(viewEventHistoryRequested.calledTimes(), 1);
compare(viewEventHistoryRequested.lastArgs()[0], '!some-event-id');
}
}
function test_pinned() {
{
const menuComp = findChild(eventViewPinned, 'bubble').menuComp;
const menu = menuComp.createObject(menuComp.parent);
const action = findChild(menu, 'pinUnpinMenuItem');
verify(action.enabled);
action.trigger();
compare(unpinEventRequested.calledTimes(), 1);
compare(unpinEventRequested.lastArgs()[0], '$123');
compare(pinEventRequested.calledTimes(), 0);
}
{
const menuComp = findChild(eventViewNotPinned, 'bubble').menuComp;
const menu = menuComp.createObject(menuComp.parent);
const action = findChild(menu, 'pinUnpinMenuItem');
verify(action.enabled);
action.trigger();
compare(pinEventRequested.calledTimes(), 1);
compare(pinEventRequested.lastArgs()[0], '$789');
compare(pinEventRequested.calledTimes(), 1);
}
{
const menuComp = findChild(eventView, 'bubble').menuComp;
const menu = menuComp.createObject(menuComp.parent);
const action = findChild(menu, 'pinUnpinMenuItem');
verify(!action.enabled);
}
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Oct 13, 1:38 AM (1 d, 22 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1786553
Default Alt Text
(75 KB)
Attached To
Mode
rK kazv
Attached
Detach File
Event Timeline
Log In to Comment