Port RoomManager to C++

This also makes it possible to handle the Matrix URI
This commit is contained in:
Carl Schwan
2021-04-27 21:07:10 +00:00
parent a2a6983123
commit b7d98fc6d9
13 changed files with 517 additions and 125 deletions

View File

@@ -13,6 +13,7 @@ add_executable(neochat
messageeventmodel.cpp
messagefiltermodel.cpp
roomlistmodel.cpp
roommanager.cpp
neochatroom.cpp
neochatuser.cpp
userlistmodel.cpp

View File

@@ -10,6 +10,7 @@
#include <QQmlContext>
#include <QQuickStyle>
#include <QQuickWindow>
#include <QDebug>
#include <KAboutData>
#ifdef HAVE_KDBUSADDONS
@@ -41,8 +42,9 @@
#include "neochatuser.h"
#include "notificationsmanager.h"
#include "publicroomlistmodel.h"
#include "room.h"
#include <room.h>
#include "roomlistmodel.h"
#include "roommanager.h"
#include "sortfilterroomlistmodel.h"
#include "userdirectorylistmodel.h"
#include "userlistmodel.h"
@@ -99,6 +101,17 @@ int main(int argc, char *argv[])
#ifdef HAVE_KDBUSADDONS
KDBusService service(KDBusService::Unique);
service.connect(&service,
&KDBusService::activateRequested,
roomManager,
[](const QStringList &arguments, const QString &workingDirectory) {
Q_UNUSED(workingDirectory);
auto args = arguments;
args.removeFirst();
for (const auto &arg : args) {
roomManager->openResource(arg);
}
});
#endif
#ifdef NEOCHAT_FLATPAK
@@ -117,6 +130,7 @@ int main(int argc, char *argv[])
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Controller", &Controller::instance());
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Clipboard", &clipboard);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "Config", config);
qmlRegisterSingletonInstance<RoomManager>("org.kde.neochat", 1, 0, "RoomManager", roomManager);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "FileType", &fileTypeSingleton);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "LoginHelper", login);
qmlRegisterSingletonInstance("org.kde.neochat", 1, 0, "ChatBoxHelper", &chatBoxHelper);
@@ -160,6 +174,7 @@ int main(int argc, char *argv[])
QCommandLineParser parser;
parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
parser.addPositionalArgument(QStringLiteral("urls"), i18n("Supports appstream: url scheme"));
about.setupCommandLine(&parser);
parser.process(app);
@@ -175,6 +190,10 @@ int main(int argc, char *argv[])
return -1;
}
if (parser.positionalArguments().length() > 0) {
roomManager->setUrlArgument(parser.positionalArguments()[0]);
}
#ifdef HAVE_KDBUSADDONS
QObject::connect(&service, &KDBusService::activateRequested, &engine, [&engine](const QStringList & /*arguments*/, const QString & /*workingDirectory*/) {
const auto rootObjects = engine.rootObjects();

View File

@@ -18,6 +18,10 @@
<label>Show notifications</label>
<default>true</default>
</entry>
<entry name="ConfirmLinksAction" type="bool">
<label>Confirm link before opening them</label>
<default>true</default>
</entry>
<entry name="MergeRoomList" type="bool">
<label>Merge Room Lists</label>
<default>false</default>

View File

@@ -222,20 +222,23 @@ void RoomListModel::handleNotifications()
}
oldNotifications += notification["event"].toObject()["event_id"].toString();
auto room = m_connection->room(notification["room_id"].toString());
auto sender = room->user(notification["event"].toObject()["sender"].toString());
if (room) {
// The room might have been deleted (for example rejected invitation).
auto sender = room->user(notification["event"].toObject()["sender"].toString());
QImage avatar_image;
if (!sender->avatarUrl(room).isEmpty()) {
avatar_image = sender->avatar(128, room);
} else {
avatar_image = room->avatar(128);
QImage avatar_image;
if (!sender->avatarUrl(room).isEmpty()) {
avatar_image = sender->avatar(128, room);
} else {
avatar_image = room->avatar(128);
}
NotificationsManager::instance().postNotification(dynamic_cast<NeoChatRoom *>(room),
room->displayName(),
sender->displayname(room),
notification["event"].toObject()["content"].toObject()["body"].toString(),
avatar_image,
notification["event"].toObject()["event_id"].toString());
}
NotificationsManager::instance().postNotification(dynamic_cast<NeoChatRoom *>(room),
room->displayName(),
sender->displayname(room),
notification["event"].toObject()["content"].toObject()["body"].toString(),
avatar_image,
notification["event"].toObject()["event_id"].toString());
}
});
}

197
src/roommanager.cpp Normal file
View File

@@ -0,0 +1,197 @@
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
// SPDX-FileCopyrightText: 2021 Alexey Rusakov <TODO>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "roommanager.h"
#include "neochatroom.h"
#include "neochatconfig.h"
#include "controller.h"
#include <QDesktopServices>
#include <KLocalizedString>
#include <csapi/joining.h>
#include <utility>
RoomManager::RoomManager(QObject *parent)
: QObject(parent)
, m_currentRoom(nullptr)
, m_lastCurrentRoom(nullptr)
{}
RoomManager::~RoomManager()
{}
NeoChatRoom *RoomManager::currentRoom() const
{
return m_currentRoom;
}
void RoomManager::openResource(const QString &idOrUri, const QString &action)
{
Uri uri { idOrUri };
if (!uri.isValid()) {
Q_EMIT warning(i18n("Malformed or empty Matrix id"),
i18n("%1 is not a correct Matrix identifier", idOrUri));
return;
}
auto account = Controller::instance().activeConnection();
if (uri.type() != Uri::NonMatrix) {
if (!account) {
return;
}
if (!action.isEmpty()) {
uri.setAction(action);
}
// TODO we should allow the user to select a connection.
}
const auto result = visitResource(account, uri);
if (result == Quotient::CouldNotResolve) {
Q_EMIT warning(i18n("Room not found"),
i18n("There's no room %1 in the room list. Check the spelling and the account.", idOrUri));
} else { // Invalid cases should have been eliminated earlier
Q_ASSERT(result == Quotient::UriResolved);
}
}
bool RoomManager::hasOpenRoom() const
{
return m_currentRoom != nullptr;
}
void RoomManager::setUrlArgument(const QString &arg)
{
m_arg = arg;
}
void RoomManager::loadInitialRoom()
{
Q_ASSERT(Controller::instance().activeConnection());
if (!m_arg.isEmpty()) {
openResource(m_arg);
}
if (m_currentRoom) {
// we opened a room with the arg parsing already
return;
}
if (!NeoChatConfig::self()->openRoom().isEmpty()) {
// Here we can cast because the controller has been configured to
// return NeoChatRoom instead of simple Quotient::Room
const auto room = qobject_cast<NeoChatRoom *>(
Controller::instance().activeConnection()->room(NeoChatConfig::self()->openRoom()));
m_lastCurrentRoom = std::exchange(m_currentRoom, room);
Q_EMIT currentRoomChanged();
Q_EMIT pushRoom(room, QString());
} else {
Q_EMIT pushWelcomePage();
}
}
void RoomManager::enterRoom(NeoChatRoom *room)
{
if (!m_currentRoom) {
m_lastCurrentRoom = std::exchange(m_currentRoom, room);
Q_EMIT currentRoomChanged();
Q_EMIT pushRoom(room, QString());
}
m_lastCurrentRoom = std::exchange(m_currentRoom, room);
Q_EMIT currentRoomChanged();
NeoChatConfig::self()->setOpenRoom(room->id());
NeoChatConfig::self()->save();
}
void RoomManager::getBack()
{
Q_ASSERT(m_currentRoom);
if (!m_lastCurrentRoom) {
Q_EMIT pushWelcomePage();
return;
}
Q_EMIT replaceRoom(m_lastCurrentRoom, QString());
}
void RoomManager::openWindow(NeoChatRoom *room)
{
// forward the call to QML
Q_EMIT openRoomInNewWindow(room);
}
UriResolveResult RoomManager::visitUser(User* user, const QString &action)
{
if (action == "mention" || action.isEmpty()) {
// send it has QVariantMap because the properties in the
#ifdef QUOTIENT_07
user->load();
#endif
Q_EMIT showUserDetail(user);
} else if (action == "_interactive") {
user->requestDirectChat();
} else if (action == "chat") {
#ifdef QUOTIENT_07
user->load();
#endif
Q_EMIT askDirectChatConfirmation(user);
} else {
return Quotient::IncorrectAction;
}
return Quotient::UriResolved;
}
void RoomManager::visitRoom(Room *room, const QString &eventId)
{
auto neoChatRoom = qobject_cast<NeoChatRoom *>(room);
Q_ASSERT(neoChatRoom != nullptr);
if (m_currentRoom) {
if (m_currentRoom->id() == room->id()) {
Q_EMIT goToEvent(eventId);
} else {
m_lastCurrentRoom = std::exchange(m_currentRoom, neoChatRoom);
Q_EMIT currentRoomChanged();
Q_EMIT replaceRoom(neoChatRoom, eventId);
}
} else {
m_lastCurrentRoom = std::exchange(m_currentRoom, neoChatRoom);
Q_EMIT currentRoomChanged();
Q_EMIT pushRoom(neoChatRoom, eventId);
}
}
void RoomManager::joinRoom(Quotient::Connection *account,
const QString &roomAliasOrId,
const QStringList &viaServers)
{
// We already listen to roomJoined signal in the Controller
account->joinRoom(QUrl::toPercentEncoding(roomAliasOrId), viaServers);
}
bool RoomManager::visitNonMatrix(const QUrl &url)
{
// Return true if the user cancels, treating it as an alternative normal
// flow (rather than an abnormal flow when the navigation itself fails).
if (NeoChatConfig::self()->confirmLinksAction()) {
Q_EMIT openLink(url);
} else {
if (!QDesktopServices::openUrl(url)) {
Q_EMIT warning(i18n("No application for the link"),
i18n("Your operating system could not find an application for the link."));
}
}
return true;
}
void RoomManager::reset()
{
m_arg = QString();
m_currentRoom = nullptr;
m_lastCurrentRoom = nullptr;
Q_EMIT currentRoomChanged();
}

107
src/roommanager.h Normal file
View File

@@ -0,0 +1,107 @@
// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#pragma once
#include <QObject>
#include <uriresolver.h>
class NeoChatRoom;
namespace Quotient {
class Room;
class User;
}
using namespace Quotient;
class RoomManager : public QObject, public UriResolverBase
{
Q_OBJECT
/// This property holds the current open room in the NeoChat, if any.
/// \sa hasOpenRoom
Q_PROPERTY(NeoChatRoom *currentRoom READ currentRoom NOTIFY currentRoomChanged)
/// This property holds whether a room is currently open in NeoChat.
/// \sa room
Q_PROPERTY(bool hasOpenRoom READ hasOpenRoom NOTIFY currentRoomChanged)
public:
explicit RoomManager(QObject *parent = nullptr);
virtual ~RoomManager();
/// Load the last opened room or the welcome page.
Q_INVOKABLE void loadInitialRoom();
/// This method will tell the NeoChat to open the message list
/// with the given room.
Q_INVOKABLE void enterRoom(NeoChatRoom *room);
/// Force refresh the view to show the last the opened room.
Q_INVOKABLE void getBack();
Q_INVOKABLE void openWindow(NeoChatRoom *room);
/// Getter for the currentRoom property.
NeoChatRoom *currentRoom() const;
/// Getter for the hasOpenRoom property.
bool hasOpenRoom() const;
// Overrided methods from UriResolverBase
UriResolveResult visitUser(User *user, const QString &action) override;
void joinRoom(Quotient::Connection *account, const QString &roomAliasOrId,
const QStringList &viaServers) override;
Q_INVOKABLE void visitRoom(Room *room, const QString &eventId) override;
Q_INVOKABLE bool visitNonMatrix(const QUrl &url) override;
Q_INVOKABLE void openResource(const QString &idOrUri, const QString &action = {});
/// Call this when the current used connection is dropped.
Q_INVOKABLE void reset();
void setUrlArgument(const QString &arg);
Q_SIGNALS:
/// Signal triggered when the current open room change.
void currentRoomChanged();
/// Signal triggered when the pageStack should push a new page with the
/// message list for the given room.
void pushRoom(NeoChatRoom *room, const QString &event);
/// Signal triggered when the room displayed by the message list should
/// be changed.
void replaceRoom(NeoChatRoom *room, const QString &event);
/// Go to the specified event in the current room.
void goToEvent(const QString &event);
/// Signal triggered when the pageStack should push a welcome page.
void pushWelcomePage();
/// Signal triggered when a room need to be opened in a new window.
void openRoomInNewWindow(NeoChatRoom *room);
/// Ask current room to open the user's details for the give user.
/// This can assume the user is loaded.
void showUserDetail(const User *user);
/// Ask current room to show confirmation dialog to open direct chat.
/// This can assume the user is loaded.
void askDirectChatConfirmation(const User *user);
/// Displays warning to the user.
void warning(const QString &title, const QString &message);
/// Ask user to open link and then open it.
void openLink(const QUrl &url);
private:
NeoChatRoom *m_currentRoom;
NeoChatRoom *m_lastCurrentRoom;
QString m_arg;
};
Q_GLOBAL_STATIC(RoomManager, roomManager)