mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-26 04:58:49 +03:00
Delete old room list
This commit is contained in:
parent
298822baea
commit
03d30a2abc
23 changed files with 76 additions and 3082 deletions
|
@ -311,8 +311,6 @@ set(SRC_FILES
|
||||||
src/ChatPage.cpp
|
src/ChatPage.cpp
|
||||||
src/Clipboard.cpp
|
src/Clipboard.cpp
|
||||||
src/ColorImageProvider.cpp
|
src/ColorImageProvider.cpp
|
||||||
src/CommunitiesList.cpp
|
|
||||||
src/CommunitiesListItem.cpp
|
|
||||||
src/CompletionProxyModel.cpp
|
src/CompletionProxyModel.cpp
|
||||||
src/DeviceVerificationFlow.cpp
|
src/DeviceVerificationFlow.cpp
|
||||||
src/EventAccessors.cpp
|
src/EventAccessors.cpp
|
||||||
|
@ -324,22 +322,14 @@ set(SRC_FILES
|
||||||
src/MxcImageProvider.cpp
|
src/MxcImageProvider.cpp
|
||||||
src/Olm.cpp
|
src/Olm.cpp
|
||||||
src/RegisterPage.cpp
|
src/RegisterPage.cpp
|
||||||
src/RoomInfoListItem.cpp
|
|
||||||
src/RoomList.cpp
|
|
||||||
src/SSOHandler.cpp
|
src/SSOHandler.cpp
|
||||||
src/SideBarActions.cpp
|
|
||||||
src/Splitter.cpp
|
|
||||||
src/TrayIcon.cpp
|
src/TrayIcon.cpp
|
||||||
src/UserInfoWidget.cpp
|
|
||||||
src/UserSettingsPage.cpp
|
src/UserSettingsPage.cpp
|
||||||
src/UsersModel.cpp
|
src/UsersModel.cpp
|
||||||
src/RoomsModel.cpp
|
src/RoomsModel.cpp
|
||||||
src/Utils.cpp
|
src/Utils.cpp
|
||||||
src/WebRTCSession.cpp
|
src/WebRTCSession.cpp
|
||||||
src/WelcomePage.cpp
|
src/WelcomePage.cpp
|
||||||
src/popups/PopupItem.cpp
|
|
||||||
src/popups/SuggestionsPopup.cpp
|
|
||||||
src/popups/UserMentions.cpp
|
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
|
|
||||||
third_party/blurhash/blurhash.cpp
|
third_party/blurhash/blurhash.cpp
|
||||||
|
@ -535,8 +525,6 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/CallManager.h
|
src/CallManager.h
|
||||||
src/ChatPage.h
|
src/ChatPage.h
|
||||||
src/Clipboard.h
|
src/Clipboard.h
|
||||||
src/CommunitiesList.h
|
|
||||||
src/CommunitiesListItem.h
|
|
||||||
src/CompletionProxyModel.h
|
src/CompletionProxyModel.h
|
||||||
src/DeviceVerificationFlow.h
|
src/DeviceVerificationFlow.h
|
||||||
src/InviteeItem.h
|
src/InviteeItem.h
|
||||||
|
@ -544,21 +532,13 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/MainWindow.h
|
src/MainWindow.h
|
||||||
src/MxcImageProvider.h
|
src/MxcImageProvider.h
|
||||||
src/RegisterPage.h
|
src/RegisterPage.h
|
||||||
src/RoomInfoListItem.h
|
|
||||||
src/RoomList.h
|
|
||||||
src/SSOHandler.h
|
src/SSOHandler.h
|
||||||
src/SideBarActions.h
|
|
||||||
src/Splitter.h
|
|
||||||
src/TrayIcon.h
|
src/TrayIcon.h
|
||||||
src/UserInfoWidget.h
|
|
||||||
src/UserSettingsPage.h
|
src/UserSettingsPage.h
|
||||||
src/UsersModel.h
|
src/UsersModel.h
|
||||||
src/RoomsModel.h
|
src/RoomsModel.h
|
||||||
src/WebRTCSession.h
|
src/WebRTCSession.h
|
||||||
src/WelcomePage.h
|
src/WelcomePage.h
|
||||||
src/popups/PopupItem.h
|
|
||||||
src/popups/SuggestionsPopup.h
|
|
||||||
src/popups/UserMentions.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
|
@ -72,6 +72,15 @@ Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Shortcut {
|
||||||
|
sequence: "Ctrl+Down"
|
||||||
|
onActivated: Rooms.nextRoom();
|
||||||
|
}
|
||||||
|
Shortcut {
|
||||||
|
sequence: "Ctrl+Up"
|
||||||
|
onActivated: Rooms.previousRoom();
|
||||||
|
}
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: deviceVerificationDialog
|
id: deviceVerificationDialog
|
||||||
|
|
||||||
|
|
309
src/ChatPage.cpp
309
src/ChatPage.cpp
|
@ -23,10 +23,6 @@
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
#include "Olm.h"
|
#include "Olm.h"
|
||||||
#include "RoomList.h"
|
|
||||||
#include "SideBarActions.h"
|
|
||||||
#include "Splitter.h"
|
|
||||||
#include "UserInfoWidget.h"
|
|
||||||
#include "UserSettingsPage.h"
|
#include "UserSettingsPage.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
#include "ui/OverlayModal.h"
|
#include "ui/OverlayModal.h"
|
||||||
|
@ -36,7 +32,6 @@
|
||||||
#include "notifications/Manager.h"
|
#include "notifications/Manager.h"
|
||||||
|
|
||||||
#include "dialogs/ReadReceipts.h"
|
#include "dialogs/ReadReceipts.h"
|
||||||
#include "popups/UserMentions.h"
|
|
||||||
#include "timeline/TimelineViewManager.h"
|
#include "timeline/TimelineViewManager.h"
|
||||||
|
|
||||||
#include "blurhash.hpp"
|
#include "blurhash.hpp"
|
||||||
|
@ -76,62 +71,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
topLayout_->setSpacing(0);
|
topLayout_->setSpacing(0);
|
||||||
topLayout_->setMargin(0);
|
topLayout_->setMargin(0);
|
||||||
|
|
||||||
communitiesList_ = new CommunitiesList(this);
|
|
||||||
topLayout_->addWidget(communitiesList_);
|
|
||||||
|
|
||||||
splitter = new Splitter(this);
|
|
||||||
splitter->setHandleWidth(0);
|
|
||||||
|
|
||||||
topLayout_->addWidget(splitter);
|
|
||||||
|
|
||||||
// SideBar
|
|
||||||
sideBar_ = new QFrame(this);
|
|
||||||
sideBar_->setObjectName("sideBar");
|
|
||||||
sideBar_->setMinimumWidth(::splitter::calculateSidebarSizes(QFont{}).normal);
|
|
||||||
sideBarLayout_ = new QVBoxLayout(sideBar_);
|
|
||||||
sideBarLayout_->setSpacing(0);
|
|
||||||
sideBarLayout_->setMargin(0);
|
|
||||||
|
|
||||||
sideBarTopWidget_ = new QWidget(sideBar_);
|
|
||||||
sidebarActions_ = new SideBarActions(this);
|
|
||||||
connect(
|
|
||||||
sidebarActions_, &SideBarActions::showSettings, this, &ChatPage::showUserSettingsPage);
|
|
||||||
connect(sidebarActions_, &SideBarActions::joinRoom, this, &ChatPage::joinRoom);
|
|
||||||
connect(sidebarActions_, &SideBarActions::createRoom, this, &ChatPage::createRoom);
|
|
||||||
|
|
||||||
user_info_widget_ = new UserInfoWidget(sideBar_);
|
|
||||||
connect(user_info_widget_, &UserInfoWidget::openGlobalUserProfile, this, [this]() {
|
|
||||||
UserProfile *userProfile = new UserProfile("", utils::localUser(), view_manager_);
|
|
||||||
emit view_manager_->openProfile(userProfile);
|
|
||||||
});
|
|
||||||
|
|
||||||
user_mentions_popup_ = new popups::UserMentions();
|
|
||||||
room_list_ = new RoomList(userSettings, sideBar_);
|
|
||||||
connect(room_list_, &RoomList::joinRoom, this, &ChatPage::joinRoom);
|
|
||||||
|
|
||||||
sideBarLayout_->addWidget(user_info_widget_);
|
|
||||||
sideBarLayout_->addWidget(room_list_);
|
|
||||||
sideBarLayout_->addWidget(sidebarActions_);
|
|
||||||
|
|
||||||
sideBarTopWidgetLayout_ = new QVBoxLayout(sideBarTopWidget_);
|
|
||||||
sideBarTopWidgetLayout_->setSpacing(0);
|
|
||||||
sideBarTopWidgetLayout_->setMargin(0);
|
|
||||||
|
|
||||||
// Content
|
|
||||||
content_ = new QFrame(this);
|
|
||||||
content_->setObjectName("mainContent");
|
|
||||||
contentLayout_ = new QVBoxLayout(content_);
|
|
||||||
contentLayout_->setSpacing(0);
|
|
||||||
contentLayout_->setMargin(0);
|
|
||||||
|
|
||||||
view_manager_ = new TimelineViewManager(callManager_, this);
|
view_manager_ = new TimelineViewManager(callManager_, this);
|
||||||
|
|
||||||
contentLayout_->addWidget(view_manager_->getWidget());
|
topLayout_->addWidget(view_manager_->getWidget());
|
||||||
|
|
||||||
// Splitter
|
|
||||||
splitter->addWidget(sideBar_);
|
|
||||||
splitter->addWidget(content_);
|
|
||||||
splitter->restoreSizes(parent->width());
|
|
||||||
|
|
||||||
connect(this,
|
connect(this,
|
||||||
&ChatPage::downloadedSecrets,
|
&ChatPage::downloadedSecrets,
|
||||||
|
@ -153,17 +95,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
trySync();
|
trySync();
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(
|
|
||||||
new QShortcut(QKeySequence("Ctrl+Down"), this), &QShortcut::activated, this, [this]() {
|
|
||||||
if (isVisible())
|
|
||||||
room_list_->nextRoom();
|
|
||||||
});
|
|
||||||
connect(
|
|
||||||
new QShortcut(QKeySequence("Ctrl+Up"), this), &QShortcut::activated, this, [this]() {
|
|
||||||
if (isVisible())
|
|
||||||
room_list_->previousRoom();
|
|
||||||
});
|
|
||||||
|
|
||||||
connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL);
|
connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL);
|
||||||
connect(&connectivityTimer_, &QTimer::timeout, this, [=]() {
|
connect(&connectivityTimer_, &QTimer::timeout, this, [=]() {
|
||||||
if (http::client()->access_token().empty()) {
|
if (http::client()->access_token().empty()) {
|
||||||
|
@ -185,10 +116,8 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
|
|
||||||
connect(this, &ChatPage::loggedOut, this, &ChatPage::logout);
|
connect(this, &ChatPage::loggedOut, this, &ChatPage::logout);
|
||||||
|
|
||||||
connect(
|
|
||||||
view_manager_, &TimelineViewManager::showRoomList, splitter, &Splitter::showFullRoomList);
|
|
||||||
connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList users) {
|
connect(view_manager_, &TimelineViewManager::inviteUsers, this, [this](QStringList users) {
|
||||||
const auto room_id = current_room_.toStdString();
|
const auto room_id = currentRoom().toStdString();
|
||||||
|
|
||||||
for (int ii = 0; ii < users.size(); ++ii) {
|
for (int ii = 0; ii < users.size(); ++ii) {
|
||||||
QTimer::singleShot(ii * 500, this, [this, room_id, ii, users]() {
|
QTimer::singleShot(ii * 500, this, [this, room_id, ii, users]() {
|
||||||
|
@ -211,29 +140,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(room_list_, &RoomList::roomChanged, this, [this](QString room_id) {
|
|
||||||
this->current_room_ = room_id;
|
|
||||||
});
|
|
||||||
connect(room_list_, &RoomList::roomChanged, splitter, &Splitter::showChatView);
|
|
||||||
|
|
||||||
connect(room_list_, &RoomList::acceptInvite, this, [this](const QString &room_id) {
|
|
||||||
joinRoom(room_id);
|
|
||||||
room_list_->removeRoom(room_id, currentRoom() == room_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(room_list_, &RoomList::declineInvite, this, [this](const QString &room_id) {
|
|
||||||
leaveRoom(room_id);
|
|
||||||
room_list_->removeRoom(room_id, currentRoom() == room_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(view_manager_,
|
|
||||||
&TimelineViewManager::updateRoomsLastMessage,
|
|
||||||
room_list_,
|
|
||||||
&RoomList::updateRoomDescription);
|
|
||||||
|
|
||||||
connect(
|
|
||||||
this, &ChatPage::updateGroupsInfo, communitiesList_, &CommunitiesList::setCommunities);
|
|
||||||
|
|
||||||
connect(this, &ChatPage::leftRoom, this, &ChatPage::removeRoom);
|
connect(this, &ChatPage::leftRoom, this, &ChatPage::removeRoom);
|
||||||
connect(this, &ChatPage::newRoom, this, &ChatPage::changeRoom, Qt::QueuedConnection);
|
connect(this, &ChatPage::newRoom, this, &ChatPage::changeRoom, Qt::QueuedConnection);
|
||||||
connect(this, &ChatPage::notificationsRetrieved, this, &ChatPage::sendNotifications);
|
connect(this, &ChatPage::notificationsRetrieved, this, &ChatPage::sendNotifications);
|
||||||
|
@ -248,60 +154,23 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
connect(communitiesList_,
|
|
||||||
&CommunitiesList::communityChanged,
|
|
||||||
this,
|
|
||||||
[this](const QString &groupId) {
|
|
||||||
current_community_ = groupId;
|
|
||||||
|
|
||||||
if (groupId == "world") {
|
|
||||||
auto hidden = communitiesList_->hiddenTagsAndCommunities();
|
|
||||||
std::set<QString> roomsToHide = communitiesList_->roomList(groupId);
|
|
||||||
for (const auto &hiddenTag : hidden) {
|
|
||||||
auto temp = communitiesList_->roomList(hiddenTag);
|
|
||||||
roomsToHide.insert(temp.begin(), temp.end());
|
|
||||||
}
|
|
||||||
|
|
||||||
room_list_->removeFilter(roomsToHide);
|
|
||||||
} else {
|
|
||||||
auto hidden = communitiesList_->hiddenTagsAndCommunities();
|
|
||||||
hidden.erase(current_community_);
|
|
||||||
|
|
||||||
auto roomsToShow = communitiesList_->roomList(groupId);
|
|
||||||
for (const auto &hiddenTag : hidden) {
|
|
||||||
for (const auto &r : communitiesList_->roomList(hiddenTag))
|
|
||||||
roomsToShow.erase(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
room_list_->applyFilter(roomsToShow);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(¬ificationsManager,
|
connect(¬ificationsManager,
|
||||||
&NotificationsManager::notificationClicked,
|
&NotificationsManager::notificationClicked,
|
||||||
this,
|
this,
|
||||||
[this](const QString &roomid, const QString &eventid) {
|
[this](const QString &roomid, const QString &eventid) {
|
||||||
Q_UNUSED(eventid)
|
Q_UNUSED(eventid)
|
||||||
room_list_->highlightSelectedRoom(roomid);
|
view_manager_->rooms()->setCurrentRoom(roomid);
|
||||||
activateWindow();
|
activateWindow();
|
||||||
});
|
});
|
||||||
connect(¬ificationsManager,
|
connect(¬ificationsManager,
|
||||||
&NotificationsManager::sendNotificationReply,
|
&NotificationsManager::sendNotificationReply,
|
||||||
this,
|
this,
|
||||||
[this](const QString &roomid, const QString &eventid, const QString &body) {
|
[this](const QString &roomid, const QString &eventid, const QString &body) {
|
||||||
|
view_manager_->rooms()->setCurrentRoom(roomid);
|
||||||
view_manager_->queueReply(roomid, eventid, body);
|
view_manager_->queueReply(roomid, eventid, body);
|
||||||
room_list_->highlightSelectedRoom(roomid);
|
|
||||||
activateWindow();
|
activateWindow();
|
||||||
});
|
});
|
||||||
|
|
||||||
setGroupViewState(userSettings_->groupView());
|
|
||||||
|
|
||||||
connect(userSettings_.data(),
|
|
||||||
&UserSettings::groupViewStateChanged,
|
|
||||||
this,
|
|
||||||
&ChatPage::setGroupViewState);
|
|
||||||
|
|
||||||
connect(this, &ChatPage::initializeRoomList, room_list_, &RoomList::initialize);
|
|
||||||
connect(
|
connect(
|
||||||
this,
|
this,
|
||||||
&ChatPage::initializeViews,
|
&ChatPage::initializeViews,
|
||||||
|
@ -312,30 +181,13 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
&ChatPage::initializeEmptyViews,
|
&ChatPage::initializeEmptyViews,
|
||||||
view_manager_,
|
view_manager_,
|
||||||
&TimelineViewManager::initializeRoomlist);
|
&TimelineViewManager::initializeRoomlist);
|
||||||
connect(this,
|
|
||||||
&ChatPage::initializeMentions,
|
|
||||||
user_mentions_popup_,
|
|
||||||
&popups::UserMentions::initializeMentions);
|
|
||||||
connect(
|
connect(
|
||||||
this, &ChatPage::chatFocusChanged, view_manager_, &TimelineViewManager::chatFocusChanged);
|
this, &ChatPage::chatFocusChanged, view_manager_, &TimelineViewManager::chatFocusChanged);
|
||||||
connect(this, &ChatPage::syncUI, this, [this](const mtx::responses::Rooms &rooms) {
|
connect(this, &ChatPage::syncUI, this, [this](const mtx::responses::Rooms &rooms) {
|
||||||
try {
|
|
||||||
room_list_->cleanupInvites(cache::invites());
|
|
||||||
} catch (const lmdb::error &e) {
|
|
||||||
nhlog::db()->error("failed to retrieve invites: {}", e.what());
|
|
||||||
}
|
|
||||||
|
|
||||||
view_manager_->sync(rooms);
|
view_manager_->sync(rooms);
|
||||||
removeLeftRooms(rooms.leave);
|
|
||||||
|
|
||||||
bool hasNotifications = false;
|
bool hasNotifications = false;
|
||||||
for (const auto &room : rooms.join) {
|
for (const auto &room : rooms.join) {
|
||||||
auto room_id = QString::fromStdString(room.first);
|
|
||||||
updateRoomNotificationCount(
|
|
||||||
room_id,
|
|
||||||
room.second.unread_notifications.notification_count,
|
|
||||||
room.second.unread_notifications.highlight_count);
|
|
||||||
|
|
||||||
if (room.second.unread_notifications.notification_count > 0)
|
if (room.second.unread_notifications.notification_count > 0)
|
||||||
hasNotifications = true;
|
hasNotifications = true;
|
||||||
}
|
}
|
||||||
|
@ -358,16 +210,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
||||||
emit notificationsRetrieved(std::move(res));
|
emit notificationsRetrieved(std::move(res));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
connect(this, &ChatPage::syncRoomlist, room_list_, &RoomList::sync);
|
|
||||||
connect(this, &ChatPage::syncTags, communitiesList_, &CommunitiesList::syncTags);
|
|
||||||
|
|
||||||
// Callbacks to update the user info (top left corner of the page).
|
|
||||||
connect(this, &ChatPage::setUserAvatar, user_info_widget_, &UserInfoWidget::setAvatar);
|
|
||||||
connect(this, &ChatPage::setUserDisplayName, this, [this](const QString &name) {
|
|
||||||
auto userid = utils::localUser();
|
|
||||||
user_info_widget_->setUserId(userid);
|
|
||||||
user_info_widget_->setDisplayName(name);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(
|
connect(
|
||||||
this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync, Qt::QueuedConnection);
|
this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync, Qt::QueuedConnection);
|
||||||
|
@ -420,8 +262,6 @@ ChatPage::dropToLoginPage(const QString &msg)
|
||||||
void
|
void
|
||||||
ChatPage::resetUI()
|
ChatPage::resetUI()
|
||||||
{
|
{
|
||||||
room_list_->clear();
|
|
||||||
user_info_widget_->reset();
|
|
||||||
view_manager_->clearAll();
|
view_manager_->clearAll();
|
||||||
|
|
||||||
emit unreadMessages(0);
|
emit unreadMessages(0);
|
||||||
|
@ -474,9 +314,6 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
|
||||||
view_manager_,
|
view_manager_,
|
||||||
&TimelineViewManager::updateReadReceipts);
|
&TimelineViewManager::updateReadReceipts);
|
||||||
|
|
||||||
connect(
|
|
||||||
cache::client(), &Cache::roomReadStatus, room_list_, &RoomList::updateReadStatus);
|
|
||||||
|
|
||||||
connect(cache::client(),
|
connect(cache::client(),
|
||||||
&Cache::removeNotification,
|
&Cache::removeNotification,
|
||||||
¬ificationsManager,
|
¬ificationsManager,
|
||||||
|
@ -553,9 +390,7 @@ ChatPage::loadStateFromCache()
|
||||||
olm::client()->load(cache::restoreOlmAccount(), STORAGE_SECRET_KEY);
|
olm::client()->load(cache::restoreOlmAccount(), STORAGE_SECRET_KEY);
|
||||||
|
|
||||||
emit initializeEmptyViews();
|
emit initializeEmptyViews();
|
||||||
emit initializeRoomList(cache::roomInfo());
|
|
||||||
emit initializeMentions(cache::getTimelineMentions());
|
emit initializeMentions(cache::getTimelineMentions());
|
||||||
emit syncTags(cache::roomInfo().toStdMap());
|
|
||||||
|
|
||||||
cache::calculateRoomReadStatus();
|
cache::calculateRoomReadStatus();
|
||||||
|
|
||||||
|
@ -593,38 +428,6 @@ ChatPage::removeRoom(const QString &room_id)
|
||||||
nhlog::db()->critical("failure while removing room: {}", e.what());
|
nhlog::db()->critical("failure while removing room: {}", e.what());
|
||||||
// TODO: Notify the user.
|
// TODO: Notify the user.
|
||||||
}
|
}
|
||||||
|
|
||||||
room_list_->removeRoom(room_id, room_id == current_room_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ChatPage::removeLeftRooms(const std::map<std::string, mtx::responses::LeftRoom> &rooms)
|
|
||||||
{
|
|
||||||
for (auto it = rooms.cbegin(); it != rooms.cend(); ++it) {
|
|
||||||
const auto room_id = QString::fromStdString(it->first);
|
|
||||||
room_list_->removeRoom(room_id, room_id == current_room_);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ChatPage::setGroupViewState(bool isEnabled)
|
|
||||||
{
|
|
||||||
if (!isEnabled) {
|
|
||||||
communitiesList_->communityChanged("world");
|
|
||||||
communitiesList_->hide();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
communitiesList_->show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ChatPage::updateRoomNotificationCount(const QString &room_id,
|
|
||||||
uint16_t notification_count,
|
|
||||||
uint16_t highlight_count)
|
|
||||||
{
|
|
||||||
room_list_->updateUnreadMessageCount(room_id, notification_count, highlight_count);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -672,18 +475,6 @@ ChatPage::sendNotifications(const mtx::responses::Notifications &res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
ChatPage::showNotificationsDialog(const QPoint &widgetPos)
|
|
||||||
{
|
|
||||||
auto notifDialog = user_mentions_popup_;
|
|
||||||
|
|
||||||
notifDialog->setGeometry(
|
|
||||||
widgetPos.x() - (width() / 10), widgetPos.y() + 25, width() / 5, height() / 2);
|
|
||||||
|
|
||||||
notifDialog->raise();
|
|
||||||
notifDialog->showPopup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatPage::tryInitialSync()
|
ChatPage::tryInitialSync()
|
||||||
{
|
{
|
||||||
|
@ -782,11 +573,9 @@ ChatPage::startInitialSync()
|
||||||
olm::handle_to_device_messages(res.to_device.events);
|
olm::handle_to_device_messages(res.to_device.events);
|
||||||
|
|
||||||
emit initializeViews(std::move(res.rooms));
|
emit initializeViews(std::move(res.rooms));
|
||||||
emit initializeRoomList(cache::roomInfo());
|
|
||||||
emit initializeMentions(cache::getTimelineMentions());
|
emit initializeMentions(cache::getTimelineMentions());
|
||||||
|
|
||||||
cache::calculateRoomReadStatus();
|
cache::calculateRoomReadStatus();
|
||||||
emit syncTags(cache::roomInfo().toStdMap());
|
|
||||||
} catch (const lmdb::error &e) {
|
} catch (const lmdb::error &e) {
|
||||||
nhlog::db()->error("failed to save state after initial sync: {}",
|
nhlog::db()->error("failed to save state after initial sync: {}",
|
||||||
e.what());
|
e.what());
|
||||||
|
@ -823,12 +612,8 @@ ChatPage::handleSyncResponse(const mtx::responses::Sync &res, const std::string
|
||||||
|
|
||||||
auto updates = cache::getRoomInfo(cache::client()->roomsWithStateUpdates(res));
|
auto updates = cache::getRoomInfo(cache::client()->roomsWithStateUpdates(res));
|
||||||
|
|
||||||
emit syncRoomlist(updates);
|
|
||||||
|
|
||||||
emit syncUI(res.rooms);
|
emit syncUI(res.rooms);
|
||||||
|
|
||||||
emit syncTags(cache::getRoomInfo(cache::client()->roomsWithTagUpdates(res)));
|
|
||||||
|
|
||||||
// if we process a lot of syncs (1 every 200ms), this means we clean the
|
// if we process a lot of syncs (1 every 200ms), this means we clean the
|
||||||
// db every 100s
|
// db every 100s
|
||||||
static int syncCounter = 0;
|
static int syncCounter = 0;
|
||||||
|
@ -932,7 +717,7 @@ ChatPage::joinRoomVia(const std::string &room_id,
|
||||||
emit showNotification(tr("Failed to remove invite: %1").arg(e.what()));
|
emit showNotification(tr("Failed to remove invite: %1").arg(e.what()));
|
||||||
}
|
}
|
||||||
|
|
||||||
room_list_->highlightSelectedRoom(QString::fromStdString(room_id));
|
view_manager_->rooms()->setCurrentRoom(QString::fromStdString(room_id));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -981,18 +766,17 @@ void
|
||||||
ChatPage::changeRoom(const QString &room_id)
|
ChatPage::changeRoom(const QString &room_id)
|
||||||
{
|
{
|
||||||
view_manager_->rooms()->setCurrentRoom(room_id);
|
view_manager_->rooms()->setCurrentRoom(room_id);
|
||||||
room_list_->highlightSelectedRoom(room_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
ChatPage::inviteUser(QString userid, QString reason)
|
ChatPage::inviteUser(QString userid, QString reason)
|
||||||
{
|
{
|
||||||
auto room = current_room_;
|
auto room = currentRoom();
|
||||||
|
|
||||||
if (QMessageBox::question(this,
|
if (QMessageBox::question(this,
|
||||||
tr("Confirm invite"),
|
tr("Confirm invite"),
|
||||||
tr("Do you really want to invite %1 (%2)?")
|
tr("Do you really want to invite %1 (%2)?")
|
||||||
.arg(cache::displayName(current_room_, userid))
|
.arg(cache::displayName(room, userid))
|
||||||
.arg(userid)) != QMessageBox::Yes)
|
.arg(userid)) != QMessageBox::Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1014,12 +798,12 @@ ChatPage::inviteUser(QString userid, QString reason)
|
||||||
void
|
void
|
||||||
ChatPage::kickUser(QString userid, QString reason)
|
ChatPage::kickUser(QString userid, QString reason)
|
||||||
{
|
{
|
||||||
auto room = current_room_;
|
auto room = currentRoom();
|
||||||
|
|
||||||
if (QMessageBox::question(this,
|
if (QMessageBox::question(this,
|
||||||
tr("Confirm kick"),
|
tr("Confirm kick"),
|
||||||
tr("Do you really want to kick %1 (%2)?")
|
tr("Do you really want to kick %1 (%2)?")
|
||||||
.arg(cache::displayName(current_room_, userid))
|
.arg(cache::displayName(room, userid))
|
||||||
.arg(userid)) != QMessageBox::Yes)
|
.arg(userid)) != QMessageBox::Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1041,12 +825,12 @@ ChatPage::kickUser(QString userid, QString reason)
|
||||||
void
|
void
|
||||||
ChatPage::banUser(QString userid, QString reason)
|
ChatPage::banUser(QString userid, QString reason)
|
||||||
{
|
{
|
||||||
auto room = current_room_;
|
auto room = currentRoom();
|
||||||
|
|
||||||
if (QMessageBox::question(this,
|
if (QMessageBox::question(this,
|
||||||
tr("Confirm ban"),
|
tr("Confirm ban"),
|
||||||
tr("Do you really want to ban %1 (%2)?")
|
tr("Do you really want to ban %1 (%2)?")
|
||||||
.arg(cache::displayName(current_room_, userid))
|
.arg(cache::displayName(room, userid))
|
||||||
.arg(userid)) != QMessageBox::Yes)
|
.arg(userid)) != QMessageBox::Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1068,12 +852,12 @@ ChatPage::banUser(QString userid, QString reason)
|
||||||
void
|
void
|
||||||
ChatPage::unbanUser(QString userid, QString reason)
|
ChatPage::unbanUser(QString userid, QString reason)
|
||||||
{
|
{
|
||||||
auto room = current_room_;
|
auto room = currentRoom();
|
||||||
|
|
||||||
if (QMessageBox::question(this,
|
if (QMessageBox::question(this,
|
||||||
tr("Confirm unban"),
|
tr("Confirm unban"),
|
||||||
tr("Do you really want to unban %1 (%2)?")
|
tr("Do you really want to unban %1 (%2)?")
|
||||||
.arg(cache::displayName(current_room_, userid))
|
.arg(cache::displayName(room, userid))
|
||||||
.arg(userid)) != QMessageBox::Yes)
|
.arg(userid)) != QMessageBox::Yes)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1175,51 +959,6 @@ ChatPage::getProfileInfo()
|
||||||
|
|
||||||
emit setUserAvatar(QString::fromStdString(res.avatar_url));
|
emit setUserAvatar(QString::fromStdString(res.avatar_url));
|
||||||
});
|
});
|
||||||
|
|
||||||
http::client()->joined_groups(
|
|
||||||
[this](const mtx::responses::JoinedGroups &res, mtx::http::RequestErr err) {
|
|
||||||
if (err) {
|
|
||||||
nhlog::net()->critical("failed to retrieve joined groups: {} {}",
|
|
||||||
static_cast<int>(err->status_code),
|
|
||||||
err->matrix_error.error);
|
|
||||||
emit updateGroupsInfo({});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit updateGroupsInfo(res);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ChatPage::hideSideBars()
|
|
||||||
{
|
|
||||||
// Don't hide side bar, if we are currently only showing the side bar!
|
|
||||||
if (view_manager_->getWidget()->isVisible()) {
|
|
||||||
communitiesList_->hide();
|
|
||||||
sideBar_->hide();
|
|
||||||
}
|
|
||||||
view_manager_->enableBackButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
ChatPage::showSideBars()
|
|
||||||
{
|
|
||||||
if (userSettings_->groupView())
|
|
||||||
communitiesList_->show();
|
|
||||||
|
|
||||||
sideBar_->show();
|
|
||||||
view_manager_->disableBackButton();
|
|
||||||
content_->show();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint64_t
|
|
||||||
ChatPage::timelineWidth()
|
|
||||||
{
|
|
||||||
int sidebarWidth = sideBar_->minimumSize().width();
|
|
||||||
sidebarWidth += communitiesList_->minimumSize().width();
|
|
||||||
nhlog::ui()->info("timelineWidth: {}", size().width() - sidebarWidth);
|
|
||||||
|
|
||||||
return size().width() - sidebarWidth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1305,7 +1044,8 @@ ChatPage::startChat(QString userid)
|
||||||
if (std::find(room_members.begin(),
|
if (std::find(room_members.begin(),
|
||||||
room_members.end(),
|
room_members.end(),
|
||||||
(userid).toStdString()) != room_members.end()) {
|
(userid).toStdString()) != room_members.end()) {
|
||||||
room_list_->highlightSelectedRoom(QString::fromStdString(room_id));
|
view_manager_->rooms()->setCurrentRoom(
|
||||||
|
QString::fromStdString(room_id));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1406,7 +1146,7 @@ ChatPage::handleMatrixUri(const QByteArray &uri)
|
||||||
|
|
||||||
for (auto roomid : joined_rooms) {
|
for (auto roomid : joined_rooms) {
|
||||||
if (roomid == targetRoomId) {
|
if (roomid == targetRoomId) {
|
||||||
room_list_->highlightSelectedRoom(mxid1);
|
view_manager_->rooms()->setCurrentRoom(mxid1);
|
||||||
if (!mxid2.isEmpty())
|
if (!mxid2.isEmpty())
|
||||||
view_manager_->showEvent(mxid1, mxid2);
|
view_manager_->showEvent(mxid1, mxid2);
|
||||||
return;
|
return;
|
||||||
|
@ -1424,7 +1164,7 @@ ChatPage::handleMatrixUri(const QByteArray &uri)
|
||||||
auto aliases = cache::client()->getRoomAliases(roomid);
|
auto aliases = cache::client()->getRoomAliases(roomid);
|
||||||
if (aliases) {
|
if (aliases) {
|
||||||
if (aliases->alias == targetRoomAlias) {
|
if (aliases->alias == targetRoomAlias) {
|
||||||
room_list_->highlightSelectedRoom(
|
view_manager_->rooms()->setCurrentRoom(
|
||||||
QString::fromStdString(roomid));
|
QString::fromStdString(roomid));
|
||||||
if (!mxid2.isEmpty())
|
if (!mxid2.isEmpty())
|
||||||
view_manager_->showEvent(
|
view_manager_->showEvent(
|
||||||
|
@ -1446,8 +1186,17 @@ ChatPage::handleMatrixUri(const QUrl &uri)
|
||||||
handleMatrixUri(uri.toString(QUrl::ComponentFormattingOption::FullyEncoded).toUtf8());
|
handleMatrixUri(uri.toString(QUrl::ComponentFormattingOption::FullyEncoded).toUtf8());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
bool
|
||||||
ChatPage::highlightRoom(const QString &room_id)
|
ChatPage::isRoomActive(const QString &room_id)
|
||||||
{
|
{
|
||||||
room_list_->highlightSelectedRoom(room_id);
|
return isActiveWindow() && currentRoom() == room_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
ChatPage::currentRoom() const
|
||||||
|
{
|
||||||
|
if (view_manager_->rooms()->currentRoom())
|
||||||
|
return view_manager_->rooms()->currentRoom()->roomId();
|
||||||
|
else
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,15 +27,10 @@
|
||||||
|
|
||||||
#include "CacheCryptoStructs.h"
|
#include "CacheCryptoStructs.h"
|
||||||
#include "CacheStructs.h"
|
#include "CacheStructs.h"
|
||||||
#include "CommunitiesList.h"
|
|
||||||
#include "notifications/Manager.h"
|
#include "notifications/Manager.h"
|
||||||
|
|
||||||
class OverlayModal;
|
class OverlayModal;
|
||||||
class RoomList;
|
|
||||||
class SideBarActions;
|
|
||||||
class Splitter;
|
|
||||||
class TimelineViewManager;
|
class TimelineViewManager;
|
||||||
class UserInfoWidget;
|
|
||||||
class UserSettings;
|
class UserSettings;
|
||||||
class NotificationsManager;
|
class NotificationsManager;
|
||||||
class TimelineModel;
|
class TimelineModel;
|
||||||
|
@ -53,11 +48,6 @@ struct Notifications;
|
||||||
struct Sync;
|
struct Sync;
|
||||||
struct Timeline;
|
struct Timeline;
|
||||||
struct Rooms;
|
struct Rooms;
|
||||||
struct LeftRoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace popups {
|
|
||||||
class UserMentions;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
using SecretsToDecrypt = std::map<std::string, mtx::secret_storage::AesHmacSha2EncryptedData>;
|
using SecretsToDecrypt = std::map<std::string, mtx::secret_storage::AesHmacSha2EncryptedData>;
|
||||||
|
@ -71,7 +61,6 @@ public:
|
||||||
|
|
||||||
// Initialize all the components of the UI.
|
// Initialize all the components of the UI.
|
||||||
void bootstrap(QString userid, QString homeserver, QString token);
|
void bootstrap(QString userid, QString homeserver, QString token);
|
||||||
QString currentRoom() const { return current_room_; }
|
|
||||||
|
|
||||||
static ChatPage *instance() { return instance_; }
|
static ChatPage *instance() { return instance_; }
|
||||||
|
|
||||||
|
@ -80,14 +69,6 @@ public:
|
||||||
TimelineViewManager *timelineManager() { return view_manager_; }
|
TimelineViewManager *timelineManager() { return view_manager_; }
|
||||||
void deleteConfigs();
|
void deleteConfigs();
|
||||||
|
|
||||||
CommunitiesList *communitiesList() { return communitiesList_; }
|
|
||||||
|
|
||||||
//! Calculate the width of the message timeline.
|
|
||||||
uint64_t timelineWidth();
|
|
||||||
//! Hide the room & group list (if it was visible).
|
|
||||||
void hideSideBars();
|
|
||||||
//! Show the room/group list (if it was visible).
|
|
||||||
void showSideBars();
|
|
||||||
void initiateLogout();
|
void initiateLogout();
|
||||||
|
|
||||||
QString status() const;
|
QString status() const;
|
||||||
|
@ -95,6 +76,9 @@ public:
|
||||||
|
|
||||||
mtx::presence::PresenceState currentPresence() const;
|
mtx::presence::PresenceState currentPresence() const;
|
||||||
|
|
||||||
|
// TODO(Nico): Get rid of this!
|
||||||
|
QString currentRoom() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void handleMatrixUri(const QByteArray &uri);
|
void handleMatrixUri(const QByteArray &uri);
|
||||||
void handleMatrixUri(const QUrl &uri);
|
void handleMatrixUri(const QUrl &uri);
|
||||||
|
@ -102,7 +86,6 @@ public slots:
|
||||||
void startChat(QString userid);
|
void startChat(QString userid);
|
||||||
void leaveRoom(const QString &room_id);
|
void leaveRoom(const QString &room_id);
|
||||||
void createRoom(const mtx::requests::CreateRoom &req);
|
void createRoom(const mtx::requests::CreateRoom &req);
|
||||||
void highlightRoom(const QString &room_id);
|
|
||||||
void joinRoom(const QString &room);
|
void joinRoom(const QString &room);
|
||||||
void joinRoomVia(const std::string &room_id,
|
void joinRoomVia(const std::string &room_id,
|
||||||
const std::vector<std::string> &via,
|
const std::vector<std::string> &via,
|
||||||
|
@ -145,13 +128,10 @@ signals:
|
||||||
void leftRoom(const QString &room_id);
|
void leftRoom(const QString &room_id);
|
||||||
void newRoom(const QString &room_id);
|
void newRoom(const QString &room_id);
|
||||||
|
|
||||||
void initializeRoomList(QMap<QString, RoomInfo>);
|
|
||||||
void initializeViews(const mtx::responses::Rooms &rooms);
|
void initializeViews(const mtx::responses::Rooms &rooms);
|
||||||
void initializeEmptyViews();
|
void initializeEmptyViews();
|
||||||
void initializeMentions(const QMap<QString, mtx::responses::Notifications> ¬ifs);
|
void initializeMentions(const QMap<QString, mtx::responses::Notifications> ¬ifs);
|
||||||
void syncUI(const mtx::responses::Rooms &rooms);
|
void syncUI(const mtx::responses::Rooms &rooms);
|
||||||
void syncRoomlist(const std::map<QString, RoomInfo> &updates);
|
|
||||||
void syncTags(const std::map<QString, RoomInfo> &updates);
|
|
||||||
void dropToLoginPageCb(const QString &msg);
|
void dropToLoginPageCb(const QString &msg);
|
||||||
|
|
||||||
void notifyMessage(const QString &roomid,
|
void notifyMessage(const QString &roomid,
|
||||||
|
@ -161,7 +141,6 @@ signals:
|
||||||
const QString &message,
|
const QString &message,
|
||||||
const QImage &icon);
|
const QImage &icon);
|
||||||
|
|
||||||
void updateGroupsInfo(const mtx::responses::JoinedGroups &groups);
|
|
||||||
void retrievedPresence(const QString &statusMsg, mtx::presence::PresenceState state);
|
void retrievedPresence(const QString &statusMsg, mtx::presence::PresenceState state);
|
||||||
void themeChanged();
|
void themeChanged();
|
||||||
void decryptSidebarChanged();
|
void decryptSidebarChanged();
|
||||||
|
@ -207,65 +186,31 @@ private:
|
||||||
void getProfileInfo();
|
void getProfileInfo();
|
||||||
|
|
||||||
//! Check if the given room is currently open.
|
//! Check if the given room is currently open.
|
||||||
bool isRoomActive(const QString &room_id)
|
bool isRoomActive(const QString &room_id);
|
||||||
{
|
|
||||||
return isActiveWindow() && currentRoom() == room_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
using UserID = QString;
|
using UserID = QString;
|
||||||
using Membership = mtx::events::StateEvent<mtx::events::state::Member>;
|
using Membership = mtx::events::StateEvent<mtx::events::state::Member>;
|
||||||
using Memberships = std::map<std::string, Membership>;
|
using Memberships = std::map<std::string, Membership>;
|
||||||
|
|
||||||
using LeftRooms = std::map<std::string, mtx::responses::LeftRoom>;
|
|
||||||
void removeLeftRooms(const LeftRooms &rooms);
|
|
||||||
|
|
||||||
void loadStateFromCache();
|
void loadStateFromCache();
|
||||||
void resetUI();
|
void resetUI();
|
||||||
//! Decides whether or not to hide the group's sidebar.
|
|
||||||
void setGroupViewState(bool isEnabled);
|
|
||||||
|
|
||||||
template<class Collection>
|
template<class Collection>
|
||||||
Memberships getMemberships(const std::vector<Collection> &events) const;
|
Memberships getMemberships(const std::vector<Collection> &events) const;
|
||||||
|
|
||||||
//! Update the room with the new notification count.
|
|
||||||
void updateRoomNotificationCount(const QString &room_id,
|
|
||||||
uint16_t notification_count,
|
|
||||||
uint16_t highlight_count);
|
|
||||||
//! Send desktop notification for the received messages.
|
//! Send desktop notification for the received messages.
|
||||||
void sendNotifications(const mtx::responses::Notifications &);
|
void sendNotifications(const mtx::responses::Notifications &);
|
||||||
|
|
||||||
void showNotificationsDialog(const QPoint &point);
|
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void connectCallMessage();
|
void connectCallMessage();
|
||||||
|
|
||||||
QHBoxLayout *topLayout_;
|
QHBoxLayout *topLayout_;
|
||||||
Splitter *splitter;
|
|
||||||
|
|
||||||
QWidget *sideBar_;
|
|
||||||
QVBoxLayout *sideBarLayout_;
|
|
||||||
QWidget *sideBarTopWidget_;
|
|
||||||
QVBoxLayout *sideBarTopWidgetLayout_;
|
|
||||||
|
|
||||||
QFrame *content_;
|
|
||||||
QVBoxLayout *contentLayout_;
|
|
||||||
|
|
||||||
CommunitiesList *communitiesList_;
|
|
||||||
RoomList *room_list_;
|
|
||||||
|
|
||||||
TimelineViewManager *view_manager_;
|
TimelineViewManager *view_manager_;
|
||||||
SideBarActions *sidebarActions_;
|
|
||||||
|
|
||||||
QTimer connectivityTimer_;
|
QTimer connectivityTimer_;
|
||||||
std::atomic_bool isConnected_;
|
std::atomic_bool isConnected_;
|
||||||
|
|
||||||
QString current_room_;
|
|
||||||
QString current_community_;
|
|
||||||
|
|
||||||
UserInfoWidget *user_info_widget_;
|
|
||||||
|
|
||||||
popups::UserMentions *user_mentions_popup_;
|
|
||||||
|
|
||||||
// Global user settings.
|
// Global user settings.
|
||||||
QSharedPointer<UserSettings> userSettings_;
|
QSharedPointer<UserSettings> userSettings_;
|
||||||
|
|
||||||
|
|
|
@ -1,345 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include "CommunitiesList.h"
|
|
||||||
#include "Cache.h"
|
|
||||||
#include "Logging.h"
|
|
||||||
#include "MatrixClient.h"
|
|
||||||
#include "MxcImageProvider.h"
|
|
||||||
#include "Splitter.h"
|
|
||||||
#include "UserSettingsPage.h"
|
|
||||||
|
|
||||||
#include <mtx/responses/groups.hpp>
|
|
||||||
#include <nlohmann/json.hpp>
|
|
||||||
|
|
||||||
#include <QLabel>
|
|
||||||
|
|
||||||
CommunitiesList::CommunitiesList(QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
|
||||||
sizePolicy.setHorizontalStretch(0);
|
|
||||||
sizePolicy.setVerticalStretch(1);
|
|
||||||
setSizePolicy(sizePolicy);
|
|
||||||
|
|
||||||
topLayout_ = new QVBoxLayout(this);
|
|
||||||
topLayout_->setSpacing(0);
|
|
||||||
topLayout_->setMargin(0);
|
|
||||||
|
|
||||||
const auto sideBarSizes = splitter::calculateSidebarSizes(QFont{});
|
|
||||||
setFixedWidth(sideBarSizes.groups);
|
|
||||||
|
|
||||||
scrollArea_ = new QScrollArea(this);
|
|
||||||
scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
scrollArea_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
scrollArea_->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
|
|
||||||
scrollArea_->setWidgetResizable(true);
|
|
||||||
scrollArea_->setAlignment(Qt::AlignLeading | Qt::AlignTop | Qt::AlignVCenter);
|
|
||||||
|
|
||||||
contentsLayout_ = new QVBoxLayout();
|
|
||||||
contentsLayout_->setSpacing(0);
|
|
||||||
contentsLayout_->setMargin(0);
|
|
||||||
|
|
||||||
addGlobalItem();
|
|
||||||
contentsLayout_->addStretch(1);
|
|
||||||
|
|
||||||
scrollArea_->setLayout(contentsLayout_);
|
|
||||||
topLayout_->addWidget(scrollArea_);
|
|
||||||
|
|
||||||
connect(
|
|
||||||
this, &CommunitiesList::avatarRetrieved, this, &CommunitiesList::updateCommunityAvatar);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesList::setCommunities(const mtx::responses::JoinedGroups &response)
|
|
||||||
{
|
|
||||||
// remove all non-tag communities
|
|
||||||
auto it = communities_.begin();
|
|
||||||
while (it != communities_.end()) {
|
|
||||||
if (it->second->is_tag()) {
|
|
||||||
++it;
|
|
||||||
} else {
|
|
||||||
it = communities_.erase(it);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addGlobalItem();
|
|
||||||
|
|
||||||
for (const auto &group : response.groups)
|
|
||||||
addCommunity(group);
|
|
||||||
|
|
||||||
communities_["world"]->setPressedState(true);
|
|
||||||
selectedCommunity_ = "world";
|
|
||||||
emit communityChanged("world");
|
|
||||||
sortEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesList::syncTags(const std::map<QString, RoomInfo> &info)
|
|
||||||
{
|
|
||||||
for (const auto &room : info)
|
|
||||||
setTagsForRoom(room.first, room.second.tags);
|
|
||||||
emit communityChanged(selectedCommunity_);
|
|
||||||
sortEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesList::setTagsForRoom(const QString &room_id, const std::vector<std::string> &tags)
|
|
||||||
{
|
|
||||||
// create missing tag if any
|
|
||||||
for (const auto &tag : tags) {
|
|
||||||
// filter out tags we should ignore according to the spec
|
|
||||||
// https://matrix.org/docs/spec/client_server/r0.4.0.html#id154
|
|
||||||
// nheko currently does not make use of internal tags
|
|
||||||
// so we ignore any tag containig a `.` (which would indicate a tag
|
|
||||||
// in the form `tld.domain.*`) except for `m.*` and `u.*`.
|
|
||||||
if (tag.find(".") != ::std::string::npos && tag.compare(0, 2, "m.") &&
|
|
||||||
tag.compare(0, 2, "u."))
|
|
||||||
continue;
|
|
||||||
QString name = QString("tag:") + QString::fromStdString(tag);
|
|
||||||
if (!communityExists(name)) {
|
|
||||||
addCommunity(std::string("tag:") + tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// update membership of the room for all tags
|
|
||||||
auto it = communities_.begin();
|
|
||||||
while (it != communities_.end()) {
|
|
||||||
// Skip if the community is not a tag
|
|
||||||
if (!it->second->is_tag()) {
|
|
||||||
++it;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// insert or remove the room from the tag as appropriate
|
|
||||||
std::string current_tag =
|
|
||||||
it->first.right(static_cast<int>(it->first.size() - strlen("tag:")))
|
|
||||||
.toStdString();
|
|
||||||
if (std::find(tags.begin(), tags.end(), current_tag) != tags.end()) {
|
|
||||||
// the room has this tag
|
|
||||||
it->second->addRoom(room_id);
|
|
||||||
} else {
|
|
||||||
// the room does not have this tag
|
|
||||||
it->second->delRoom(room_id);
|
|
||||||
}
|
|
||||||
// Check if the tag is now empty, if yes delete it
|
|
||||||
if (it->second->rooms().empty()) {
|
|
||||||
it = communities_.erase(it);
|
|
||||||
} else {
|
|
||||||
++it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesList::addCommunity(const std::string &group_id)
|
|
||||||
{
|
|
||||||
auto hiddenTags = UserSettings::instance()->hiddenTags();
|
|
||||||
|
|
||||||
const auto id = QString::fromStdString(group_id);
|
|
||||||
|
|
||||||
CommunitiesListItem *list_item = new CommunitiesListItem(id, scrollArea_);
|
|
||||||
|
|
||||||
if (hiddenTags.contains(id))
|
|
||||||
list_item->setDisabled(true);
|
|
||||||
|
|
||||||
communities_.emplace(id, QSharedPointer<CommunitiesListItem>(list_item));
|
|
||||||
contentsLayout_->insertWidget(contentsLayout_->count() - 1, list_item);
|
|
||||||
|
|
||||||
connect(list_item,
|
|
||||||
&CommunitiesListItem::clicked,
|
|
||||||
this,
|
|
||||||
&CommunitiesList::highlightSelectedCommunity);
|
|
||||||
connect(list_item, &CommunitiesListItem::isDisabledChanged, this, [this]() {
|
|
||||||
for (const auto &community : communities_) {
|
|
||||||
if (community.second->isPressed()) {
|
|
||||||
emit highlightSelectedCommunity(community.first);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto hiddenTags = hiddenTagsAndCommunities();
|
|
||||||
// Qt < 5.14 compat
|
|
||||||
QStringList hiddenTags_;
|
|
||||||
for (auto &&t : hiddenTags)
|
|
||||||
hiddenTags_.push_back(t);
|
|
||||||
UserSettings::instance()->setHiddenTags(hiddenTags_);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (group_id.empty() || group_id.front() != '+')
|
|
||||||
return;
|
|
||||||
|
|
||||||
nhlog::ui()->debug("Add community: {}", group_id);
|
|
||||||
|
|
||||||
connect(this,
|
|
||||||
&CommunitiesList::groupProfileRetrieved,
|
|
||||||
this,
|
|
||||||
[this](const QString &id, const mtx::responses::GroupProfile &profile) {
|
|
||||||
if (communities_.find(id) == communities_.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
communities_.at(id)->setName(QString::fromStdString(profile.name));
|
|
||||||
|
|
||||||
if (!profile.avatar_url.empty())
|
|
||||||
fetchCommunityAvatar(id,
|
|
||||||
QString::fromStdString(profile.avatar_url));
|
|
||||||
});
|
|
||||||
connect(this,
|
|
||||||
&CommunitiesList::groupRoomsRetrieved,
|
|
||||||
this,
|
|
||||||
[this](const QString &id, const std::set<QString> &rooms) {
|
|
||||||
nhlog::ui()->info(
|
|
||||||
"Fetched rooms for {}: {}", id.toStdString(), rooms.size());
|
|
||||||
if (communities_.find(id) == communities_.end())
|
|
||||||
return;
|
|
||||||
|
|
||||||
communities_.at(id)->setRooms(rooms);
|
|
||||||
});
|
|
||||||
|
|
||||||
http::client()->group_profile(
|
|
||||||
group_id, [id, this](const mtx::responses::GroupProfile &res, mtx::http::RequestErr err) {
|
|
||||||
if (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit groupProfileRetrieved(id, res);
|
|
||||||
});
|
|
||||||
|
|
||||||
http::client()->group_rooms(
|
|
||||||
group_id, [id, this](const nlohmann::json &res, mtx::http::RequestErr err) {
|
|
||||||
if (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<QString> room_ids;
|
|
||||||
for (const auto &room : res.at("chunk"))
|
|
||||||
room_ids.emplace(QString::fromStdString(room.at("room_id")));
|
|
||||||
|
|
||||||
emit groupRoomsRetrieved(id, room_ids);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesList::updateCommunityAvatar(const QString &community_id, const QPixmap &img)
|
|
||||||
{
|
|
||||||
if (!communityExists(community_id)) {
|
|
||||||
nhlog::ui()->warn("Avatar update on nonexistent community {}",
|
|
||||||
community_id.toStdString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
communities_.at(community_id)->setAvatar(img.toImage());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesList::highlightSelectedCommunity(const QString &community_id)
|
|
||||||
{
|
|
||||||
if (!communityExists(community_id)) {
|
|
||||||
nhlog::ui()->debug("CommunitiesList: clicked unknown community");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedCommunity_ = community_id;
|
|
||||||
emit communityChanged(community_id);
|
|
||||||
|
|
||||||
for (const auto &community : communities_) {
|
|
||||||
if (community.first != community_id) {
|
|
||||||
community.second->setPressedState(false);
|
|
||||||
} else {
|
|
||||||
community.second->setPressedState(true);
|
|
||||||
scrollArea_->ensureWidgetVisible(community.second.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesList::fetchCommunityAvatar(const QString &id, const QString &avatarUrl)
|
|
||||||
{
|
|
||||||
MxcImageProvider::download(
|
|
||||||
QString(avatarUrl).remove(QStringLiteral("mxc://")),
|
|
||||||
QSize(96, 96),
|
|
||||||
[this, id](QString, QSize, QImage img, QString) {
|
|
||||||
if (img.isNull()) {
|
|
||||||
nhlog::net()->warn("failed to download avatar: {})", id.toStdString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit avatarRetrieved(id, QPixmap::fromImage(img));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<QString>
|
|
||||||
CommunitiesList::roomList(const QString &id) const
|
|
||||||
{
|
|
||||||
if (communityExists(id))
|
|
||||||
return communities_.at(id)->rooms();
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string>
|
|
||||||
CommunitiesList::currentTags() const
|
|
||||||
{
|
|
||||||
std::vector<std::string> tags;
|
|
||||||
for (auto &entry : communities_) {
|
|
||||||
CommunitiesListItem *item = entry.second.data();
|
|
||||||
if (item->is_tag())
|
|
||||||
tags.push_back(entry.first.mid(4).toStdString());
|
|
||||||
}
|
|
||||||
return tags;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::set<QString>
|
|
||||||
CommunitiesList::hiddenTagsAndCommunities() const
|
|
||||||
{
|
|
||||||
std::set<QString> hiddenTags;
|
|
||||||
for (auto &entry : communities_) {
|
|
||||||
if (entry.second->isDisabled())
|
|
||||||
hiddenTags.insert(entry.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hiddenTags;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesList::sortEntries()
|
|
||||||
{
|
|
||||||
std::vector<CommunitiesListItem *> header;
|
|
||||||
std::vector<CommunitiesListItem *> communities;
|
|
||||||
std::vector<CommunitiesListItem *> tags;
|
|
||||||
std::vector<CommunitiesListItem *> footer;
|
|
||||||
// remove all the contents and sort them in the 4 vectors
|
|
||||||
for (auto &entry : communities_) {
|
|
||||||
CommunitiesListItem *item = entry.second.data();
|
|
||||||
contentsLayout_->removeWidget(item);
|
|
||||||
// world is handled separately
|
|
||||||
if (entry.first == "world")
|
|
||||||
continue;
|
|
||||||
// sort the rest
|
|
||||||
if (item->is_tag())
|
|
||||||
if (entry.first == "tag:m.favourite")
|
|
||||||
header.push_back(item);
|
|
||||||
else if (entry.first == "tag:m.lowpriority")
|
|
||||||
footer.push_back(item);
|
|
||||||
else
|
|
||||||
tags.push_back(item);
|
|
||||||
else
|
|
||||||
communities.push_back(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
// now there remains only the stretch in the layout, remove it
|
|
||||||
QLayoutItem *stretch = contentsLayout_->itemAt(0);
|
|
||||||
contentsLayout_->removeItem(stretch);
|
|
||||||
|
|
||||||
contentsLayout_->addWidget(communities_["world"].data());
|
|
||||||
|
|
||||||
auto insert_widgets = [this](auto &vec) {
|
|
||||||
for (auto item : vec)
|
|
||||||
contentsLayout_->addWidget(item);
|
|
||||||
};
|
|
||||||
insert_widgets(header);
|
|
||||||
insert_widgets(communities);
|
|
||||||
insert_widgets(tags);
|
|
||||||
insert_widgets(footer);
|
|
||||||
|
|
||||||
contentsLayout_->addItem(stretch);
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QScrollArea>
|
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "CacheStructs.h"
|
|
||||||
#include "CommunitiesListItem.h"
|
|
||||||
|
|
||||||
namespace mtx::responses {
|
|
||||||
struct GroupProfile;
|
|
||||||
struct JoinedGroups;
|
|
||||||
}
|
|
||||||
|
|
||||||
class CommunitiesList : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
CommunitiesList(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
void clear() { communities_.clear(); }
|
|
||||||
|
|
||||||
void addCommunity(const std::string &id);
|
|
||||||
void removeCommunity(const QString &id) { communities_.erase(id); };
|
|
||||||
std::set<QString> roomList(const QString &id) const;
|
|
||||||
|
|
||||||
void syncTags(const std::map<QString, RoomInfo> &info);
|
|
||||||
void setTagsForRoom(const QString &id, const std::vector<std::string> &tags);
|
|
||||||
std::vector<std::string> currentTags() const;
|
|
||||||
std::set<QString> hiddenTagsAndCommunities() const;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void communityChanged(const QString &id);
|
|
||||||
void avatarRetrieved(const QString &id, const QPixmap &img);
|
|
||||||
void groupProfileRetrieved(const QString &group_id, const mtx::responses::GroupProfile &);
|
|
||||||
void groupRoomsRetrieved(const QString &group_id, const std::set<QString> &res);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void updateCommunityAvatar(const QString &id, const QPixmap &img);
|
|
||||||
void highlightSelectedCommunity(const QString &id);
|
|
||||||
void setCommunities(const mtx::responses::JoinedGroups &groups);
|
|
||||||
|
|
||||||
private:
|
|
||||||
void fetchCommunityAvatar(const QString &id, const QString &avatarUrl);
|
|
||||||
void addGlobalItem() { addCommunity("world"); }
|
|
||||||
void sortEntries();
|
|
||||||
|
|
||||||
//! Check whether or not a community id is currently managed.
|
|
||||||
bool communityExists(const QString &id) const
|
|
||||||
{
|
|
||||||
return communities_.find(id) != communities_.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString selectedCommunity_;
|
|
||||||
QVBoxLayout *topLayout_;
|
|
||||||
QVBoxLayout *contentsLayout_;
|
|
||||||
QScrollArea *scrollArea_;
|
|
||||||
|
|
||||||
std::map<QString, QSharedPointer<CommunitiesListItem>> communities_;
|
|
||||||
};
|
|
|
@ -1,201 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include "CommunitiesListItem.h"
|
|
||||||
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QMouseEvent>
|
|
||||||
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "ui/Painter.h"
|
|
||||||
#include "ui/Ripple.h"
|
|
||||||
#include "ui/RippleOverlay.h"
|
|
||||||
|
|
||||||
CommunitiesListItem::CommunitiesListItem(QString group_id, QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, groupId_(group_id)
|
|
||||||
{
|
|
||||||
setMouseTracking(true);
|
|
||||||
setAttribute(Qt::WA_Hover);
|
|
||||||
|
|
||||||
QPainterPath path;
|
|
||||||
path.addRect(0, 0, parent->width(), height());
|
|
||||||
rippleOverlay_ = new RippleOverlay(this);
|
|
||||||
rippleOverlay_->setClipPath(path);
|
|
||||||
rippleOverlay_->setClipping(true);
|
|
||||||
|
|
||||||
menu_ = new QMenu(this);
|
|
||||||
hideRoomsWithTagAction_ =
|
|
||||||
new QAction(tr("Hide rooms with this tag or from this community"), this);
|
|
||||||
hideRoomsWithTagAction_->setCheckable(true);
|
|
||||||
menu_->addAction(hideRoomsWithTagAction_);
|
|
||||||
connect(menu_, &QMenu::aboutToShow, this, [this]() {
|
|
||||||
hideRoomsWithTagAction_->setChecked(isDisabled_);
|
|
||||||
});
|
|
||||||
|
|
||||||
connect(hideRoomsWithTagAction_, &QAction::triggered, this, [this](bool checked) {
|
|
||||||
this->setDisabled(checked);
|
|
||||||
});
|
|
||||||
|
|
||||||
updateTooltip();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesListItem::contextMenuEvent(QContextMenuEvent *event)
|
|
||||||
{
|
|
||||||
menu_->popup(event->globalPos());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesListItem::setName(QString name)
|
|
||||||
{
|
|
||||||
name_ = name;
|
|
||||||
updateTooltip();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesListItem::setPressedState(bool state)
|
|
||||||
{
|
|
||||||
if (isPressed_ != state) {
|
|
||||||
isPressed_ = state;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesListItem::setDisabled(bool state)
|
|
||||||
{
|
|
||||||
if (isDisabled_ != state) {
|
|
||||||
isDisabled_ = state;
|
|
||||||
update();
|
|
||||||
emit isDisabledChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesListItem::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
if (event->buttons() == Qt::RightButton) {
|
|
||||||
QWidget::mousePressEvent(event);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit clicked(groupId_);
|
|
||||||
|
|
||||||
setPressedState(true);
|
|
||||||
|
|
||||||
QPoint pos = event->pos();
|
|
||||||
qreal radiusEndValue = static_cast<qreal>(width()) / 3;
|
|
||||||
|
|
||||||
auto ripple = new Ripple(pos);
|
|
||||||
ripple->setRadiusEndValue(radiusEndValue);
|
|
||||||
ripple->setOpacityStartValue(0.15);
|
|
||||||
ripple->setColor("white");
|
|
||||||
ripple->radiusAnimation()->setDuration(200);
|
|
||||||
ripple->opacityAnimation()->setDuration(400);
|
|
||||||
rippleOverlay_->addRipple(ripple);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesListItem::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
Painter p(this);
|
|
||||||
PainterHighQualityEnabler hq(p);
|
|
||||||
|
|
||||||
if (isPressed_)
|
|
||||||
p.fillRect(rect(), highlightedBackgroundColor_);
|
|
||||||
else if (isDisabled_)
|
|
||||||
p.fillRect(rect(), disabledBackgroundColor_);
|
|
||||||
else if (underMouse())
|
|
||||||
p.fillRect(rect(), hoverBackgroundColor_);
|
|
||||||
else
|
|
||||||
p.fillRect(rect(), backgroundColor_);
|
|
||||||
|
|
||||||
if (avatar_.isNull()) {
|
|
||||||
QPixmap source;
|
|
||||||
if (groupId_ == "world")
|
|
||||||
source = QPixmap(":/icons/icons/ui/world.png");
|
|
||||||
else if (groupId_ == "tag:m.favourite")
|
|
||||||
source = QPixmap(":/icons/icons/ui/star.png");
|
|
||||||
else if (groupId_ == "tag:m.lowpriority")
|
|
||||||
source = QPixmap(":/icons/icons/ui/lowprio.png");
|
|
||||||
else if (groupId_.startsWith("tag:"))
|
|
||||||
source = QPixmap(":/icons/icons/ui/tag.png");
|
|
||||||
|
|
||||||
if (source.isNull()) {
|
|
||||||
QFont font;
|
|
||||||
font.setPointSizeF(font.pointSizeF() * 1.3);
|
|
||||||
p.setFont(font);
|
|
||||||
|
|
||||||
p.drawLetterAvatar(utils::firstChar(resolveName()),
|
|
||||||
avatarFgColor_,
|
|
||||||
avatarBgColor_,
|
|
||||||
width(),
|
|
||||||
height(),
|
|
||||||
IconSize);
|
|
||||||
} else {
|
|
||||||
QPainter painter(&source);
|
|
||||||
painter.setCompositionMode(QPainter::CompositionMode_SourceIn);
|
|
||||||
painter.fillRect(source.rect(), avatarFgColor_);
|
|
||||||
painter.end();
|
|
||||||
|
|
||||||
const int imageSz = 32;
|
|
||||||
p.drawPixmap(
|
|
||||||
QRect(
|
|
||||||
(width() - imageSz) / 2, (height() - imageSz) / 2, imageSz, imageSz),
|
|
||||||
source);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
p.save();
|
|
||||||
|
|
||||||
p.drawAvatar(avatar_, width(), height(), IconSize);
|
|
||||||
p.restore();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesListItem::setAvatar(const QImage &img)
|
|
||||||
{
|
|
||||||
avatar_ = utils::scaleImageToPixmap(img, IconSize);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
QString
|
|
||||||
CommunitiesListItem::resolveName() const
|
|
||||||
{
|
|
||||||
if (!name_.isEmpty())
|
|
||||||
return name_;
|
|
||||||
if (groupId_.startsWith("tag:"))
|
|
||||||
return groupId_.right(static_cast<int>(groupId_.size() - strlen("tag:")));
|
|
||||||
if (!groupId_.startsWith("+"))
|
|
||||||
return QString("Group"); // Group with no name or id.
|
|
||||||
|
|
||||||
// Extract the localpart of the group.
|
|
||||||
auto firstPart = groupId_.split(':').at(0);
|
|
||||||
return firstPart.right(firstPart.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
CommunitiesListItem::updateTooltip()
|
|
||||||
{
|
|
||||||
if (groupId_ == "world")
|
|
||||||
setToolTip(tr("All rooms"));
|
|
||||||
else if (is_tag()) {
|
|
||||||
QStringRef tag =
|
|
||||||
groupId_.rightRef(static_cast<int>(groupId_.size() - strlen("tag:")));
|
|
||||||
if (tag == "m.favourite")
|
|
||||||
setToolTip(tr("Favourite rooms"));
|
|
||||||
else if (tag == "m.lowpriority")
|
|
||||||
setToolTip(tr("Low priority rooms"));
|
|
||||||
else if (tag == "m.server_notice")
|
|
||||||
setToolTip(tr("Server Notices", "Tag translation for m.server_notice"));
|
|
||||||
else if (tag.startsWith("u."))
|
|
||||||
setToolTip(tag.right(tag.size() - 2) + tr(" (tag)"));
|
|
||||||
else
|
|
||||||
setToolTip(tag + tr(" (tag)"));
|
|
||||||
} else {
|
|
||||||
QString name = resolveName();
|
|
||||||
setToolTip(name + tr(" (community)"));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "Config.h"
|
|
||||||
|
|
||||||
class RippleOverlay;
|
|
||||||
class QMouseEvent;
|
|
||||||
class QMenu;
|
|
||||||
|
|
||||||
class CommunitiesListItem : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QColor highlightedBackgroundColor READ highlightedBackgroundColor WRITE
|
|
||||||
setHighlightedBackgroundColor)
|
|
||||||
Q_PROPERTY(QColor disabledBackgroundColor READ disabledBackgroundColor WRITE
|
|
||||||
setDisabledBackgroundColor)
|
|
||||||
Q_PROPERTY(
|
|
||||||
QColor hoverBackgroundColor READ hoverBackgroundColor WRITE setHoverBackgroundColor)
|
|
||||||
Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor avatarFgColor READ avatarFgColor WRITE setAvatarFgColor)
|
|
||||||
Q_PROPERTY(QColor avatarBgColor READ avatarBgColor WRITE setAvatarBgColor)
|
|
||||||
|
|
||||||
public:
|
|
||||||
CommunitiesListItem(QString group_id, QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
void setName(QString name);
|
|
||||||
bool isPressed() const { return isPressed_; }
|
|
||||||
bool isDisabled() const { return isDisabled_; }
|
|
||||||
void setAvatar(const QImage &img);
|
|
||||||
|
|
||||||
void setRooms(std::set<QString> room_ids) { room_ids_ = std::move(room_ids); }
|
|
||||||
void addRoom(const QString &id) { room_ids_.insert(id); }
|
|
||||||
void delRoom(const QString &id) { room_ids_.erase(id); }
|
|
||||||
std::set<QString> rooms() const { return room_ids_; }
|
|
||||||
|
|
||||||
bool is_tag() const { return groupId_.startsWith("tag:"); }
|
|
||||||
|
|
||||||
QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; }
|
|
||||||
QColor disabledBackgroundColor() const { return disabledBackgroundColor_; }
|
|
||||||
QColor hoverBackgroundColor() const { return hoverBackgroundColor_; }
|
|
||||||
QColor backgroundColor() const { return backgroundColor_; }
|
|
||||||
|
|
||||||
QColor avatarFgColor() const { return avatarFgColor_; }
|
|
||||||
QColor avatarBgColor() const { return avatarBgColor_; }
|
|
||||||
|
|
||||||
void setHighlightedBackgroundColor(QColor &color) { highlightedBackgroundColor_ = color; }
|
|
||||||
void setDisabledBackgroundColor(QColor &color) { disabledBackgroundColor_ = color; }
|
|
||||||
void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; }
|
|
||||||
void setBackgroundColor(QColor &color) { backgroundColor_ = color; }
|
|
||||||
|
|
||||||
void setAvatarFgColor(QColor &color) { avatarFgColor_ = color; }
|
|
||||||
void setAvatarBgColor(QColor &color) { avatarBgColor_ = color; }
|
|
||||||
|
|
||||||
QSize sizeHint() const override
|
|
||||||
{
|
|
||||||
return QSize(IconSize + IconSize / 3, IconSize + IconSize / 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void clicked(const QString &group_id);
|
|
||||||
void isDisabledChanged();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setPressedState(bool state);
|
|
||||||
void setDisabled(bool state);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
const int IconSize = 36;
|
|
||||||
|
|
||||||
QString resolveName() const;
|
|
||||||
void updateTooltip();
|
|
||||||
|
|
||||||
std::set<QString> room_ids_;
|
|
||||||
|
|
||||||
QString name_;
|
|
||||||
QString groupId_;
|
|
||||||
QPixmap avatar_;
|
|
||||||
|
|
||||||
QColor highlightedBackgroundColor_;
|
|
||||||
QColor disabledBackgroundColor_;
|
|
||||||
QColor hoverBackgroundColor_;
|
|
||||||
QColor backgroundColor_;
|
|
||||||
|
|
||||||
QColor avatarFgColor_;
|
|
||||||
QColor avatarBgColor_;
|
|
||||||
|
|
||||||
bool isPressed_ = false;
|
|
||||||
bool isDisabled_ = false;
|
|
||||||
|
|
||||||
RippleOverlay *rippleOverlay_;
|
|
||||||
QMenu *menu_;
|
|
||||||
QAction *hideRoomsWithTagAction_;
|
|
||||||
};
|
|
|
@ -109,10 +109,6 @@ MainWindow::MainWindow(QWidget *parent)
|
||||||
userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool)));
|
userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool)));
|
||||||
connect(
|
connect(
|
||||||
userSettingsPage_, &UserSettingsPage::themeChanged, chat_page_, &ChatPage::themeChanged);
|
userSettingsPage_, &UserSettingsPage::themeChanged, chat_page_, &ChatPage::themeChanged);
|
||||||
connect(userSettingsPage_,
|
|
||||||
&UserSettingsPage::decryptSidebarChanged,
|
|
||||||
chat_page_,
|
|
||||||
&ChatPage::decryptSidebarChanged);
|
|
||||||
connect(trayIcon_,
|
connect(trayIcon_,
|
||||||
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||||
this,
|
this,
|
||||||
|
@ -176,20 +172,6 @@ MainWindow::setWindowTitle(int notificationCount)
|
||||||
QMainWindow::setWindowTitle(name);
|
QMainWindow::setWindowTitle(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MainWindow::showEvent(QShowEvent *event)
|
|
||||||
{
|
|
||||||
adjustSideBars();
|
|
||||||
QMainWindow::showEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
MainWindow::resizeEvent(QResizeEvent *event)
|
|
||||||
{
|
|
||||||
adjustSideBars();
|
|
||||||
QMainWindow::resizeEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MainWindow::event(QEvent *event)
|
MainWindow::event(QEvent *event)
|
||||||
{
|
{
|
||||||
|
@ -203,22 +185,6 @@ MainWindow::event(QEvent *event)
|
||||||
return QMainWindow::event(event);
|
return QMainWindow::event(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
MainWindow::adjustSideBars()
|
|
||||||
{
|
|
||||||
const auto sz = splitter::calculateSidebarSizes(QFont{});
|
|
||||||
|
|
||||||
const uint64_t timelineWidth = chat_page_->timelineWidth();
|
|
||||||
const uint64_t minAvailableWidth = sz.collapsePoint + sz.groups;
|
|
||||||
|
|
||||||
nhlog::ui()->info("timelineWidth: {}, min {}", timelineWidth, minAvailableWidth);
|
|
||||||
if (timelineWidth < minAvailableWidth) {
|
|
||||||
chat_page_->hideSideBars();
|
|
||||||
} else {
|
|
||||||
chat_page_->showSideBars();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
MainWindow::restoreWindowSize()
|
MainWindow::restoreWindowSize()
|
||||||
{
|
{
|
||||||
|
|
|
@ -77,13 +77,9 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void closeEvent(QCloseEvent *event) override;
|
void closeEvent(QCloseEvent *event) override;
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
|
||||||
void showEvent(QShowEvent *event) override;
|
|
||||||
bool event(QEvent *event) override;
|
bool event(QEvent *event) override;
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
//! Show or hide the sidebars based on window's size.
|
|
||||||
void adjustSideBars();
|
|
||||||
//! Handle interaction with the tray icon.
|
//! Handle interaction with the tray icon.
|
||||||
void iconActivated(QSystemTrayIcon::ActivationReason reason);
|
void iconActivated(QSystemTrayIcon::ActivationReason reason);
|
||||||
|
|
||||||
|
|
|
@ -1,522 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
|
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QInputDialog>
|
|
||||||
#include <QMenu>
|
|
||||||
#include <QMouseEvent>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QtGlobal>
|
|
||||||
|
|
||||||
#include "AvatarProvider.h"
|
|
||||||
#include "Cache.h"
|
|
||||||
#include "ChatPage.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "Logging.h"
|
|
||||||
#include "MatrixClient.h"
|
|
||||||
#include "RoomInfoListItem.h"
|
|
||||||
#include "Splitter.h"
|
|
||||||
#include "UserSettingsPage.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "ui/Ripple.h"
|
|
||||||
#include "ui/RippleOverlay.h"
|
|
||||||
|
|
||||||
constexpr int MaxUnreadCountDisplayed = 99;
|
|
||||||
|
|
||||||
struct WidgetMetrics
|
|
||||||
{
|
|
||||||
int maxHeight;
|
|
||||||
int iconSize;
|
|
||||||
int padding;
|
|
||||||
int unit;
|
|
||||||
|
|
||||||
int unreadLineWidth;
|
|
||||||
int unreadLineOffset;
|
|
||||||
|
|
||||||
int inviteBtnX;
|
|
||||||
int inviteBtnY;
|
|
||||||
};
|
|
||||||
|
|
||||||
WidgetMetrics
|
|
||||||
getMetrics(const QFont &font)
|
|
||||||
{
|
|
||||||
WidgetMetrics m;
|
|
||||||
|
|
||||||
const int height = QFontMetrics(font).lineSpacing();
|
|
||||||
|
|
||||||
m.unit = height;
|
|
||||||
m.maxHeight = std::ceil((double)height * 3.8);
|
|
||||||
m.iconSize = std::ceil((double)height * 2.8);
|
|
||||||
m.padding = std::ceil((double)height / 2.0);
|
|
||||||
m.unreadLineWidth = m.padding - m.padding / 3;
|
|
||||||
m.unreadLineOffset = m.padding - m.padding / 4;
|
|
||||||
|
|
||||||
m.inviteBtnX = m.iconSize + 2 * m.padding;
|
|
||||||
m.inviteBtnY = m.iconSize / 2.0 + m.padding + m.padding / 3.0;
|
|
||||||
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomInfoListItem::init(QWidget *parent)
|
|
||||||
{
|
|
||||||
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
|
|
||||||
setMouseTracking(true);
|
|
||||||
setAttribute(Qt::WA_Hover);
|
|
||||||
|
|
||||||
auto wm = getMetrics(QFont{});
|
|
||||||
setFixedHeight(wm.maxHeight);
|
|
||||||
|
|
||||||
QPainterPath path;
|
|
||||||
path.addRect(0, 0, parent->width(), height());
|
|
||||||
|
|
||||||
ripple_overlay_ = new RippleOverlay(this);
|
|
||||||
ripple_overlay_->setClipPath(path);
|
|
||||||
ripple_overlay_->setClipping(true);
|
|
||||||
|
|
||||||
avatar_ = new Avatar(nullptr, wm.iconSize);
|
|
||||||
avatar_->setLetter(utils::firstChar(roomName_));
|
|
||||||
avatar_->resize(wm.iconSize, wm.iconSize);
|
|
||||||
|
|
||||||
unreadCountFont_.setPointSizeF(unreadCountFont_.pointSizeF() * 0.8);
|
|
||||||
unreadCountFont_.setBold(true);
|
|
||||||
|
|
||||||
bubbleDiameter_ = QFontMetrics(unreadCountFont_).averageCharWidth() * 3;
|
|
||||||
|
|
||||||
menu_ = new QMenu(this);
|
|
||||||
leaveRoom_ = new QAction(tr("Leave room"), this);
|
|
||||||
connect(leaveRoom_, &QAction::triggered, this, [this]() { emit leaveRoom(roomId_); });
|
|
||||||
|
|
||||||
connect(menu_, &QMenu::aboutToShow, this, [this]() {
|
|
||||||
menu_->clear();
|
|
||||||
menu_->addAction(leaveRoom_);
|
|
||||||
|
|
||||||
menu_->addSection(QIcon(":/icons/icons/ui/tag.png"), tr("Tag room as:"));
|
|
||||||
|
|
||||||
auto roomInfo = cache::singleRoomInfo(roomId_.toStdString());
|
|
||||||
|
|
||||||
auto tags = ChatPage::instance()->communitiesList()->currentTags();
|
|
||||||
|
|
||||||
// add default tag, remove server notice tag
|
|
||||||
if (std::find(tags.begin(), tags.end(), "m.favourite") == tags.end())
|
|
||||||
tags.push_back("m.favourite");
|
|
||||||
if (std::find(tags.begin(), tags.end(), "m.lowpriority") == tags.end())
|
|
||||||
tags.push_back("m.lowpriority");
|
|
||||||
if (auto it = std::find(tags.begin(), tags.end(), "m.server_notice");
|
|
||||||
it != tags.end())
|
|
||||||
tags.erase(it);
|
|
||||||
|
|
||||||
for (const auto &tag : tags) {
|
|
||||||
QString tagName;
|
|
||||||
if (tag == "m.favourite")
|
|
||||||
tagName = tr("Favourite", "Standard matrix tag for favourites");
|
|
||||||
else if (tag == "m.lowpriority")
|
|
||||||
tagName =
|
|
||||||
tr("Low Priority", "Standard matrix tag for low priority rooms");
|
|
||||||
else if (tag == "m.server_notice")
|
|
||||||
tagName =
|
|
||||||
tr("Server Notice", "Standard matrix tag for server notices");
|
|
||||||
else if ((tag.size() > 2 && tag.substr(0, 2) == "u.") ||
|
|
||||||
tag.find(".") !=
|
|
||||||
std::string::npos) // tag manager creates tags without u., which
|
|
||||||
// is wrong, but we still want to display them
|
|
||||||
tagName = QString::fromStdString(tag.substr(2));
|
|
||||||
|
|
||||||
if (tagName.isEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
auto tagAction = menu_->addAction(tagName);
|
|
||||||
tagAction->setCheckable(true);
|
|
||||||
tagAction->setWhatsThis(tr("Adds or removes the specified tag.",
|
|
||||||
"WhatsThis hint for tag menu actions"));
|
|
||||||
|
|
||||||
for (const auto &riTag : roomInfo.tags) {
|
|
||||||
if (riTag == tag) {
|
|
||||||
tagAction->setChecked(true);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
connect(tagAction, &QAction::triggered, this, [this, tag](bool checked) {
|
|
||||||
if (checked)
|
|
||||||
http::client()->put_tag(
|
|
||||||
roomId_.toStdString(),
|
|
||||||
tag,
|
|
||||||
{},
|
|
||||||
[tag](mtx::http::RequestErr err) {
|
|
||||||
if (err) {
|
|
||||||
nhlog::ui()->error(
|
|
||||||
"Failed to add tag: {}, {}",
|
|
||||||
tag,
|
|
||||||
err->matrix_error.error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
else
|
|
||||||
http::client()->delete_tag(
|
|
||||||
roomId_.toStdString(),
|
|
||||||
tag,
|
|
||||||
[tag](mtx::http::RequestErr err) {
|
|
||||||
if (err) {
|
|
||||||
nhlog::ui()->error(
|
|
||||||
"Failed to delete tag: {}, {}",
|
|
||||||
tag,
|
|
||||||
err->matrix_error.error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
auto newTagAction = menu_->addAction(tr("New tag...", "Add a new tag to the room"));
|
|
||||||
connect(newTagAction, &QAction::triggered, this, [this]() {
|
|
||||||
QString tagName =
|
|
||||||
QInputDialog::getText(this,
|
|
||||||
tr("New Tag", "Tag name prompt title"),
|
|
||||||
tr("Tag:", "Tag name prompt"));
|
|
||||||
if (tagName.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
std::string tag = "u." + tagName.toStdString();
|
|
||||||
|
|
||||||
http::client()->put_tag(
|
|
||||||
roomId_.toStdString(), tag, {}, [tag](mtx::http::RequestErr err) {
|
|
||||||
if (err) {
|
|
||||||
nhlog::ui()->error("Failed to add tag: {}, {}",
|
|
||||||
tag,
|
|
||||||
err->matrix_error.error);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
RoomInfoListItem::RoomInfoListItem(QString room_id, const RoomInfo &info, QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, roomType_{info.is_invite ? RoomType::Invited : RoomType::Joined}
|
|
||||||
, roomId_(std::move(room_id))
|
|
||||||
, roomName_{QString::fromStdString(std::move(info.name))}
|
|
||||||
, isPressed_(false)
|
|
||||||
, unreadMsgCount_(0)
|
|
||||||
, unreadHighlightedMsgCount_(0)
|
|
||||||
{
|
|
||||||
init(parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomInfoListItem::resizeEvent(QResizeEvent *)
|
|
||||||
{
|
|
||||||
// Update ripple's clipping path.
|
|
||||||
QPainterPath path;
|
|
||||||
path.addRect(0, 0, width(), height());
|
|
||||||
|
|
||||||
const auto sidebarSizes = splitter::calculateSidebarSizes(QFont{});
|
|
||||||
|
|
||||||
if (width() > sidebarSizes.small)
|
|
||||||
setToolTip("");
|
|
||||||
else
|
|
||||||
setToolTip(roomName_);
|
|
||||||
|
|
||||||
ripple_overlay_->setClipPath(path);
|
|
||||||
ripple_overlay_->setClipping(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomInfoListItem::paintEvent(QPaintEvent *event)
|
|
||||||
{
|
|
||||||
Q_UNUSED(event);
|
|
||||||
|
|
||||||
QPainter p(this);
|
|
||||||
p.setRenderHint(QPainter::TextAntialiasing);
|
|
||||||
p.setRenderHint(QPainter::SmoothPixmapTransform);
|
|
||||||
p.setRenderHint(QPainter::Antialiasing);
|
|
||||||
|
|
||||||
QFontMetrics metrics(QFont{});
|
|
||||||
|
|
||||||
QPen titlePen(titleColor_);
|
|
||||||
QPen subtitlePen(subtitleColor_);
|
|
||||||
|
|
||||||
auto wm = getMetrics(QFont{});
|
|
||||||
|
|
||||||
QPixmap pixmap(avatar_->size() * p.device()->devicePixelRatioF());
|
|
||||||
pixmap.setDevicePixelRatio(p.device()->devicePixelRatioF());
|
|
||||||
if (isPressed_) {
|
|
||||||
p.fillRect(rect(), highlightedBackgroundColor_);
|
|
||||||
titlePen.setColor(highlightedTitleColor_);
|
|
||||||
subtitlePen.setColor(highlightedSubtitleColor_);
|
|
||||||
pixmap.fill(highlightedBackgroundColor_);
|
|
||||||
} else if (underMouse()) {
|
|
||||||
p.fillRect(rect(), hoverBackgroundColor_);
|
|
||||||
titlePen.setColor(hoverTitleColor_);
|
|
||||||
subtitlePen.setColor(hoverSubtitleColor_);
|
|
||||||
pixmap.fill(hoverBackgroundColor_);
|
|
||||||
} else {
|
|
||||||
p.fillRect(rect(), backgroundColor_);
|
|
||||||
titlePen.setColor(titleColor_);
|
|
||||||
subtitlePen.setColor(subtitleColor_);
|
|
||||||
pixmap.fill(backgroundColor_);
|
|
||||||
}
|
|
||||||
|
|
||||||
avatar_->render(&pixmap, QPoint(), QRegion(), RenderFlags(DrawChildren));
|
|
||||||
p.drawPixmap(QPoint(wm.padding, wm.padding), pixmap);
|
|
||||||
|
|
||||||
// Description line with the default font.
|
|
||||||
int bottom_y = wm.maxHeight - wm.padding - metrics.ascent() / 2;
|
|
||||||
|
|
||||||
const auto sidebarSizes = splitter::calculateSidebarSizes(QFont{});
|
|
||||||
|
|
||||||
if (width() > sidebarSizes.small) {
|
|
||||||
QFont headingFont;
|
|
||||||
headingFont.setWeight(QFont::Medium);
|
|
||||||
p.setFont(headingFont);
|
|
||||||
p.setPen(titlePen);
|
|
||||||
|
|
||||||
QFont tsFont;
|
|
||||||
tsFont.setPointSizeF(tsFont.pointSizeF() * 0.9);
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
|
|
||||||
const int msgStampWidth =
|
|
||||||
QFontMetrics(tsFont).width(lastMsgInfo_.descriptiveTime) + 4;
|
|
||||||
#else
|
|
||||||
const int msgStampWidth =
|
|
||||||
QFontMetrics(tsFont).horizontalAdvance(lastMsgInfo_.descriptiveTime) + 4;
|
|
||||||
#endif
|
|
||||||
// We use the full width of the widget if there is no unread msg bubble.
|
|
||||||
const int bottomLineWidthLimit = (unreadMsgCount_ > 0) ? msgStampWidth : 0;
|
|
||||||
|
|
||||||
// Name line.
|
|
||||||
QFontMetrics fontNameMetrics(headingFont);
|
|
||||||
int top_y = 2 * wm.padding + fontNameMetrics.ascent() / 2;
|
|
||||||
|
|
||||||
const auto name = metrics.elidedText(
|
|
||||||
roomName(),
|
|
||||||
Qt::ElideRight,
|
|
||||||
(width() - wm.iconSize - 2 * wm.padding - msgStampWidth) * 0.8);
|
|
||||||
p.drawText(QPoint(2 * wm.padding + wm.iconSize, top_y), name);
|
|
||||||
|
|
||||||
if (roomType_ == RoomType::Joined) {
|
|
||||||
p.setFont(QFont{});
|
|
||||||
p.setPen(subtitlePen);
|
|
||||||
|
|
||||||
int descriptionLimit = std::max(
|
|
||||||
0, width() - 3 * wm.padding - bottomLineWidthLimit - wm.iconSize);
|
|
||||||
auto description =
|
|
||||||
metrics.elidedText(lastMsgInfo_.body, Qt::ElideRight, descriptionLimit);
|
|
||||||
p.drawText(QPoint(2 * wm.padding + wm.iconSize, bottom_y), description);
|
|
||||||
|
|
||||||
// We show the last message timestamp.
|
|
||||||
p.save();
|
|
||||||
if (isPressed_) {
|
|
||||||
p.setPen(QPen(highlightedTimestampColor_));
|
|
||||||
} else if (underMouse()) {
|
|
||||||
p.setPen(QPen(hoverTimestampColor_));
|
|
||||||
} else {
|
|
||||||
p.setPen(QPen(timestampColor_));
|
|
||||||
}
|
|
||||||
|
|
||||||
p.setFont(tsFont);
|
|
||||||
p.drawText(QPoint(width() - wm.padding - msgStampWidth, top_y),
|
|
||||||
lastMsgInfo_.descriptiveTime);
|
|
||||||
p.restore();
|
|
||||||
} else {
|
|
||||||
int btnWidth = (width() - wm.iconSize - 6 * wm.padding) / 2;
|
|
||||||
|
|
||||||
acceptBtnRegion_ = QRectF(wm.inviteBtnX, wm.inviteBtnY, btnWidth, 20);
|
|
||||||
declineBtnRegion_ = QRectF(
|
|
||||||
wm.inviteBtnX + btnWidth + 2 * wm.padding, wm.inviteBtnY, btnWidth, 20);
|
|
||||||
|
|
||||||
QPainterPath acceptPath;
|
|
||||||
acceptPath.addRoundedRect(acceptBtnRegion_, 10, 10);
|
|
||||||
|
|
||||||
p.setPen(Qt::NoPen);
|
|
||||||
p.fillPath(acceptPath, btnColor_);
|
|
||||||
p.drawPath(acceptPath);
|
|
||||||
|
|
||||||
QPainterPath declinePath;
|
|
||||||
declinePath.addRoundedRect(declineBtnRegion_, 10, 10);
|
|
||||||
|
|
||||||
p.setPen(Qt::NoPen);
|
|
||||||
p.fillPath(declinePath, btnColor_);
|
|
||||||
p.drawPath(declinePath);
|
|
||||||
|
|
||||||
p.setPen(QPen(btnTextColor_));
|
|
||||||
p.setFont(QFont{});
|
|
||||||
p.drawText(acceptBtnRegion_,
|
|
||||||
Qt::AlignCenter,
|
|
||||||
metrics.elidedText(tr("Accept"), Qt::ElideRight, btnWidth));
|
|
||||||
p.drawText(declineBtnRegion_,
|
|
||||||
Qt::AlignCenter,
|
|
||||||
metrics.elidedText(tr("Decline"), Qt::ElideRight, btnWidth));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
p.setPen(Qt::NoPen);
|
|
||||||
|
|
||||||
if (unreadMsgCount_ > 0) {
|
|
||||||
QBrush brush;
|
|
||||||
brush.setStyle(Qt::SolidPattern);
|
|
||||||
if (unreadHighlightedMsgCount_ > 0) {
|
|
||||||
brush.setColor(mentionedColor());
|
|
||||||
} else {
|
|
||||||
brush.setColor(bubbleBgColor());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isPressed_)
|
|
||||||
brush.setColor(bubbleFgColor());
|
|
||||||
|
|
||||||
p.setBrush(brush);
|
|
||||||
p.setPen(Qt::NoPen);
|
|
||||||
p.setFont(unreadCountFont_);
|
|
||||||
|
|
||||||
// Extra space on the x-axis to accomodate the extra character space
|
|
||||||
// inside the bubble.
|
|
||||||
const int x_width = unreadMsgCount_ > MaxUnreadCountDisplayed
|
|
||||||
? QFontMetrics(p.font()).averageCharWidth()
|
|
||||||
: 0;
|
|
||||||
|
|
||||||
QRectF r(width() - bubbleDiameter_ - wm.padding - x_width,
|
|
||||||
bottom_y - bubbleDiameter_ / 2 - 5,
|
|
||||||
bubbleDiameter_ + x_width,
|
|
||||||
bubbleDiameter_);
|
|
||||||
|
|
||||||
if (width() == sidebarSizes.small)
|
|
||||||
r = QRectF(width() - bubbleDiameter_ - 5,
|
|
||||||
height() - bubbleDiameter_ - 5,
|
|
||||||
bubbleDiameter_ + x_width,
|
|
||||||
bubbleDiameter_);
|
|
||||||
|
|
||||||
p.setPen(Qt::NoPen);
|
|
||||||
p.drawEllipse(r);
|
|
||||||
|
|
||||||
p.setPen(QPen(bubbleFgColor()));
|
|
||||||
|
|
||||||
if (isPressed_)
|
|
||||||
p.setPen(QPen(bubbleBgColor()));
|
|
||||||
|
|
||||||
auto countTxt = unreadMsgCount_ > MaxUnreadCountDisplayed
|
|
||||||
? QString("99+")
|
|
||||||
: QString::number(unreadMsgCount_);
|
|
||||||
|
|
||||||
p.setBrush(Qt::NoBrush);
|
|
||||||
p.drawText(r.translated(0, -0.5), Qt::AlignCenter, countTxt);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isPressed_ && hasUnreadMessages_) {
|
|
||||||
QPen pen;
|
|
||||||
pen.setWidth(wm.unreadLineWidth);
|
|
||||||
pen.setColor(highlightedBackgroundColor_);
|
|
||||||
|
|
||||||
p.setPen(pen);
|
|
||||||
p.drawLine(0, wm.unreadLineOffset, 0, height() - wm.unreadLineOffset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomInfoListItem::updateUnreadMessageCount(int count, int highlightedCount)
|
|
||||||
{
|
|
||||||
unreadMsgCount_ = count;
|
|
||||||
unreadHighlightedMsgCount_ = highlightedCount;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
enum NotificationImportance : short
|
|
||||||
{
|
|
||||||
ImportanceDisabled = -1,
|
|
||||||
AllEventsRead = 0,
|
|
||||||
NewMessage = 1,
|
|
||||||
NewMentions = 2,
|
|
||||||
Invite = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
short int
|
|
||||||
RoomInfoListItem::calculateImportance() const
|
|
||||||
{
|
|
||||||
// Returns the degree of importance of the unread messages in the room.
|
|
||||||
// If sorting by importance is disabled in settings, this only ever
|
|
||||||
// returns ImportanceDisabled or Invite
|
|
||||||
if (isInvite()) {
|
|
||||||
return Invite;
|
|
||||||
} else if (!ChatPage::instance()->userSettings()->sortByImportance()) {
|
|
||||||
return ImportanceDisabled;
|
|
||||||
} else if (unreadHighlightedMsgCount_) {
|
|
||||||
return NewMentions;
|
|
||||||
} else if (unreadMsgCount_) {
|
|
||||||
return NewMessage;
|
|
||||||
} else {
|
|
||||||
return AllEventsRead;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomInfoListItem::setPressedState(bool state)
|
|
||||||
{
|
|
||||||
if (isPressed_ != state) {
|
|
||||||
isPressed_ = state;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomInfoListItem::contextMenuEvent(QContextMenuEvent *event)
|
|
||||||
{
|
|
||||||
Q_UNUSED(event);
|
|
||||||
|
|
||||||
if (roomType_ == RoomType::Invited)
|
|
||||||
return;
|
|
||||||
|
|
||||||
menu_->popup(event->globalPos());
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomInfoListItem::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
if (event->buttons() == Qt::RightButton) {
|
|
||||||
QWidget::mousePressEvent(event);
|
|
||||||
return;
|
|
||||||
} else if (event->buttons() == Qt::LeftButton) {
|
|
||||||
if (roomType_ == RoomType::Invited) {
|
|
||||||
const auto point = event->pos();
|
|
||||||
|
|
||||||
if (acceptBtnRegion_.contains(point))
|
|
||||||
emit acceptInvite(roomId_);
|
|
||||||
|
|
||||||
if (declineBtnRegion_.contains(point))
|
|
||||||
emit declineInvite(roomId_);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emit clicked(roomId_);
|
|
||||||
|
|
||||||
setPressedState(true);
|
|
||||||
|
|
||||||
// Ripple on mouse position by default.
|
|
||||||
QPoint pos = event->pos();
|
|
||||||
qreal radiusEndValue = static_cast<qreal>(width()) / 3;
|
|
||||||
|
|
||||||
Ripple *ripple = new Ripple(pos);
|
|
||||||
|
|
||||||
ripple->setRadiusEndValue(radiusEndValue);
|
|
||||||
ripple->setOpacityStartValue(0.15);
|
|
||||||
ripple->setColor(QColor("white"));
|
|
||||||
ripple->radiusAnimation()->setDuration(200);
|
|
||||||
ripple->opacityAnimation()->setDuration(400);
|
|
||||||
|
|
||||||
ripple_overlay_->addRipple(ripple);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomInfoListItem::setAvatar(const QString &avatar_url)
|
|
||||||
{
|
|
||||||
if (avatar_url.isEmpty())
|
|
||||||
avatar_->setLetter(utils::firstChar(roomName_));
|
|
||||||
else
|
|
||||||
avatar_->setImage(avatar_url);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomInfoListItem::setDescriptionMessage(const DescInfo &info)
|
|
||||||
{
|
|
||||||
lastMsgInfo_ = info;
|
|
||||||
update();
|
|
||||||
}
|
|
|
@ -1,210 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
|
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QAction>
|
|
||||||
#include <QDateTime>
|
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include <mtx/responses/sync.hpp>
|
|
||||||
|
|
||||||
#include "CacheStructs.h"
|
|
||||||
#include "UserSettingsPage.h"
|
|
||||||
#include "ui/Avatar.h"
|
|
||||||
|
|
||||||
class QMenu;
|
|
||||||
class RippleOverlay;
|
|
||||||
|
|
||||||
class RoomInfoListItem : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
Q_PROPERTY(QColor highlightedBackgroundColor READ highlightedBackgroundColor WRITE
|
|
||||||
setHighlightedBackgroundColor)
|
|
||||||
Q_PROPERTY(
|
|
||||||
QColor hoverBackgroundColor READ hoverBackgroundColor WRITE setHoverBackgroundColor)
|
|
||||||
Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor)
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor bubbleBgColor READ bubbleBgColor WRITE setBubbleBgColor)
|
|
||||||
Q_PROPERTY(QColor bubbleFgColor READ bubbleFgColor WRITE setBubbleFgColor)
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor titleColor READ titleColor WRITE setTitleColor)
|
|
||||||
Q_PROPERTY(QColor subtitleColor READ subtitleColor WRITE setSubtitleColor)
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor timestampColor READ timestampColor WRITE setTimestampColor)
|
|
||||||
Q_PROPERTY(QColor highlightedTimestampColor READ highlightedTimestampColor WRITE
|
|
||||||
setHighlightedTimestampColor)
|
|
||||||
Q_PROPERTY(QColor hoverTimestampColor READ hoverTimestampColor WRITE setHoverTimestampColor)
|
|
||||||
|
|
||||||
Q_PROPERTY(
|
|
||||||
QColor highlightedTitleColor READ highlightedTitleColor WRITE setHighlightedTitleColor)
|
|
||||||
Q_PROPERTY(QColor highlightedSubtitleColor READ highlightedSubtitleColor WRITE
|
|
||||||
setHighlightedSubtitleColor)
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor hoverTitleColor READ hoverTitleColor WRITE setHoverTitleColor)
|
|
||||||
Q_PROPERTY(QColor hoverSubtitleColor READ hoverSubtitleColor WRITE setHoverSubtitleColor)
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor mentionedColor READ mentionedColor WRITE setMentionedColor)
|
|
||||||
Q_PROPERTY(QColor btnColor READ btnColor WRITE setBtnColor)
|
|
||||||
Q_PROPERTY(QColor btnTextColor READ btnTextColor WRITE setBtnTextColor)
|
|
||||||
|
|
||||||
public:
|
|
||||||
RoomInfoListItem(QString room_id, const RoomInfo &info, QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
void updateUnreadMessageCount(int count, int highlightedCount);
|
|
||||||
void clearUnreadMessageCount() { updateUnreadMessageCount(0, 0); };
|
|
||||||
|
|
||||||
short int calculateImportance() const;
|
|
||||||
|
|
||||||
QString roomId() { return roomId_; }
|
|
||||||
bool isPressed() const { return isPressed_; }
|
|
||||||
int unreadMessageCount() const { return unreadMsgCount_; }
|
|
||||||
|
|
||||||
void setAvatar(const QString &avatar_url);
|
|
||||||
void setDescriptionMessage(const DescInfo &info);
|
|
||||||
DescInfo lastMessageInfo() const { return lastMsgInfo_; }
|
|
||||||
|
|
||||||
QColor highlightedBackgroundColor() const { return highlightedBackgroundColor_; }
|
|
||||||
QColor hoverBackgroundColor() const { return hoverBackgroundColor_; }
|
|
||||||
QColor hoverTitleColor() const { return hoverTitleColor_; }
|
|
||||||
QColor hoverSubtitleColor() const { return hoverSubtitleColor_; }
|
|
||||||
QColor hoverTimestampColor() const { return hoverTimestampColor_; }
|
|
||||||
QColor backgroundColor() const { return backgroundColor_; }
|
|
||||||
|
|
||||||
QColor highlightedTitleColor() const { return highlightedTitleColor_; }
|
|
||||||
QColor highlightedSubtitleColor() const { return highlightedSubtitleColor_; }
|
|
||||||
QColor highlightedTimestampColor() const { return highlightedTimestampColor_; }
|
|
||||||
|
|
||||||
QColor titleColor() const { return titleColor_; }
|
|
||||||
QColor subtitleColor() const { return subtitleColor_; }
|
|
||||||
QColor timestampColor() const { return timestampColor_; }
|
|
||||||
QColor btnColor() const { return btnColor_; }
|
|
||||||
QColor btnTextColor() const { return btnTextColor_; }
|
|
||||||
|
|
||||||
QColor bubbleFgColor() const { return bubbleFgColor_; }
|
|
||||||
QColor bubbleBgColor() const { return bubbleBgColor_; }
|
|
||||||
QColor mentionedColor() const { return mentionedFontColor_; }
|
|
||||||
|
|
||||||
void setHighlightedBackgroundColor(QColor &color) { highlightedBackgroundColor_ = color; }
|
|
||||||
void setHoverBackgroundColor(QColor &color) { hoverBackgroundColor_ = color; }
|
|
||||||
void setHoverSubtitleColor(QColor &color) { hoverSubtitleColor_ = color; }
|
|
||||||
void setHoverTitleColor(QColor &color) { hoverTitleColor_ = color; }
|
|
||||||
void setHoverTimestampColor(QColor &color) { hoverTimestampColor_ = color; }
|
|
||||||
void setBackgroundColor(QColor &color) { backgroundColor_ = color; }
|
|
||||||
void setTimestampColor(QColor &color) { timestampColor_ = color; }
|
|
||||||
|
|
||||||
void setHighlightedTitleColor(QColor &color) { highlightedTitleColor_ = color; }
|
|
||||||
void setHighlightedSubtitleColor(QColor &color) { highlightedSubtitleColor_ = color; }
|
|
||||||
void setHighlightedTimestampColor(QColor &color) { highlightedTimestampColor_ = color; }
|
|
||||||
|
|
||||||
void setTitleColor(QColor &color) { titleColor_ = color; }
|
|
||||||
void setSubtitleColor(QColor &color) { subtitleColor_ = color; }
|
|
||||||
|
|
||||||
void setBtnColor(QColor &color) { btnColor_ = color; }
|
|
||||||
void setBtnTextColor(QColor &color) { btnTextColor_ = color; }
|
|
||||||
|
|
||||||
void setBubbleFgColor(QColor &color) { bubbleFgColor_ = color; }
|
|
||||||
void setBubbleBgColor(QColor &color) { bubbleBgColor_ = color; }
|
|
||||||
void setMentionedColor(QColor &color) { mentionedFontColor_ = color; }
|
|
||||||
|
|
||||||
void setRoomName(const QString &name) { roomName_ = name; }
|
|
||||||
void setRoomType(bool isInvite)
|
|
||||||
{
|
|
||||||
if (isInvite)
|
|
||||||
roomType_ = RoomType::Invited;
|
|
||||||
else
|
|
||||||
roomType_ = RoomType::Joined;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isInvite() const { return roomType_ == RoomType::Invited; }
|
|
||||||
void setReadState(bool hasUnreadMessages)
|
|
||||||
{
|
|
||||||
if (hasUnreadMessages_ != hasUnreadMessages) {
|
|
||||||
hasUnreadMessages_ = hasUnreadMessages;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void clicked(const QString &room_id);
|
|
||||||
void leaveRoom(const QString &room_id);
|
|
||||||
void acceptInvite(const QString &room_id);
|
|
||||||
void declineInvite(const QString &room_id);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void setPressedState(bool state);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
void resizeEvent(QResizeEvent *event) override;
|
|
||||||
void contextMenuEvent(QContextMenuEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void init(QWidget *parent);
|
|
||||||
QString roomName() { return roomName_; }
|
|
||||||
|
|
||||||
RippleOverlay *ripple_overlay_;
|
|
||||||
Avatar *avatar_;
|
|
||||||
|
|
||||||
enum class RoomType
|
|
||||||
{
|
|
||||||
Joined,
|
|
||||||
Invited,
|
|
||||||
};
|
|
||||||
|
|
||||||
RoomType roomType_ = RoomType::Joined;
|
|
||||||
|
|
||||||
// State information for the invited rooms.
|
|
||||||
mtx::responses::InvitedRoom invitedRoom_;
|
|
||||||
|
|
||||||
QString roomId_;
|
|
||||||
QString roomName_;
|
|
||||||
|
|
||||||
DescInfo lastMsgInfo_;
|
|
||||||
|
|
||||||
QMenu *menu_;
|
|
||||||
QAction *leaveRoom_;
|
|
||||||
|
|
||||||
bool isPressed_ = false;
|
|
||||||
bool hasUnreadMessages_ = true;
|
|
||||||
|
|
||||||
int unreadMsgCount_ = 0;
|
|
||||||
int unreadHighlightedMsgCount_ = 0;
|
|
||||||
|
|
||||||
QColor highlightedBackgroundColor_;
|
|
||||||
QColor hoverBackgroundColor_;
|
|
||||||
QColor backgroundColor_;
|
|
||||||
|
|
||||||
QColor highlightedTitleColor_;
|
|
||||||
QColor highlightedSubtitleColor_;
|
|
||||||
|
|
||||||
QColor titleColor_;
|
|
||||||
QColor subtitleColor_;
|
|
||||||
|
|
||||||
QColor hoverTitleColor_;
|
|
||||||
QColor hoverSubtitleColor_;
|
|
||||||
|
|
||||||
QColor btnColor_;
|
|
||||||
QColor btnTextColor_;
|
|
||||||
|
|
||||||
QRectF acceptBtnRegion_;
|
|
||||||
QRectF declineBtnRegion_;
|
|
||||||
|
|
||||||
// Fonts
|
|
||||||
QColor mentionedFontColor_;
|
|
||||||
QFont unreadCountFont_;
|
|
||||||
int bubbleDiameter_;
|
|
||||||
|
|
||||||
QColor timestampColor_;
|
|
||||||
QColor highlightedTimestampColor_;
|
|
||||||
QColor hoverTimestampColor_;
|
|
||||||
|
|
||||||
QColor bubbleBgColor_;
|
|
||||||
QColor bubbleFgColor_;
|
|
||||||
|
|
||||||
friend struct room_sort;
|
|
||||||
};
|
|
535
src/RoomList.cpp
535
src/RoomList.cpp
|
@ -1,535 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
|
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <limits>
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include <QObject>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QScroller>
|
|
||||||
#include <QStyle>
|
|
||||||
#include <QStyleOption>
|
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
#include "Logging.h"
|
|
||||||
#include "MainWindow.h"
|
|
||||||
#include "RoomInfoListItem.h"
|
|
||||||
#include "RoomList.h"
|
|
||||||
#include "UserSettingsPage.h"
|
|
||||||
#include "Utils.h"
|
|
||||||
#include "ui/OverlayModal.h"
|
|
||||||
|
|
||||||
RoomList::RoomList(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
topLayout_ = new QVBoxLayout(this);
|
|
||||||
topLayout_->setSpacing(0);
|
|
||||||
topLayout_->setMargin(0);
|
|
||||||
|
|
||||||
scrollArea_ = new QScrollArea(this);
|
|
||||||
scrollArea_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
scrollArea_->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents);
|
|
||||||
scrollArea_->setWidgetResizable(true);
|
|
||||||
scrollArea_->setAlignment(Qt::AlignLeading | Qt::AlignTop | Qt::AlignVCenter);
|
|
||||||
scrollArea_->setAttribute(Qt::WA_AcceptTouchEvents);
|
|
||||||
|
|
||||||
QScroller::grabGesture(scrollArea_, QScroller::TouchGesture);
|
|
||||||
QScroller::grabGesture(scrollArea_, QScroller::LeftMouseButtonGesture);
|
|
||||||
|
|
||||||
// The scrollbar on macOS will hide itself when not active so it won't interfere
|
|
||||||
// with the content.
|
|
||||||
#if not defined(Q_OS_MAC)
|
|
||||||
scrollArea_->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
scrollAreaContents_ = new QWidget(this);
|
|
||||||
scrollAreaContents_->setObjectName("roomlist_area");
|
|
||||||
|
|
||||||
contentsLayout_ = new QVBoxLayout(scrollAreaContents_);
|
|
||||||
contentsLayout_->setAlignment(Qt::AlignTop);
|
|
||||||
contentsLayout_->setSpacing(0);
|
|
||||||
contentsLayout_->setMargin(0);
|
|
||||||
|
|
||||||
scrollArea_->setWidget(scrollAreaContents_);
|
|
||||||
topLayout_->addWidget(scrollArea_);
|
|
||||||
|
|
||||||
connect(this, &RoomList::updateRoomAvatarCb, this, &RoomList::updateRoomAvatar);
|
|
||||||
connect(userSettings.data(),
|
|
||||||
&UserSettings::roomSortingChanged,
|
|
||||||
this,
|
|
||||||
&RoomList::sortRoomsByLastMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::addRoom(const QString &room_id, const RoomInfo &info)
|
|
||||||
{
|
|
||||||
auto room_item = new RoomInfoListItem(room_id, info, scrollArea_);
|
|
||||||
room_item->setRoomName(QString::fromStdString(std::move(info.name)));
|
|
||||||
|
|
||||||
connect(room_item, &RoomInfoListItem::clicked, this, &RoomList::highlightSelectedRoom);
|
|
||||||
connect(room_item, &RoomInfoListItem::leaveRoom, this, [](const QString &room_id) {
|
|
||||||
MainWindow::instance()->openLeaveRoomDialog(room_id);
|
|
||||||
});
|
|
||||||
|
|
||||||
QSharedPointer<RoomInfoListItem> roomWidget(room_item, &QObject::deleteLater);
|
|
||||||
rooms_.emplace(room_id, roomWidget);
|
|
||||||
rooms_sort_cache_.push_back(roomWidget);
|
|
||||||
|
|
||||||
if (!info.avatar_url.empty())
|
|
||||||
updateAvatar(room_id, QString::fromStdString(info.avatar_url));
|
|
||||||
|
|
||||||
int pos = contentsLayout_->count() - 1;
|
|
||||||
contentsLayout_->insertWidget(pos, room_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::updateAvatar(const QString &room_id, const QString &url)
|
|
||||||
{
|
|
||||||
emit updateRoomAvatarCb(room_id, url);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::removeRoom(const QString &room_id, bool reset)
|
|
||||||
{
|
|
||||||
auto roomIt = rooms_.find(room_id);
|
|
||||||
if (roomIt == rooms_.end()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto roomSortIt = rooms_sort_cache_.begin(); roomSortIt != rooms_sort_cache_.end();
|
|
||||||
++roomSortIt) {
|
|
||||||
if (roomIt->second == *roomSortIt) {
|
|
||||||
rooms_sort_cache_.erase(roomSortIt);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rooms_.erase(room_id);
|
|
||||||
|
|
||||||
if (rooms_.empty() || !reset)
|
|
||||||
return;
|
|
||||||
|
|
||||||
auto room = firstRoom();
|
|
||||||
|
|
||||||
if (room.second.isNull())
|
|
||||||
return;
|
|
||||||
|
|
||||||
room.second->setPressedState(true);
|
|
||||||
emit roomChanged(room.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::updateUnreadMessageCount(const QString &roomid, int count, int highlightedCount)
|
|
||||||
{
|
|
||||||
if (!roomExists(roomid)) {
|
|
||||||
nhlog::ui()->warn("updateUnreadMessageCount: unknown room_id {}",
|
|
||||||
roomid.toStdString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rooms_[roomid]->updateUnreadMessageCount(count, highlightedCount);
|
|
||||||
|
|
||||||
calculateUnreadMessageCount();
|
|
||||||
|
|
||||||
sortRoomsByLastMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::calculateUnreadMessageCount()
|
|
||||||
{
|
|
||||||
int total_unread_msgs = 0;
|
|
||||||
|
|
||||||
for (const auto &room : rooms_) {
|
|
||||||
if (!room.second.isNull())
|
|
||||||
total_unread_msgs += room.second->unreadMessageCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
emit totalUnreadMessageCountUpdated(total_unread_msgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::initialize(const QMap<QString, RoomInfo> &info)
|
|
||||||
{
|
|
||||||
nhlog::ui()->info("initialize room list");
|
|
||||||
|
|
||||||
rooms_.clear();
|
|
||||||
|
|
||||||
// prevent flickering and save time sorting over and over again
|
|
||||||
setUpdatesEnabled(false);
|
|
||||||
for (auto it = info.begin(); it != info.end(); it++) {
|
|
||||||
if (it.value().is_invite)
|
|
||||||
addInvitedRoom(it.key(), it.value());
|
|
||||||
else
|
|
||||||
addRoom(it.key(), it.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto it = info.begin(); it != info.end(); it++)
|
|
||||||
updateRoomDescription(it.key(), it.value().msgInfo);
|
|
||||||
|
|
||||||
setUpdatesEnabled(true);
|
|
||||||
|
|
||||||
if (rooms_.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
sortRoomsByLastMessage();
|
|
||||||
|
|
||||||
auto room = firstRoom();
|
|
||||||
if (room.second.isNull())
|
|
||||||
return;
|
|
||||||
|
|
||||||
room.second->setPressedState(true);
|
|
||||||
emit roomChanged(room.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::cleanupInvites(const QHash<QString, RoomInfo> &invites)
|
|
||||||
{
|
|
||||||
if (invites.size() == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
utils::erase_if(rooms_, [invites](auto &room) {
|
|
||||||
auto room_id = room.first;
|
|
||||||
auto item = room.second;
|
|
||||||
|
|
||||||
if (!item)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return item->isInvite() && (invites.find(room_id) == invites.end());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::sync(const std::map<QString, RoomInfo> &info)
|
|
||||||
|
|
||||||
{
|
|
||||||
for (const auto &room : info)
|
|
||||||
updateRoom(room.first, room.second);
|
|
||||||
|
|
||||||
if (!info.empty())
|
|
||||||
sortRoomsByLastMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::highlightSelectedRoom(const QString &room_id)
|
|
||||||
{
|
|
||||||
emit roomChanged(room_id);
|
|
||||||
|
|
||||||
if (!roomExists(room_id)) {
|
|
||||||
nhlog::ui()->warn("roomlist: clicked unknown room_id");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto const &room : rooms_) {
|
|
||||||
if (room.second.isNull())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (room.first != room_id) {
|
|
||||||
room.second->setPressedState(false);
|
|
||||||
} else {
|
|
||||||
room.second->setPressedState(true);
|
|
||||||
scrollArea_->ensureWidgetVisible(room.second.data());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedRoom_ = room_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::nextRoom()
|
|
||||||
{
|
|
||||||
for (int ii = 0; ii < contentsLayout_->count() - 1; ++ii) {
|
|
||||||
auto room = qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(ii)->widget());
|
|
||||||
|
|
||||||
if (!room)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (room->roomId() == selectedRoom_) {
|
|
||||||
auto nextRoom = qobject_cast<RoomInfoListItem *>(
|
|
||||||
contentsLayout_->itemAt(ii + 1)->widget());
|
|
||||||
|
|
||||||
// Not a room message.
|
|
||||||
if (!nextRoom || nextRoom->isInvite())
|
|
||||||
return;
|
|
||||||
|
|
||||||
emit roomChanged(nextRoom->roomId());
|
|
||||||
if (!roomExists(nextRoom->roomId())) {
|
|
||||||
nhlog::ui()->warn("roomlist: clicked unknown room_id");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
room->setPressedState(false);
|
|
||||||
nextRoom->setPressedState(true);
|
|
||||||
|
|
||||||
scrollArea_->ensureWidgetVisible(nextRoom);
|
|
||||||
selectedRoom_ = nextRoom->roomId();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::previousRoom()
|
|
||||||
{
|
|
||||||
for (int ii = 1; ii < contentsLayout_->count(); ++ii) {
|
|
||||||
auto room = qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(ii)->widget());
|
|
||||||
|
|
||||||
if (!room)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (room->roomId() == selectedRoom_) {
|
|
||||||
auto nextRoom = qobject_cast<RoomInfoListItem *>(
|
|
||||||
contentsLayout_->itemAt(ii - 1)->widget());
|
|
||||||
|
|
||||||
// Not a room message.
|
|
||||||
if (!nextRoom || nextRoom->isInvite())
|
|
||||||
return;
|
|
||||||
|
|
||||||
emit roomChanged(nextRoom->roomId());
|
|
||||||
if (!roomExists(nextRoom->roomId())) {
|
|
||||||
nhlog::ui()->warn("roomlist: clicked unknown room_id");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
room->setPressedState(false);
|
|
||||||
nextRoom->setPressedState(true);
|
|
||||||
|
|
||||||
scrollArea_->ensureWidgetVisible(nextRoom);
|
|
||||||
selectedRoom_ = nextRoom->roomId();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::updateRoomAvatar(const QString &roomid, const QString &img)
|
|
||||||
{
|
|
||||||
if (!roomExists(roomid)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rooms_[roomid]->setAvatar(img);
|
|
||||||
|
|
||||||
// Used to inform other widgets for the new image data.
|
|
||||||
emit roomAvatarChanged(roomid, img);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::updateRoomDescription(const QString &roomid, const DescInfo &info)
|
|
||||||
{
|
|
||||||
if (!roomExists(roomid)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rooms_[roomid]->setDescriptionMessage(info);
|
|
||||||
|
|
||||||
if (underMouse()) {
|
|
||||||
// When the user hover out of the roomlist a sort will be triggered.
|
|
||||||
isSortPending_ = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
isSortPending_ = false;
|
|
||||||
|
|
||||||
emit sortRoomsByLastMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
struct room_sort
|
|
||||||
{
|
|
||||||
bool operator()(const QSharedPointer<RoomInfoListItem> &a,
|
|
||||||
const QSharedPointer<RoomInfoListItem> &b) const
|
|
||||||
{
|
|
||||||
// Sort by "importance" (i.e. invites before mentions before
|
|
||||||
// notifs before new events before old events), then secondly
|
|
||||||
// by recency.
|
|
||||||
|
|
||||||
// Checking importance first
|
|
||||||
const auto a_importance = a->calculateImportance();
|
|
||||||
const auto b_importance = b->calculateImportance();
|
|
||||||
if (a_importance != b_importance) {
|
|
||||||
return a_importance > b_importance;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now sort by recency
|
|
||||||
// Zero if empty, otherwise the time that the event occured
|
|
||||||
const uint64_t a_recency =
|
|
||||||
a->lastMsgInfo_.userid.isEmpty() ? 0 : a->lastMsgInfo_.timestamp;
|
|
||||||
const uint64_t b_recency =
|
|
||||||
b->lastMsgInfo_.userid.isEmpty() ? 0 : b->lastMsgInfo_.timestamp;
|
|
||||||
return a_recency > b_recency;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::sortRoomsByLastMessage()
|
|
||||||
{
|
|
||||||
isSortPending_ = false;
|
|
||||||
|
|
||||||
std::stable_sort(begin(rooms_sort_cache_), end(rooms_sort_cache_), room_sort{});
|
|
||||||
|
|
||||||
int newIndex = 0;
|
|
||||||
for (const auto &roomWidget : rooms_sort_cache_) {
|
|
||||||
const auto currentIndex = contentsLayout_->indexOf(roomWidget.data());
|
|
||||||
|
|
||||||
if (currentIndex != newIndex) {
|
|
||||||
contentsLayout_->removeWidget(roomWidget.data());
|
|
||||||
contentsLayout_->insertWidget(newIndex, roomWidget.data());
|
|
||||||
}
|
|
||||||
newIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::leaveEvent(QEvent *event)
|
|
||||||
{
|
|
||||||
if (isSortPending_)
|
|
||||||
QTimer::singleShot(700, this, &RoomList::sortRoomsByLastMessage);
|
|
||||||
|
|
||||||
QWidget::leaveEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::closeJoinRoomDialog(bool isJoining, QString roomAlias)
|
|
||||||
{
|
|
||||||
joinRoomModal_->hide();
|
|
||||||
|
|
||||||
if (isJoining)
|
|
||||||
emit joinRoom(roomAlias);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::removeFilter(const std::set<QString> &roomsToHide)
|
|
||||||
{
|
|
||||||
setUpdatesEnabled(false);
|
|
||||||
for (int i = 0; i < contentsLayout_->count(); i++) {
|
|
||||||
auto widget =
|
|
||||||
qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(i)->widget());
|
|
||||||
if (widget) {
|
|
||||||
if (roomsToHide.find(widget->roomId()) == roomsToHide.end())
|
|
||||||
widget->show();
|
|
||||||
else
|
|
||||||
widget->hide();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setUpdatesEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::applyFilter(const std::set<QString> &filter)
|
|
||||||
{
|
|
||||||
// Disabling paint updates will resolve issues with screen flickering on big room lists.
|
|
||||||
setUpdatesEnabled(false);
|
|
||||||
|
|
||||||
for (int i = 0; i < contentsLayout_->count(); i++) {
|
|
||||||
// If filter contains the room for the current RoomInfoListItem,
|
|
||||||
// show the list item, otherwise hide it
|
|
||||||
auto listitem =
|
|
||||||
qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(i)->widget());
|
|
||||||
|
|
||||||
if (!listitem)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (filter.find(listitem->roomId()) != filter.end())
|
|
||||||
listitem->show();
|
|
||||||
else
|
|
||||||
listitem->hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
setUpdatesEnabled(true);
|
|
||||||
|
|
||||||
// If the already selected room is part of the group, make sure it's visible.
|
|
||||||
if (!selectedRoom_.isEmpty() && (filter.find(selectedRoom_) != filter.end()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
selectFirstVisibleRoom();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::selectFirstVisibleRoom()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < contentsLayout_->count(); i++) {
|
|
||||||
auto item = qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(i)->widget());
|
|
||||||
|
|
||||||
if (item && item->isVisible()) {
|
|
||||||
highlightSelectedRoom(item->roomId());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
QStyleOption opt;
|
|
||||||
opt.init(this);
|
|
||||||
QPainter p(this);
|
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::updateRoom(const QString &room_id, const RoomInfo &info)
|
|
||||||
{
|
|
||||||
if (!roomExists(room_id)) {
|
|
||||||
if (info.is_invite)
|
|
||||||
addInvitedRoom(room_id, info);
|
|
||||||
else
|
|
||||||
addRoom(room_id, info);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto room = rooms_[room_id];
|
|
||||||
updateAvatar(room_id, QString::fromStdString(info.avatar_url));
|
|
||||||
room->setRoomName(QString::fromStdString(info.name));
|
|
||||||
room->setRoomType(info.is_invite);
|
|
||||||
room->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::addInvitedRoom(const QString &room_id, const RoomInfo &info)
|
|
||||||
{
|
|
||||||
auto room_item = new RoomInfoListItem(room_id, info, scrollArea_);
|
|
||||||
|
|
||||||
connect(room_item, &RoomInfoListItem::acceptInvite, this, &RoomList::acceptInvite);
|
|
||||||
connect(room_item, &RoomInfoListItem::declineInvite, this, &RoomList::declineInvite);
|
|
||||||
|
|
||||||
QSharedPointer<RoomInfoListItem> roomWidget(room_item);
|
|
||||||
rooms_.emplace(room_id, roomWidget);
|
|
||||||
rooms_sort_cache_.push_back(roomWidget);
|
|
||||||
|
|
||||||
updateAvatar(room_id, QString::fromStdString(info.avatar_url));
|
|
||||||
|
|
||||||
int pos = contentsLayout_->count() - 1;
|
|
||||||
contentsLayout_->insertWidget(pos, room_item);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::pair<QString, QSharedPointer<RoomInfoListItem>>
|
|
||||||
RoomList::firstRoom() const
|
|
||||||
{
|
|
||||||
for (int i = 0; i < contentsLayout_->count(); i++) {
|
|
||||||
auto item = qobject_cast<RoomInfoListItem *>(contentsLayout_->itemAt(i)->widget());
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
auto topRoom = rooms_.find(item->roomId());
|
|
||||||
if (topRoom != rooms_.end()) {
|
|
||||||
return std::pair<QString, QSharedPointer<RoomInfoListItem>>(
|
|
||||||
item->roomId(), topRoom->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomList::updateReadStatus(const std::map<QString, bool> &status)
|
|
||||||
{
|
|
||||||
for (const auto &room : status) {
|
|
||||||
if (roomExists(room.first)) {
|
|
||||||
auto item = rooms_.at(room.first);
|
|
||||||
|
|
||||||
if (item)
|
|
||||||
item->setReadState(room.second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
101
src/RoomList.h
101
src/RoomList.h
|
@ -1,101 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
|
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QPushButton>
|
|
||||||
#include <QScrollArea>
|
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include <set>
|
|
||||||
|
|
||||||
#include "CacheStructs.h"
|
|
||||||
#include "UserSettingsPage.h"
|
|
||||||
|
|
||||||
class LeaveRoomDialog;
|
|
||||||
class OverlayModal;
|
|
||||||
class RoomInfoListItem;
|
|
||||||
class Sync;
|
|
||||||
struct DescInfo;
|
|
||||||
struct RoomInfo;
|
|
||||||
|
|
||||||
class RoomList : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit RoomList(QSharedPointer<UserSettings> userSettings, QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
void initialize(const QMap<QString, RoomInfo> &info);
|
|
||||||
void sync(const std::map<QString, RoomInfo> &info);
|
|
||||||
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
rooms_.clear();
|
|
||||||
rooms_sort_cache_.clear();
|
|
||||||
};
|
|
||||||
void updateAvatar(const QString &room_id, const QString &url);
|
|
||||||
|
|
||||||
void addRoom(const QString &room_id, const RoomInfo &info);
|
|
||||||
void addInvitedRoom(const QString &room_id, const RoomInfo &info);
|
|
||||||
void removeRoom(const QString &room_id, bool reset);
|
|
||||||
//! Hide rooms that are not present in the given filter.
|
|
||||||
void applyFilter(const std::set<QString> &rooms);
|
|
||||||
//! Show all the available rooms.
|
|
||||||
void removeFilter(const std::set<QString> &roomsToHide);
|
|
||||||
void updateRoom(const QString &room_id, const RoomInfo &info);
|
|
||||||
void cleanupInvites(const QHash<QString, RoomInfo> &invites);
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void roomChanged(const QString &room_id);
|
|
||||||
void totalUnreadMessageCountUpdated(int count);
|
|
||||||
void acceptInvite(const QString &room_id);
|
|
||||||
void declineInvite(const QString &room_id);
|
|
||||||
void roomAvatarChanged(const QString &room_id, const QString &img);
|
|
||||||
void joinRoom(const QString &room_id);
|
|
||||||
void updateRoomAvatarCb(const QString &room_id, const QString &img);
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void updateRoomAvatar(const QString &roomid, const QString &img);
|
|
||||||
void highlightSelectedRoom(const QString &room_id);
|
|
||||||
void updateUnreadMessageCount(const QString &roomid, int count, int highlightedCount);
|
|
||||||
void updateRoomDescription(const QString &roomid, const DescInfo &info);
|
|
||||||
void closeJoinRoomDialog(bool isJoining, QString roomAlias);
|
|
||||||
void updateReadStatus(const std::map<QString, bool> &status);
|
|
||||||
void nextRoom();
|
|
||||||
void previousRoom();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
void leaveEvent(QEvent *event) override;
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
void sortRoomsByLastMessage();
|
|
||||||
|
|
||||||
private:
|
|
||||||
//! Return the first non-null room.
|
|
||||||
std::pair<QString, QSharedPointer<RoomInfoListItem>> firstRoom() const;
|
|
||||||
void calculateUnreadMessageCount();
|
|
||||||
bool roomExists(const QString &room_id) { return rooms_.find(room_id) != rooms_.end(); }
|
|
||||||
//! Select the first visible room in the room list.
|
|
||||||
void selectFirstVisibleRoom();
|
|
||||||
|
|
||||||
QVBoxLayout *topLayout_;
|
|
||||||
QVBoxLayout *contentsLayout_;
|
|
||||||
QScrollArea *scrollArea_;
|
|
||||||
QWidget *scrollAreaContents_;
|
|
||||||
|
|
||||||
QPushButton *joinRoomButton_;
|
|
||||||
|
|
||||||
OverlayModal *joinRoomModal_;
|
|
||||||
|
|
||||||
std::map<QString, QSharedPointer<RoomInfoListItem>> rooms_;
|
|
||||||
std::vector<QSharedPointer<RoomInfoListItem>> rooms_sort_cache_;
|
|
||||||
QString selectedRoom_;
|
|
||||||
|
|
||||||
bool isSortPending_ = false;
|
|
||||||
};
|
|
|
@ -1,89 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <QLabel>
|
|
||||||
#include <QPaintEvent>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QStyleOption>
|
|
||||||
|
|
||||||
#include "../Utils.h"
|
|
||||||
#include "../ui/Avatar.h"
|
|
||||||
#include "PopupItem.h"
|
|
||||||
|
|
||||||
constexpr int PopupHMargin = 4;
|
|
||||||
constexpr int PopupItemMargin = 3;
|
|
||||||
|
|
||||||
PopupItem::PopupItem(QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
, avatar_{new Avatar(this, conf::popup::avatar)}
|
|
||||||
, hovering_{false}
|
|
||||||
{
|
|
||||||
setMouseTracking(true);
|
|
||||||
setAttribute(Qt::WA_Hover);
|
|
||||||
|
|
||||||
topLayout_ = new QHBoxLayout(this);
|
|
||||||
topLayout_->setContentsMargins(
|
|
||||||
PopupHMargin, PopupItemMargin, PopupHMargin, PopupItemMargin);
|
|
||||||
|
|
||||||
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
PopupItem::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
QStyleOption opt;
|
|
||||||
opt.init(this);
|
|
||||||
QPainter p(this);
|
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
||||||
|
|
||||||
if (underMouse() || hovering_)
|
|
||||||
p.fillRect(rect(), hoverColor_);
|
|
||||||
}
|
|
||||||
|
|
||||||
RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res)
|
|
||||||
: PopupItem(parent)
|
|
||||||
, roomId_{QString::fromStdString(res.room_id)}
|
|
||||||
{
|
|
||||||
auto name = QFontMetrics(QFont()).elidedText(
|
|
||||||
QString::fromStdString(res.info.name), Qt::ElideRight, parentWidget()->width() - 10);
|
|
||||||
|
|
||||||
avatar_->setLetter(utils::firstChar(name));
|
|
||||||
|
|
||||||
roomName_ = new QLabel(name, this);
|
|
||||||
roomName_->setMargin(0);
|
|
||||||
|
|
||||||
topLayout_->addWidget(avatar_);
|
|
||||||
topLayout_->addWidget(roomName_, 1);
|
|
||||||
|
|
||||||
if (!res.info.avatar_url.empty())
|
|
||||||
avatar_->setImage(QString::fromStdString(res.info.avatar_url));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomItem::updateItem(const RoomSearchResult &result)
|
|
||||||
{
|
|
||||||
roomId_ = QString::fromStdString(std::move(result.room_id));
|
|
||||||
|
|
||||||
auto name =
|
|
||||||
QFontMetrics(QFont()).elidedText(QString::fromStdString(std::move(result.info.name)),
|
|
||||||
Qt::ElideRight,
|
|
||||||
parentWidget()->width() - 10);
|
|
||||||
|
|
||||||
roomName_->setText(name);
|
|
||||||
|
|
||||||
// if there is not an avatar set for the room, we want to at least show the letter
|
|
||||||
// correctly!
|
|
||||||
avatar_->setLetter(utils::firstChar(name));
|
|
||||||
if (!result.info.avatar_url.empty())
|
|
||||||
avatar_->setImage(QString::fromStdString(result.info.avatar_url));
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
RoomItem::mousePressEvent(QMouseEvent *event)
|
|
||||||
{
|
|
||||||
if (event->buttons() != Qt::RightButton)
|
|
||||||
emit clicked(selectedText());
|
|
||||||
|
|
||||||
QWidget::mousePressEvent(event);
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "../AvatarProvider.h"
|
|
||||||
#include "../ChatPage.h"
|
|
||||||
|
|
||||||
class Avatar;
|
|
||||||
struct SearchResult;
|
|
||||||
class QLabel;
|
|
||||||
class QHBoxLayout;
|
|
||||||
|
|
||||||
class PopupItem : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(QColor hoverColor READ hoverColor WRITE setHoverColor)
|
|
||||||
Q_PROPERTY(bool hovering READ hovering WRITE setHovering)
|
|
||||||
|
|
||||||
public:
|
|
||||||
PopupItem(QWidget *parent);
|
|
||||||
|
|
||||||
QString selectedText() const { return QString(); }
|
|
||||||
QColor hoverColor() const { return hoverColor_; }
|
|
||||||
void setHoverColor(QColor &color) { hoverColor_ = color; }
|
|
||||||
|
|
||||||
bool hovering() const { return hovering_; }
|
|
||||||
void setHovering(const bool hover) { hovering_ = hover; };
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void clicked(const QString &text);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
QHBoxLayout *topLayout_;
|
|
||||||
Avatar *avatar_;
|
|
||||||
QColor hoverColor_;
|
|
||||||
|
|
||||||
//! Set if the item is currently being
|
|
||||||
//! hovered during tab completion (cycling).
|
|
||||||
bool hovering_;
|
|
||||||
};
|
|
||||||
|
|
||||||
class RoomItem : public PopupItem
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
RoomItem(QWidget *parent, const RoomSearchResult &res);
|
|
||||||
QString selectedText() const { return roomId_; }
|
|
||||||
void updateItem(const RoomSearchResult &res);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void mousePressEvent(QMouseEvent *event) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
QLabel *roomName_;
|
|
||||||
QString roomId_;
|
|
||||||
RoomSearchResult info_;
|
|
||||||
};
|
|
|
@ -1,164 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <QPaintEvent>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QStyleOption>
|
|
||||||
|
|
||||||
#include "../Config.h"
|
|
||||||
#include "../Utils.h"
|
|
||||||
#include "../ui/Avatar.h"
|
|
||||||
#include "../ui/DropShadow.h"
|
|
||||||
#include "ChatPage.h"
|
|
||||||
#include "PopupItem.h"
|
|
||||||
#include "SuggestionsPopup.h"
|
|
||||||
|
|
||||||
SuggestionsPopup::SuggestionsPopup(QWidget *parent)
|
|
||||||
: QWidget(parent)
|
|
||||||
{
|
|
||||||
setAttribute(Qt::WA_ShowWithoutActivating, true);
|
|
||||||
setWindowFlags(Qt::ToolTip | Qt::NoDropShadowWindowHint);
|
|
||||||
|
|
||||||
layout_ = new QVBoxLayout(this);
|
|
||||||
layout_->setMargin(0);
|
|
||||||
layout_->setSpacing(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
QString
|
|
||||||
SuggestionsPopup::displayName(QString room, QString user)
|
|
||||||
{
|
|
||||||
return cache::displayName(room, user);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::addRooms(const std::vector<RoomSearchResult> &rooms)
|
|
||||||
{
|
|
||||||
if (rooms.empty()) {
|
|
||||||
hide();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const int layoutCount = (int)layout_->count();
|
|
||||||
const int roomCount = (int)rooms.size();
|
|
||||||
|
|
||||||
// Remove the extra widgets from the layout.
|
|
||||||
if (roomCount < layoutCount)
|
|
||||||
removeLayoutItemsAfter(roomCount - 1);
|
|
||||||
|
|
||||||
for (int i = 0; i < roomCount; ++i) {
|
|
||||||
auto item = layout_->itemAt(i);
|
|
||||||
|
|
||||||
// Create a new widget if there isn't already one in that
|
|
||||||
// layout position.
|
|
||||||
if (!item) {
|
|
||||||
auto room = new RoomItem(this, rooms.at(i));
|
|
||||||
connect(room, &RoomItem::clicked, this, &SuggestionsPopup::itemSelected);
|
|
||||||
layout_->addWidget(room);
|
|
||||||
} else {
|
|
||||||
// Update the current widget with the new data.
|
|
||||||
auto room = qobject_cast<RoomItem *>(item->widget());
|
|
||||||
if (room)
|
|
||||||
room->updateItem(rooms.at(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
resetSelection();
|
|
||||||
adjustSize();
|
|
||||||
|
|
||||||
resize(geometry().width(), 40 * (int)rooms.size());
|
|
||||||
|
|
||||||
selectNextSuggestion();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::hoverSelection()
|
|
||||||
{
|
|
||||||
resetHovering();
|
|
||||||
setHovering(selectedItem_);
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::selectHoveredSuggestion()
|
|
||||||
{
|
|
||||||
const auto item = layout_->itemAt(selectedItem_);
|
|
||||||
if (!item)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const auto &widget = qobject_cast<RoomItem *>(item->widget());
|
|
||||||
emit itemSelected(displayName(ChatPage::instance()->currentRoom(), widget->selectedText()));
|
|
||||||
|
|
||||||
resetSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::selectNextSuggestion()
|
|
||||||
{
|
|
||||||
selectedItem_++;
|
|
||||||
if (selectedItem_ >= layout_->count())
|
|
||||||
selectFirstItem();
|
|
||||||
|
|
||||||
hoverSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::selectPreviousSuggestion()
|
|
||||||
{
|
|
||||||
selectedItem_--;
|
|
||||||
if (selectedItem_ < 0)
|
|
||||||
selectLastItem();
|
|
||||||
|
|
||||||
hoverSelection();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::resetHovering()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < layout_->count(); ++i) {
|
|
||||||
const auto item = qobject_cast<PopupItem *>(layout_->itemAt(i)->widget());
|
|
||||||
|
|
||||||
if (item)
|
|
||||||
item->setHovering(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::setHovering(int pos)
|
|
||||||
{
|
|
||||||
const auto &item = layout_->itemAt(pos);
|
|
||||||
const auto &widget = qobject_cast<PopupItem *>(item->widget());
|
|
||||||
|
|
||||||
if (widget)
|
|
||||||
widget->setHovering(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
QStyleOption opt;
|
|
||||||
opt.init(this);
|
|
||||||
QPainter p(this);
|
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::selectLastItem()
|
|
||||||
{
|
|
||||||
selectedItem_ = layout_->count() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
SuggestionsPopup::removeLayoutItemsAfter(size_t startingPos)
|
|
||||||
{
|
|
||||||
size_t posToRemove = layout_->count() - 1;
|
|
||||||
|
|
||||||
QLayoutItem *item;
|
|
||||||
while (startingPos <= posToRemove &&
|
|
||||||
(item = layout_->takeAt((int)posToRemove)) != nullptr) {
|
|
||||||
delete item->widget();
|
|
||||||
delete item;
|
|
||||||
|
|
||||||
posToRemove = layout_->count() - 1;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
#include "CacheStructs.h"
|
|
||||||
|
|
||||||
class QVBoxLayout;
|
|
||||||
class QLayoutItem;
|
|
||||||
|
|
||||||
class SuggestionsPopup : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit SuggestionsPopup(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
void selectHoveredSuggestion();
|
|
||||||
|
|
||||||
public slots:
|
|
||||||
void addRooms(const std::vector<RoomSearchResult> &rooms);
|
|
||||||
|
|
||||||
//! Move to the next available suggestion item.
|
|
||||||
void selectNextSuggestion();
|
|
||||||
//! Move to the previous available suggestion item.
|
|
||||||
void selectPreviousSuggestion();
|
|
||||||
//! Remove hovering from all items.
|
|
||||||
void resetHovering();
|
|
||||||
//! Set hovering to the item in the given layout position.
|
|
||||||
void setHovering(int pos);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *event) override;
|
|
||||||
|
|
||||||
signals:
|
|
||||||
void itemSelected(const QString &user);
|
|
||||||
|
|
||||||
private:
|
|
||||||
QString displayName(QString roomid, QString userid);
|
|
||||||
void hoverSelection();
|
|
||||||
void resetSelection() { selectedItem_ = -1; }
|
|
||||||
void selectFirstItem() { selectedItem_ = 0; }
|
|
||||||
void selectLastItem();
|
|
||||||
void removeLayoutItemsAfter(size_t startingPos);
|
|
||||||
|
|
||||||
QVBoxLayout *layout_;
|
|
||||||
|
|
||||||
//! Counter for tab completion (cycling).
|
|
||||||
int selectedItem_ = -1;
|
|
||||||
};
|
|
|
@ -1,178 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#include <QPaintEvent>
|
|
||||||
#include <QPainter>
|
|
||||||
#include <QScrollArea>
|
|
||||||
#include <QStyleOption>
|
|
||||||
#include <QTabWidget>
|
|
||||||
#include <QTimer>
|
|
||||||
#include <QVBoxLayout>
|
|
||||||
|
|
||||||
#include "Cache.h"
|
|
||||||
#include "ChatPage.h"
|
|
||||||
#include "EventAccessors.h"
|
|
||||||
#include "Logging.h"
|
|
||||||
#include "UserMentions.h"
|
|
||||||
|
|
||||||
using namespace popups;
|
|
||||||
|
|
||||||
UserMentions::UserMentions(QWidget *parent)
|
|
||||||
: QWidget{parent}
|
|
||||||
{
|
|
||||||
setAttribute(Qt::WA_ShowWithoutActivating, true);
|
|
||||||
setWindowFlags(Qt::FramelessWindowHint | Qt::Popup);
|
|
||||||
|
|
||||||
tab_layout_ = new QTabWidget(this);
|
|
||||||
|
|
||||||
top_layout_ = new QVBoxLayout(this);
|
|
||||||
top_layout_->setSpacing(0);
|
|
||||||
top_layout_->setMargin(0);
|
|
||||||
|
|
||||||
local_scroll_area_ = new QScrollArea(this);
|
|
||||||
local_scroll_area_->setWidgetResizable(true);
|
|
||||||
local_scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
|
|
||||||
local_scroll_widget_ = new QWidget(this);
|
|
||||||
local_scroll_widget_->setObjectName("local_scroll_widget");
|
|
||||||
|
|
||||||
all_scroll_area_ = new QScrollArea(this);
|
|
||||||
all_scroll_area_->setWidgetResizable(true);
|
|
||||||
all_scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
||||||
|
|
||||||
all_scroll_widget_ = new QWidget(this);
|
|
||||||
all_scroll_widget_->setObjectName("all_scroll_widget");
|
|
||||||
|
|
||||||
// Height of the typing display.
|
|
||||||
QFont f;
|
|
||||||
f.setPointSizeF(f.pointSizeF() * 0.9);
|
|
||||||
const int bottomMargin = QFontMetrics(f).height() + 6;
|
|
||||||
|
|
||||||
local_scroll_layout_ = new QVBoxLayout(local_scroll_widget_);
|
|
||||||
local_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin);
|
|
||||||
local_scroll_layout_->setSpacing(0);
|
|
||||||
local_scroll_layout_->setObjectName("localscrollarea");
|
|
||||||
|
|
||||||
all_scroll_layout_ = new QVBoxLayout(all_scroll_widget_);
|
|
||||||
all_scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin);
|
|
||||||
all_scroll_layout_->setSpacing(0);
|
|
||||||
all_scroll_layout_->setObjectName("allscrollarea");
|
|
||||||
|
|
||||||
local_scroll_area_->setWidget(local_scroll_widget_);
|
|
||||||
local_scroll_area_->setAlignment(Qt::AlignBottom);
|
|
||||||
|
|
||||||
all_scroll_area_->setWidget(all_scroll_widget_);
|
|
||||||
all_scroll_area_->setAlignment(Qt::AlignBottom);
|
|
||||||
|
|
||||||
tab_layout_->addTab(local_scroll_area_, tr("This Room"));
|
|
||||||
tab_layout_->addTab(all_scroll_area_, tr("All Rooms"));
|
|
||||||
top_layout_->addWidget(tab_layout_);
|
|
||||||
|
|
||||||
setLayout(top_layout_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserMentions::initializeMentions(const QMap<QString, mtx::responses::Notifications> ¬ifs)
|
|
||||||
{
|
|
||||||
nhlog::ui()->debug("Initializing " + std::to_string(notifs.size()) + " notifications.");
|
|
||||||
|
|
||||||
for (const auto &item : notifs) {
|
|
||||||
for (const auto ¬if : item.notifications) {
|
|
||||||
const auto event_id =
|
|
||||||
QString::fromStdString(mtx::accessors::event_id(notif.event));
|
|
||||||
|
|
||||||
try {
|
|
||||||
const auto room_id = QString::fromStdString(notif.room_id);
|
|
||||||
const auto user_id =
|
|
||||||
QString::fromStdString(mtx::accessors::sender(notif.event));
|
|
||||||
const auto body =
|
|
||||||
QString::fromStdString(mtx::accessors::body(notif.event));
|
|
||||||
|
|
||||||
pushItem(event_id,
|
|
||||||
user_id,
|
|
||||||
body,
|
|
||||||
room_id,
|
|
||||||
ChatPage::instance()->currentRoom());
|
|
||||||
|
|
||||||
} catch (const lmdb::error &e) {
|
|
||||||
nhlog::db()->warn("error while sending desktop notification: {}",
|
|
||||||
e.what());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserMentions::showPopup()
|
|
||||||
{
|
|
||||||
for (auto widget : all_scroll_layout_->findChildren<QWidget *>()) {
|
|
||||||
delete widget;
|
|
||||||
}
|
|
||||||
for (auto widget : local_scroll_layout_->findChildren<QWidget *>()) {
|
|
||||||
delete widget;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto notifs = cache::getTimelineMentions();
|
|
||||||
|
|
||||||
initializeMentions(notifs);
|
|
||||||
show();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserMentions::pushItem(const QString &event_id,
|
|
||||||
const QString &user_id,
|
|
||||||
const QString &body,
|
|
||||||
const QString &room_id,
|
|
||||||
const QString ¤t_room_id)
|
|
||||||
{
|
|
||||||
(void)event_id;
|
|
||||||
(void)user_id;
|
|
||||||
(void)body;
|
|
||||||
(void)room_id;
|
|
||||||
(void)current_room_id;
|
|
||||||
// setUpdatesEnabled(false);
|
|
||||||
//
|
|
||||||
// // Add to the 'all' section
|
|
||||||
// TimelineItem *view_item = new TimelineItem(
|
|
||||||
// mtx::events::MessageType::Text, user_id, body, true, room_id,
|
|
||||||
// all_scroll_widget_);
|
|
||||||
// view_item->setEventId(event_id);
|
|
||||||
// view_item->hide();
|
|
||||||
//
|
|
||||||
// all_scroll_layout_->addWidget(view_item);
|
|
||||||
// QTimer::singleShot(0, this, [view_item, this]() {
|
|
||||||
// view_item->show();
|
|
||||||
// view_item->adjustSize();
|
|
||||||
// setUpdatesEnabled(true);
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// // if it matches the current room... add it to the current room as well.
|
|
||||||
// if (QString::compare(room_id, current_room_id, Qt::CaseInsensitive) == 0) {
|
|
||||||
// // Add to the 'local' section
|
|
||||||
// TimelineItem *local_view_item = new
|
|
||||||
// TimelineItem(mtx::events::MessageType::Text,
|
|
||||||
// user_id,
|
|
||||||
// body,
|
|
||||||
// true,
|
|
||||||
// room_id,
|
|
||||||
// local_scroll_widget_);
|
|
||||||
// local_view_item->setEventId(event_id);
|
|
||||||
// local_view_item->hide();
|
|
||||||
// local_scroll_layout_->addWidget(local_view_item);
|
|
||||||
//
|
|
||||||
// QTimer::singleShot(0, this, [local_view_item]() {
|
|
||||||
// local_view_item->show();
|
|
||||||
// local_view_item->adjustSize();
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
UserMentions::paintEvent(QPaintEvent *)
|
|
||||||
{
|
|
||||||
QStyleOption opt;
|
|
||||||
opt.init(this);
|
|
||||||
QPainter p(this);
|
|
||||||
style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Nheko Contributors
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <mtx/responses/notifications.hpp>
|
|
||||||
|
|
||||||
#include <QMap>
|
|
||||||
#include <QString>
|
|
||||||
#include <QWidget>
|
|
||||||
|
|
||||||
class QPaintEvent;
|
|
||||||
class QTabWidget;
|
|
||||||
class QScrollArea;
|
|
||||||
class QVBoxLayout;
|
|
||||||
|
|
||||||
namespace popups {
|
|
||||||
|
|
||||||
class UserMentions : public QWidget
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
public:
|
|
||||||
UserMentions(QWidget *parent = nullptr);
|
|
||||||
|
|
||||||
void initializeMentions(const QMap<QString, mtx::responses::Notifications> ¬ifs);
|
|
||||||
void showPopup();
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void pushItem(const QString &event_id,
|
|
||||||
const QString &user_id,
|
|
||||||
const QString &body,
|
|
||||||
const QString &room_id,
|
|
||||||
const QString ¤t_room_id);
|
|
||||||
QTabWidget *tab_layout_;
|
|
||||||
QVBoxLayout *top_layout_;
|
|
||||||
QVBoxLayout *local_scroll_layout_;
|
|
||||||
QVBoxLayout *all_scroll_layout_;
|
|
||||||
|
|
||||||
QScrollArea *local_scroll_area_;
|
|
||||||
QWidget *local_scroll_widget_;
|
|
||||||
|
|
||||||
QScrollArea *all_scroll_area_;
|
|
||||||
QWidget *all_scroll_widget_;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include "Cache.h"
|
#include "Cache.h"
|
||||||
#include "ChatPage.h"
|
#include "ChatPage.h"
|
||||||
#include "CompletionProxyModel.h"
|
#include "CompletionProxyModel.h"
|
||||||
|
#include "Config.h"
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
#include "MainWindow.h"
|
#include "MainWindow.h"
|
||||||
#include "MatrixClient.h"
|
#include "MatrixClient.h"
|
||||||
|
|
|
@ -530,3 +530,33 @@ FilteredRoomlistModel::toggleTag(QString roomid, QString tag, bool on)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FilteredRoomlistModel::nextRoom()
|
||||||
|
{
|
||||||
|
auto r = currentRoom();
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
int idx = roomidToIndex(r->roomId());
|
||||||
|
idx++;
|
||||||
|
if (idx < rowCount()) {
|
||||||
|
setCurrentRoom(
|
||||||
|
data(index(idx, 0), RoomlistModel::Roles::RoomId).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
FilteredRoomlistModel::previousRoom()
|
||||||
|
{
|
||||||
|
auto r = currentRoom();
|
||||||
|
|
||||||
|
if (r) {
|
||||||
|
int idx = roomidToIndex(r->roomId());
|
||||||
|
idx--;
|
||||||
|
if (idx > 0) {
|
||||||
|
setCurrentRoom(
|
||||||
|
data(index(idx, 0), RoomlistModel::Roles::RoomId).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -118,6 +118,9 @@ public slots:
|
||||||
TimelineModel *currentRoom() const { return roomlistmodel->currentRoom(); }
|
TimelineModel *currentRoom() const { return roomlistmodel->currentRoom(); }
|
||||||
void setCurrentRoom(QString roomid) { roomlistmodel->setCurrentRoom(std::move(roomid)); }
|
void setCurrentRoom(QString roomid) { roomlistmodel->setCurrentRoom(std::move(roomid)); }
|
||||||
|
|
||||||
|
void nextRoom();
|
||||||
|
void previousRoom();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void currentRoomChanged();
|
void currentRoomChanged();
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue