Page MenuHomePhorge

No OneTemporary

Size
20 KB
Referenced Files
None
Subscribers
None
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 309550d..3d4c747 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,113 +1,113 @@
cmake_minimum_required(VERSION 3.16)
project(kazv)
include(FeatureSummary)
set(kazv_VERSION_MAJOR 0)
set(kazv_VERSION_MINOR 6)
set(kazv_VERSION_PATCH 0)
set(kazv_VERSION_STRING ${kazv_VERSION_MAJOR}.${kazv_VERSION_MINOR}.${kazv_VERSION_PATCH})
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH})
set(CMAKE_CXX_STANDARD 20)
if(NOT kazv_KF_QT_MAJOR_VERSION)
set(kazv_KF_QT_MAJOR_VERSION 6)
endif()
message(STATUS "KF/Qt major version: ${kazv_KF_QT_MAJOR_VERSION}")
if(NOT kazv_KF_QT_MAJOR_VERSION EQUAL 6)
message(FATAL_ERROR "KF/Qt major version must be 6")
endif()
set(QT_MAJOR_VERSION ${kazv_KF_QT_MAJOR_VERSION})
set(KF_MAJOR_VERSION ${kazv_KF_QT_MAJOR_VERSION})
-set(QT_MIN_VERSION 6.5.0)
+set(QT_MIN_VERSION 6.9.0)
set(KF_MIN_VERSION 6.0.0)
option(kazv_LINK_BREEZE_ICONS "Link to Breeze icons library" OFF)
option(kazv_BUILD_TESTS "Build tests" ON)
option(kazv_ENABLE_APPIUM_TESTS "Run appium tests" "")
set(kazv_LINK_BREEZE_ICONS_MACRO 0)
if(kazv_LINK_BREEZE_ICONS)
set(kazv_LINK_BREEZE_ICONS_MACRO 1)
endif()
find_package(ECM ${KF_MIN_VERSION} REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH})
set(KDE_SKIP_NULLPTR_WARNINGS_SETTINGS ON)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDECompilerSettings NO_POLICY_SCOPE)
include(ECMSetupVersion)
include(ECMGenerateHeaders)
include(ECMPoQmTools)
include(ECMAddTests)
include(ECMQtDeclareLoggingCategory)
include(ECMDeprecationSettings)
include(ECMQmlModule)
if(ANDROID)
find_package(Perl 5.12 REQUIRED)
include(ECMAddAndroidApk)
endif()
ecm_set_disabled_deprecation_versions(
QT 5.15.14
KF 5.116.0
KCOREADDONS 5.116.0
)
find_package(Qt${QT_MAJOR_VERSION} ${QT_MIN_VERSION} REQUIRED COMPONENTS
Core Gui Qml QuickControls2 Svg Concurrent Widgets
Multimedia Test Network QuickTest HttpServer Sql
)
qt6_policy(SET QTP0001 NEW)
find_package(QCoro6 REQUIRED COMPONENTS Core)
qcoro_enable_coroutines()
set(kazv_KF_EXTRA_MODULES)
if(kazv_LINK_BREEZE_ICONS)
list(APPEND kazv_KF_EXTRA_MODULES BreezeIcons)
endif()
find_package(KF${KF_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami Config Notifications CoreAddons ${kazv_KF_EXTRA_MODULES})
find_package(KF6KirigamiAddons)
find_package(Threads REQUIRED)
find_package(nlohmann_json REQUIRED)
find_package(libkazv REQUIRED)
find_package(cmark)
set(CMARK_TARGET_NAME cmark::cmark)
if(NOT cmark_FOUND)
# CMark 0.29.0 on Ubuntu 20.04 has cmark.cmake instead of cmark-config.cmake
# But that is not usable, so we fall back to pkgconfig
find_package(PkgConfig REQUIRED)
pkg_search_module(cmark libcmark IMPORTED_TARGET GLOBAL)
set(CMARK_TARGET_NAME cmark)
endif()
if(ANDROID)
find_package(OpenSSL REQUIRED)
endif()
if(${kazv_ENABLE_APPIUM_TESTS} STREQUAL "")
find_package(SeleniumWebDriverATSPI)
if(SeleniumWebDriverATSPI_FOUND)
message(STATUS "Enabling appium tests because selenium-webdriver-at-spi is found. To turn it off, explicitly set kazv_ENABLE_APPIUM_TESTS to OFF")
set(kazv_ENABLE_APPIUM_TESTS ON)
endif()
elseif(kazv_ENABLE_APPIUM_TESTS)
find_package(SeleniumWebDriverATSPI REQUIRED)
endif()
kde_enable_exceptions()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -foperator-names -DQT_NO_EMIT")
add_subdirectory(src)
install(PROGRAMS moe.kazv.mxc.kazv.desktop DESTINATION ${KDE_INSTALL_APPDIR})
install(FILES moe.kazv.mxc.kazv.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
install(FILES kazv.notifyrc DESTINATION ${KDE_INSTALL_KNOTIFYRCDIR})
install(FILES icons/kazvlogo-256.png DESTINATION ${KDE_INSTALL_ICONDIR}/hicolor/256x256/apps RENAME moe.kazv.mxc.kazv.png)
feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/src/contents/ui/RoomListView.qml b/src/contents/ui/RoomListView.qml
index f552e4c..f376472 100644
--- a/src/contents/ui/RoomListView.qml
+++ b/src/contents/ui/RoomListView.qml
@@ -1,36 +1,37 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.2
import QtQuick.Layouts 1.15
import QtQuick.Controls 2.15
import org.kde.kirigami 2.13 as Kirigami
import 'matrix-helpers.js' as Helpers
import '.' as Kazv
import 'room-settings' as RoomSettings
ListView {
id: roomListView
property var roomList
property var roomIds: roomList.roomIds
property var iconSize: Kirigami.Units.iconSizes.large
Layout.fillHeight: true
model: roomList
Layout.minimumHeight: childrenRect.height
currentIndex: roomIds.indexOf(sdkVars.currentRoomId)
delegate: Kazv.RoomListViewItemDelegate {
required property var index
+ width: ListView.view.width
item: roomList.at(index)
iconSize: roomListView.iconSize
}
}
diff --git a/src/contents/ui/RoomListViewItemDelegate.qml b/src/contents/ui/RoomListViewItemDelegate.qml
index 0cb87a7..897549f 100644
--- a/src/contents/ui/RoomListViewItemDelegate.qml
+++ b/src/contents/ui/RoomListViewItemDelegate.qml
@@ -1,174 +1,179 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
import QtQuick 2.2
import QtQuick.Layouts 1.15
-import QtQuick.Controls 2.15
+import QtQuick.Controls
import org.kde.kirigami 2.13 as Kirigami
import moe.kazv.mxc.kazv 0.0 as MK
import 'matrix-helpers.js' as Helpers
import 'room-settings' as RoomSettings
import '.' as Kazv
-Kirigami.SwipeListItem {
+ItemDelegate {
id: upper
property var item
property var iconSize: 1
property var isFavourite: item.tagIds.includes('m.favourite')
property var roomNameProvider: Kazv.RoomNameProvider {
room: upper.item
}
property var roomDisplayName: roomNameProvider.name
property var roomTimeline: item.timeline()
property var lastUnreadMessage: getLastUnreadMessage(roomTimeline, roomTimeline.count, item)
property var lastUnreadMessageReaders: lastUnreadMessage ? lastUnreadMessage.readers() : null
// The readers count is to make hasUnreadMessages prop reactive
// against our newly-posted receipts
property var hasUnreadMessages: getHasUnreadMessages(item, roomTimeline, roomTimeline.count, lastUnreadMessageReaders ? lastUnreadMessageReaders.count : 0)
onClicked: switchToRoomRequested(item.roomId)
checkable: true
checked: sdkVars.currentRoomId == item.roomId
autoExclusive: true
+ highlighted: checked
Kirigami.Theme.colorSet: !checked ? Kirigami.Theme.View : Kirigami.Theme.Selection
Kirigami.Theme.inherit: false
focusPolicy: Qt.StrongFocus
Accessible.focusable: true
Accessible.name: roomDisplayName
- background: Rectangle {
- anchors.fill: parent
- color: (upper.hovered || upper.visualFocus) ? Kirigami.Theme.hoverColor : Kirigami.Theme.backgroundColor
- }
contentItem: RowLayout {
Kazv.AvatarAdapter {
mxcUri: item.roomOrHeroAvatarMxcUri
name: Helpers.roomAvatarPlaceholderName(item)
}
+ Kirigami.Icon {
+ objectName: 'favIndicator'
+ source: /* iconName: */ 'starred-symbolic'
+ visible: upper.isFavourite
+ Accessible.role: Accessible.Indicator
+ Accessible.name: l10n.get('room-list-view-room-item-fav-indicator')
+ }
+
Label {
objectName: 'tombstoneIndicator'
visible: item.tombstoned === true
text: l10n.get('room-list-view-room-item-tombstone')
background: Rectangle {
color: Kirigami.Theme.negativeBackgroundColor
}
color: Kirigami.Theme.negativeTextColor
}
Label {
objectName: 'inviteIndicator'
visible: item.membership === MK.MatrixRoom.Invite
text: l10n.get('room-list-view-room-item-invited')
background: Rectangle {
color: Kirigami.Theme.activeBackgroundColor
}
color: Kirigami.Theme.activeTextColor
}
Label {
objectName: 'leaveIndicator'
visible: item.membership === MK.MatrixRoom.Leave
text: l10n.get('room-list-view-room-item-left')
color: Kirigami.Theme.disabledTextColor
}
Label {
objectName: 'roomDisplayNameLabel'
text: roomDisplayName
Layout.fillWidth: true
elide: Text.ElideRight
color: item.membership === MK.MatrixRoom.Leave ? Kirigami.Theme.disabledTextColor : Kirigami.Theme.textColor
}
Rectangle {
objectName: 'unreadNotificationCount'
Layout.alignment: Qt.AlignRight
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.preferredWidth: Math.max(unreadNotificationCountLabel.implicitWidth, Kirigami.Units.gridUnit)
Layout.preferredHeight: Kirigami.Units.gridUnit
radius: Kirigami.Units.gridUnit / 2
color: Kirigami.Theme.activeBackgroundColor
visible: !!item.unreadNotificationCount
RowLayout {
anchors.fill: parent
Label {
id: unreadNotificationCountLabel
Layout.alignment: Qt.AlignHCenter
color: Kirigami.Theme.activeTextColor
text: l10n.get('room-list-view-room-item-unread-notification-count', { count: item.unreadNotificationCount })
Accessible.ignored: true
}
}
Accessible.role: Accessible.Indicator
Accessible.name: l10n.get('room-list-view-room-item-unread-notification-count-text', { count: item.unreadNotificationCount })
}
Rectangle {
objectName: 'unreadIndicator'
Layout.alignment: Qt.AlignRight
Layout.rightMargin: Kirigami.Units.largeSpacing
Layout.preferredWidth: Kirigami.Units.iconSizes.small
Layout.preferredHeight: Kirigami.Units.iconSizes.small
radius: Kirigami.Units.iconSizes.small / 2
color: Kirigami.Theme.activeTextColor
visible: !item.unreadNotificationCount && hasUnreadMessages
Accessible.role: Accessible.Indicator
Accessible.name: l10n.get('room-list-view-room-item-unread-indicator')
}
}
- actions: [
+ ContextMenu.menu: Menu {
Kirigami.Action {
text: l10n.get("room-list-view-room-item-fav-action")
checkable: true
checked: isFavourite
icon.name: isFavourite ? 'starred-symbolic' : 'non-starred-symbolic'
onTriggered: toggleFavourite()
- },
+ }
Kirigami.Action {
text: l10n.get('room-list-view-room-item-more-action')
icon.name: 'view-more-horizontal-symbolic'
onTriggered: activateRoomSettingsPage(item)
}
- ]
+ }
function toggleFavourite() {
favouriteToggler.toggleTag('m.favourite');
}
property var favouriteToggler: RoomSettings.RoomTagHandler {
room: item
}
function getHasUnreadMessages(room, timeline, timelineCount, readersCount) {
return !!room.unreadNotificationCount || !!getLastUnreadMessage(timeline, timelineCount, room);
}
function getLastUnreadMessage(timeline, timelineCount, room) {
for (let i = 0; i < timelineCount; ++i) {
const event = timeline.at(i);
if (event.isLocalEcho) {
// Skip local echoes
} else if (room.localReadMarker === event.eventId) {
// This message is read locally
return undefined;
} else if (event.sender === matrixSession.userId) {
// Own message here
return undefined;
} else if (Helpers.isEventReadBy(event, matrixSession.userId)) {
// This message is read
return undefined;
} else {
// The latest message is not read and not sent by the current user
return event;
}
}
}
}
diff --git a/src/tests/quick-tests/tst_RoomListViewItemDelegate.qml b/src/tests/quick-tests/tst_RoomListViewItemDelegate.qml
index ad99a7d..d8a133f 100644
--- a/src/tests/quick-tests/tst_RoomListViewItemDelegate.qml
+++ b/src/tests/quick-tests/tst_RoomListViewItemDelegate.qml
@@ -1,313 +1,337 @@
/*
* 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 '../../contents/ui' as Kazv
import 'test-helpers.js' as JsHelpers
import 'test-helpers' as QmlHelpers
import moe.kazv.mxc.kazv 0.0 as MK
QmlHelpers.TestItem {
id: item
property var roomInvite: JsHelpers.factory.room({
membership: MK.MatrixRoom.Invite,
unreadNotificationCount: 0,
tombstoned: true,
})
property var roomJoin: JsHelpers.factory.room({
membership: MK.MatrixRoom.Join,
unreadNotificationCount: 0,
})
property var roomLeave: JsHelpers.factory.room({
membership: MK.MatrixRoom.Leave,
unreadNotificationCount: 0,
})
property var roomWithUnreadNotificationCount: JsHelpers.factory.room({
membership: MK.MatrixRoom.Join,
unreadNotificationCount: 2,
})
property var roomTombstone: JsHelpers.factory.room({
tombstoned: true,
unreadNotificationCount: 0,
})
+ property var roomFav: JsHelpers.factory.room({
+ membership: MK.MatrixRoom.Join,
+ unreadNotificationCount: 0,
+ tagIds: ['m.favourite'],
+ })
+
property var timelineUnread: QtObject {
property var count: 5
function at(i) {
if (i < 2) {
return {
isLocalEcho: true,
readers: () => ({ count: 0 }),
};
}
if (i < 3) {
return {
isLocalEcho: false,
sender: '@bar:example.org',
eventId: `$event${i}`,
readers: () => ({ count: 0 }),
};
}
return {
isLocalEcho: false,
sender: '@foo:example.org',
eventId: `$event${i}`,
readers: () => ({ count: 0 }),
};
}
}
property var timelineLastSentIsMine: QtObject {
property var count: 5
function at(i) {
if (i < 2) {
return {
isLocalEcho: true,
readers: () => ({ count: 0 }),
};
}
if (i < 3) {
return {
isLocalEcho: false,
sender: '@foo:example.org',
eventId: `$event${i}`,
readers: () => ({ count: 0 }),
};
}
return {
isLocalEcho: false,
sender: '@bar:example.org',
eventId: `$event${i}`,
readers: () => ({ count: 0 }),
};
}
}
property var timelineRead: QtObject {
property var count: 5
function at(i) {
if (i < 2) {
return {
isLocalEcho: true,
readers: () => ({ count: 0 }),
};
}
if (i < 3) {
return {
isLocalEcho: false,
sender: '@bar:example.org',
eventId: `$event${i}`,
readers: () => ({
count: 1,
at() {
return { userId: '@foo:example.org' };
},
}),
};
}
return {
isLocalEcho: false,
sender: '@foo:example.org',
eventId: `$event${i}`,
readers: () => ({ count: 0 }),
};
}
}
property string testRoomId: 'room-test'
signal switchToRoomRequested(string roomId)
SignalSpy {
id: switchToRoomSpy
target: item
signalName: 'switchToRoomRequested'
}
ColumnLayout {
anchors.fill: parent
Kazv.RoomListViewItemDelegate {
id: delegateInvite
item: roomInvite
}
Kazv.RoomListViewItemDelegate {
id: delegateJoin
item: roomJoin
}
Kazv.RoomListViewItemDelegate {
id: delegateLeave
item: roomLeave
}
Kazv.RoomListViewItemDelegate {
id: delegateRoom
item: JsHelpers.factory.room({
roomId: testRoomId
})
}
Kazv.RoomListViewItemDelegate {
id: delegateRoomWithName
item: QtObject {
property var roomId: testRoomId
property var name: 'some name'
}
}
Kazv.RoomListViewItemDelegate {
id: delegateRoomWithHeroes
item: QtObject {
property var roomId: testRoomId
property var heroEvents: [{
type: 'm.room.member',
state_key: '@foo:example.com',
content: {
displayname: 'foo',
},
}, {
type: 'm.room.member',
state_key: '@bar:example.com',
content: {
displayname: 'bar',
},
}]
}
}
Kazv.RoomListViewItemDelegate {
id: delegateUnread
item: Object.assign({}, roomJoin, {
timeline () {
return timelineUnread;
},
})
}
Kazv.RoomListViewItemDelegate {
id: delegateWithUnreadNotifications
item: Object.assign({}, roomWithUnreadNotificationCount, {
timeline () {
return timelineUnread;
},
})
}
Kazv.RoomListViewItemDelegate {
id: delegateTombstone
item: roomTombstone
}
+
+ Kazv.RoomListViewItemDelegate {
+ id: delegateFav
+ item: roomFav
+ }
}
TestCase {
id: roomListViewItemDelegateTest
name: 'RoomListViewItemDelegateTest'
when: windowShown
function initTestCase() {
item.matrixSession.userId = '@foo:example.org';
item.sdkVars.userGivenNicknameMap.map = {
'@foo:example.com': 'something',
};
}
function test_inviteIndicator() {
const indicator = findChild(delegateInvite, 'inviteIndicator');
verify(indicator);
verify(indicator.visible);
const leaveIndicator = findChild(delegateInvite, 'leaveIndicator');
verify(leaveIndicator);
verify(!leaveIndicator.visible);
}
function test_inviteIndicatorJoin() {
const indicator = findChild(delegateJoin, 'inviteIndicator');
verify(indicator);
verify(!indicator.visible);
const leaveIndicator = findChild(delegateJoin, 'leaveIndicator');
verify(leaveIndicator);
verify(!leaveIndicator.visible);
}
function test_inviteIndicatorLeave() {
const indicator = findChild(delegateLeave, 'inviteIndicator');
verify(indicator);
verify(!indicator.visible);
const leaveIndicator = findChild(delegateLeave, 'leaveIndicator');
verify(leaveIndicator);
verify(leaveIndicator.visible);
}
+ function test_favIndicator() {
+ {
+ const indicator = findChild(delegateJoin, 'favIndicator');
+ verify(indicator);
+ verify(!indicator.visible);
+ }
+ {
+ const indicator = findChild(delegateFav, 'favIndicator');
+ verify(indicator);
+ verify(indicator.visible);
+ }
+ }
+
function test_lastUnreadMessage() {
const room = {};
compare(delegateJoin.getLastUnreadMessage(timelineUnread, timelineUnread.count, room).eventId, '$event2');
compare(delegateJoin.getLastUnreadMessage(timelineLastSentIsMine, timelineLastSentIsMine.count, room), undefined);
compare(delegateJoin.getLastUnreadMessage(timelineRead, timelineRead.count, room), undefined);
room.localReadMarker = '$event2';
compare(delegateJoin.getLastUnreadMessage(timelineUnread, timelineUnread.count, room), undefined);
}
function test_returnRoom() {
compare(switchToRoomSpy.count, 0);
mouseClick(delegateRoom);
tryCompare(switchToRoomSpy, 'count', 1);
verify(JsHelpers.deepEqual(switchToRoomSpy.signalArguments[0], [testRoomId]));
}
function test_roomName() {
compare(
findChild(delegateRoomWithName, 'roomDisplayNameLabel').text,
l10n.get('room-list-view-room-item-title-name', { name: 'some name' }));
compare(
findChild(delegateRoomWithHeroes, 'roomDisplayNameLabel').text,
l10n.get('room-list-view-room-item-title-heroes', {
hero: l10n.get(
'user-name-overrided',
{ overridedName: 'something', globalName: 'foo' }),
secondHero: 'bar',
otherNum: 1,
}));
}
function test_unreadIndicators() {
verify(!findChild(delegateWithUnreadNotifications, 'unreadIndicator').visible);
verify(findChild(delegateWithUnreadNotifications, 'unreadNotificationCount').visible);
verify(findChild(delegateUnread, 'unreadIndicator').visible);
verify(!findChild(delegateUnread, 'unreadNotificationCount').visible);
}
function test_tombstoneIndictor() {
const indicator = findChild(delegateTombstone, 'tombstoneIndicator');
verify(indicator);
verify(indicator.visible);
const indicatorJoin = findChild(delegateJoin, 'tombstoneIndicator');
verify(indicatorJoin);
verify(!indicatorJoin.visible);
}
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Aug 9, 1:16 PM (1 d, 12 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1724584
Default Alt Text
(20 KB)

Event Timeline