mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +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/Clipboard.cpp
|
||||
src/ColorImageProvider.cpp
|
||||
src/CommunitiesList.cpp
|
||||
src/CommunitiesListItem.cpp
|
||||
src/CompletionProxyModel.cpp
|
||||
src/DeviceVerificationFlow.cpp
|
||||
src/EventAccessors.cpp
|
||||
|
@ -324,22 +322,14 @@ set(SRC_FILES
|
|||
src/MxcImageProvider.cpp
|
||||
src/Olm.cpp
|
||||
src/RegisterPage.cpp
|
||||
src/RoomInfoListItem.cpp
|
||||
src/RoomList.cpp
|
||||
src/SSOHandler.cpp
|
||||
src/SideBarActions.cpp
|
||||
src/Splitter.cpp
|
||||
src/TrayIcon.cpp
|
||||
src/UserInfoWidget.cpp
|
||||
src/UserSettingsPage.cpp
|
||||
src/UsersModel.cpp
|
||||
src/RoomsModel.cpp
|
||||
src/Utils.cpp
|
||||
src/WebRTCSession.cpp
|
||||
src/WelcomePage.cpp
|
||||
src/popups/PopupItem.cpp
|
||||
src/popups/SuggestionsPopup.cpp
|
||||
src/popups/UserMentions.cpp
|
||||
src/main.cpp
|
||||
|
||||
third_party/blurhash/blurhash.cpp
|
||||
|
@ -535,8 +525,6 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
src/CallManager.h
|
||||
src/ChatPage.h
|
||||
src/Clipboard.h
|
||||
src/CommunitiesList.h
|
||||
src/CommunitiesListItem.h
|
||||
src/CompletionProxyModel.h
|
||||
src/DeviceVerificationFlow.h
|
||||
src/InviteeItem.h
|
||||
|
@ -544,21 +532,13 @@ qt5_wrap_cpp(MOC_HEADERS
|
|||
src/MainWindow.h
|
||||
src/MxcImageProvider.h
|
||||
src/RegisterPage.h
|
||||
src/RoomInfoListItem.h
|
||||
src/RoomList.h
|
||||
src/SSOHandler.h
|
||||
src/SideBarActions.h
|
||||
src/Splitter.h
|
||||
src/TrayIcon.h
|
||||
src/UserInfoWidget.h
|
||||
src/UserSettingsPage.h
|
||||
src/UsersModel.h
|
||||
src/RoomsModel.h
|
||||
src/WebRTCSession.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 {
|
||||
id: deviceVerificationDialog
|
||||
|
||||
|
|
309
src/ChatPage.cpp
309
src/ChatPage.cpp
|
@ -23,10 +23,6 @@
|
|||
#include "MainWindow.h"
|
||||
#include "MatrixClient.h"
|
||||
#include "Olm.h"
|
||||
#include "RoomList.h"
|
||||
#include "SideBarActions.h"
|
||||
#include "Splitter.h"
|
||||
#include "UserInfoWidget.h"
|
||||
#include "UserSettingsPage.h"
|
||||
#include "Utils.h"
|
||||
#include "ui/OverlayModal.h"
|
||||
|
@ -36,7 +32,6 @@
|
|||
#include "notifications/Manager.h"
|
||||
|
||||
#include "dialogs/ReadReceipts.h"
|
||||
#include "popups/UserMentions.h"
|
||||
#include "timeline/TimelineViewManager.h"
|
||||
|
||||
#include "blurhash.hpp"
|
||||
|
@ -76,62 +71,9 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
topLayout_->setSpacing(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);
|
||||
|
||||
contentLayout_->addWidget(view_manager_->getWidget());
|
||||
|
||||
// Splitter
|
||||
splitter->addWidget(sideBar_);
|
||||
splitter->addWidget(content_);
|
||||
splitter->restoreSizes(parent->width());
|
||||
topLayout_->addWidget(view_manager_->getWidget());
|
||||
|
||||
connect(this,
|
||||
&ChatPage::downloadedSecrets,
|
||||
|
@ -153,17 +95,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
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);
|
||||
connect(&connectivityTimer_, &QTimer::timeout, this, [=]() {
|
||||
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(
|
||||
view_manager_, &TimelineViewManager::showRoomList, splitter, &Splitter::showFullRoomList);
|
||||
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) {
|
||||
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::newRoom, this, &ChatPage::changeRoom, Qt::QueuedConnection);
|
||||
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,
|
||||
&NotificationsManager::notificationClicked,
|
||||
this,
|
||||
[this](const QString &roomid, const QString &eventid) {
|
||||
Q_UNUSED(eventid)
|
||||
room_list_->highlightSelectedRoom(roomid);
|
||||
view_manager_->rooms()->setCurrentRoom(roomid);
|
||||
activateWindow();
|
||||
});
|
||||
connect(¬ificationsManager,
|
||||
&NotificationsManager::sendNotificationReply,
|
||||
this,
|
||||
[this](const QString &roomid, const QString &eventid, const QString &body) {
|
||||
view_manager_->rooms()->setCurrentRoom(roomid);
|
||||
view_manager_->queueReply(roomid, eventid, body);
|
||||
room_list_->highlightSelectedRoom(roomid);
|
||||
activateWindow();
|
||||
});
|
||||
|
||||
setGroupViewState(userSettings_->groupView());
|
||||
|
||||
connect(userSettings_.data(),
|
||||
&UserSettings::groupViewStateChanged,
|
||||
this,
|
||||
&ChatPage::setGroupViewState);
|
||||
|
||||
connect(this, &ChatPage::initializeRoomList, room_list_, &RoomList::initialize);
|
||||
connect(
|
||||
this,
|
||||
&ChatPage::initializeViews,
|
||||
|
@ -312,30 +181,13 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
&ChatPage::initializeEmptyViews,
|
||||
view_manager_,
|
||||
&TimelineViewManager::initializeRoomlist);
|
||||
connect(this,
|
||||
&ChatPage::initializeMentions,
|
||||
user_mentions_popup_,
|
||||
&popups::UserMentions::initializeMentions);
|
||||
connect(
|
||||
this, &ChatPage::chatFocusChanged, view_manager_, &TimelineViewManager::chatFocusChanged);
|
||||
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);
|
||||
removeLeftRooms(rooms.leave);
|
||||
|
||||
bool hasNotifications = false;
|
||||
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)
|
||||
hasNotifications = true;
|
||||
}
|
||||
|
@ -358,16 +210,6 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
|
|||
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(
|
||||
this, &ChatPage::tryInitialSyncCb, this, &ChatPage::tryInitialSync, Qt::QueuedConnection);
|
||||
|
@ -420,8 +262,6 @@ ChatPage::dropToLoginPage(const QString &msg)
|
|||
void
|
||||
ChatPage::resetUI()
|
||||
{
|
||||
room_list_->clear();
|
||||
user_info_widget_->reset();
|
||||
view_manager_->clearAll();
|
||||
|
||||
emit unreadMessages(0);
|
||||
|
@ -474,9 +314,6 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
|
|||
view_manager_,
|
||||
&TimelineViewManager::updateReadReceipts);
|
||||
|
||||
connect(
|
||||
cache::client(), &Cache::roomReadStatus, room_list_, &RoomList::updateReadStatus);
|
||||
|
||||
connect(cache::client(),
|
||||
&Cache::removeNotification,
|
||||
¬ificationsManager,
|
||||
|
@ -553,9 +390,7 @@ ChatPage::loadStateFromCache()
|
|||
olm::client()->load(cache::restoreOlmAccount(), STORAGE_SECRET_KEY);
|
||||
|
||||
emit initializeEmptyViews();
|
||||
emit initializeRoomList(cache::roomInfo());
|
||||
emit initializeMentions(cache::getTimelineMentions());
|
||||
emit syncTags(cache::roomInfo().toStdMap());
|
||||
|
||||
cache::calculateRoomReadStatus();
|
||||
|
||||
|
@ -593,38 +428,6 @@ ChatPage::removeRoom(const QString &room_id)
|
|||
nhlog::db()->critical("failure while removing room: {}", e.what());
|
||||
// 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
|
||||
|
@ -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
|
||||
ChatPage::tryInitialSync()
|
||||
{
|
||||
|
@ -782,11 +573,9 @@ ChatPage::startInitialSync()
|
|||
olm::handle_to_device_messages(res.to_device.events);
|
||||
|
||||
emit initializeViews(std::move(res.rooms));
|
||||
emit initializeRoomList(cache::roomInfo());
|
||||
emit initializeMentions(cache::getTimelineMentions());
|
||||
|
||||
cache::calculateRoomReadStatus();
|
||||
emit syncTags(cache::roomInfo().toStdMap());
|
||||
} catch (const lmdb::error &e) {
|
||||
nhlog::db()->error("failed to save state after initial sync: {}",
|
||||
e.what());
|
||||
|
@ -823,12 +612,8 @@ ChatPage::handleSyncResponse(const mtx::responses::Sync &res, const std::string
|
|||
|
||||
auto updates = cache::getRoomInfo(cache::client()->roomsWithStateUpdates(res));
|
||||
|
||||
emit syncRoomlist(updates);
|
||||
|
||||
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
|
||||
// db every 100s
|
||||
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()));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
view_manager_->rooms()->setCurrentRoom(room_id);
|
||||
room_list_->highlightSelectedRoom(room_id);
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::inviteUser(QString userid, QString reason)
|
||||
{
|
||||
auto room = current_room_;
|
||||
auto room = currentRoom();
|
||||
|
||||
if (QMessageBox::question(this,
|
||||
tr("Confirm invite"),
|
||||
tr("Do you really want to invite %1 (%2)?")
|
||||
.arg(cache::displayName(current_room_, userid))
|
||||
.arg(cache::displayName(room, userid))
|
||||
.arg(userid)) != QMessageBox::Yes)
|
||||
return;
|
||||
|
||||
|
@ -1014,12 +798,12 @@ ChatPage::inviteUser(QString userid, QString reason)
|
|||
void
|
||||
ChatPage::kickUser(QString userid, QString reason)
|
||||
{
|
||||
auto room = current_room_;
|
||||
auto room = currentRoom();
|
||||
|
||||
if (QMessageBox::question(this,
|
||||
tr("Confirm kick"),
|
||||
tr("Do you really want to kick %1 (%2)?")
|
||||
.arg(cache::displayName(current_room_, userid))
|
||||
.arg(cache::displayName(room, userid))
|
||||
.arg(userid)) != QMessageBox::Yes)
|
||||
return;
|
||||
|
||||
|
@ -1041,12 +825,12 @@ ChatPage::kickUser(QString userid, QString reason)
|
|||
void
|
||||
ChatPage::banUser(QString userid, QString reason)
|
||||
{
|
||||
auto room = current_room_;
|
||||
auto room = currentRoom();
|
||||
|
||||
if (QMessageBox::question(this,
|
||||
tr("Confirm ban"),
|
||||
tr("Do you really want to ban %1 (%2)?")
|
||||
.arg(cache::displayName(current_room_, userid))
|
||||
.arg(cache::displayName(room, userid))
|
||||
.arg(userid)) != QMessageBox::Yes)
|
||||
return;
|
||||
|
||||
|
@ -1068,12 +852,12 @@ ChatPage::banUser(QString userid, QString reason)
|
|||
void
|
||||
ChatPage::unbanUser(QString userid, QString reason)
|
||||
{
|
||||
auto room = current_room_;
|
||||
auto room = currentRoom();
|
||||
|
||||
if (QMessageBox::question(this,
|
||||
tr("Confirm unban"),
|
||||
tr("Do you really want to unban %1 (%2)?")
|
||||
.arg(cache::displayName(current_room_, userid))
|
||||
.arg(cache::displayName(room, userid))
|
||||
.arg(userid)) != QMessageBox::Yes)
|
||||
return;
|
||||
|
||||
|
@ -1175,51 +959,6 @@ ChatPage::getProfileInfo()
|
|||
|
||||
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
|
||||
|
@ -1305,7 +1044,8 @@ ChatPage::startChat(QString userid)
|
|||
if (std::find(room_members.begin(),
|
||||
room_members.end(),
|
||||
(userid).toStdString()) != room_members.end()) {
|
||||
room_list_->highlightSelectedRoom(QString::fromStdString(room_id));
|
||||
view_manager_->rooms()->setCurrentRoom(
|
||||
QString::fromStdString(room_id));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1406,7 +1146,7 @@ ChatPage::handleMatrixUri(const QByteArray &uri)
|
|||
|
||||
for (auto roomid : joined_rooms) {
|
||||
if (roomid == targetRoomId) {
|
||||
room_list_->highlightSelectedRoom(mxid1);
|
||||
view_manager_->rooms()->setCurrentRoom(mxid1);
|
||||
if (!mxid2.isEmpty())
|
||||
view_manager_->showEvent(mxid1, mxid2);
|
||||
return;
|
||||
|
@ -1424,7 +1164,7 @@ ChatPage::handleMatrixUri(const QByteArray &uri)
|
|||
auto aliases = cache::client()->getRoomAliases(roomid);
|
||||
if (aliases) {
|
||||
if (aliases->alias == targetRoomAlias) {
|
||||
room_list_->highlightSelectedRoom(
|
||||
view_manager_->rooms()->setCurrentRoom(
|
||||
QString::fromStdString(roomid));
|
||||
if (!mxid2.isEmpty())
|
||||
view_manager_->showEvent(
|
||||
|
@ -1446,8 +1186,17 @@ ChatPage::handleMatrixUri(const QUrl &uri)
|
|||
handleMatrixUri(uri.toString(QUrl::ComponentFormattingOption::FullyEncoded).toUtf8());
|
||||
}
|
||||
|
||||
void
|
||||
ChatPage::highlightRoom(const QString &room_id)
|
||||
bool
|
||||
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 "CacheStructs.h"
|
||||
#include "CommunitiesList.h"
|
||||
#include "notifications/Manager.h"
|
||||
|
||||
class OverlayModal;
|
||||
class RoomList;
|
||||
class SideBarActions;
|
||||
class Splitter;
|
||||
class TimelineViewManager;
|
||||
class UserInfoWidget;
|
||||
class UserSettings;
|
||||
class NotificationsManager;
|
||||
class TimelineModel;
|
||||
|
@ -53,11 +48,6 @@ struct Notifications;
|
|||
struct Sync;
|
||||
struct Timeline;
|
||||
struct Rooms;
|
||||
struct LeftRoom;
|
||||
}
|
||||
|
||||
namespace popups {
|
||||
class UserMentions;
|
||||
}
|
||||
|
||||
using SecretsToDecrypt = std::map<std::string, mtx::secret_storage::AesHmacSha2EncryptedData>;
|
||||
|
@ -71,7 +61,6 @@ public:
|
|||
|
||||
// Initialize all the components of the UI.
|
||||
void bootstrap(QString userid, QString homeserver, QString token);
|
||||
QString currentRoom() const { return current_room_; }
|
||||
|
||||
static ChatPage *instance() { return instance_; }
|
||||
|
||||
|
@ -80,14 +69,6 @@ public:
|
|||
TimelineViewManager *timelineManager() { return view_manager_; }
|
||||
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();
|
||||
|
||||
QString status() const;
|
||||
|
@ -95,6 +76,9 @@ public:
|
|||
|
||||
mtx::presence::PresenceState currentPresence() const;
|
||||
|
||||
// TODO(Nico): Get rid of this!
|
||||
QString currentRoom() const;
|
||||
|
||||
public slots:
|
||||
void handleMatrixUri(const QByteArray &uri);
|
||||
void handleMatrixUri(const QUrl &uri);
|
||||
|
@ -102,7 +86,6 @@ public slots:
|
|||
void startChat(QString userid);
|
||||
void leaveRoom(const QString &room_id);
|
||||
void createRoom(const mtx::requests::CreateRoom &req);
|
||||
void highlightRoom(const QString &room_id);
|
||||
void joinRoom(const QString &room);
|
||||
void joinRoomVia(const std::string &room_id,
|
||||
const std::vector<std::string> &via,
|
||||
|
@ -145,13 +128,10 @@ signals:
|
|||
void leftRoom(const QString &room_id);
|
||||
void newRoom(const QString &room_id);
|
||||
|
||||
void initializeRoomList(QMap<QString, RoomInfo>);
|
||||
void initializeViews(const mtx::responses::Rooms &rooms);
|
||||
void initializeEmptyViews();
|
||||
void initializeMentions(const QMap<QString, mtx::responses::Notifications> ¬ifs);
|
||||
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 notifyMessage(const QString &roomid,
|
||||
|
@ -161,7 +141,6 @@ signals:
|
|||
const QString &message,
|
||||
const QImage &icon);
|
||||
|
||||
void updateGroupsInfo(const mtx::responses::JoinedGroups &groups);
|
||||
void retrievedPresence(const QString &statusMsg, mtx::presence::PresenceState state);
|
||||
void themeChanged();
|
||||
void decryptSidebarChanged();
|
||||
|
@ -207,65 +186,31 @@ private:
|
|||
void getProfileInfo();
|
||||
|
||||
//! Check if the given room is currently open.
|
||||
bool isRoomActive(const QString &room_id)
|
||||
{
|
||||
return isActiveWindow() && currentRoom() == room_id;
|
||||
}
|
||||
bool isRoomActive(const QString &room_id);
|
||||
|
||||
using UserID = QString;
|
||||
using Membership = mtx::events::StateEvent<mtx::events::state::Member>;
|
||||
using Memberships = std::map<std::string, Membership>;
|
||||
|
||||
using LeftRooms = std::map<std::string, mtx::responses::LeftRoom>;
|
||||
void removeLeftRooms(const LeftRooms &rooms);
|
||||
|
||||
void loadStateFromCache();
|
||||
void resetUI();
|
||||
//! Decides whether or not to hide the group's sidebar.
|
||||
void setGroupViewState(bool isEnabled);
|
||||
|
||||
template<class Collection>
|
||||
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.
|
||||
void sendNotifications(const mtx::responses::Notifications &);
|
||||
|
||||
void showNotificationsDialog(const QPoint &point);
|
||||
|
||||
template<typename T>
|
||||
void connectCallMessage();
|
||||
|
||||
QHBoxLayout *topLayout_;
|
||||
Splitter *splitter;
|
||||
|
||||
QWidget *sideBar_;
|
||||
QVBoxLayout *sideBarLayout_;
|
||||
QWidget *sideBarTopWidget_;
|
||||
QVBoxLayout *sideBarTopWidgetLayout_;
|
||||
|
||||
QFrame *content_;
|
||||
QVBoxLayout *contentLayout_;
|
||||
|
||||
CommunitiesList *communitiesList_;
|
||||
RoomList *room_list_;
|
||||
|
||||
TimelineViewManager *view_manager_;
|
||||
SideBarActions *sidebarActions_;
|
||||
|
||||
QTimer connectivityTimer_;
|
||||
std::atomic_bool isConnected_;
|
||||
|
||||
QString current_room_;
|
||||
QString current_community_;
|
||||
|
||||
UserInfoWidget *user_info_widget_;
|
||||
|
||||
popups::UserMentions *user_mentions_popup_;
|
||||
|
||||
// Global user settings.
|
||||
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)));
|
||||
connect(
|
||||
userSettingsPage_, &UserSettingsPage::themeChanged, chat_page_, &ChatPage::themeChanged);
|
||||
connect(userSettingsPage_,
|
||||
&UserSettingsPage::decryptSidebarChanged,
|
||||
chat_page_,
|
||||
&ChatPage::decryptSidebarChanged);
|
||||
connect(trayIcon_,
|
||||
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
|
||||
this,
|
||||
|
@ -176,20 +172,6 @@ MainWindow::setWindowTitle(int notificationCount)
|
|||
QMainWindow::setWindowTitle(name);
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::showEvent(QShowEvent *event)
|
||||
{
|
||||
adjustSideBars();
|
||||
QMainWindow::showEvent(event);
|
||||
}
|
||||
|
||||
void
|
||||
MainWindow::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
adjustSideBars();
|
||||
QMainWindow::resizeEvent(event);
|
||||
}
|
||||
|
||||
bool
|
||||
MainWindow::event(QEvent *event)
|
||||
{
|
||||
|
@ -203,22 +185,6 @@ MainWindow::event(QEvent *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
|
||||
MainWindow::restoreWindowSize()
|
||||
{
|
||||
|
|
|
@ -77,13 +77,9 @@ public:
|
|||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) override;
|
||||
void resizeEvent(QResizeEvent *event) override;
|
||||
void showEvent(QShowEvent *event) override;
|
||||
bool event(QEvent *event) override;
|
||||
|
||||
private slots:
|
||||
//! Show or hide the sidebars based on window's size.
|
||||
void adjustSideBars();
|
||||
//! Handle interaction with the tray icon.
|
||||
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 "ChatPage.h"
|
||||
#include "CompletionProxyModel.h"
|
||||
#include "Config.h"
|
||||
#include "Logging.h"
|
||||
#include "MainWindow.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(); }
|
||||
void setCurrentRoom(QString roomid) { roomlistmodel->setCurrentRoom(std::move(roomid)); }
|
||||
|
||||
void nextRoom();
|
||||
void previousRoom();
|
||||
|
||||
signals:
|
||||
void currentRoomChanged();
|
||||
|
||||
|
|
Loading…
Reference in a new issue