summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorJoseph Donofry <joedonofry@gmail.com>2020-06-11 22:28:44 -0400
committerGitHub <noreply@github.com>2020-06-11 22:28:44 -0400
commit21dfb3c0b9fb760391ff76f39a81889db94d50e3 (patch)
treefa33fb0a8be883401da6bc90054be31a42df4db0 /src
parentMerge pull request #217 from Nheko-Reborn/reactions (diff)
parentMerge branch origin/master and update translations (diff)
downloadnheko-21dfb3c0b9fb760391ff76f39a81889db94d50e3.tar.xz
Merge pull request #216 from Nheko-Reborn/presence
Presence support
Diffstat (limited to 'src')
-rw-r--r--src/Cache.cpp72
-rw-r--r--src/Cache.h6
-rw-r--r--src/Cache_p.h13
-rw-r--r--src/ChatPage.cpp40
-rw-r--r--src/ChatPage.h6
-rw-r--r--src/UserInfoWidget.cpp50
-rw-r--r--src/UserInfoWidget.h4
-rw-r--r--src/UserSettingsPage.cpp15
-rw-r--r--src/UserSettingsPage.h14
-rw-r--r--src/main.cpp18
-rw-r--r--src/timeline/TimelineViewManager.cpp12
-rw-r--r--src/timeline/TimelineViewManager.h3
12 files changed, 246 insertions, 7 deletions
diff --git a/src/Cache.cpp b/src/Cache.cpp

index 009cbabc..d9d1134e 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp
@@ -952,6 +952,8 @@ Cache::saveState(const mtx::responses::Sync &res) saveInvites(txn, res.rooms.invite); + savePresence(txn, res.presence); + removeLeftRooms(txn, res.rooms.leave); txn.commit(); @@ -1037,6 +1039,21 @@ Cache::saveInvite(lmdb::txn &txn, } } +void +Cache::savePresence( + lmdb::txn &txn, + const std::vector<mtx::events::Event<mtx::events::presence::Presence>> &presenceUpdates) +{ + for (const auto &update : presenceUpdates) { + auto presenceDb = getPresenceDb(txn); + + lmdb::dbi_put(txn, + presenceDb, + lmdb::val(update.sender), + lmdb::val(json(update.content).dump())); + } +} + std::vector<std::string> Cache::roomsWithStateUpdates(const mtx::responses::Sync &res) { @@ -2254,6 +2271,50 @@ Cache::removeAvatarUrl(const QString &room_id, const QString &user_id) AvatarUrls.remove(fmt); } +mtx::presence::PresenceState +Cache::presenceState(const std::string &user_id) +{ + lmdb::val presenceVal; + + auto txn = lmdb::txn::begin(env_); + auto db = getPresenceDb(txn); + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), presenceVal); + + mtx::presence::PresenceState state = mtx::presence::offline; + + if (res) { + mtx::events::presence::Presence presence = + json::parse(std::string(presenceVal.data(), presenceVal.size())); + state = presence.presence; + } + + txn.commit(); + + return state; +} + +std::string +Cache::statusMessage(const std::string &user_id) +{ + lmdb::val presenceVal; + + auto txn = lmdb::txn::begin(env_); + auto db = getPresenceDb(txn); + auto res = lmdb::dbi_get(txn, db, lmdb::val(user_id), presenceVal); + + std::string status_msg; + + if (res) { + mtx::events::presence::Presence presence = + json::parse(std::string(presenceVal.data(), presenceVal.size())); + status_msg = presence.status_msg; + } + + txn.commit(); + + return status_msg; +} + void to_json(json &j, const RoomInfo &info) { @@ -2425,6 +2486,17 @@ insertAvatarUrl(const QString &room_id, const QString &user_id, const QString &a instance_->insertAvatarUrl(room_id, user_id, avatar_url); } +mtx::presence::PresenceState +presenceState(const std::string &user_id) +{ + return instance_->presenceState(user_id); +} +std::string +statusMessage(const std::string &user_id) +{ + return instance_->statusMessage(user_id); +} + //! Load saved data for the display names & avatars. void populateMembers() diff --git a/src/Cache.h b/src/Cache.h
index 12465c9d..b5275623 100644 --- a/src/Cache.h +++ b/src/Cache.h
@@ -54,6 +54,12 @@ insertDisplayName(const QString &room_id, const QString &user_id, const QString void insertAvatarUrl(const QString &room_id, const QString &user_id, const QString &avatar_url); +// presence +mtx::presence::PresenceState +presenceState(const std::string &user_id); +std::string +statusMessage(const std::string &user_id); + //! Load saved data for the display names & avatars. void populateMembers(); diff --git a/src/Cache_p.h b/src/Cache_p.h
index 0d66a608..892b66a5 100644 --- a/src/Cache_p.h +++ b/src/Cache_p.h
@@ -52,6 +52,10 @@ public: static QString displayName(const QString &room_id, const QString &user_id); static QString avatarUrl(const QString &room_id, const QString &user_id); + // presence + mtx::presence::PresenceState presenceState(const std::string &user_id); + std::string statusMessage(const std::string &user_id); + static void removeDisplayName(const QString &room_id, const QString &user_id); static void removeAvatarUrl(const QString &room_id, const QString &user_id); @@ -377,6 +381,10 @@ private: void saveInvites(lmdb::txn &txn, const std::map<std::string, mtx::responses::InvitedRoom> &rooms); + void savePresence( + lmdb::txn &txn, + const std::vector<mtx::events::Event<mtx::events::presence::Presence>> &presenceUpdates); + //! Sends signals for the rooms that are removed. void removeLeftRooms(lmdb::txn &txn, const std::map<std::string, mtx::responses::LeftRoom> &rooms) @@ -430,6 +438,11 @@ private: return lmdb::dbi::open(txn, std::string(room_id + "/mentions").c_str(), MDB_CREATE); } + lmdb::dbi getPresenceDb(lmdb::txn &txn) + { + return lmdb::dbi::open(txn, "presence", MDB_CREATE); + } + //! Retrieves or creates the database that stores the open OLM sessions between our device //! and the given curve25519 key which represents another device. //! diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp
index 0ca20c52..3b8af33a 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp
@@ -61,6 +61,7 @@ constexpr size_t MAX_ONETIME_KEYS = 50; Q_DECLARE_METATYPE(std::optional<mtx::crypto::EncryptedFile>) Q_DECLARE_METATYPE(std::optional<RelatedInfo>) +Q_DECLARE_METATYPE(mtx::presence::PresenceState) ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) : QWidget(parent) @@ -72,6 +73,7 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent) qRegisterMetaType<std::optional<mtx::crypto::EncryptedFile>>(); qRegisterMetaType<std::optional<RelatedInfo>>(); + qRegisterMetaType<mtx::presence::PresenceState>(); topLayout_ = new QHBoxLayout(this); topLayout_->setSpacing(0); @@ -990,7 +992,9 @@ ChatPage::startInitialSync() nhlog::net()->info("trying initial sync"); mtx::http::SyncOpts opts; - opts.timeout = 0; + opts.timeout = 0; + opts.set_presence = currentPresence(); + http::client()->sync( opts, std::bind( @@ -1001,6 +1005,7 @@ void ChatPage::trySync() { mtx::http::SyncOpts opts; + opts.set_presence = currentPresence(); if (!connectivityTimer_.isActive()) connectivityTimer_.start(); @@ -1228,6 +1233,39 @@ ChatPage::sendTypingNotifications() }); } +QString +ChatPage::status() const +{ + return QString::fromStdString(cache::statusMessage(utils::localUser().toStdString())); +} + +void +ChatPage::setStatus(const QString &status) +{ + http::client()->put_presence_status( + currentPresence(), status.toStdString(), [](mtx::http::RequestErr err) { + if (err) { + nhlog::net()->warn("failed to set presence status_msg: {}", + err->matrix_error.error); + } + }); +} + +mtx::presence::PresenceState +ChatPage::currentPresence() const +{ + switch (userSettings_->presence()) { + case UserSettings::Presence::Online: + return mtx::presence::online; + case UserSettings::Presence::Unavailable: + return mtx::presence::unavailable; + case UserSettings::Presence::Offline: + return mtx::presence::offline; + default: + return mtx::presence::online; + } +} + void ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::RequestErr err) { diff --git a/src/ChatPage.h b/src/ChatPage.h
index 8c33a63e..c38d7717 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h
@@ -89,6 +89,11 @@ public: void initiateLogout(); void focusMessageInput(); + QString status() const; + void setStatus(const QString &status); + + mtx::presence::PresenceState currentPresence() const; + public slots: void leaveRoom(const QString &room_id); void createRoom(const mtx::requests::CreateRoom &req); @@ -154,6 +159,7 @@ signals: const QImage &icon); void updateGroupsInfo(const mtx::responses::JoinedGroups &groups); + void retrievedPresence(const QString &statusMsg, mtx::presence::PresenceState state); void themeChanged(); void decryptSidebarChanged(); diff --git a/src/UserInfoWidget.cpp b/src/UserInfoWidget.cpp
index e11aa6aa..f8e94431 100644 --- a/src/UserInfoWidget.cpp +++ b/src/UserInfoWidget.cpp
@@ -16,7 +16,9 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QInputDialog> #include <QLabel> +#include <QMenu> #include <QPainter> #include <QStyle> #include <QStyleOption> @@ -24,10 +26,12 @@ #include <iostream> +#include "ChatPage.h" #include "Config.h" #include "MainWindow.h" #include "Splitter.h" #include "UserInfoWidget.h" +#include "UserSettingsPage.h" #include "ui/Avatar.h" #include "ui/FlatButton.h" #include "ui/OverlayModal.h" @@ -105,6 +109,52 @@ UserInfoWidget::UserInfoWidget(QWidget *parent) connect(logoutButton_, &QPushButton::clicked, this, []() { MainWindow::instance()->openLogoutDialog(); }); + + menu = new QMenu(this); + + auto setStatusAction = menu->addAction(tr("Set custom status message")); + connect(setStatusAction, &QAction::triggered, this, [this]() { + bool ok = false; + QString text = QInputDialog::getText(this, + tr("Custom status message"), + tr("Status:"), + QLineEdit::Normal, + ChatPage::instance()->status(), + &ok); + if (ok) + ChatPage::instance()->setStatus(text); + }); + +#if 0 // disable presence menu until issues in synapse are resolved + auto setAutoPresence = menu->addAction(tr("Set presence automatically")); + connect(setAutoPresence, &QAction::triggered, this, []() { + ChatPage::instance()->userSettings()->setPresence( + UserSettings::Presence::AutomaticPresence); + ChatPage::instance()->setStatus(ChatPage::instance()->status()); + }); + auto setOnline = menu->addAction(tr("Online")); + connect(setOnline, &QAction::triggered, this, []() { + ChatPage::instance()->userSettings()->setPresence(UserSettings::Presence::Online); + ChatPage::instance()->setStatus(ChatPage::instance()->status()); + }); + auto setUnavailable = menu->addAction(tr("Unavailable")); + connect(setUnavailable, &QAction::triggered, this, []() { + ChatPage::instance()->userSettings()->setPresence( + UserSettings::Presence::Unavailable); + ChatPage::instance()->setStatus(ChatPage::instance()->status()); + }); + auto setOffline = menu->addAction(tr("Offline")); + connect(setOffline, &QAction::triggered, this, []() { + ChatPage::instance()->userSettings()->setPresence(UserSettings::Presence::Offline); + ChatPage::instance()->setStatus(ChatPage::instance()->status()); + }); +#endif +} + +void +UserInfoWidget::contextMenuEvent(QContextMenuEvent *event) +{ + menu->popup(event->globalPos()); } void diff --git a/src/UserInfoWidget.h b/src/UserInfoWidget.h
index 575ade52..03ab2cf0 100644 --- a/src/UserInfoWidget.h +++ b/src/UserInfoWidget.h
@@ -26,6 +26,7 @@ class OverlayModal; class QLabel; class QHBoxLayout; class QVBoxLayout; +class QMenu; class UserInfoWidget : public QWidget { @@ -48,6 +49,7 @@ public: protected: void resizeEvent(QResizeEvent *event) override; void paintEvent(QPaintEvent *event) override; + void contextMenuEvent(QContextMenuEvent *) override; private: Avatar *userAvatar_; @@ -70,4 +72,6 @@ private: int logoutButtonSize_; QColor borderColor_; + + QMenu *menu = nullptr; }; diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index 4e9e0cb0..88cbd1c9 100644 --- a/src/UserSettingsPage.cpp +++ b/src/UserSettingsPage.cpp
@@ -34,6 +34,7 @@ #include <QStandardPaths> #include <QString> #include <QTextStream> +#include <QtQml> #include "Cache.h" #include "Config.h" @@ -73,6 +74,9 @@ UserSettings::load() decryptSidebar_ = settings.value("user/decrypt_sidebar", true).toBool(); emojiFont_ = settings.value("user/emoji_font_family", "default").toString(); baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble(); + presence_ = + settings.value("user/presence", QVariant::fromValue(Presence::AutomaticPresence)) + .value<Presence>(); applyTheme(); } @@ -255,6 +259,16 @@ UserSettings::setEmojiFontFamily(QString family) } void +UserSettings::setPresence(Presence state) +{ + if (state == presence_) + return; + presence_ = state; + emit presenceChanged(state); + save(); +} + +void UserSettings::setTheme(QString theme) { if (theme == theme) @@ -349,6 +363,7 @@ UserSettings::save() settings.setValue("theme", theme()); settings.setValue("font_family", font_); settings.setValue("emoji_font_family", emojiFont_); + settings.setValue("presence", QVariant::fromValue(presence_)); settings.endGroup(); diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index c90dc759..d2a1c641 100644 --- a/src/UserSettingsPage.h +++ b/src/UserSettingsPage.h
@@ -70,10 +70,20 @@ class UserSettings : public QObject Q_PROPERTY(QString font READ font WRITE setFontFamily NOTIFY fontChanged) Q_PROPERTY( QString emojiFont READ emojiFont WRITE setEmojiFontFamily NOTIFY emojiFontChanged) + Q_PROPERTY(Presence presence READ presence WRITE setPresence NOTIFY presenceChanged) public: UserSettings(); + enum class Presence + { + AutomaticPresence, + Online, + Unavailable, + Offline, + }; + Q_ENUM(Presence); + void save(); void load(); void applyTheme(); @@ -96,6 +106,7 @@ public: void setAlertOnNotification(bool state); void setAvatarCircles(bool state); void setDecryptSidebar(bool state); + void setPresence(Presence state); QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; } bool messageHoverHighlight() const { return messageHoverHighlight_; } @@ -120,6 +131,7 @@ public: double fontSize() const { return baseFontSize_; } QString font() const { return font_; } QString emojiFont() const { return emojiFont_; } + Presence presence() const { return presence_; } signals: void groupViewStateChanged(bool state); @@ -141,6 +153,7 @@ signals: void fontSizeChanged(double state); void fontChanged(QString state); void emojiFontChanged(QString state); + void presenceChanged(Presence state); private: // Default to system theme if QT_QPA_PLATFORMTHEME var is set. @@ -167,6 +180,7 @@ private: double baseFontSize_; QString font_; QString emojiFont_; + Presence presence_; }; class HorizontalLine : public QFrame diff --git a/src/main.cpp b/src/main.cpp
index ec4f638d..46691e6f 100644 --- a/src/main.cpp +++ b/src/main.cpp
@@ -104,6 +104,18 @@ createCacheDirectory() int main(int argc, char *argv[]) { + // needed for settings so need to register before any settings are read to prevent warings + qRegisterMetaType<UserSettings::Presence>(); + + QCoreApplication::setApplicationName("nheko"); + QCoreApplication::setApplicationVersion(nheko::version); + QCoreApplication::setOrganizationName("nheko"); + QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); + QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); + QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); + + // this needs to be after setting the application name. Or how would we find our settings + // file then? #if defined(Q_OS_LINUX) || defined(Q_OS_WIN) || defined(Q_OS_FREEBSD) if (qgetenv("QT_SCALE_FACTOR").size() == 0) { float factor = utils::scaleFactor(); @@ -113,12 +125,6 @@ main(int argc, char *argv[]) } #endif - QCoreApplication::setApplicationName("nheko"); - QCoreApplication::setApplicationVersion(nheko::version); - QCoreApplication::setOrganizationName("nheko"); - QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); - QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); SingleApplication app(argc, argv, false, diff --git a/src/timeline/TimelineViewManager.cpp b/src/timeline/TimelineViewManager.cpp
index d6f9fde1..151cfb4e 100644 --- a/src/timeline/TimelineViewManager.cpp +++ b/src/timeline/TimelineViewManager.cpp
@@ -59,6 +59,18 @@ TimelineViewManager::userColor(QString id, QColor background) return userColors.value(id); } +QString +TimelineViewManager::userPresence(QString id) const +{ + return QString::fromStdString( + mtx::presence::to_string(cache::presenceState(id.toStdString()))); +} +QString +TimelineViewManager::userStatus(QString id) const +{ + return QString::fromStdString(cache::statusMessage(id.toStdString())); +} + TimelineViewManager::TimelineViewManager(QSharedPointer<UserSettings> userSettings, QWidget *parent) : imgProvider(new MxcImageProvider()) , colorImgProvider(new ColorImageProvider()) diff --git a/src/timeline/TimelineViewManager.h b/src/timeline/TimelineViewManager.h
index 48505bc0..ed095058 100644 --- a/src/timeline/TimelineViewManager.h +++ b/src/timeline/TimelineViewManager.h
@@ -44,6 +44,9 @@ public: Q_INVOKABLE void openImageOverlay(QString mxcUrl, QString eventId) const; Q_INVOKABLE QColor userColor(QString id, QColor background); + Q_INVOKABLE QString userPresence(QString id) const; + Q_INVOKABLE QString userStatus(QString id) const; + signals: void clearRoomMessageCount(QString roomid); void updateRoomsLastMessage(QString roomid, const DescInfo &info);