diff --git a/src/apps/qml/RoomsComponent.qml b/src/apps/qml/RoomsComponent.qml index 280d0e8e..4daea5c6 100644 --- a/src/apps/qml/RoomsComponent.qml +++ b/src/apps/qml/RoomsComponent.qml @@ -1,176 +1,184 @@ /* * Copyright 2016 Riccardo Iaconelli * Copyright (C) 2017-2019 Laurent Montel * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ import QtQuick 2.9 import QtQuick.Window 2.2 import QtQuick.Dialogs 1.2 import QtQuick.Layouts 1.12 import QtQuick.Controls 2.5 as QQC2 import KDE.Ruqola.RocketChatAccount 1.0 import KDE.Ruqola.Ruqola 1.0 import KDE.Ruqola.RoomFilterProxyModel 1.0 import KDE.Ruqola.UsersForRoomFilterProxyModel 1.0 import org.kde.kirigami 2.7 as Kirigami import KDE.Ruqola.StatusModel 1.0 Component { id: roomsComponent Kirigami.ScrollablePage { id: roomsPage title: i18n("Rooms") actions { contextualActions: [ Kirigami.Action { id: editAction iconName: "list-add" text: i18n("Open room"); onTriggered: { searchChannelDialog.initializeAndOpen(); } }, + Kirigami.Action { + iconName: "edit-symbolic" + text: i18n("Unread on Top"); + checkable: true + onToggled: { + appid.rocketChatAccount.setSortUnreadOnTop(checked); + } + }, Kirigami.Action { iconName: "edit-symbolic" text: i18n("Show Close Icons"); checkable: true onToggled: { appid.rocketChatAccount.switchEditingMode(checked); } }, Kirigami.Action { text: i18n("Create New Channel") onTriggered: { createNewChannelDialogLoader.active = true; } }, Kirigami.Action { separator: true }, Kirigami.Action { text: i18n("Server Info") onTriggered: { serverinfodialogLoader.active = true; } } ] } // Since we can't have actions at the bottom on mobile, force always toolbar mode globalToolBarStyle: Kirigami.ApplicationHeaderStyle.ToolBar titleDelegate: LineEditWithClearButton { id: searchField placeholderText: i18n("Search Room...") Layout.fillWidth: true onTextChanged: { appid.rocketChatAccount.roomFilterProxyModel().setFilterString(text); } } background: Rectangle { color: Kirigami.Theme.backgroundColor } footer: QQC2.ToolBar { Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.Window position: QQC2.ToolBar.Footer RowLayout { anchors { verticalCenter: parent.verticalCenter left: parent.left } QQC2.Label { id: comboboxLabel text: i18n("Status:") } QQC2.ComboBox { id: statusCombobox Layout.alignment: Qt.AlignLeft model: appid.rocketChatAccount.statusModel() //textRole is removed as a workaround for now to draw our own text //textRole: "statusi18n" property variant icon property string text onActivated: { appid.rocketChatAccount.changeDefaultStatus(index) } currentIndex: model.currentStatus delegate: Kirigami.BasicListItem { property bool current: index === statusCombobox.currentIndex separatorVisible: false onCurrentChanged: { if (current) { statusCombobox.text = model.statusi18n statusCombobox.icon = model.icon } } icon: model.icon label: model.statusi18n } //FIXME: QQC2 combobox really, really needs icons support contentItem: RowLayout { Kirigami.Icon { Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium Layout.fillHeight: true source: statusCombobox.icon } QQC2.Label { text: statusCombobox.text verticalAlignment: Text.AlignVCenter Layout.fillHeight: true } Item { Layout.fillWidth: true } } } } } mainItem: RoomsView { id: roomsList editingMode: appid.rocketChatAccount.editingMode implicitWidth: Kirigami.Units.gridUnit * 10 anchors.fill: parent model: appid.rocketChatAccount.roomFilterProxyModel() selectedRoomID: appid.selectedRoomID; onHideRoom: { rocketChatAccount.hideRoom(roomID, roomType) } onLeaveRoom: { //TODO move to desktop.qml leaveChannelDialog.rId = roomID leaveChannelDialog.channelType = roomType leaveChannelDialog.open() } onRoomSelected: { appid.switchToRoom(roomID) } } //RoomsView } } diff --git a/src/ruqolacore/model/roommodel.cpp b/src/ruqolacore/model/roommodel.cpp index 5fe3fa43..c87972bf 100644 --- a/src/ruqolacore/model/roommodel.cpp +++ b/src/ruqolacore/model/roommodel.cpp @@ -1,531 +1,550 @@ /* * Copyright 2016 Riccardo Iaconelli * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License or (at your option) version 3 or any later version * accepted by the membership of KDE e.V. (or its successor approved * by the membership of KDE e.V.), which shall act as a proxy * defined in Section 14 of version 3 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "roommodel.h" #include "ruqola_debug.h" #include "rocketchataccount.h" #include "usersforroommodel.h" #include "threadsmodel.h" #include "roomwrapper.h" #include #include #include #include #include RoomModel::RoomModel(RocketChatAccount *account, QObject *parent) : QAbstractListModel(parent) , mRocketChatAccount(account) { } RoomModel::~RoomModel() { #if 0 if (mRocketChatAccount && mRocketChatAccount->settings()) { const QString cachePath = mRocketChatAccount->settings()->cacheBasePath(); if (cachePath.isEmpty()) { qCWarning(RUQOLA_LOG) << " Cache Path is not defined"; return; } QDir cacheDir(cachePath); if (!cacheDir.exists(cacheDir.path())) { cacheDir.mkpath(cacheDir.path()); } QFile f(cacheDir.absoluteFilePath(QStringLiteral("rooms"))); if (f.open(QIODevice::WriteOnly)) { QDataStream out(&f); for (Room *m : qAsConst(mRoomsList)) { qCDebug(RUQOLA_LOG) << " save cache for room " << m->name(); const QByteArray ms = Room::serialize(m); out.writeBytes(ms.constData(), ms.size()); } } } #endif qDeleteAll(mRoomsList); } void RoomModel::clear() { if (!mRoomsList.isEmpty()) { beginRemoveRows(QModelIndex(), 0, rowCount() - 1); qDeleteAll(mRoomsList); mRoomsList.clear(); endRemoveRows(); } } Room *RoomModel::findRoom(const QString &roomID) const { for (Room *r : qAsConst(mRoomsList)) { if (r->roomId() == roomID) { return r; } } return nullptr; } RoomWrapper *RoomModel::findRoomWrapper(const QString &roomID) const { for (Room *r : qAsConst(mRoomsList)) { if (r->roomId() == roomID) { RoomWrapper *wrapper = new RoomWrapper(r); return wrapper; } } return nullptr; } // Clear data and refill it with data in the cache, if there is void RoomModel::reset() { clear(); if (!mRocketChatAccount) { return; } if (mRocketChatAccount->settings()->cacheBasePath().isEmpty()) { return; } //Laurent disable cache for the moment /* QDir cacheDir(Ruqola::self()->cacheBasePath()); // load cache if (cacheDir.exists(cacheDir.path())) { QFile f(cacheDir.absoluteFilePath(QStringLiteral("rooms"))); if (f.open(QIODevice::ReadOnly)) { QDataStream in(&f); while (!f.atEnd()) { char *byteArray; quint32 length; in.readBytes(byteArray, length); QByteArray arr = QByteArray::fromRawData(byteArray, length); Room m = Room::fromJSon(QJsonDocument::fromBinaryData(arr).object()); qDebug() <<" Load from cache room name: " << m.name; addRoom(m.id, m.name, m.selected); } } qCDebug(RUQOLA_LOG) << "Cache Loaded"; } */ } QHash RoomModel::roleNames() const { QHash roles; roles[RoomName] = QByteArrayLiteral("name"); roles[RoomFName] = QByteArrayLiteral("fname"); roles[RoomID] = QByteArrayLiteral("room_id"); roles[RoomSelected] = QByteArrayLiteral("selected"); roles[RoomUnread] = QByteArrayLiteral("unread"); roles[RoomType] = QByteArrayLiteral("type"); roles[RoomOwnerUserName] = QByteArrayLiteral("username"); roles[RoomOwnerUserID] = QByteArrayLiteral("userID"); roles[RoomTopic] = QByteArrayLiteral("topic"); roles[RoomMutedUsers] = QByteArrayLiteral("mutedUsers"); roles[RoomJitsiTimeout] = QByteArrayLiteral("jitsiTimeout"); roles[RoomRo] = QByteArrayLiteral("readOnly"); roles[RoomAnnouncement] = QByteArrayLiteral("announcement"); roles[RoomOpen] = QByteArrayLiteral("open"); roles[RoomAlert] = QByteArrayLiteral("alert"); roles[RoomOrder] = QByteArrayLiteral("roomorder"); roles[RoomFavorite] = QByteArrayLiteral("favorite"); roles[RoomSection] = QByteArrayLiteral("sectionname"); roles[RoomIcon] = QByteArrayLiteral("channelicon"); roles[RoomUserMentions] = QByteArrayLiteral("userMentions"); return roles; } int RoomModel::rowCount(const QModelIndex &parent) const { Q_UNUSED(parent); return mRoomsList.size(); } QVariant RoomModel::data(const QModelIndex &index, int role) const { if (index.row() < 0 || index.row() >= mRoomsList.count()) { return QVariant(); } Room *r = mRoomsList.at(index.row()); switch (role) { case RoomModel::RoomName: return r->name(); case RoomModel::RoomFName: return r->displayFName(); case RoomModel::RoomID: return r->roomId(); case RoomModel::RoomSelected: return r->selected(); case RoomModel::RoomType: return r->channelType(); case RoomModel::RoomOwnerUserID: return r->roomCreatorUserId(); case RoomModel::RoomOwnerUserName: return r->roomOwnerUserName(); case RoomModel::RoomTopic: return r->topic(); case RoomModel::RoomMutedUsers: return r->mutedUsers(); case RoomModel::RoomJitsiTimeout: return r->jitsiTimeout(); case RoomModel::RoomRo: return r->readOnly(); case RoomModel::RoomAnnouncement: return r->announcement(); case RoomModel::RoomUnread: return r->unread(); case RoomModel::RoomOpen: return r->open(); case RoomModel::RoomAlert: return r->alert(); case RoomModel::RoomFavorite: return r->favorite(); case RoomModel::RoomSection: return sectionName(r); case RoomModel::RoomOrder: return order(r); case RoomModel::RoomIcon: return icon(r); case RoomModel::RoomOtr: //TODO implement it. return {}; case RoomModel::RoomUserMentions: return r->userMentions(); case RoomModel::RoomIgnoredUsers: return r->ignoredUsers(); } return {}; } void RoomModel::addRoom(const QString &roomID, const QString &roomName, bool selected) { if (roomID.isEmpty() || roomName.isEmpty()) { qCDebug(RUQOLA_LOG) << " Impossible to add a room"; return; } qCDebug(RUQOLA_LOG) << "Adding room : roomId: " << roomID << " room Name " << roomName << " isSelected : " << selected; Room *r = createNewRoom(); r->setRoomId(roomID); r->setName(roomName); r->setSelected(selected); addRoom(r); } Room *RoomModel::createNewRoom() { Room *r = new Room(mRocketChatAccount); connect(r, &Room::alertChanged, this, &RoomModel::needToUpdateNotification); connect(r, &Room::unreadChanged, this, &RoomModel::needToUpdateNotification); return r; } void RoomModel::getUnreadAlertFromAccount(bool &hasAlert, int &nbUnread) { for (int i = 0; i < mRoomsList.count(); ++i) { if (mRoomsList.at(i)->open()) { if (mRoomsList.at(i)->alert()) { hasAlert = true; } nbUnread += mRoomsList.at(i)->unread(); } } } void RoomModel::updateSubscriptionRoom(const QJsonObject &roomData) { //TODO fix me! //Use "_id" QString rId = roomData.value(QLatin1String("rid")).toString(); if (rId.isEmpty()) { rId = roomData.value(QLatin1String("_id")).toString(); } if (!rId.isEmpty()) { const int roomCount = mRoomsList.size(); for (int i = 0; i < roomCount; ++i) { if (mRoomsList.at(i)->roomId() == rId) { qCDebug(RUQOLA_LOG) << " void RoomModel::updateSubscriptionRoom(const QJsonArray &array) room found"; Room *room = mRoomsList.at(i); room->updateSubscriptionRoom(roomData); Q_EMIT dataChanged(createIndex(i, 0), createIndex(i, 0)); break; } } } else { //qCWarning(RUQOLA_LOG) << "RoomModel::updateRoom incorrect jsonobject "<< roomData; qWarning() << "RoomModel::updateSubscriptionRoom incorrect jsonobject "<< roomData; } } QString RoomModel::insertRoom(const QJsonObject &room) { Room *r = createNewRoom(); r->parseInsertRoom(room); qCDebug(RUQOLA_LOG) << "Inserting room" << r->name() << r->roomId() << r->topic(); addRoom(r); return r->roomId(); } Room *RoomModel::addRoom(const QJsonObject &room) { Room *r = createNewRoom(); r->parseSubscriptionRoom(room); qCDebug(RUQOLA_LOG) << "Adding room subscription" << r->name() << r->roomId() << r->topic(); addRoom(r); return r; } void RoomModel::addRoom(Room *room) { qCDebug(RUQOLA_LOG) << " void RoomModel::addRoom(const Room &room)"<name(); int roomCount = mRoomsList.count(); for (int i = 0; i < roomCount; ++i) { if (mRoomsList.at(i)->roomId() == room->roomId()) { delete mRoomsList.takeAt(i); break; } } roomCount = mRoomsList.count(); beginInsertRows(QModelIndex(), roomCount, roomCount); qCDebug(RUQOLA_LOG) << "Inserting room at position" <roomId() == id) { beginRemoveRows(QModelIndex(), i, i); mRoomsList.remove(i); endRemoveRows(); break; } } } else if (actionName == QStringLiteral("inserted")) { qCDebug(RUQOLA_LOG) << "INSERT ROOM name " << roomData.value(QLatin1String("name")) << " rid " << roomData.value(QLatin1String("rid")); //TODO fix me! addRoom(roomData); //addRoom(roomData.value(QLatin1String("rid")).toString(), roomData.value(QLatin1String("name")).toString(), false); } else if (actionName == QStringLiteral("updated")) { //qCDebug(RUQOLA_LOG) << "UPDATE ROOM name " << roomData.value(QLatin1String("name")).toString() << " rid " << roomData.value(QLatin1String("rid")) << " roomData " << roomData; qDebug() << "UPDATE ROOM name " << roomData.value(QLatin1String("name")).toString() << " rid " << roomData.value(QLatin1String("rid")) << " roomData " << roomData; updateSubscriptionRoom(roomData); } else if (actionName == QStringLiteral("changed")) { //qDebug() << "CHANGED ROOM name " << roomData.value(QLatin1String("name")).toString() << " rid " << roomData.value(QLatin1String("rid")) << " roomData " << roomData; qCDebug(RUQOLA_LOG) << "CHANGED ROOM name " << roomData.value(QLatin1String("name")).toString() << " rid " << roomData.value(QLatin1String("rid")) << " roomData " << roomData; qCDebug(RUQOLA_LOG) << "RoomModel::updateSubscription Not implementer changed room yet" << array; updateRoom(roomData); } else { qCDebug(RUQOLA_LOG) << "RoomModel::updateSubscription Undefined type" << actionName; } } void RoomModel::updateRoom(const QJsonObject &roomData) { qCDebug(RUQOLA_LOG) << " void RoomModel::updateRoom(const QJsonObject &roomData)"; //TODO fix me! //Use "_id" QString rId = roomData.value(QLatin1String("rid")).toString(); if (rId.isEmpty()) { rId = roomData.value(QLatin1String("_id")).toString(); } if (!rId.isEmpty()) { const int roomCount = mRoomsList.size(); for (int i = 0; i < roomCount; ++i) { if (mRoomsList.at(i)->roomId() == rId) { qCDebug(RUQOLA_LOG) << " void RoomModel::updateRoom(const QJsonArray &array) room found"; Room *room = mRoomsList.at(i); room->parseUpdateRoom(roomData); Q_EMIT dataChanged(createIndex(i, 0), createIndex(i, 0)); break; } } } else { //qCWarning(RUQOLA_LOG) << "RoomModel::updateRoom incorrect jsonobject "<< roomData; qWarning() << "RoomModel::updateRoom incorrect jsonobject "<< roomData; } } void RoomModel::userStatusChanged(const User &user) { const int roomCount = mRoomsList.count(); for (int i = 0; i < roomCount; ++i) { Room *room = mRoomsList.at(i); if (room->name() == user.userName()) { const QModelIndex idx = createIndex(i, 0); Q_EMIT dataChanged(idx, idx); } room->usersModelForRoom()->userStatusChanged(user); } } UsersForRoomModel *RoomModel::usersModelForRoom(const QString &roomId) const { const int roomCount = mRoomsList.count(); for (int i = 0; i < roomCount; ++i) { Room *room = mRoomsList.at(i); if (room->roomId() == roomId) { return room->usersModelForRoom(); } } qCWarning(RUQOLA_LOG) << " Users model for room undefined !"; return nullptr; } UsersForRoomFilterProxyModel *RoomModel::usersForRoomFilterProxyModel(const QString &roomId) const { const int roomCount = mRoomsList.count(); for (int i = 0; i < roomCount; ++i) { Room *room = mRoomsList.at(i); if (room->roomId() == roomId) { return room->usersModelForRoomProxyModel(); } } return {}; } MessageModel *RoomModel::messageModel(const QString &roomId) const { const int roomCount = mRoomsList.count(); for (int i = 0; i < roomCount; ++i) { Room *room = mRoomsList.at(i); if (room->roomId() == roomId) { return room->messageModel(); } } return {}; } QString RoomModel::inputMessage(const QString &roomId) const { const int roomCount = mRoomsList.count(); for (int i = 0; i < roomCount; ++i) { Room *room = mRoomsList.at(i); if (room->roomId() == roomId) { return room->inputMessage(); } } return {}; } void RoomModel::setInputMessage(const QString &roomId, const QString &inputMessage) { const int roomCount = mRoomsList.count(); for (int i = 0; i < roomCount; ++i) { Room *room = mRoomsList.at(i); if (room->roomId() == roomId) { room->setInputMessage(inputMessage); return; } } } QString RoomModel::sectionName(Room *r) const { QString str; if (r->favorite()) { str = i18n("Favorites"); } else { const QString channelTypeStr = r->channelType(); - if (channelTypeStr == QLatin1String("p")) { - if (r->parentRid().isEmpty()) { + if (mRocketChatAccount && mRocketChatAccount->sortUnreadOnTop() && (r->unread() > 0 || r->alert())) { + if (channelTypeStr == QLatin1String("p")) { + if (r->parentRid().isEmpty()) { + str = i18n("Unread Rooms"); + } else { + str = i18n("Unread Discussions"); + } + } else if (channelTypeStr == QLatin1String("c")) { + str = i18n("Unread Rooms"); + } else if (channelTypeStr == QLatin1String("d")) { + str = i18n("Unread Private Messages"); + } + } else { + if (channelTypeStr == QLatin1String("p")) { + if (r->parentRid().isEmpty()) { + str = i18n("Rooms"); + } else { + str = i18n("Discussions"); + } + } else if (channelTypeStr == QLatin1String("c")) { str = i18n("Rooms"); - } else { - str = i18n("Discussions"); + } else if (channelTypeStr == QLatin1String("d")) { + str = i18n("Private Messages"); } - } else if (channelTypeStr == QLatin1String("c")) { - str = i18n("Rooms"); - } else if (channelTypeStr == QLatin1String("d")) { - str = i18n("Private Message"); } } return str; } int RoomModel::order(Room *r) const { int order = 0; - //First item are favorites channels + // Unread on top: push down everything that isn't unread + if (mRocketChatAccount && mRocketChatAccount->sortUnreadOnTop() && r->unread() == 0 && !r->alert()) { + order += 20; + } + + // Then we have favorites channels, push down everything else if (!r->favorite()) { order += 10; } const QString channelTypeStr = r->channelType(); if (channelTypeStr == QLatin1String("c")) { order += 1; } else if (channelTypeStr == QLatin1String("d")) { order += 2; } else if (channelTypeStr == QLatin1String("p")) { if (r->parentRid().isEmpty()) { order += 1; } else { order += 4; } } else { qCDebug(RUQOLA_LOG) << r->name() << "has unhandled channel type" << channelTypeStr; order += 5; } return order; } QIcon RoomModel::icon(Room *r) const { if (r->channelType() == QLatin1String("c")) { if (r->unread() > 0 || r->alert()) { return QIcon::fromTheme(QStringLiteral("irc-channel-active")); } else { return QIcon::fromTheme(QStringLiteral("irc-channel-inactive")); } } else if (r->channelType() == QLatin1String("d")) { const QString userStatusIconFileName = mRocketChatAccount ? mRocketChatAccount->userStatusIconFileName(r->name()) : QString(); if (userStatusIconFileName.isEmpty()) { return QIcon::fromTheme(QStringLiteral("user-available")); } else { return QIcon::fromTheme(userStatusIconFileName); } } else if (r->channelType() == QLatin1String("p")) { return QIcon::fromTheme(QStringLiteral("lock")); } return {}; } diff --git a/src/ruqolacore/rocketchataccount.cpp b/src/ruqolacore/rocketchataccount.cpp index fb15f5be..117f8b32 100644 --- a/src/ruqolacore/rocketchataccount.cpp +++ b/src/ruqolacore/rocketchataccount.cpp @@ -1,1554 +1,1567 @@ /* Copyright (c) 2017-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "model/messagemodel.h" #include "rocketchataccount.h" #include "model/roommodel.h" #include "roomwrapper.h" #include "typingnotification.h" #include "model/usersmodel.h" #include "ruqola_debug.h" #include "ruqola.h" #include "messagequeue.h" #include "rocketchatbackend.h" #include "model/roomfilterproxymodel.h" #include "ruqolalogger.h" #include "ruqolaserverconfig.h" #include "model/usercompletermodel.h" #include "model/usercompleterfilterproxymodel.h" #include "model/statusmodel.h" #include "utils.h" #include "rocketchatcache.h" #include "emoticons/emojimanager.h" #include "model/emoticonmodel.h" #include "otrmanager.h" #include "inputtextmanager.h" #include "model/usersforroommodel.h" #include "model/filesforroommodel.h" #include "model/searchchannelfilterproxymodel.h" #include "model/searchchannelmodel.h" #include "model/loginmethodmodel.h" #include "model/inputcompletermodel.h" #include "model/searchmessagemodel.h" #include "model/searchmessagefilterproxymodel.h" #include "model/discussionsmodel.h" #include "model/threadsmodel.h" #include "model/filesforroomfilterproxymodel.h" #include "model/discussionsfilterproxymodel.h" #include "model/threadsfilterproxymodel.h" #include "model/mentionsmodel.h" #include "model/mentionsfilterproxymodel.h" #include "managerdatapaths.h" #include "authenticationmanager.h" #include "ddpapi/ddpclient.h" #include "discussions.h" #include "receivetypingnotificationmanager.h" #include "restapirequest.h" #include "serverconfiginfo.h" #include #include #include #include #include #define USE_REASTAPI_JOB 1 RocketChatAccount::RocketChatAccount(const QString &accountFileName, QObject *parent) : QObject(parent) { qCDebug(RUQOLA_LOG) << " RocketChatAccount::RocketChatAccount(const QString &accountFileName, QObject *parent)"<setServerUrl(mSettings->serverUrl()); mEmoticonModel->setEmoticons(mEmojiManager->unicodeEmojiList()); mOtrManager = new OtrManager(this); mRoomFilterProxyModel = new RoomFilterProxyModel(this); mUserCompleterModel = new UserCompleterModel(this); mUserCompleterFilterModelProxy = new UserCompleterFilterProxyModel(this); mUserCompleterFilterModelProxy->setSourceModel(mUserCompleterModel); mSearchChannelModel = new SearchChannelModel(this); mSearchChannelFilterProxyModel = new SearchChannelFilterProxyModel(this); mSearchChannelFilterProxyModel->setSourceModel(mSearchChannelModel); mSearchMessageModel = new SearchMessageModel(this); mSearchMessageFilterProxyModel = new SearchMessageFilterProxyModel(this); mSearchMessageFilterProxyModel->setSourceModel(mSearchMessageModel); mFilesModelForRoom = new FilesForRoomModel(this, this); mFilesModelForRoom->setObjectName(QStringLiteral("filesmodelforrooms")); mFilesForRoomFilterProxyModel = new FilesForRoomFilterProxyModel(this); mFilesForRoomFilterProxyModel->setObjectName(QStringLiteral("filesforroomfiltermodelproxy")); mFilesForRoomFilterProxyModel->setSourceModel(mFilesModelForRoom); mDiscussionsModel = new DiscussionsModel(this); mDiscussionsModel->setObjectName(QStringLiteral("discussionsmodel")); mDiscussionsFilterProxyModel = new DiscussionsFilterProxyModel(this); mDiscussionsFilterProxyModel->setObjectName(QStringLiteral("discussionsfilterproxymodel")); mDiscussionsFilterProxyModel->setSourceModel(mDiscussionsModel); mThreadsModel = new ThreadsModel(this); mThreadsModel->setObjectName(QStringLiteral("threadsmodel")); mThreadsFilterProxyModel = new ThreadsFilterProxyModel(this); mThreadsFilterProxyModel->setObjectName(QStringLiteral("threadsfiltermodelproxy")); mThreadsFilterProxyModel->setSourceModel(mThreadsModel); mMentionsModel = new MentionsModel(this); mMentionsModel->setObjectName(QStringLiteral("mentionsmodel")); mMentionsFilterProxyModel = new MentionsFilterProxyModel(this); mMentionsFilterProxyModel->setObjectName(QStringLiteral("mentionsfiltermodelproxy")); mMentionsFilterProxyModel->setSourceModel(mMentionsModel); mStatusModel = new StatusModel(this); mRoomModel = new RoomModel(this, this); connect(mRoomModel, &RoomModel::needToUpdateNotification, this, &RocketChatAccount::slotNeedToUpdateNotification); mRoomFilterProxyModel->setSourceModel(mRoomModel); mUserModel = new UsersModel(this); connect(mUserModel, &UsersModel::userStatusChanged, this, &RocketChatAccount::userStatusChanged); mMessageQueue = new MessageQueue(this, this); //TODO fix mem leak ! mTypingNotification = new TypingNotification(this); mCache = new RocketChatCache(this, this); connect(mCache, &RocketChatCache::fileDownloaded, this, &RocketChatAccount::fileDownloaded); connect(mTypingNotification, &TypingNotification::informTypingStatus, this, &RocketChatAccount::slotInformTypingStatus); QTimer::singleShot(0, this, &RocketChatAccount::clearModels); } RocketChatAccount::~RocketChatAccount() { delete mCache; mCache = nullptr; delete mRuqolaServerConfig; delete mRuqolaLogger; } void RocketChatAccount::removeSettings() { mSettings->removeSettings(); } void RocketChatAccount::loadSettings(const QString &accountFileName) { delete mSettings; mSettings = new RocketChatAccountSettings(accountFileName, this); connect(mSettings, &RocketChatAccountSettings::serverURLChanged, this, &RocketChatAccount::serverUrlChanged); connect(mSettings, &RocketChatAccountSettings::userIDChanged, this, &RocketChatAccount::userIDChanged); connect(mSettings, &RocketChatAccountSettings::userNameChanged, this, &RocketChatAccount::userNameChanged); connect(mSettings, &RocketChatAccountSettings::passwordChanged, this, &RocketChatAccount::passwordChanged); } void RocketChatAccount::slotNeedToUpdateNotification() { bool hasAlert = false; int nbUnread = 0; mRoomModel->getUnreadAlertFromAccount(hasAlert, nbUnread); Q_EMIT updateNotification(hasAlert, nbUnread, accountName()); } void RocketChatAccount::clearModels() { // Clear rooms data and refill it with data in the cache, if there is mRoomModel->reset(); mMessageQueue->loadCache(); //Try to send queue message mMessageQueue->processQueue(); } SearchChannelFilterProxyModel *RocketChatAccount::searchChannelFilterProxyModel() const { return mSearchChannelFilterProxyModel; } SearchChannelModel *RocketChatAccount::searchChannelModel() const { return mSearchChannelModel; } UserCompleterModel *RocketChatAccount::userCompleterModel() const { return mUserCompleterModel; } UserCompleterFilterProxyModel *RocketChatAccount::userCompleterFilterModelProxy() const { return mUserCompleterFilterModelProxy; } EmojiManager *RocketChatAccount::emojiManager() const { return mEmojiManager; } QString RocketChatAccount::userStatusIconFileName(const QString &name) { return mUserModel->userStatusIconFileName(name); } StatusModel *RocketChatAccount::statusModel() const { return mStatusModel; } RuqolaServerConfig *RocketChatAccount::ruqolaServerConfig() const { return mRuqolaServerConfig; } RuqolaLogger *RocketChatAccount::ruqolaLogger() const { return mRuqolaLogger; } RoomFilterProxyModel *RocketChatAccount::roomFilterProxyModel() const { return mRoomFilterProxyModel; } UsersForRoomFilterProxyModel *RocketChatAccount::usersForRoomFilterProxyModel(const QString &roomId) const { return mRoomModel->usersForRoomFilterProxyModel(roomId); } FilesForRoomFilterProxyModel *RocketChatAccount::filesForRoomFilterProxyModel() const { return mFilesForRoomFilterProxyModel; } RocketChatBackend *RocketChatAccount::rocketChatBackend() const { return mRocketChatBackend; } MessageQueue *RocketChatAccount::messageQueue() const { return mMessageQueue; } RocketChatAccountSettings *RocketChatAccount::settings() const { return mSettings; } void RocketChatAccount::slotInformTypingStatus(const QString &room, bool typing) { ddp()->informTypingStatus(room, typing, mSettings->userName()); } RoomModel *RocketChatAccount::roomModel() const { return mRoomModel; } UsersModel *RocketChatAccount::usersModel() const { return mUserModel; } Room *RocketChatAccount::getRoom(const QString &roomId) { return mRoomModel->findRoom(roomId); } DiscussionsFilterProxyModel *RocketChatAccount::discussionsFilterProxyModel() const { return mDiscussionsFilterProxyModel; } ThreadsFilterProxyModel *RocketChatAccount::threadsFilterProxyModel() const { return mThreadsFilterProxyModel; } RoomWrapper *RocketChatAccount::getRoomWrapper(const QString &roomId) { return mRoomModel->findRoomWrapper(roomId); } MessageModel *RocketChatAccount::messageModelForRoom(const QString &roomID) { return mRoomModel->messageModel(roomID); } QString RocketChatAccount::getUserCurrentMessage(const QString &roomId) { return mRoomModel->inputMessage(roomId); } void RocketChatAccount::setUserCurrentMessage(const QString &message, const QString &roomId) { mRoomModel->setInputMessage(roomId, message); } void RocketChatAccount::setInputTextChanged(const QString &str, int position) { mInputTextManager->setInputTextChanged(str, position); } QString RocketChatAccount::replaceWord(const QString &newWord, const QString &str, int position) { return mInputTextManager->replaceWord(newWord, str, position); } void RocketChatAccount::textEditing(const QString &roomId, const QString &str) { mTypingNotification->setText(roomId, str); } void RocketChatAccount::reactOnMessage(const QString &messageId, const QString &emoji, bool shouldReact) { restApi()->reactOnMessage(messageId, emoji, shouldReact); } void RocketChatAccount::sendMessage(const QString &roomID, const QString &message) { restApi()->postMessage(roomID, message); } void RocketChatAccount::updateMessage(const QString &roomID, const QString &messageId, const QString &message) { restApi()->updateMessage(roomID, messageId, message); } QString RocketChatAccount::avatarUrlFromDirectChannel(const QString &rid) { return mCache->avatarUrl(Utils::userIdFromDirectChannel(rid, userID())); } void RocketChatAccount::deleteFileMessage(const QString &roomId, const QString &fileId, const QString &channelType) { ddp()->deleteFileMessage(roomId, fileId, channelType); } QString RocketChatAccount::avatarUrl(const QString &userId) { return mCache->avatarUrl(userId); } void RocketChatAccount::insertAvatarUrl(const QString &userId, const QString &url) { mCache->insertAvatarUrl(userId, url); } RocketChatRestApi::RestApiRequest *RocketChatAccount::restApi() { if (!mRestApi) { mRestApi = new RocketChatRestApi::RestApiRequest(this); connect(mRestApi, &RocketChatRestApi::RestApiRequest::setChannelJoinDone, this, &RocketChatAccount::setChannelJoinDone); connect(mRestApi, &RocketChatRestApi::RestApiRequest::missingChannelPassword, this, &RocketChatAccount::missingChannelPassword); connect(mRestApi, &RocketChatRestApi::RestApiRequest::loadEmojiCustomDone, this, &RocketChatAccount::loadEmojiRestApi); connect(mRestApi, &RocketChatRestApi::RestApiRequest::openArchivedRoom, this, &RocketChatAccount::openArchivedRoom); connect(mRestApi, &RocketChatRestApi::RestApiRequest::channelMembersDone, this, &RocketChatAccount::parseUsersForRooms); connect(mRestApi, &RocketChatRestApi::RestApiRequest::channelFilesDone, this, &RocketChatAccount::slotChannelFilesDone); connect(mRestApi, &RocketChatRestApi::RestApiRequest::channelRolesDone, this, &RocketChatAccount::slotChannelRolesDone); connect(mRestApi, &RocketChatRestApi::RestApiRequest::searchMessageDone, this, &RocketChatAccount::slotSearchMessages); connect(mRestApi, &RocketChatRestApi::RestApiRequest::failed, this, &RocketChatAccount::jobFailed); connect(mRestApi, &RocketChatRestApi::RestApiRequest::spotlightDone, this, &RocketChatAccount::slotSplotLightDone); connect(mRestApi, &RocketChatRestApi::RestApiRequest::getThreadMessagesDone, this, &RocketChatAccount::slotGetThreadMessagesDone); connect(mRestApi, &RocketChatRestApi::RestApiRequest::getThreadsDone, this, &RocketChatAccount::slotGetThreadsListDone); connect(mRestApi, &RocketChatRestApi::RestApiRequest::getDiscussionsDone, this, &RocketChatAccount::slotGetDiscussionsListDone); connect(mRestApi, &RocketChatRestApi::RestApiRequest::channelGetAllUserMentionsDone, this, &RocketChatAccount::slotGetAllUserMentionsDone); mRestApi->setServerUrl(mSettings->serverUrl()); mRestApi->setRestApiLogger(mRuqolaLogger); } return mRestApi; } void RocketChatAccount::leaveRoom(const QString &roomId, const QString &channelType) { if (channelType == QStringLiteral("c")) { restApi()->leaveChannel(roomId); } else if (channelType == QStringLiteral("p")) { restApi()->leaveGroups(roomId); } else { qCWarning(RUQOLA_LOG) << " unsupport leave room for type " << channelType; } } void RocketChatAccount::hideRoom(const QString &roomId, const QString &channelType) { restApi()->closeChannel(roomId, channelType); } DDPClient *RocketChatAccount::ddp() { if (!mDdp) { mDdp = new DDPClient(this, this); connect(mDdp, &DDPClient::loginStatusChanged, this, &RocketChatAccount::loginStatusChanged); connect(mDdp, &DDPClient::connectedChanged, this, &RocketChatAccount::connectedChanged); connect(mDdp, &DDPClient::changed, this, &RocketChatAccount::changed); connect(mDdp, &DDPClient::added, this, &RocketChatAccount::added); connect(mDdp, &DDPClient::removed, this, &RocketChatAccount::removed); if (mSettings) { mDdp->setServerUrl(mSettings->serverUrl()); } mDdp->start(); } return mDdp; } bool RocketChatAccount::editingMode() const { return mEditingMode; } +bool RocketChatAccount::sortUnreadOnTop() const +{ + return mUnreadOnTop; +} + DDPClient::LoginStatus RocketChatAccount::loginStatus() { if (mDdp) { return ddp()->loginStatus(); } else { return DDPClient::LoggedOut; } } void RocketChatAccount::tryLogin() { qCDebug(RUQOLA_LOG) << "Attempting login" << mSettings->userName() << "on" << mSettings->serverUrl(); delete mDdp; mDdp = nullptr; // This creates a new ddp() object. // DDP will automatically try to connect and login. ddp(); // In the meantime, load cache... mRoomModel->reset(); } void RocketChatAccount::logOut() { mSettings->logout(); mRoomModel->clear(); #ifdef USE_REASTAPI_JOB restApi()->logout(); #else QJsonObject user; user[QStringLiteral("username")] = mSettings->userName(); QJsonObject json; json[QStringLiteral("user")] = user; ddp()->method(QStringLiteral("logout"), QJsonDocument(json)); #endif delete mDdp; mDdp = nullptr; Q_EMIT logoutDone(accountName()); Q_EMIT loginStatusChanged(); qCDebug(RUQOLA_LOG) << "Successfully logged out!"; } void RocketChatAccount::clearUnreadMessages(const QString &roomId) { restApi()->markAsRead(roomId); } void RocketChatAccount::changeFavorite(const QString &roomId, bool checked) { if (mRuqolaServerConfig->hasAtLeastVersion(0, 64, 0)) { restApi()->markAsFavorite(roomId, checked); } else { ddp()->toggleFavorite(roomId, checked); } } void RocketChatAccount::openChannel(const QString &url) { //qCDebug(RUQOLA_LOG) << " void RocketChatAccount::openChannel(const QString &url)"<channelJoin(url, QString()); //TODO search correct room + select it. } void RocketChatAccount::setChannelJoinDone(const QString &roomId) { ddp()->subscribeRoomMessage(roomId); } void RocketChatAccount::openArchivedRoom(const QString &roomId) { //TODO } void RocketChatAccount::joinJitsiConfCall(const QString &roomId) { qCDebug(RUQOLA_LOG) << " void RocketChatAccount::joinJitsiConfCall(const QString &roomId)"<uniqueId() + roomId).toUtf8(), QCryptographicHash::Md5).toHex()); #if defined(Q_OS_IOS) || defined(Q_OS_ANDROID) const QString scheme = "org.jitsi.meet://"; #else const QString scheme = QStringLiteral("https://"); #endif const QString url = scheme + mRuqolaServerConfig->jitsiMeetUrl() + QLatin1Char('/') + mRuqolaServerConfig->jitsiMeetPrefix() + hash; const QUrl clickedUrl = QUrl::fromUserInput(url); QDesktopServices::openUrl(clickedUrl); } void RocketChatAccount::eraseRoom(const QString &roomId, const QString &channelType) { if (channelType == QStringLiteral("c")) { restApi()->channelDelete(roomId); } else if (channelType == QStringLiteral("p")) { restApi()->groupDelete(roomId); } else { qCWarning(RUQOLA_LOG) << " unsupport delete for type " << channelType; } } void RocketChatAccount::openDirectChannel(const QString &username) { //TODO verify username vs userId #ifdef USE_REASTAPI_JOB restApi()->openDirectMessage(username); #else ddp()->openDirectChannel(username); #endif } void RocketChatAccount::createNewChannel(const QString &name, bool readOnly, bool privateRoom, const QString &userNames, bool encryptedRoom, const QString &password, bool broadcast) { //TODO use encryted room //TODO use broadcast if (!name.trimmed().isEmpty()) { const QStringList lstUsers = userNames.split(QLatin1Char(','), QString::SkipEmptyParts); if (privateRoom) { //TODO add password ??? restApi()->createGroups(name, readOnly, lstUsers); } else { restApi()->createChannels(name, readOnly, lstUsers, password); } } else { qCDebug(RUQOLA_LOG) << "Channel name can't be empty"; } } void RocketChatAccount::joinRoom(const QString &roomId, const QString &joinCode) { restApi()->channelJoin(roomId, joinCode); } void RocketChatAccount::channelAndPrivateAutocomplete(const QString &pattern) { //TODO use restapi if (pattern.isEmpty()) { searchChannelModel()->clear(); } else { //Use restapi //Avoid to show own user #ifdef USE_REASTAPI_JOB restApi()->searchRoomUser(pattern); #else const QString addUserNameToException = userName(); ddp()->channelAndPrivateAutocomplete(pattern, addUserNameToException); #endif } } void RocketChatAccount::listEmojiCustom() { if (mRuqolaServerConfig->hasAtLeastVersion(0, 63, 0)) { restApi()->listEmojiCustom(); } else { ddp()->listEmojiCustom(); } } void RocketChatAccount::setDefaultStatus(User::PresenceStatus status) { //Not implemented yet //TODO use restapi if (statusModel()->currentUserStatus() != status) { ddp()->setDefaultStatus(status); } } void RocketChatAccount::changeDefaultStatus(int index) { setDefaultStatus(mStatusModel->status(index)); } void RocketChatAccount::loadEmojiRestApi(const QJsonObject &obj) { mEmojiManager->loadCustomEmoji(obj, true); } void RocketChatAccount::loadEmoji(const QJsonObject &obj) { mEmojiManager->loadCustomEmoji(obj, false); } void RocketChatAccount::deleteMessage(const QString &messageId, const QString &roomId) { restApi()->deleteMessage(roomId, messageId); } void RocketChatAccount::insertCompleterUsers() { userCompleterModel()->insertUsers(rocketChatBackend()->users()); } void RocketChatAccount::userAutocomplete(const QString &searchText, const QString &exception) { //Clear before to create new search userCompleterModel()->clear(); rocketChatBackend()->clearUsersList(); if (!searchText.isEmpty()) { //Avoid to show own user QString addUserNameToException; if (exception.isEmpty()) { addUserNameToException = userName(); } else { addUserNameToException = exception + QLatin1Char(',') + userName(); } //TODO use restapi ddp()->userAutocomplete(searchText, addUserNameToException); } } void RocketChatAccount::membersInRoom(const QString &roomId, const QString &roomType) { restApi()->membersInRoom(roomId, roomType); } void RocketChatAccount::parseUsersForRooms(const QJsonObject &obj, const QString &roomId) { UsersForRoomModel *usersModelForRoom = roomModel()->usersModelForRoom(roomId); if (usersModelForRoom) { usersModelForRoom->parseUsersForRooms(obj, mUserModel, true); } else { qCWarning(RUQOLA_LOG) << " Impossible to find room " << roomId; } } void RocketChatAccount::loadAutoCompleteChannel(const QJsonObject &obj) { mSearchChannelModel->parseChannels(obj); } void RocketChatAccount::roomFiles(const QString &roomId, const QString &channelType) { restApi()->filesInRoom(roomId, channelType); } QVector RocketChatAccount::parseFilesInChannel(const QJsonObject &obj) const { //TODO add autotests QVector files; const QJsonArray filesArray = obj.value(QLatin1String("files")).toArray(); files.reserve(filesArray.count()); for (int i = 0; i < filesArray.count(); ++i) { QJsonObject fileObj = filesArray.at(i).toObject(); File f; f.parseFile(fileObj, true); files.append(f); } return files; } MentionsFilterProxyModel *RocketChatAccount::mentionsFilterProxyModel() const { return mMentionsFilterProxyModel; } MentionsModel *RocketChatAccount::mentionsModel() const { return mMentionsModel; } ThreadsModel *RocketChatAccount::threadsModel() const { return mThreadsModel; } DiscussionsModel *RocketChatAccount::discussionsModel() const { return mDiscussionsModel; } FilesForRoomModel *RocketChatAccount::filesModelForRoom() const { return mFilesModelForRoom; } EmoticonModel *RocketChatAccount::emoticonModel() const { return mEmoticonModel; } void RocketChatAccount::setEmoticonModel(EmoticonModel *emoticonModel) { mEmoticonModel = emoticonModel; } ReceiveTypingNotificationManager *RocketChatAccount::receiveTypingNotificationManager() const { return mReceiveTypingNotificationManager; } void RocketChatAccount::slotChannelRolesDone(const QJsonObject &obj, const QString &roomId) { Room *room = mRoomModel->findRoom(roomId); if (room) { Roles r; r.parseRole(obj); room->setRolesForRooms(r); } else { qCWarning(RUQOLA_LOG) << " Impossible to find room " << roomId; } } void RocketChatAccount::slotGetThreadMessagesDone(const QJsonObject &obj, const QString &threadMessageId) { //USe a specific method for threadmessages } void RocketChatAccount::slotGetDiscussionsListDone(const QJsonObject &obj, const QString &roomId) { Discussions discussions; discussions.parseDiscussions(obj); mDiscussionsModel->setDiscussions(discussions); } void RocketChatAccount::slotGetAllUserMentionsDone(const QJsonObject &obj, const QString &roomId) { Mentions mentions; mentions.parseMentions(obj); mMentionsModel->setMentions(mentions); } void RocketChatAccount::slotGetThreadsListDone(const QJsonObject &obj, const QString &roomId) { Threads threads; threads.parseThreads(obj); mThreadsModel->setThreads(threads); } void RocketChatAccount::slotSplotLightDone(const QJsonObject &obj) { //qDebug() << " void RocketChatAccount::slotSplotLightDone(const QJsonObject &obj)"< files = parseFilesInChannel(obj); mFilesModelForRoom->setFiles(files); } void RocketChatAccount::createJitsiConfCall(const QString &roomId) { //TODO use restapi ddp()->createJitsiConfCall(roomId); joinJitsiConfCall(roomId); } void RocketChatAccount::addUserToRoom(const QString &userId, const QString &roomId, const QString &channelType) { if (channelType == QStringLiteral("c")) { restApi()->addUserInChannel(roomId, userId); } else if (channelType == QStringLiteral("p")) { restApi()->addUserInGroup(roomId, userId); } } void RocketChatAccount::clearSearchModel() { mSearchMessageModel->clear(); } void RocketChatAccount::messageSearch(const QString &pattern, const QString &rid) { if (pattern.isEmpty()) { clearSearchModel(); } else { restApi()->searchMessages(rid, pattern); } } void RocketChatAccount::slotSearchMessages(const QJsonObject &obj) { mSearchMessageModel->parseResult(obj, true); } void RocketChatAccount::starMessage(const QString &messageId, bool starred) { restApi()->starMessage(messageId, starred); } void RocketChatAccount::pinMessage(const QString &messageId, bool pinned) { restApi()->pinMessage(messageId, pinned); } void RocketChatAccount::uploadFile(const QString &roomId, const QString &description, const QString &messageText, const QUrl &fileUrl) { restApi()->uploadFile(roomId, description, messageText, fileUrl); } void RocketChatAccount::changeChannelSettings(const QString &roomId, RocketChatAccount::RoomInfoType infoType, const QVariant &newValue, const QString &channelType) { switch (infoType) { case Announcement: if (channelType == QStringLiteral("c")) { restApi()->changeChannelAnnouncement(roomId, newValue.toString()); } else if (channelType == QStringLiteral("p")) { //FOR the moment we can't change group announcement with restapi if (mRuqolaServerConfig->hasAtLeastVersion(0, 70, 0)) { restApi()->changeGroupsAnnouncement(roomId, newValue.toString()); } else { ddp()->setRoomAnnouncement(roomId, newValue.toString()); } } else { qCWarning(RUQOLA_LOG) << " unsupport change announcement for type " << channelType; } break; case Description: if (channelType == QStringLiteral("c")) { restApi()->changeChannelDescription(roomId, newValue.toString()); } else if (channelType == QStringLiteral("p")) { restApi()->changeGroupsDescription(roomId, newValue.toString()); } else { qCWarning(RUQOLA_LOG) << " unsupport change description for type " << channelType; } break; case Name: if (channelType == QStringLiteral("c")) { restApi()->changeChannelName(roomId, newValue.toString()); } else if (channelType == QStringLiteral("p")) { restApi()->changeGroupName(roomId, newValue.toString()); } else { qCWarning(RUQOLA_LOG) << " unsupport change name for type " << channelType; } break; case Topic: if (channelType == QStringLiteral("c")) { restApi()->changeChannelTopic(roomId, newValue.toString()); } else if (channelType == QStringLiteral("p")) { restApi()->changeGroupsTopic(roomId, newValue.toString()); } else { //TODO : change topic in direct channel qCWarning(RUQOLA_LOG) << " unsupport change topic for type " << channelType; } break; case ReadOnly: if (channelType == QStringLiteral("c")) { restApi()->changeChannelReadOnly(roomId, newValue.toBool()); } else if (channelType == QStringLiteral("p")) { restApi()->changeGroupsReadOnly(roomId, newValue.toBool()); } else { qCWarning(RUQOLA_LOG) << " unsupport change readonly for type " << channelType; } break; case Archive: if (channelType == QStringLiteral("c")) { restApi()->archiveChannel(roomId); } else if (channelType == QStringLiteral("p")) { restApi()->archiveGroups(roomId); } else { qCWarning(RUQOLA_LOG) << " unsupport archiving for type " << channelType; } break; case RoomType: if (channelType == QStringLiteral("c")) { restApi()->setChannelType(roomId, channelType); } else if (channelType == QStringLiteral("p")) { restApi()->setGroupType(roomId, channelType); } else { qCWarning(RUQOLA_LOG) << " unsupport roomtype for type " << channelType; } break; case Encrypted: if (channelType == QStringLiteral("c")) { restApi()->changeChannelEncrypted(roomId, newValue.toBool()); } else if (channelType == QStringLiteral("p")) { restApi()->changeGroupsEncrypted(roomId, newValue.toBool()); } else { qCWarning(RUQOLA_LOG) << " unsupport encrypted mode for type " << channelType; } break; } } void RocketChatAccount::reportMessage(const QString &messageId, const QString &message) { if (mRuqolaServerConfig->hasAtLeastVersion(0, 64, 0)) { restApi()->reportMessage(messageId, message); } else { qCWarning(RUQOLA_LOG) << " RocketChatAccount::reportMessage is not supported before server 0.64"; } } void RocketChatAccount::getThreadMessages(const QString &threadMessageId) { if (mRuqolaServerConfig->hasAtLeastVersion(1, 0, 0)) { restApi()->getThreadMessages(threadMessageId); } else { qCWarning(RUQOLA_LOG) << " RocketChatAccount::getThreadMessages is not supported before server 1.0.0"; } } void RocketChatAccount::changeNotificationsSettings(const QString &roomId, RocketChatAccount::NotificationOptionsType notificationsType, const QVariant &newValue) { switch (notificationsType) { case DisableNotifications: restApi()->disableNotifications(roomId, newValue.toBool()); break; case HideUnreadStatus: restApi()->hideUnreadStatus(roomId, newValue.toBool()); break; case AudioNotifications: restApi()->audioNotifications(roomId, newValue.toString()); break; case DesktopNotifications: restApi()->desktopNotifications(roomId, newValue.toString()); break; case EmailNotifications: restApi()->emailNotifications(roomId, newValue.toString()); break; case MobilePushNotifications: restApi()->mobilePushNotifications(roomId, newValue.toString()); break; case UnreadAlert: restApi()->unreadAlert(roomId, newValue.toString()); break; case MuteGroupMentions: restApi()->muteGroupMentions(roomId, newValue.toBool()); break; case DesktopDurationNotifications: restApi()->desktopDurationNotifications(roomId, newValue.toInt()); break; case DesktopSoundNotifications: restApi()->desktopSoundNotifications(roomId, newValue.toString()); break; } } void RocketChatAccount::parsePublicSettings(const QJsonObject &obj) { QJsonArray configs = obj.value(QLatin1String("result")).toArray(); for (QJsonValueRef currentConfig : configs) { QJsonObject currentConfObject = currentConfig.toObject(); const QString id = currentConfObject[QStringLiteral("_id")].toString(); const QVariant value = currentConfObject[QStringLiteral("value")].toVariant(); if (id == QLatin1String("uniqueID")) { mRuqolaServerConfig->setUniqueId(value.toString()); } else if (id == QLatin1String("Jitsi_Enabled")) { mRuqolaServerConfig->setJitsiEnabled(value.toBool()); } else if (id == QLatin1String("Jitsi_Domain")) { mRuqolaServerConfig->setJitsiMeetUrl(value.toString()); } else if (id == QLatin1String("Jitsi_URL_Room_Prefix")) { mRuqolaServerConfig->setJitsiMeetPrefix(value.toString()); } else if (id == QLatin1String("FileUpload_Storage_Type")) { mRuqolaServerConfig->setFileUploadStorageType(value.toString()); } else if (id == QLatin1String("Message_AllowEditing")) { mRuqolaServerConfig->setAllowMessageEditing(value.toBool()); } else if (id == QLatin1String("Message_AllowEditing_BlockEditInMinutes")) { mRuqolaServerConfig->setBlockEditingMessageInMinutes(value.toInt()); } else if (id == QLatin1String("OTR_Enable")) { mRuqolaServerConfig->setOtrEnabled(value.toBool()); } else if (id.contains(QRegularExpression(QStringLiteral("^Accounts_OAuth_\\w+")))) { if (value.toBool()) { mRuqolaServerConfig->addOauthService(id); } } else if (id == QLatin1String("Site_Url")) { mRuqolaServerConfig->setSiteUrl(value.toString()); } else if (id == QLatin1String("Site_Name")) { mRuqolaServerConfig->setSiteName(value.toString()); } else if (id == QLatin1String("E2E_Enable")) { mRuqolaServerConfig->setEncryptionEnabled(value.toBool()); } else if (id == QLatin1String("Message_AllowPinning")) { mRuqolaServerConfig->setAllowMessagePinningEnabled(value.toBool()); } else if (id == QLatin1String("Message_AllowSnippeting")) { mRuqolaServerConfig->setAllowMessageSnippetingEnabled(value.toBool()); } else if (id == QLatin1String("Message_AllowStarring")) { mRuqolaServerConfig->setAllowMessageStarringEnabled(value.toBool()); } else if (id == QLatin1String("Message_AllowDeleting")) { mRuqolaServerConfig->setAllowMessageDeletingEnabled(value.toBool()); } else if (id == QLatin1String("Threads_enabled")) { mRuqolaServerConfig->setThreadsEnabled(value.toBool()); } else if (id == QLatin1String("Discussion_enabled")) { mRuqolaServerConfig->setDiscussionEnabled(value.toBool()); } else { qCDebug(RUQOLA_LOG) << "Other public settings id " << id << value; } } fillOauthModel(); } void RocketChatAccount::fillOauthModel() { QVector fillModel; for (int i = 0, total = mLstInfos.count(); i < total; ++i) { if (mRuqolaServerConfig->canShowOauthService(mLstInfos.at(i).oauthType())) { fillModel.append(mLstInfos.at(i)); } } mLoginMethodModel->setAuthenticationInfos(fillModel); } void RocketChatAccount::changeDefaultAuthentication(int index) { setDefaultAuthentication(mLoginMethodModel->loginType(index)); } void RocketChatAccount::setDefaultAuthentication(AuthenticationManager::OauthType type) { PluginAuthenticationInterface *interface = mLstPluginAuthenticationInterface.value(type); if (interface) { mDefaultAuthenticationInterface = interface; } else { qCWarning(RUQOLA_LOG) << "No interface defined for " << type; } } SearchMessageFilterProxyModel *RocketChatAccount::searchMessageFilterProxyModel() const { return mSearchMessageFilterProxyModel; } SearchMessageModel *RocketChatAccount::searchMessageModel() const { return mSearchMessageModel; } void RocketChatAccount::initializeAuthenticationPlugins() { //TODO change it when we change server //Clean up at the end. const QVector lstPlugins = AuthenticationManager::self()->pluginsList(); qCDebug(RUQOLA_LOG) <<" void RocketChatAccount::initializeAuthenticationPlugins()" << lstPlugins.count(); mLstPluginAuthenticationInterface.clear(); mLstInfos.clear(); for (PluginAuthentication *abstractPlugin : lstPlugins) { AuthenticationInfo info; info.setIconName(abstractPlugin->iconName()); info.setName(abstractPlugin->name()); info.setOauthType(abstractPlugin->type()); if (info.isValid()) { mLstInfos.append(info); } PluginAuthenticationInterface *interface = abstractPlugin->createInterface(this); interface->setAccount(this); mRuqolaServerConfig->addRuqolaAuthenticationSupport(abstractPlugin->type()); mLstPluginAuthenticationInterface.insert(abstractPlugin->type(), interface); //For the moment initialize default interface if (abstractPlugin->type() == AuthenticationManager::OauthType::Password) { mDefaultAuthenticationInterface = interface; } qCDebug(RUQOLA_LOG) << " plugin type " << abstractPlugin->type(); } //TODO fill ??? or store QVector } PluginAuthenticationInterface *RocketChatAccount::defaultAuthenticationInterface() const { return mDefaultAuthenticationInterface; } InputCompleterModel *RocketChatAccount::inputCompleterModel() const { return mInputTextManager->inputCompleterModel(); } LoginMethodModel *RocketChatAccount::loginMethodModel() const { return mLoginMethodModel; } QString RocketChatAccount::authToken() const { return settings()->authToken(); } QString RocketChatAccount::userName() const { return settings()->userName(); } void RocketChatAccount::setAccountName(const QString &accountname) { //Initialize new account room ManagerDataPaths::self()->initializeAccountPath(accountname); //qDebug() << "void RocketChatAccount::setAccountName(const QString &servername)"<accountConfigFileName(accountname)); settings()->setAccountName(accountname); } QString RocketChatAccount::accountName() const { return settings()->accountName(); } QString RocketChatAccount::userID() const { return settings()->userId(); } QString RocketChatAccount::password() const { return settings()->password(); } void RocketChatAccount::setAuthToken(const QString &token) { settings()->setAuthToken(token); } void RocketChatAccount::setPassword(const QString &password) { settings()->setPassword(password); } void RocketChatAccount::setUserName(const QString &username) { settings()->setUserName(username); } void RocketChatAccount::setUserID(const QString &userID) { settings()->setUserId(userID); } QString RocketChatAccount::serverUrl() const { return settings()->serverUrl(); } void RocketChatAccount::setServerUrl(const QString &serverURL) { settings()->setServerUrl(serverURL); restApi()->setServerUrl(serverURL); mEmojiManager->setServerUrl(serverURL); } QString RocketChatAccount::recordingVideoPath() const { return mCache->recordingVideoPath(accountName()); } QString RocketChatAccount::recordingImagePath() const { return mCache->recordingImagePath(accountName()); } void RocketChatAccount::downloadFile(const QString &downloadFileUrl, const QUrl &localFile) { mCache->downloadFile(downloadFileUrl, localFile, false); } QUrl RocketChatAccount::attachmentUrl(const QString &url) { return mCache->attachmentUrl(url); } void RocketChatAccount::loadHistory(const QString &roomID, const QString &channelType, bool initial) { //TODO port to restapi Q_UNUSED(channelType); MessageModel *roomModel = messageModelForRoom(roomID); if (roomModel) { //TODO add autotest for it ! QJsonArray params; params.append(QJsonValue(roomID)); // Load history const qint64 endDateTime = roomModel->lastTimestamp(); if (initial || roomModel->isEmpty()) { params.append(QJsonValue(QJsonValue::Null)); params.append(QJsonValue(50)); // Max number of messages to load; QJsonObject dateObject; //qDebug() << "roomModel->lastTimestamp()" << roomModel->lastTimestamp() << " ROOMID " << roomID; dateObject[QStringLiteral("$date")] = QJsonValue(endDateTime); params.append(dateObject); } else { const qint64 startDateTime = roomModel->generateNewStartTimeStamp(endDateTime); QJsonObject dateObjectEnd; dateObjectEnd[QStringLiteral("$date")] = QJsonValue(endDateTime); //qDebug() << " QDATE TIME END" << QDateTime::fromMSecsSinceEpoch(endDateTime) << " START " << QDateTime::fromMSecsSinceEpoch(startDateTime) << " ROOMID" << roomID; params.append(dateObjectEnd); params.append(QJsonValue(50)); // Max number of messages to load; QJsonObject dateObjectStart; //qDebug() << "roomModel->lastTimestamp()" << roomModel->lastTimestamp() << " ROOMID " << roomID; dateObjectStart[QStringLiteral("$date")] = QJsonValue(startDateTime); params.append(dateObjectStart); } ddp()->loadHistory(params); } else { qCWarning(RUQOLA_LOG) << "Room is not found " << roomID; } } void RocketChatAccount::setServerVersion(const QString &version) { qCDebug(RUQOLA_LOG) << " void RocketChatAccount::setServerVersion(const QString &version)" << version; mRuqolaServerConfig->setServerVersion(version); } bool RocketChatAccount::needAdaptNewSubscriptionRC60() const { return mRuqolaServerConfig->needAdaptNewSubscriptionRC60(); } bool RocketChatAccount::otrEnabled() const { return mRuqolaServerConfig->otrEnabled(); } bool RocketChatAccount::encryptedEnabled() const { return mRuqolaServerConfig->encryptionEnabled(); } bool RocketChatAccount::allowMessagePinningEnabled() const { return mRuqolaServerConfig->allowMessagePinningEnabled(); } bool RocketChatAccount::allowMessageSnippetingEnabled() const { return mRuqolaServerConfig->allowMessageSnippetingEnabled(); } bool RocketChatAccount::allowMessageStarringEnabled() const { return mRuqolaServerConfig->allowMessageStarringEnabled(); } bool RocketChatAccount::allowMessageDeletingEnabled() const { return mRuqolaServerConfig->allowMessageDeletingEnabled(); } bool RocketChatAccount::threadsEnabled() const { return mRuqolaServerConfig->threadsEnabled(); } bool RocketChatAccount::discussionEnabled() const { return mRuqolaServerConfig->discussionEnabled(); } QString RocketChatAccount::serverVersionStr() const { return mRuqolaServerConfig->serverVersionStr(); } ServerConfigInfo *RocketChatAccount::serverConfigInfo() const { return mServerConfigInfo; } bool RocketChatAccount::jitsiEnabled() const { return mRuqolaServerConfig->jitsiEnabled(); } void RocketChatAccount::groupInfo(const QString &roomId) { restApi()->groupInfo(roomId); } void RocketChatAccount::switchEditingMode(bool b) { if (mEditingMode != b) { mEditingMode = b; Q_EMIT editingModeChanged(); } } +void RocketChatAccount::setSortUnreadOnTop(bool b) +{ + if (mUnreadOnTop != b) { + mUnreadOnTop = b; + mRoomFilterProxyModel->invalidate(); + } +} + void RocketChatAccount::kickUser(const QString &roomId, const QString &userId, const QString &channelType) { if (channelType == QStringLiteral("c")) { restApi()->channelKick(roomId, userId); } else if (channelType == QStringLiteral("p")) { restApi()->groupKick(roomId, userId); } else { qCWarning(RUQOLA_LOG) << " unsupport kickUser room for type " << channelType; } } void RocketChatAccount::rolesInRoom(const QString &roomId, const QString &channelType) { if (channelType == QStringLiteral("c")) { restApi()->getChannelRoles(roomId); } else if (channelType == QStringLiteral("p")) { restApi()->getGroupRoles(roomId); } else { qCWarning(RUQOLA_LOG) << " unsupport get roles room for type " << channelType; } } void RocketChatAccount::switchingToRoom(const QString &roomID) { clearTypingNotification(); checkInitializedRoom(roomID); } void RocketChatAccount::changeRoles(const QString &roomId, const QString &userId, const QString &channelType, RocketChatAccount::RoleType roleType) { if (channelType == QStringLiteral("c")) { switch (roleType) { case RocketChatAccount::AddOwner: restApi()->channelAddOwner(roomId, userId); break; case RocketChatAccount::AddLeader: if (mRuqolaServerConfig->hasAtLeastVersion(0, 75, 0)) { restApi()->channelAddLeader(roomId, userId); } else { qCWarning(RUQOLA_LOG) << " RocketChatAccount::AddLeader is not supported before server 0.75"; } break; case RocketChatAccount::AddModerator: restApi()->channelAddModerator(roomId, userId); break; case RocketChatAccount::RemoveOwner: restApi()->channelRemoveOwner(roomId, userId); break; case RocketChatAccount::RemoveLeader: if (mRuqolaServerConfig->hasAtLeastVersion(0, 75, 0)) { restApi()->channelRemoveLeader(roomId, userId); } else { qCWarning(RUQOLA_LOG) << " RocketChatAccount::RemoveLeader is not supported before server 0.75"; } break; case RocketChatAccount::RemoveModerator: restApi()->channelRemoveModerator(roomId, userId); break; } } else if (channelType == QStringLiteral("p")) { switch (roleType) { case RocketChatAccount::AddOwner: restApi()->groupAddOwner(roomId, userId); break; case RocketChatAccount::AddLeader: restApi()->groupAddLeader(roomId, userId); break; case RocketChatAccount::AddModerator: restApi()->groupAddModerator(roomId, userId); break; case RocketChatAccount::RemoveOwner: restApi()->groupRemoveOwner(roomId, userId); break; case RocketChatAccount::RemoveLeader: restApi()->groupRemoveLeader(roomId, userId); break; case RocketChatAccount::RemoveModerator: restApi()->groupRemoveModerator(roomId, userId); break; } } else { qCWarning(RUQOLA_LOG) << " unsupport changeRoles room for type " << channelType; } } void RocketChatAccount::channelInfo(const QString &roomId) { restApi()->channelInfo(roomId); } bool RocketChatAccount::allowEditingMessages() const { return mRuqolaServerConfig->allowMessageEditing(); } void RocketChatAccount::parseOtr(const QJsonArray &contents) { const Otr t = mOtrManager->parseOtr(contents); qDebug() << " void RocketChatAccount::parseOtr(const QJsonArray &contents)"<avatarUrlFromCacheOnly(sender); //qDebug() << " iconFileName"<inputChannelAutocomplete(pattern, exceptions); } void RocketChatAccount::inputUserAutocomplete(const QString &pattern, const QString &exceptions) { ddp()->inputUserAutocomplete(pattern, exceptions); } void RocketChatAccount::inputTextCompleter(const QJsonObject &obj) { mInputTextManager->inputTextCompleter(obj); } void RocketChatAccount::displaySearchedMessage(const QJsonObject &obj) { mSearchMessageModel->parseResult(obj); } void RocketChatAccount::updateUser(const QJsonObject &object) { mUserModel->updateUser(object); } void RocketChatAccount::userStatusChanged(const User &user) { //qDebug() << " void RocketChatAccount::userStatusChanged(const User &user)"<setCurrentPresenceStatus(Utils::presenceStatusFromString(user.status())); } mRoomModel->userStatusChanged(user); } void RocketChatAccount::ignoreUser(const QString &rid, const QString &userId, bool ignore) { restApi()->ignoreUser(rid, userId, ignore); } void RocketChatAccount::blockUser(const QString &rid, bool block) { //TODO use restapi if (rid.isEmpty()) { qCWarning(RUQOLA_LOG) << " void RocketChatAccount::blockUser EMPTY rid ! block " << block; } else { //qDebug() << " void RocketChatAccount::blockUser userId " << userId << " block " << block << " rid " << rid << " own userdId" << userID(); const QString userId = Utils::userIdFromDirectChannel(rid, userID()); if (block) { ddp()->blockUser(rid, userId); } else { ddp()->unBlockUser(rid, userId); } } } void RocketChatAccount::clearTypingNotification() { mReceiveTypingNotificationManager->clearTypingNotification(); } void RocketChatAccount::checkInitializedRoom(const QString &roomId) { Room *r = mRoomModel->findRoom(roomId); if (r && !r->wasInitialized()) { r->setWasInitialized(true); ddp()->subscribeRoomMessage(roomId); if (!r->archived()) { membersInRoom(r->roomId(), r->channelType()); rolesInRoom(r->roomId(), r->channelType()); } loadHistory(r->roomId(), QString(), true /*initial loading*/); } } void RocketChatAccount::openDocumentation() { QDesktopServices::openUrl(QUrl(QStringLiteral("help:/"))); } void RocketChatAccount::channelGetAllUserMentions(const QString &roomId) { restApi()->channelGetAllUserMentions(roomId); } void RocketChatAccount::rolesChanged(const QJsonArray &contents) { for (int i = 0; i < contents.count(); ++i) { const QJsonObject obj = contents.at(i).toObject(); const QString scope = obj[QLatin1String("scope")].toString(); Room *room = mRoomModel->findRoom(scope); if (room) { room->updateRoles(obj); } } } void RocketChatAccount::createDiscussion(const QString &parentRoomId, const QString &discussionName, const QString &replyMessage, const QString &messageId) { restApi()->createDiscussion(parentRoomId, discussionName, replyMessage, messageId); } void RocketChatAccount::threadsInRoom(const QString &roomId) { if (mRuqolaServerConfig->hasAtLeastVersion(1, 0, 0)) { restApi()->getThreadsList(roomId); } else { qCWarning(RUQOLA_LOG) << " RocketChatAccount::threadsInRoom is not supported before server 1.0.0"; } } void RocketChatAccount::discussionsInRoom(const QString &roomId) { if (mRuqolaServerConfig->hasAtLeastVersion(1, 0, 0)) { restApi()->getDiscussions(roomId); } else { qCWarning(RUQOLA_LOG) << " RocketChatAccount::discussionsInRoom is not supported before server 1.0.0"; } } diff --git a/src/ruqolacore/rocketchataccount.h b/src/ruqolacore/rocketchataccount.h index 9c97fe52..be34f691 100644 --- a/src/ruqolacore/rocketchataccount.h +++ b/src/ruqolacore/rocketchataccount.h @@ -1,412 +1,415 @@ /* Copyright (c) 2017-2019 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License or ( at your option ) version 3 or, at the discretion of KDE e.V. ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef ROCKETCHATACCOUNT_H #define ROCKETCHATACCOUNT_H #include #include #include #include "rocketchataccountsettings.h" #include "libruqola_private_export.h" #include "authenticationinfo.h" #include "file.h" class TypingNotification; class UsersModel; class RoomModel; class RoomWrapper; class MessageModel; class DDPClient; class MessageQueue; class RocketChatBackend; class RoomFilterProxyModel; class RuqolaLogger; class RuqolaServerConfig; class UserCompleterModel; class UserCompleterFilterProxyModel; class StatusModel; class RocketChatCache; class EmojiManager; class OtrManager; class UsersForRoomFilterProxyModel; class FilesForRoomFilterProxyModel; class FilesForRoomModel; class SearchChannelModel; class SearchChannelFilterProxyModel; class LoginMethodModel; class InputCompleterModel; class InputTextManager; class PluginAuthenticationInterface; class Room; class SearchMessageModel; class SearchMessageFilterProxyModel; class ThreadsFilterProxyModel; class ServerConfigInfo; class ReceiveTypingNotificationManager; class EmoticonModel; class DiscussionsFilterProxyModel; class DiscussionsModel; class ThreadsModel; class MentionsModel; class MentionsFilterProxyModel; namespace RocketChatRestApi { class RestApiRequest; } class LIBRUQOLACORE_EXPORT RocketChatAccount : public QObject { Q_OBJECT Q_PROPERTY(QString userName READ userName WRITE setUserName NOTIFY userNameChanged) Q_PROPERTY(QString userID READ userID WRITE setUserID NOTIFY userIDChanged) Q_PROPERTY(QString serverUrl READ serverUrl WRITE setServerUrl NOTIFY serverUrlChanged) Q_PROPERTY(QString accountName READ accountName WRITE setAccountName NOTIFY accountNameChanged) Q_PROPERTY(QString password READ password WRITE setPassword NOTIFY passwordChanged) Q_PROPERTY(DDPClient::LoginStatus loginStatus READ loginStatus NOTIFY loginStatusChanged) Q_PROPERTY(bool editingMode READ editingMode NOTIFY editingModeChanged) public: explicit RocketChatAccount(const QString &accountName = QString(), QObject *parent = nullptr); ~RocketChatAccount(); enum RoomInfoType { Announcement, Description, Name, Topic, ReadOnly, Archive, RoomType, Encrypted, }; Q_ENUM(RoomInfoType) enum NotificationOptionsType { DisableNotifications, HideUnreadStatus, AudioNotifications, DesktopNotifications, DesktopDurationNotifications, DesktopSoundNotifications, EmailNotifications, MobilePushNotifications, UnreadAlert, MuteGroupMentions }; Q_ENUM(NotificationOptionsType) enum RoleType { AddOwner, AddLeader, AddModerator, RemoveOwner, RemoveLeader, RemoveModerator }; Q_ENUM(RoleType) Q_INVOKABLE UsersModel *usersModel() const; Q_INVOKABLE RoomModel *roomModel() const; Q_INVOKABLE RoomFilterProxyModel *roomFilterProxyModel() const; Q_INVOKABLE UsersForRoomFilterProxyModel *usersForRoomFilterProxyModel(const QString &roomId) const; Q_INVOKABLE RoomWrapper *getRoomWrapper(const QString &roomId); Q_INVOKABLE MessageModel *messageModelForRoom(const QString &roomID); Q_INVOKABLE QString getUserCurrentMessage(const QString &roomId); Q_INVOKABLE void setUserCurrentMessage(const QString &message, const QString &roomId); Q_INVOKABLE void textEditing(const QString &roomId, const QString &str); Q_INVOKABLE void leaveRoom(const QString &roomId, const QString &channelType); Q_INVOKABLE void hideRoom(const QString &roomId, const QString &channelType = QString()); Q_INVOKABLE void tryLogin(); Q_INVOKABLE void logOut(); Q_INVOKABLE void clearUnreadMessages(const QString &roomId); Q_INVOKABLE void changeFavorite(const QString &roomId, bool checked); Q_INVOKABLE void sendMessage(const QString &roomID, const QString &message); Q_INVOKABLE void updateMessage(const QString &roomID, const QString &messageId, const QString &message); Q_INVOKABLE void openChannel(const QString &url); Q_INVOKABLE void joinJitsiConfCall(const QString &roomId); Q_INVOKABLE void createNewChannel(const QString &name, bool readOnly, bool privateRoom, const QString &userNames, bool encryptedRoom, const QString &password, bool broadcast); Q_INVOKABLE void joinRoom(const QString &roomId, const QString &joinCode = QString()); Q_INVOKABLE void openDirectChannel(const QString &username); Q_INVOKABLE void listEmojiCustom(); Q_INVOKABLE void setDefaultStatus(User::PresenceStatus status); Q_INVOKABLE void changeDefaultStatus(int index); Q_INVOKABLE void createJitsiConfCall(const QString &roomId); Q_INVOKABLE void deleteMessage(const QString &messageId, const QString &roomId); Q_INVOKABLE void userAutocomplete(const QString &searchText, const QString &exception); Q_INVOKABLE void eraseRoom(const QString &roomId, const QString &channelType); Q_INVOKABLE void changeChannelSettings(const QString &roomId, RocketChatAccount::RoomInfoType infoType, const QVariant &newValue, const QString &channelType = QString()); Q_INVOKABLE void changeNotificationsSettings(const QString &roomId, RocketChatAccount::NotificationOptionsType notificationsType, const QVariant &newValue); Q_INVOKABLE QString recordingVideoPath() const; Q_INVOKABLE QString recordingImagePath() const; Q_INVOKABLE void downloadFile(const QString &downloadFileUrl, const QUrl &localFile); Q_INVOKABLE void starMessage(const QString &messageId, bool starred); Q_INVOKABLE void pinMessage(const QString &messageId, bool pinned); Q_INVOKABLE void uploadFile(const QString &roomId, const QString &description, const QString &messageText, const QUrl &fileUrl); Q_INVOKABLE QString serverUrl() const; Q_INVOKABLE QString avatarUrl(const QString &userId); Q_INVOKABLE StatusModel *statusModel() const; Q_INVOKABLE Q_REQUIRED_RESULT QUrl attachmentUrl(const QString &url); Q_INVOKABLE void loadHistory(const QString &roomID, const QString &channelType = QString(), bool initial = false); Q_INVOKABLE Q_REQUIRED_RESULT bool allowEditingMessages() const; Q_INVOKABLE Q_REQUIRED_RESULT bool otrEnabled() const; Q_INVOKABLE void channelAndPrivateAutocomplete(const QString &pattern); Q_INVOKABLE UserCompleterFilterProxyModel *userCompleterFilterModelProxy() const; Q_INVOKABLE void roomFiles(const QString &roomId, const QString &channelType = QString()); Q_INVOKABLE FilesForRoomFilterProxyModel *filesForRoomFilterProxyModel() const; Q_INVOKABLE void addUserToRoom(const QString &username, const QString &roomId, const QString &channelType); Q_INVOKABLE SearchChannelFilterProxyModel *searchChannelFilterProxyModel() const; Q_INVOKABLE InputCompleterModel *inputCompleterModel() const; Q_INVOKABLE LoginMethodModel *loginMethodModel() const; Q_INVOKABLE Room *getRoom(const QString &roomId); Q_INVOKABLE DiscussionsFilterProxyModel *discussionsFilterProxyModel() const; Q_INVOKABLE ThreadsFilterProxyModel *threadsFilterProxyModel() const; Q_INVOKABLE MentionsFilterProxyModel *mentionsFilterProxyModel() const; Q_INVOKABLE void changeDefaultAuthentication(int index); Q_INVOKABLE void messageSearch(const QString &pattern, const QString &rid); Q_INVOKABLE SearchMessageFilterProxyModel *searchMessageFilterProxyModel() const; Q_INVOKABLE void setInputTextChanged(const QString &str, int position); Q_INVOKABLE QString replaceWord(const QString &newWord, const QString &str, int position); Q_INVOKABLE void blockUser(const QString &userId, bool block); Q_INVOKABLE QString avatarUrlFromDirectChannel(const QString &rid); Q_INVOKABLE void deleteFileMessage(const QString &roomId, const QString &fileId, const QString &channelType); Q_INVOKABLE void openDocumentation(); Q_INVOKABLE void clearSearchModel(); Q_INVOKABLE void reactOnMessage(const QString &messageId, const QString &emoji, bool shouldReact); Q_INVOKABLE void ignoreUser(const QString &rid, const QString &userId, bool ignore); Q_INVOKABLE ReceiveTypingNotificationManager *receiveTypingNotificationManager() const; Q_INVOKABLE Q_REQUIRED_RESULT bool encryptedEnabled() const; Q_INVOKABLE Q_REQUIRED_RESULT QString serverVersionStr() const; Q_INVOKABLE Q_REQUIRED_RESULT ServerConfigInfo *serverConfigInfo() const; Q_INVOKABLE Q_REQUIRED_RESULT bool jitsiEnabled() const; Q_INVOKABLE Q_REQUIRED_RESULT bool allowMessagePinningEnabled() const; Q_INVOKABLE Q_REQUIRED_RESULT bool allowMessageSnippetingEnabled() const; Q_INVOKABLE Q_REQUIRED_RESULT bool allowMessageStarringEnabled() const; Q_INVOKABLE Q_REQUIRED_RESULT bool allowMessageDeletingEnabled() const; Q_INVOKABLE void channelInfo(const QString &roomId); Q_INVOKABLE void groupInfo(const QString &roomId); Q_INVOKABLE void channelGetAllUserMentions(const QString &roomId); Q_INVOKABLE void switchEditingMode(bool b); + Q_INVOKABLE void setSortUnreadOnTop(bool b); Q_INVOKABLE void kickUser(const QString &rid, const QString &userId, const QString &channelType); Q_INVOKABLE void changeRoles(const QString &rid, const QString &userId, const QString &channelType, RocketChatAccount::RoleType roleType); Q_INVOKABLE void rolesInRoom(const QString &roomId, const QString &channelType); Q_INVOKABLE void switchingToRoom(const QString &roomID); Q_INVOKABLE void reportMessage(const QString &messageId, const QString &message); Q_INVOKABLE void getThreadMessages(const QString &threadMessageId); Q_INVOKABLE void createDiscussion(const QString &parentRoomName, const QString &discussionName, const QString &replyMessage, const QString &messageId); Q_INVOKABLE Q_REQUIRED_RESULT bool threadsEnabled() const; Q_INVOKABLE Q_REQUIRED_RESULT bool discussionEnabled() const; Q_INVOKABLE void threadsInRoom(const QString &roomId); Q_INVOKABLE void discussionsInRoom(const QString &roomId); SearchChannelModel *searchChannelModel() const; UserCompleterModel *userCompleterModel() const; RocketChatAccountSettings *settings() const; DDPClient *ddp(); Q_REQUIRED_RESULT bool editingMode() const; + Q_REQUIRED_RESULT bool sortUnreadOnTop() const; Q_REQUIRED_RESULT DDPClient::LoginStatus loginStatus(); RocketChatRestApi::RestApiRequest *restApi(); //Make it private in future void slotInformTypingStatus(const QString &room, bool typing); MessageQueue *messageQueue() const; RocketChatBackend *rocketChatBackend() const; RuqolaLogger *ruqolaLogger() const; void loadEmoji(const QJsonObject &obj); void parsePublicSettings(const QJsonObject &obj); RuqolaServerConfig *ruqolaServerConfig() const; void setUserName(const QString &username); Q_REQUIRED_RESULT QString userName() const; void setAccountName(const QString &servername); Q_REQUIRED_RESULT QString accountName() const; void setUserID(const QString &userID); Q_REQUIRED_RESULT QString userID() const; void setPassword(const QString &password); Q_REQUIRED_RESULT QString password() const; void setAuthToken(const QString &token); Q_REQUIRED_RESULT QString authToken() const; void setServerUrl(const QString &serverUrl); void sendNotification(const QJsonArray &contents); void parseOtr(const QJsonArray &contents); void setServerVersion(const QString &version); Q_REQUIRED_RESULT bool needAdaptNewSubscriptionRC60() const; EmojiManager *emojiManager() const; Q_REQUIRED_RESULT QString userStatusIconFileName(const QString &id); void membersInRoom(const QString &roomId, const QString &roomType); void parseUsersForRooms(const QJsonObject &obj, const QString &roomId); void loadAutoCompleteChannel(const QJsonObject &obj); void insertCompleterUsers(); void inputChannelAutocomplete(const QString &pattern, const QString &exceptions); void inputUserAutocomplete(const QString &pattern, const QString &exceptions); void inputTextCompleter(const QJsonObject &obj); PluginAuthenticationInterface *defaultAuthenticationInterface() const; SearchMessageModel *searchMessageModel() const; void displaySearchedMessage(const QJsonObject &obj); void updateUser(const QJsonObject &object); void removeSettings(); void rolesChanged(const QJsonArray &contents); Q_INVOKABLE EmoticonModel *emoticonModel() const; void setEmoticonModel(EmoticonModel *emoticonModel); FilesForRoomModel *filesModelForRoom() const; DiscussionsModel *discussionsModel() const; ThreadsModel *threadsModel() const; MentionsModel *mentionsModel() const; Q_SIGNALS: void connectedChanged(); void accountNameChanged(); void userNameChanged(); void userIDChanged(); void passwordChanged(); void serverUrlChanged(); void loginStatusChanged(); void logoutDone(const QString &accountname); void added(const QJsonObject &item); void changed(const QJsonObject &item); void removed(const QJsonObject &item); void notification(const QString &title, const QString &message, const QPixmap &pixmap); void fileDownloaded(const QString &filePath, const QUrl &cacheImageUrl); void updateNotification(bool hasAlert, int nbUnread, const QString &accountName); void missingChannelPassword(const QString &roomId); void editingModeChanged(); void jobFailed(const QString &message); private: Q_DISABLE_COPY(RocketChatAccount) void slotChannelFilesDone(const QJsonObject &obj, const QString &roomId); void slotChannelRolesDone(const QJsonObject &obj, const QString &roomId); void slotSplotLightDone(const QJsonObject &obj); void slotGetThreadMessagesDone(const QJsonObject &obj, const QString &threadMessageId); void slotGetThreadsListDone(const QJsonObject &obj, const QString &roomId); void slotGetDiscussionsListDone(const QJsonObject &obj, const QString &roomId); void slotGetAllUserMentionsDone(const QJsonObject &obj, const QString &roomId); void loadEmojiRestApi(const QJsonObject &obj); void slotSearchMessages(const QJsonObject &obj); void slotNeedToUpdateNotification(); void insertAvatarUrl(const QString &userId, const QString &url); void loadSettings(const QString &accountFileName); void clearModels(); void fillOauthModel(); void initializeAuthenticationPlugins(); void setDefaultAuthentication(AuthenticationManager::OauthType type); void userStatusChanged(const User &user); void setChannelJoinDone(const QString &roomId); void openArchivedRoom(const QString &roomId); void checkInitializedRoom(const QString &roomId); void clearTypingNotification(); QVector parseFilesInChannel(const QJsonObject &obj) const; PluginAuthenticationInterface *mDefaultAuthenticationInterface = nullptr; QHash mLstPluginAuthenticationInterface; QVector mLstInfos; RocketChatAccountSettings *mSettings = nullptr; EmojiManager *mEmojiManager = nullptr; TypingNotification *mTypingNotification = nullptr; UsersModel *mUserModel = nullptr; RoomModel *mRoomModel = nullptr; RoomFilterProxyModel *mRoomFilterProxyModel = nullptr; DDPClient *mDdp = nullptr; RocketChatRestApi::RestApiRequest *mRestApi = nullptr; MessageQueue *mMessageQueue = nullptr; RocketChatBackend *mRocketChatBackend = nullptr; RuqolaLogger *mRuqolaLogger = nullptr; RuqolaServerConfig *mRuqolaServerConfig = nullptr; UserCompleterModel *mUserCompleterModel = nullptr; UserCompleterFilterProxyModel *mUserCompleterFilterModelProxy = nullptr; StatusModel *mStatusModel = nullptr; RocketChatCache *mCache = nullptr; OtrManager *mOtrManager = nullptr; SearchChannelModel *mSearchChannelModel = nullptr; SearchChannelFilterProxyModel *mSearchChannelFilterProxyModel = nullptr; LoginMethodModel *mLoginMethodModel = nullptr; InputTextManager *mInputTextManager = nullptr; SearchMessageModel *mSearchMessageModel = nullptr; SearchMessageFilterProxyModel *mSearchMessageFilterProxyModel = nullptr; ReceiveTypingNotificationManager *mReceiveTypingNotificationManager = nullptr; ServerConfigInfo *mServerConfigInfo = nullptr; FilesForRoomModel *mFilesModelForRoom = nullptr; FilesForRoomFilterProxyModel *mFilesForRoomFilterProxyModel = nullptr; DiscussionsFilterProxyModel *mDiscussionsFilterProxyModel = nullptr; DiscussionsModel *mDiscussionsModel = nullptr; ThreadsModel *mThreadsModel = nullptr; ThreadsFilterProxyModel *mThreadsFilterProxyModel = nullptr; MentionsModel *mMentionsModel = nullptr; MentionsFilterProxyModel *mMentionsFilterProxyModel = nullptr; EmoticonModel *mEmoticonModel = nullptr; bool mEditingMode = false; + bool mUnreadOnTop = false; }; #endif // ROCKETCHATACCOUNT_H