Page MenuHomePhorge

No OneTemporary

Size
48 KB
Referenced Files
None
Subscribers
None
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 69a9095..a233809 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,86 +1,88 @@
set(KAZV_DATA_DIR ${KDE_INSTALL_DATADIR})
set(KAZV_L10N_DIR ${KAZV_DATA_DIR}/l10n)
if(WIN32)
set(KAZV_IS_WINDOWS 1)
else()
set(KAZV_IS_WINDOWS 0)
endif()
configure_file(kazv-path-config.hpp.in kazv-path-config.hpp)
configure_file(kazv-version.cpp.in kazv-version.cpp)
configure_file(kazv-platform.hpp.in kazv-platform.hpp)
set(kazvprivlib_SRCS
qt-job-handler.cpp
qt-job.cpp
${CMAKE_CURRENT_BINARY_DIR}/kazv-version.cpp
matrix-sdk.cpp
matrix-room.cpp
matrix-room-list.cpp
matrix-room-timeline.cpp
matrix-room-member.cpp
matrix-room-member-list-model.cpp
matrix-event.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
device-mgmt/matrix-device.cpp
device-mgmt/matrix-device-list.cpp
kazv-config.cpp
kazv-io-manager.cpp
kazv-io-job.cpp
kazv-file.cpp
upload-job-model.cpp
kazv-markdown.cpp
shortcuts/shortcut-util.cpp
register-types.cpp
)
ecm_qt_declare_logging_category(kazvprivlib_SRCS
HEADER kazv-log.hpp
IDENTIFIER kazvLog
CATEGORY_NAME moe.kazv.mxc.kazv
)
add_library(kazvprivlib STATIC ${kazvprivlib_SRCS})
target_link_libraries(kazvprivlib PUBLIC
libkazv::kazvall
Qt5::Core
Qt5::Network
Threads::Threads
Qt5::Gui Qt5::Qml Qt5::Quick Qt5::QuickControls2 Qt5::Svg Qt5::Concurrent Qt5::Widgets
KF5::ConfigCore KF5::KIOCore KF5::Notifications
${CMARK_TARGET_NAME}
)
target_include_directories(kazvprivlib PUBLIC ${CMAKE_CURRENT_BINARY_DIR})
target_include_directories(kazvprivlib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/device-mgmt)
set(kazv_SRCS
main.cpp
resources.qrc
)
add_executable(kazv ${kazv_SRCS})
target_link_libraries(kazv
PRIVATE
kazvprivlib
)
install(TARGETS kazv ${KF5_INSTALL_TARGETS_DEFAULT_ARGS})
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/l10n/
DESTINATION ${KAZV_L10N_DIR}
FILES_MATCHING PATTERN "*.ftl"
PATTERN "*.json"
)
add_subdirectory(tests)
diff --git a/src/main.cpp b/src/main.cpp
index e71c982..fab703f 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,74 +1,75 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2021-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.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 "register-types.hpp"
#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);
}
registerKazvQmlTypes();
#if KAZV_IS_WINDOWS
qRegisterMetaType<Kazv::KazvEvent>();
+ qRegisterMetaType<MatrixStickerPackSource>();
#endif
#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")));
if (engine.rootObjects().isEmpty()) {
return -1;
}
return app.exec();
}
diff --git a/src/matrix-sdk.cpp b/src/matrix-sdk.cpp
index 69d3017..3fb7aa6 100644
--- a/src/matrix-sdk.cpp
+++ b/src/matrix-sdk.cpp
@@ -1,615 +1,621 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2024 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>
#include <filesystem>
#include <chrono>
#include <QMutex>
#include <QMutexLocker>
#include <QtConcurrent>
#include <QThreadPool>
#include <KConfig>
#include <KConfigGroup>
#include <eventemitter/lagerstoreeventemitter.hpp>
#include <client/sdk.hpp>
#include <client/notification-handler.hpp>
#include <crypto/base64.hpp>
#include <zug/util.hpp>
#include <lager/event_loop/qt.hpp>
#include "matrix-sdk.hpp"
#include "matrix-room-list.hpp"
#include "matrix-promise.hpp"
#include "matrix-event.hpp"
#include "helper.hpp"
#include "kazv-path-config.hpp"
#include "kazv-version.hpp"
#include "qt-json.hpp"
#include "qt-rand-adapter.hpp"
#include "qt-promise-handler.hpp"
#include "qt-job-handler.hpp"
#include "device-mgmt/matrix-device-list.hpp"
+#include "matrix-sticker-pack-list.hpp"
using namespace Kazv;
// Sdk with qt event loop, identity transform and no enhancers
using SdkT =
decltype(makeSdk(
SdkModel{},
detail::declref<JobInterface>(),
detail::declref<EventInterface>(),
QtPromiseHandler(detail::declref<QObject>()),
zug::identity,
withRandomGenerator(detail::declref<RandomInterface>())));
static void serializeClientToFile(Client c);
struct QtEventLoop
{
QObject *m_obj;
template<class Fn>
void async(Fn &&) { throw std::runtime_error{"not implemented!"}; }
template<class Fn>
void post(Fn &&fn)
{
QMetaObject::invokeMethod(
m_obj, std::forward<Fn>(fn), Qt::QueuedConnection
);
}
void finish() {}
void pause() { throw std::runtime_error{"not implemented!"}; }
void resume() { throw std::runtime_error{"not implemented!"}; }
};
struct MatrixSdkPrivate
{
MatrixSdkPrivate(MatrixSdk *q, bool testing);
MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model);
bool testing;
RandomInterface randomGenerator;
QThread *thread;
QObject *obj;
QtJobHandler *jobHandler;
LagerStoreEventEmitter ee;
LagerStoreEventEmitter::Watchable watchable;
SdkT sdk;
QTimer saveTimer;
using SecondaryRootT = decltype(sdk.createSecondaryRoot(std::declval<lager::with_qt_event_loop>()));
SecondaryRootT secondaryRoot;
Client clientOnSecondaryRoot;
NotificationHandler notificationHandler;
void runIoContext() {
thread->start();
}
void maybeSerialize()
{
if (!testing) {
serializeClientToFile(clientOnSecondaryRoot);
}
}
};
// Cleaning up notes:
// 0. Callback functions may store the context for an indefinite time
// 1. The QThread event loop can be stopped
// 2. QtJobHandler::submit() should only happen in the primary event loop thread
// 3. QtJobHandler lives in the primary event loop thread
// 4. QtJobs live in the primary event loop thread
// 5. Job callbacks are called in the primary event loop thread
// 6. QtPromise::then() callbacks are called in the primary event loop thread
// 7. When the QThread event loop stops, no more callbacks will be executed (there is nothing to post to)
// 8. The QThread should stop before obj is deleted
class CleanupHelper : public QObject
{
Q_OBJECT
public:
explicit CleanupHelper(std::unique_ptr<MatrixSdkPrivate> d)
: oldD(std::move(d))
{
// After the thread's event loop is finished, we can delete the root object
connect(oldD->thread, &QThread::finished, oldD->obj, &QObject::deleteLater);
connect(oldD->obj, &QObject::destroyed, this, &QObject::deleteLater);
}
std::shared_ptr<MatrixSdkPrivate> oldD;
void cleanup()
{
qCInfo(kazvLog) << "start to clean up everything";
oldD->clientOnSecondaryRoot.stopSyncing()
.then([oldD=oldD](auto &&) {
qCDebug(kazvLog) << "stopped syncing";
QMetaObject::invokeMethod(oldD->obj, [thread=oldD->thread]() {
thread->quit();
});
});
oldD->thread->wait();
oldD->thread->deleteLater();
qCInfo(kazvLog) << "thread is done";
}
};
static void serializeClientToFile(Client c)
{
using namespace Kazv::CursorOp;
auto userId = +c.userId();
auto deviceId = +c.deviceId();
if (userId.empty() || deviceId.empty()) {
qDebug() << "Not logged in, nothing to serialize";
return;
}
using StdPath = std::filesystem::path;
auto userDataDir = StdPath(kazvUserDataDir().toStdString());
auto encodedUserId = encodeBase64(userId, Base64Opts::urlSafe);
auto sessionDir = userDataDir / "sessions"
/ encodedUserId / deviceId;
auto storeFile = sessionDir / "store";
auto metadataFile = sessionDir / "metadata";
qDebug() << "storeFile=" << QString::fromStdString(storeFile.string());
std::error_code err;
if ((! std::filesystem::create_directories(sessionDir, err))
&& err) {
qDebug() << "Unable to create sessionDir";
return;
}
{
auto storeStream = std::ofstream(storeFile);
if (! storeStream) {
qDebug() << "Unable to open storeFile";
return;
}
using OAr = boost::archive::text_oarchive;
auto archive = OAr{storeStream};
c.serializeTo(archive);
qDebug() << "Serialization done";
}
// store metadata
{
KConfig metadata(QString::fromStdString(metadataFile.string()));
KConfigGroup mdGroup(&metadata, "Metadata");
mdGroup.writeEntry("kazvVersion", QString::fromStdString(kazvVersionString()));
mdGroup.writeEntry("archiveFormat", "text");
}
}
MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing)
: testing(testing)
, randomGenerator(QtRandAdapter{})
, thread(new QThread())
, obj(new QObject())
, jobHandler(new QtJobHandler(obj))
, ee{QtEventLoop{obj}}
, watchable(ee.watchable())
, sdk(makeDefaultSdkWithCryptoRandom(
randomGenerator.generateRange<std::string>(makeDefaultSdkWithCryptoRandomSize()),
static_cast<JobInterface &>(*jobHandler),
static_cast<EventInterface &>(ee),
QtPromiseHandler(*obj),
zug::identity,
withRandomGenerator(randomGenerator)))
, secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{q}))
, clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot))
, notificationHandler(clientOnSecondaryRoot.notificationHandler())
{
obj->moveToThread(thread);
}
MatrixSdkPrivate::MatrixSdkPrivate(MatrixSdk *q, bool testing, SdkModel model)
: testing(testing)
, randomGenerator(QtRandAdapter{})
, thread(new QThread())
, obj(new QObject())
, jobHandler(new QtJobHandler(obj))
, ee{QtEventLoop{obj}}
, watchable(ee.watchable())
, sdk(makeSdk(
model,
static_cast<JobInterface &>(*jobHandler),
static_cast<EventInterface &>(ee),
QtPromiseHandler(*obj),
zug::identity,
withRandomGenerator(randomGenerator)))
, secondaryRoot(sdk.createSecondaryRoot(QtEventLoop{q}, std::move(model)))
, clientOnSecondaryRoot(sdk.clientFromSecondaryRoot(secondaryRoot))
, notificationHandler(clientOnSecondaryRoot.notificationHandler())
{
obj->moveToThread(thread);
}
MatrixSdk::MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent)
: QObject(parent)
, m_d(std::move(d))
{
init();
connect(this, &MatrixSdk::trigger,
this, [](KazvEvent e) {
qDebug() << "receiving trigger:";
if (std::holds_alternative<LoginSuccessful>(e)) {
qDebug() << "Login successful";
}
});
}
void MatrixSdk::init()
{
LAGER_QT(serverUrl) = m_d->clientOnSecondaryRoot.serverUrl().xform(strToQt); Q_EMIT serverUrlChanged(serverUrl());
LAGER_QT(userId) = m_d->clientOnSecondaryRoot.userId().xform(strToQt); Q_EMIT userIdChanged(userId());
LAGER_QT(token) = m_d->clientOnSecondaryRoot.token().xform(strToQt); Q_EMIT tokenChanged(token());
LAGER_QT(deviceId) = m_d->clientOnSecondaryRoot.deviceId().xform(strToQt); Q_EMIT deviceIdChanged(deviceId());
m_d->watchable.afterAll(
[this](KazvEvent e) {
Q_EMIT this->trigger(e);
});
m_d->watchable.after<LoginSuccessful>(
[this](LoginSuccessful e) {
Q_EMIT this->loginSuccessful(e);
});
m_d->watchable.after<LoginFailed>(
[this](LoginFailed e) {
Q_EMIT this->loginFailed(
QString::fromStdString(e.errorCode),
QString::fromStdString(e.error)
);
});
m_d->watchable.after<ReceivingRoomTimelineEvent>(
[this](ReceivingRoomTimelineEvent e) {
Q_EMIT this->receivedMessage(
QString::fromStdString(e.roomId),
QString::fromStdString(e.event.id())
);
});
connect(&m_d->saveTimer, &QTimer::timeout, &m_d->saveTimer, [m_d=m_d.get()]() {
m_d->maybeSerialize();
});
const int saveIntervalMs = 1000 * 60 * 5;
m_d->saveTimer.start(std::chrono::milliseconds{saveIntervalMs});
}
MatrixSdk::MatrixSdk(QObject *parent)
: MatrixSdk(std::make_unique<MatrixSdkPrivate>(this, /* testing = */ false), parent)
{
}
MatrixSdk::MatrixSdk(SdkModel model, bool testing, QObject *parent)
: MatrixSdk(std::make_unique<MatrixSdkPrivate>(this, testing, std::move(model)), parent)
{
}
static void cleanupDPointer(std::unique_ptr<MatrixSdkPrivate> oldD)
{
oldD->saveTimer.disconnect();
oldD->saveTimer.stop();
auto helper = new CleanupHelper(std::move(oldD));
helper->cleanup();
}
MatrixSdk::~MatrixSdk()
{
if (m_d) {
serializeToFile();
cleanupDPointer(std::move(m_d));
}
}
QString MatrixSdk::mxcUriToHttp(QString mxcUri) const
{
return QString::fromStdString(m_d->clientOnSecondaryRoot.mxcUriToHttp(mxcUri.toStdString()));
}
MatrixDeviceList *MatrixSdk::devicesOfUser(QString userId) const
{
return new MatrixDeviceList(m_d->clientOnSecondaryRoot.devicesOfUser(userId.toStdString()));
}
void MatrixSdk::login(const QString &userId, const QString &password)
{
m_d->clientOnSecondaryRoot
.autoDiscover(userId.toStdString())
.then([
this,
client=m_d->clientOnSecondaryRoot.toEventLoop(),
userId,
password
](auto res) {
if (!res.success()) {
// FIXME use real error codes and msgs when available in libkazv
Q_EMIT this->discoverFailed("", "");
return res;
}
auto serverUrl = res.dataStr("homeserverUrl");
client.passwordLogin(
serverUrl,
userId.toStdString(),
password.toStdString(),
"kazv"
);
return res;
});
m_d->runIoContext();
}
MatrixRoomList *MatrixSdk::roomList() const
{
return new MatrixRoomList(m_d->clientOnSecondaryRoot);
}
void MatrixSdk::emplace(std::optional<SdkModel> model)
{
auto testing = m_d->testing;
if (m_d) {
cleanupDPointer(std::move(m_d));
}
m_d = (model.has_value()
? std::make_unique<MatrixSdkPrivate>(this, testing, std::move(model.value()))
: std::make_unique<MatrixSdkPrivate>(this, testing));
// Re-initialize lager-qt cursors and watchable connections
init();
m_d->runIoContext();
m_d->clientOnSecondaryRoot.startSyncing();
Q_EMIT sessionChanged();
}
QStringList MatrixSdk::allSessions() const
{
using StdPath = std::filesystem::path;
auto userDataDir = StdPath(kazvUserDataDir().toStdString());
auto allSessionsDir = userDataDir / "sessions";
QStringList sessionNames;
try {
for (const auto &p : std::filesystem::directory_iterator(allSessionsDir)) {
if (p.is_directory()) {
auto maybeEncodedUserId = p.path().filename().string();
auto userId = (!maybeEncodedUserId.empty() && maybeEncodedUserId[0] == '@') ? maybeEncodedUserId : decodeBase64(maybeEncodedUserId, Base64Opts::urlSafe);
for (const auto &q : std::filesystem::directory_iterator(p.path())) {
auto path = q.path();
auto deviceId = path.filename().string();
std::error_code err;
if (std::filesystem::exists(path / "store", err)) {
sessionNames.append(QString::fromStdString(userId + "/" + deviceId));
}
}
}
}
} catch (const std::filesystem::filesystem_error &) {
qDebug() << "sessionDir not available, ignoring";
}
return sessionNames;
}
void MatrixSdk::serializeToFile() const
{
m_d->maybeSerialize();
}
bool MatrixSdk::loadSession(QString sessionName)
{
using StdPath = std::filesystem::path;
auto loadFromSession = [this, sessionName](StdPath sessionDir) {
auto storeFile = sessionDir / "store";
auto metadataFile = sessionDir / "metadata";
if (! std::filesystem::exists(storeFile)) {
qDebug() << "storeFile does not exist, skip loading session " << sessionName;
return false;
}
if (std::filesystem::exists(metadataFile)) {
KConfig metadata(QString::fromStdString(metadataFile.string()));
KConfigGroup mdGroup(&metadata, "Metadata");
auto format = mdGroup.readEntry("archiveFormat");
if (format != QStringLiteral("text")) {
qDebug() << "Unknown archive format:" << format;
return false;
}
auto version = mdGroup.readEntry("kazvVersion");
auto curVersion = kazvVersionString();
if (version != QString::fromStdString(curVersion)) {
qDebug() << "A different version from the current one, making a backup";
std::error_code err;
auto now = std::chrono::system_clock::now();
auto backupName =
std::to_string(std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count());
auto backupDir = sessionDir / "backup" / backupName;
if (! std::filesystem::create_directories(backupDir, err)
&& err) {
qDebug() << "Cannot create backup directory";
return false;
}
std::filesystem::copy_file(storeFile, backupDir / "store");
std::filesystem::copy_file(metadataFile, backupDir / "metadata");
}
}
SdkModel model;
try {
auto storeStream = std::ifstream(storeFile);
if (! storeStream) {
qDebug() << "Unable to open storeFile";
return false;
}
using IAr = boost::archive::text_iarchive;
auto archive = IAr{storeStream};
archive >> model;
qDebug() << "Finished loading session";
} catch (const std::exception &e) {
qDebug() << "Error when loading session:" << QString::fromStdString(e.what());
return false;
}
emplace(std::move(model));
return true;
};
qDebug() << "in loadSession(), sessionName=" << sessionName;
auto userDataDir = StdPath(kazvUserDataDir().toStdString());
auto parts = sessionName.split('/');
if (parts.size() == 2) {
auto userId = parts[0].toStdString();
auto sessionId = parts[1].toStdString();
auto encodedUserId = encodeBase64(userId, Base64Opts::urlSafe);
qCDebug(kazvLog) << "trying new path";
auto sessionDir = userDataDir / "sessions" / encodedUserId / sessionId;
if (loadFromSession(sessionDir)) {
qCDebug(kazvLog) << "new path works";
return true;
}
qCDebug(kazvLog) << "trying legacy path";
auto legacySessionDir = userDataDir / "sessions" / userId / sessionId;
return loadFromSession(legacySessionDir);
}
qDebug(kazvLog) << "no session found for" << sessionName;
return false;
}
bool MatrixSdk::startNewSession()
{
emplace(std::nullopt);
return true;
}
static std::optional<std::string> optMaybe(QString s)
{
if (s.isEmpty()) {
return std::nullopt;
} else {
return s.toStdString();
}
}
MatrixPromise *MatrixSdk::createRoom(
bool isPrivate,
const QString &name,
const QString &alias,
const QStringList &invite,
bool isDirect,
bool allowFederate,
const QString &topic,
const QJsonValue &powerLevelContentOverride,
CreateRoomPreset preset
)
{
return new MatrixPromise(m_d->clientOnSecondaryRoot.createRoom(
isPrivate ? Kazv::RoomVisibility::Private : Kazv::RoomVisibility::Public,
optMaybe(name),
optMaybe(alias),
qStringListToStdF(invite),
isDirect,
allowFederate,
optMaybe(topic),
nlohmann::json(powerLevelContentOverride),
static_cast<Kazv::CreateRoomPreset>(preset)
));
}
MatrixPromise *MatrixSdk::joinRoom(const QString &idOrAlias, const QStringList &servers)
{
return new MatrixPromise(m_d->clientOnSecondaryRoot.joinRoom(
idOrAlias.toStdString(),
qStringListToStdF(servers)
)
.then([this, idOrAlias, client=m_d->clientOnSecondaryRoot.toEventLoop()](auto stat) {
if (stat.success()) {
Q_EMIT joinRoomSuccessful(idOrAlias);
} else {
Q_EMIT joinRoomFailed(idOrAlias, "", "");
}
}));
}
MatrixPromise *MatrixSdk::setDeviceTrustLevel(QString userId, QString deviceId, QString trustLevel)
{
return new MatrixPromise(
m_d->clientOnSecondaryRoot.setDeviceTrustLevel(
userId.toStdString(),
deviceId.toStdString(),
qStringToTrustLevelFunc(trustLevel)
)
);
}
MatrixPromise *MatrixSdk::getSelfProfile()
{
return new MatrixPromise(
m_d->clientOnSecondaryRoot.getProfile(userId().toStdString())
);
}
MatrixPromise *MatrixSdk::setDisplayName(QString displayName)
{
return new MatrixPromise(
m_d->clientOnSecondaryRoot.setDisplayName(
displayName.isEmpty() ? std::nullopt : std::optional<std::string>(displayName.toStdString())
)
);
}
MatrixPromise *MatrixSdk::setAvatarUrl(QString avatarUrl)
{
return new MatrixPromise(
m_d->clientOnSecondaryRoot.setAvatarUrl(
avatarUrl.isEmpty() ? std::nullopt : std::optional<std::string>(avatarUrl.toStdString())
)
);
}
void MatrixSdk::startThread()
{
m_d->runIoContext();
}
RandomInterface &MatrixSdk::randomGenerator() const
{
return m_d->randomGenerator;
}
bool MatrixSdk::shouldNotify(MatrixEvent *event) const
{
// Do not notify own event
if (event->sender() == userId()) {
return false;
}
return m_d->notificationHandler.handleNotification(event->underlyingEvent()).shouldNotify;
}
+MatrixStickerPackList *MatrixSdk::stickerPackList() const
+{
+ return new MatrixStickerPackList(m_d->clientOnSecondaryRoot);
+}
+
#include "matrix-sdk.moc"
diff --git a/src/matrix-sdk.hpp b/src/matrix-sdk.hpp
index 8e4e396..ab70eb5 100644
--- a/src/matrix-sdk.hpp
+++ b/src/matrix-sdk.hpp
@@ -1,205 +1,213 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <QObject>
#include <QQmlEngine>
#include <QString>
#include <memory>
#include <lager/extra/qt.hpp>
#include <sdk-model.hpp>
#include <random-generator.hpp>
#include "meta-types.hpp"
class MatrixRoomList;
class MatrixDeviceList;
class MatrixPromise;
class MatrixSdkTest;
class MatrixEvent;
+class MatrixStickerPackList;
struct MatrixSdkPrivate;
class MatrixSdk : public QObject
{
Q_OBJECT
QML_ELEMENT
std::unique_ptr<MatrixSdkPrivate> m_d;
/// @param d A dynamically allocated d-pointer, whose ownership
/// will be transferred to this MatrixSdk.
explicit MatrixSdk(std::unique_ptr<MatrixSdkPrivate> d, QObject *parent);
void init();
public:
enum CreateRoomPreset {
PrivateChat = Kazv::CreateRoomPreset::PrivateChat,
PublicChat = Kazv::CreateRoomPreset::PublicChat,
TrustedPrivateChat = Kazv::CreateRoomPreset::TrustedPrivateChat,
};
Q_ENUM(CreateRoomPreset);
explicit MatrixSdk(QObject *parent = 0);
~MatrixSdk() override;
LAGER_QT_READER(QString, serverUrl);
LAGER_QT_READER(QString, userId);
LAGER_QT_READER(QString, token);
LAGER_QT_READER(QString, deviceId);
Q_INVOKABLE MatrixRoomList *roomList() const;
Q_INVOKABLE QString mxcUriToHttp(QString mxcUri) const;
Q_INVOKABLE MatrixDeviceList *devicesOfUser(QString userId) const;
Kazv::RandomInterface &randomGenerator() const;
private:
// Replaces the store with another one
void emplace(std::optional<Kazv::SdkModel> model);
Q_SIGNALS:
void trigger(Kazv::KazvEvent e);
void loginSuccessful(Kazv::KazvEvent e);
void loginFailed(QString errorCode, QString errorMsg);
void discoverFailed(QString errorCode, QString errorMsg);
void joinRoomSuccessful(const QString &idOrAlias);
void joinRoomFailed(const QString &idOrAlias, const QString &errorCode, const QString &errorMsg);
void receivedMessage(QString roomId, QString eventId);
void sessionChanged();
public Q_SLOTS:
void login(const QString &userId, const QString &password);
/**
* Serialize data to <AppDataDir>/sessions/<userid>/<deviceid>/
*
* If not logged in, do nothing.
*/
void serializeToFile() const;
/**
* Load session at <AppDataDir>/sessions/<sessionName> .
*
* @param sessionName A string in the form of <userid>/<deviceid> .
*
* @return true if successful, false otherwise.
*/
bool loadSession(QString sessionName);
/**
* Start an empty session.
*
* The new session is not logged in, and need to call login().
*
* @return true if successful, false otherwise.
*/
bool startNewSession();
/**
* Get all saved sessions.
*
* @return A list of session names in the form of <userid>/<deviceid> .
*/
QStringList allSessions() const;
/**
* Create a new room.
*
* @param isPrivate Whether the room is private.
* @param name The room's name.
* @param alias The alias of the room.
* @param invite List of matrix ids of users to invite.
* @param isDirect Whether it is a direct message room.
* @param allowFederate Whether to allow users on other servers to join.
* @param topic The topic of the room.
* @param powerLevelContentOverride The content to override m.room.power_levels event.
* @param preset The preset to create the room with.
*/
MatrixPromise *createRoom(
bool isPrivate,
const QString &name,
const QString &alias,
const QStringList &invite,
bool isDirect,
bool allowFederate,
const QString &topic,
const QJsonValue &powerLevelContentOverride,
CreateRoomPreset preset
);
/**
* Join a room.
* @param idOrAlias The id or alias of the room to join.
* @param servers The servers to use when joining the room.
*/
MatrixPromise *joinRoom(
const QString &idOrAlias,
const QStringList &servers
);
/**
* Change the trust level of a device.
*
* @param userId The user id that owns the device.
* @param deviceId The device id to set the trust level.
* @param trustLevel The trust level.
*
* @return A MatrixPromise representing the progress.
*/
MatrixPromise *setDeviceTrustLevel(QString userId, QString deviceId, QString trustLevel);
/**
* Get the profile of the current user.
*
* @return A MatrixPromise representing the progress.
*/
MatrixPromise *getSelfProfile();
/**
* Set the display name of the current user.
*
* @return A MatrixPromise representing the progress.
*/
MatrixPromise *setDisplayName(QString displayName);
/**
* Set the avatar url of the current user.
*
* @return A MatrixPromise representing the progress.
*/
MatrixPromise *setAvatarUrl(QString avatarUrl);
/**
* Check if an event should be notified.
*
* @param event The event to check.
* @return Whether `event` should be notified.
*/
bool shouldNotify(MatrixEvent *event) const;
+ /**
+ * Get the sticker pack list for the current account.
+ *
+ * @return A list of sticker packs associated with the current account.
+ */
+ MatrixStickerPackList *stickerPackList() const;
+
private: // Testing
friend MatrixSdkTest;
friend MatrixSdk *makeTestSdk(Kazv::SdkModel model);
explicit MatrixSdk(Kazv::SdkModel model, bool testing = false, QObject *parent = 0);
void startThread();
};
diff --git a/src/matrix-sticker-pack-list.cpp b/src/matrix-sticker-pack-list.cpp
new file mode 100644
index 0000000..0894b15
--- /dev/null
+++ b/src/matrix-sticker-pack-list.cpp
@@ -0,0 +1,90 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libkazv-config.hpp>
+#include <immer/config.hpp>
+
+#include <lager/lenses/optional.hpp>
+#include <lager/lenses/at.hpp>
+
+#include "matrix-sticker-pack.hpp"
+#include "matrix-sticker-pack-list.hpp"
+
+using namespace Kazv;
+
+static const std::string accountDataEventType = "im.ponies.user_emotes";
+
+lager::reader<immer::flex_vector<MatrixStickerPackSource>> getEventsFromClient(Client client)
+{
+ lager::reader<Event> userEmotes = client.accountData()[accountDataEventType][lager::lenses::or_default];
+ return userEmotes.map([](Event e) {
+ return immer::flex_vector<MatrixStickerPackSource>{
+ {MatrixStickerPackSource::AccountData, accountDataEventType, e},
+ };
+ });
+}
+
+MatrixStickerPackList::MatrixStickerPackList(Client client, QObject *parent)
+ : QAbstractListModel(parent)
+ , m_client(client)
+ , m_events(getEventsFromClient(m_client))
+ , m_internalCount(0)
+ , LAGER_QT(count)(m_events.map([](const auto &events) {
+ return static_cast<int>(events.size());
+ }))
+{
+ m_internalCount = count();
+
+ connect(this, &MatrixStickerPackList::countChanged, this, &MatrixStickerPackList::updateInternalCount);
+}
+
+MatrixStickerPackList::~MatrixStickerPackList() = default;
+
+MatrixStickerPack *MatrixStickerPackList::at(int index) const
+{
+ return new MatrixStickerPack(m_events.map([index](const auto &events) {
+ if (events.size() > std::size_t(index)) {
+ return events[index];
+ } else {
+ return MatrixStickerPackSource{
+ MatrixStickerPackSource::AccountData,
+ accountDataEventType,
+ Event(),
+ };
+ }
+ }));
+}
+
+QVariant MatrixStickerPackList::data(const QModelIndex &/* index */, int /* role */) const
+{
+ return QVariant();
+}
+
+int MatrixStickerPackList::rowCount(const QModelIndex &parent = QModelIndex()) const
+{
+ if (parent.isValid()) {
+ return 0;
+ } else {
+ return count();
+ }
+}
+
+void MatrixStickerPackList::updateInternalCount()
+{
+ auto curCount = count();
+ auto oldCount = m_internalCount;
+ auto diff = std::abs(curCount - oldCount);
+
+ if (curCount > oldCount) {
+ beginInsertRows(QModelIndex(), 0, diff - 1);
+ m_internalCount = curCount;
+ endInsertRows();
+ } else if (curCount < oldCount) {
+ beginRemoveRows(QModelIndex(), 0, diff - 1);
+ m_internalCount = curCount;
+ endRemoveRows();
+ }
+}
diff --git a/src/matrix-sticker-pack.hpp b/src/matrix-sticker-pack-list.hpp
similarity index 59%
copy from src/matrix-sticker-pack.hpp
copy to src/matrix-sticker-pack-list.hpp
index d9be0ea..5aa21c2 100644
--- a/src/matrix-sticker-pack.hpp
+++ b/src/matrix-sticker-pack-list.hpp
@@ -1,45 +1,50 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <immer/config.hpp>
#include <QObject>
#include <QQmlEngine>
#include <QAbstractListModel>
+#include <immer/flex_vector.hpp>
+
#include <lager/reader.hpp>
#include <lager/extra/qt.hpp>
+#include <client/client.hpp>
#include <base/event.hpp>
-class MatrixSticker;
+#include "matrix-sticker-pack-source.hpp"
+
+class MatrixStickerPack;
-class MatrixStickerPack : public QAbstractListModel
+class MatrixStickerPackList : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
- lager::reader<Kazv::Event> m_event;
- lager::reader<Kazv::json> m_images;
+ Kazv::Client m_client;
+ lager::reader<immer::flex_vector<MatrixStickerPackSource>> m_events;
int m_internalCount;
public:
- explicit MatrixStickerPack(lager::reader<Kazv::Event> event, QObject *parent = 0);
- ~MatrixStickerPack() override;
+ explicit MatrixStickerPackList(Kazv::Client client, QObject *parent = 0);
+ ~MatrixStickerPackList() override;
LAGER_QT_READER(int, count);
- Q_INVOKABLE MatrixSticker *at(int index) const;
+ Q_INVOKABLE MatrixStickerPack *at(int index) const;
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override;
private Q_SLOTS:
void updateInternalCount();
};
diff --git a/src/matrix-sticker-pack-source.cpp b/src/matrix-sticker-pack-source.cpp
new file mode 100644
index 0000000..f6e4b3c
--- /dev/null
+++ b/src/matrix-sticker-pack-source.cpp
@@ -0,0 +1,22 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#include <libkazv-config.hpp>
+#include <immer/config.hpp>
+
+#include "matrix-sticker-pack-source.hpp"
+
+bool operator==(const MatrixStickerPackSource &a, const MatrixStickerPackSource &b)
+{
+ return a.source == b.source
+ && b.eventType == b.eventType
+ && a.event == b.event;
+}
+
+bool operator!=(const MatrixStickerPackSource &a, const MatrixStickerPackSource &b)
+{
+ return !(a == b);
+}
diff --git a/src/matrix-sticker-pack-source.hpp b/src/matrix-sticker-pack-source.hpp
new file mode 100644
index 0000000..25c0c8f
--- /dev/null
+++ b/src/matrix-sticker-pack-source.hpp
@@ -0,0 +1,28 @@
+/*
+ * This file is part of kazv.
+ * SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+#pragma once
+#include <libkazv-config.hpp>
+#include <immer/config.hpp>
+
+#include <base/event.hpp>
+
+struct MatrixStickerPackSource
+{
+ enum Source
+ {
+ AccountData,
+ RoomState,
+ };
+
+ Source source;
+ std::string eventType;
+ Kazv::Event event;
+};
+
+bool operator==(const MatrixStickerPackSource &a, const MatrixStickerPackSource &b);
+
+bool operator!=(const MatrixStickerPackSource &a, const MatrixStickerPackSource &b);
diff --git a/src/matrix-sticker-pack.cpp b/src/matrix-sticker-pack.cpp
index fdfef27..21fc826 100644
--- a/src/matrix-sticker-pack.cpp
+++ b/src/matrix-sticker-pack.cpp
@@ -1,98 +1,98 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <immer/config.hpp>
#include <cursorutil.hpp>
#include "matrix-sticker.hpp"
#include "matrix-sticker-pack.hpp"
using namespace Kazv;
-MatrixStickerPack::MatrixStickerPack(lager::reader<Kazv::Event> event, QObject *parent)
+MatrixStickerPack::MatrixStickerPack(lager::reader<MatrixStickerPackSource> source, QObject *parent)
: QAbstractListModel(parent)
- , m_event(event)
+ , m_event(source[&MatrixStickerPackSource::event])
, m_images(m_event.xform(
eventContent
| zug::map([](const JsonWrap &content) {
if (content.get().contains("images")
&& content.get()["images"].is_object()) {
auto size = content.get()["images"].size();
auto items = content.get()["images"].items();
auto array = json(size, json::object());
std::transform(
items.begin(), items.end(),
array.begin(), [](const auto &it) {
return json::array({it.key(), it.value()});
}
);
return array;
} else {
return json::array();
}
})))
, m_internalCount(0)
, LAGER_QT(count)(m_images.map([](const JsonWrap &images) -> int {
return images.get().size();
}))
{
m_internalCount = count();
connect(this, &MatrixStickerPack::countChanged, this, &MatrixStickerPack::updateInternalCount);
}
MatrixStickerPack::~MatrixStickerPack() = default;
MatrixSticker *MatrixStickerPack::at(int index) const
{
using namespace nlohmann::literals;
auto image = m_images.map([index](const json &j) {
if (j.size() > std::size_t(index)) {
return j[index];
} else {
return json::array();
}
}).make();
return new MatrixSticker(
image.xform(jsonAtOr("/0"_json_pointer, json(std::string())) | zug::map([](const json &j) {
return j.template get<std::string>();
})),
image.xform(jsonAtOr("/1"_json_pointer, json::object()))
);
}
QVariant MatrixStickerPack::data(const QModelIndex &/* index */, int /* role */) const
{
return QVariant();
}
int MatrixStickerPack::rowCount(const QModelIndex &parent = QModelIndex()) const
{
if (parent.isValid()) {
return 0;
} else {
return count();
}
}
void MatrixStickerPack::updateInternalCount()
{
auto curCount = count();
auto oldCount = m_internalCount;
auto diff = std::abs(curCount - oldCount);
if (curCount > oldCount) {
beginInsertRows(QModelIndex(), 0, diff - 1);
m_internalCount = curCount;
endInsertRows();
} else if (curCount < oldCount) {
beginRemoveRows(QModelIndex(), 0, diff - 1);
m_internalCount = curCount;
endRemoveRows();
}
}
diff --git a/src/matrix-sticker-pack.hpp b/src/matrix-sticker-pack.hpp
index d9be0ea..75e765f 100644
--- a/src/matrix-sticker-pack.hpp
+++ b/src/matrix-sticker-pack.hpp
@@ -1,45 +1,47 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <immer/config.hpp>
#include <QObject>
#include <QQmlEngine>
#include <QAbstractListModel>
#include <lager/reader.hpp>
#include <lager/extra/qt.hpp>
#include <base/event.hpp>
+#include "matrix-sticker-pack-source.hpp"
+
class MatrixSticker;
class MatrixStickerPack : public QAbstractListModel
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
lager::reader<Kazv::Event> m_event;
lager::reader<Kazv::json> m_images;
int m_internalCount;
public:
- explicit MatrixStickerPack(lager::reader<Kazv::Event> event, QObject *parent = 0);
+ explicit MatrixStickerPack(lager::reader<MatrixStickerPackSource> source, QObject *parent = 0);
~MatrixStickerPack() override;
LAGER_QT_READER(int, count);
Q_INVOKABLE MatrixSticker *at(int index) const;
QVariant data(const QModelIndex &index, int role) const override;
int rowCount(const QModelIndex &parent) const override;
private Q_SLOTS:
void updateInternalCount();
};
diff --git a/src/meta-types.cpp b/src/meta-types.cpp
index 2b577ce..32d0683 100644
--- a/src/meta-types.cpp
+++ b/src/meta-types.cpp
@@ -1,17 +1,18 @@
/*
* This file is part of kazv.
- * SPDX-FileCopyrightText: 2020 Tusooa Zhu <tusooa@kazv.moe>
+ * SPDX-FileCopyrightText: 2020-2024 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include "meta-types.hpp"
#include "kazv-platform.hpp"
#if !KAZV_IS_WINDOWS
KazvMetaTypeRegistration KazvMetaTypeRegistration::instance{
- qRegisterMetaType<Kazv::KazvEvent>()
+ qRegisterMetaType<Kazv::KazvEvent>(),
+ qRegisterMetaType<MatrixStickerPackSource>(),
};
#endif
diff --git a/src/meta-types.hpp b/src/meta-types.hpp
index 8536cf5..64bab16 100644
--- a/src/meta-types.hpp
+++ b/src/meta-types.hpp
@@ -1,25 +1,28 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2020 Tusooa Zhu <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#pragma once
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <QMetaType>
#include <base/kazvevents.hpp>
+#include "matrix-sticker-pack-source.hpp"
#include "kazv-platform.hpp"
Q_DECLARE_METATYPE(Kazv::KazvEvent)
+Q_DECLARE_METATYPE(MatrixStickerPackSource)
#if !KAZV_IS_WINDOWS
class KazvMetaTypeRegistration
{
static KazvMetaTypeRegistration instance;
public:
int m_kazvEvent;
+ int m_matrixStickerPackSource;
};
#endif
diff --git a/src/register-types.cpp b/src/register-types.cpp
index cadd61a..51d4bae 100644
--- a/src/register-types.cpp
+++ b/src/register-types.cpp
@@ -1,47 +1,53 @@
/*
* This file is part of kazv.
- * SPDX-FileCopyrightText: 2021-2023 tusooa <tusooa@kazv.moe>
+ * SPDX-FileCopyrightText: 2021-2024 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <QtQml>
#include "matrix-sdk.hpp"
#include "matrix-room-list.hpp"
#include "matrix-room.hpp"
#include "matrix-room-timeline.hpp"
#include "matrix-room-member.hpp"
#include "matrix-room-member-list-model.hpp"
#include "matrix-event.hpp"
#include "l10n-provider.hpp"
#include "kazv-config.hpp"
#include "kazv-io-manager.hpp"
#include "shortcuts/shortcut-util.hpp"
#include "kazv-io-job.hpp"
#include "device-mgmt/matrix-device-list.hpp"
#include "device-mgmt/matrix-device.hpp"
#include "matrix-promise.hpp"
#include "kazv-util.hpp"
+#include "matrix-sticker.hpp"
+#include "matrix-sticker-pack.hpp"
+#include "matrix-sticker-pack-list.hpp"
void registerKazvQmlTypes()
{
qmlRegisterType<MatrixSdk>("moe.kazv.mxc.kazv", 0, 0, "MatrixSdk");
qmlRegisterType<L10nProvider>("moe.kazv.mxc.kazv", 0, 0, "L10nProvider");
qmlRegisterType<KazvConfig>("moe.kazv.mxc.kazv", 0, 0, "KazvConfig");
qmlRegisterType<KazvIOBaseJob>("moe.kazv.mxc.kazv", 0, 0, "KazvIOBaseJob");
qmlRegisterType<KazvIOManager>("moe.kazv.mxc.kazv", 0, 0, "KazvIOManager");
qmlRegisterType<UploadJobModel>("moe.kazv.mxc.kazv", 0, 0, "UploadJobModel");
qmlRegisterUncreatableType<MatrixRoomList>("moe.kazv.mxc.kazv", 0, 0, "MatrixRoomList", "");
qmlRegisterUncreatableType<MatrixRoom>("moe.kazv.mxc.kazv", 0, 0, "MatrixRoom", "");
qmlRegisterUncreatableType<MatrixRoomMember>("moe.kazv.mxc.kazv", 0, 0, "MatrixRoomMember", "");
qmlRegisterUncreatableType<MatrixRoomMemberListModel>("moe.kazv.mxc.kazv", 0, 0, "MatrixRoomMemberListModel", "");
qmlRegisterUncreatableType<MatrixRoomTimeline>("moe.kazv.mxc.kazv", 0, 0, "MatrixRoomTimeline", "");
qmlRegisterUncreatableType<MatrixEvent>("moe.kazv.mxc.kazv", 0, 0, "MatrixEvent", "");
qmlRegisterUncreatableType<MatrixDeviceList>("moe.kazv.mxc.kazv", 0, 0, "MatrixDeviceList", "");
qmlRegisterUncreatableType<MatrixDevice>("moe.kazv.mxc.kazv", 0, 0, "MatrixDevice", "");
qmlRegisterUncreatableType<MatrixPromise>("moe.kazv.mxc.kazv", 0, 0, "MatrixPromise", "");
+ qmlRegisterUncreatableType<MatrixSticker>("moe.kazv.mxc.kazv", 0, 0, "MatrixSticker", "");
+ qmlRegisterUncreatableType<MatrixStickerPack>("moe.kazv.mxc.kazv", 0, 0, "MatrixStickerPack", "");
+ qmlRegisterUncreatableType<MatrixStickerPackList>("moe.kazv.mxc.kazv", 0, 0, "MatrixStickerPackList", "");
qmlRegisterSingletonInstance<KazvUtil>("moe.kazv.mxc.kazv", 0, 0, "KazvUtil", new KazvUtil());
qmlRegisterSingletonInstance<ShortcutUtil>("moe.kazv.mxc.kazvshortcuts", 0, 0, "ShortcutUtil", new ShortcutUtil());
}
diff --git a/src/tests/matrix-sticker-pack-test.cpp b/src/tests/matrix-sticker-pack-test.cpp
index 38a7387..6eb951d 100644
--- a/src/tests/matrix-sticker-pack-test.cpp
+++ b/src/tests/matrix-sticker-pack-test.cpp
@@ -1,79 +1,103 @@
/*
* This file is part of kazv.
* SPDX-FileCopyrightText: 2024 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include <libkazv-config.hpp>
#include <immer/config.hpp> // https://github.com/arximboldi/immer/issues/168
#include <memory>
#include <QtTest>
#include <lager/state.hpp>
#include <base/event.hpp>
+#include <testfixtures/factory.hpp>
+
+#include "test-model.hpp"
#include "test-utils.hpp"
+#include "matrix-sticker-pack-list.hpp"
#include "matrix-sticker-pack.hpp"
#include "matrix-sticker.hpp"
+#include "matrix-sdk.hpp"
using namespace Kazv;
+using namespace Kazv::Factory;
class MatrixStickerPackTest : public QObject
{
Q_OBJECT
private Q_SLOTS:
void testStickerPack();
+ void testStickerPackList();
};
// https://github.com/Sorunome/matrix-doc/blob/soru/emotes/proposals/2545-emotes.md
static auto stickerPackEvent = Event{R"({
"content": {
"images": {
"myemote": {
"url": "mxc://example.org/blah"
},
"mysticker": {
"body": "my sticker",
"url": "mxc://example.org/sticker",
"usage": ["sticker"],
"info": {
}
}
},
"pack": {
"display_name": "Awesome Pack",
"usage": ["emoticon"]
}
},
"type": "im.ponies.user_emotes"
})"_json};
void MatrixStickerPackTest::testStickerPack()
{
- auto eventCursor = lager::make_state(stickerPackEvent, lager::automatic_tag{});
+ auto sourceCursor = lager::make_state(MatrixStickerPackSource{
+ MatrixStickerPackSource::AccountData,
+ "im.ponies.user_emotes",
+ stickerPackEvent,
+ }, lager::automatic_tag{});
- auto stickerPack = toUniquePtr(new MatrixStickerPack(eventCursor));
+ auto stickerPack = toUniquePtr(new MatrixStickerPack(sourceCursor));
QCOMPARE(stickerPack->rowCount(QModelIndex()), 2);
QCOMPARE(stickerPack->count(), 2);
auto sticker0 = toUniquePtr(stickerPack->at(0));
QCOMPARE(sticker0->shortCode(), QStringLiteral("myemote"));
QCOMPARE(sticker0->body(), QStringLiteral("myemote"));
QCOMPARE(sticker0->mxcUri(), QStringLiteral("mxc://example.org/blah"));
QCOMPARE(sticker0->info(), QJsonObject());
auto sticker1 = toUniquePtr(stickerPack->at(1));
QCOMPARE(sticker1->shortCode(), QStringLiteral("mysticker"));
QCOMPARE(sticker1->body(), QStringLiteral("my sticker"));
QCOMPARE(sticker1->mxcUri(), QStringLiteral("mxc://example.org/sticker"));
QCOMPARE(sticker1->info(), QJsonObject());
}
+void MatrixStickerPackTest::testStickerPackList()
+{
+ auto model = makeClient(withAccountData({stickerPackEvent}));
+ std::unique_ptr<MatrixSdk> sdk{makeTestSdk(SdkModel{model})};
+ auto stickerPackList = toUniquePtr(sdk->stickerPackList());
+
+ QCOMPARE(stickerPackList->rowCount(QModelIndex()), 1);
+ QCOMPARE(stickerPackList->count(), 1);
+
+ auto stickerPack = toUniquePtr(stickerPackList->at(0));
+ QCOMPARE(stickerPack->count(), 2);
+}
+
QTEST_MAIN(MatrixStickerPackTest)
#include "matrix-sticker-pack-test.moc"

File Metadata

Mime Type
text/x-diff
Expires
Sun, Jul 19, 11:54 PM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1695393
Default Alt Text
(48 KB)

Event Timeline