Newer
Older
// SPDX-FileCopyrightText: 2017 Konstantinos Sideris <siderisk@auth.gr>
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later
#include <QShortcut>
#include <mtx/requests.hpp>
#include "Cache.h"
#include "LoginPage.h"
#include "MainWindow.h"
#include "MatrixClient.h"
#include "RegisterPage.h"
#include "TrayIcon.h"
#include "ui/LoadingIndicator.h"
#include "ui/OverlayModal.h"
#include "ui/SnackBar.h"
#include "voip/WebRTCSession.h"
#include "dialogs/CreateRoom.h"
MainWindow *MainWindow::instance_ = nullptr;
MainWindow::MainWindow(QWidget *parent)
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
instance_ = this;
setWindowTitle(0);
setObjectName("MainWindow");
modal_ = new OverlayModal(this);
restoreWindowSize();
QFont font;
font.setStyleStrategy(QFont::PreferAntialias);
setFont(font);
trayIcon_ = new TrayIcon(":/logos/nheko.svg", this);
welcome_page_ = new WelcomePage(this);
login_page_ = new LoginPage(this);
register_page_ = new RegisterPage(this);
chat_page_ = new ChatPage(userSettings_, this);
userSettingsPage_ = new UserSettingsPage(userSettings_, this);
// Initialize sliding widget manager.
pageStack_ = new QStackedWidget(this);
pageStack_->addWidget(welcome_page_);
pageStack_->addWidget(login_page_);
pageStack_->addWidget(register_page_);
pageStack_->addWidget(chat_page_);
pageStack_->addWidget(userSettingsPage_);
setCentralWidget(pageStack_);
connect(welcome_page_, SIGNAL(userLogin()), this, SLOT(showLoginPage()));
connect(welcome_page_, SIGNAL(userRegister()), this, SLOT(showRegisterPage()));
connect(login_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
connect(login_page_, &LoginPage::loggingIn, this, &MainWindow::showOverlayProgressBar);
connect(register_page_, &RegisterPage::registering, this, &MainWindow::showOverlayProgressBar);
connect(login_page_, &LoginPage::errorOccurred, this, [this]() { removeOverlayProgressBar(); });
connect(
register_page_, &RegisterPage::errorOccurred, this, [this]() { removeOverlayProgressBar(); });
connect(register_page_, SIGNAL(backButtonClicked()), this, SLOT(showWelcomePage()));
connect(chat_page_, &ChatPage::closing, this, &MainWindow::showWelcomePage);
connect(
chat_page_, &ChatPage::showOverlayProgressBar, this, &MainWindow::showOverlayProgressBar);
connect(chat_page_, &ChatPage::unreadMessages, this, &MainWindow::setWindowTitle);
connect(chat_page_, SIGNAL(unreadMessages(int)), trayIcon_, SLOT(setUnreadCount(int)));
connect(chat_page_, &ChatPage::showLoginPage, this, [this](const QString &msg) {
login_page_->showError(msg);
showLoginPage();
});
connect(userSettingsPage_, &UserSettingsPage::moveBack, this, [this]() {
pageStack_->setCurrentWidget(chat_page_);
});
connect(userSettingsPage_, SIGNAL(trayOptionChanged(bool)), trayIcon_, SLOT(setVisible(bool)));
connect(
userSettingsPage_, &UserSettingsPage::themeChanged, chat_page_, &ChatPage::themeChanged);
connect(trayIcon_,
SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
this,
SLOT(iconActivated(QSystemTrayIcon::ActivationReason)));
connect(chat_page_, SIGNAL(contentLoaded()), this, SLOT(removeOverlayProgressBar()));
connect(this, &MainWindow::focusChanged, chat_page_, &ChatPage::chatFocusChanged);
connect(chat_page_, &ChatPage::showUserSettingsPage, this, &MainWindow::showUserSettingsPage);
connect(login_page_, &LoginPage::loginOk, this, [this](const mtx::responses::Login &res) {
http::client()->set_user(res.user_id);
showChatPage();
});
connect(register_page_, &RegisterPage::registerOk, this, &MainWindow::showChatPage);
QShortcut *quitShortcut = new QShortcut(QKeySequence::Quit, this);
connect(quitShortcut, &QShortcut::activated, this, QApplication::quit);
trayIcon_->setVisible(userSettings_->tray());
// load cache on event loop
QTimer::singleShot(0, this, [this] {
if (hasActiveUser()) {
QString token = userSettings_->accessToken();
QString home_server = userSettings_->homeserver();
QString user_id = userSettings_->userId();
QString device_id = userSettings_->deviceId();
http::client()->set_access_token(token.toStdString());
http::client()->set_server(home_server.toStdString());
http::client()->set_device_id(device_id.toStdString());
try {
using namespace mtx::identifiers;
http::client()->set_user(parse<User>(user_id.toStdString()));
} catch (const std::invalid_argument &) {
nhlog::ui()->critical("bootstrapped with invalid user_id: {}",
user_id.toStdString());
}
showChatPage();
}
});
void
MainWindow::setWindowTitle(int notificationCount)
{
QString name = "nheko";
if (!userSettings_.data()->profile().isEmpty())
name += " | " + userSettings_.data()->profile();
if (notificationCount > 0) {
name.append(QString{" (%1)"}.arg(notificationCount));
}
QMainWindow::setWindowTitle(name);
auto type = event->type();
if (type == QEvent::WindowActivate) {
emit focusChanged(true);
} else if (type == QEvent::WindowDeactivate) {
emit focusChanged(false);
}
return QMainWindow::event(event);
int savedWidth = userSettings_->qsettings()->value("window/width").toInt();
int savedheight = userSettings_->qsettings()->value("window/height").toInt();
nhlog::ui()->info("Restoring window size {}x{}", savedWidth, savedheight);
if (savedWidth == 0 || savedheight == 0)
resize(conf::window::width, conf::window::height);
else
resize(savedWidth, savedheight);
auto settings = userSettings_->qsettings();
QSize current = size();
settings->setValue("window/width", current.width());
settings->setValue("window/height", current.height());
void
MainWindow::removeOverlayProgressBar()
QTimer *timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, [this, timer]() {
timer->deleteLater();
if (spinner_)
spinner_->stop();
});
// FIXME: Snackbar doesn't work if it's initialized in the constructor.
QTimer::singleShot(0, this, [this]() {
snackBar_ = new SnackBar(this);
connect(chat_page_, &ChatPage::showNotification, snackBar_, &SnackBar::showMessage);
});
MainWindow::showChatPage()
auto userid = QString::fromStdString(http::client()->user_id().to_string());
auto device_id = QString::fromStdString(http::client()->device_id());
auto homeserver = QString::fromStdString(http::client()->server() + ":" +
std::to_string(http::client()->port()));
auto token = QString::fromStdString(http::client()->access_token());
userSettings_.data()->setUserId(userid);
userSettings_.data()->setAccessToken(token);
userSettings_.data()->setDeviceId(device_id);
userSettings_.data()->setHomeserver(homeserver);
showOverlayProgressBar();
pageStack_->setCurrentWidget(chat_page_);
pageStack_->removeWidget(welcome_page_);
pageStack_->removeWidget(login_page_);
pageStack_->removeWidget(register_page_);
login_page_->reset();
chat_page_->bootstrap(userid, homeserver, token);
connect(cache::client(),
&Cache::databaseReady,
userSettingsPage_,
&UserSettingsPage::updateSecretStatus);
connect(cache::client(),
&Cache::secretChanged,
userSettingsPage_,
&UserSettingsPage::updateSecretStatus);
emit reload();
void
MainWindow::closeEvent(QCloseEvent *event)
if (WebRTCSession::instance().state() != webrtc::State::DISCONNECTED) {
if (QMessageBox::question(this, "nheko", "A call is in progress. Quit?") !=
QMessageBox::Yes) {
event->ignore();
return;
if (!qApp->isSavingSession() && isVisible() && pageSupportsTray() && userSettings_->tray()) {
event->ignore();
hide();
}
void
MainWindow::iconActivated(QSystemTrayIcon::ActivationReason reason)
switch (reason) {
case QSystemTrayIcon::Trigger:
if (!isVisible()) {
show();
} else {
hide();
auto settings = userSettings_->qsettings();
QString prefix;
if (userSettings_->profile() != "")
prefix = "profile/" + userSettings_->profile() + "/";
return settings->contains(prefix + "auth/access_token") &&
settings->contains(prefix + "auth/home_server") &&
settings->contains(prefix + "auth/user_id");
void
MainWindow::showOverlayProgressBar()
{
spinner_ = new LoadingIndicator(this);
spinner_->setFixedHeight(100);
spinner_->setFixedWidth(100);
spinner_->setObjectName("ChatPageLoadSpinner");
spinner_->start();
void
MainWindow::openCreateRoomDialog(
std::function<void(const mtx::requests::CreateRoom &request)> callback)
{
auto dialog = new dialogs::CreateRoom(this);
connect(dialog,
&dialogs::CreateRoom::createRoom,
this,
[callback](const mtx::requests::CreateRoom &request) { callback(request); });
}
void
MainWindow::showTransparentOverlayModal(QWidget *content, QFlags<Qt::AlignmentFlag> flags)
{
modal_->setWidget(content);
modal_->setColor(QColor(30, 30, 30, 150));
modal_->setDismissible(true);
modal_->setContentAlignment(flags);
modal_->raise();
modal_->show();
}
void
MainWindow::showSolidOverlayModal(QWidget *content, QFlags<Qt::AlignmentFlag> flags)
{
modal_->setWidget(content);
modal_->setColor(QColor(30, 30, 30));
modal_->setDismissible(false);
modal_->setContentAlignment(flags);
modal_->raise();
modal_->show();
bool
MainWindow::hasActiveDialogs() const
{
bool
MainWindow::pageSupportsTray() const
{
return !welcome_page_->isVisible() && !login_page_->isVisible() && !register_page_->isVisible();
void
MainWindow::hideOverlay()
{
inline void
MainWindow::showDialog(QWidget *dialog)
{
utils::centerWidget(dialog, this);
dialog->raise();
dialog->show();
removeOverlayProgressBar();
pageStack_->addWidget(welcome_page_);
pageStack_->setCurrentWidget(welcome_page_);
pageStack_->addWidget(login_page_);
pageStack_->setCurrentWidget(login_page_);
}
void
MainWindow::showRegisterPage()
{
pageStack_->addWidget(register_page_);
pageStack_->setCurrentWidget(register_page_);
}
void
MainWindow::showUserSettingsPage()
{
pageStack_->setCurrentWidget(userSettingsPage_);