diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,7 @@ Multimedia Test Network QuickTest ) qt6_policy(SET QTP0001 NEW) -find_package(KF${KF_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami Config KIO Notifications) +find_package(KF${KF_MAJOR_VERSION} ${KF_MIN_VERSION} REQUIRED COMPONENTS Kirigami Config KIO Notifications CoreAddons) find_package(KF6KirigamiAddons) find_package(Threads REQUIRED) diff --git a/packaging/GNU-Linux/appimage/build.sh b/packaging/GNU-Linux/appimage/build.sh --- a/packaging/GNU-Linux/appimage/build.sh +++ b/packaging/GNU-Linux/appimage/build.sh @@ -44,6 +44,7 @@ qml6-module-qtquick-dialogs qml6-module-org-kde-notification kf6-kirigami-addons-dev + kf6-kcoreaddons-dev cmark ) export QMAKE=qmake6 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -103,6 +103,7 @@ Qt${QT_MAJOR_VERSION}::Widgets KF${KF_MAJOR_VERSION}::ConfigCore KF${KF_MAJOR_VERSION}::KIOCore KF${KF_MAJOR_VERSION}::Notifications + KF${KF_MAJOR_VERSION}::CoreAddons ${CMARK_TARGET_NAME} ) @@ -147,6 +148,7 @@ BASE_DIRECTORY contents/ui SOURCES Main.qml + About.qml PageManager.qml LoginPage.qml MainPage.qml diff --git a/src/contents/ui/About.qml b/src/contents/ui/About.qml new file mode 100644 --- /dev/null +++ b/src/contents/ui/About.qml @@ -0,0 +1,173 @@ +/* + * This file is part of kazv. + * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import org.kde.kirigami as Kirigami +import org.kde.kirigamiaddons.formcard as FormCard +import org.kde.coreaddons as CoreAddons +import moe.kazv.mxc.kazv as MK +import 'matrix-helpers.js' as Helpers + +FormCard.FormCardPage { + id: aboutPage + title: l10n.get('about-page-title') + property var aboutData: CoreAddons.AboutData + + property var authors: [ + { + name: 'tusooa', + task: l10n.get('about-author-task-maintainer'), + emailAddress: 'tusooa@kazv.moe', + website: 'https://blog.tusooa.xyz', + }, + { + name: 'nannanko', + task: l10n.get('about-author-task-developer'), + emailAddress: 'nannanko@kazv.moe', + }, + { + name: 'April Simone', + task: l10n.get('about-author-task-developer'), + emailAddress: 'apr3vau@outlook.com', + website: 'https://apr3vau.github.io', + }, + ] + + property var libraries: [ + { + name: 'libkazv', + website: 'https://lily-is.land/kazv/libkazv', + }, + { + name: 'cmark', + website: 'https://github.com/commonmark/cmark', + }, + { + name: 'nlohmann::json', + website: 'https://github.com/nlohmann/json', + }, + { + name: 'zug', + website: 'https://github.com/arximboldi/zug', + }, + { + name: 'immer', + website: 'https://github.com/arximboldi/immer', + }, + { + name: 'lager', + website: 'https://github.com/arximboldi/lager', + }, + { + name: 'crypto++', + website: 'https://github.com/weidai11/cryptopp', + }, + ] + + FormCard.FormCard { + Layout.topMargin: Kirigami.Units.gridUnit + + FormCard.FormTextDelegate { + text: l10n.get('about-display-name', { version: aboutData.version }) + } + + FormCard.FormButtonDelegate { + text: aboutData.homepage + onClicked: Qt.openUrlExternally(aboutData.homepage) + } + + FormCard.FormTextDelegate { + text: l10n.get('about-short-description') + } + + FormCard.FormTextDelegate { + text: l10n.get('about-copyright') + } + + FormCard.FormButtonDelegate { + text: l10n.get('about-license') + onClicked: Qt.openUrlExternally('https://www.gnu.org/licenses/agpl-3.0.html') + } + } + + FormCard.FormHeader { + Layout.topMargin: Kirigami.Units.gridUnit + title: l10n.get('about-used-libraries') + } + + FormCard.FormCard { + Repeater { + model: Kirigami.Settings.information + delegate: FormCard.FormTextDelegate { + required property string modelData + text: modelData + } + } + + Repeater { + model: aboutPage.libraries + delegate: FormCard.FormButtonDelegate { + required property string name + required property string website + text: name + description: website + onClicked: Qt.openUrlExternally(website) + } + } + } + + FormCard.FormHeader { + Layout.topMargin: Kirigami.Units.gridUnit + + title: l10n.get('about-authors') + } + + FormCard.FormCard { + objectName: 'authorsCard' + Repeater { + model: aboutPage.authors + delegate: FormCard.FormTextDelegate { + id: author + required property string name + required property string task + required property var modelData + required property int index + objectName: `authors${index}` + text: name + description: task + trailing: RowLayout { + ToolButton { + objectName: 'emailButton' + visible: !!author.modelData.emailAddress + display: AbstractButton.IconOnly + icon.name: 'mail-send' + text: l10n.get('about-author-email-action') + ToolTip.text: author.modelData.emailAddress + ToolTip.delay: Kirigami.Units.toolTipDelay + ToolTip.timeout: Helpers.toolTipTimeout + ToolTip.visible: hovered + onClicked: Qt.openUrlExternally('mailto:' + author.modelData.emailAddress) + } + + ToolButton { + objectName: 'websiteButton' + visible: !!author.modelData.website + display: AbstractButton.IconOnly + icon.name: 'internet-services' + text: l10n.get('about-author-website-action') + ToolTip.text: author.modelData.website + ToolTip.delay: Kirigami.Units.toolTipDelay + ToolTip.timeout: Helpers.toolTipTimeout + ToolTip.visible: hovered + onClicked: Qt.openUrlExternally(author.modelData.website) + } + } + } + } + } +} diff --git a/src/contents/ui/Main.qml b/src/contents/ui/Main.qml --- a/src/contents/ui/Main.qml +++ b/src/contents/ui/Main.qml @@ -171,6 +171,12 @@ onTriggered: { pushJoinRoomPage(); } + }, + Kirigami.Action { + objectName: 'aboutAction' + text: l10n.get('global-drawer-action-about') + icon.name: 'help-about' + onTriggered: pageStack.pushDialogLayer(Qt.resolvedUrl('About.qml')) } ] } diff --git a/src/l10n/cmn-Hans/100-ui.ftl b/src/l10n/cmn-Hans/100-ui.ftl --- a/src/l10n/cmn-Hans/100-ui.ftl +++ b/src/l10n/cmn-Hans/100-ui.ftl @@ -4,6 +4,18 @@ app-title = { -kt-app-name } +about-page-title = 关于 { -kt-app-name } +about-copyright = (c) 2020- the Kazv Project +about-display-name = { -kt-app-name } { $version } +about-short-description = 各平台同一的 Matrix 客户端和即时通讯软件 +about-license = 以 AGPL 3 或以后版本授权 +about-used-libraries = 使用了的库 +about-authors = 作者 +about-author-email-action = 写电邮 +about-author-website-action = 访问网站 +about-author-task-maintainer = 维护者 +about-author-task-developer = 开发者 + user-name-with-id = { $name } ({ $userId }) user-name-overrided = { $overridedName } ({ $globalName }) @@ -15,6 +27,7 @@ global-drawer-action-settings = 设置 global-drawer-action-create-room = 创建房间 global-drawer-action-join-room = 加入房间 +global-drawer-action-about = 关于 { -kt-app-name } action-settings-page-title = 配置快捷键 action-settings-shortcut-prompt = 快捷键:{ $shortcut } diff --git a/src/l10n/en/100-ui.ftl b/src/l10n/en/100-ui.ftl --- a/src/l10n/en/100-ui.ftl +++ b/src/l10n/en/100-ui.ftl @@ -4,6 +4,18 @@ app-title = { -kt-app-name } +about-page-title = About { -kt-app-name } +about-copyright = (c) 2020- the Kazv Project +about-display-name = { -kt-app-name } { $version } +about-short-description = Convergent Matrix client and instant messaging app +about-license = licensed under AGPL 3 or later +about-used-libraries = Used libraries +about-authors = Authors +about-author-email-action = Write an email +about-author-website-action = Access website +about-author-task-maintainer = Maintainer +about-author-task-developer = Developer + user-name-with-id = { $name } ({ $userId }) user-name-overrided = { $overridedName } ({ $globalName }) @@ -15,6 +27,7 @@ global-drawer-action-settings = Settings global-drawer-action-create-room = Create room global-drawer-action-join-room = Join room +global-drawer-action-about = About { -kt-app-name } action-settings-page-title = Configure shortcuts action-settings-shortcut-prompt = Shortcut: { $shortcut } diff --git a/src/main.cpp b/src/main.cpp --- a/src/main.cpp +++ b/src/main.cpp @@ -9,16 +9,18 @@ #include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168 #include <QApplication> +#include <QByteArray> #include <QQmlApplicationEngine> #include <QtQml> #include <QUrl> #include <QIcon> #include <QCommandLineParser> #include <QQuickStyle> - +#include <KAboutData> #include "meta-types.hpp" #include "kazv-platform.hpp" #include "kazv-path-config.hpp" +#include "kazv-version.hpp" using namespace Qt::Literals::StringLiterals; @@ -31,6 +33,16 @@ QCoreApplication::setApplicationName(u"kazv"_s); QGuiApplication::setDesktopFileName(u"moe.kazv.mxc.kazv.desktop"_s); + KAboutData aboutData; + aboutData + .setComponentName(u"kazv"_s) + .setVersion(QByteArray::fromStdString(kazvVersionString())) + .setOrganizationDomain(QByteArray("mxc.kazv.moe")) + .setDesktopFileName(u"moe.kazv.mxc.kazv"_s) + .setBugAddress(QByteArray("https://lily-is.land/kazv/kazv/-/issues")) + .setHomepage(u"https://kazv.chat"_s); + KAboutData::setApplicationData(aboutData); + #if KAZV_IS_WINDOWS if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) { QQuickStyle::setStyle(QStringLiteral("org.kde.desktop")); diff --git a/src/tests/quick-tests/tst_About.qml b/src/tests/quick-tests/tst_About.qml new file mode 100644 --- /dev/null +++ b/src/tests/quick-tests/tst_About.qml @@ -0,0 +1,65 @@ +/* + * This file is part of kazv. + * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe> + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import QtQuick +import QtTest +import '../../contents/ui' as Kazv +import 'test-helpers.js' as Helpers + +Item { + id: item + width: 800 + height: 600 + + property var l10n: Helpers.fluentMock + + Kazv.About { + id: about + authors: [ + { + name: 'author 0', + task: 'task 0', + }, + { + name: 'author 1', + task: 'task 1', + emailAddress: 'email@example.com', + }, + { + name: 'author 2', + task: 'task 2', + website: 'https://example.com', + }, + { + name: 'author 3', + task: 'task 3', + emailAddress: 'email@example.com', + website: 'https://example.com', + }, + ] + } + + TestCase { + id: aboutTest + name: 'AboutTest' + when: windowShown + + function test_authors() { + const card = findChild(about, 'authorsCard'); + verify(!findChild(findChild(card, 'authors0'), 'emailButton').visible); + verify(!findChild(findChild(card, 'authors0'), 'websiteButton').visible); + + verify(findChild(findChild(card, 'authors1'), 'emailButton').visible); + verify(!findChild(findChild(card, 'authors1'), 'websiteButton').visible); + + verify(!findChild(findChild(card, 'authors2'), 'emailButton').visible); + verify(findChild(findChild(card, 'authors2'), 'websiteButton').visible); + + verify(findChild(findChild(card, 'authors3'), 'emailButton').visible); + verify(findChild(findChild(card, 'authors3'), 'websiteButton').visible); + } + } +}