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

@@ -5,6 +5,7 @@ import QtQuick 2.15
import QtQuick.Controls 2.15 as QQC2
import QtQuick.Layouts 1.15
import org.kde.neochat 1.0
import org.kde.kirigami 2.15 as Kirigami
TextEdit {
@@ -47,7 +48,7 @@ a{
wrapMode: Text.WordWrap
textFormat: Text.RichText
onLinkActivated: applicationWindow().handleLink(link, currentRoom)
onLinkActivated: RoomManager.openResource(link)
MouseArea {
anchors.fill: parent

View File

@@ -93,9 +93,7 @@ Loader {
Layout.maximumWidth: Kirigami.Units.gridUnit * 24
wrapMode: Text.WordWrap
onLinkActivated: {
applicationWindow().handleLink(link, currentRoom)
}
onLinkActivated: RoomManager.openResource(link);
}
}
}
@@ -189,9 +187,7 @@ Loader {
Layout.fillWidth: true
wrapMode: Text.WordWrap
onLinkActivated: {
applicationWindow().handleLink(link, currentRoom)
}
onLinkActivated: RoomManager.openResource(link);
}
}
}

View File

@@ -116,16 +116,15 @@ Kirigami.ScrollablePage {
action: Kirigami.Action {
id: enterRoomAction
onTriggered: {
var roomItem = roomManager.enterRoom(currentRoom)
roomListItem.KeyNavigation.right = roomItem
roomItem.focus = true;
itemSelection.setCurrentIndex(sortFilterRoomListModel.mapToSource(sortFilterRoomListModel.index(index, 0)), ItemSelectionModel.SelectCurrent)
RoomManager.enterRoom(currentRoom);
itemSelection.setCurrentIndex(sortFilterRoomListModel.mapToSource(
sortFilterRoomListModel.index(index, 0)), ItemSelectionModel.SelectCurrent)
}
}
bold: unreadCount > 0
label: name ?? ""
subtitle: {
let txt = (lastEvent == "" ? topic : lastEvent).replace(/(\r\n\t|\n|\r\t)/gm," ")
let txt = (lastEvent.length === 0 ? topic : lastEvent).replace(/(\r\n\t|\n|\r\t)/gm, " ")
if (txt.length) {
return txt
}

View File

@@ -21,7 +21,8 @@ import NeoChat.Menu.Timeline 1.0
Kirigami.ScrollablePage {
id: page
required property var currentRoom
/// It's not readonly because of the seperate window view.
property var currentRoom: RoomManager.currentRoom
title: currentRoom.displayName
@@ -30,19 +31,17 @@ Kirigami.ScrollablePage {
onCurrentRoomChanged: ChatBoxHelper.clearEditReply()
ActionsHandler {
id: actionsHandler
room: page.currentRoom
connection: Controller.activeConnection
}
Connections {
target: Controller.activeConnection
function onJoinedRoom(room) {
if(room.id === invitation.id) {
roomManager.enterRoom(room);
RoomManager.enterRoom(room);
}
}
}
@@ -81,7 +80,7 @@ Kirigami.ScrollablePage {
onClicked: {
page.currentRoom.forget()
roomManager.getBack();
RoomManager.getBack();
}
}
@@ -667,6 +666,11 @@ Kirigami.ScrollablePage {
FullScreenImage {}
}
Component {
id: userDetailDialog
UserDetailDialog {}
}
header: TypingPane {
id: typingPane
@@ -746,6 +750,20 @@ Kirigami.ScrollablePage {
}
}
function warning(title, message) {
page.header.contentItem.text = `${title}<br />${message}`;
page.header.contentItem.type = Kirigami.MessageType.Warning;
page.header.contentItem.visible = true;
}
function showUserDetail(user) {
userDetailDialog.createObject(QQC2.ApplicationWindow.overlay, {
room: currentRoom,
user: user,
}).open();
}
function goToLastMessage() {
currentRoom.markAllMessagesAsRead()
// scroll to the very end, i.e to messageListView.YEnd

View File

@@ -16,7 +16,7 @@ import NeoChat.Dialog 1.0
Kirigami.OverlayDrawer {
id: roomDrawer
property var room
readonly property var room: RoomManager.currentRoom
enabled: true

View File

@@ -70,7 +70,8 @@ Comment[sv]=Klient för protokollet Matrix
Comment[uk]=Клієнт протоколу Matrix
Comment[x-test]=xxClient for the Matrix protocolxx
Comment[zh_CN]=为 Matrix 协议打造的客户端
Exec=neochat
MimeType=x-scheme-handler/matrix;
Exec=neochat %u
Terminal=false
Icon=org.kde.neochat
Type=Application

View File

@@ -28,6 +28,8 @@ Kirigami.ApplicationWindow {
pageStack.initialPage: LoadingPage {}
property bool roomListLoaded: false
Connections {
target: root.quitAction
function onTriggered() {
@@ -50,78 +52,85 @@ Kirigami.ApplicationWindow {
onXChanged: saveWindowGeometryTimer.restart()
onYChanged: saveWindowGeometryTimer.restart()
/**
* Manage opening and close rooms
* TODO this should probably be moved to C++
*/
QtObject {
id: roomManager
property var currentRoom: null
property alias pageStack: root.pageStack
property var roomList: null
property Item roomItem: null
readonly property bool hasOpenRoom: currentRoom !== null
signal leaveRoom(string room);
signal openRoom(string room);
function roomByAliasOrId(aliasOrId) {
return Controller.activeConnection.room(aliasOrId)
/// Setup keyboard navigation to the room page.
function connectRoomToSignal(item) {
if (!roomListLoaded) {
console.log("Should not happen: no room list page but room page");
}
const roomList = pageStack.get(0);
item.switchRoomUp.connect(function() {
roomList.goToNextRoom();
});
function openRoomAndEvent(room, event) {
enterRoom(room)
roomItem.goToEvent(event)
}
item.switchRoomDown.connect(function() {
roomList.goToPreviousRoom();
});
item.forceActiveFocus();
item.KeyNavigation.left = pageStack.get(0);
}
function loadInitialRoom() {
if (Config.openRoom) {
const room = Controller.activeConnection.room(Config.openRoom);
currentRoom = room;
roomItem = pageStack.push("qrc:/imports/NeoChat/Page/RoomPage.qml", { 'currentRoom': room, });
connectRoomToSignal(roomItem);
} else {
// TODO create welcome page
}
}
Connections {
target: RoomManager
function enterRoom(room) {
if (currentRoom != null) {
roomItem.currentRoom = room;
pageStack.currentIndex = pageStack.depth - 1;
} else {
roomItem = pageStack.push("qrc:/imports/NeoChat/Page/RoomPage.qml", { 'currentRoom': room, });
}
currentRoom = room;
Config.openRoom = room.id;
Config.save();
function onPushRoom(room, event) {
const roomItem = pageStack.push("qrc:/imports/NeoChat/Page/RoomPage.qml");
connectRoomToSignal(roomItem);
return roomItem;
if (event.length > 0) {
roomItem.goToEvent(event);
}
}
function getBack() {
pageStack.replace("qrc:/imports/NeoChat/Page/RoomPage.qml", { 'currentRoom': currentRoom, });
function onReplaceRoom(room, event) {
const roomItem = pageStack.get(pageStack.depth - 1);
pageStack.currentIndex = pageStack.depth - 1;
connectRoomToSignal(roomItem);
if (event.length > 0) {
roomItem.goToEvent(event);
}
}
function openWindow(room) {
function goToEvent(event) {
if (event.length > 0) {
roomItem.goToEvent(event);
}
roomItem.forceActiveFocus();
}
function onPushWelcomePage() {
// TODO
}
function onOpenRoomInNewWindow(room) {
const secondayWindow = roomWindow.createObject(applicationWindow(), {currentRoom: room});
secondayWindow.width = root.width - roomList.width;
secondayWindow.show();
}
function connectRoomToSignal(item) {
if (!roomList) {
console.log("Should not happen: no room list page but room page");
}
item.switchRoomUp.connect(function() {
roomList.goToNextRoom();
});
function onShowUserDetail(user) {
const roomItem = pageStack.get(pageStack.depth - 1);
roomItem.showUserDetail(user);
}
item.switchRoomDown.connect(function() {
roomList.goToPreviousRoom();
});
function onAskDirectChatConfirmation(user) {
askDirectChatConfirmationComponent.createObject(QQC2.ApplicationWindow.overlay, {
user: user,
}).open();
}
function onWarning(title, message) {
if (RoomManager.currentRoom) {
const roomItem = pageStack.get(pageStack.depth - 1);
roomItem.warning(title, message);
} else {
showPassiveNotification(i18n("Warning: %1", message));
}
}
function onOpenLink(url) {
openLinkConfirmationComponent.createObject(QQC2.ApplicationWindow.overlay, {
url: url,
}).open();
}
}
@@ -146,8 +155,7 @@ Kirigami.ApplicationWindow {
modal: !root.wideScreen || !enabled
onEnabledChanged: drawerOpen = enabled && !modal
onModalChanged: drawerOpen = !modal
enabled: roomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3
room: roomManager.currentRoom
enabled: RoomManager.hasOpenRoom && pageStack.layers.depth < 2 && pageStack.depth < 3
handleVisible: enabled && pageStack.layers.depth < 2 && pageStack.depth < 3
}
@@ -238,7 +246,7 @@ Kirigami.ApplicationWindow {
Connections {
target: LoginHelper
function onInitialSyncFinished() {
roomManager.roomList = pageStack.replace(roomListComponent);
RoomManager.roomList = pageStack.replace(roomListComponent);
}
}
@@ -246,32 +254,38 @@ Kirigami.ApplicationWindow {
target: Controller
function onInitiated() {
if (roomManager.hasOpenRoom) {
if (RoomManager.hasOpenRoom) {
return;
}
if (Controller.accountCount === 0) {
pageStack.replace("qrc:/imports/NeoChat/Page/WelcomePage.qml", {});
} else {
roomManager.roomList = pageStack.replace(roomListComponent, {'activeConnection': Controller.activeConnection});
roomManager.loadInitialRoom();
pageStack.replace(roomListComponent, {
activeConnection: Controller.activeConnection
});
roomListLoaded = true;
RoomManager.loadInitialRoom();
}
}
function onBusyChanged() {
if(!Controller.busy && roomManager.roomList === null) {
roomManager.roomList = pageStack.replace(roomListComponent);
if(!Controller.busy && roomListLoaded === false) {
pageStack.replace(roomListComponent);
roomListLoaded = true;
}
}
function onConnectionDropped() {
if (Controller.accountCount === 0) {
RoomManager.reset();
pageStack.clear();
roomListLoaded = false;
pageStack.replace("qrc:/imports/NeoChat/Page/WelcomePage.qml");
}
}
function onGlobalErrorOccured(error, detail) {
showPassiveNotification(error + ": " + detail)
showPassiveNotification(i18nc("%1: %2", error, detail));
}
function onShowWindow() {
@@ -279,7 +293,7 @@ Kirigami.ApplicationWindow {
}
function onOpenRoom(room) {
roomManager.enterRoom(room)
RoomManager.enterRoom(room)
}
function onUserConsentRequired(url) {
@@ -288,14 +302,14 @@ Kirigami.ApplicationWindow {
}
function onRoomJoined(roomName) {
roomManager.enterRoom(Controller.activeConnection.room(roomName))
RoomManager.enterRoom(Controller.activeConnection.room(roomName))
}
}
Connections {
target: Controller.activeConnection
onDirectChatAvailable: {
roomManager.enterRoom(Controller.activeConnection.room(directChat.id));
RoomManager.enterRoom(Controller.activeConnection.room(directChat.id));
}
}
@@ -332,36 +346,68 @@ Kirigami.ApplicationWindow {
RoomWindow {}
}
function handleLink(link, currentRoom) {
if (link.startsWith("https://matrix.to/")) {
var content = link.replace("https://matrix.to/#/", "").replace(/\?.*/, "")
if(content.match("^[#!]")) {
if(content.includes("/")) {
var result = content.match("([!#].*:.*)/(\\$.*)")
if(!result) {
return
}
if(result[1] == currentRoom.id) {
roomManager.roomItem.goToEvent(result[2])
} else {
roomManager.openRoomAndEvent(roomManager.roomByAliasOrId(result[1]), result[2])
}
} else {
roomManager.enterRoom(roomManager.roomByAliasOrId(content))
}
} else if(content.match("^@")) {
let dialog = userDialog.createObject(root.overlay, {room: currentRoom, user: currentRoom.user(content)})
dialog.open()
console.log(dialog.user)
Component {
id: userDialog
UserDetailDialog {}
}
Component {
id: askDirectChatConfirmationComponent
Kirigami.OverlaySheet {
id: askDirectChatConfirmation
required property var user;
parent: QQC2.ApplicationWindow.overlay
header: Kirigami.Heading {
text: i18n("Start a chat")
}
contentItem: QQC2.Label {
text: i18n("Do you want to start a chat with %1?", user.displayName)
wrapMode: Text.WordWrap
}
footer: QQC2.DialogButtonBox {
standardButtons: QQC2.DialogButtonBox.Ok | QQC2.DialogButtonBox.Cancel
onAccepted: {
user.requestDirectChat();
askDirectChatConfirmation.close();
}
onRejected: askDirectChatConfirmation.close();
}
} else {
Qt.openUrlExternally(link)
}
}
Component {
id: userDialog
UserDetailDialog {
id: openLinkConfirmationComponent
Kirigami.OverlaySheet {
id: openLinkConfirmation
required property var url;
header: Kirigami.Heading {
text: i18n("Confirm opening a link")
}
parent: QQC2.ApplicationWindow.overlay
contentItem: ColumnLayout {
QQC2.Label {
text: i18n("Do you want to open the link to %1?", `<a href='${url}'>${url}</a>`)
wrapMode: Text.WordWrap
}
QQC2.CheckBox {
id: dontAskAgain
text: i18n("Don't ask again")
}
}
footer: QQC2.DialogButtonBox {
standardButtons: QQC2.DialogButtonBox.Ok | QQC2.DialogButtonBox.Cancel
onAccepted: {
Config.confirmLinksAction = dontAskAgain.checked;
Config.save();
Qt.openUrlExternally(url);
openLinkConfirmation.close();
}
onRejected: openLinkConfirmation.close();
}
}
}
}

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)