mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Update presence dynamically and reduce allocations
This commit is contained in:
parent
4428388b3f
commit
9a9dbda571
11 changed files with 145 additions and 79 deletions
|
@ -328,6 +328,7 @@ set(SRC_FILES
|
||||||
src/timeline/TimelineModel.cpp
|
src/timeline/TimelineModel.cpp
|
||||||
src/timeline/DelegateChooser.cpp
|
src/timeline/DelegateChooser.cpp
|
||||||
src/timeline/Permissions.cpp
|
src/timeline/Permissions.cpp
|
||||||
|
src/timeline/PresenceEmitter.cpp
|
||||||
src/timeline/RoomlistModel.cpp
|
src/timeline/RoomlistModel.cpp
|
||||||
|
|
||||||
# UI components
|
# UI components
|
||||||
|
@ -535,6 +536,7 @@ qt5_wrap_cpp(MOC_HEADERS
|
||||||
src/timeline/TimelineModel.h
|
src/timeline/TimelineModel.h
|
||||||
src/timeline/DelegateChooser.h
|
src/timeline/DelegateChooser.h
|
||||||
src/timeline/Permissions.h
|
src/timeline/Permissions.h
|
||||||
|
src/timeline/PresenceEmitter.h
|
||||||
src/timeline/RoomlistModel.h
|
src/timeline/RoomlistModel.h
|
||||||
|
|
||||||
# UI components
|
# UI components
|
||||||
|
|
|
@ -87,14 +87,18 @@ Rectangle {
|
||||||
}
|
}
|
||||||
|
|
||||||
Rectangle {
|
Rectangle {
|
||||||
|
id: onlineIndicator
|
||||||
|
|
||||||
anchors.bottom: avatar.bottom
|
anchors.bottom: avatar.bottom
|
||||||
anchors.right: avatar.right
|
anchors.right: avatar.right
|
||||||
visible: !!userid
|
visible: !!userid
|
||||||
height: avatar.height / 6
|
height: avatar.height / 6
|
||||||
width: height
|
width: height
|
||||||
radius: Settings.avatarCircles ? height / 2 : height / 8
|
radius: Settings.avatarCircles ? height / 2 : height / 8
|
||||||
color: {
|
color: updatePresence()
|
||||||
switch (TimelineManager.userPresence(userid)) {
|
|
||||||
|
function updatePresence() {
|
||||||
|
switch (Presence.userPresence(userid)) {
|
||||||
case "online":
|
case "online":
|
||||||
return "#00cc66";
|
return "#00cc66";
|
||||||
case "unavailable":
|
case "unavailable":
|
||||||
|
@ -102,7 +106,15 @@ Rectangle {
|
||||||
case "offline":
|
case "offline":
|
||||||
default:
|
default:
|
||||||
// return "#a82353" don't show anything if offline, since it is confusing, if presence is disabled
|
// return "#a82353" don't show anything if offline, since it is confusing, if presence is disabled
|
||||||
"transparent";
|
return "transparent";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Presence
|
||||||
|
|
||||||
|
function onPresenceChanged(id) {
|
||||||
|
if (id == userid) onlineIndicator.color = onlineIndicator.updatePresence();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -329,12 +329,21 @@ ScrollView {
|
||||||
}
|
}
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
|
id: statusMsg
|
||||||
color: Nheko.colors.buttonText
|
color: Nheko.colors.buttonText
|
||||||
text: TimelineManager.userStatus(userId)
|
text: Presence.userStatus(userId)
|
||||||
textFormat: Text.PlainText
|
textFormat: Text.PlainText
|
||||||
elide: Text.ElideRight
|
elide: Text.ElideRight
|
||||||
width: chat.delegateMaxWidth - parent.spacing * 2 - userName.implicitWidth - Nheko.avatarSize
|
width: chat.delegateMaxWidth - parent.spacing * 2 - userName.implicitWidth - Nheko.avatarSize
|
||||||
font.italic: true
|
font.italic: true
|
||||||
|
|
||||||
|
Connections {
|
||||||
|
target: Presence
|
||||||
|
|
||||||
|
function onPresenceChanged(id) {
|
||||||
|
if (id == userId) statusMsg.text = Presence.userStatus(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3897,53 +3897,26 @@ Cache::avatarUrl(const QString &room_id, const QString &user_id)
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
mtx::presence::PresenceState
|
mtx::events::presence::Presence
|
||||||
Cache::presenceState(const std::string &user_id)
|
Cache::presence(const std::string &user_id)
|
||||||
{
|
{
|
||||||
if (user_id.empty())
|
if (user_id.empty())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
std::string_view presenceVal;
|
std::string_view presenceVal;
|
||||||
|
|
||||||
auto txn = lmdb::txn::begin(env_);
|
auto txn = ro_txn(env_);
|
||||||
auto db = getPresenceDb(txn);
|
auto db = getPresenceDb(txn);
|
||||||
auto res = db.get(txn, user_id, presenceVal);
|
auto res = db.get(txn, user_id, presenceVal);
|
||||||
|
|
||||||
mtx::presence::PresenceState state = mtx::presence::offline;
|
mtx::events::presence::Presence presence_{};
|
||||||
|
presence_.presence = mtx::presence::PresenceState::offline;
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
mtx::events::presence::Presence presence =
|
presence_ = json::parse(std::string_view(presenceVal.data(), presenceVal.size()));
|
||||||
json::parse(std::string_view(presenceVal.data(), presenceVal.size()));
|
|
||||||
state = presence.presence;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
txn.commit();
|
return presence_;
|
||||||
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string
|
|
||||||
Cache::statusMessage(const std::string &user_id)
|
|
||||||
{
|
|
||||||
if (user_id.empty())
|
|
||||||
return {};
|
|
||||||
|
|
||||||
std::string_view presenceVal;
|
|
||||||
|
|
||||||
auto txn = lmdb::txn::begin(env_);
|
|
||||||
auto db = getPresenceDb(txn);
|
|
||||||
auto res = db.get(txn, user_id, presenceVal);
|
|
||||||
|
|
||||||
std::string status_msg;
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
mtx::events::presence::Presence presence = json::parse(presenceVal);
|
|
||||||
status_msg = presence.status_msg;
|
|
||||||
}
|
|
||||||
|
|
||||||
txn.commit();
|
|
||||||
|
|
||||||
return status_msg;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -4747,17 +4720,12 @@ avatarUrl(const QString &room_id, const QString &user_id)
|
||||||
return instance_->avatarUrl(room_id, user_id);
|
return instance_->avatarUrl(room_id, user_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
mtx::presence::PresenceState
|
mtx::events::presence::Presence
|
||||||
presenceState(const std::string &user_id)
|
presence(const std::string &user_id)
|
||||||
{
|
{
|
||||||
if (!instance_)
|
if (!instance_)
|
||||||
return {};
|
return {};
|
||||||
return instance_->presenceState(user_id);
|
return instance_->presence(user_id);
|
||||||
}
|
|
||||||
std::string
|
|
||||||
statusMessage(const std::string &user_id)
|
|
||||||
{
|
|
||||||
return instance_->statusMessage(user_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// user cache stores user keys
|
// user cache stores user keys
|
||||||
|
|
|
@ -38,10 +38,8 @@ QString
|
||||||
avatarUrl(const QString &room_id, const QString &user_id);
|
avatarUrl(const QString &room_id, const QString &user_id);
|
||||||
|
|
||||||
// presence
|
// presence
|
||||||
mtx::presence::PresenceState
|
mtx::events::presence::Presence
|
||||||
presenceState(const std::string &user_id);
|
presence(const std::string &user_id);
|
||||||
std::string
|
|
||||||
statusMessage(const std::string &user_id);
|
|
||||||
|
|
||||||
// user cache stores user keys
|
// user cache stores user keys
|
||||||
std::optional<UserKeyCache>
|
std::optional<UserKeyCache>
|
||||||
|
|
|
@ -43,8 +43,7 @@ public:
|
||||||
QString avatarUrl(const QString &room_id, const QString &user_id);
|
QString avatarUrl(const QString &room_id, const QString &user_id);
|
||||||
|
|
||||||
// presence
|
// presence
|
||||||
mtx::presence::PresenceState presenceState(const std::string &user_id);
|
mtx::events::presence::Presence presence(const std::string &user_id);
|
||||||
std::string statusMessage(const std::string &user_id);
|
|
||||||
|
|
||||||
// user cache stores user keys
|
// user cache stores user keys
|
||||||
std::map<std::string, std::optional<UserKeyCache>>
|
std::map<std::string, std::optional<UserKeyCache>>
|
||||||
|
|
|
@ -877,7 +877,7 @@ ChatPage::receivedSessionKey(const std::string &room_id, const std::string &sess
|
||||||
QString
|
QString
|
||||||
ChatPage::status() const
|
ChatPage::status() const
|
||||||
{
|
{
|
||||||
return QString::fromStdString(cache::statusMessage(utils::localUser().toStdString()));
|
return QString::fromStdString(cache::presence(utils::localUser().toStdString()).status_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
73
src/timeline/PresenceEmitter.cpp
Normal file
73
src/timeline/PresenceEmitter.cpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
#include "PresenceEmitter.h"
|
||||||
|
|
||||||
|
#include <QCache>
|
||||||
|
#include <Utils.h>
|
||||||
|
|
||||||
|
#include "Cache.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
struct CacheEntry
|
||||||
|
{
|
||||||
|
QString status;
|
||||||
|
mtx::presence::PresenceState state;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static QCache<QString, CacheEntry> presences;
|
||||||
|
|
||||||
|
static QString
|
||||||
|
presenceToStr(mtx::presence::PresenceState state)
|
||||||
|
{
|
||||||
|
switch (state) {
|
||||||
|
case mtx::presence::PresenceState::offline:
|
||||||
|
return QStringLiteral("offline");
|
||||||
|
case mtx::presence::PresenceState::unavailable:
|
||||||
|
return QStringLiteral("unavailable");
|
||||||
|
case mtx::presence::PresenceState::online:
|
||||||
|
default:
|
||||||
|
return QStringLiteral("online");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static CacheEntry *
|
||||||
|
pullPresence(const QString &id)
|
||||||
|
{
|
||||||
|
auto p = cache::presence(id.toStdString());
|
||||||
|
auto c = new CacheEntry{
|
||||||
|
utils::replaceEmoji(QString::fromStdString(p.status_msg).toHtmlEscaped()), p.presence};
|
||||||
|
presences.insert(id, c);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PresenceEmitter::sync(
|
||||||
|
const std::vector<mtx::events::Event<mtx::events::presence::Presence>> &presences_)
|
||||||
|
{
|
||||||
|
for (const auto &p : presences_) {
|
||||||
|
auto id = QString::fromStdString(p.sender);
|
||||||
|
presences.remove(id);
|
||||||
|
emit presenceChanged(std::move(id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
PresenceEmitter::userPresence(QString id) const
|
||||||
|
{
|
||||||
|
if (id.isEmpty())
|
||||||
|
return {};
|
||||||
|
else if (auto p = presences[id])
|
||||||
|
return presenceToStr(p->state);
|
||||||
|
else
|
||||||
|
return presenceToStr(pullPresence(id)->state);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
PresenceEmitter::userStatus(QString id) const
|
||||||
|
{
|
||||||
|
if (id.isEmpty())
|
||||||
|
return {};
|
||||||
|
else if (auto p = presences[id])
|
||||||
|
return p->status;
|
||||||
|
else
|
||||||
|
return pullPresence(id)->status;
|
||||||
|
}
|
26
src/timeline/PresenceEmitter.h
Normal file
26
src/timeline/PresenceEmitter.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <mtx/events.hpp>
|
||||||
|
#include <mtx/events/presence.hpp>
|
||||||
|
|
||||||
|
class PresenceEmitter : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
PresenceEmitter(QObject *p = nullptr)
|
||||||
|
: QObject(p)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void sync(const std::vector<mtx::events::Event<mtx::events::presence::Presence>> &presences);
|
||||||
|
|
||||||
|
Q_INVOKABLE QString userPresence(QString id) const;
|
||||||
|
Q_INVOKABLE QString userStatus(QString id) const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void presenceChanged(QString userid);
|
||||||
|
};
|
|
@ -124,29 +124,6 @@ TimelineViewManager::userColor(QString id, QColor background)
|
||||||
return userColors.value(idx);
|
return userColors.value(idx);
|
||||||
}
|
}
|
||||||
|
|
||||||
QString
|
|
||||||
TimelineViewManager::userPresence(QString id) const
|
|
||||||
{
|
|
||||||
if (id.isEmpty())
|
|
||||||
return {};
|
|
||||||
else
|
|
||||||
switch (cache::presenceState(id.toStdString())) {
|
|
||||||
case mtx::presence::PresenceState::offline:
|
|
||||||
return QStringLiteral("offline");
|
|
||||||
case mtx::presence::PresenceState::unavailable:
|
|
||||||
return QStringLiteral("unavailable");
|
|
||||||
case mtx::presence::PresenceState::online:
|
|
||||||
default:
|
|
||||||
return QStringLiteral("online");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString
|
|
||||||
TimelineViewManager::userStatus(QString id) const
|
|
||||||
{
|
|
||||||
return QString::fromStdString(cache::statusMessage(id.toStdString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *parent)
|
TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, imgProvider(new MxcImageProvider())
|
, imgProvider(new MxcImageProvider())
|
||||||
|
@ -157,6 +134,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
|
||||||
, communities_(new CommunitiesModel(this))
|
, communities_(new CommunitiesModel(this))
|
||||||
, callManager_(callManager)
|
, callManager_(callManager)
|
||||||
, verificationManager_(new VerificationManager(this))
|
, verificationManager_(new VerificationManager(this))
|
||||||
|
, presenceEmitter(new PresenceEmitter(this))
|
||||||
{
|
{
|
||||||
qRegisterMetaType<mtx::events::msg::KeyVerificationAccept>();
|
qRegisterMetaType<mtx::events::msg::KeyVerificationAccept>();
|
||||||
qRegisterMetaType<mtx::events::msg::KeyVerificationCancel>();
|
qRegisterMetaType<mtx::events::msg::KeyVerificationCancel>();
|
||||||
|
@ -280,6 +258,7 @@ TimelineViewManager::TimelineViewManager(CallManager *callManager, ChatPage *par
|
||||||
return new Nheko();
|
return new Nheko();
|
||||||
});
|
});
|
||||||
qmlRegisterSingletonInstance("im.nheko", 1, 0, "VerificationManager", verificationManager_);
|
qmlRegisterSingletonInstance("im.nheko", 1, 0, "VerificationManager", verificationManager_);
|
||||||
|
qmlRegisterSingletonInstance("im.nheko", 1, 0, "Presence", presenceEmitter);
|
||||||
qmlRegisterSingletonType<SelfVerificationStatus>(
|
qmlRegisterSingletonType<SelfVerificationStatus>(
|
||||||
"im.nheko", 1, 0, "SelfVerificationStatus", [](QQmlEngine *, QJSEngine *) -> QObject * {
|
"im.nheko", 1, 0, "SelfVerificationStatus", [](QQmlEngine *, QJSEngine *) -> QObject * {
|
||||||
auto ptr = new SelfVerificationStatus();
|
auto ptr = new SelfVerificationStatus();
|
||||||
|
@ -407,6 +386,7 @@ TimelineViewManager::sync(const mtx::responses::Sync &sync_)
|
||||||
{
|
{
|
||||||
this->rooms_->sync(sync_);
|
this->rooms_->sync(sync_);
|
||||||
this->communities_->sync(sync_);
|
this->communities_->sync(sync_);
|
||||||
|
this->presenceEmitter->sync(sync_.presence);
|
||||||
|
|
||||||
if (isInitialSync_) {
|
if (isInitialSync_) {
|
||||||
this->isInitialSync_ = false;
|
this->isInitialSync_ = false;
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
#include "emoji/Provider.h"
|
#include "emoji/Provider.h"
|
||||||
#include "encryption/VerificationManager.h"
|
#include "encryption/VerificationManager.h"
|
||||||
#include "timeline/CommunitiesModel.h"
|
#include "timeline/CommunitiesModel.h"
|
||||||
|
#include "timeline/PresenceEmitter.h"
|
||||||
#include "timeline/RoomlistModel.h"
|
#include "timeline/RoomlistModel.h"
|
||||||
#include "voip/CallManager.h"
|
#include "voip/CallManager.h"
|
||||||
#include "voip/WebRTCSession.h"
|
#include "voip/WebRTCSession.h"
|
||||||
|
@ -64,9 +65,6 @@ public:
|
||||||
Q_INVOKABLE QString escapeEmoji(QString str) const;
|
Q_INVOKABLE QString escapeEmoji(QString str) const;
|
||||||
Q_INVOKABLE QString htmlEscape(QString str) const { return str.toHtmlEscaped(); }
|
Q_INVOKABLE QString htmlEscape(QString str) const { return str.toHtmlEscaped(); }
|
||||||
|
|
||||||
Q_INVOKABLE QString userPresence(QString id) const;
|
|
||||||
Q_INVOKABLE QString userStatus(QString id) const;
|
|
||||||
|
|
||||||
Q_INVOKABLE void openRoomMembers(TimelineModel *room);
|
Q_INVOKABLE void openRoomMembers(TimelineModel *room);
|
||||||
Q_INVOKABLE void openRoomSettings(QString room_id);
|
Q_INVOKABLE void openRoomSettings(QString room_id);
|
||||||
Q_INVOKABLE void openInviteUsers(QString roomId);
|
Q_INVOKABLE void openInviteUsers(QString roomId);
|
||||||
|
@ -146,6 +144,7 @@ private:
|
||||||
// don't move this above the rooms_
|
// don't move this above the rooms_
|
||||||
CallManager *callManager_ = nullptr;
|
CallManager *callManager_ = nullptr;
|
||||||
VerificationManager *verificationManager_ = nullptr;
|
VerificationManager *verificationManager_ = nullptr;
|
||||||
|
PresenceEmitter *presenceEmitter = nullptr;
|
||||||
|
|
||||||
QHash<QPair<QString, quint64>, QColor> userColors;
|
QHash<QPair<QString, quint64>, QColor> userColors;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue