Compare commits

...

2 Commits

Author SHA1 Message Date
Tobias Fella
27e5713731 CI 2025-03-22 17:14:34 +01:00
Tobias Fella
073e756364 Port to Integral 2025-03-22 17:10:33 +01:00
46 changed files with 3224 additions and 3586 deletions

View File

@@ -50,48 +50,6 @@
}
]
},
{
"name": "olm",
"buildsystem": "cmake-ninja",
"config-opts": [
"-DOLM_TESTS=OFF"
],
"sources": [
{
"type": "git",
"url": "https://gitlab.matrix.org/matrix-org/olm.git",
"tag": "3.2.16",
"x-checker-data": {
"type": "git",
"tag-pattern": "^([\\d.]+)$"
},
"commit": "7e0c8277032e40308987257b711b38af8d77cc69"
}
]
},
{
"name": "libsecret",
"buildsystem": "meson",
"config-opts": [
"-Dmanpage=false",
"-Dvapi=false",
"-Dgtk_doc=false",
"-Dintrospection=false",
"-Dcrypto=disabled"
],
"sources": [
{
"type": "archive",
"url": "https://download.gnome.org/sources/libsecret/0.21/libsecret-0.21.6.tar.xz",
"sha256": "747b8c175be108c880d3adfb9c3537ea66e520e4ad2dccf5dce58003aeeca090",
"x-checker-data": {
"type": "gnome",
"name": "libsecret",
"stable-only": true
}
}
]
},
{
"name": "qtkeychain",
"buildsystem": "cmake-ninja",
@@ -117,20 +75,14 @@
]
},
{
"name": "libQuotient",
"name": "integral",
"buildsystem": "cmake-ninja",
"sources": [
{
"type": "git",
"url": "https://github.com/quotient-im/libQuotient.git",
"branch": "dev",
"disable-submodules": true
"url": "https://invent.kde.org/tfella/integral.git",
"branch": "master"
}
],
"config-opts": [
"-DBUILD_WITH_QT6=ON",
"-DQuotient_ENABLE_E2EE=ON",
"-DBUILD_TESTING=OFF"
]
},
{

View File

@@ -8,14 +8,14 @@ include:
- /gitlab-templates/json-validation.yml
- /gitlab-templates/xml-lint.yml
- /gitlab-templates/yaml-lint.yml
- /gitlab-templates/android-qt6.yml
- /gitlab-templates/linux-qt6.yml
- /gitlab-templates/linux-qt6-next.yml
- /gitlab-templates/windows-qt6.yml
# - /gitlab-templates/android-qt6.yml
# - /gitlab-templates/linux-qt6.yml
# - /gitlab-templates/linux-qt6-next.yml
# - /gitlab-templates/windows-qt6.yml
# - /gitlab-templates/freebsd-qt6.yml
- /gitlab-templates/flatpak.yml
- /gitlab-templates/snap-snapcraft-lxd.yml
- /gitlab-templates/craft-android-qt6-apks.yml
- /gitlab-templates/craft-appimage-qt6.yml
- /gitlab-templates/craft-windows-x86-64-qt6.yml
- /gitlab-templates/craft-windows-appx-qt6.yml
# - /gitlab-templates/snap-snapcraft-lxd.yml
# - /gitlab-templates/craft-android-qt6-apks.yml
# - /gitlab-templates/craft-appimage-qt6.yml
# - /gitlab-templates/craft-windows-x86-64-qt6.yml
# - /gitlab-templates/craft-windows-appx-qt6.yml

View File

@@ -49,8 +49,6 @@ if(NEOCHAT_FLATPAK)
include(cmake/Flatpak.cmake)
endif()
set(QUOTIENT_FORCE_NAMESPACED_INCLUDES TRUE)
ecm_setup_version(${PROJECT_VERSION}
VARIABLE_PREFIX NEOCHAT
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/neochat-version.h
@@ -107,13 +105,7 @@ if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
find_package(KF6DBusAddons ${KF_MIN_VERSION} REQUIRED)
endif()
find_package(QuotientQt6 0.9)
set_package_properties(QuotientQt6 PROPERTIES
TYPE REQUIRED
DESCRIPTION "Qt wrapper around Matrix API"
URL "https://github.com/quotient-im/libQuotient/"
PURPOSE "Talk with matrix server"
)
find_package(Integral 0.1 REQUIRED)
find_package(cmark)
set_package_properties(cmark PROPERTIES

View File

@@ -10,192 +10,192 @@ endif()
add_library(neochat STATIC
controller.cpp
controller.h
models/emojimodel.cpp
models/emojimodel.h
emojitones.cpp
emojitones.h
models/customemojimodel.cpp
models/customemojimodel.h
clipboard.cpp
clipboard.h
models/timelinemessagemodel.cpp
models/timelinemessagemodel.h
models/messagefiltermodel.cpp
models/messagefiltermodel.h
models/roomlistmodel.cpp
models/roomlistmodel.h
models/sortfilterspacelistmodel.cpp
models/sortfilterspacelistmodel.h
models/accountemoticonmodel.cpp
models/accountemoticonmodel.h
spacehierarchycache.cpp
spacehierarchycache.h
roommanager.cpp
roommanager.h
# models/emojimodel.cpp
# models/emojimodel.h
# emojitones.cpp
# emojitones.h
# models/customemojimodel.cpp
# models/customemojimodel.h
# clipboard.cpp
# clipboard.h
# models/timelinemessagemodel.cpp
# models/timelinemessagemodel.h
# models/messagefiltermodel.cpp
# models/messagefiltermodel.h
# models/roomlistmodel.cpp
# models/roomlistmodel.h
# models/sortfilterspacelistmodel.cpp
# models/sortfilterspacelistmodel.h
# models/accountemoticonmodel.cpp
# models/accountemoticonmodel.h
# spacehierarchycache.cpp
# spacehierarchycache.h
# roommanager.cpp
# roommanager.h
neochatroom.cpp
neochatroom.h
models/userlistmodel.cpp
models/userlistmodel.h
models/userfiltermodel.cpp
models/userfiltermodel.h
models/publicroomlistmodel.cpp
models/publicroomlistmodel.h
models/spacechildrenmodel.cpp
models/spacechildrenmodel.h
models/spacechildsortfiltermodel.cpp
models/spacechildsortfiltermodel.h
models/spacetreeitem.cpp
models/spacetreeitem.h
models/userdirectorylistmodel.cpp
models/userdirectorylistmodel.h
# models/publicroomlistmodel.cpp
# models/publicroomlistmodel.h
# models/spacechildrenmodel.cpp
# models/spacechildrenmodel.h
# models/spacechildsortfiltermodel.cpp
# models/spacechildsortfiltermodel.h
# models/spacetreeitem.cpp
# models/spacetreeitem.h
# models/userdirectorylistmodel.cpp
# models/userdirectorylistmodel.h
models/pushrulemodel.cpp
models/pushrulemodel.h
models/emoticonfiltermodel.cpp
models/emoticonfiltermodel.h
notificationsmanager.cpp
notificationsmanager.h
models/sortfilterroomlistmodel.cpp
models/sortfilterroomlistmodel.h
# models/emoticonfiltermodel.cpp
# models/emoticonfiltermodel.h
# notificationsmanager.cpp
# notificationsmanager.h
# models/sortfilterroomlistmodel.cpp
# models/sortfilterroomlistmodel.h
models/roomtreemodel.cpp
models/roomtreemodel.h
chatdocumenthandler.cpp
chatdocumenthandler.h
models/devicesmodel.cpp
models/devicesmodel.h
models/devicesproxymodel.cpp
filetype.cpp
filetype.h
login.cpp
login.h
models/webshortcutmodel.cpp
models/webshortcutmodel.h
# models/devicesmodel.cpp
# models/devicesmodel.h
# models/devicesproxymodel.cpp
# filetype.cpp
# filetype.h
# login.cpp
# login.h
# models/webshortcutmodel.cpp
# models/webshortcutmodel.h
blurhash.cpp
blurhash.h
blurhashimageprovider.cpp
blurhashimageprovider.h
models/mediamessagefiltermodel.cpp
models/mediamessagefiltermodel.h
urlhelper.cpp
urlhelper.h
# models/mediamessagefiltermodel.cpp
# models/mediamessagefiltermodel.h
# urlhelper.cpp
# urlhelper.h
windowcontroller.cpp
windowcontroller.h
linkpreviewer.cpp
linkpreviewer.h
# linkpreviewer.cpp
# linkpreviewer.h
models/completionmodel.cpp
models/completionmodel.h
models/completionproxymodel.cpp
models/completionproxymodel.h
models/actionsmodel.cpp
models/actionsmodel.h
models/serverlistmodel.cpp
models/serverlistmodel.h
models/statemodel.cpp
models/statemodel.h
models/statefiltermodel.cpp
models/statefiltermodel.h
filetransferpseudojob.cpp
filetransferpseudojob.h
models/searchmodel.cpp
models/searchmodel.h
texthandler.cpp
texthandler.h
# models/completionproxymodel.cpp
# models/completionproxymodel.h
# models/actionsmodel.cpp
# models/actionsmodel.h
# models/serverlistmodel.cpp
# models/serverlistmodel.h
# models/statemodel.cpp
# models/statemodel.h
# models/statefiltermodel.cpp
# models/statefiltermodel.h
# filetransferpseudojob.cpp
# filetransferpseudojob.h
# models/searchmodel.cpp
# models/searchmodel.h
# texthandler.cpp
# texthandler.h
logger.cpp
logger.h
models/stickermodel.cpp
models/stickermodel.h
models/imagepacksmodel.cpp
models/imagepacksmodel.h
events/imagepackevent.cpp
events/imagepackevent.h
models/reactionmodel.cpp
models/reactionmodel.h
# models/stickermodel.cpp
# models/stickermodel.h
# models/imagepacksmodel.cpp
# models/imagepacksmodel.h
# events/imagepackevent.cpp
# events/imagepackevent.h
# models/reactionmodel.cpp
# models/reactionmodel.h
delegatesizehelper.cpp
delegatesizehelper.h
models/livelocationsmodel.cpp
models/livelocationsmodel.h
models/locationsmodel.cpp
models/locationsmodel.h
locationhelper.cpp
locationhelper.h
events/pollevent.cpp
pollhandler.cpp
# models/livelocationsmodel.cpp
# models/livelocationsmodel.h
# models/locationsmodel.cpp
# models/locationsmodel.h
# locationhelper.cpp
# locationhelper.h
# events/pollevent.cpp
# pollhandler.cpp
utils.h
utils.cpp
registration.cpp
# registration.cpp
neochatconnection.cpp
neochatconnection.h
jobs/neochatgetcommonroomsjob.cpp
jobs/neochatgetcommonroomsjob.h
# jobs/neochatgetcommonroomsjob.cpp
# jobs/neochatgetcommonroomsjob.h
mediasizehelper.cpp
mediasizehelper.h
eventhandler.cpp
enums/delegatetype.h
roomlastmessageprovider.cpp
roomlastmessageprovider.h
# eventhandler.cpp
# enums/delegatetype.h
# roomlastmessageprovider.cpp
# roomlastmessageprovider.h
chatbarcache.cpp
chatbarcache.h
colorschemer.cpp
colorschemer.h
models/notificationsmodel.cpp
models/notificationsmodel.h
models/timelinemodel.cpp
models/timelinemodel.h
# models/notificationsmodel.cpp
# models/notificationsmodel.h
# models/timelinemodel.cpp
# models/timelinemodel.h
enums/pushrule.h
models/itinerarymodel.cpp
models/itinerarymodel.h
# models/itinerarymodel.cpp
# models/itinerarymodel.h
proxycontroller.cpp
proxycontroller.h
models/linemodel.cpp
models/linemodel.h
events/locationbeaconevent.h
events/widgetevent.h
enums/messagecomponenttype.h
models/messagecontentmodel.cpp
models/messagecontentmodel.h
# events/locationbeaconevent.h
# events/widgetevent.h
# enums/messagecomponenttype.h
# models/messagecontentmodel.cpp
# models/messagecontentmodel.h
enums/neochatroomtype.h
models/sortfilterroomtreemodel.cpp
models/sortfilterroomtreemodel.h
mediamanager.cpp
mediamanager.h
models/statekeysmodel.cpp
models/statekeysmodel.h
sharehandler.cpp
sharehandler.h
# models/statekeysmodel.cpp
# models/statekeysmodel.h
# sharehandler.cpp
# sharehandler.h
models/roomtreeitem.cpp
models/roomtreeitem.h
foreigntypes.h
models/threepidmodel.cpp
models/threepidmodel.h
threepidaddhelper.cpp
threepidaddhelper.h
identityserverhelper.cpp
identityserverhelper.h
enums/powerlevel.cpp
enums/powerlevel.h
models/permissionsmodel.cpp
models/permissionsmodel.h
threepidbindhelper.cpp
threepidbindhelper.h
models/readmarkermodel.cpp
models/readmarkermodel.h
neochatroommember.cpp
neochatroommember.h
models/threadmodel.cpp
models/threadmodel.h
enums/messagetype.h
messagecomponent.h
# models/threepidmodel.cpp
# models/threepidmodel.h
# threepidaddhelper.cpp
# threepidaddhelper.h
# identityserverhelper.cpp
# identityserverhelper.h
# enums/powerlevel.cpp
# enums/powerlevel.h
# models/permissionsmodel.cpp
# models/permissionsmodel.h
# threepidbindhelper.cpp
# threepidbindhelper.h
# models/readmarkermodel.cpp
# models/readmarkermodel.h
# neochatroommember.cpp
# neochatroommember.h
# models/threadmodel.cpp
# models/threadmodel.h
# enums/messagetype.h
# messagecomponent.h
enums/roomsortparameter.cpp
enums/roomsortparameter.h
models/roomsortparametermodel.cpp
models/roomsortparametermodel.h
models/messagemodel.cpp
models/messagemodel.h
models/messagecontentfiltermodel.cpp
models/messagecontentfiltermodel.h
models/pinnedmessagemodel.cpp
models/pinnedmessagemodel.h
models/commonroomsmodel.cpp
models/commonroomsmodel.h
# models/roomsortparametermodel.cpp
# models/roomsortparametermodel.h
# models/messagemodel.cpp
# models/messagemodel.h
# models/messagecontentfiltermodel.cpp
# models/messagecontentfiltermodel.h
# models/pinnedmessagemodel.cpp
# models/pinnedmessagemodel.h
# models/commonroomsmodel.cpp
# models/commonroomsmodel.h
)
set_source_files_properties(qml/OsmLocationPlugin.qml PROPERTIES
@@ -297,8 +297,8 @@ ecm_add_qml_module(neochat URI org.kde.neochat GENERATE_PLUGIN_SOURCE
qml/AvatarNotification.qml
qml/ReasonDialog.qml
SOURCES
messageattached.cpp
messageattached.h
# messageattached.cpp
# messageattached.h
DEPENDENCIES
QtCore
QtQuick
@@ -398,10 +398,10 @@ endif()
if (NOT ANDROID AND NOT WIN32 AND NOT APPLE AND NOT HAIKU)
target_compile_definitions(neochat PUBLIC -DHAVE_RUNNER)
target_compile_definitions(neochat PUBLIC -DHAVE_X11=1)
target_sources(neochat PRIVATE runner.cpp)
# target_sources(neochat PRIVATE runner.cpp)
if (TARGET KUnifiedPush)
target_sources(neochat PRIVATE fakerunner.cpp)
# target_sources(neochat PRIVATE fakerunner.cpp)
endif()
else()
target_compile_definitions(neochat PUBLIC -DHAVE_X11=0)
@@ -427,7 +427,7 @@ target_link_libraries(neochat PUBLIC
KF6::IconThemes
KF6::ColorScheme
KF6::ItemModels
QuotientQt6
Integral
cmark::cmark
QCoro::Core
QCoro::Network

View File

@@ -344,13 +344,13 @@ QQC2.Control {
Item {
implicitWidth: replyComponent.implicitWidth
implicitHeight: replyComponent.implicitHeight
ReplyComponent {
id: replyComponent
replyEventId: _private.chatBarCache.replyId
replyAuthor: _private.chatBarCache.relationAuthor
replyContentModel: _private.chatBarCache.relationEventContentModel
Message.maxContentWidth: paneLoader.item.width
}
// ReplyComponent {
// id: replyComponent
// replyEventId: _private.chatBarCache.replyId
// replyAuthor: _private.chatBarCache.relationAuthor
// replyContentModel: _private.chatBarCache.relationEventContentModel
// Message.maxContentWidth: paneLoader.item.width
// }
QQC2.Button {
id: cancelButton
@@ -498,23 +498,23 @@ QQC2.Control {
}
}
EmojiDialog {
id: emojiDialog
x: root.width - width
y: -implicitHeight
modal: false
includeCustom: true
closeOnChosen: false
currentRoom: root.currentRoom
onChosen: emoji => insertText(emoji)
onClosed: if (emojiAction.checked) {
emojiAction.checked = false;
}
}
// EmojiDialog {
// id: emojiDialog
//
// x: root.width - width
// y: -implicitHeight
//
// modal: false
// includeCustom: true
// closeOnChosen: false
//
// currentRoom: root.currentRoom
//
// onChosen: emoji => insertText(emoji)
// onClosed: if (emojiAction.checked) {
// emojiAction.checked = false;
// }
// }
function insertText(text) {
let initialCursorPosition = textField.cursorPosition;

View File

@@ -2,14 +2,15 @@
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "chatbarcache.h"
#include "chatdocumenthandler.h"
#include <Quotient/roommember.h>
#include "chatdocumenthandler.h"
#include "eventhandler.h"
#include "models/actionsmodel.h"
// #include "chatdocumenthandler.h"
// #include "eventhandler.h"
// #include "models/actionsmodel.h"
#include "neochatroom.h"
#include "texthandler.h"
// #include "texthandler.h"
using namespace Qt::StringLiterals;
@@ -88,7 +89,7 @@ void ChatBarCache::setReplyId(const QString &replyId)
m_relationType = Reply;
}
m_attachmentPath = QString();
delete m_relationContentModel;
// delete m_relationContentModel;
Q_EMIT relationIdChanged(oldEventId, m_relationId);
Q_EMIT attachmentPathChanged();
}
@@ -118,27 +119,27 @@ void ChatBarCache::setEditId(const QString &editId)
m_relationType = Edit;
}
m_attachmentPath = QString();
delete m_relationContentModel;
// delete m_relationContentModel;
Q_EMIT relationIdChanged(oldEventId, m_relationId);
Q_EMIT attachmentPathChanged();
}
Quotient::RoomMember ChatBarCache::relationAuthor() const
{
if (parent() == nullptr) {
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
return {};
}
auto room = dynamic_cast<NeoChatRoom *>(parent());
if (room == nullptr) {
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
return {};
}
if (m_relationId.isEmpty()) {
return room->member(QString());
}
return room->member((*room->findInTimeline(m_relationId))->senderId());
}
// Quotient::RoomMember ChatBarCache::relationAuthor() const
// {
// if (parent() == nullptr) {
// qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
// return {};
// }
// auto room = dynamic_cast<NeoChatRoom *>(parent());
// if (room == nullptr) {
// qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
// return {};
// }
// if (m_relationId.isEmpty()) {
// return room->member(QString());
// }
// return room->member((*room->findInTimeline(m_relationId))->senderId());
// }
QString ChatBarCache::relationMessage() const
{
@@ -155,33 +156,33 @@ QString ChatBarCache::relationMessage() const
return {};
}
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
return EventHandler::markdownBody(&**event);
}
// if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
// return EventHandler::markdownBody(&**event);
// }
return {};
}
MessageContentModel *ChatBarCache::relationEventContentModel()
{
if (parent() == nullptr) {
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
return nullptr;
}
if (m_relationId.isEmpty()) {
return nullptr;
}
if (m_relationContentModel != nullptr) {
return m_relationContentModel;
}
auto room = dynamic_cast<NeoChatRoom *>(parent());
if (room == nullptr) {
qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
return nullptr;
}
m_relationContentModel = new MessageContentModel(room, m_relationId, true);
return m_relationContentModel;
}
// MessageContentModel *ChatBarCache::relationEventContentModel()
// {
// if (parent() == nullptr) {
// qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
// return nullptr;
// }
// if (m_relationId.isEmpty()) {
// return nullptr;
// }
// if (m_relationContentModel != nullptr) {
// return m_relationContentModel;
// }
//
// auto room = dynamic_cast<NeoChatRoom *>(parent());
// if (room == nullptr) {
// qWarning() << "ChatBarCache created with incorrect parent, a NeoChatRoom must be set as the parent on creation.";
// return nullptr;
// }
// m_relationContentModel = new MessageContentModel(room, m_relationId, true);
// return m_relationContentModel;
// }
bool ChatBarCache::isThreaded() const
{
@@ -215,7 +216,7 @@ void ChatBarCache::setAttachmentPath(const QString &attachmentPath)
m_attachmentPath = attachmentPath;
m_relationType = None;
const auto oldEventId = std::exchange(m_relationId, QString());
delete m_relationContentModel;
// delete m_relationContentModel;
Q_EMIT attachmentPathChanged();
Q_EMIT relationIdChanged(oldEventId, m_relationId);
}
@@ -225,7 +226,7 @@ void ChatBarCache::clearRelations()
const auto oldEventId = std::exchange(m_relationId, QString());
const auto oldThreadId = std::exchange(m_threadId, QString());
m_attachmentPath = QString();
delete m_relationContentModel;
// delete m_relationContentModel;
Q_EMIT relationIdChanged(oldEventId, m_relationId);
Q_EMIT threadIdChanged(oldThreadId, m_threadId);
Q_EMIT attachmentPathChanged();
@@ -238,7 +239,7 @@ QList<Mention> *ChatBarCache::mentions()
void ChatBarCache::updateMentions(QQuickTextDocument *document, ChatDocumentHandler *documentHandler)
{
documentHandler->setDocument(document);
// documentHandler->setDocument(document);
if (parent() == nullptr) {
qWarning() << "ChatBarCache created with no parent, a NeoChatRoom must be set as the parent on creation.";
@@ -253,35 +254,35 @@ void ChatBarCache::updateMentions(QQuickTextDocument *document, ChatDocumentHand
return;
}
if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
if (const auto &roomMessageEvent = &*event->viewAs<Quotient::RoomMessageEvent>()) {
// Replaces the mentions that are baked into the HTML but plaintext in the original markdown
const QRegularExpression re(uR"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"_s);
m_mentions.clear();
int linkSize = 0;
auto matches = re.globalMatch(EventHandler::rawMessageBody(*roomMessageEvent));
while (matches.hasNext()) {
const QRegularExpressionMatch match = matches.next();
if (match.hasMatch()) {
const QString id = match.captured(1);
const QString name = match.captured(2);
const int position = match.capturedStart(0) - linkSize;
const int end = position + name.length();
linkSize += match.capturedLength(0) - name.length();
QTextCursor cursor(documentHandler->document()->textDocument());
cursor.setPosition(position);
cursor.setPosition(end, QTextCursor::KeepAnchor);
cursor.setKeepPositionOnInsert(true);
m_mentions.push_back(Mention{.cursor = cursor, .text = name, .start = position, .position = end, .id = id});
}
}
}
}
// if (auto event = room->findInTimeline(m_relationId); event != room->historyEdge()) {
// if (const auto &roomMessageEvent = &*event->viewAs<Quotient::RoomMessageEvent>()) {
// // Replaces the mentions that are baked into the HTML but plaintext in the original markdown
// const QRegularExpression re(uR"lit(<a\shref="https:\/\/matrix.to\/#\/([\S]*)"\s?>([\S]*)<\/a>)lit"_s);
//
// m_mentions.clear();
//
// int linkSize = 0;
// auto matches = re.globalMatch(EventHandler::rawMessageBody(*roomMessageEvent));
// while (matches.hasNext()) {
// const QRegularExpressionMatch match = matches.next();
// if (match.hasMatch()) {
// const QString id = match.captured(1);
// const QString name = match.captured(2);
//
// const int position = match.capturedStart(0) - linkSize;
// const int end = position + name.length();
// linkSize += match.capturedLength(0) - name.length();
//
// QTextCursor cursor(documentHandler->document()->textDocument());
// cursor.setPosition(position);
// cursor.setPosition(end, QTextCursor::KeepAnchor);
// cursor.setKeepPositionOnInsert(true);
//
// m_mentions.push_back(Mention{.cursor = cursor, .text = name, .start = position, .position = end, .id = id});
// }
// }
// }
// }
}
QString ChatBarCache::savedText() const
@@ -308,37 +309,37 @@ void ChatBarCache::postMessage()
return;
}
const auto result = ActionsModel::handleAction(room, this);
if (!result.second.has_value()) {
return;
}
// const auto result = ActionsModel::handleAction(room, this);
// if (!result.second.has_value()) {
// return;
// }
TextHandler textHandler;
textHandler.setData(*std::get<std::optional<QString>>(result));
const auto sendText = textHandler.handleSendText();
if (sendText.length() == 0) {
return;
}
// TextHandler textHandler;
// textHandler.setData(*std::get<std::optional<QString>>(result));
// const auto sendText = textHandler.handleSendText();
//
// if (sendText.length() == 0) {
// return;
// }
bool isReply = !replyId().isEmpty();
const auto replyIt = room->findInTimeline(replyId());
if (replyIt == room->historyEdge()) {
isReply = false;
}
// const auto replyIt = room->findInTimeline(replyId());
// if (replyIt == room->historyEdge()) {
// isReply = false;
// }
auto content = std::make_unique<Quotient::EventContent::TextContent>(sendText, u"text/html"_s);
std::optional<Quotient::EventRelation> relatesTo = std::nullopt;
// auto content = std::make_unique<Quotient::EventContent::TextContent>(sendText, u"text/html"_s);
// std::optional<Quotient::EventRelation> relatesTo = std::nullopt;
//
// if (!threadId().isEmpty()) {
// relatesTo = Quotient::EventRelation::replyInThread(threadId(), !isReply, isReply ? replyId() : threadId());
// } else if (!editId().isEmpty()) {
// relatesTo = Quotient::EventRelation::replace(editId());
// } else if (isReply) {
// relatesTo = Quotient::EventRelation::replyTo(replyId());
// }
if (!threadId().isEmpty()) {
relatesTo = Quotient::EventRelation::replyInThread(threadId(), !isReply, isReply ? replyId() : threadId());
} else if (!editId().isEmpty()) {
relatesTo = Quotient::EventRelation::replace(editId());
} else if (isReply) {
relatesTo = Quotient::EventRelation::replyTo(replyId());
}
room->post<Quotient::RoomMessageEvent>(text(), *std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result), std::move(content), relatesTo);
// room->post<Quotient::RoomMessageEvent>(text(), *std::get<std::optional<Quotient::RoomMessageEvent::MsgType>>(result), std::move(content), relatesTo);
clearCache();
}

View File

@@ -8,7 +8,7 @@
#include <QQuickTextDocument>
#include <QTextCursor>
#include "models/messagecontentmodel.h"
// #include "models/messagecontentmodel.h"
class ChatDocumentHandler;
@@ -102,7 +102,7 @@ class ChatBarCache : public QObject
*
* @sa Quotient::RoomMember
*/
Q_PROPERTY(Quotient::RoomMember relationAuthor READ relationAuthor NOTIFY relationIdChanged)
// Q_PROPERTY(Quotient::RoomMember relationAuthor READ relationAuthor NOTIFY relationIdChanged)
/**
* @brief The content of the related message.
@@ -116,7 +116,7 @@ class ChatBarCache : public QObject
*
* Will be nullptr if no related message.
*/
Q_PROPERTY(MessageContentModel *relationEventContentModel READ relationEventContentModel NOTIFY relationIdChanged)
// Q_PROPERTY(MessageContentModel *relationEventContentModel READ relationEventContentModel NOTIFY relationIdChanged)
/**
* @brief Whether the chat bar is replying in a thread.
@@ -164,10 +164,10 @@ public:
QString editId() const;
void setEditId(const QString &editId);
Quotient::RoomMember relationAuthor() const;
// Quotient::RoomMember relationAuthor() const;
QString relationMessage() const;
MessageContentModel *relationEventContentModel();
// MessageContentModel *relationEventContentModel();
bool isThreaded() const;
QString threadId() const;
@@ -225,7 +225,7 @@ private:
QList<Mention> m_mentions;
QString m_savedText;
QPointer<MessageContentModel> m_relationContentModel;
// QPointer<MessageContentModel> m_relationContentModel;
void clearCache();
};

View File

@@ -105,7 +105,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
, m_completionModel(new CompletionModel(this))
{
connect(this, &ChatDocumentHandler::roomChanged, this, [this]() {
m_completionModel->setRoom(m_room);
// m_completionModel->setRoom(m_room);
static QPointer<NeoChatRoom> previousRoom = nullptr;
if (previousRoom) {
disconnect(m_chatBarCache, &ChatBarCache::textChanged, this, nullptr);
@@ -113,7 +113,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
previousRoom = m_room;
connect(m_chatBarCache, &ChatBarCache::textChanged, this, [this]() {
int start = completionStartIndex();
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
// m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
});
});
connect(this, &ChatDocumentHandler::documentChanged, this, [this]() {
@@ -124,7 +124,7 @@ ChatDocumentHandler::ChatDocumentHandler(QObject *parent)
return;
}
int start = completionStartIndex();
m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
// m_completionModel->setText(getText().mid(start, cursorPosition() - start), getText().mid(start));
});
}
@@ -217,58 +217,58 @@ void ChatDocumentHandler::complete(int index)
qCWarning(ChatDocumentHandling) << "complete called with m_document set to nullptr.";
return;
}
if (m_completionModel->autoCompletionType() == CompletionModel::None) {
qCWarning(ChatDocumentHandling) << "complete called with m_completionModel->autoCompletionType() == CompletionModel::None.";
return;
}
// if (m_completionModel->autoCompletionType() == CompletionModel::None) {
// qCWarning(ChatDocumentHandling) << "complete called with m_completionModel->autoCompletionType() == CompletionModel::None.";
// return;
// }
// Ensure we only search for the beginning of the current completion identifier
const auto fromIndex = qMax(completionStartIndex(), 0);
if (m_completionModel->autoCompletionType() == CompletionModel::User) {
auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString();
auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
auto text = getText();
auto at = text.indexOf(QLatin1Char('@'), fromIndex);
QTextCursor cursor(document()->textDocument());
cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
cursor.insertText(name + u" "_s);
cursor.setPosition(at);
cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
cursor.setKeepPositionOnInsert(true);
pushMention({cursor, name, 0, 0, id});
m_highlighter->rehighlight();
} else if (m_completionModel->autoCompletionType() == CompletionModel::Command) {
auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
auto text = getText();
auto at = text.indexOf(QLatin1Char('/'), fromIndex);
QTextCursor cursor(document()->textDocument());
cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
cursor.insertText(u"/%1 "_s.arg(command));
} else if (m_completionModel->autoCompletionType() == CompletionModel::Room) {
auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
auto text = getText();
auto at = text.indexOf(QLatin1Char('#'), fromIndex);
QTextCursor cursor(document()->textDocument());
cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
cursor.insertText(alias + u" "_s);
cursor.setPosition(at);
cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
cursor.setKeepPositionOnInsert(true);
pushMention({cursor, alias, 0, 0, alias});
m_highlighter->rehighlight();
} else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) {
auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
auto text = getText();
auto at = text.indexOf(QLatin1Char(':'), fromIndex);
QTextCursor cursor(document()->textDocument());
cursor.setPosition(at);
cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
cursor.insertText(shortcode);
}
// if (m_completionModel->autoCompletionType() == CompletionModel::User) {
// auto name = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::DisplayNameRole).toString();
// auto id = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
// auto text = getText();
// auto at = text.indexOf(QLatin1Char('@'), fromIndex);
// QTextCursor cursor(document()->textDocument());
// cursor.setPosition(at);
// cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
// cursor.insertText(name + u" "_s);
// cursor.setPosition(at);
// cursor.setPosition(cursor.position() + name.size(), QTextCursor::KeepAnchor);
// cursor.setKeepPositionOnInsert(true);
// pushMention({cursor, name, 0, 0, id});
// m_highlighter->rehighlight();
// } else if (m_completionModel->autoCompletionType() == CompletionModel::Command) {
// auto command = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
// auto text = getText();
// auto at = text.indexOf(QLatin1Char('/'), fromIndex);
// QTextCursor cursor(document()->textDocument());
// cursor.setPosition(at);
// cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
// cursor.insertText(u"/%1 "_s.arg(command));
// } else if (m_completionModel->autoCompletionType() == CompletionModel::Room) {
// auto alias = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::SubtitleRole).toString();
// auto text = getText();
// auto at = text.indexOf(QLatin1Char('#'), fromIndex);
// QTextCursor cursor(document()->textDocument());
// cursor.setPosition(at);
// cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
// cursor.insertText(alias + u" "_s);
// cursor.setPosition(at);
// cursor.setPosition(cursor.position() + alias.size(), QTextCursor::KeepAnchor);
// cursor.setKeepPositionOnInsert(true);
// pushMention({cursor, alias, 0, 0, alias});
// m_highlighter->rehighlight();
// } else if (m_completionModel->autoCompletionType() == CompletionModel::Emoji) {
// auto shortcode = m_completionModel->data(m_completionModel->index(index, 0), CompletionModel::ReplacedTextRole).toString();
// auto text = getText();
// auto at = text.indexOf(QLatin1Char(':'), fromIndex);
// QTextCursor cursor(document()->textDocument());
// cursor.setPosition(at);
// cursor.setPosition(cursorPosition(), QTextCursor::KeepAnchor);
// cursor.insertText(shortcode);
// }
}
CompletionModel *ChatDocumentHandler::completionModel() const

View File

@@ -8,19 +8,16 @@
#include <KLocalizedString>
#include <QFile>
#include <QGuiApplication>
#include <QTimer>
#include <signal.h>
#include <Quotient/csapi/notifications.h>
#include <Quotient/qt_connection_util.h>
#include <Quotient/settings.h>
#include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "notificationsmanager.h"
// #include "neochatroom.h"
// #include "notificationsmanager.h"
#include "proxycontroller.h"
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
@@ -43,12 +40,13 @@
bool testMode = false;
using namespace Quotient;
using namespace Integral;
using namespace Qt::Literals::StringLiterals;
Controller::Controller(QObject *parent)
: QObject(parent)
{
Connection::setRoomType<NeoChatRoom>();
// Connection::setRoomType<NeoChatRoom>();
ProxyController::instance().setApplicationProxy();
@@ -57,18 +55,18 @@ Controller::Controller(QObject *parent)
connect(NeoChatConfig::self(), &NeoChatConfig::SystemTrayChanged, this, &Controller::setQuitOnLastWindowClosed);
#endif
if (!testMode) {
QTimer::singleShot(0, this, [this] {
invokeLogin();
});
} else {
auto c = new NeoChatConnection(this);
c->assumeIdentity(u"@user:localhost:1234"_s, u"device_1234"_s, u"token_1234"_s);
connect(c, &Connection::connected, this, [c, this]() {
m_accountRegistry.add(c);
c->syncLoop();
});
}
// if (!testMode) {
// QTimer::singleShot(0, this, [this] {
// invokeLogin();
// });
// } else {
// auto c = new NeoChatConnection(this);
// c->assumeIdentity(u"@user:localhost:1234"_s, u"device_1234"_s, u"token_1234"_s);
// connect(c, &Connection::connected, this, [c, this]() {
// m_accountRegistry.add(c);
// c->syncLoop();
// });
// }
QObject::connect(QGuiApplication::instance(), &QCoreApplication::aboutToQuit, QGuiApplication::instance(), [this] {
delete m_trayIcon;
@@ -99,31 +97,31 @@ Controller::Controller(QObject *parent)
#endif
static int oldAccountCount = 0;
connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
if (m_accountRegistry.size() > oldAccountCount) {
auto connection = dynamic_cast<NeoChatConnection *>(m_accountRegistry.accounts()[m_accountRegistry.size() - 1]);
connect(
connection,
&NeoChatConnection::syncDone,
this,
[this, connection] {
if (!m_endpoint.isEmpty()) {
connection->setupPushNotifications(m_endpoint);
}
},
Qt::SingleShotConnection);
}
oldAccountCount = m_accountRegistry.size();
});
// connect(&m_accountRegistry, &AccountRegistry::accountCountChanged, this, [this]() {
// if (m_accountRegistry.size() > oldAccountCount) {
// auto connection = dynamic_cast<NeoChatConnection *>(m_accountRegistry.accounts()[m_accountRegistry.size() - 1]);
// connect(
// connection,
// &NeoChatConnection::syncDone,
// this,
// [this, connection] {
// if (!m_endpoint.isEmpty()) {
// connection->setupPushNotifications(m_endpoint);
// }
// },
// Qt::SingleShotConnection);
// }
// oldAccountCount = m_accountRegistry.size();
// });
#ifdef HAVE_KUNIFIEDPUSH
auto connector = new KUnifiedPush::Connector(u"org.kde.neochat"_s);
connect(connector, &KUnifiedPush::Connector::endpointChanged, this, [this](const QString &endpoint) {
m_endpoint = endpoint;
for (auto &quotientConnection : m_accountRegistry) {
auto connection = dynamic_cast<NeoChatConnection *>(quotientConnection);
connection->setupPushNotifications(endpoint);
}
// for (auto &quotientConnection : m_accountRegistry) {
// auto connection = dynamic_cast<NeoChatConnection *>(quotientConnection);
// connection->setupPushNotifications(endpoint);
// }
});
connector->registerClient(
@@ -140,147 +138,6 @@ Controller &Controller::instance()
return _instance;
}
void Controller::addConnection(NeoChatConnection *c)
{
Q_ASSERT_X(c, __FUNCTION__, "Attempt to add a null connection");
m_accountRegistry.add(c);
c->setLazyLoading(true);
connect(c, &NeoChatConnection::syncDone, this, [c] {
c->sync(30000);
c->saveState();
});
connect(c, &NeoChatConnection::loggedOut, this, [this, c] {
if (accounts().count() > 1) {
// Only set the connection if the account being logged out is currently active
if (c == activeConnection()) {
setActiveConnection(dynamic_cast<NeoChatConnection *>(accounts().accounts()[0]));
}
} else {
setActiveConnection(nullptr);
}
dropConnection(c);
});
connect(c, &NeoChatConnection::badgeNotificationCountChanged, this, &Controller::updateBadgeNotificationCount);
connect(c, &NeoChatConnection::syncDone, this, [this, c]() {
m_notificationsManager.handleNotifications(c);
});
c->sync();
Q_EMIT connectionAdded(c);
}
void Controller::dropConnection(NeoChatConnection *c)
{
Q_ASSERT_X(c, __FUNCTION__, "Attempt to drop a null connection");
c->disconnect(this);
c->disconnect(&m_notificationsManager);
m_accountRegistry.drop(c);
Q_EMIT connectionDropped(c);
}
void Controller::invokeLogin()
{
const auto accounts = SettingsGroup("Accounts"_L1).childGroups();
for (const auto &accountId : accounts) {
AccountSettings account{accountId};
m_accountsLoading += accountId;
Q_EMIT accountsLoadingChanged();
if (!account.homeserver().isEmpty()) {
auto accessTokenLoadingJob = loadAccessTokenFromKeyChain(account.userId());
connect(accessTokenLoadingJob, &QKeychain::Job::finished, this, [accountId, this, accessTokenLoadingJob](QKeychain::Job *) {
AccountSettings account{accountId};
QString accessToken;
if (accessTokenLoadingJob->error() == QKeychain::Error::NoError) {
accessToken = QString::fromLatin1(accessTokenLoadingJob->binaryData());
} else {
return;
}
auto connection = new NeoChatConnection(account.homeserver());
m_connectionsLoading[accountId] = connection;
connect(connection, &NeoChatConnection::connected, this, [this, connection, accountId] {
connection->loadState();
if (connection->allRooms().size() == 0 || connection->allRooms()[0]->currentState().get<RoomCreateEvent>()) {
addConnection(connection);
m_accountsLoading.removeAll(connection->userId());
m_connectionsLoading.remove(accountId);
Q_EMIT accountsLoadingChanged();
} else {
connect(
connection->allRooms()[0],
&Room::baseStateLoaded,
this,
[this, connection, accountId]() {
addConnection(connection);
m_accountsLoading.removeAll(connection->userId());
m_connectionsLoading.remove(accountId);
Q_EMIT accountsLoadingChanged();
},
Qt::SingleShotConnection);
}
});
connection->assumeIdentity(account.userId(), account.deviceId(), accessToken);
});
}
}
}
QKeychain::ReadPasswordJob *Controller::loadAccessTokenFromKeyChain(const QString &userId)
{
qDebug() << "Reading access token from the keychain for" << userId;
auto job = new QKeychain::ReadPasswordJob(qAppName(), this);
job->setKey(userId);
// Handling of errors
connect(job, &QKeychain::Job::finished, this, [this, job]() {
if (job->error() == QKeychain::Error::NoError) {
return;
}
switch (job->error()) {
case QKeychain::EntryNotFound:
Q_EMIT errorOccured(i18n("Access token wasn't found: Maybe it was deleted?"));
break;
case QKeychain::AccessDeniedByUser:
case QKeychain::AccessDenied:
Q_EMIT errorOccured(i18n("Access to keychain was denied: Please allow NeoChat to read the access token"));
break;
case QKeychain::NoBackendAvailable:
Q_EMIT errorOccured(i18n("No keychain available: Please install a keychain, e.g. KWallet or GNOME keyring on Linux"));
break;
case QKeychain::OtherError:
Q_EMIT errorOccured(i18n("Unable to read access token: %1", job->errorString()));
break;
default:
break;
}
});
job->start();
return job;
}
void Controller::saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken)
{
qDebug() << "Save the access token to the keychain for " << userId;
auto job = new QKeychain::WritePasswordJob(qAppName());
job->setAutoDelete(true);
job->setKey(userId);
job->setBinaryData(accessToken);
connect(job, &QKeychain::WritePasswordJob::finished, this, [job]() {
if (job->error()) {
qWarning() << "Could not save access token to the keychain: " << qPrintable(job->errorString());
}
});
job->start();
}
bool Controller::supportSystemTray() const
{
#ifdef Q_OS_ANDROID
@@ -322,7 +179,7 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
if (m_connection != nullptr) {
m_connection->disconnect(this);
m_connection->disconnect(&m_notificationsManager);
// m_connection->disconnect(&m_notificationsManager);
}
m_connection = connection;
@@ -331,7 +188,7 @@ void Controller::setActiveConnection(NeoChatConnection *connection)
m_connection->refreshBadgeNotificationCount();
updateBadgeNotificationCount(m_connection, m_connection->badgeNotificationCount());
connect(m_connection, &NeoChatConnection::errorOccured, this, &Controller::errorOccured);
// connect(m_connection, &NeoChatConnection::errorOccured, this, &Controller::errorOccured);
}
Q_EMIT activeConnectionChanged(m_connection);
@@ -346,7 +203,7 @@ void Controller::listenForNotifications()
connect(timer, &QTimer::timeout, qGuiApp, &QGuiApplication::quit);
connect(connector, &KUnifiedPush::Connector::messageReceived, [timer](const QByteArray &data) {
instance().m_notificationsManager.postPushNotification(data);
// instance().m_notificationsManager.postPushNotification(data);
timer->stop();
});
@@ -360,7 +217,7 @@ void Controller::listenForNotifications()
void Controller::clearInvitationNotification(const QString &roomId)
{
m_notificationsManager.clearInvitationNotification(roomId);
// m_notificationsManager.clearInvitationNotification(roomId);
}
void Controller::updateBadgeNotificationCount(NeoChatConnection *connection, int count)
@@ -402,9 +259,9 @@ bool Controller::isFlatpak() const
#endif
}
AccountRegistry &Controller::accounts()
Accounts &Controller::accounts()
{
return m_accountRegistry;
return m_accounts;
}
QString Controller::loadFileContent(const QString &path) const
@@ -420,20 +277,6 @@ void Controller::setTestMode(bool test)
testMode = test;
}
void Controller::removeConnection(const QString &userId)
{
// When loadAccessTokenFromKeyChain() fails m_connectionsLoading won't have an
// entry for it so we need to check both separately.
if (m_accountsLoading.contains(userId)) {
m_accountsLoading.removeAll(userId);
Q_EMIT accountsLoadingChanged();
}
if (m_connectionsLoading.contains(userId) && m_connectionsLoading[userId]) {
auto connection = m_connectionsLoading[userId];
SettingsGroup("Accounts"_L1).remove(userId);
}
}
bool Controller::csSupported() const
{
return true;

View File

@@ -7,8 +7,8 @@
#include <QQmlEngine>
#include "neochatconnection.h"
#include "notificationsmanager.h"
#include <Quotient/accountregistry.h>
// #include "notificationsmanager.h"
#include <Integral/Accounts>
class TrayIcon;
@@ -63,21 +63,6 @@ public:
void setActiveConnection(NeoChatConnection *connection);
[[nodiscard]] NeoChatConnection *activeConnection() const;
/**
* @brief Add a new connection to the account registry.
*/
void addConnection(NeoChatConnection *c);
/**
* @brief Drop a connection from the account registry.
*/
void dropConnection(NeoChatConnection *c);
/**
* @brief Save an access token to the keychain for the given account.
*/
void saveAccessTokenToKeyChain(const QString &userId, const QByteArray &accessToken);
[[nodiscard]] bool supportSystemTray() const;
bool isFlatpak() const;
@@ -97,12 +82,10 @@ public:
Q_INVOKABLE QString loadFileContent(const QString &path) const;
Quotient::AccountRegistry &accounts();
Integral::Accounts &accounts();
static void setTestMode(bool testMode);
Q_INVOKABLE void removeConnection(const QString &userId);
bool csSupported() const;
/**
@@ -122,18 +105,15 @@ private:
QPointer<NeoChatConnection> m_connection;
TrayIcon *m_trayIcon = nullptr;
QKeychain::ReadPasswordJob *loadAccessTokenFromKeyChain(const QString &account);
Quotient::AccountRegistry m_accountRegistry;
Integral::Accounts m_accounts;
QStringList m_accountsLoading;
QMap<QString, QPointer<NeoChatConnection>> m_connectionsLoading;
QString m_endpoint;
QStringList m_shownImages;
NotificationsManager m_notificationsManager;
// NotificationsManager m_notificationsManager;
private Q_SLOTS:
void invokeLogin();
void setQuitOnLastWindowClosed();
void updateBadgeNotificationCount(NeoChatConnection *connection, int count);

View File

@@ -4,12 +4,12 @@
#pragma once
#include <QObject>
#include "neochatroom.h"
#include <Quotient/quotient_common.h>
#include <QQmlEngine>
#include <KLocalizedString>
#include <Integral/lib.rs.h>
using namespace Qt::StringLiterals;
class NeoChatRoomType : public QObject
@@ -22,7 +22,7 @@ public:
/**
* @brief Defines the room list categories a room can be assigned.
*/
enum Types {
enum Type {
Invited, /**< The user has been invited to the room. */
Favorite, /**< The room is set as a favourite. */
Direct, /**< The room is a direct chat. */
@@ -32,23 +32,23 @@ public:
AddDirect, /**< So we can show the add friend delegate. */
TypesCount, /**< Number of different types (this should always be last). */
};
Q_ENUM(Types);
Q_ENUM(Type);
static NeoChatRoomType::Types typeForRoom(const NeoChatRoom *room)
static NeoChatRoomType::Type typeForRoom(rust::Box<sdk::RoomListRoom> room)
{
if (room->isSpace()) {
if (room->is_space()) {
return NeoChatRoomType::Space;
}
if (room->joinState() == Quotient::JoinState::Invite) {
if (room->state() == 2) {
return NeoChatRoomType::Invited;
}
if (room->isFavourite()) {
if (room->is_favourite()) {
return NeoChatRoomType::Favorite;
}
if (room->isLowPriority()) {
if (room->is_low_priority()) {
return NeoChatRoomType::Deprioritized;
}
if (room->isDirectChat()) {
if (false /*room->isDirectChat()*/) {
return NeoChatRoomType::Direct;
}
return NeoChatRoomType::Normal;

View File

@@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
@@ -5,8 +6,8 @@
#include <algorithm>
#include "neochatconfig.h"
#include "neochatroom.h"
// #include "neochatconfig.h"
#include <Integral/Utils>
namespace
{
@@ -56,27 +57,27 @@ QList<RoomSortParameter::Parameter> RoomSortParameter::allParameterList()
QList<RoomSortParameter::Parameter> RoomSortParameter::currentParameterList()
{
QList<RoomSortParameter::Parameter> configParamList;
switch (static_cast<NeoChatConfig::EnumSortOrder::type>(NeoChatConfig::sortOrder())) {
case NeoChatConfig::EnumSortOrder::Activity:
configParamList = activitySortPriorities;
break;
case NeoChatConfig::EnumSortOrder::Alphabetical:
configParamList = alphabeticalSortPriorities;
break;
case NeoChatConfig::EnumSortOrder::LastMessage:
configParamList = lastMessageSortPriorities;
break;
case NeoChatConfig::EnumSortOrder::Custom: {
const auto intList = NeoChatConfig::customSortOrder();
std::transform(intList.constBegin(), intList.constEnd(), std::back_inserter(configParamList), [](int param) {
return static_cast<Parameter>(param);
});
break;
}
default:
break;
}
QList<RoomSortParameter::Parameter> configParamList = activitySortPriorities;
// switch (static_cast<NeoChatConfig::EnumSortOrder::type>(NeoChatConfig::sortOrder())) {
// case NeoChatConfig::EnumSortOrder::Activity:
// configParamList = activitySortPriorities;
// break;
// case NeoChatConfig::EnumSortOrder::Alphabetical:
// configParamList = alphabeticalSortPriorities;
// break;
// case NeoChatConfig::EnumSortOrder::LastMessage:
// configParamList = lastMessageSortPriorities;
// break;
// case NeoChatConfig::EnumSortOrder::Custom: {
// const auto intList = NeoChatConfig::customSortOrder();
// std::transform(intList.constBegin(), intList.constEnd(), std::back_inserter(configParamList), [](int param) {
// return static_cast<Parameter>(param);
// });
// break;
// }
// default:
// break;
// }
if (configParamList.isEmpty()) {
return activitySortPriorities;
@@ -90,73 +91,74 @@ void RoomSortParameter::saveNewParameterList(const QList<Parameter> &newList)
std::transform(newList.constBegin(), newList.constEnd(), std::back_inserter(intList), [](Parameter param) {
return static_cast<int>(param);
});
NeoChatConfig::setCustomSortOrder(intList);
NeoChatConfig::setSortOrder(NeoChatConfig::EnumSortOrder::Custom);
NeoChatConfig::self()->save();
// NeoChatConfig::setCustomSortOrder(intList);
// NeoChatConfig::setSortOrder(NeoChatConfig::EnumSortOrder::Custom);
// NeoChatConfig::self()->save();
}
int RoomSortParameter::compareParameter(Parameter parameter, NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
int RoomSortParameter::compareParameter(Parameter parameter, rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
{
switch (parameter) {
case AlphabeticalAscending:
return compareParameter<AlphabeticalAscending>(leftRoom, rightRoom);
return compareParameter<AlphabeticalAscending>(leftRoom->box_me(), rightRoom->box_me());
case AlphabeticalDescending:
return compareParameter<AlphabeticalDescending>(leftRoom, rightRoom);
return compareParameter<AlphabeticalDescending>(leftRoom->box_me(), rightRoom->box_me());
case HasUnread:
return compareParameter<HasUnread>(leftRoom, rightRoom);
return compareParameter<HasUnread>(leftRoom->box_me(), rightRoom->box_me());
case MostUnread:
return compareParameter<MostUnread>(leftRoom, rightRoom);
return compareParameter<MostUnread>(leftRoom->box_me(), rightRoom->box_me());
case HasHighlight:
return compareParameter<HasHighlight>(leftRoom, rightRoom);
return compareParameter<HasHighlight>(leftRoom->box_me(), rightRoom->box_me());
case MostHighlights:
return compareParameter<MostHighlights>(leftRoom, rightRoom);
return compareParameter<MostHighlights>(leftRoom->box_me(), rightRoom->box_me());
case LastActive:
return compareParameter<LastActive>(leftRoom, rightRoom);
return compareParameter<LastActive>(leftRoom->box_me(), rightRoom->box_me());
default:
return 0;
}
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
{
return -typeCompare(leftRoom->displayName(), rightRoom->displayName());
return -typeCompare(stringFromRust(leftRoom->display_name()), stringFromRust(rightRoom->display_name()));
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(rust::Box<sdk::RoomListRoom> leftRoom,
rust::Box<sdk::RoomListRoom> rightRoom)
{
return typeCompare(leftRoom->displayName(), rightRoom->displayName());
return typeCompare(stringFromRust(leftRoom->display_name()), stringFromRust(rightRoom->display_name()));
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
{
return typeCompare(leftRoom->contextAwareNotificationCount() > 0, rightRoom->contextAwareNotificationCount() > 0);
return typeCompare(leftRoom->num_unread_messages() > 0, rightRoom->num_unread_messages() > 0);
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
{
return typeCompare(leftRoom->contextAwareNotificationCount(), rightRoom->contextAwareNotificationCount());
return typeCompare(leftRoom->num_unread_messages(), rightRoom->num_unread_messages());
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
{
const auto leftHighlight = leftRoom->highlightCount() > 0 && leftRoom->contextAwareNotificationCount() > 0;
const auto rightHighlight = rightRoom->highlightCount() > 0 && rightRoom->contextAwareNotificationCount() > 0;
const auto leftHighlight = leftRoom->num_unread_mentions() > 0 && leftRoom->num_unread_messages() > 0;
const auto rightHighlight = rightRoom->num_unread_mentions() > 0 && rightRoom->num_unread_messages() > 0;
return typeCompare(leftHighlight, rightHighlight);
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
{
return typeCompare(int(leftRoom->highlightCount()), int(rightRoom->highlightCount()));
return typeCompare(int(leftRoom->num_unread_mentions()), int(rightRoom->num_unread_mentions()));
}
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom)
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom)
{
return typeCompare(leftRoom->lastActiveTime(), rightRoom->lastActiveTime());
return 1;
}

View File

@@ -8,7 +8,9 @@
#include <KLocalizedString>
class NeoChatRoom;
#include <Integral/lib.rs.h>
class Room;
/**
* @class RoomSortParameter
@@ -116,27 +118,29 @@ public:
*
* @sa Parameter
*/
static int compareParameter(Parameter parameter, NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
static int compareParameter(Parameter parameter, rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
private:
template<Parameter parameter>
static int compareParameter(NeoChatRoom *, NeoChatRoom *)
static int compareParameter(rust::Box<sdk::RoomListRoom>, rust::Box<sdk::RoomListRoom>)
{
return false;
}
};
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalAscending>(rust::Box<sdk::RoomListRoom> leftRoom,
rust::Box<sdk::RoomListRoom> rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
int RoomSortParameter::compareParameter<RoomSortParameter::AlphabeticalDescending>(rust::Box<sdk::RoomListRoom> leftRoom,
rust::Box<sdk::RoomListRoom> rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
int RoomSortParameter::compareParameter<RoomSortParameter::HasUnread>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
int RoomSortParameter::compareParameter<RoomSortParameter::MostUnread>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
int RoomSortParameter::compareParameter<RoomSortParameter::HasHighlight>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
int RoomSortParameter::compareParameter<RoomSortParameter::MostHighlights>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);
template<>
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(NeoChatRoom *leftRoom, NeoChatRoom *rightRoom);
int RoomSortParameter::compareParameter<RoomSortParameter::LastActive>(rust::Box<sdk::RoomListRoom> leftRoom, rust::Box<sdk::RoomListRoom> rightRoom);

View File

@@ -5,44 +5,36 @@
#include <QQmlEngine>
#include <Quotient/accountregistry.h>
#include <Quotient/e2ee/sssshandler.h>
#include <Quotient/keyimport.h>
#include <Quotient/keyverificationsession.h>
#include <Quotient/roommember.h>
#include <Integral/Accounts>
#include <Integral/Homeserver>
#include "controller.h"
#include "neochatconfig.h"
struct ForeignAccountRegistry {
struct ForeignAccounts {
Q_GADGET
QML_FOREIGN(Quotient::AccountRegistry)
QML_NAMED_ELEMENT(AccountRegistry)
QML_ELEMENT
QML_SINGLETON
public:
static Quotient::AccountRegistry *create(QQmlEngine *, QJSEngine *)
QML_FOREIGN(Integral::Accounts)
QML_NAMED_ELEMENT(Accounts)
static Integral::Accounts *create(QQmlEngine *, QJSEngine *)
{
QQmlEngine::setObjectOwnership(&Controller::instance().accounts(), QQmlEngine::CppOwnership);
return &Controller::instance().accounts();
auto &accounts = Controller::instance().accounts();
QQmlEngine::setObjectOwnership(&accounts, QQmlEngine::CppOwnership);
return &accounts;
}
};
struct ForeignKeyVerificationSession {
struct ForeignHomeserver {
Q_GADGET
QML_FOREIGN(Quotient::KeyVerificationSession)
QML_NAMED_ELEMENT(KeyVerificationSession)
QML_UNCREATABLE("")
QML_ELEMENT
QML_FOREIGN(Integral::Homeserver)
QML_NAMED_ELEMENT(Homeserver)
};
struct ForeignSSSSHandler {
struct ForeignConnection {
Q_GADGET
QML_FOREIGN(Quotient::SSSSHandler)
QML_NAMED_ELEMENT(SSSSHandler)
};
struct ForeignKeyImport {
Q_GADGET
QML_SINGLETON
QML_FOREIGN(Quotient::KeyImport)
QML_NAMED_ELEMENT(KeyImport)
QML_ELEMENT
QML_FOREIGN(Integral::Connection)
QML_NAMED_ELEMENT(Connection)
};

View File

@@ -16,8 +16,15 @@ LoginStep {
onActiveFocusChanged: if (activeFocus)
matrixIdField.forceActiveFocus()
Component.onCompleted: {
LoginHelper.matrixId = "";
property Homeserver homeserver
Timer {
id: timer
interval: 500
repeat: false
onTriggered: if (matrixIdField.text.length > 0) {
root.homeserver.resolveFromMatrixId(matrixIdField.text)
}
}
FormCard.FormTextFieldDelegate {
@@ -26,7 +33,7 @@ LoginStep {
placeholderText: "@user:example.org"
Accessible.name: i18n("Matrix ID")
onTextChanged: {
LoginHelper.matrixId = text;
timer.restart()
}
Keys.onReturnPressed: {
@@ -35,17 +42,17 @@ LoginStep {
}
nextAction: Kirigami.Action {
text: LoginHelper.isLoggedIn ? i18n("Already logged in") : (LoginHelper.testing && matrixIdField.acceptableInput) ? i18n("Loading…") : i18nc("@action:button", "Continue")
// text: LoginHelper.isLoggedIn ? i18n("Already logged in") : (LoginHelper.testing && matrixIdField.acceptableInput) ? i18n("Loading…") : i18nc("@action:button", "Continue")
onTriggered: {
if (LoginHelper.supportsSso && LoginHelper.supportsPassword) {
if (root.homeserver.ssoLoginSupported && root.homeserver.passwordLoginSupported) {
processed("LoginMethod");
} else if (LoginHelper.supportsSso) {
} else if (root.homeserver.ssoLoginSupported) {
processed("Sso");
} else {
processed("Password");
}
}
enabled: LoginHelper.homeserverReachable
enabled: root.homeserver.loginFlowsLoaded
}
previousAction: Kirigami.Action {
onTriggered: {

View File

@@ -12,6 +12,8 @@ import org.kde.neochat
LoginStep {
id: root
property Homeserver homeserver
Connections {
target: LoginHelper
function onConnected() {
@@ -26,7 +28,6 @@ LoginStep {
id: passwordField
label: i18n("Password:")
onTextChanged: LoginHelper.password = text
enabled: !LoginHelper.isLoggingIn
echoMode: TextInput.Password
Accessible.name: i18n("Password")
@@ -39,10 +40,10 @@ LoginStep {
nextAction: Kirigami.Action {
text: i18nc("@action:button", "Login")
enabled: passwordField.text.length > 0 && !LoginHelper.isLoggingIn
// enabled: passwordField.text.length > 0 && !LoginHelper.isLoggingIn
onTriggered: {
root.clearError();
LoginHelper.login();
let pending = Accounts.loginWithPassword(root.homeserver.matrixId, passwordField.text)
}
}
previousAction: Kirigami.Action {

View File

@@ -27,6 +27,10 @@ Kirigami.Page {
title: i18n("Welcome")
globalToolBarStyle: Kirigami.ApplicationHeaderStyle.None
Homeserver {
id: homeserver
}
header: QQC2.Control {
topPadding: 0
bottomPadding: 0
@@ -81,7 +85,7 @@ Kirigami.Page {
FormCard.FormHeader {
id: existingAccountsHeader
title: i18nc("@title", "Continue with an existing account")
visible: (loadedAccounts.count > 0 || loadingAccounts.count > 0) && root._showExisting
// visible: (loadedAccounts.count > 0 || loadingAccounts.count > 0) && root._showExisting
maximumWidth: Kirigami.Units.gridUnit * 20
}
@@ -90,15 +94,21 @@ Kirigami.Page {
maximumWidth: Kirigami.Units.gridUnit * 20
Repeater {
id: loadedAccounts
model: AccountRegistry
model: Accounts
delegate: FormCard.FormButtonDelegate {
id: delegate
required property string userId
required property NeoChatConnection connection
required property string matrixId
required property string displayName
required property string avatarUrl
required property int index
required property bool ready
required property Connection connection
text: QmlUtils.escapeString(connection.localUser.displayName)
description: connection.localUser.id
enabled: ready
text: QmlUtils.escapeString(delegate.displayName)
description: delegate.matrixId
leadingPadding: Kirigami.Units.largeSpacing
onClicked: {
@@ -109,62 +119,12 @@ Kirigami.Page {
id: avatar
name: delegate.text
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
source: delegate.connection.localUser.avatarUrl.toString().length > 0 ? delegate.connection.makeMediaUrl(delegate.connection.localUser.avatarUrl) : ""
source: delegate.avatarUrl
implicitWidth: Kirigami.Units.iconSizes.medium
implicitHeight: Kirigami.Units.iconSizes.medium
}
}
}
Repeater {
id: loadingAccounts
model: Controller.accountsLoading
delegate: FormCard.AbstractFormDelegate {
id: loadingDelegate
topPadding: Kirigami.Units.smallSpacing
bottomPadding: Kirigami.Units.smallSpacing
background: null
contentItem: RowLayout {
spacing: 0
QQC2.Label {
Layout.fillWidth: true
text: i18nc("As in 'this account is still loading'", "%1 (loading)", modelData)
elide: Text.ElideRight
wrapMode: Text.Wrap
maximumLineCount: 2
color: Kirigami.Theme.disabledTextColor
Accessible.ignored: true // base class sets this text on root already
}
QQC2.ToolButton {
text: i18nc("@action:button", "Log out of this account")
icon.name: "im-kick-user"
onClicked: Controller.removeConnection(modelData)
display: QQC2.Button.IconOnly
QQC2.ToolTip.text: text
QQC2.ToolTip.visible: hovered
QQC2.ToolTip.delay: Kirigami.Units.toolTipDelay
enabled: true
Layout.preferredHeight: Kirigami.Units.gridUnit * 2
}
FormCard.FormArrow {
Layout.leftMargin: Kirigami.Units.smallSpacing
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
direction: Qt.RightArrow
visible: root.background.visible
}
}
}
onCountChanged: {
if (loadingAccounts.count === 0 && loadedAccounts.count === 1 && showExisting) {
Controller.activeConnection = AccountRegistry.data(AccountRegistry.index(0, 0), 257);
root.connectionChosen();
}
}
}
}
FormCard.FormHeader {
@@ -188,6 +148,7 @@ Kirigami.Page {
root.currentStepString = nextStep;
headerMessage.text = "";
headerMessage.visible = false;
module.item.homeserver = homeserver
if (!module.item.noControls) {
module.item.forceActiveFocus();
} else {
@@ -211,33 +172,33 @@ Kirigami.Page {
}
}
Connections {
target: Registration
function onNextStepChanged() {
if (Registration.nextStep === "m.login.recaptcha") {
stepConnections.onProcessed("Captcha");
}
if (Registration.nextStep === "m.login.terms") {
stepConnections.onProcessed("Terms");
}
if (Registration.nextStep === "m.login.email.identity") {
stepConnections.onProcessed("Email");
}
if (Registration.nextStep === "loading") {
stepConnections.onProcessed("Loading");
}
}
}
Connections {
target: LoginHelper
function onLoginErrorOccured(message) {
headerMessage.text = message;
headerMessage.visible = message.length > 0;
headerMessage.type = Kirigami.MessageType.Error;
}
}
// Connections {
// target: Registration
//
// function onNextStepChanged() {
// if (Registration.nextStep === "m.login.recaptcha") {
// stepConnections.onProcessed("Captcha");
// }
// if (Registration.nextStep === "m.login.terms") {
// stepConnections.onProcessed("Terms");
// }
// if (Registration.nextStep === "m.login.email.identity") {
// stepConnections.onProcessed("Email");
// }
// if (Registration.nextStep === "loading") {
// stepConnections.onProcessed("Loading");
// }
// }
// }
// Connections {
// target: LoginHelper
//
// function onLoginErrorOccured(message) {
// headerMessage.text = message;
// headerMessage.visible = message.length > 0;
// headerMessage.type = Kirigami.MessageType.Error;
// }
// }
}
FormCard.FormDelegateSeparator {
@@ -276,11 +237,11 @@ Kirigami.Page {
}
}
Component.onCompleted: {
LoginHelper.init();
module.item.forceActiveFocus();
Registration.username = "";
Registration.password = "";
Registration.email = "";
}
// Component.onCompleted: {
// LoginHelper.init();
// module.item.forceActiveFocus();
// Registration.username = "";
// Registration.password = "";
// Registration.email = "";
// }
}

View File

@@ -13,7 +13,12 @@
#include <QQuickStyle>
#include <QQuickWindow>
#include <QtQml/QQmlExtensionPlugin>
#include <Quotient/connection.h>
#include <Integral/Connection_p>
#include <Integral/NetworkAccessManager>
#include <Integral/PendingConnection>
// #include <Quotient/connection.h>
#ifdef Q_OS_ANDROID
#include <QGuiApplication>
@@ -43,31 +48,35 @@
#include "neochat-version.h"
#include <Quotient/networkaccessmanager.h>
// #include <Quotient/networkaccessmanager.h>
#include "blurhashimageprovider.h"
#include "colorschemer.h"
#include "controller.h"
// #include "controller.h"
#include "logger.h"
#include "roommanager.h"
#include "sharehandler.h"
// #include "roommanager.h"
// #include "sharehandler.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "windowcontroller.h"
#ifdef HAVE_RUNNER
#include "runner.h"
// #include "runner.h"
#include <QDBusConnection>
#include <QDBusMetaType>
#endif
#if defined(HAVE_RUNNER) && defined(HAVE_KUNIFIEDPUSH)
#include "fakerunner.h"
// #include "fakerunner.h"
#endif
#ifdef Q_OS_WINDOWS
#include <Windows.h>
#endif
using namespace Quotient;
using namespace Qt::Literals::StringLiterals;
using namespace Integral;
void qml_register_types_org_kde_neochat();
@@ -158,14 +167,14 @@ int main(int argc, char *argv[])
about.setTranslator(i18nc("NAME OF TRANSLATORS", "Your names"), i18nc("EMAIL OF TRANSLATORS", "Your emails"));
about.setOrganizationDomain("kde.org");
about.addComponent(u"libQuotient"_s,
i18n("A Qt library to write cross-platform clients for Matrix"),
i18nc("<version number> (built against <possibly different version number>)",
"%1 (built against %2)",
Quotient::versionString(),
QStringLiteral(Quotient_VERSION_STRING)),
u"https://github.com/quotient-im/libquotient"_s,
KAboutLicense::LGPL_V2_1);
// about.addComponent(u"libQuotient"_s,
// i18n("A Qt library to write cross-platform clients for Matrix"),
// i18nc("<version number> (built against <possibly different version number>)",
// "%1 (built against %2)",
// Quotient::versionString(),
// QStringLiteral(Quotient_VERSION_STRING)),
// u"https://github.com/quotient-im/libquotient"_s,
// KAboutLicense::LGPL_V2_1);
KAboutData::setApplicationData(about);
QGuiApplication::setWindowIcon(QIcon::fromTheme(u"org.kde.neochat"_s));
@@ -174,10 +183,13 @@ int main(int argc, char *argv[])
KCrash::initialize();
#endif
PendingConnection::setConnectionType<NeoChatConnection>();
Connection::setRoomType<NeoChatRoom>();
initLogging();
Connection::setEncryptionDefault(true);
Connection::setDirectChatEncryptionDefault(true);
// Connection::setEncryptionDefault(true);
// Connection::setDirectChatEncryptionDefault(true);
#ifdef NEOCHAT_FLATPAK
// Copy over the included FontConfig configuration to the
@@ -185,7 +197,7 @@ int main(int argc, char *argv[])
QFile::copy(u"/app/etc/fonts/conf.d/99-noto-mono-color-emoji.conf"_s, u"/var/config/fontconfig/conf.d/99-noto-mono-color-emoji.conf"_s);
#endif
ColorSchemer colorScheme;
// ColorSchemer colorScheme;
QCommandLineParser parser;
parser.setApplicationDescription(i18n("Client for the matrix communication protocol"));
@@ -208,7 +220,7 @@ int main(int argc, char *argv[])
about.setupCommandLine(&parser);
parser.process(app);
about.processCommandLine(&parser);
Controller::setTestMode(parser.isSet("test"_L1));
// Controller::setTestMode(parser.isSet("test"_L1));
#ifdef HAVE_KUNIFIEDPUSH
if (parser.isSet(dbusActivatedOption)) {
@@ -220,10 +232,10 @@ int main(int argc, char *argv[])
// Because KRunner may call us on the D-Bus (under the same service name org.kde.neochat) then it may
// accidentally activate us for push notifications instead. If this happens, then immediately quit if the fake
// runner is called.
QDBusConnection::sessionBus().registerObject("/RoomRunner"_L1, new FakeRunner(), QDBusConnection::ExportScriptableContents);
// QDBusConnection::sessionBus().registerObject("/RoomRunner"_L1, new FakeRunner(), QDBusConnection::ExportScriptableContents);
#endif
Controller::listenForNotifications();
// Controller::listenForNotifications();
return QCoreApplication::exec();
}
#endif
@@ -239,51 +251,51 @@ int main(int argc, char *argv[])
Q_IMPORT_QML_PLUGIN(org_kde_neochat_chatbarPlugin)
qml_register_types_org_kde_neochat();
qmlRegisterUncreatableMetaObject(Quotient::staticMetaObject, "Quotient", 1, 0, "JoinRule", u"Access to JoinRule enum only"_s);
// qmlRegisterUncreatableMetaObject(Quotient::staticMetaObject, "Quotient", 1, 0, "JoinRule", u"Access to JoinRule enum only"_s);
QQmlApplicationEngine engine;
#ifdef HAVE_KDBUSADDONS
service.connect(&service,
&KDBusService::activateRequested,
&RoomManager::instance(),
[&engine](const QStringList &arguments, const QString &workingDirectory) {
Q_UNUSED(workingDirectory);
QWindow *window = windowFromEngine(&engine);
KWindowSystem::updateStartupId(window);
WindowController::instance().showAndRaiseWindow(QString());
// Open matrix uri
if (arguments.isEmpty()) {
return;
}
auto args = arguments;
args.removeFirst();
if (args.length() == 2 && args[0] == "--share"_L1) {
ShareHandler::instance().setText(args[1]);
return;
}
for (const auto &arg : args) {
RoomManager::instance().resolveResource(arg);
}
});
#endif
// #ifdef HAVE_KDBUSADDONS
// service.connect(&service,
// &KDBusService::activateRequested,
// &RoomManager::instance(),
// [&engine](const QStringList &arguments, const QString &workingDirectory) {
// Q_UNUSED(workingDirectory);
//
// QWindow *window = windowFromEngine(&engine);
// KWindowSystem::updateStartupId(window);
//
// // WindowController::instance().showAndRaiseWindow(QString());
//
// // Open matrix uri
// if (arguments.isEmpty()) {
// return;
// }
//
// auto args = arguments;
// args.removeFirst();
// if (args.length() == 2 && args[0] == "--share"_L1) {
// // ShareHandler::instance().setText(args[1]);
// return;
// }
//
// for (const auto &arg : args) {
// // RoomManager::instance().resolveResource(arg);
// }
// });
// #endif
engine.rootContext()->setContextObject(new KLocalizedContext(&engine));
engine.setNetworkAccessManagerFactory(new NetworkAccessManagerFactory());
if (parser.isSet("ignore-ssl-errors"_L1)) {
QObject::connect(NetworkAccessManager::instance(), &QNetworkAccessManager::sslErrors, NetworkAccessManager::instance(), [](QNetworkReply *reply) {
reply->ignoreSslErrors();
});
// QObject::connect(NetworkAccessManager::instance(), &QNetworkAccessManager::sslErrors, NetworkAccessManager::instance(), [](QNetworkReply *reply) {
// reply->ignoreSslErrors();
// });
}
if (parser.isSet("share"_L1)) {
ShareHandler::instance().setText(parser.value(shareOption));
// ShareHandler::instance().setText(parser.value(shareOption));
}
engine.addImageProvider(u"blurhash"_s, new BlurhashImageProvider);
@@ -294,12 +306,12 @@ int main(int argc, char *argv[])
}
if (!parser.positionalArguments().isEmpty() && !parser.isSet("share"_L1)) {
RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
// RoomManager::instance().setUrlArgument(parser.positionalArguments()[0]);
}
#ifdef HAVE_RUNNER
auto runner = Runner::create(&engine, &engine);
QDBusConnection::sessionBus().registerObject("/RoomRunner"_L1, runner, QDBusConnection::ExportScriptableContents);
// auto runner = Runner::create(&engine, &engine);
// QDBusConnection::sessionBus().registerObject("/RoomRunner"_L1, runner, QDBusConnection::ExportScriptableContents);
#endif
QWindow *window = windowFromEngine(&engine);

View File

@@ -4,23 +4,23 @@
#include "completionmodel.h"
#include <QDebug>
#include "actionsmodel.h"
#include "completionproxymodel.h"
#include "customemojimodel.h"
#include "emojimodel.h"
// #include "actionsmodel.h"
// #include "completionproxymodel.h"
// #include "customemojimodel.h"
// #include "emojimodel.h"
#include "neochatroom.h"
#include "roommanager.h"
#include "userlistmodel.h"
// #include "roommanager.h"
// #include "userlistmodel.h"
CompletionModel::CompletionModel(QObject *parent)
: QAbstractListModel(parent)
, m_filterModel(new CompletionProxyModel())
, m_userListModel(RoomManager::instance().userListModel())
// , m_filterModel(new CompletionProxyModel())
// , m_userListModel(RoomManager::instance().userListModel())
, m_emojiModel(new QConcatenateTablesProxyModel(this))
{
connect(this, &CompletionModel::textChanged, this, &CompletionModel::updateCompletion);
m_emojiModel->addSourceModel(&CustomEmojiModel::instance());
m_emojiModel->addSourceModel(&EmojiModel::instance());
// m_emojiModel->addSourceModel(&CustomEmojiModel::instance());
// m_emojiModel->addSourceModel(&EmojiModel::instance());
}
QString CompletionModel::text() const
@@ -41,67 +41,68 @@ int CompletionModel::rowCount(const QModelIndex &parent) const
if (m_autoCompletionType == None) {
return 0;
}
return m_filterModel->rowCount();
// return m_filterModel->rowCount();
return {};
}
QVariant CompletionModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_filterModel->rowCount()) {
return {};
}
auto filterIndex = m_filterModel->index(index.row(), 0);
if (m_autoCompletionType == User) {
if (role == DisplayNameRole) {
return m_filterModel->data(filterIndex, UserListModel::DisplayNameRole);
}
if (role == SubtitleRole) {
return m_filterModel->data(filterIndex, UserListModel::UserIdRole);
}
if (role == IconNameRole) {
return m_filterModel->data(filterIndex, UserListModel::AvatarRole);
}
}
if (m_autoCompletionType == Command) {
if (role == DisplayNameRole) {
return u"%1 %2"_s.arg(m_filterModel->data(filterIndex, ActionsModel::Prefix).toString(),
m_filterModel->data(filterIndex, ActionsModel::Parameters).toString());
}
if (role == SubtitleRole) {
return m_filterModel->data(filterIndex, ActionsModel::Description);
}
if (role == IconNameRole) {
return u"invalid"_s;
}
if (role == ReplacedTextRole) {
return m_filterModel->data(filterIndex, ActionsModel::Prefix);
}
}
if (m_autoCompletionType == Room) {
if (role == DisplayNameRole) {
return m_filterModel->data(filterIndex, RoomListModel::DisplayNameRole);
}
if (role == SubtitleRole) {
return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
}
if (role == IconNameRole) {
return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
}
}
if (m_autoCompletionType == Emoji) {
if (role == DisplayNameRole) {
return m_filterModel->data(filterIndex, CustomEmojiModel::DisplayRole);
}
if (role == IconNameRole) {
return m_filterModel->data(filterIndex, CustomEmojiModel::MxcUrl);
}
if (role == ReplacedTextRole) {
return m_filterModel->data(filterIndex, CustomEmojiModel::ReplacedTextRole);
}
if (role == SubtitleRole) {
return m_filterModel->data(filterIndex, EmojiModel::DescriptionRole);
}
}
// if (index.row() < 0 || index.row() >= m_filterModel->rowCount()) {
// return {};
// }
// auto filterIndex = m_filterModel->index(index.row(), 0);
// if (m_autoCompletionType == User) {
// if (role == DisplayNameRole) {
// return m_filterModel->data(filterIndex, UserListModel::DisplayNameRole);
// }
// if (role == SubtitleRole) {
// return m_filterModel->data(filterIndex, UserListModel::UserIdRole);
// }
// if (role == IconNameRole) {
// return m_filterModel->data(filterIndex, UserListModel::AvatarRole);
// }
// }
//
// if (m_autoCompletionType == Command) {
// if (role == DisplayNameRole) {
// return u"%1 %2"_s.arg(m_filterModel->data(filterIndex, ActionsModel::Prefix).toString(),
// m_filterModel->data(filterIndex, ActionsModel::Parameters).toString());
// }
// if (role == SubtitleRole) {
// return m_filterModel->data(filterIndex, ActionsModel::Description);
// }
// if (role == IconNameRole) {
// return u"invalid"_s;
// }
// if (role == ReplacedTextRole) {
// return m_filterModel->data(filterIndex, ActionsModel::Prefix);
// }
// }
// if (m_autoCompletionType == Room) {
// if (role == DisplayNameRole) {
// return m_filterModel->data(filterIndex, RoomListModel::DisplayNameRole);
// }
// if (role == SubtitleRole) {
// return m_filterModel->data(filterIndex, RoomListModel::CanonicalAliasRole);
// }
// if (role == IconNameRole) {
// return m_filterModel->data(filterIndex, RoomListModel::AvatarRole).toString();
// }
// }
// if (m_autoCompletionType == Emoji) {
// if (role == DisplayNameRole) {
// return m_filterModel->data(filterIndex, CustomEmojiModel::DisplayRole);
// }
// if (role == IconNameRole) {
// return m_filterModel->data(filterIndex, CustomEmojiModel::MxcUrl);
// }
// if (role == ReplacedTextRole) {
// return m_filterModel->data(filterIndex, CustomEmojiModel::ReplacedTextRole);
// }
// if (role == SubtitleRole) {
// return m_filterModel->data(filterIndex, EmojiModel::DescriptionRole);
// }
// }
return {};
}
@@ -118,50 +119,50 @@ QHash<int, QByteArray> CompletionModel::roleNames() const
void CompletionModel::updateCompletion()
{
if (text().startsWith(QLatin1Char('@'))) {
m_filterModel->setSourceModel(m_userListModel);
m_filterModel->setFilterRole(UserListModel::UserIdRole);
m_filterModel->setSecondaryFilterRole(UserListModel::DisplayNameRole);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text);
m_autoCompletionType = User;
m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char('/'))) {
m_filterModel->setSourceModel(&ActionsModel::instance());
m_filterModel->setFilterRole(ActionsModel::Prefix);
m_filterModel->setSecondaryFilterRole(-1);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text.mid(1));
m_autoCompletionType = Command;
m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char('#'))) {
m_autoCompletionType = Room;
m_filterModel->setSourceModel(m_roomListModel);
m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text);
m_filterModel->invalidate();
} else if (text().startsWith(QLatin1Char(':')) && text().size() > 1 && !text()[1].isUpper()
&& (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
|| (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
m_filterModel->setSourceModel(m_emojiModel);
m_autoCompletionType = Emoji;
m_filterModel->setFilterRole(CustomEmojiModel::Name);
m_filterModel->setSecondaryFilterRole(EmojiModel::DescriptionRole);
m_filterModel->setFullText(m_fullText);
m_filterModel->setFilterText(m_text);
m_filterModel->invalidate();
} else {
m_autoCompletionType = None;
}
// if (text().startsWith(QLatin1Char('@'))) {
// m_filterModel->setSourceModel(m_userListModel);
// m_filterModel->setFilterRole(UserListModel::UserIdRole);
// m_filterModel->setSecondaryFilterRole(UserListModel::DisplayNameRole);
// m_filterModel->setFullText(m_fullText);
// m_filterModel->setFilterText(m_text);
// m_autoCompletionType = User;
// m_filterModel->invalidate();
// } else if (text().startsWith(QLatin1Char('/'))) {
// m_filterModel->setSourceModel(&ActionsModel::instance());
// m_filterModel->setFilterRole(ActionsModel::Prefix);
// m_filterModel->setSecondaryFilterRole(-1);
// m_filterModel->setFullText(m_fullText);
// m_filterModel->setFilterText(m_text.mid(1));
// m_autoCompletionType = Command;
// m_filterModel->invalidate();
// } else if (text().startsWith(QLatin1Char('#'))) {
// m_autoCompletionType = Room;
// m_filterModel->setSourceModel(m_roomListModel);
// m_filterModel->setFilterRole(RoomListModel::CanonicalAliasRole);
// m_filterModel->setSecondaryFilterRole(RoomListModel::DisplayNameRole);
// m_filterModel->setFullText(m_fullText);
// m_filterModel->setFilterText(m_text);
// m_filterModel->invalidate();
// } else if (text().startsWith(QLatin1Char(':')) && text().size() > 1 && !text()[1].isUpper()
// && (m_fullText.indexOf(QLatin1Char(':'), 1) == -1
// || (m_fullText.indexOf(QLatin1Char(' ')) != -1 && m_fullText.indexOf(QLatin1Char(':'), 1) > m_fullText.indexOf(QLatin1Char(' '), 1)))) {
// m_filterModel->setSourceModel(m_emojiModel);
// m_autoCompletionType = Emoji;
// m_filterModel->setFilterRole(CustomEmojiModel::Name);
// m_filterModel->setSecondaryFilterRole(EmojiModel::DescriptionRole);
// m_filterModel->setFullText(m_fullText);
// m_filterModel->setFilterText(m_text);
// m_filterModel->invalidate();
// } else {
// m_autoCompletionType = None;
// }
beginResetModel();
endResetModel();
}
NeoChatRoom *CompletionModel::room() const
{
return m_room;
return m_room.get();
}
void CompletionModel::setRoom(NeoChatRoom *room)

View File

@@ -7,7 +7,7 @@
#include <QQmlEngine>
#include <QSortFilterProxyModel>
#include "roomlistmodel.h"
// #include "roomlistmodel.h"
class CompletionProxyModel;
class UserListModel;
@@ -47,7 +47,7 @@ class CompletionModel : public QAbstractListModel
/**
* @brief The RoomListModel to be used for room completions.
*/
Q_PROPERTY(RoomListModel *roomListModel READ roomListModel WRITE setRoomListModel NOTIFY roomListModelChanged)
// Q_PROPERTY(RoomListModel *roomListModel READ roomListModel WRITE setRoomListModel NOTIFY roomListModelChanged)
public:
/**

View File

@@ -5,10 +5,10 @@
#include <QDebug>
#include <Quotient/converters.h>
#include <Quotient/csapi/definitions/push_ruleset.h>
#include <Quotient/csapi/pushrules.h>
#include <Quotient/jobs/basejob.h>
// #include <Quotient/converters.h>
// #include <Quotient/csapi/definitions/push_ruleset.h>
// #include <Quotient/csapi/pushrules.h>
// #include <Quotient/jobs/basejob.h>
#include "neochatconfig.h"
@@ -74,18 +74,18 @@ void PushRuleModel::updateNotificationRules(const QString &type)
return;
}
const QJsonObject ruleDataJson = m_connection->accountDataJson(u"m.push_rules"_s);
const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"_L1].toObject());
// const QJsonObject ruleDataJson = m_connection->accountDataJson(u"m.push_rules"_s);
// const Quotient::PushRuleset ruleData = Quotient::fromJson<Quotient::PushRuleset>(ruleDataJson["global"_L1].toObject());
beginResetModel();
m_rules.clear();
// Doing this 5 times because PushRuleset is a struct.
setRules(ruleData.override, PushRuleKind::Override);
setRules(ruleData.content, PushRuleKind::Content);
setRules(ruleData.room, PushRuleKind::Room);
setRules(ruleData.sender, PushRuleKind::Sender);
setRules(ruleData.underride, PushRuleKind::Underride);
// setRules(ruleData.override, PushRuleKind::Override);
// setRules(ruleData.content, PushRuleKind::Content);
// setRules(ruleData.room, PushRuleKind::Room);
// setRules(ruleData.sender, PushRuleKind::Sender);
// setRules(ruleData.underride, PushRuleKind::Underride);
Q_EMIT globalNotificationsEnabledChanged();
Q_EMIT globalNotificationsSetChanged();
@@ -93,28 +93,28 @@ void PushRuleModel::updateNotificationRules(const QString &type)
endResetModel();
}
void PushRuleModel::setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind)
{
for (const auto &rule : rules) {
QString roomId;
if (rule.conditions.size() > 0) {
for (const auto &condition : std::as_const(rule.conditions)) {
if (condition.key == u"room_id"_s) {
roomId = condition.pattern;
}
}
}
m_rules.append(Rule{
rule.ruleId,
kind,
variantToAction(rule.actions, rule.enabled),
getSection(rule),
rule.enabled,
roomId,
});
}
}
// void PushRuleModel::setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind)
// {
// for (const auto &rule : rules) {
// QString roomId;
// if (rule.conditions.size() > 0) {
// for (const auto &condition : std::as_const(rule.conditions)) {
// if (condition.key == u"room_id"_s) {
// roomId = condition.pattern;
// }
// }
// }
//
// m_rules.append(Rule{
// rule.ruleId,
// kind,
// variantToAction(rule.actions, rule.enabled),
// getSection(rule),
// rule.enabled,
// roomId,
// });
// }
// }
int PushRuleModel::getRuleIndex(const QString &ruleId) const
{
@@ -126,51 +126,51 @@ int PushRuleModel::getRuleIndex(const QString &ruleId) const
return -1;
}
PushRuleSection::Section PushRuleModel::getSection(Quotient::PushRule rule)
{
auto ruleId = rule.ruleId;
if (defaultSections.contains(ruleId)) {
return defaultSections.value(ruleId);
} else {
if (rule.ruleId.startsWith(u'.')) {
return PushRuleSection::Unknown;
}
/**
* If the rule name resolves to a matrix id for a room that the user is part
* of it shouldn't appear in the global list as it's overriding the global
* state for that room.
*
* Rooms that the user hasn't joined shouldn't have a rule.
*/
if (m_connection->room(ruleId) != nullptr) {
return PushRuleSection::Undefined;
}
/**
* If the rule name resolves to a matrix id for a user it shouldn't appear
* in the global list as it's a rule to block notifications from a user and
* is handled elsewhere.
*/
auto testUserId = ruleId;
// Rules for user matrix IDs often don't have the @ on the beginning so add
// if not there to avoid malformed ID.
if (!testUserId.startsWith(u'@')) {
testUserId.prepend(u'@');
}
if (testUserId.startsWith(u'@') && !Quotient::serverPart(testUserId).isEmpty() && m_connection->user(testUserId) != nullptr) {
return PushRuleSection::Undefined;
}
// If the rule has push conditions and one is a room ID it is a room only keyword.
if (!rule.conditions.isEmpty()) {
for (const auto &condition : std::as_const(rule.conditions)) {
if (condition.key == u"room_id"_s) {
return PushRuleSection::RoomKeywords;
}
}
}
return PushRuleSection::Keywords;
}
}
// PushRuleSection::Section PushRuleModel::getSection(Quotient::PushRule rule)
// {
// auto ruleId = rule.ruleId;
//
// if (defaultSections.contains(ruleId)) {
// return defaultSections.value(ruleId);
// } else {
// if (rule.ruleId.startsWith(u'.')) {
// return PushRuleSection::Unknown;
// }
// /**
// * If the rule name resolves to a matrix id for a room that the user is part
// * of it shouldn't appear in the global list as it's overriding the global
// * state for that room.
// *
// * Rooms that the user hasn't joined shouldn't have a rule.
// */
// if (m_connection->room(ruleId) != nullptr) {
// return PushRuleSection::Undefined;
// }
// /**
// * If the rule name resolves to a matrix id for a user it shouldn't appear
// * in the global list as it's a rule to block notifications from a user and
// * is handled elsewhere.
// */
// auto testUserId = ruleId;
// // Rules for user matrix IDs often don't have the @ on the beginning so add
// // if not there to avoid malformed ID.
// if (!testUserId.startsWith(u'@')) {
// testUserId.prepend(u'@');
// }
// if (testUserId.startsWith(u'@') && !Quotient::serverPart(testUserId).isEmpty() && m_connection->user(testUserId) != nullptr) {
// return PushRuleSection::Undefined;
// }
// // If the rule has push conditions and one is a room ID it is a room only keyword.
// if (!rule.conditions.isEmpty()) {
// for (const auto &condition : std::as_const(rule.conditions)) {
// if (condition.key == u"room_id"_s) {
// return PushRuleSection::RoomKeywords;
// }
// }
// }
// return PushRuleSection::Keywords;
// }
// }
PushRuleAction::Action PushRuleModel::defaultState() const
{
@@ -294,33 +294,33 @@ void PushRuleModel::addKeyword(const QString &keyword, const QString &roomId)
{
PushRuleKind::Kind kind = PushRuleKind::Content;
const QList<QVariant> actions = actionToVariant(m_defaultKeywordAction);
QList<Quotient::PushCondition> pushConditions;
if (!roomId.isEmpty()) {
kind = PushRuleKind::Override;
Quotient::PushCondition roomCondition;
roomCondition.kind = u"event_match"_s;
roomCondition.key = u"room_id"_s;
roomCondition.pattern = roomId;
pushConditions.append(roomCondition);
Quotient::PushCondition keywordCondition;
keywordCondition.kind = u"event_match"_s;
keywordCondition.key = u"content.body"_s;
keywordCondition.pattern = keyword;
pushConditions.append(keywordCondition);
}
auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
keyword,
actions,
QString(),
QString(),
pushConditions,
roomId.isEmpty() ? keyword : QString());
connect(job, &Quotient::BaseJob::failure, this, [job, keyword]() {
qWarning() << "Unable to set push rule for keyword %1: "_L1.arg(keyword) << job->errorString();
});
// QList<Quotient::PushCondition> pushConditions;
// if (!roomId.isEmpty()) {
// kind = PushRuleKind::Override;
//
// Quotient::PushCondition roomCondition;
// roomCondition.kind = u"event_match"_s;
// roomCondition.key = u"room_id"_s;
// roomCondition.pattern = roomId;
// pushConditions.append(roomCondition);
//
// Quotient::PushCondition keywordCondition;
// keywordCondition.kind = u"event_match"_s;
// keywordCondition.key = u"content.body"_s;
// keywordCondition.pattern = keyword;
// pushConditions.append(keywordCondition);
// }
//
// auto job = m_connection->callApi<Quotient::SetPushRuleJob>(PushRuleKind::kindString(kind),
// keyword,
// actions,
// QString(),
// QString(),
// pushConditions,
// roomId.isEmpty() ? keyword : QString());
// connect(job, &Quotient::BaseJob::failure, this, [job, keyword]() {
// qWarning() << "Unable to set push rule for keyword %1: "_L1.arg(keyword) << job->errorString();
// });
}
/**
@@ -336,20 +336,20 @@ void PushRuleModel::removeKeyword(const QString &keyword)
}
auto kind = PushRuleKind::kindString(m_rules[index].kind);
auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
qWarning() << "Unable to remove push rule for keyword %1: "_L1.arg(m_rules[index].id) << job->errorString();
});
// auto job = m_connection->callApi<Quotient::DeletePushRuleJob>(kind, m_rules[index].id);
// connect(job, &Quotient::BaseJob::failure, this, [this, job, index]() {
// qWarning() << "Unable to remove push rule for keyword %1: "_L1.arg(m_rules[index].id) << job->errorString();
// });
}
void PushRuleModel::setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled)
{
auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
if (job->enabled() != enabled) {
m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
}
});
// auto job = m_connection->callApi<Quotient::IsPushRuleEnabledJob>(kind, ruleId);
// connect(job, &Quotient::BaseJob::success, this, [job, kind, ruleId, enabled, this]() {
// if (job->enabled() != enabled) {
// m_connection->callApi<Quotient::SetPushRuleEnabledJob>(kind, ruleId, enabled);
// }
// });
}
void PushRuleModel::setNotificationRuleActions(const QString &kind, const QString &ruleId, PushRuleAction::Action action)
@@ -361,7 +361,7 @@ void PushRuleModel::setNotificationRuleActions(const QString &kind, const QStrin
actions = actionToVariant(action);
}
m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
// m_connection->callApi<Quotient::SetPushRuleActionsJob>(kind, ruleId, actions);
}
PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &actions, bool enabled)
@@ -378,14 +378,14 @@ PushRuleAction::Action PushRuleModel::variantToAction(const QList<QVariant> &act
continue;
}
QJsonObject action = i.toJsonObject();
if (action["set_tweak"_L1].toString() == u"sound"_s) {
isNoisy = true;
} else if (action["set_tweak"_L1].toString() == u"highlight"_s) {
if (action["value"_L1].toString() != u"false"_s) {
highlightEnabled = true;
}
}
// QJsonObject action = i.toJsonObject();
// if (action["set_tweak"_L1].toString() == u"sound"_s) {
// isNoisy = true;
// } else if (action["set_tweak"_L1].toString() == u"highlight"_s) {
// if (action["value"_L1].toString() != u"false"_s) {
// highlightEnabled = true;
// }
// }
}
if (!enabled) {
@@ -424,15 +424,15 @@ QList<QVariant> PushRuleModel::actionToVariant(PushRuleAction::Action action, co
actions.append(u"dont_notify"_s);
}
if (action == PushRuleAction::Noisy || action == PushRuleAction::NoisyHighlight) {
QJsonObject soundTweak;
soundTweak.insert("set_tweak"_L1, u"sound"_s);
soundTweak.insert("value"_L1, sound);
actions.append(soundTweak);
// QJsonObject soundTweak;
// soundTweak.insert("set_tweak"_L1, u"sound"_s);
// soundTweak.insert("value"_L1, sound);
// actions.append(soundTweak);
}
if (action == PushRuleAction::Highlight || action == PushRuleAction::NoisyHighlight) {
QJsonObject highlightTweak;
highlightTweak.insert("set_tweak"_L1, u"highlight"_s);
actions.append(highlightTweak);
// QJsonObject highlightTweak;
// highlightTweak.insert("set_tweak"_L1, u"highlight"_s);
// actions.append(highlightTweak);
}
return actions;
@@ -452,7 +452,7 @@ void PushRuleModel::setConnection(NeoChatConnection *connection)
Q_EMIT connectionChanged();
if (m_connection) {
connect(m_connection, &NeoChatConnection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
// connect(m_connection, &NeoChatConnection::accountDataChanged, this, &PushRuleModel::updateNotificationRules);
updateNotificationRules(u"m.push_rules"_s);
}
}

View File

@@ -6,7 +6,7 @@
#include <QAbstractListModel>
#include <QQmlEngine>
#include <Quotient/csapi/definitions/push_rule.h>
// #include <Quotient/csapi/definitions/push_rule.h>
#include "enums/pushrule.h"
#include "neochatconnection.h"
@@ -130,10 +130,10 @@ private:
QList<Rule> m_rules;
QPointer<NeoChatConnection> m_connection;
void setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind);
// void setRules(QList<Quotient::PushRule> rules, PushRuleKind::Kind kind);
int getRuleIndex(const QString &ruleId) const;
PushRuleSection::Section getSection(Quotient::PushRule rule);
// PushRuleSection::Section getSection(Quotient::PushRule rule);
void setNotificationRuleEnabled(const QString &kind, const QString &ruleId, bool enabled);
void setNotificationRuleActions(const QString &kind, const QString &ruleId, PushRuleAction::Action action);

View File

@@ -12,11 +12,11 @@ RoomTreeItem::RoomTreeItem(TreeData data, RoomTreeItem *parent)
bool RoomTreeItem::operator==(const RoomTreeItem &other) const
{
if (std::holds_alternative<NeoChatRoomType::Types>(m_data) && std::holds_alternative<NeoChatRoomType::Types>(other.data())) {
return std::get<NeoChatRoomType::Types>(m_data) == std::get<NeoChatRoomType::Types>(m_data);
if (std::holds_alternative<NeoChatRoomType::Type>(m_data) && std::holds_alternative<NeoChatRoomType::Type>(other.data())) {
return std::get<NeoChatRoomType::Type>(m_data) == std::get<NeoChatRoomType::Type>(m_data);
}
if (std::holds_alternative<NeoChatRoom *>(m_data) && std::holds_alternative<NeoChatRoom *>(other.data())) {
return std::get<NeoChatRoom *>(m_data)->id() == std::get<NeoChatRoom *>(m_data)->id();
if (std::holds_alternative<RoomWrapper *>(m_data) && std::holds_alternative<RoomWrapper *>(other.data())) {
return (*std::get<RoomWrapper *>(m_data)->item)->id() == (*std::get<RoomWrapper *>(other.data())->item)->id();
}
return false;
}
@@ -84,13 +84,13 @@ RoomTreeItem::TreeData RoomTreeItem::data() const
return m_data;
}
std::optional<int> RoomTreeItem::rowForRoom(Quotient::Room *room) const
std::optional<int> RoomTreeItem::rowForRoom(rust::Box<sdk::RoomListRoom> room) const
{
Q_ASSERT_X(std::holds_alternative<NeoChatRoomType::Types>(m_data), __FUNCTION__, "rowForRoom only works items for rooms not categories");
Q_ASSERT_X(std::holds_alternative<NeoChatRoomType::Type>(m_data), __FUNCTION__, "rowForRoom only works items for rooms not categories");
int i = 0;
for (const auto &child : m_children) {
if (std::get<NeoChatRoom *>(child->data()) == room) {
if ((*std::get<RoomWrapper *>(child->data())->item)->id() == room->id()) {
return i;
}
i++;

View File

@@ -1,27 +1,28 @@
// SPDX-FileCopyrightText: 2024 Carl Schwan <carl@carlschwan.eu>
// SPDX-FileCopyrightText: 2024 James Graham <james.h.graham@protonmail.com>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
#include "enums/neochatroomtype.h"
#include "neochatroomtype.h"
class NeoChatRoom;
namespace sdk
{
struct RoomListRoom;
}
struct RoomWrapper {
std::optional<rust::Box<sdk::RoomListRoom>> item;
};
/**
* @class RoomTreeItem
*
* This class defines an item in the space tree hierarchy model.
*
* @note This is separate from Quotient::Room and NeoChatRoom because we don't have
* full room information for any room/space the user hasn't joined and we
* don't want to create one for ever possible child in a space as that would
* be expensive.
*
* @sa Quotient::Room, NeoChatRoom
* This class defines an item in a room tree.
*/
class RoomTreeItem
{
public:
using TreeData = std::variant<NeoChatRoom *, NeoChatRoomType::Types>;
using TreeData = std::variant<RoomWrapper *, NeoChatRoomType::Type>;
explicit RoomTreeItem(TreeData data, RoomTreeItem *parent = nullptr);
@@ -68,7 +69,7 @@ public:
*/
TreeData data() const;
std::optional<int> rowForRoom(Quotient::Room *room) const;
std::optional<int> rowForRoom(rust::Box<sdk::RoomListRoom> room) const;
private:
std::vector<std::unique_ptr<RoomTreeItem>> m_children;

View File

@@ -1,23 +1,45 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
#include "roomtreemodel.h"
#include <Quotient/room.h>
#include "eventhandler.h"
#include "neochatconnection.h"
// #include "eventhandler.h"
#include "neochatroomtype.h"
#include "spacehierarchycache.h"
#include "rust/cxx.h"
#include <Integral/lib.rs.h>
// #include "spacehierarchycache.h"
#include <Integral/RoomStream>
#include <Integral/Utils>
using namespace Quotient;
using namespace Integral;
class RoomTreeModel::Private
{
public:
QPointer<Integral::Connection> connection;
std::unique_ptr<RoomStream> roomStream = nullptr;
std::unique_ptr<RoomTreeItem> rootItem;
// Since the rooms are streamed as vector diffs we need to keep track of them
// for things like the index value of insert to make sense.
QList<QPersistentModelIndex> roomIndexes;
void roomsUpdate();
void resetTree();
RoomTreeModel *q = nullptr;
};
RoomTreeModel::RoomTreeModel(QObject *parent)
: QAbstractItemModel(parent)
, m_rootItem(new RoomTreeItem(nullptr))
, d(std::make_unique<Private>())
{
d->q = this;
}
RoomTreeModel::~RoomTreeModel() = default;
RoomTreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
{
if (index.isValid()) {
@@ -26,179 +48,226 @@ RoomTreeItem *RoomTreeModel::getItem(const QModelIndex &index) const
return item;
}
}
return m_rootItem.get();
return d->rootItem.get();
}
void RoomTreeModel::resetModel()
{
if (m_connection == nullptr) {
if (d->connection == nullptr) {
beginResetModel();
m_rootItem.reset();
d->rootItem.reset();
d->roomStream.reset();
endResetModel();
return;
}
beginResetModel();
m_rootItem.reset(new RoomTreeItem(nullptr));
d->resetTree();
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
m_rootItem->insertChild(std::make_unique<RoomTreeItem>(NeoChatRoomType::Types(i), m_rootItem.get()));
}
d->roomStream = d->connection->roomStream();
connect(d->roomStream.get(), &RoomStream::roomsUpdate, this, [this]() {
d->roomsUpdate();
});
for (const auto &r : m_connection->allRooms()) {
const auto room = dynamic_cast<NeoChatRoom *>(r);
const auto type = NeoChatRoomType::typeForRoom(room);
const auto categoryItem = m_rootItem->child(type);
if (categoryItem->insertChild(std::make_unique<RoomTreeItem>(room, categoryItem))) {
connectRoomSignals(room);
}
}
d->roomStream->startStream();
endResetModel();
}
void RoomTreeModel::setConnection(NeoChatConnection *connection)
void RoomTreeModel::Private::resetTree()
{
if (m_connection == connection) {
rootItem.reset(new RoomTreeItem(nullptr));
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
rootItem->insertChild(std::make_unique<RoomTreeItem>(NeoChatRoomType::Type(i), rootItem.get()));
}
}
void RoomTreeModel::setConnection(Connection *connection)
{
if (d->connection == connection) {
return;
}
if (m_connection) {
disconnect(m_connection.get(), nullptr, this, nullptr);
if (d->connection) {
d->connection->disconnect(this);
}
m_connection = connection;
d->connection = connection;
resetModel();
connect(connection, &Connection::newRoom, this, &RoomTreeModel::newRoom);
connect(connection, &Connection::leftRoom, this, &RoomTreeModel::leftRoom);
connect(connection, &Connection::aboutToDeleteRoom, this, &RoomTreeModel::leftRoom);
Q_EMIT connectionChanged();
}
void RoomTreeModel::newRoom(Room *r)
void RoomTreeModel::Private::roomsUpdate()
{
const auto room = dynamic_cast<NeoChatRoom *>(r);
const auto type = NeoChatRoomType::typeForRoom(room);
// Check if the room is already in the model.
const auto checkRoomIndex = indexForRoom(room);
if (checkRoomIndex.isValid()) {
// If the room is in the wrong type category for whatever reason, move it.
if (checkRoomIndex.parent().row() != type) {
moveRoom(room);
const auto diff = roomStream->next();
switch (diff->op()) {
case 0: { // Append
for (const auto &it : diff->items_vec()) {
const auto type = NeoChatRoomType::typeForRoom(it.box_me());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{it.box_me()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.append(q->indexForRoom(it.box_me()));
}
return;
break;
}
const auto parentItem = m_rootItem->child(type);
beginInsertRows(index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
parentItem->insertChild(std::make_unique<RoomTreeItem>(room, parentItem));
connectRoomSignals(room);
endInsertRows();
}
void RoomTreeModel::leftRoom(Room *r)
{
const auto room = dynamic_cast<NeoChatRoom *>(r);
auto index = indexForRoom(room);
if (!index.isValid()) {
return;
case 1: { // Clear
q->beginResetModel();
resetTree();
roomIndexes.clear();
q->endResetModel();
break;
}
const auto parentItem = getItem(index.parent());
Q_ASSERT(parentItem);
beginRemoveRows(index.parent(), index.row(), index.row());
parentItem->removeChild(index.row());
room->disconnect(this);
endRemoveRows();
}
void RoomTreeModel::moveRoom(Quotient::Room *room)
{
// We can't assume the type as it has changed so currently the return of
// NeoChatRoomType::typeForRoom doesn't match it's current location. So find the room.
NeoChatRoomType::Types oldType;
int oldRow = -1;
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
const auto categoryItem = m_rootItem->child(i);
const auto row = categoryItem->rowForRoom(room);
if (row) {
oldType = static_cast<NeoChatRoomType::Types>(i);
oldRow = *row;
case 2: { // Push Front
const auto type = NeoChatRoomType::typeForRoom(diff->item());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), 0, 0);
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.prepend(q->indexForRoom(diff->item()));
break;
}
if (oldRow == -1) {
return;
case 3: { // Push Back
const auto type = NeoChatRoomType::typeForRoom(diff->item());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.append(q->indexForRoom(diff->item()));
break;
}
auto neochatRoom = dynamic_cast<NeoChatRoom *>(room);
const auto newType = NeoChatRoomType::typeForRoom(neochatRoom);
if (newType == oldType) {
return;
case 4: { // Pop Front
const auto index = roomIndexes.front();
q->beginRemoveRows(index.parent(), index.row(), index.row());
const auto parentItem = q->getItem(index.parent());
parentItem->removeChild(index.row());
roomIndexes.removeFirst();
q->endRemoveRows();
break;
}
case 5: { // Pop Back
const auto index = roomIndexes.back();
q->beginRemoveRows(index.parent(), index.row(), index.row());
const auto parentItem = q->getItem(index.parent());
parentItem->removeChild(index.row());
roomIndexes.removeLast();
q->endRemoveRows();
break;
}
case 6: { // Insert
const auto type = NeoChatRoomType::typeForRoom(diff->item());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.insert(diff->index(), q->indexForRoom(diff->item()));
break;
}
case 7: { // Set
const auto index = roomIndexes.at(diff->index());
q->beginRemoveRows(index.parent(), index.row(), index.row());
q->getItem(index.parent())->removeChild(index.row());
q->endRemoveRows();
const auto oldParent = index(oldType, 0, {});
auto oldParentItem = getItem(oldParent);
Q_ASSERT(oldParentItem);
const auto type = NeoChatRoomType::typeForRoom(diff->item());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{diff->item()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes[diff->index()] = q->indexForRoom(diff->item());
break;
}
case 8: { // Remove
const auto index = roomIndexes.at(diff->index());
q->beginRemoveRows(index.parent(), index.row(), index.row());
q->getItem(index.parent())->removeChild(index.row());
q->endRemoveRows();
roomIndexes.removeAt(diff->index());
break;
}
case 9: { // Truncate
for (int i = q->rowCount({}) - 1; i >= int(diff->index()); i--) {
const auto index = roomIndexes.at(i);
q->beginRemoveRows(index.parent(), index.row(), index.row());
q->getItem(index.parent())->removeChild(index.row());
q->endRemoveRows();
roomIndexes.removeAt(i);
}
break;
}
case 10: { // Reset
q->beginResetModel();
resetTree();
roomIndexes.clear();
q->endResetModel();
const auto newParent = index(newType, 0, {});
auto newParentItem = getItem(newParent);
Q_ASSERT(newParentItem);
// HACK: We're doing this as a remove then insert because moving doesn't work
// properly with DelegateChooser for whatever reason.
Q_ASSERT(checkIndex(index(oldRow, 0, oldParent), QAbstractItemModel::CheckIndexOption::IndexIsValid));
beginRemoveRows(oldParent, oldRow, oldRow);
const bool success = oldParentItem->removeChild(oldRow);
Q_ASSERT(success);
endRemoveRows();
beginInsertRows(newParent, newParentItem->childCount(), newParentItem->childCount());
newParentItem->insertChild(std::make_unique<RoomTreeItem>(neochatRoom, newParentItem));
endInsertRows();
for (const auto &it : diff->items_vec()) {
const auto type = NeoChatRoomType::typeForRoom(it.box_me());
const auto parentItem = rootItem->child(type);
q->beginInsertRows(q->index(parentItem->row(), 0), parentItem->childCount(), parentItem->childCount());
if (parentItem->insertChild(std::make_unique<RoomTreeItem>(new RoomWrapper{it.box_me()}, parentItem))) {
// connectRoomSignals(room);
}
q->endInsertRows();
roomIndexes.append(q->indexForRoom(it.box_me()));
}
break;
}
}
}
void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
{
connect(room, &Room::displaynameChanged, this, [this, room] {
refreshRoomRoles(room, {DisplayNameRole});
});
connect(room, &Room::unreadStatsChanged, this, [this, room] {
refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
});
connect(room, &Room::avatarChanged, this, [this, room] {
refreshRoomRoles(room, {AvatarRole});
});
connect(room, &Room::tagsChanged, this, [this, room] {
moveRoom(room);
});
connect(room, &Room::joinStateChanged, this, [this, room] {
refreshRoomRoles(room);
});
connect(room, &Room::addedMessages, this, [this, room] {
refreshRoomRoles(room, {SubtitleTextRole});
});
connect(room, &Room::pendingEventMerged, this, [this, room] {
refreshRoomRoles(room, {SubtitleTextRole});
});
connect(room, &NeoChatRoom::pushNotificationStateChanged, this, [this, room] {
refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
});
}
// void RoomTreeModel::connectRoomSignals(NeoChatRoom *room)
// {
// connect(room, &Room::displaynameChanged, this, [this, room] {
// refreshRoomRoles(room, {DisplayNameRole});
// });
// connect(room, &Room::unreadStatsChanged, this, [this, room] {
// refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
// });
// connect(room, &Room::avatarChanged, this, [this, room] {
// refreshRoomRoles(room, {AvatarRole});
// });
// connect(room, &Room::tagsChanged, this, [this, room] {
// moveRoom(room);
// });
// connect(room, &Room::joinStateChanged, this, [this, room] {
// refreshRoomRoles(room);
// });
// connect(room, &Room::addedMessages, this, [this, room] {
// refreshRoomRoles(room, {SubtitleTextRole});
// });
// connect(room, &Room::pendingEventMerged, this, [this, room] {
// refreshRoomRoles(room, {SubtitleTextRole});
// });
// connect(room, &NeoChatRoom::pushNotificationStateChanged, this, [this, room] {
// refreshRoomRoles(room, {ContextNotificationCountRole, HasHighlightNotificationsRole});
// });
// }
void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
{
const auto index = indexForRoom(room);
if (!index.isValid()) {
qCritical() << "Room" << room->id() << "not found in the room list";
return;
}
Q_EMIT dataChanged(index, index, roles);
}
// void RoomTreeModel::refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles)
// {
// const auto index = indexForRoom(room);
// if (!index.isValid()) {
// qCritical() << "Room" << room->id() << "not found in the room list";
// return;
// }
// Q_EMIT dataChanged(index, index, roles);
// }
NeoChatConnection *RoomTreeModel::connection() const
Connection *RoomTreeModel::connection() const
{
return m_connection;
return d->connection;
}
int RoomTreeModel::columnCount(const QModelIndex &parent) const
@@ -215,7 +284,7 @@ int RoomTreeModel::rowCount(const QModelIndex &parent) const
}
if (!parent.isValid()) {
parentItem = m_rootItem.get();
parentItem = d->rootItem.get();
} else {
parentItem = static_cast<RoomTreeItem *>(parent.internalPointer());
}
@@ -239,7 +308,7 @@ QModelIndex RoomTreeModel::parent(const QModelIndex &index) const
}
RoomTreeItem *parentItem = childItem->parentItem();
if (parentItem == m_rootItem.get()) {
if (parentItem == d->rootItem.get()) {
return QModelIndex();
}
@@ -295,7 +364,7 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
}
RoomTreeItem *child = getItem(index);
if (std::holds_alternative<NeoChatRoomType::Types>(child->data())) {
if (std::holds_alternative<NeoChatRoomType::Type>(child->data())) {
if (role == DisplayNameRole) {
return NeoChatRoomType::typeName(index.row());
}
@@ -314,98 +383,91 @@ QVariant RoomTreeModel::data(const QModelIndex &index, int role) const
return {};
}
const auto room = std::get<NeoChatRoom *>(child->data());
const auto room = std::get<RoomWrapper *>(child->data());
Q_ASSERT(room);
if (role == DisplayNameRole) {
return room->displayName();
return stringFromRust((*room->item)->display_name()).toHtmlEscaped();
}
if (role == AvatarRole) {
return room->avatarMediaUrl();
return u"%1?user_id=%2"_s.arg(stringFromRust((*room->item)->avatar_url()), d->connection->matrixId());
}
if (role == CanonicalAliasRole) {
return room->canonicalAlias();
return stringFromRust((*room->item)->canonical_alias()).toHtmlEscaped();
}
if (role == TopicRole) {
return room->topic();
return stringFromRust((*room->item)->topic()).toHtmlEscaped();
}
if (role == CategoryRole) {
return NeoChatRoomType::typeForRoom(room);
return NeoChatRoomType::typeForRoom((*room->item)->box_me());
}
if (role == ContextNotificationCountRole) {
return int(room->contextAwareNotificationCount());
return int((*room->item)->num_unread_messages());
}
if (role == HasHighlightNotificationsRole) {
return room->highlightCount() > 0 && room->contextAwareNotificationCount() > 0;
return (*room->item)->num_unread_mentions() > 0 && (*room->item)->num_unread_messages() > 0;
}
if (role == JoinStateRole) {
if (!room->successorId().isEmpty()) {
if (!(*room->item)->tombstone()->replacement_room().empty()) {
return u"upgraded"_s;
}
return QVariant::fromValue(room->joinState());
return QVariant::fromValue((*room->item)->state());
}
if (role == CurrentRoomRole) {
return QVariant::fromValue(room);
return {};
// return QVariant::fromValue(room);
}
if (role == SubtitleTextRole) {
if (room->isInvite()) {
if (room->isDirectChat()) {
return i18nc("@info:label", "Invited you to chat");
}
return i18nc("@info:label", "%1 invited you", room->member(room->invitingUserId()).displayName());
}
if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
return QString();
}
return EventHandler::subtitleText(room, room->lastEvent());
return {};
// if (room->lastEvent() == nullptr || room->lastEventIsSpoiler()) {
// return QString();
// }
// return EventHandler::subtitleText(room, room->lastEvent());
}
if (role == AvatarImageRole) {
return room->avatar(128);
return {};
// return room->avatar(128);
}
if (role == RoomIdRole) {
return room->id();
return stringFromRust((*room->item)->id()).toHtmlEscaped();
}
if (role == IsSpaceRole) {
return room->isSpace();
return (*room->item)->is_space();
}
if (role == IsChildSpaceRole) {
return SpaceHierarchyCache::instance().isChild(room->id());
return false;
// return SpaceHierarchyCache::instance().isChild(room->id());
}
if (role == ReplacementIdRole) {
return room->successorId();
return stringFromRust((*room->item)->tombstone()->replacement_room()).toHtmlEscaped();
}
if (role == IsDirectChat) {
return room->isDirectChat();
return false;
// return room->isDirectChat();
}
if (role == DelegateTypeRole) {
return u"normal"_s;
}
if (role == RoomTypeRole) {
if (room->creation()) {
return room->creation()->contentPart<QString>("type"_L1);
}
return stringFromRust((*room->item)->room_type()).toHtmlEscaped();
}
return {};
}
QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
QModelIndex RoomTreeModel::indexForRoom(rust::Box<sdk::RoomListRoom> room) const
{
if (room == nullptr) {
return {};
}
// Try and find by checking type.
const auto type = NeoChatRoomType::typeForRoom(room);
const auto parentItem = m_rootItem->child(type);
const auto row = parentItem->rowForRoom(room);
const auto type = NeoChatRoomType::typeForRoom(room->box_me());
const auto parentItem = d->rootItem->child(type);
const auto row = parentItem->rowForRoom(room->box_me());
if (row) {
return index(*row, 0, index(type, 0));
}
// Double check that the room isn't in the wrong category.
for (int i = 0; i < NeoChatRoomType::TypesCount; i++) {
const auto parentItem = m_rootItem->child(i);
const auto row = parentItem->rowForRoom(room);
const auto parentItem = d->rootItem->child(i);
const auto row = parentItem->rowForRoom(room->box_me());
if (row) {
return index(*row, 0, index(i, 0));
}
@@ -414,4 +476,13 @@ QModelIndex RoomTreeModel::indexForRoom(NeoChatRoom *room) const
return {};
}
std::optional<rust::Box<sdk::RoomListRoom>> RoomTreeModel::roomForIndex(QModelIndex index) const
{
RoomTreeItem *child = getItem(index);
if (std::holds_alternative<NeoChatRoomType::Type>(child->data())) {
return std::nullopt;
}
return (*std::get<RoomWrapper *>(child->data())->item)->box_me();
}
#include "moc_roomtreemodel.cpp"

View File

@@ -1,3 +1,4 @@
// SPDX-FileCopyrightText: 2023 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: LGPL-2.0-or-later
@@ -6,23 +7,20 @@
#include <QAbstractItemModel>
#include <QPointer>
#include "enums/neochatroomtype.h"
#include "roomtreeitem.h"
namespace Quotient
namespace Integral
{
class Connection;
class Room;
}
class NeoChatConnection;
class NeoChatRoom;
class RoomTreeModel : public QAbstractItemModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(NeoChatConnection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
Q_PROPERTY(Integral::Connection *connection READ connection WRITE setConnection NOTIFY connectionChanged)
public:
/**
@@ -51,9 +49,10 @@ public:
};
Q_ENUM(EventRoles)
explicit RoomTreeModel(QObject *parent = nullptr);
~RoomTreeModel();
void setConnection(NeoChatConnection *connection);
NeoChatConnection *connection() const;
void setConnection(Integral::Connection *connection);
Integral::Connection *connection() const;
/**
* @brief Get the given role value at the given index.
@@ -75,23 +74,21 @@ public:
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
Q_INVOKABLE QModelIndex indexForRoom(NeoChatRoom *room) const;
QModelIndex indexForRoom(rust::Box<sdk::RoomListRoom> room) const;
std::optional<rust::Box<sdk::RoomListRoom>> roomForIndex(QModelIndex index) const;
Q_SIGNALS:
void connectionChanged();
private:
QPointer<NeoChatConnection> m_connection;
std::unique_ptr<RoomTreeItem> m_rootItem;
class Private;
std::unique_ptr<Private> d;
RoomTreeItem *getItem(const QModelIndex &index) const;
void resetModel();
void connectRoomSignals(NeoChatRoom *room);
void newRoom(Quotient::Room *room);
void leftRoom(Quotient::Room *room);
void moveRoom(Quotient::Room *room);
// void connectRoomSignals(NeoChatRoom *room);
void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
// void refreshRoomRoles(NeoChatRoom *room, const QList<int> &roles = {});
};

View File

@@ -4,26 +4,26 @@
#include "sortfilterroomtreemodel.h"
#include "enums/roomsortparameter.h"
#include "neochatconfig.h"
#include "roomsortparameter.h"
// #include "neochatconfig.h"
#include "neochatconnection.h"
#include "neochatroom.h"
#include "neochatroomtype.h"
#include "roommanager.h"
#include <Integral/Room>
// #include "roommanager.h"
#include "roomtreemodel.h"
#include "spacehierarchycache.h"
// #include "spacehierarchycache.h"
SortFilterRoomTreeModel::SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QObject *parent)
SortFilterRoomTreeModel::SortFilterRoomTreeModel(QObject *parent)
: QSortFilterProxyModel(parent)
{
Q_ASSERT(sourceModel);
setSourceModel(sourceModel);
// Q_ASSERT(sourceModel);
// setSourceModel(sourceModel);
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, [this]() {
setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
invalidateFilter();
});
// setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
// connect(NeoChatConfig::self(), &NeoChatConfig::SortOrderChanged, this, [this]() {
// setRoomSortOrder(static_cast<RoomSortOrder>(NeoChatConfig::sortOrder()));
// invalidateFilter();
// });
setRecursiveFilteringEnabled(true);
sort(0);
@@ -34,13 +34,13 @@ SortFilterRoomTreeModel::SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QOb
connect(this->sourceModel(), &QAbstractItemModel::rowsRemoved, this, &SortFilterRoomTreeModel::invalidateFilter);
});
connect(NeoChatConfig::self(), &NeoChatConfig::CollapsedChanged, this, &SortFilterRoomTreeModel::invalidateFilter);
connect(NeoChatConfig::self(), &NeoChatConfig::AllRoomsInHomeChanged, this, [this]() {
invalidateFilter();
if (NeoChatConfig::self()->allRoomsInHome()) {
RoomManager::instance().resetState();
}
});
// connect(NeoChatConfig::self(), &NeoChatConfig::CollapsedChanged, this, &SortFilterRoomTreeModel::invalidateFilter);
// connect(NeoChatConfig::self(), &NeoChatConfig::AllRoomsInHomeChanged, this, [this]() {
// invalidateFilter();
// if (NeoChatConfig::self()->allRoomsInHome()) {
// RoomManager::instance().resetState();
// }
// });
}
void SortFilterRoomTreeModel::setRoomSortOrder(SortFilterRoomTreeModel::RoomSortOrder sortOrder)
@@ -78,14 +78,14 @@ bool SortFilterRoomTreeModel::lessThan(const QModelIndex &source_left, const QMo
return false;
}
const auto leftRoom = dynamic_cast<NeoChatRoom *>(treeModel->connection()->room(source_left.data(RoomTreeModel::RoomIdRole).toString()));
const auto rightRoom = dynamic_cast<NeoChatRoom *>(treeModel->connection()->room(source_right.data(RoomTreeModel::RoomIdRole).toString()));
if (leftRoom == nullptr || rightRoom == nullptr) {
const auto leftRoom = treeModel->roomForIndex(source_left);
const auto rightRoom = treeModel->roomForIndex(source_right);
if (!leftRoom.has_value() || !rightRoom.has_value()) {
return false;
}
for (auto sortRole : RoomSortParameter::currentParameterList()) {
auto result = RoomSortParameter::compareParameter(sortRole, leftRoom, rightRoom);
auto result = RoomSortParameter::compareParameter(sortRole, leftRoom.value()->box_me(), rightRoom.value()->box_me());
if (result != 0) {
return result > 0;
@@ -141,20 +141,22 @@ bool SortFilterRoomTreeModel::filterAcceptsRow(int source_row, const QModelIndex
return false;
}
static auto config = NeoChatConfig::self();
if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
return acceptRoom;
}
return acceptRoom;
if (m_activeSpaceId.isEmpty()) {
if (!SpaceHierarchyCache::instance().isChild(sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString())) {
return acceptRoom;
}
return false;
} else {
const auto &rooms = SpaceHierarchyCache::instance().getRoomListForSpace(m_activeSpaceId, false);
return std::find(rooms.begin(), rooms.end(), sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString()) != rooms.end() && acceptRoom;
}
// static auto config = NeoChatConfig::self();
// if (config->allRoomsInHome() && RoomManager::instance().currentSpace().isEmpty()) {
// return acceptRoom;
// }
//
// if (m_activeSpaceId.isEmpty()) {
// if (!SpaceHierarchyCache::instance().isChild(sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString())) {
// return acceptRoom;
// }
// return false;
// } else {
// const auto &rooms = SpaceHierarchyCache::instance().getRoomListForSpace(m_activeSpaceId, false);
// return std::find(rooms.begin(), rooms.end(), sourceModel()->data(index, RoomTreeModel::RoomIdRole).toString()) != rooms.end() && acceptRoom;
// }
}
QString SortFilterRoomTreeModel::activeSpaceId() const
@@ -192,7 +194,7 @@ QModelIndex SortFilterRoomTreeModel::currentRoomIndex() const
return {};
}
return mapFromSource(roomModel->indexForRoom(RoomManager::instance().currentRoom()));
return {}; // mapFromSource(roomModel->indexForRoom(RoomManager::instance().currentRoom()));
}
#include "moc_sortfilterroomtreemodel.cpp"

View File

@@ -32,7 +32,7 @@ class SortFilterRoomTreeModel : public QSortFilterProxyModel
{
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
// QML_UNCREATABLE("")
/**
* @brief The text to use to filter room names.
@@ -64,7 +64,7 @@ public:
};
Q_ENUM(Mode)
explicit SortFilterRoomTreeModel(RoomTreeModel *sourceModel, QObject *parent = nullptr);
explicit SortFilterRoomTreeModel(QObject *parent = nullptr);
void setRoomSortOrder(RoomSortOrder sortOrder);

View File

@@ -5,8 +5,8 @@
#include <QGuiApplication>
#include <Quotient/avatar.h>
#include <Quotient/events/roompowerlevelsevent.h>
// #include <Quotient/avatar.h>
// #include <Quotient/events/roompowerlevelsevent.h>
#include "enums/powerlevel.h"
#include "neochatroom.h"
@@ -30,7 +30,7 @@ void UserListModel::setRoom(NeoChatRoom *room)
// last room's objects before the room is actually changed
beginResetModel();
m_currentRoom->disconnect(this);
m_currentRoom->connection()->disconnect(this);
// m_currentRoom->connection()->disconnect(this);
m_currentRoom = nullptr;
m_members.clear();
endResetModel();
@@ -39,21 +39,21 @@ void UserListModel::setRoom(NeoChatRoom *room)
m_currentRoom = room;
if (m_currentRoom) {
connect(m_currentRoom, &Room::memberJoined, this, &UserListModel::memberJoined);
connect(m_currentRoom, &Room::memberLeft, this, &UserListModel::memberLeft);
connect(m_currentRoom, &Room::memberNameUpdated, this, [this](RoomMember member) {
refreshMember(member, {DisplayNameRole});
});
connect(m_currentRoom, &Room::memberAvatarUpdated, this, [this](RoomMember member) {
refreshMember(member, {AvatarRole});
});
connect(m_currentRoom, &Room::memberListChanged, this, [this]() {
// this is slow
UserListModel::refreshAllMembers();
});
connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() {
setRoom(nullptr);
});
// connect(m_currentRoom, &Room::memberJoined, this, &UserListModel::memberJoined);
// connect(m_currentRoom, &Room::memberLeft, this, &UserListModel::memberLeft);
// connect(m_currentRoom, &Room::memberNameUpdated, this, [this](RoomMember member) {
// refreshMember(member, {DisplayNameRole});
// });
// connect(m_currentRoom, &Room::memberAvatarUpdated, this, [this](RoomMember member) {
// refreshMember(member, {AvatarRole});
// });
// connect(m_currentRoom, &Room::memberListChanged, this, [this]() {
// // this is slow
// UserListModel::refreshAllMembers();
// });
// connect(m_currentRoom->connection(), &Connection::loggedOut, this, [this]() {
// setRoom(nullptr);
// });
}
m_active = false;
@@ -80,40 +80,40 @@ QVariant UserListModel::data(const QModelIndex &index, int role) const
return {};
}
auto memberId = m_members.at(index.row());
if (role == DisplayNameRole) {
return m_currentRoom->member(memberId).disambiguatedName();
}
if (role == UserIdRole) {
return memberId;
}
if (role == AvatarRole) {
return m_currentRoom->member(memberId).avatarUrl();
}
if (role == ObjectRole) {
return QVariant::fromValue(memberId);
}
if (role == PowerLevelRole) {
auto plEvent = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
if (!plEvent) {
return 0;
}
return plEvent->powerLevelForUser(memberId);
}
if (role == PowerLevelStringRole) {
auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
// User might not in the room yet, in this case pl can be nullptr.
// e.g. When invited but user not accepted or denied the invitation.
if (!pl) {
return u"Not Available"_s;
}
auto userPl = pl->powerLevelForUser(memberId);
return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
"%1 (%2)",
PowerLevel::nameForLevel(PowerLevel::levelForValue(userPl)),
userPl);
}
// if (role == DisplayNameRole) {
// return m_currentRoom->member(memberId).disambiguatedName();
// }
// if (role == UserIdRole) {
// return memberId;
// }
// if (role == AvatarRole) {
// return m_currentRoom->member(memberId).avatarUrl();
// }
// if (role == ObjectRole) {
// return QVariant::fromValue(memberId);
// }
// if (role == PowerLevelRole) {
// auto plEvent = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
// if (!plEvent) {
// return 0;
// }
// return plEvent->powerLevelForUser(memberId);
// }
// if (role == PowerLevelStringRole) {
// auto pl = m_currentRoom->currentState().get<RoomPowerLevelsEvent>();
// // User might not in the room yet, in this case pl can be nullptr.
// // e.g. When invited but user not accepted or denied the invitation.
// if (!pl) {
// return u"Not Available"_s;
// }
//
// auto userPl = pl->powerLevelForUser(memberId);
//
// return i18nc("%1 is the name of the power level, e.g. admin and %2 is the value that represents.",
// "%1 (%2)",
// PowerLevel::nameForLevel(PowerLevel::levelForValue(userPl)),
// userPl);
// }
return {};
}
@@ -134,65 +134,65 @@ bool UserListModel::event(QEvent *event)
return QObject::event(event);
}
void UserListModel::memberJoined(const Quotient::RoomMember &member)
{
auto pos = findUserPos(member);
beginInsertRows(QModelIndex(), pos, pos);
m_members.insert(pos, member.id());
endInsertRows();
}
void UserListModel::memberLeft(const Quotient::RoomMember &member)
{
auto pos = findUserPos(member);
if (pos != m_members.size()) {
beginRemoveRows(QModelIndex(), pos, pos);
m_members.removeAt(pos);
endRemoveRows();
} else {
qWarning() << "Trying to remove a room member not in the user list";
}
}
void UserListModel::refreshMember(const Quotient::RoomMember &member, const QList<int> &roles)
{
auto pos = findUserPos(member);
if (pos != m_members.size()) {
// The update will have changed the state event so we need to insert the updated member object.
m_members.insert(pos, member.id());
Q_EMIT dataChanged(index(pos), index(pos), roles);
} else {
qWarning() << "Trying to access a room member not in the user list";
}
}
// void UserListModel::memberJoined(const Quotient::RoomMember &member)
// {
// auto pos = findUserPos(member);
// beginInsertRows(QModelIndex(), pos, pos);
// m_members.insert(pos, member.id());
// endInsertRows();
// }
//
// void UserListModel::memberLeft(const Quotient::RoomMember &member)
// {
// auto pos = findUserPos(member);
// if (pos != m_members.size()) {
// beginRemoveRows(QModelIndex(), pos, pos);
// m_members.removeAt(pos);
// endRemoveRows();
// } else {
// qWarning() << "Trying to remove a room member not in the user list";
// }
// }
//
// void UserListModel::refreshMember(const Quotient::RoomMember &member, const QList<int> &roles)
// {
// auto pos = findUserPos(member);
// if (pos != m_members.size()) {
// // The update will have changed the state event so we need to insert the updated member object.
// m_members.insert(pos, member.id());
// Q_EMIT dataChanged(index(pos), index(pos), roles);
// } else {
// qWarning() << "Trying to access a room member not in the user list";
// }
// }
void UserListModel::refreshAllMembers()
{
beginResetModel();
if (m_currentRoom != nullptr) {
m_members = m_currentRoom->joinedMemberIds();
MemberSorter sorter;
std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
const auto leftPl = m_currentRoom->memberEffectivePowerLevel(left);
const auto rightPl = m_currentRoom->memberEffectivePowerLevel(right);
if (leftPl > rightPl) {
return true;
} else if (rightPl > leftPl) {
return false;
}
return sorter(m_currentRoom->member(left), m_currentRoom->member(right));
});
// m_members = m_currentRoom->joinedMemberIds();
// MemberSorter sorter;
// std::sort(m_members.begin(), m_members.end(), [&sorter, this](const auto &left, const auto &right) {
// const auto leftPl = m_currentRoom->memberEffectivePowerLevel(left);
// const auto rightPl = m_currentRoom->memberEffectivePowerLevel(right);
// if (leftPl > rightPl) {
// return true;
// } else if (rightPl > leftPl) {
// return false;
// }
//
// return sorter(m_currentRoom->member(left), m_currentRoom->member(right));
// });
}
endResetModel();
Q_EMIT usersRefreshed();
}
int UserListModel::findUserPos(const RoomMember &member) const
{
return findUserPos(member.id());
}
// int UserListModel::findUserPos(const RoomMember &member) const
// {
// return findUserPos(member.id());
// }
int UserListModel::findUserPos(const QString &userId) const
{

View File

@@ -3,7 +3,7 @@
#pragma once
#include <Quotient/room.h>
#include <Integral/Room>
#include <QAbstractListModel>
#include <QObject>
@@ -87,9 +87,9 @@ protected:
bool event(QEvent *event) override;
private Q_SLOTS:
void memberJoined(const Quotient::RoomMember &member);
void memberLeft(const Quotient::RoomMember &member);
void refreshMember(const Quotient::RoomMember &member, const QList<int> &roles = {});
// void memberJoined(const Quotient::RoomMember &member);
// void memberLeft(const Quotient::RoomMember &member);
// void refreshMember(const Quotient::RoomMember &member, const QList<int> &roles = {});
void refreshAllMembers();
private:
@@ -98,6 +98,6 @@ private:
bool m_active = false;
int findUserPos(const Quotient::RoomMember &member) const;
// int findUserPos(const Quotient::RoomMember &member) const;
[[nodiscard]] int findUserPos(const QString &username) const;
};

View File

@@ -6,147 +6,142 @@
#include <QImageReader>
#include <QJsonDocument>
#include "neochatconfig.h"
#include "neochatroom.h"
#include "spacehierarchycache.h"
// #include "neochatconfig.h"
// #include "neochatroom.h"
// #include "spacehierarchycache.h"
#include <Quotient/jobs/basejob.h>
#include <Quotient/quotient_common.h>
// #include <Quotient/jobs/basejob.h>
// #include <Quotient/quotient_common.h>
#include <qt6keychain/keychain.h>
#include <KLocalizedString>
#include <Quotient/csapi/content-repo.h>
#include <Quotient/csapi/profile.h>
#include <Quotient/csapi/registration.h>
#include <Quotient/csapi/versions.h>
#include <Quotient/jobs/downloadfilejob.h>
#include <Quotient/qt_connection_util.h>
#include <Quotient/room.h>
#include <Quotient/settings.h>
#include <Quotient/user.h>
// #include <Quotient/csapi/content-repo.h>
// #include <Quotient/csapi/profile.h>
// #include <Quotient/csapi/registration.h>
// #include <Quotient/csapi/versions.h>
// #include <Quotient/jobs/downloadfilejob.h>
// #include <Quotient/qt_connection_util.h>
// #include <Quotient/room.h>
// #include <Quotient/settings.h>
// #include <Quotient/user.h>
#ifdef HAVE_KUNIFIEDPUSH
#include <QCoroNetwork>
#include <Quotient/csapi/pusher.h>
#include <Quotient/networkaccessmanager.h>
#endif
// #ifdef HAVE_KUNIFIEDPUSH
// #include <QCoroNetwork>
// #include <Quotient/csapi/pusher.h>
// #include <Quotient/networkaccessmanager.h>
// #endif
using namespace Quotient;
#include <Integral/Connection_p>
using namespace Integral;
using namespace Qt::StringLiterals;
NeoChatConnection::NeoChatConnection(QObject *parent)
: Connection(parent)
NeoChatConnection::NeoChatConnection(std::unique_ptr<Connection::Private> d)
: Connection(std::move(d))
{
m_linkPreviewers.setMaxCost(20);
connectSignals();
}
NeoChatConnection::NeoChatConnection(const QUrl &server, QObject *parent)
: Connection(server, parent)
{
m_linkPreviewers.setMaxCost(20);
// m_linkPreviewers.setMaxCost(20);
connectSignals();
}
void NeoChatConnection::connectSignals()
{
connect(this, &NeoChatConnection::accountDataChanged, this, [this](const QString &type) {
if (type == u"org.kde.neochat.account_label"_s) {
Q_EMIT labelChanged();
}
if (type == u"m.identity_server"_s) {
Q_EMIT identityServerChanged();
}
});
connect(this, &NeoChatConnection::syncDone, this, [this] {
setIsOnline(true);
});
connect(this, &NeoChatConnection::networkError, this, [this]() {
setIsOnline(false);
});
connect(this, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
if (job->error() == BaseJob::UserConsentRequired) {
Q_EMIT userConsentRequired(job->errorUrl());
}
});
connect(this, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"_L1].toString() == "M_TOO_LARGE"_L1) {
Q_EMIT showMessage(MessageType::Warning, i18n("File too large to download.<br />Contact your matrix server administrator for support."));
}
});
connect(this, &NeoChatConnection::directChatsListChanged, this, [this](DirectChatsMap additions, DirectChatsMap removals) {
Q_EMIT directChatInvitesChanged();
for (const auto &chatId : additions) {
if (const auto chat = room(chatId)) {
connect(chat, &Room::unreadStatsChanged, this, [this]() {
refreshBadgeNotificationCount();
Q_EMIT directChatNotificationsChanged();
Q_EMIT directChatsHaveHighlightNotificationsChanged();
});
}
}
for (const auto &chatId : removals) {
if (const auto chat = room(chatId)) {
disconnect(chat, &Room::unreadStatsChanged, this, nullptr);
}
}
});
connect(this, &NeoChatConnection::joinedRoom, this, [this](Room *room) {
if (room->isDirectChat()) {
connect(room, &Room::unreadStatsChanged, this, [this]() {
Q_EMIT directChatNotificationsChanged();
Q_EMIT directChatsHaveHighlightNotificationsChanged();
});
}
connect(room, &Room::unreadStatsChanged, this, [this]() {
refreshBadgeNotificationCount();
Q_EMIT homeNotificationsChanged();
Q_EMIT homeHaveHighlightNotificationsChanged();
});
});
connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
Q_UNUSED(room)
if (prev && prev->isDirectChat()) {
Q_EMIT directChatInvitesChanged();
Q_EMIT directChatNotificationsChanged();
Q_EMIT directChatsHaveHighlightNotificationsChanged();
}
refreshBadgeNotificationCount();
Q_EMIT homeNotificationsChanged();
Q_EMIT homeHaveHighlightNotificationsChanged();
});
connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceHierarchyChanged, this, [this]() {
refreshBadgeNotificationCount();
Q_EMIT homeNotificationsChanged();
Q_EMIT homeHaveHighlightNotificationsChanged();
});
// Fetch unstable features
// TODO: Expose unstableFeatures() in libQuotient
connect(
this,
&Connection::connected,
this,
[this] {
auto job = callApi<GetVersionsJob>(BackgroundRequest);
connect(job, &GetVersionsJob::success, this, [this, job] {
m_canCheckMutualRooms = job->unstableFeatures().contains("uk.half-shot.msc2666.query_mutual_rooms"_L1);
Q_EMIT canCheckMutualRoomsChanged();
m_canEraseData = job->unstableFeatures().contains("org.matrix.msc4025"_L1) || job->versions().count("v1.10"_L1);
Q_EMIT canEraseDataChanged();
});
},
Qt::SingleShotConnection);
setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
connect(NeoChatConfig::self(), &NeoChatConfig::PreferUsingEncryptionChanged, this, [] {
setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
});
setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this]() {
setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
});
// connect(this, &NeoChatConnection::accountDataChanged, this, [this](const QString &type) {
// if (type == u"org.kde.neochat.account_label"_s) {
// Q_EMIT labelChanged();
// }
// if (type == u"m.identity_server"_s) {
// Q_EMIT identityServerChanged();
// }
// });
// connect(this, &NeoChatConnection::syncDone, this, [this] {
// setIsOnline(true);
// });
// connect(this, &NeoChatConnection::networkError, this, [this]() {
// setIsOnline(false);
// });
// connect(this, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
// if (job->error() == BaseJob::UserConsentRequired) {
// Q_EMIT userConsentRequired(job->errorUrl());
// }
// });
// connect(this, &NeoChatConnection::requestFailed, this, [this](BaseJob *job) {
// if (dynamic_cast<DownloadFileJob *>(job) && job->jsonData()["errcode"_L1].toString() == "M_TOO_LARGE"_L1) {
// Q_EMIT showMessage(MessageType::Warning, i18n("File too large to download.<br />Contact your matrix server administrator for support."));
// }
// });
// connect(this, &NeoChatConnection::directChatsListChanged, this, [this](DirectChatsMap additions, DirectChatsMap removals) {
// Q_EMIT directChatInvitesChanged();
// for (const auto &chatId : additions) {
// if (const auto chat = room(chatId)) {
// connect(chat, &Room::unreadStatsChanged, this, [this]() {
// refreshBadgeNotificationCount();
// Q_EMIT directChatNotificationsChanged();
// Q_EMIT directChatsHaveHighlightNotificationsChanged();
// });
// }
// }
// for (const auto &chatId : removals) {
// if (const auto chat = room(chatId)) {
// disconnect(chat, &Room::unreadStatsChanged, this, nullptr);
// }
// }
// });
// connect(this, &NeoChatConnection::joinedRoom, this, [this](Room *room) {
// if (room->isDirectChat()) {
// connect(room, &Room::unreadStatsChanged, this, [this]() {
// Q_EMIT directChatNotificationsChanged();
// Q_EMIT directChatsHaveHighlightNotificationsChanged();
// });
// }
// connect(room, &Room::unreadStatsChanged, this, [this]() {
// refreshBadgeNotificationCount();
// Q_EMIT homeNotificationsChanged();
// Q_EMIT homeHaveHighlightNotificationsChanged();
// });
// });
// connect(this, &NeoChatConnection::leftRoom, this, [this](Room *room, Room *prev) {
// Q_UNUSED(room)
// if (prev && prev->isDirectChat()) {
// Q_EMIT directChatInvitesChanged();
// Q_EMIT directChatNotificationsChanged();
// Q_EMIT directChatsHaveHighlightNotificationsChanged();
// }
// refreshBadgeNotificationCount();
// Q_EMIT homeNotificationsChanged();
// Q_EMIT homeHaveHighlightNotificationsChanged();
// });
//
// connect(&SpaceHierarchyCache::instance(), &SpaceHierarchyCache::spaceHierarchyChanged, this, [this]() {
// refreshBadgeNotificationCount();
// Q_EMIT homeNotificationsChanged();
// Q_EMIT homeHaveHighlightNotificationsChanged();
// });
//
// // Fetch unstable features
// // TODO: Expose unstableFeatures() in libQuotient
// connect(
// this,
// &Connection::connected,
// this,
// [this] {
// auto job = callApi<GetVersionsJob>(BackgroundRequest);
// connect(job, &GetVersionsJob::success, this, [this, job] {
// m_canCheckMutualRooms = job->unstableFeatures().contains("uk.half-shot.msc2666.query_mutual_rooms"_L1);
// Q_EMIT canCheckMutualRoomsChanged();
// m_canEraseData = job->unstableFeatures().contains("org.matrix.msc4025"_L1) || job->versions().count("v1.10"_L1);
// Q_EMIT canEraseDataChanged();
// });
// },
// Qt::SingleShotConnection);
// setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
// connect(NeoChatConfig::self(), &NeoChatConfig::PreferUsingEncryptionChanged, this, [] {
// setDirectChatEncryptionDefault(NeoChatConfig::preferUsingEncryption());
// });
// setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
// connect(NeoChatConfig::self(), &NeoChatConfig::ShowLinkPreviewChanged, this, [this]() {
// setGlobalUrlPreviewEnabled(NeoChatConfig::showLinkPreview());
// });
}
int NeoChatConnection::badgeNotificationCount() const
@@ -157,11 +152,11 @@ int NeoChatConnection::badgeNotificationCount() const
void NeoChatConnection::refreshBadgeNotificationCount()
{
int count = 0;
for (const auto &r : allRooms()) {
if (const auto room = static_cast<NeoChatRoom *>(r)) {
count += room->contextAwareNotificationCount();
}
}
// for (const auto &r : allRooms()) {
// if (const auto room = static_cast<NeoChatRoom *>(r)) {
// count += room->contextAwareNotificationCount();
// }
// }
if (count != m_badgeNotificationCount) {
m_badgeNotificationCount = count;
@@ -182,56 +177,58 @@ void NeoChatConnection::setGlobalUrlPreviewEnabled(bool newState)
m_globalUrlPreviewEnabled = newState;
if (!m_globalUrlPreviewEnabled) {
m_linkPreviewers.clear();
// m_linkPreviewers.clear();
}
NeoChatConfig::setShowLinkPreview(m_globalUrlPreviewEnabled);
// NeoChatConfig::setShowLinkPreview(m_globalUrlPreviewEnabled);
Q_EMIT globalUrlPreviewEnabledChanged();
}
void NeoChatConnection::logout(bool serverSideLogout)
{
SettingsGroup(u"Accounts"_s).remove(userId());
QKeychain::DeletePasswordJob job(qAppName());
job.setAutoDelete(true);
job.setKey(userId());
QEventLoop loop;
QKeychain::DeletePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
job.start();
loop.exec();
if (!serverSideLogout) {
return;
}
Connection::logout();
// SettingsGroup(u"Accounts"_s).remove(userId());
//
// QKeychain::DeletePasswordJob job(qAppName());
// job.setAutoDelete(true);
// job.setKey(userId());
// QEventLoop loop;
// QKeychain::DeletePasswordJob::connect(&job, &QKeychain::Job::finished, &loop, &QEventLoop::quit);
// job.start();
// loop.exec();
//
// if (!serverSideLogout) {
// return;
// }
// Connection::logout();
}
bool NeoChatConnection::setAvatar(const QUrl &avatarSource)
{
QString decoded = avatarSource.path();
if (decoded.isEmpty()) {
callApi<SetAvatarUrlJob>(user()->id(), avatarSource);
return true;
}
if (QImageReader(decoded).read().isNull()) {
return false;
} else {
return user()->setAvatar(decoded);
}
// QString decoded = avatarSource.path();
// if (decoded.isEmpty()) {
// callApi<SetAvatarUrlJob>(user()->id(), avatarSource);
// return true;
// }
// if (QImageReader(decoded).read().isNull()) {
// return false;
// } else {
// return user()->setAvatar(decoded);
// }
return {};
}
QVariantList NeoChatConnection::getSupportedRoomVersions() const
{
const auto &roomVersions = availableRoomVersions();
QVariantList supportedRoomVersions;
for (const auto &v : roomVersions) {
QVariantMap roomVersionMap;
roomVersionMap.insert("id"_L1, v.id);
roomVersionMap.insert("status"_L1, v.status);
roomVersionMap.insert("isStable"_L1, v.isStable());
supportedRoomVersions.append(roomVersionMap);
}
return supportedRoomVersions;
// const auto &roomVersions = availableRoomVersions();
// QVariantList supportedRoomVersions;
// for (const auto &v : roomVersions) {
// QVariantMap roomVersionMap;
// roomVersionMap.insert("id"_L1, v.id);
// roomVersionMap.insert("status"_L1, v.status);
// roomVersionMap.insert("isStable"_L1, v.isStable());
// supportedRoomVersions.append(roomVersionMap);
// }
// return supportedRoomVersions;
return {};
}
bool NeoChatConnection::canCheckMutualRooms() const
@@ -241,85 +238,86 @@ bool NeoChatConnection::canCheckMutualRooms() const
void NeoChatConnection::changePassword(const QString &currentPassword, const QString &newPassword)
{
auto job = callApi<ChangePasswordJob>(newPassword, false);
connect(job, &BaseJob::result, this, [this, job, currentPassword, newPassword] {
if (job->error() == 103) {
QJsonObject replyData = job->jsonData();
AuthenticationData authData;
authData.session = replyData["session"_L1].toString();
authData.type = "m.login.password"_L1;
authData.authInfo["password"_L1] = currentPassword;
authData.authInfo["user"_L1] = user()->id();
authData.authInfo["identifier"_L1] = QJsonObject{{"type"_L1, "m.id.user"_L1}, {"user"_L1, user()->id()}};
auto innerJob = callApi<ChangePasswordJob>(newPassword, false, authData);
connect(innerJob, &BaseJob::success, this, [this]() {
Q_EMIT passwordStatus(PasswordStatus::Success);
});
connect(innerJob, &BaseJob::failure, this, [innerJob, this]() {
Q_EMIT passwordStatus(innerJob->jsonData()["errcode"_L1] == "M_FORBIDDEN"_L1 ? PasswordStatus::Wrong : PasswordStatus::Other);
});
}
});
// auto job = callApi<ChangePasswordJob>(newPassword, false);
// connect(job, &BaseJob::result, this, [this, job, currentPassword, newPassword] {
// if (job->error() == 103) {
// QJsonObject replyData = job->jsonData();
// AuthenticationData authData;
// authData.session = replyData["session"_L1].toString();
// authData.type = "m.login.password"_L1;
// authData.authInfo["password"_L1] = currentPassword;
// authData.authInfo["user"_L1] = user()->id();
// authData.authInfo["identifier"_L1] = QJsonObject{{"type"_L1, "m.id.user"_L1}, {"user"_L1, user()->id()}};
// auto innerJob = callApi<ChangePasswordJob>(newPassword, false, authData);
// connect(innerJob, &BaseJob::success, this, [this]() {
// Q_EMIT passwordStatus(PasswordStatus::Success);
// });
// connect(innerJob, &BaseJob::failure, this, [innerJob, this]() {
// Q_EMIT passwordStatus(innerJob->jsonData()["errcode"_L1] == "M_FORBIDDEN"_L1 ? PasswordStatus::Wrong : PasswordStatus::Other);
// });
// }
// });
}
void NeoChatConnection::setLabel(const QString &label)
{
QJsonObject json{
{"account_label"_L1, label},
};
setAccountData("org.kde.neochat.account_label"_L1, json);
Q_EMIT labelChanged();
// QJsonObject json{
// {"account_label"_L1, label},
// };
// setAccountData("org.kde.neochat.account_label"_L1, json);
// Q_EMIT labelChanged();
}
QString NeoChatConnection::label() const
{
return accountDataJson("org.kde.neochat.account_label"_L1)["account_label"_L1].toString();
// return accountDataJson("org.kde.neochat.account_label"_L1)["account_label"_L1].toString();
return {};
}
void NeoChatConnection::deactivateAccount(const QString &password, const bool erase)
{
auto job = callApi<DeactivateAccountJob>();
connect(job, &BaseJob::result, this, [this, job, password, erase] {
if (job->error() == 103) {
QJsonObject replyData = job->jsonData();
AuthenticationData authData;
authData.session = replyData["session"_L1].toString();
authData.authInfo["password"_L1] = password;
authData.type = "m.login.password"_L1;
authData.authInfo["user"_L1] = user()->id();
QJsonObject identifier = {{"type"_L1, "m.id.user"_L1}, {"user"_L1, user()->id()}};
authData.authInfo["identifier"_L1] = identifier;
auto innerJob = callApi<DeactivateAccountJob>(authData, QString{}, erase);
connect(innerJob, &BaseJob::success, this, [this]() {
logout(false);
});
}
});
// auto job = callApi<DeactivateAccountJob>();
// connect(job, &BaseJob::result, this, [this, job, password, erase] {
// if (job->error() == 103) {
// QJsonObject replyData = job->jsonData();
// AuthenticationData authData;
// authData.session = replyData["session"_L1].toString();
// authData.authInfo["password"_L1] = password;
// authData.type = "m.login.password"_L1;
// authData.authInfo["user"_L1] = user()->id();
// QJsonObject identifier = {{"type"_L1, "m.id.user"_L1}, {"user"_L1, user()->id()}};
// authData.authInfo["identifier"_L1] = identifier;
// auto innerJob = callApi<DeactivateAccountJob>(authData, QString{}, erase);
// connect(innerJob, &BaseJob::success, this, [this]() {
// logout(false);
// });
// }
// });
}
bool NeoChatConnection::hasIdentityServer() const
{
if (!hasAccountData(u"m.identity_server"_s)) {
return false;
}
// if (!hasAccountData(u"m.identity_server"_s)) {
// return false;
// }
const auto url = accountData(u"m.identity_server"_s)->contentPart<QUrl>("base_url"_L1);
if (!url.isEmpty()) {
return true;
}
// const auto url = accountData(u"m.identity_server"_s)->contentPart<QUrl>("base_url"_L1);
// if (!url.isEmpty()) {
// return true;
// }
return false;
}
QUrl NeoChatConnection::identityServer() const
{
if (!hasAccountData(u"m.identity_server"_s)) {
return {};
}
// if (!hasAccountData(u"m.identity_server"_s)) {
// return {};
// }
const auto url = accountData(u"m.identity_server"_s)->contentPart<QUrl>("base_url"_L1);
if (!url.isEmpty()) {
return url;
}
// const auto url = accountData(u"m.identity_server"_s)->contentPart<QUrl>("base_url"_L1);
// if (!url.isEmpty()) {
// return url;
// }
return {};
}
@@ -334,92 +332,93 @@ QString NeoChatConnection::identityServerUIString() const
void NeoChatConnection::createRoom(const QString &name, const QString &topic, const QString &parent, bool setChildParent)
{
QList<CreateRoomJob::StateEvent> initialStateEvents;
if (!parent.isEmpty()) {
initialStateEvents.append(CreateRoomJob::StateEvent{
"m.space.parent"_L1,
QJsonObject{
{"canonical"_L1, true},
{"via"_L1, QJsonArray{domain()}},
},
parent,
});
}
const auto job = Connection::createRoom(Connection::PublishRoom, QString(), name, topic, QStringList(), {}, {}, {}, initialStateEvents);
if (!parent.isEmpty()) {
connect(job, &Quotient::CreateRoomJob::success, this, [this, parent, setChildParent, job]() {
if (setChildParent) {
if (auto parentRoom = room(parent)) {
parentRoom->setState(u"m.space.child"_s, job->roomId(), QJsonObject{{"via"_L1, QJsonArray{domain()}}});
}
}
});
}
connect(job, &CreateRoomJob::failure, this, [this, job] {
Q_EMIT errorOccured(i18n("Room creation failed: %1", job->errorString()));
});
// QList<CreateRoomJob::StateEvent> initialStateEvents;
// if (!parent.isEmpty()) {
// initialStateEvents.append(CreateRoomJob::StateEvent{
// "m.space.parent"_L1,
// QJsonObject{
// {"canonical"_L1, true},
// {"via"_L1, QJsonArray{domain()}},
// },
// parent,
// });
// }
//
// const auto job = Connection::createRoom(Connection::PublishRoom, QString(), name, topic, QStringList(), {}, {}, {}, initialStateEvents);
// if (!parent.isEmpty()) {
// connect(job, &Quotient::CreateRoomJob::success, this, [this, parent, setChildParent, job]() {
// if (setChildParent) {
// if (auto parentRoom = room(parent)) {
// parentRoom->setState(u"m.space.child"_s, job->roomId(), QJsonObject{{"via"_L1, QJsonArray{domain()}}});
// }
// }
// });
// }
// connect(job, &CreateRoomJob::failure, this, [this, job] {
// Q_EMIT errorOccured(i18n("Room creation failed: %1", job->errorString()));
// });
}
void NeoChatConnection::createSpace(const QString &name, const QString &topic, const QString &parent, bool setChildParent)
{
QList<CreateRoomJob::StateEvent> initialStateEvents;
if (!parent.isEmpty()) {
initialStateEvents.append(CreateRoomJob::StateEvent{
"m.space.parent"_L1,
QJsonObject{
{"canonical"_L1, true},
{"via"_L1, QJsonArray{domain()}},
},
parent,
});
}
const auto job =
Connection::createRoom(Connection::UnpublishRoom, {}, name, topic, {}, {}, {}, false, initialStateEvents, {}, QJsonObject{{"type"_L1, "m.space"_L1}});
if (!parent.isEmpty()) {
connect(job, &Quotient::CreateRoomJob::success, this, [this, parent, setChildParent, job]() {
if (setChildParent) {
if (auto parentRoom = room(parent)) {
parentRoom->setState(u"m.space.child"_s, job->roomId(), QJsonObject{{"via"_L1, QJsonArray{domain()}}});
}
}
});
}
connect(job, &CreateRoomJob::failure, this, [this, job] {
Q_EMIT errorOccured(i18n("Space creation failed: %1", job->errorString()));
});
// QList<CreateRoomJob::StateEvent> initialStateEvents;
// if (!parent.isEmpty()) {
// initialStateEvents.append(CreateRoomJob::StateEvent{
// "m.space.parent"_L1,
// QJsonObject{
// {"canonical"_L1, true},
// {"via"_L1, QJsonArray{domain()}},
// },
// parent,
// });
// }
//
// const auto job =
// Connection::createRoom(Connection::UnpublishRoom, {}, name, topic, {}, {}, {}, false, initialStateEvents, {}, QJsonObject{{"type"_L1,
// "m.space"_L1}});
// if (!parent.isEmpty()) {
// connect(job, &Quotient::CreateRoomJob::success, this, [this, parent, setChildParent, job]() {
// if (setChildParent) {
// if (auto parentRoom = room(parent)) {
// parentRoom->setState(u"m.space.child"_s, job->roomId(), QJsonObject{{"via"_L1, QJsonArray{domain()}}});
// }
// }
// });
// }
// connect(job, &CreateRoomJob::failure, this, [this, job] {
// Q_EMIT errorOccured(i18n("Space creation failed: %1", job->errorString()));
// });
}
bool NeoChatConnection::directChatExists(Quotient::User *user)
{
return directChats().contains(user);
}
// bool NeoChatConnection::directChatExists(Quotient::User *user)
// {
// return directChats().contains(user);
// }
qsizetype NeoChatConnection::directChatNotifications() const
{
qsizetype notifications = 0;
QStringList added; // The same ID can be in the list multiple times.
for (const auto &chatId : directChats()) {
if (!added.contains(chatId)) {
if (const auto chat = room(chatId)) {
notifications += dynamic_cast<NeoChatRoom *>(chat)->contextAwareNotificationCount();
added += chatId;
}
}
}
// for (const auto &chatId : directChats()) {
// if (!added.contains(chatId)) {
// if (const auto chat = room(chatId)) {
// notifications += dynamic_cast<NeoChatRoom *>(chat)->contextAwareNotificationCount();
// added += chatId;
// }
// }
// }
return notifications;
}
bool NeoChatConnection::directChatsHaveHighlightNotifications() const
{
for (const auto &childId : directChats()) {
if (const auto child = static_cast<NeoChatRoom *>(room(childId))) {
if (child->highlightCount() > 0) {
return true;
}
}
}
// for (const auto &childId : directChats()) {
// if (const auto child = static_cast<NeoChatRoom *>(room(childId))) {
// if (child->highlightCount() > 0) {
// return true;
// }
// }
// }
return false;
}
@@ -427,78 +426,78 @@ qsizetype NeoChatConnection::homeNotifications() const
{
qsizetype notifications = 0;
QStringList added;
const auto &spaceHierarchyCache = SpaceHierarchyCache::instance();
for (const auto &r : allRooms()) {
if (const auto room = static_cast<NeoChatRoom *>(r)) {
if (!added.contains(room->id()) && !room->isDirectChat() && !spaceHierarchyCache.isChild(room->id())) {
notifications += dynamic_cast<NeoChatRoom *>(room)->contextAwareNotificationCount();
added += room->id();
}
}
}
// const auto &spaceHierarchyCache = SpaceHierarchyCache::instance();
// for (const auto &r : allRooms()) {
// if (const auto room = static_cast<NeoChatRoom *>(r)) {
// if (!added.contains(room->id()) && !room->isDirectChat() && !spaceHierarchyCache.isChild(room->id())) {
// notifications += dynamic_cast<NeoChatRoom *>(room)->contextAwareNotificationCount();
// added += room->id();
// }
// }
// }
return notifications;
}
bool NeoChatConnection::homeHaveHighlightNotifications() const
{
const auto &spaceHierarchyCache = SpaceHierarchyCache::instance();
for (const auto &r : allRooms()) {
if (const auto room = static_cast<NeoChatRoom *>(r)) {
if (!room->isDirectChat() && !spaceHierarchyCache.isChild(room->id()) && room->highlightCount() > 0) {
return true;
}
}
}
// const auto &spaceHierarchyCache = SpaceHierarchyCache::instance();
// for (const auto &r : allRooms()) {
// if (const auto room = static_cast<NeoChatRoom *>(r)) {
// if (!room->isDirectChat() && !spaceHierarchyCache.isChild(room->id()) && room->highlightCount() > 0) {
// return true;
// }
// }
// }
return false;
}
bool NeoChatConnection::directChatInvites() const
{
auto inviteRooms = rooms(JoinState::Invite);
for (const auto inviteRoom : inviteRooms) {
if (inviteRoom->isDirectChat()) {
return true;
}
}
// auto inviteRooms = rooms(JoinState::Invite);
// for (const auto inviteRoom : inviteRooms) {
// if (inviteRoom->isDirectChat()) {
// return true;
// }
// }
return false;
}
QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint)
{
#ifdef HAVE_KUNIFIEDPUSH
QUrl gatewayEndpoint(endpoint);
gatewayEndpoint.setPath(u"/_matrix/push/v1/notify"_s);
QNetworkRequest checkGateway(gatewayEndpoint);
auto reply = co_await NetworkAccessManager::instance()->get(checkGateway);
// We want to check if this UnifiedPush server has a Matrix gateway
// This is because Matrix does not natively support UnifiedPush
const auto &replyJson = QJsonDocument::fromJson(reply->readAll()).object();
if (replyJson["unifiedpush"_L1]["gateway"_L1].toString() == u"matrix"_s) {
callApi<PostPusherJob>(endpoint,
u"http"_s,
u"org.kde.neochat"_s,
u"NeoChat"_s,
deviceId(),
QString(), // profileTag is intentionally left empty for now, it's optional
u"en-US"_s,
PostPusherJob::PusherData{QUrl::fromUserInput(gatewayEndpoint.toString()), u" "_s},
false);
qInfo() << "Registered for push notifications";
m_pushNotificationsEnabled = true;
} else {
qWarning() << "There's no gateway, not setting up push notifications.";
m_pushNotificationsEnabled = false;
}
Q_EMIT enablePushNotificationsChanged();
#else
Q_UNUSED(endpoint)
co_return;
#endif
}
// QCoro::Task<void> NeoChatConnection::setupPushNotifications(QString endpoint)
// {
// #ifdef HAVE_KUNIFIEDPUSH
// QUrl gatewayEndpoint(endpoint);
// gatewayEndpoint.setPath(u"/_matrix/push/v1/notify"_s);
//
// QNetworkRequest checkGateway(gatewayEndpoint);
// auto reply = co_await NetworkAccessManager::instance()->get(checkGateway);
//
// // We want to check if this UnifiedPush server has a Matrix gateway
// // This is because Matrix does not natively support UnifiedPush
// const auto &replyJson = QJsonDocument::fromJson(reply->readAll()).object();
//
// if (replyJson["unifiedpush"_L1]["gateway"_L1].toString() == u"matrix"_s) {
// callApi<PostPusherJob>(endpoint,
// u"http"_s,
// u"org.kde.neochat"_s,
// u"NeoChat"_s,
// deviceId(),
// QString(), // profileTag is intentionally left empty for now, it's optional
// u"en-US"_s,
// PostPusherJob::PusherData{QUrl::fromUserInput(gatewayEndpoint.toString()), u" "_s},
// false);
//
// qInfo() << "Registered for push notifications";
// m_pushNotificationsEnabled = true;
// } else {
// qWarning() << "There's no gateway, not setting up push notifications.";
// m_pushNotificationsEnabled = false;
// }
// Q_EMIT enablePushNotificationsChanged();
// #else
// Q_UNUSED(endpoint)
// co_return;
// #endif
// }
bool NeoChatConnection::isOnline() const
{
@@ -516,39 +515,39 @@ void NeoChatConnection::setIsOnline(bool isOnline)
QString NeoChatConnection::accountDataJsonString(const QString &type) const
{
return QString::fromUtf8(QJsonDocument(accountDataJson(type)).toJson());
return {}; // QString::fromUtf8(QJsonDocument(accountDataJson(type)).toJson());
}
LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
{
if (!m_globalUrlPreviewEnabled) {
return nullptr;
}
// LinkPreviewer *NeoChatConnection::previewerForLink(const QUrl &link)
// {
// if (!m_globalUrlPreviewEnabled) {
// return nullptr;
// }
//
// auto previewer = m_linkPreviewers.object(link);
// if (previewer != nullptr) {
// return previewer;
// }
//
// previewer = new LinkPreviewer(link, this);
// m_linkPreviewers.insert(link, previewer);
// return previewer;
// }
auto previewer = m_linkPreviewers.object(link);
if (previewer != nullptr) {
return previewer;
}
previewer = new LinkPreviewer(link, this);
m_linkPreviewers.insert(link, previewer);
return previewer;
}
KeyImport::Error NeoChatConnection::exportMegolmSessions(const QString &passphrase, const QString &path)
{
KeyImport keyImport;
auto result = keyImport.exportKeys(passphrase, this);
if (!result.has_value()) {
return result.error();
}
QUrl url(path);
QFile file(url.toLocalFile());
file.open(QFile::WriteOnly);
file.write(result.value());
file.close();
return KeyImport::Success;
}
// KeyImport::Error NeoChatConnection::exportMegolmSessions(const QString &passphrase, const QString &path)
// {
// KeyImport keyImport;
// auto result = keyImport.exportKeys(passphrase, this);
// if (!result.has_value()) {
// return result.error();
// }
// QUrl url(path);
// QFile file(url.toLocalFile());
// file.open(QFile::WriteOnly);
// file.write(result.value());
// file.close();
// return KeyImport::Success;
// }
bool NeoChatConnection::canEraseData() const
{

View File

@@ -7,15 +7,16 @@
#include <QObject>
#include <QQmlEngine>
#include <Integral/Connection>
#include <QCoroTask>
#include <Quotient/connection.h>
#include <Quotient/keyimport.h>
// #include <Quotient/keyimport.h>
#include "enums/messagetype.h"
#include "linkpreviewer.h"
// #include "enums/messagetype.h"
// #include "linkpreviewer.h"
// #include "models/threepidmodel.h"
class NeoChatConnection : public Quotient::Connection
class NeoChatConnection : public Integral::Connection
{
Q_OBJECT
QML_ELEMENT
@@ -110,8 +111,7 @@ public:
};
Q_ENUM(PasswordStatus)
NeoChatConnection(QObject *parent = nullptr);
NeoChatConnection(const QUrl &server, QObject *parent = nullptr);
NeoChatConnection(std::unique_ptr<Integral::Connection::Private> d);
Q_INVOKABLE void logout(bool serverSideLogout);
Q_INVOKABLE QVariantList getSupportedRoomVersions() const;
@@ -162,14 +162,14 @@ public:
/**
* @brief Whether a direct chat with the user exists.
*/
Q_INVOKABLE bool directChatExists(Quotient::User *user);
// Q_INVOKABLE bool directChatExists(Quotient::User *user);
/**
* @brief Get the account data with \param type as a formatted JSON string.
*/
Q_INVOKABLE QString accountDataJsonString(const QString &type) const;
Q_INVOKABLE Quotient::KeyImport::Error exportMegolmSessions(const QString &passphrase, const QString &path);
// Q_INVOKABLE Quotient::KeyImport::Error exportMegolmSessions(const QString &passphrase, const QString &path);
qsizetype directChatNotifications() const;
bool directChatsHaveHighlightNotifications() const;
@@ -186,14 +186,14 @@ public:
// note: this is intentionally a copied QString because
// the reference could be destroyed before the task is finished
QCoro::Task<void> setupPushNotifications(QString endpoint);
// QCoro::Task<void> setupPushNotifications(QString endpoint);
bool pushNotificationsAvailable() const;
bool enablePushNotifications() const;
bool isOnline() const;
LinkPreviewer *previewerForLink(const QUrl &link);
// LinkPreviewer *previewerForLink(const QUrl &link);
Q_SIGNALS:
void globalUrlPreviewEnabledChanged();
@@ -215,7 +215,7 @@ Q_SIGNALS:
/**
* @brief Request a message be shown to the user of the given type.
*/
void showMessage(MessageType::Type messageType, const QString &message);
// void showMessage(MessageType::Type messageType, const QString &message);
/**
* @brief Request a error message be shown to the user.
@@ -231,7 +231,7 @@ private:
int m_badgeNotificationCount = 0;
bool m_globalUrlPreviewEnabled = true;
QCache<QUrl, LinkPreviewer> m_linkPreviewers;
// QCache<QUrl, LinkPreviewer> m_linkPreviewers;
bool m_canCheckMutualRooms = false;
bool m_canEraseData = false;

File diff suppressed because it is too large Load Diff

View File

@@ -3,8 +3,9 @@
#pragma once
#include <Quotient/events/roomevent.h>
#include <Quotient/room.h>
// #include <Quotient/events/roomevent.h>
// #include <Quotient/room.h>
#include <Integral/Room>
#include <QCache>
#include <QObject>
@@ -13,12 +14,12 @@
#include <QCoroTask>
#include <Quotient/roommember.h>
#include "enums/messagetype.h"
#include "enums/pushrule.h"
#include "models/messagecontentmodel.h"
#include "models/threadmodel.h"
#include "neochatroommember.h"
#include "pollhandler.h"
// #include "enums/messagetype.h"
// #include "models/messagecontentmodel.h"
// #include "models/threadmodel.h"
// #include "neochatroommember.h"
// #include "pollhandler.h"
namespace Quotient
{
@@ -39,7 +40,7 @@ class ChatBarCache;
*
* @sa Quotient::Room
*/
class NeoChatRoom : public Quotient::Room
class NeoChatRoom : public Integral::Room
{
Q_OBJECT
QML_ELEMENT
@@ -79,7 +80,7 @@ class NeoChatRoom : public Quotient::Room
/**
* @brief Get a RoomMember object for the other person in a direct chat.
*/
Q_PROPERTY(NeochatRoomMember *directChatRemoteMember READ directChatRemoteMember CONSTANT)
// Q_PROPERTY(NeochatRoomMember *directChatRemoteMember READ directChatRemoteMember CONSTANT)
/**
* @brief The Matrix IDs of this room's parents.
@@ -183,20 +184,20 @@ class NeoChatRoom : public Quotient::Room
/**
* @brief The cache for the main chat bar in the room.
*/
Q_PROPERTY(ChatBarCache *mainCache READ mainCache CONSTANT)
// Q_PROPERTY(ChatBarCache *mainCache READ mainCache CONSTANT)
/**
* @brief The cache for the edit chat bar in the room.
*/
Q_PROPERTY(ChatBarCache *editCache READ editCache CONSTANT)
// Q_PROPERTY(ChatBarCache *editCache READ editCache CONSTANT)
/**
* @brief The cache for the thread chat bar in the room.
*/
Q_PROPERTY(ChatBarCache *threadCache READ threadCache CONSTANT)
// Q_PROPERTY(ChatBarCache *threadCache READ threadCache CONSTANT)
public:
explicit NeoChatRoom(Quotient::Connection *connection, QString roomId, Quotient::JoinState joinState = {});
explicit NeoChatRoom(std::unique_ptr<Private> d, QObject *parent = nullptr);
bool visible() const;
void setVisible(bool visible);
@@ -212,7 +213,7 @@ public:
* @warning This function can return an empty pointer if the room does not have
* any RoomMessageEvents loaded.
*/
[[nodiscard]] const Quotient::RoomEvent *lastEvent() const;
// [[nodiscard]] const Quotient::RoomEvent *lastEvent() const;
/**
* @brief Convenient way to check if the last event looks like it has spoilers.
@@ -259,7 +260,7 @@ public:
* An event is highlighted if it contains the local user's id but was not sent by the
* local user.
*/
bool isEventHighlighted(const Quotient::RoomEvent *e) const;
// bool isEventHighlighted(const Quotient::RoomEvent *e) const;
/**
* @brief Convenience function to find out if the room contains the given user.
@@ -312,7 +313,7 @@ public:
[[nodiscard]] QUrl avatarMediaUrl() const;
NeochatRoomMember *directChatRemoteMember();
// NeochatRoomMember *directChatRemoteMember();
/**
* @brief Whether this room has one or more parent spaces set.
@@ -501,14 +502,14 @@ public:
*
* @sa PollHandler
*/
PollHandler *poll(const QString &eventId) const;
// PollHandler *poll(const QString &eventId) const;
/**
* @brief Create a PollHandler object for the given event.
*
* @sa PollHandler
*/
void createPollHandler(const Quotient::PollStartEvent *event);
// void createPollHandler(const Quotient::PollStartEvent *event);
/**
* @brief Get the full Json data for a given room account data event.
@@ -533,12 +534,12 @@ public:
*
* The result will be nullptr if not found so needs to be managed.
*/
std::pair<const Quotient::RoomEvent *, bool> getEvent(const QString &eventId) const;
// std::pair<const Quotient::RoomEvent *, bool> getEvent(const QString &eventId) const;
/**
* @brief Returns the event that is being replied to. This includes events that were manually loaded using NeoChatRoom::loadReply.
*/
const Quotient::RoomEvent *getReplyForEvent(const Quotient::RoomEvent &event) const;
// const Quotient::RoomEvent *getReplyForEvent(const Quotient::RoomEvent &event) const;
/**
* If we're invited to this room, the user that invited us. Undefined in other cases.
@@ -551,7 +552,7 @@ public:
* If we downloaded the file previously, return a struct with Completed status
* and the local file path stored in KSharedCOnfig
*/
Quotient::FileTransferInfo cachedFileTransferInfo(const Quotient::RoomEvent *event) const;
// Quotient::FileTransferInfo cachedFileTransferInfo(const Quotient::RoomEvent *event) const;
/**
* @brief Return a NeochatRoomMember object for the given user ID.
@@ -562,7 +563,7 @@ public:
* responsibility of the caller to ensure that they only ask for objects
* for real senders.
*/
NeochatRoomMember *qmlSafeMember(const QString &memberId);
// NeochatRoomMember *qmlSafeMember(const QString &memberId);
/**
* @brief Returns the content model for the given event ID.
@@ -576,7 +577,7 @@ public:
*
* @warning Do NOT use for pending events as this function has no way to differentiate.
*/
MessageContentModel *contentModelForEvent(const QString &evtOrTxnId);
// MessageContentModel *contentModelForEvent(const QString &evtOrTxnId);
/**
* @brief Returns the content model for the given event.
@@ -591,7 +592,7 @@ public:
*
* @note This version must be used for pending events as it can differentiate.
*/
MessageContentModel *contentModelForEvent(const Quotient::RoomEvent *event);
// MessageContentModel *contentModelForEvent(const Quotient::RoomEvent *event);
/**
* @brief Returns the thread model for the given thread root event ID.
@@ -599,7 +600,7 @@ public:
* A model is created is one doesn't exist. Will return nullptr if threadRootId
* is empty.
*/
Q_INVOKABLE ThreadModel *modelForThread(const QString &threadRootId);
// Q_INVOKABLE ThreadModel *modelForThread(const QString &threadRootId);
/**
* @brief Pin a message in the room.
@@ -621,7 +622,7 @@ public:
private:
bool m_visible = false;
QSet<const Quotient::RoomEvent *> highlights;
// QSet<const Quotient::RoomEvent *> highlights;
bool m_hasFileUploading = false;
int m_fileUploadingProgress = 0;
@@ -629,29 +630,29 @@ private:
PushNotificationState::State m_currentPushNotificationState = PushNotificationState::Unknown;
bool m_pushNotificationStateUpdating = false;
void checkForHighlights(const Quotient::TimelineItem &ti);
// void checkForHighlights(const Quotient::TimelineItem &ti);
void onAddNewTimelineEvents(timeline_iter_t from) override;
void onAddHistoricalTimelineEvents(rev_iter_t from) override;
void onRedaction(const Quotient::RoomEvent &prevEvent, const Quotient::RoomEvent &after) override;
// void onAddNewTimelineEvents(timeline_iter_t from) override;
// void onAddHistoricalTimelineEvents(rev_iter_t from) override;
// void onRedaction(const Quotient::RoomEvent &prevEvent, const Quotient::RoomEvent &after) override;
QCoro::Task<void> doDeleteMessagesByUser(const QString &user, QString reason);
// QCoro::Task<void> doDeleteMessagesByUser(const QString &user, QString reason);
QCoro::Task<void> doUploadFile(QUrl url, QString body = QString());
std::unique_ptr<Quotient::RoomEvent> m_cachedEvent;
// std::unique_ptr<Quotient::RoomEvent> m_cachedEvent;
ChatBarCache *m_mainCache;
ChatBarCache *m_editCache;
ChatBarCache *m_threadCache;
QCache<QString, PollHandler> m_polls;
std::vector<Quotient::event_ptr_tt<Quotient::RoomEvent>> m_extraEvents;
void cleanupExtraEventRange(Quotient::RoomEventsRange events);
// QCache<QString, PollHandler> m_polls;
// std::vector<Quotient::event_ptr_tt<Quotient::RoomEvent>> m_extraEvents;
// void cleanupExtraEventRange(Quotient::RoomEventsRange events);
void cleanupExtraEvent(const QString &eventId);
std::unordered_map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
std::unordered_map<QString, std::unique_ptr<MessageContentModel>> m_eventContentModels;
std::unordered_map<QString, std::unique_ptr<ThreadModel>> m_threadModels;
// std::unordered_map<QString, std::unique_ptr<NeochatRoomMember>> m_memberObjects;
// std::unordered_map<QString, std::unique_ptr<MessageContentModel>> m_eventContentModels;
// std::unordered_map<QString, std::unique_ptr<ThreadModel>> m_threadModels;
private Q_SLOTS:
void updatePushNotificationState(QString type);
@@ -681,11 +682,12 @@ Q_SIGNALS:
void maxRoomVersionChanged();
void extraEventLoaded(const QString &eventId);
void extraEventNotFound(const QString &eventId);
void avatarChanged();
/**
* @brief Request a message be shown to the user of the given type.
*/
void showMessage(MessageType::Type messageType, const QString &message);
// void showMessage(MessageType::Type messageType, const QString &message);
public Q_SLOTS:
/**

View File

@@ -75,7 +75,7 @@ Kirigami.Dialog {
}
}
clip: true
model: AccountRegistry
model: Accounts
keyNavigationEnabled: false
Keys.onDownPressed: {

View File

@@ -23,8 +23,8 @@ Kirigami.ApplicationWindow {
title: {
if (NeoChatConfig.windowTitleFocus) {
return activeFocusItem + " " + (activeFocusItem ? activeFocusItem.Accessible.name : "");
} else if (RoomManager.currentRoom) {
return RoomManager.currentRoom.displayName;
// } else if (RoomManager.currentRoom) {
// return RoomManager.currentRoom.displayName;
} else {
return Application.displayName;
}
@@ -49,20 +49,20 @@ Kirigami.ApplicationWindow {
}
onConnectionChanged: {
CustomEmojiModel.connection = root.connection;
SpaceHierarchyCache.connection = root.connection;
// CustomEmojiModel.connection = root.connection;
// SpaceHierarchyCache.connection = root.connection;
NeoChatSettingsView.connection = root.connection;
if (ShareHandler.text && root.connection) {
root.handleShare();
}
// if (ShareHandler.text && root.connection) {
// root.handleShare();
// }
}
Connections {
target: LoginHelper
function onLoaded() {
root.load();
}
}
// Connections {
// target: LoginHelper
// function onLoaded() {
// root.load();
// }
// }
Connections {
target: root.quitAction
@@ -83,64 +83,64 @@ Kirigami.ApplicationWindow {
configGroupName: "MainWindow"
}
QuickSwitcher {
id: quickSwitcher
connection: root.connection
}
Connections {
target: RoomManager
function onCurrentRoomChanged() {
if (RoomManager.currentRoom && pageStack.depth <= 1 && root.initialized && Kirigami.Settings.isMobile) {
let roomPage = pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'RoomPage'), {
connection: root.connection
});
roomPage.backRequested.connect(event => {
RoomManager.clearCurrentRoom();
});
}
}
function onAskJoinRoom(room) {
Qt.createComponent("org.kde.neochat", "JoinRoomDialog").createObject(root, {
room: room,
connection: root.connection
}).open();
}
function onShowUserDetail(user, room) {
root.showUserDetail(user, room);
}
function goToEvent(event) {
if (event.length > 0) {
roomItem.goToEvent(event);
}
roomItem.forceActiveFocus();
}
function onAskDirectChatConfirmation(user) {
Qt.createComponent("org.kde.neochat", "AskDirectChatConfirmation").createObject(this, {
user: user
}).open();
}
function onExternalUrl(url) {
let dialog = Qt.createComponent("org.kde.neochat", "ConfirmUrlDialog").createObject(this);
dialog.link = url;
dialog.open();
}
}
function pushReplaceLayer(page, args) {
if (pageStack.layers.depth === 2) {
pageStack.layers.replace(page, args);
} else {
pageStack.layers.push(page, args);
}
}
// QuickSwitcher {
// id: quickSwitcher
// connection: root.connection
// }
// Connections {
// target: RoomManager
//
// function onCurrentRoomChanged() {
// if (RoomManager.currentRoom && pageStack.depth <= 1 && root.initialized && Kirigami.Settings.isMobile) {
// let roomPage = pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'RoomPage'), {
// connection: root.connection
// });
// roomPage.backRequested.connect(event => {
// RoomManager.clearCurrentRoom();
// });
// }
// }
//
// function onAskJoinRoom(room) {
// Qt.createComponent("org.kde.neochat", "JoinRoomDialog").createObject(root, {
// room: room,
// connection: root.connection
// }).open();
// }
//
// function onShowUserDetail(user, room) {
// root.showUserDetail(user, room);
// }
//
// function goToEvent(event) {
// if (event.length > 0) {
// roomItem.goToEvent(event);
// }
// roomItem.forceActiveFocus();
// }
//
// function onAskDirectChatConfirmation(user) {
// Qt.createComponent("org.kde.neochat", "AskDirectChatConfirmation").createObject(this, {
// user: user
// }).open();
// }
//
// function onExternalUrl(url) {
// let dialog = Qt.createComponent("org.kde.neochat", "ConfirmUrlDialog").createObject(this);
// dialog.link = url;
// dialog.open();
// }
// }
//
// function pushReplaceLayer(page, args) {
// if (pageStack.layers.depth === 2) {
// pageStack.layers.replace(page, args);
// } else {
// pageStack.layers.push(page, args);
// }
// }
//
function openRoomDrawer() {
pageStack.push(Qt.createComponent('org.kde.neochat', 'RoomDrawerPage'), {
connection: root.connection
@@ -183,15 +183,15 @@ Kirigami.ApplicationWindow {
}
Component.onCompleted: {
CustomEmojiModel.connection = root.connection;
SpaceHierarchyCache.connection = root.connection;
RoomSettingsView.window = root;
// CustomEmojiModel.connection = root.connection;
// SpaceHierarchyCache.connection = root.connection;
// RoomSettingsView.window = root;
NeoChatSettingsView.window = root;
NeoChatSettingsView.connection = root.connection;
WindowController.setBlur(pageStack, NeoChatConfig.blur && !NeoChatConfig.compactLayout);
if (ShareHandler.text && root.connection) {
root.handleShare()
}
// if (ShareHandler.text && root.connection) {
// root.handleShare()
// }
const hasSystemTray = Controller.supportSystemTray && NeoChatConfig.systemTray;
if (Kirigami.Settings.isMobile || !(hasSystemTray && NeoChatConfig.minimizeToSystemTrayOnStartup)) {
visible = true;
@@ -210,7 +210,7 @@ Kirigami.ApplicationWindow {
// blur effect
color: NeoChatConfig.blur && !NeoChatConfig.compactLayout ? "transparent" : Kirigami.Theme.backgroundColor
// we need to apply the translucency effect separately on top of the color
// // we need to apply the translucency effect separately on top of the color
background: Rectangle {
color: NeoChatConfig.blur && !NeoChatConfig.compactLayout ? Qt.rgba(Kirigami.Theme.backgroundColor.r, Kirigami.Theme.backgroundColor.g, Kirigami.Theme.backgroundColor.b, 1 - NeoChatConfig.transparency) : "transparent"
}
@@ -220,7 +220,7 @@ Kirigami.ApplicationWindow {
RoomListPage {
id: roomList
onSearch: quickSwitcher.open()
// onSearch: quickSwitcher.open()
connection: root.connection
@@ -255,9 +255,9 @@ Kirigami.ApplicationWindow {
}
Connections {
target: AccountRegistry
target: Accounts
function onRowsRemoved() {
if (AccountRegistry.rowCount() === 0) {
if (Accounts.rowCount() === 0) {
pageStack.clear();
pageStack.push(Qt.createComponent('org.kde.neochat.login', 'WelcomePage'));
}
@@ -272,22 +272,22 @@ Kirigami.ApplicationWindow {
}
}
Connections {
target: root.connection
function onNewKeyVerificationSession(session) {
root.pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "KeyVerificationDialog"), {
session: session
}, {
title: i18nc("@title:window", "Session Verification")
});
}
function onUserConsentRequired(url) {
Qt.createComponent("org.kde.neochat", "ConsentDialog").createObject(this, {
url: url
}).open();
}
}
// Connections {
// target: root.connection
//
// function onNewKeyVerificationSession(session) {
// root.pageStack.pushDialogLayer(Qt.createComponent("org.kde.neochat", "KeyVerificationDialog"), {
// session: session
// }, {
// title: i18nc("@title:window", "Session Verification")
// });
// }
// function onUserConsentRequired(url) {
// Qt.createComponent("org.kde.neochat", "ConsentDialog").createObject(this, {
// url: url
// }).open();
// }
// }
HoverLinkIndicator {
id: linkIndicator
@@ -303,36 +303,36 @@ Kirigami.ApplicationWindow {
}
}
Connections {
target: ShareHandler
function onTextChanged(): void {
if (root.connection && ShareHandler.text.length > 0) {
root.handleShare();
}
}
}
function handleShare(): void {
const dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
connection: root.connection
}, {
title: i18nc("@title", "Share"),
width: Kirigami.Units.gridUnit * 25
})
dialog.chosen.connect(function(targetRoomId) {
RoomManager.resolveResource(targetRoomId)
ShareHandler.room = targetRoomId
dialog.closeDialog()
})
}
function showUserDetail(user, room) {
const dialog = Qt.createComponent("org.kde.neochat", "UserDetailDialog").createObject(root, {
room: room,
user: user,
connection: root.connection,
});
dialog.parent = QmlUtils.focusedWindowItem(); // Kirigami Dialogs overwrite the parent, so we need to set it again
dialog.open();
}
// Connections {
// target: ShareHandler
// function onTextChanged(): void {
// if (root.connection && ShareHandler.text.length > 0) {
// root.handleShare();
// }
// }
// }
// function handleShare(): void {
// const dialog = applicationWindow().pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'ChooseRoomDialog'), {
// connection: root.connection
// }, {
// title: i18nc("@title", "Share"),
// width: Kirigami.Units.gridUnit * 25
// })
// dialog.chosen.connect(function(targetRoomId) {
// RoomManager.resolveResource(targetRoomId)
// ShareHandler.room = targetRoomId
// dialog.closeDialog()
// })
// }
// function showUserDetail(user, room) {
// const dialog = Qt.createComponent("org.kde.neochat", "UserDetailDialog").createObject(root, {
// room: room,
// user: user,
// connection: root.connection,
// });
// dialog.parent = QmlUtils.focusedWindowItem(); // Kirigami Dialogs overwrite the parent, so we need to set it again
// dialog.open();
// }
function load() {
pageStack.replace(roomListComponent);

View File

@@ -19,7 +19,7 @@ Delegates.RoundedItemDelegate {
required property int index
required property int contextNotificationCount
required property bool hasHighlightNotifications
required property NeoChatRoom currentRoom
required property string roomId
required property NeoChatConnection connection
required property url avatar
required property string subtitleText
@@ -37,11 +37,13 @@ Delegates.RoundedItemDelegate {
onClicked: {
if (root.openOnClick) {
RoomManager.resolveResource(currentRoom.id);
root.connection.open(root.roomId)
pageStack.currentIndex = 1;
}
}
onPressAndHold: createRoomListContextMenu()
Keys.onSpacePressed: clicked()
Keys.onEnterPressed: clicked()
Keys.onReturnPressed: clicked()
@@ -52,19 +54,14 @@ Delegates.RoundedItemDelegate {
onTapped: (eventPoint, button) => root.createRoomListContextMenu()
}
TapHandler {
acceptedDevices: PointerDevice.TouchScreen
onLongPressed: root.createRoomListContextMenu()
}
contentItem: RowLayout {
spacing: Kirigami.Units.largeSpacing
AvatarNotification {
source: root.avatar
name: root.displayName
visible: NeoChatConfig.showAvatarInRoomDrawer
implicitHeight: Kirigami.Units.gridUnit + (NeoChatConfig.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2)
// visible: NeoChatConfig.showAvatarInRoomDrawer
implicitHeight: Kirigami.Units.gridUnit + Kirigami.Units.largeSpacing * 2 // (NeoChatConfig.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2)
implicitWidth: visible ? implicitHeight : 0
notificationCount: root.contextNotificationCount
@@ -101,7 +98,7 @@ Delegates.RoundedItemDelegate {
elide: Text.ElideRight
font: Kirigami.Theme.smallFont
opacity: root.hasNotifications ? 0.9 : 0.7
visible: !NeoChatConfig.compactRoomList && text.length > 0
// visible: !NeoChatConfig.compactRoomList && text.length > 0
textFormat: Text.PlainText
Layout.fillWidth: true
@@ -147,7 +144,7 @@ Delegates.RoundedItemDelegate {
QQC2.Button {
id: configButton
visible: root.hovered && !Kirigami.Settings.isMobile && !NeoChatConfig.compactRoomList && !root.collapsed && root.showConfigure
visible: root.hovered && !Kirigami.Settings.isMobile /*&& !NeoChatConfig.compactRoomList*/ && !root.collapsed && root.showConfigure
text: i18n("Configure room")
display: QQC2.Button.IconOnly

View File

@@ -1,295 +1,117 @@
// SPDX-FileCopyrightText: 2019 Black Hat <bhat@encom.eu.org>
// SPDX-FileCopyrightText: 2020 Carl Schwan <carl@carlschwan.eu>
// SPDX-License-Identifier: GPL-3.0-only
// SPDX-FileCopyrightText: 2025 Tobias Fella <tobias.fella@kde.org>
// SPDX-License-Identifier: GPL-2.0-or-later
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import QtQml.Models
import Qt.labs.qmlmodels
import org.kde.kirigami as Kirigami
import org.kde.kirigamiaddons.components as KirigamiComponents
import org.kde.kirigamiaddons.formcard as FormCard
import org.kde.kirigamiaddons.delegates as Delegates
import org.kde.neochat
Kirigami.Page {
Kirigami.ScrollablePage {
id: root
/**
* @brief The current width of the room list.
*
* @note Other objects can access the value but the private function makes sure
* that only the internal members can modify it.
*/
readonly property int currentWidth: _private.currentWidth + spaceDrawer.width + 1
title: i18nc("@title", "Rooms")
required property NeoChatConnection connection
readonly property bool collapsed: NeoChatConfig.collapsed
signal search
onCurrentWidthChanged: pageStack.defaultColumnWidth = root.currentWidth
Component.onCompleted: pageStack.defaultColumnWidth = root.currentWidth
onCollapsedChanged: {
if (collapsed) {
RoomManager.sortFilterRoomTreeModel.filterText = "";
actions: [
Kirigami.Action {
text: i18nc("@action:button", "Log out")
onTriggered: root.connection.logout()
},
Kirigami.Action {
text: i18nc("@action:button", "Create Room")
onTriggered: root.connection.createRoom("Hello", "World", "")
}
}
]
function goToNextRoomFiltered(condition) {
let index = treeView.rowAtIndex(RoomManager.sortFilterRoomTreeModel.currentRoomIndex());
while (index++ < treeView.rows) {
let item = treeView.itemAtIndex(treeView.index(index, 0))
if (condition(item)) {
RoomManager.resolveResource(item.currentRoom.id)
return;
}
Connections {
target: root.connection
function onOpenRoom(): void {
room => pageStack.push(Qt.createComponent("org.kde.neochat", "RoomPage"), {
room: room,
connection: connection,
});
}
}
function goToPreviousRoomFiltered(condition) {
let index = treeView.rowAtIndex(RoomManager.sortFilterRoomTreeModel.currentRoomIndex());
while (index-- > 0) {
let item = treeView.itemAtIndex(treeView.index(index, 0))
if (condition(item)) {
RoomManager.resolveResource(item.currentRoom.id)
return;
}
}
}
function goToNextRoom() {
goToNextRoomFiltered(item => (item && item instanceof RoomDelegate));
}
function goToPreviousRoom() {
goToPreviousRoomFiltered(item => (item && item instanceof RoomDelegate));
}
function goToNextUnreadRoom() {
goToNextRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnread));
}
function goToPreviousUnreadRoom() {
goToPreviousRoomFiltered(item => (item && item instanceof RoomDelegate && item.hasUnread));
}
titleDelegate: Loader {
Layout.fillWidth: true
sourceComponent: Kirigami.Settings.isMobile ? userInfo : exploreComponent
}
padding: 0
Connections {
target: RoomManager
function onCurrentSpaceChanged() {
treeView.expandRecursively();
}
function onCurrentRoomChanged() {
treeView.positionViewAtIndex(RoomManager.sortFilterRoomTreeModel.currentRoomIndex(), TableView.AlignVCenter)
}
}
RowLayout {
anchors.fill: parent
spacing: 0
SpaceDrawer {
id: spaceDrawer
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
Layout.fillHeight: true
connection: root.connection
}
Kirigami.Separator {
Layout.fillHeight: true
Layout.preferredWidth: 1
}
QQC2.ScrollView {
id: scrollView
Layout.fillWidth: true
Layout.fillHeight: true
background: Rectangle {
color: Kirigami.Theme.backgroundColor
Kirigami.Theme.colorSet: Kirigami.Theme.View
}
Keys.onDownPressed: ; // Do not delete 🫠
Keys.onUpPressed: ; // These make sure the scrollview doesn't also scroll while going through the roomlist using the arrow keys
contentItem: TreeView {
id: treeView
topMargin: Math.round(Kirigami.Units.smallSpacing / 2)
clip: true
reuseItems: false
model: RoomManager.sortFilterRoomTreeModel
selectionModel: ItemSelectionModel {}
delegate: DelegateChooser {
role: "delegateType"
DelegateChoice {
roleValue: "section"
delegate: RoomTreeSection {
collapsed: root.collapsed
}
}
DelegateChoice {
roleValue: "normal"
delegate: RoomDelegate {
id: roomDelegate
required property int row
required property TreeView treeView
required property bool current
onCurrentChanged: if (current) {
forceActiveFocus(Qt.TabFocusReason);
}
implicitWidth: treeView.width
connection: root.connection
collapsed: root.collapsed
highlighted: RoomManager.currentRoom === currentRoom
}
}
DelegateChoice {
roleValue: "addDirect"
delegate: Delegates.RoundedItemDelegate {
text: i18n("Find your friends")
icon.name: "list-add-user"
icon.width: Kirigami.Units.gridUnit * 2
icon.height: Kirigami.Units.gridUnit * 2
onClicked: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Find your friends")
})
}
}
}
}
}
}
Kirigami.PlaceholderMessage {
anchors.centerIn: parent
anchors.horizontalCenterOffset: (spaceDrawer.width + 1) / 2
width: scrollView.width - Kirigami.Units.largeSpacing * 4
visible: treeView.rows == 0
text: if (RoomManager.sortFilterRoomTreeModel.filterText.length > 0) {
return spaceDrawer.showDirectChats ? i18n("No friends found") : i18n("No rooms found");
} else {
return spaceDrawer.showDirectChats ? i18n("You haven't added any of your friends yet, click below to search for them.") : i18n("Join some rooms to get started");
}
helpfulAction: spaceDrawer.showDirectChats ? userSearchAction : exploreRoomAction
Kirigami.Action {
id: exploreRoomAction
icon.name: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? "search" : "list-add"
text: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? i18n("Search in room directory") : i18n("Explore rooms")
onTriggered: {
let dialog = pageStack.layers.push(Qt.createComponent('org.kde.neochat', 'ExploreRoomsPage'), {
connection: root.connection,
keyword: RoomManager.sortFilterRoomTreeModel.filterText
}, {
title: i18nc("@title", "Explore Rooms")
});
dialog.roomSelected.connect((roomId, displayName, avatarUrl, alias, topic, memberCount, isJoined) => {
RoomManager.resolveResource(roomId.length > 0 ? roomId : alias, isJoined ? "" : "join");
});
}
}
Kirigami.Action {
id: userSearchAction
icon.name: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? "search" : "list-add"
text: RoomManager.sortFilterRoomTreeModel.filterText.length > 0 ? i18n("Search in friend directory") : i18n("Find your friends")
onTriggered: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Find your friends")
})
}
}
footer: Loader {
width: parent.width
sourceComponent: Kirigami.Settings.isMobile ? exploreComponentMobile : userInfoDesktop
}
MouseArea {
anchors.top: parent.top
anchors.bottom: parent.bottom
parent: applicationWindow().overlay.parent
TreeView {
id: treeView
topMargin: Math.round(Kirigami.Units.smallSpacing / 2)
x: root.currentWidth - width / 2
width: Kirigami.Units.smallSpacing * 2
z: root.z + 1
enabled: RoomManager.hasOpenRoom && applicationWindow().width >= Kirigami.Units.gridUnit * 35
visible: enabled
cursorShape: Qt.SplitHCursor
clip: true
reuseItems: false
property int _lastX
onPressed: mouse => {
_lastX = mouse.x;
}
onPositionChanged: mouse => {
if (_lastX == -1) {
return;
model: SortFilterRoomTreeModel {
sourceModel: RoomTreeModel {
connection: root.connection
}
if (mouse.x > _lastX) {
// we moved to the right
if (_private.currentWidth < _private.collapseWidth && _private.currentWidth + (mouse.x - _lastX) >= _private.collapseWidth) {
// Here we get back directly to a more wide mode.
_private.currentWidth = _private.defaultWidth;
NeoChatConfig.collapsed = false;
} else if (_private.currentWidth >= _private.collapseWidth) {
// Increase page width
_private.currentWidth = Math.min(_private.defaultWidth, _private.currentWidth + (mouse.x - _lastX));
}
selectionModel: ItemSelectionModel {}
delegate: DelegateChooser {
role: "delegateType"
DelegateChoice {
roleValue: "section"
delegate: RoomTreeSection {
collapsed: root.collapsed
}
} else if (mouse.x < _lastX) {
const tmpWidth = _private.currentWidth - (_lastX - mouse.x);
if (tmpWidth < _private.collapseWidth) {
_private.currentWidth = Qt.binding(() => _private.collapsedSize);
NeoChatConfig.collapsed = true;
} else {
_private.currentWidth = tmpWidth;
}
DelegateChoice {
roleValue: "normal"
delegate: RoomDelegate {
id: roomDelegate
required property int row
required property TreeView treeView
required property bool current
onCurrentChanged: if (current) {
forceActiveFocus(Qt.TabFocusReason);
}
implicitWidth: treeView.width
connection: root.connection
collapsed: root.collapsed
highlighted: RoomManager.currentRoom === currentRoom
}
}
DelegateChoice {
roleValue: "addDirect"
delegate: Delegates.RoundedItemDelegate {
text: i18n("Find your friends")
icon.name: "list-add-user"
icon.width: Kirigami.Units.gridUnit * 2
icon.height: Kirigami.Units.gridUnit * 2
onClicked: pageStack.pushDialogLayer(Qt.createComponent('org.kde.neochat', 'UserSearchPage'), {
connection: root.connection
}, {
title: i18nc("@title", "Find your friends")
})
}
}
}
}
Component {
id: userInfo
UserInfo {
bottomEdge: false
connection: root.connection
}
}
Component {
id: userInfoDesktop
UserInfoDesktop {
connection: root.connection
collapsed: root.collapsed
}
}
Component {
id: exploreComponent
@@ -308,25 +130,10 @@ Kirigami.Page {
}
Component {
id: exploreComponentMobile
ExploreComponentMobile {
id: userInfoDesktop
UserInfoDesktop {
connection: root.connection
onTextChanged: newText => {
RoomManager.sortFilterRoomTreeModel.filterText = newText;
}
collapsed: root.collapsed
}
}
/*
* Hold the modifiable currentWidth in a private object so that only internal
* members can modify it.
*/
QtObject {
id: _private
property int currentWidth: NeoChatConfig.collapsed ? collapsedSize : defaultWidth
readonly property int defaultWidth: Kirigami.Units.gridUnit * 15
readonly property int collapseWidth: Kirigami.Units.gridUnit * 10
readonly property int collapsedSize: Kirigami.Units.gridUnit + (NeoChatConfig.compactRoomList ? 0 : Kirigami.Units.largeSpacing * 2) + Kirigami.Units.largeSpacing * 2 + (scrollView.QQC2.ScrollBar.vertical.visible ? scrollView.QQC2.ScrollBar.vertical.width : 0)
}
}

View File

@@ -38,32 +38,32 @@ QQC2.ScrollView {
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
ListView {
clip: true
verticalLayoutDirection: ListView.BottomToTop
model: RoomManager.mediaMessageFilterModel
delegate: DelegateChooser {
role: "type"
DelegateChoice {
roleValue: MediaMessageFilterModel.Image
delegate: MessageDelegate {
alwaysFillWidth: true
cardBackground: false
room: root.currentRoom
}
}
DelegateChoice {
roleValue: MediaMessageFilterModel.Video
delegate: MessageDelegate {
alwaysFillWidth: true
cardBackground: false
room: root.currentRoom
}
}
}
}
// ListView {
// clip: true
// verticalLayoutDirection: ListView.BottomToTop
//
// model: RoomManager.mediaMessageFilterModel
//
// delegate: DelegateChooser {
// role: "type"
//
// DelegateChoice {
// roleValue: MediaMessageFilterModel.Image
// delegate: MessageDelegate {
// alwaysFillWidth: true
// cardBackground: false
// room: root.currentRoom
// }
// }
//
// DelegateChoice {
// roleValue: MediaMessageFilterModel.Video
// delegate: MessageDelegate {
// alwaysFillWidth: true
// cardBackground: false
// room: root.currentRoom
// }
// }
// }
// }
}

View File

@@ -32,7 +32,7 @@ Kirigami.Page {
*
* @sa TimelineModel
*/
property TimelineModel timelineModel: RoomManager.timelineModel
// property TimelineModel timelineModel: RoomManager.timelineModel
/**
* @brief The MessageFilterModel to use.
@@ -44,7 +44,7 @@ Kirigami.Page {
*
* @sa TimelineModel, MessageFilterModel
*/
property MessageFilterModel messageFilterModel: RoomManager.messageFilterModel
// property MessageFilterModel messageFilterModel: RoomManager.messageFilterModel
/**
* @brief The MediaMessageFilterModel to use.
@@ -57,7 +57,7 @@ Kirigami.Page {
*
* @sa TimelineModel, MessageFilterModel
*/
property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
// property MediaMessageFilterModel mediaMessageFilterModel: RoomManager.mediaMessageFilterModel
property bool loading: !root.currentRoom || (root.currentRoom.timelineSize === 0 && !root.currentRoom.allHistoryLoaded)
@@ -106,58 +106,58 @@ Kirigami.Page {
position: Kirigami.InlineMessage.Position.Header
}
Loader {
id: timelineViewLoader
anchors.fill: parent
active: root.currentRoom && !root.currentRoom.isInvite && !root.loading && !root.currentRoom.isSpace
sourceComponent: TimelineView {
id: timelineView
currentRoom: root.currentRoom
page: root
timelineModel: root.timelineModel
messageFilterModel: root.messageFilterModel
onFocusChatBar: {
if (chatBarLoader.item) {
chatBarLoader.item.forceActiveFocus();
}
}
}
}
// Loader {
// id: timelineViewLoader
// anchors.fill: parent
// active: root.currentRoom && !root.currentRoom.isInvite && !root.loading && !root.currentRoom.isSpace
// sourceComponent: TimelineView {
// id: timelineView
// currentRoom: root.currentRoom
// page: root
// timelineModel: root.timelineModel
// messageFilterModel: root.messageFilterModel
// onFocusChatBar: {
// if (chatBarLoader.item) {
// chatBarLoader.item.forceActiveFocus();
// }
// }
// }
// }
Loader {
id: invitationLoader
active: root.currentRoom && root.currentRoom.isInvite
anchors.centerIn: parent
sourceComponent: InvitationView {
currentRoom: root.currentRoom
anchors.centerIn: parent
}
}
// Loader {
// id: invitationLoader
// active: root.currentRoom && root.currentRoom.isInvite
// anchors.centerIn: parent
// sourceComponent: InvitationView {
// currentRoom: root.currentRoom
// anchors.centerIn: parent
// }
// }
Loader {
id: spaceLoader
active: root.currentRoom && root.currentRoom.isSpace
anchors.fill: parent
sourceComponent: SpaceHomePage {}
}
// Loader {
// id: spaceLoader
// active: root.currentRoom && root.currentRoom.isSpace
// anchors.fill: parent
// sourceComponent: SpaceHomePage {}
// }
Loader {
active: !RoomManager.currentRoom
anchors.centerIn: parent
sourceComponent: Kirigami.PlaceholderMessage {
icon.name: "org.kde.neochat"
text: i18n("Welcome to NeoChat")
explanation: i18n("Select or join a room to get started")
}
}
// Loader {
// active: !RoomManager.currentRoom
// anchors.centerIn: parent
// sourceComponent: Kirigami.PlaceholderMessage {
// icon.name: "org.kde.neochat"
// text: i18n("Welcome to NeoChat")
// explanation: i18n("Select or join a room to get started")
// }
// }
Loader {
active: root.loading && !invitationLoader.active && RoomManager.currentRoom && !spaceLoader.active
anchors.centerIn: parent
sourceComponent: Kirigami.LoadingPlaceholder {
anchors.centerIn: parent
}
}
// Loader {
// active: root.loading && !invitationLoader.active && RoomManager.currentRoom && !spaceLoader.active
// anchors.centerIn: parent
// sourceComponent: Kirigami.LoadingPlaceholder {
// anchors.centerIn: parent
// }
// }
background: Rectangle {
Kirigami.Theme.colorSet: Kirigami.Theme.View
@@ -290,26 +290,26 @@ Kirigami.Page {
}
}
Component {
id: messageDelegateContextMenu
MessageDelegateContextMenu {
connection: root.connection
}
}
// Component {
// id: messageDelegateContextMenu
// MessageDelegateContextMenu {
// connection: root.connection
// }
// }
Component {
id: fileDelegateContextMenu
FileDelegateContextMenu {
connection: root.connection
}
}
// Component {
// id: fileDelegateContextMenu
// FileDelegateContextMenu {
// connection: root.connection
// }
// }
Component {
id: maximizeComponent
NeochatMaximizeComponent {
currentRoom: root.currentRoom
model: root.mediaMessageFilterModel
parent: root.QQC2.Overlay.overlay
}
}
// Component {
// id: maximizeComponent
// NeochatMaximizeComponent {
// currentRoom: root.currentRoom
// model: root.mediaMessageFilterModel
// parent: root.QQC2.Overlay.overlay
// }
// }
}

View File

@@ -47,7 +47,7 @@ QQC2.ItemDelegate {
opacity: 0.7
level: 5
type: Kirigami.Heading.Primary
text: root.collapsed ? "" : model.displayName
text: root.collapsed ? "" : root.displayName
elide: Text.ElideRight
// we override the Primary type's font weight (DemiBold) for Bold for contrast with small text

View File

@@ -54,7 +54,7 @@ RowLayout {
spacing: Kirigami.Units.largeSpacing
KirigamiComponents.Avatar {
readonly property url avatarUrl: root.connection.localUser.avatarUrl
readonly property url avatarUrl: root.connection.avatarUrl
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
@@ -62,7 +62,7 @@ RowLayout {
// Note: User::avatarUrl does not set user_id, and thus cannot be used directly here. Hence the makeMediaUrl.
source: avatarUrl.toString().length > 0 ? root.connection.makeMediaUrl(avatarUrl) : ""
name: root.connection.localUser.displayName
name: root.connection.displayName
}
ColumnLayout {
@@ -73,14 +73,14 @@ RowLayout {
QQC2.Label {
id: displayNameLabel
Layout.fillWidth: true
text: root.connection.localUser.displayName
text: root.connection.displayName
textFormat: Text.PlainText
elide: Text.ElideRight
}
QQC2.Label {
id: idLabel
Layout.fillWidth: true
text: (root.connection.label.length > 0 ? (root.connection.label + " ") : "") + root.connection.localUser.id
text: root.connection.matrixId
font.pointSize: displayNameLabel.font.pointSize * 0.8
opacity: 0.7
textFormat: Text.PlainText

View File

@@ -7,9 +7,9 @@
#include <KSharedConfig>
#include <QObject>
#include <QQmlEngine>
#include <Quotient/room.h>
#include <Quotient/roommember.h>
#include <Quotient/uriresolver.h>
// #include <Quotient/room.h>
// #include <Quotient/roommember.h>
// #include <Quotient/uriresolver.h>
#include "chatdocumenthandler.h"
#include "enums/messagecomponenttype.h"

View File

@@ -4,7 +4,7 @@
qt_add_library(settings STATIC)
set_source_files_properties(
RoomSettingsView.qml
# RoomSettingsView.qml
NeoChatSettingsView.qml
PROPERTIES
QT_QML_SINGLETON_TYPE TRUE
@@ -15,33 +15,33 @@ ecm_add_qml_module(settings GENERATE_PLUGIN_SOURCE
OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/src/org/kde/neochat/settings
QML_FILES
NeoChatSettingsView.qml
RoomSettingsView.qml
AccountsPage.qml
AccountEditorPage.qml
# RoomSettingsView.qml
# AccountsPage.qml
# AccountEditorPage.qml
AppearanceSettingsPage.qml
DevicesPage.qml
EmoticonsPage.qml
EmoticonEditorPage.qml
# DevicesPage.qml
# EmoticonsPage.qml
# EmoticonEditorPage.qml
GlobalNotificationsPage.qml
NeoChatGeneralPage.qml
NeoChatSecurityPage.qml
NetworkProxyPage.qml
Permissions.qml
PushNotification.qml
RoomGeneralPage.qml
RoomSecurityPage.qml
ColorScheme.qml
DevicesCard.qml
DeviceDelegate.qml
EmoticonFormCard.qml
IdentityServerDelegate.qml
IgnoredUsersDialog.qml
# Permissions.qml
# PushNotification.qml
# RoomGeneralPage.qml
# RoomSecurityPage.qml
# ColorScheme.qml
# DevicesCard.qml
# DeviceDelegate.qml
# EmoticonFormCard.qml
# IdentityServerDelegate.qml
# IgnoredUsersDialog.qml
NotificationRuleItem.qml
PasswordSheet.qml
# PasswordSheet.qml
ThemeRadioButton.qml
ThreePIdCard.qml
ImportKeysDialog.qml
ExportKeysDialog.qml
RoomSortParameterDialog.qml
RoomProfile.qml
# ThreePIdCard.qml
# ImportKeysDialog.qml
# ExportKeysDialog.qml
# RoomSortParameterDialog.qml
# RoomProfile.qml
)

View File

@@ -10,13 +10,9 @@
#include <unicode/urename.h>
#endif
#include <Quotient/connection.h>
#include <QJsonDocument>
#include <QQuickWindow>
using namespace Quotient;
bool QmlUtils::isEmoji(const QString &text)
{
return Utils::isEmoji(text);
@@ -48,10 +44,10 @@ QQuickItem *QmlUtils::focusedWindowItem()
}
}
QString QmlUtils::nameForPowerLevelValue(const int value)
{
return PowerLevel::nameForLevel(PowerLevel::levelForValue(value));
}
// QString QmlUtils::nameForPowerLevelValue(const int value)
// {
// return PowerLevel::nameForLevel(PowerLevel::levelForValue(value));
// }
bool Utils::isEmoji(const QString &text)
{

View File

@@ -10,9 +10,7 @@
#include <QQuickItem>
#include <QRegularExpression>
#include <Quotient/user.h>
#include "enums/powerlevel.h"
// #include "enums/powerlevel.h"
using namespace Qt::StringLiterals;
@@ -43,7 +41,7 @@ public:
/**
* @brief Invokable version of PowerLevel::nameForLevel which also calls PowerLevel::levelForValue.
*/
Q_INVOKABLE QString nameForPowerLevelValue(int value);
// Q_INVOKABLE QString nameForPowerLevelValue(int value);
private:
QmlUtils() = default;