Rework roommanager for improved stability

Fixes #645

- Active space handling is moved from QML to RoomManager
- Active tab in SpaceDrawer (space / no space / DM) is unified in a single variable
- RoomList & RoomPage loading is simplified: We're always pushing a RoomPage now; if there is no room, a placeholder is shown
- SpaceHomePage is moved into RoomPage; This replaces the entire push/replace room/spacehome logic
- If the current room is a space, the space home is shown, otherwise the timeline
- The concept of "previous room" is removed entirely. If we're leaving the active room, the placeholder room page is shown
- When clicking on a space in the list, the space room list is switched and the space home page is shown

In short, these changes should (after some initial regressions) lead to a less crashy NeoChat :)
This commit is contained in:
Tobias Fella
2024-03-29 23:23:28 +01:00
parent eaf4663c84
commit b75dbe8d5c
8 changed files with 315 additions and 430 deletions

View File

@@ -7,11 +7,10 @@
#include "chatbarcache.h"
#include "controller.h"
#include "eventhandler.h"
#include "messagecomponenttype.h"
#include "models/timelinemodel.h"
#include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "spacehierarchycache.h"
#include <KLocalizedString>
#include <QDesktopServices>
@@ -29,8 +28,6 @@
RoomManager::RoomManager(QObject *parent)
: QObject(parent)
, m_currentRoom(nullptr)
, m_lastCurrentRoom(nullptr)
, m_config(KSharedConfig::openStateConfig())
, m_timelineModel(new TimelineModel(this))
, m_messageFilterModel(new MessageFilterModel(this, m_timelineModel))
@@ -104,7 +101,7 @@ void RoomManager::resolveResource(const QString &idOrUri, const QString &action)
const auto result = visitResource(m_connection, uri);
if (result == Quotient::CouldNotResolve) {
if (uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) {
if ((uri.type() == Uri::RoomAlias || uri.type() == Uri::RoomId) && action != "no_join"_ls) {
Q_EMIT askJoinRoom(uri.primaryId());
}
} else { // Invalid cases should have been eliminated earlier
@@ -187,72 +184,17 @@ void RoomManager::loadInitialRoom()
connect(this, &RoomManager::connectionChanged, this, &RoomManager::openRoomForActiveConnection);
}
QString RoomManager::lastSpaceId() const
{
if (!m_connection) {
return {};
}
return m_lastSpaceConfig.readEntry(m_connection->userId(), QString());
}
void RoomManager::setLastSpaceId(const QString &lastSpaceId)
{
if (!m_connection) {
return;
}
const auto currentLastSpaceId = m_lastSpaceConfig.readEntry(m_connection->userId(), QString());
if (lastSpaceId == currentLastSpaceId) {
return;
}
m_lastSpaceConfig.writeEntry(m_connection->userId(), lastSpaceId);
Q_EMIT lastSpaceIdChanged();
}
bool RoomManager::directChatsActive() const
{
if (!m_connection) {
return {};
}
return m_directChatsConfig.readEntry(m_connection->userId(), bool());
}
void RoomManager::setDirectChatsActive(bool directChatsActive)
{
if (!m_connection) {
return;
}
const auto currentDirectChatsActive = m_directChatsConfig.readEntry(m_connection->userId(), bool());
if (directChatsActive == currentDirectChatsActive) {
return;
}
m_directChatsConfig.writeEntry(m_connection->userId(), directChatsActive);
Q_EMIT directChatsActiveChanged();
}
void RoomManager::openRoomForActiveConnection()
{
if (!m_connection) {
return;
m_currentRoom = nullptr;
}
// Read from last open room
QString roomId = m_lastRoomConfig.readEntry(m_connection->userId(), QString());
// TODO remove legacy check at some point.
if (roomId.isEmpty()) {
roomId = NeoChatConfig::self()->openRoom();
}
if (!roomId.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 *>(m_connection->room(roomId));
if (room) {
resolveResource(room->id());
}
if (m_lastRoomConfig.readEntry(m_connection->userId(), QString()).isEmpty()) {
setCurrentRoom({});
} else {
resolveResource(m_lastRoomConfig.readEntry(m_connection->userId(), QString()));
}
setCurrentSpace(m_lastSpaceConfig.readEntry(m_connection->userId(), QString()), false);
}
UriResolveResult RoomManager::visitUser(User *user, const QString &action)
@@ -273,10 +215,9 @@ UriResolveResult RoomManager::visitUser(User *user, const QString &action)
return Quotient::UriResolved;
}
void RoomManager::visitRoom(Room *room, const QString &eventId)
void RoomManager::visitRoom(Room *r, const QString &eventId)
{
auto neoChatRoom = qobject_cast<NeoChatRoom *>(room);
Q_ASSERT(neoChatRoom != nullptr);
auto room = qobject_cast<NeoChatRoom *>(r);
if (m_currentRoom && !m_currentRoom->editCache()->editId().isEmpty()) {
m_currentRoom->editCache()->setEditId({});
@@ -285,41 +226,26 @@ void RoomManager::visitRoom(Room *room, const QString &eventId)
// We're doing these things here because it is critical that they are switched at the same time
if (m_chatDocumentHandler->document()) {
m_currentRoom->mainCache()->setSavedText(m_chatDocumentHandler->document()->textDocument()->toPlainText());
m_chatDocumentHandler->setRoom(neoChatRoom);
m_chatDocumentHandler->document()->textDocument()->setPlainText(neoChatRoom->mainCache()->savedText());
neoChatRoom->mainCache()->setText(neoChatRoom->mainCache()->savedText());
} else {
m_chatDocumentHandler->setRoom(neoChatRoom);
}
}
if (m_currentRoom) {
if (m_currentRoom->id() == room->id()) {
Q_EMIT goToEvent(eventId);
} else {
m_lastCurrentRoom = std::exchange(m_currentRoom, neoChatRoom);
Q_EMIT currentRoomChanged();
if (neoChatRoom->isSpace()) {
m_lastSpaceConfig.writeEntry(m_connection->userId(), room->id());
Q_EMIT replaceSpaceHome(neoChatRoom);
} else {
Q_EMIT replaceRoom(neoChatRoom, eventId);
m_chatDocumentHandler->setRoom(room);
if (room) {
m_chatDocumentHandler->document()->textDocument()->setPlainText(room->mainCache()->savedText());
room->mainCache()->setText(room->mainCache()->savedText());
}
}
} else {
m_lastCurrentRoom = std::exchange(m_currentRoom, neoChatRoom);
Q_EMIT currentRoomChanged();
if (neoChatRoom->isSpace()) {
m_lastSpaceConfig.writeEntry(m_connection->userId(), room->id());
Q_EMIT pushSpaceHome(neoChatRoom);
} else {
Q_EMIT pushRoom(neoChatRoom, eventId);
m_chatDocumentHandler->setRoom(room);
}
}
// Save last open room
m_lastRoomConfig.writeEntry(m_connection->userId(), room->id());
if (!room) {
setCurrentRoom({});
return;
}
if (m_currentRoom && m_currentRoom->id() == room->id()) {
Q_EMIT goToEvent(eventId);
} else {
setCurrentRoom(room->id());
}
}
void RoomManager::joinRoom(Quotient::Connection *account, const QString &roomAliasOrId, const QStringList &viaServers)
@@ -360,31 +286,18 @@ bool RoomManager::visitNonMatrix(const QUrl &url)
return true;
}
void RoomManager::reset()
{
m_arg = QString();
m_currentRoom = nullptr;
m_lastCurrentRoom = nullptr;
Q_EMIT currentRoomChanged();
}
void RoomManager::leaveRoom(NeoChatRoom *room)
{
if (!room) {
return;
}
if (m_lastCurrentRoom && room->id() == m_lastCurrentRoom->id()) {
m_lastCurrentRoom = nullptr;
}
if (m_currentRoom && m_currentRoom->id() == room->id()) {
m_currentRoom = m_lastCurrentRoom;
m_lastCurrentRoom = nullptr;
Q_EMIT currentRoomChanged();
if (m_currentRoom->isSpace()) {
Q_EMIT replaceSpaceHome(m_currentRoom);
} else {
Q_EMIT replaceRoom(m_currentRoom, {});
}
setCurrentRoom({});
}
if (m_currentSpaceId == room->id()) {
setCurrentSpace({});
}
room->forget();
@@ -411,4 +324,69 @@ void RoomManager::setConnection(NeoChatConnection *connection)
Q_EMIT connectionChanged();
}
void RoomManager::setCurrentSpace(const QString &spaceId, bool setRoom)
{
m_currentSpaceId = spaceId;
Q_EMIT currentSpaceChanged();
m_lastSpaceConfig.writeEntry(m_connection->userId(), spaceId);
if (!setRoom) {
return;
}
if (spaceId.length() > 3) {
resolveResource(spaceId, "no_join"_ls);
} else {
visitRoom({}, {});
}
}
void RoomManager::setCurrentRoom(const QString &roomId)
{
if (roomId.isEmpty()) {
m_currentRoom = nullptr;
} else {
m_currentRoom = dynamic_cast<NeoChatRoom *>(m_connection->room(roomId));
}
Q_EMIT currentRoomChanged();
if (m_connection) {
m_lastRoomConfig.writeEntry(m_connection->userId(), roomId);
}
if (roomId.isEmpty()) {
return;
}
if (m_currentRoom->isSpace()) {
return;
}
if (m_currentRoom->isDirectChat() && m_currentSpaceId != "DM"_ls) {
setCurrentSpace("DM"_ls, false);
return;
}
const auto &parentSpaces = SpaceHierarchyCache::instance().parentSpaces(roomId);
if (parentSpaces.contains(m_currentSpaceId)) {
return;
}
if (const auto &parent = m_connection->room(m_currentRoom->canonicalParent())) {
for (const auto &parentParent : SpaceHierarchyCache::instance().parentSpaces(parent->id())) {
if (SpaceHierarchyCache::instance().parentSpaces(parentParent).isEmpty()) {
setCurrentSpace(parentParent, false);
return;
}
}
setCurrentSpace(parent->id(), false);
return;
}
for (const auto &space : parentSpaces) {
if (SpaceHierarchyCache::instance().parentSpaces(space).isEmpty()) {
setCurrentSpace(space, false);
return;
}
}
setCurrentSpace({}, false);
}
QString RoomManager::currentSpace() const
{
return m_currentSpaceId;
}
#include "moc_roommanager.cpp"