diff --git a/imports/NeoChat/Component/Timeline/TextDelegate.qml b/imports/NeoChat/Component/Timeline/TextDelegate.qml
index 2f47608f9..4cc06e372 100644
--- a/imports/NeoChat/Component/Timeline/TextDelegate.qml
+++ b/imports/NeoChat/Component/Timeline/TextDelegate.qml
@@ -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
diff --git a/imports/NeoChat/Menu/Timeline/MessageDelegateContextMenu.qml b/imports/NeoChat/Menu/Timeline/MessageDelegateContextMenu.qml
index 409a36f8b..60e0492fa 100644
--- a/imports/NeoChat/Menu/Timeline/MessageDelegateContextMenu.qml
+++ b/imports/NeoChat/Menu/Timeline/MessageDelegateContextMenu.qml
@@ -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);
}
}
}
diff --git a/imports/NeoChat/Page/RoomListPage.qml b/imports/NeoChat/Page/RoomListPage.qml
index 743b4778b..9fff16cbf 100644
--- a/imports/NeoChat/Page/RoomListPage.qml
+++ b/imports/NeoChat/Page/RoomListPage.qml
@@ -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
}
diff --git a/imports/NeoChat/Page/RoomPage.qml b/imports/NeoChat/Page/RoomPage.qml
index 6e488e3f1..c9dc20568 100644
--- a/imports/NeoChat/Page/RoomPage.qml
+++ b/imports/NeoChat/Page/RoomPage.qml
@@ -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}
${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
diff --git a/imports/NeoChat/Panel/RoomDrawer.qml b/imports/NeoChat/Panel/RoomDrawer.qml
index 5c47e930e..4e463d48d 100644
--- a/imports/NeoChat/Panel/RoomDrawer.qml
+++ b/imports/NeoChat/Panel/RoomDrawer.qml
@@ -16,7 +16,7 @@ import NeoChat.Dialog 1.0
Kirigami.OverlayDrawer {
id: roomDrawer
- property var room
+ readonly property var room: RoomManager.currentRoom
enabled: true
diff --git a/org.kde.neochat.desktop b/org.kde.neochat.desktop
index 7b8f56818..b97af09cd 100644
--- a/org.kde.neochat.desktop
+++ b/org.kde.neochat.desktop
@@ -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
diff --git a/qml/main.qml b/qml/main.qml
index 677f4a350..900cec78a 100644
--- a/qml/main.qml
+++ b/qml/main.qml
@@ -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?", `${url}`)
+ 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();
+ }
}
}
}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 842dc4e81..088203e7e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -13,6 +13,7 @@ add_executable(neochat
messageeventmodel.cpp
messagefiltermodel.cpp
roomlistmodel.cpp
+ roommanager.cpp
neochatroom.cpp
neochatuser.cpp
userlistmodel.cpp
diff --git a/src/main.cpp b/src/main.cpp
index 83d006375..9777804d6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
#include
#ifdef HAVE_KDBUSADDONS
@@ -41,8 +42,9 @@
#include "neochatuser.h"
#include "notificationsmanager.h"
#include "publicroomlistmodel.h"
-#include "room.h"
+#include
#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("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();
diff --git a/src/neochatconfig.kcfg b/src/neochatconfig.kcfg
index d0e83866c..07b32dd0d 100644
--- a/src/neochatconfig.kcfg
+++ b/src/neochatconfig.kcfg
@@ -18,6 +18,10 @@
true
+
+
+ true
+
false
diff --git a/src/roomlistmodel.cpp b/src/roomlistmodel.cpp
index bf9495f94..2a23588a6 100644
--- a/src/roomlistmodel.cpp
+++ b/src/roomlistmodel.cpp
@@ -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(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(room),
- room->displayName(),
- sender->displayname(room),
- notification["event"].toObject()["content"].toObject()["body"].toString(),
- avatar_image,
- notification["event"].toObject()["event_id"].toString());
}
});
}
diff --git a/src/roommanager.cpp b/src/roommanager.cpp
new file mode 100644
index 000000000..2cccdf61c
--- /dev/null
+++ b/src/roommanager.cpp
@@ -0,0 +1,197 @@
+// SPDX-FileCopyrightText: 2021 Carl Schwan
+// SPDX-FileCopyrightText: 2021 Alexey Rusakov
+// 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
+#include
+#include
+#include
+
+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(
+ 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(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();
+}
diff --git a/src/roommanager.h b/src/roommanager.h
new file mode 100644
index 000000000..e2fb2a955
--- /dev/null
+++ b/src/roommanager.h
@@ -0,0 +1,107 @@
+// SPDX-FileCopyrightText: 2021 Carl Schwan
+// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
+
+#pragma once
+
+#include
+#include
+
+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)