diff --git a/src/AvatarProvider.cpp b/src/AvatarProvider.cpp
index 57b61c75..ec745c04 100644
--- a/src/AvatarProvider.cpp
+++ b/src/AvatarProvider.cpp
@@ -16,30 +16,44 @@
*/
#include <QBuffer>
+#include <QPixmapCache>
#include <memory>
+#include <unordered_map>
#include "AvatarProvider.h"
#include "Cache.h"
#include "Logging.h"
#include "MatrixClient.h"
-namespace AvatarProvider {
+static QPixmapCache avatar_cache;
+namespace AvatarProvider {
void
-resolve(const QString &room_id, const QString &user_id, QObject *receiver, AvatarCallback callback)
+resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback callback)
{
- const auto key = QString("%1 %2").arg(room_id).arg(user_id);
- const auto avatarUrl = Cache::avatarUrl(room_id, user_id);
+ avatar_cache.setCacheLimit(1024 * 1024);
- if (!Cache::AvatarUrls.contains(key) || !cache::client())
+ const auto cacheKey = avatarUrl + "_size_" + size;
+
+ if (!cache::client())
return;
if (avatarUrl.isEmpty())
return;
+ QPixmap pixmap;
+ if (avatar_cache.find(cacheKey, &pixmap)) {
+ nhlog::net()->info("cached pixmap {}", avatarUrl.toStdString());
+ callback(pixmap);
+ return;
+ }
+
auto data = cache::client()->image(avatarUrl);
if (!data.isNull()) {
- callback(QImage::fromData(data));
+ pixmap.loadFromData(data);
+ avatar_cache.insert(cacheKey, pixmap);
+ nhlog::net()->info("loaded pixmap from disk cache {}", avatarUrl.toStdString());
+ callback(pixmap);
return;
}
@@ -47,7 +61,12 @@ resolve(const QString &room_id, const QString &user_id, QObject *receiver, Avata
QObject::connect(proxy.get(),
&AvatarProxy::avatarDownloaded,
receiver,
- [callback](const QByteArray &data) { callback(QImage::fromData(data)); });
+ [callback, cacheKey](const QByteArray &data) {
+ QPixmap pm;
+ pm.loadFromData(data);
+ avatar_cache.insert(cacheKey, pm);
+ callback(pm);
+ });
mtx::http::ThumbOpts opts;
opts.width = 256;
@@ -67,8 +86,26 @@ resolve(const QString &room_id, const QString &user_id, QObject *receiver, Avata
cache::client()->saveImage(opts.mxc_url, res);
- auto data = QByteArray(res.data(), res.size());
- emit proxy->avatarDownloaded(data);
+ nhlog::net()->info("downloaded pixmap {}", opts.mxc_url);
+
+ emit proxy->avatarDownloaded(QByteArray(res.data(), res.size()));
});
}
+
+void
+resolve(const QString &room_id,
+ const QString &user_id,
+ int size,
+ QObject *receiver,
+ AvatarCallback callback)
+{
+ const auto key = QString("%1 %2").arg(room_id).arg(user_id);
+ const auto avatarUrl = Cache::avatarUrl(room_id, user_id);
+ const auto cacheKey = avatarUrl + "_size_" + size;
+
+ if (!Cache::AvatarUrls.contains(key) || !cache::client())
+ return;
+
+ resolve(avatarUrl, size, receiver, callback);
+}
}
diff --git a/src/AvatarProvider.h b/src/AvatarProvider.h
index 4b4e15e9..47ed028e 100644
--- a/src/AvatarProvider.h
+++ b/src/AvatarProvider.h
@@ -17,7 +17,7 @@
#pragma once
-#include <QImage>
+#include <QPixmap>
#include <functional>
class AvatarProxy : public QObject
@@ -28,9 +28,15 @@ signals:
void avatarDownloaded(const QByteArray &data);
};
-using AvatarCallback = std::function<void(QImage)>;
+using AvatarCallback = std::function<void(QPixmap)>;
namespace AvatarProvider {
void
-resolve(const QString &room_id, const QString &user_id, QObject *receiver, AvatarCallback cb);
+resolve(const QString &avatarUrl, int size, QObject *receiver, AvatarCallback cb);
+void
+resolve(const QString &room_id,
+ const QString &user_id,
+ int size,
+ QObject *receiver,
+ AvatarCallback cb);
}
diff --git a/src/Cache.cpp b/src/Cache.cpp
index 56c79678..083dbe89 100644
--- a/src/Cache.cpp
+++ b/src/Cache.cpp
@@ -22,6 +22,7 @@
#include <QCoreApplication>
#include <QFile>
#include <QHash>
+#include <QMap>
#include <QSettings>
#include <QStandardPaths>
@@ -91,6 +92,7 @@ init(const QString &user_id)
qRegisterMetaType<RoomSearchResult>();
qRegisterMetaType<RoomInfo>();
qRegisterMetaType<QMap<QString, RoomInfo>>();
+ qRegisterMetaType<QMap<QString, mtx::responses::Notifications>>();
qRegisterMetaType<std::map<QString, RoomInfo>>();
qRegisterMetaType<std::map<QString, mtx::responses::Timeline>>();
@@ -1232,6 +1234,27 @@ Cache::roomMessages()
return msgs;
}
+QMap<QString, mtx::responses::Notifications>
+Cache::getTimelineMentions()
+{
+ // TODO: Should be read-only, but getMentionsDb will attempt to create a DB
+ // if it doesn't exist, throwing an error.
+ auto txn = lmdb::txn::begin(env_, nullptr);
+
+ QMap<QString, mtx::responses::Notifications> notifs;
+
+ auto room_ids = getRoomIds(txn);
+
+ for (const auto &room_id : room_ids) {
+ auto roomNotifs = getTimelineMentionsForRoom(txn, room_id);
+ notifs[QString::fromStdString(room_id)] = roomNotifs;
+ }
+
+ txn.commit();
+
+ return notifs;
+}
+
mtx::responses::Timeline
Cache::getTimelineMessages(lmdb::txn &txn, const std::string &room_id)
{
@@ -1807,10 +1830,7 @@ Cache::searchRooms(const std::string &query, std::uint8_t max_items)
std::vector<RoomSearchResult> results;
for (auto it = items.begin(); it != end; it++) {
- results.push_back(
- RoomSearchResult{it->second.first,
- it->second.second,
- QImage::fromData(image(txn, it->second.second.avatar_url))});
+ results.push_back(RoomSearchResult{it->second.first, it->second.second});
}
txn.commit();
@@ -1935,6 +1955,88 @@ Cache::saveTimelineMessages(lmdb::txn &txn,
}
}
+mtx::responses::Notifications
+Cache::getTimelineMentionsForRoom(lmdb::txn &txn, const std::string &room_id)
+{
+ auto db = getMentionsDb(txn, room_id);
+
+ if (db.size(txn) == 0) {
+ return mtx::responses::Notifications{};
+ }
+
+ mtx::responses::Notifications notif;
+ std::string event_id, msg;
+
+ auto cursor = lmdb::cursor::open(txn, db);
+
+ while (cursor.get(event_id, msg, MDB_NEXT)) {
+ auto obj = json::parse(msg);
+
+ if (obj.count("event") == 0)
+ continue;
+
+ mtx::responses::Notification notification;
+ mtx::responses::from_json(obj, notification);
+
+ notif.notifications.push_back(notification);
+ }
+ cursor.close();
+
+ std::reverse(notif.notifications.begin(), notif.notifications.end());
+
+ return notif;
+}
+
+//! Add all notifications containing a user mention to the db.
+void
+Cache::saveTimelineMentions(const mtx::responses::Notifications &res)
+{
+ QMap<std::string, QList<mtx::responses::Notification>> notifsByRoom;
+
+ // Sort into room-specific 'buckets'
+ for (const auto ¬if : res.notifications) {
+ json val = notif;
+ notifsByRoom[notif.room_id].push_back(notif);
+ }
+
+ auto txn = lmdb::txn::begin(env_);
+ // Insert the entire set of mentions for each room at a time.
+ QMap<std::string, QList<mtx::responses::Notification>>::const_iterator it =
+ notifsByRoom.constBegin();
+ auto end = notifsByRoom.constEnd();
+ while (it != end) {
+ nhlog::db()->debug("Storing notifications for " + it.key());
+ saveTimelineMentions(txn, it.key(), std::move(it.value()));
+ ++it;
+ }
+
+ txn.commit();
+}
+
+void
+Cache::saveTimelineMentions(lmdb::txn &txn,
+ const std::string &room_id,
+ const QList<mtx::responses::Notification> &res)
+{
+ auto db = getMentionsDb(txn, room_id);
+
+ using namespace mtx::events;
+ using namespace mtx::events::state;
+
+ for (const auto ¬if : res) {
+ const auto event_id = utils::event_id(notif.event);
+
+ // double check that we have the correct room_id...
+ if (room_id.compare(notif.room_id) != 0) {
+ return;
+ }
+
+ json obj = notif;
+
+ lmdb::dbi_put(txn, db, lmdb::val(event_id), lmdb::val(obj.dump()));
+ }
+}
+
void
Cache::markSentNotification(const std::string &event_id)
{
diff --git a/src/Cache.h b/src/Cache.h
index 65c5263d..0da49793 100644
--- a/src/Cache.h
+++ b/src/Cache.h
@@ -32,6 +32,7 @@
#include <nlohmann/json.hpp>
#include "Logging.h"
+#include "MatrixClient.h"
using mtx::events::state::JoinRule;
@@ -152,7 +153,6 @@ struct RoomSearchResult
{
std::string room_id;
RoomInfo info;
- QImage img;
};
Q_DECLARE_METATYPE(RoomSearchResult)
@@ -323,6 +323,8 @@ public:
std::map<QString, mtx::responses::Timeline> roomMessages();
+ QMap<QString, mtx::responses::Notifications> getTimelineMentions();
+
//! Retrieve all the user ids from a room.
std::vector<std::string> roomMembers(const std::string &room_id);
@@ -402,6 +404,9 @@ public:
//! Check if we have sent a desktop notification for the given event id.
bool isNotificationSent(const std::string &event_id);
+ //! Add all notifications containing a user mention to the db.
+ void saveTimelineMentions(const mtx::responses::Notifications &res);
+
//! Remove old unused data.
void deleteOldMessages();
void deleteOldData() noexcept;
@@ -470,6 +475,15 @@ private:
lmdb::dbi &membersdb,
const mtx::responses::InvitedRoom &room);
+ //! Add a notification containing a user mention to the db.
+ void saveTimelineMentions(lmdb::txn &txn,
+ const std::string &room_id,
+ const QList<mtx::responses::Notification> &res);
+
+ //! Get timeline items that a user was mentions in for a given room
+ mtx::responses::Notifications getTimelineMentionsForRoom(lmdb::txn &txn,
+ const std::string &room_id);
+
QString getInviteRoomName(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
QString getInviteRoomTopic(lmdb::txn &txn, lmdb::dbi &statesdb);
QString getInviteRoomAvatarUrl(lmdb::txn &txn, lmdb::dbi &statesdb, lmdb::dbi &membersdb);
@@ -660,6 +674,11 @@ private:
return lmdb::dbi::open(txn, std::string(room_id + "/members").c_str(), MDB_CREATE);
}
+ lmdb::dbi getMentionsDb(lmdb::txn &txn, const std::string &room_id)
+ {
+ return lmdb::dbi::open(txn, std::string(room_id + "/mentions").c_str(), 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 18188429..21ded4b3 100644
--- a/src/ChatPage.cpp
+++ b/src/ChatPage.cpp
@@ -35,7 +35,6 @@
#include "TopRoomBar.h"
#include "TypingDisplay.h"
#include "UserInfoWidget.h"
-#include "UserMentionsWidget.h"
#include "UserSettingsPage.h"
#include "Utils.h"
#include "ui/OverlayModal.h"
@@ -44,7 +43,7 @@
#include "notifications/Manager.h"
#include "dialogs/ReadReceipts.h"
-#include "dialogs/UserMentions.h"
+#include "popups/UserMentions.h"
#include "timeline/TimelineViewManager.h"
// TODO: Needs to be updated with an actual secret.
@@ -90,13 +89,12 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
connect(sidebarActions_, &SideBarActions::joinRoom, this, &ChatPage::joinRoom);
connect(sidebarActions_, &SideBarActions::createRoom, this, &ChatPage::createRoom);
- user_info_widget_ = new UserInfoWidget(sideBar_);
- // user_mentions_widget_ = new UserMentionsWidget(sideBar_);
- room_list_ = new RoomList(sideBar_);
+ user_info_widget_ = new UserInfoWidget(sideBar_);
+ user_mentions_popup_ = new popups::UserMentions();
+ room_list_ = new RoomList(sideBar_);
connect(room_list_, &RoomList::joinRoom, this, &ChatPage::joinRoom);
sideBarLayout_->addWidget(user_info_widget_);
- // sideBarLayout_->addWidget(user_mentions_widget_);
sideBarLayout_->addWidget(room_list_);
sideBarLayout_->addWidget(sidebarActions_);
@@ -155,21 +153,27 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
});
connect(top_bar_, &TopRoomBar::mentionsClicked, this, [this](const QPoint &mentionsPos) {
- http::client()->notifications(
- 1000,
- "",
- "highlight",
- [this, mentionsPos](const mtx::responses::Notifications &res,
- mtx::http::RequestErr err) {
- if (err) {
- nhlog::net()->warn("failed to retrieve notifications: {} ({})",
- err->matrix_error.error,
- static_cast<int>(err->status_code));
- return;
- }
+ if (user_mentions_popup_->isVisible()) {
+ user_mentions_popup_->hide();
+ } else {
+ showNotificationsDialog(mentionsPos);
+ http::client()->notifications(
+ 1000,
+ "",
+ "highlight",
+ [this, mentionsPos](const mtx::responses::Notifications &res,
+ mtx::http::RequestErr err) {
+ if (err) {
+ nhlog::net()->warn(
+ "failed to retrieve notifications: {} ({})",
+ err->matrix_error.error,
+ static_cast<int>(err->status_code));
+ return;
+ }
- emit highlightedNotifsRetrieved(std::move(res), mentionsPos);
- });
+ emit highlightedNotifsRetrieved(std::move(res), mentionsPos);
+ });
+ }
});
connectivityTimer_.setInterval(CHECK_CONNECTIVITY_INTERVAL);
@@ -519,8 +523,16 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
connect(this, &ChatPage::leftRoom, this, &ChatPage::removeRoom);
connect(this, &ChatPage::notificationsRetrieved, this, &ChatPage::sendDesktopNotifications);
- connect(
- this, &ChatPage::highlightedNotifsRetrieved, this, &ChatPage::showNotificationsDialog);
+ connect(this,
+ &ChatPage::highlightedNotifsRetrieved,
+ this,
+ [](const mtx::responses::Notifications ¬if) {
+ try {
+ cache::client()->saveTimelineMentions(notif);
+ } catch (const lmdb::error &e) {
+ nhlog::db()->error("failed to save mentions: {}", e.what());
+ }
+ });
connect(communitiesList_,
&CommunitiesList::communityChanged,
@@ -559,6 +571,10 @@ ChatPage::ChatPage(QSharedPointer<UserSettings> userSettings, QWidget *parent)
&ChatPage::initializeEmptyViews,
view_manager_,
&TimelineViewManager::initWithMessages);
+ connect(this,
+ &ChatPage::initializeMentions,
+ user_mentions_popup_,
+ &popups::UserMentions::initializeMentions);
connect(this, &ChatPage::syncUI, this, [this](const mtx::responses::Rooms &rooms) {
try {
room_list_->cleanupInvites(cache::client()->invites());
@@ -758,12 +774,12 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
}
void
-ChatPage::updateTopBarAvatar(const QString &roomid, const QPixmap &img)
+ChatPage::updateTopBarAvatar(const QString &roomid, const QString &img)
{
if (current_room_ != roomid)
return;
- top_bar_->updateRoomAvatar(img.toImage());
+ top_bar_->updateRoomAvatar(img);
}
void
@@ -791,7 +807,7 @@ ChatPage::changeTopRoomInfo(const QString &room_id)
if (img.isNull())
top_bar_->updateRoomAvatarFromName(name);
else
- top_bar_->updateRoomAvatar(img);
+ top_bar_->updateRoomAvatar(avatar_url);
} catch (const lmdb::error &e) {
nhlog::ui()->error("failed to change top bar room info: {}", e.what());
@@ -831,6 +847,7 @@ ChatPage::loadStateFromCache()
emit initializeEmptyViews(cache::client()->roomMessages());
emit initializeRoomList(cache::client()->roomInfo());
+ emit initializeMentions(cache::client()->getTimelineMentions());
emit syncTags(cache::client()->roomInfo().toStdMap());
cache::client()->calculateRoomReadStatus();
@@ -987,32 +1004,15 @@ ChatPage::sendDesktopNotifications(const mtx::responses::Notifications &res)
}
void
-ChatPage::showNotificationsDialog(const mtx::responses::Notifications &res, const QPoint &widgetPos)
+ChatPage::showNotificationsDialog(const QPoint &widgetPos)
{
- // TODO: This should NOT BE A DIALOG. Make the TimelineView support
- // creating a timeline view from notifications (similarly to how it can show history views)
- auto notifDialog = new dialogs::UserMentions();
- for (const auto &item : res.notifications) {
- const auto event_id = QString::fromStdString(utils::event_id(item.event));
+ auto notifDialog = user_mentions_popup_;
- try {
- const auto room_id = QString::fromStdString(item.room_id);
- const auto user_id = utils::event_sender(item.event);
- const auto body = utils::event_body(item.event);
-
- notifDialog->pushItem(event_id, user_id, body, room_id);
-
- } catch (const lmdb::error &e) {
- nhlog::db()->warn("error while sending desktop notification: {}", e.what());
- }
- }
notifDialog->setGeometry(
widgetPos.x() - (width() / 10), widgetPos.y() + 25, width() / 5, height() / 2);
- // notifDialog->move(widgetPos.x(), widgetPos.y());
- // notifDialog->setFixedWidth(width() / 10);
- // notifDialog->setFixedHeight(height() / 2);
+
notifDialog->raise();
- notifDialog->show();
+ notifDialog->showPopup();
}
void
@@ -1243,6 +1243,8 @@ ChatPage::sendTypingNotifications()
void
ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::RequestErr err)
{
+ // TODO: Initial Sync should include mentions as well...
+
if (err) {
const auto error = QString::fromStdString(err->matrix_error.error);
const auto msg = tr("Please try to login again: %1").arg(error);
@@ -1280,6 +1282,7 @@ ChatPage::initialSyncHandler(const mtx::responses::Sync &res, mtx::http::Request
emit initializeViews(std::move(res.rooms));
emit initializeRoomList(cache::client()->roomInfo());
+ emit initializeMentions(cache::client()->getTimelineMentions());
cache::client()->calculateRoomReadStatus();
emit syncTags(cache::client()->roomInfo().toStdMap());
@@ -1334,37 +1337,7 @@ ChatPage::getProfileInfo()
emit setUserDisplayName(QString::fromStdString(res.display_name));
- if (cache::client()) {
- auto data = cache::client()->image(res.avatar_url);
- if (!data.isNull()) {
- emit setUserAvatar(QImage::fromData(data));
- return;
- }
- }
-
- if (res.avatar_url.empty())
- return;
-
- http::client()->download(
- res.avatar_url,
- [this, res](const std::string &data,
- const std::string &,
- const std::string &,
- mtx::http::RequestErr err) {
- if (err) {
- nhlog::net()->warn(
- "failed to download user avatar: {} - {}",
- mtx::errors::to_string(err->matrix_error.errcode),
- err->matrix_error.error);
- return;
- }
-
- if (cache::client())
- cache::client()->saveImage(res.avatar_url, data);
-
- emit setUserAvatar(
- QImage::fromData(QByteArray(data.data(), data.size())));
- });
+ emit setUserAvatar(QString::fromStdString(res.avatar_url));
});
http::client()->joined_groups(
diff --git a/src/ChatPage.h b/src/ChatPage.h
index 3c97ba25..e41ae1ae 100644
--- a/src/ChatPage.h
+++ b/src/ChatPage.h
@@ -19,6 +19,7 @@
#include <atomic>
#include <boost/variant.hpp>
+#include <mtx/responses.hpp>
#include <QFrame>
#include <QHBoxLayout>
@@ -33,6 +34,7 @@
#include "MatrixClient.h"
#include "Utils.h"
#include "notifications/Manager.h"
+#include "popups/UserMentions.h"
class OverlayModal;
class QuickSwitcher;
@@ -44,7 +46,6 @@ class TimelineViewManager;
class TopRoomBar;
class TypingDisplay;
class UserInfoWidget;
-class UserMentionsWidget;
class UserSettings;
class NotificationsManager;
@@ -128,7 +129,7 @@ signals:
void ownProfileOk();
void setUserDisplayName(const QString &name);
- void setUserAvatar(const QImage &avatar);
+ void setUserAvatar(const QString &avatar);
void loggedOut();
void trySyncCb();
@@ -139,6 +140,7 @@ signals:
void initializeRoomList(QMap<QString, RoomInfo>);
void initializeViews(const mtx::responses::Rooms &rooms);
void initializeEmptyViews(const std::map<QString, mtx::responses::Timeline> &msgs);
+ 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);
@@ -157,7 +159,7 @@ signals:
private slots:
void showUnreadMessageNotification(int count);
- void updateTopBarAvatar(const QString &roomid, const QPixmap &img);
+ void updateTopBarAvatar(const QString &roomid, const QString &img);
void changeTopRoomInfo(const QString &room_id);
void logout();
void removeRoom(const QString &room_id);
@@ -208,7 +210,7 @@ private:
//! Send desktop notification for the received messages.
void sendDesktopNotifications(const mtx::responses::Notifications &);
- void showNotificationsDialog(const mtx::responses::Notifications &, const QPoint &point);
+ void showNotificationsDialog(const QPoint &point);
QStringList generateTypingUsers(const QString &room_id,
const std::vector<std::string> &typing_users);
@@ -242,7 +244,7 @@ private:
UserInfoWidget *user_info_widget_;
- UserMentionsWidget *user_mentions_widget_;
+ popups::UserMentions *user_mentions_popup_;
// Keeps track of the users currently typing on each room.
std::map<QString, QList<QString>> typingUsers_;
diff --git a/src/Logging.cpp b/src/Logging.cpp
index 686274d8..32287582 100644
--- a/src/Logging.cpp
+++ b/src/Logging.cpp
@@ -16,6 +16,8 @@ constexpr auto MAX_LOG_FILES = 3;
}
namespace nhlog {
+bool enable_debug_log_from_commandline = false;
+
void
init(const std::string &file_path)
{
diff --git a/src/Logging.h b/src/Logging.h
index 2feae60d..e54f3c3f 100644
--- a/src/Logging.h
+++ b/src/Logging.h
@@ -18,4 +18,6 @@ db();
std::shared_ptr<spdlog::logger>
crypto();
+
+extern bool enable_debug_log_from_commandline;
}
diff --git a/src/RoomInfoListItem.cpp b/src/RoomInfoListItem.cpp
index 9bcce134..8aadbea2 100644
--- a/src/RoomInfoListItem.cpp
+++ b/src/RoomInfoListItem.cpp
@@ -19,8 +19,10 @@
#include <QDebug>
#include <QMouseEvent>
#include <QPainter>
+#include <QSettings>
#include <QtGlobal>
+#include "AvatarProvider.h"
#include "Cache.h"
#include "Config.h"
#include "RoomInfoListItem.h"
@@ -140,6 +142,8 @@ RoomInfoListItem::resizeEvent(QResizeEvent *)
void
RoomInfoListItem::paintEvent(QPaintEvent *event)
{
+ bool rounded = QSettings().value("user/avatar/circles", true).toBool();
+
Q_UNUSED(event);
QPainter p(this);
@@ -287,7 +291,8 @@ RoomInfoListItem::paintEvent(QPaintEvent *event)
p.setPen(Qt::NoPen);
p.setBrush(brush);
- p.drawEllipse(avatarRegion.center(), wm.iconSize / 2, wm.iconSize / 2);
+ rounded ? p.drawEllipse(avatarRegion.center(), wm.iconSize / 2, wm.iconSize / 2)
+ : p.drawRoundedRect(avatarRegion, 3, 3);
QFont bubbleFont;
bubbleFont.setPointSizeF(bubbleFont.pointSizeF() * 1.4);
@@ -300,7 +305,9 @@ RoomInfoListItem::paintEvent(QPaintEvent *event)
p.save();
QPainterPath path;
- path.addEllipse(wm.padding, wm.padding, wm.iconSize, wm.iconSize);
+ rounded ? path.addEllipse(wm.padding, wm.padding, wm.iconSize, wm.iconSize)
+ : path.addRoundedRect(avatarRegion, 3, 3);
+
p.setClipPath(path);
p.drawPixmap(avatarRegion, roomAvatar_);
@@ -434,10 +441,12 @@ RoomInfoListItem::mousePressEvent(QMouseEvent *event)
}
void
-RoomInfoListItem::setAvatar(const QImage &img)
+RoomInfoListItem::setAvatar(const QString &avatar_url)
{
- roomAvatar_ = utils::scaleImageToPixmap(img, IconSize);
- update();
+ AvatarProvider::resolve(avatar_url, IconSize, this, [this](const QPixmap &img) {
+ roomAvatar_ = img;
+ update();
+ });
}
void
diff --git a/src/RoomInfoListItem.h b/src/RoomInfoListItem.h
index 40c938c1..54e02a76 100644
--- a/src/RoomInfoListItem.h
+++ b/src/RoomInfoListItem.h
@@ -73,7 +73,7 @@ public:
bool isPressed() const { return isPressed_; }
int unreadMessageCount() const { return unreadMsgCount_; }
- void setAvatar(const QImage &avatar_image);
+ void setAvatar(const QString &avatar_url);
void setDescriptionMessage(const DescInfo &info);
DescInfo lastMessageInfo() const { return lastMsgInfo_; }
diff --git a/src/RoomList.cpp b/src/RoomList.cpp
index 1abf3533..c5e05621 100644
--- a/src/RoomList.cpp
+++ b/src/RoomList.cpp
@@ -89,40 +89,7 @@ RoomList::updateAvatar(const QString &room_id, const QString &url)
if (url.isEmpty())
return;
- QByteArray savedImgData;
-
- if (cache::client())
- savedImgData = cache::client()->image(url);
-
- if (savedImgData.isEmpty()) {
- mtx::http::ThumbOpts opts;
- opts.mxc_url = url.toStdString();
- http::client()->get_thumbnail(
- opts, [room_id, opts, this](const std::string &res, mtx::http::RequestErr err) {
- if (err) {
- nhlog::net()->warn(
- "failed to download room avatar: {} {} {}",
- opts.mxc_url,
- mtx::errors::to_string(err->matrix_error.errcode),
- err->matrix_error.error);
- return;
- }
-
- if (cache::client())
- cache::client()->saveImage(opts.mxc_url, res);
-
- auto data = QByteArray(res.data(), res.size());
- QPixmap pixmap;
- pixmap.loadFromData(data);
-
- emit updateRoomAvatarCb(room_id, pixmap);
- });
- } else {
- QPixmap img;
- img.loadFromData(savedImgData);
-
- updateRoomAvatar(room_id, img);
- }
+ emit updateRoomAvatarCb(room_id, url);
}
void
@@ -252,7 +219,7 @@ RoomList::highlightSelectedRoom(const QString &room_id)
}
void
-RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img)
+RoomList::updateRoomAvatar(const QString &roomid, const QString &img)
{
if (!roomExists(roomid)) {
nhlog::ui()->warn("avatar update on non-existent room_id: {}",
@@ -260,7 +227,7 @@ RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img)
return;
}
- rooms_[roomid]->setAvatar(img.toImage());
+ rooms_[roomid]->setAvatar(img);
// Used to inform other widgets for the new image data.
emit roomAvatarChanged(roomid, img);
diff --git a/src/RoomList.h b/src/RoomList.h
index 155a969c..95fc0d9b 100644
--- a/src/RoomList.h
+++ b/src/RoomList.h
@@ -61,12 +61,12 @@ signals:
void totalUnreadMessageCountUpdated(int count);
void acceptInvite(const QString &room_id);
void declineInvite(const QString &room_id);
- void roomAvatarChanged(const QString &room_id, const QPixmap &img);
+ void roomAvatarChanged(const QString &room_id, const QString &img);
void joinRoom(const QString &room_id);
- void updateRoomAvatarCb(const QString &room_id, const QPixmap &img);
+ void updateRoomAvatarCb(const QString &room_id, const QString &img);
public slots:
- void updateRoomAvatar(const QString &roomid, const QPixmap &img);
+ 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);
diff --git a/src/TopRoomBar.cpp b/src/TopRoomBar.cpp
index a8049e3a..712fe9aa 100644
--- a/src/TopRoomBar.cpp
+++ b/src/TopRoomBar.cpp
@@ -46,9 +46,8 @@ TopRoomBar::TopRoomBar(QWidget *parent)
topLayout_->setContentsMargins(
2 * widgetMargin, widgetMargin, 2 * widgetMargin, widgetMargin);
- avatar_ = new Avatar(this);
+ avatar_ = new Avatar(this, fontHeight * 2);
avatar_->setLetter("");
- avatar_->setSize(fontHeight * 2);
textLayout_ = new QVBoxLayout();
textLayout_->setSpacing(0);
@@ -183,7 +182,7 @@ TopRoomBar::reset()
}
void
-TopRoomBar::updateRoomAvatar(const QImage &avatar_image)
+TopRoomBar::updateRoomAvatar(const QString &avatar_image)
{
avatar_->setImage(avatar_image);
update();
diff --git a/src/TopRoomBar.h b/src/TopRoomBar.h
index 5f2c936e..3243064e 100644
--- a/src/TopRoomBar.h
+++ b/src/TopRoomBar.h
@@ -44,7 +44,7 @@ class TopRoomBar : public QWidget
public:
TopRoomBar(QWidget *parent = 0);
- void updateRoomAvatar(const QImage &avatar_image);
+ void updateRoomAvatar(const QString &avatar_image);
void updateRoomAvatar(const QIcon &icon);
void updateRoomName(const QString &name);
void updateRoomTopic(QString topic);
diff --git a/src/TypingDisplay.cpp b/src/TypingDisplay.cpp
index 6059601d..43fabcd8 100644
--- a/src/TypingDisplay.cpp
+++ b/src/TypingDisplay.cpp
@@ -33,6 +33,14 @@ TypingDisplay::setUsers(const QStringList &uid)
text_.clear();
+ QString temp = text_ +=
+ tr("%1 and %2 are typing",
+ "Multiple users are typing. First argument is a comma separated list of potentially "
+ "multiple users. Second argument is the last user of that list. (If only one user is "
+ "typing, %1 is empty. You should still use it in your string though to silence Qt "
+ "warnings.)",
+ uid.size());
+
if (uid.isEmpty()) {
hide();
update();
@@ -40,12 +48,9 @@ TypingDisplay::setUsers(const QStringList &uid)
return;
}
- text_ = uid.join(", ");
-
- if (uid.size() == 1)
- text_ += tr(" is typing");
- else if (uid.size() > 1)
- text_ += tr(" are typing");
+ QStringList uidWithoutLast = uid;
+ uidWithoutLast.pop_back();
+ text_ = temp.arg(uidWithoutLast.join(", ")).arg(uid.back());
show();
update();
diff --git a/src/UserInfoWidget.cpp b/src/UserInfoWidget.cpp
index 5345fb2a..7a910340 100644
--- a/src/UserInfoWidget.cpp
+++ b/src/UserInfoWidget.cpp
@@ -1,3 +1,4 @@
+
/*
* nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>
*
@@ -52,10 +53,9 @@ UserInfoWidget::UserInfoWidget(QWidget *parent)
textLayout_->setSpacing(widgetMargin / 2);
textLayout_->setContentsMargins(widgetMargin * 2, widgetMargin, widgetMargin, widgetMargin);
- userAvatar_ = new Avatar(this);
+ userAvatar_ = new Avatar(this, fontHeight * 2.5);
userAvatar_->setObjectName("userAvatar");
userAvatar_->setLetter(QChar('?'));
- userAvatar_->setSize(fontHeight * 2.5);
QFont nameFont;
nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1);
@@ -135,14 +135,6 @@ UserInfoWidget::reset()
}
void
-UserInfoWidget::setAvatar(const QImage &img)
-{
- avatar_image_ = img;
- userAvatar_->setImage(img);
- update();
-}
-
-void
UserInfoWidget::setDisplayName(const QString &name)
{
if (name.isEmpty())
@@ -160,6 +152,14 @@ UserInfoWidget::setUserId(const QString &userid)
{
user_id_ = userid;
userIdLabel_->setText(userid);
+ update();
+}
+
+void
+UserInfoWidget::setAvatar(const QString &url)
+{
+ userAvatar_->setImage(url);
+ update();
}
void
diff --git a/src/UserInfoWidget.h b/src/UserInfoWidget.h
index 65de7be9..263dd0c2 100644
--- a/src/UserInfoWidget.h
+++ b/src/UserInfoWidget.h
@@ -33,9 +33,9 @@ class UserInfoWidget : public QWidget
public:
UserInfoWidget(QWidget *parent = 0);
- void setAvatar(const QImage &img);
void setDisplayName(const QString &name);
void setUserId(const QString &userid);
+ void setAvatar(const QString &url);
void reset();
diff --git a/src/UserMentionsWidget.cpp b/src/UserMentionsWidget.cpp
deleted file mode 100644
index a28db930..00000000
--- a/src/UserMentionsWidget.cpp
+++ /dev/null
@@ -1,309 +0,0 @@
-#include <QDateTime>
-#include <QDebug>
-#include <QMouseEvent>
-#include <QPainter>
-#include <QtGlobal>
-
-#include "MainWindow.h"
-#include "UserMentionsWidget.h"
-#include "Utils.h"
-#include "ui/Ripple.h"
-#include "ui/RippleOverlay.h"
-
-constexpr int MaxUnreadCountDisplayed = 99;
-
-struct WMetrics
-{
- int maxHeight;
- int iconSize;
- int padding;
- int unit;
-
- int unreadLineWidth;
- int unreadLineOffset;
-
- int inviteBtnX;
- int inviteBtnY;
-};
-
-WMetrics
-getWMetrics(const QFont &font)
-{
- WMetrics 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.inviteBtnX = m.iconSize / 2.0 + m.padding + m.padding / 3.0;
-
- return m;
-}
-
-UserMentionsWidget::UserMentionsWidget(QWidget *parent)
- : QWidget(parent)
- , isPressed_(false)
- , unreadMsgCount_(0)
-{
- init(parent);
-
- QFont f;
- f.setPointSizeF(f.pointSizeF());
-
- const int fontHeight = QFontMetrics(f).height();
- const int widgetMargin = fontHeight / 3;
- const int contentHeight = fontHeight * 3;
-
- setFixedHeight(contentHeight + widgetMargin);
-
- topLayout_ = new QHBoxLayout(this);
- topLayout_->setSpacing(0);
- topLayout_->setMargin(widgetMargin);
-}
-
-void
-UserMentionsWidget::init(QWidget *parent)
-{
- setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
- setMouseTracking(true);
- setAttribute(Qt::WA_Hover);
-
- setFixedHeight(getWMetrics(QFont{}).maxHeight);
-
- QPainterPath path;
- path.addRect(0, 0, parent->width(), height());
-
- ripple_overlay_ = new RippleOverlay(this);
- ripple_overlay_->setClipPath(path);
- ripple_overlay_->setClipping(true);
-
- unreadCountFont_.setPointSizeF(unreadCountFont_.pointSizeF() * 0.8);
- unreadCountFont_.setBold(true);
-
- bubbleDiameter_ = QFontMetrics(unreadCountFont_).averageCharWidth() * 3;
-}
-
-// void
-// UserMentionsWidget::resizeEvent(QResizeEvent *event)
-// {
-// Q_UNUSED(event);
-
-// const auto sz = utils::calculateSidebarSizes(QFont{});
-
-// if (width() <= sz.small) {
-// topLayout_->setContentsMargins(0, 0, logoutButtonSize_, 0);
-
-// } else {
-// topLayout_->setMargin(5);
-// }
-
-// QWidget::resizeEvent(event);
-// }
-
-void
-UserMentionsWidget::setPressedState(bool state)
-{
- if (isPressed_ != state) {
- isPressed_ = state;
- update();
- }
-}
-
-void
-UserMentionsWidget::resizeEvent(QResizeEvent *)
-{
- // Update ripple's clipping path.
- QPainterPath path;
- path.addRect(0, 0, width(), height());
-
- const auto sidebarSizes = utils::calculateSidebarSizes(QFont{});
-
- if (width() > sidebarSizes.small)
- setToolTip("");
- else
- setToolTip("");
-
- ripple_overlay_->setClipPath(path);
- ripple_overlay_->setClipping(true);
-}
-
-void
-UserMentionsWidget::mousePressEvent(QMouseEvent *event)
-{
- if (event->buttons() == Qt::RightButton) {
- QWidget::mousePressEvent(event);
- return;
- }
-
- emit clicked();
-
- 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
-UserMentionsWidget::paintEvent(QPaintEvent *event)
-{
- Q_UNUSED(event);
-
- QPainter p(this);
- p.setRenderHint(QPainter::TextAntialiasing);
- p.setRenderHint(QPainter::SmoothPixmapTransform);
- p.setRenderHint(QPainter::Antialiasing);
-
- auto wm = getWMetrics(QFont{});
-
- QPen titlePen(titleColor_);
- QPen subtitlePen(subtitleColor_);
-
- QFontMetrics metrics(QFont{});
-
- if (isPressed_) {
- p.fillRect(rect(), highlightedBackgroundColor_);
- titlePen.setColor(highlightedTitleColor_);
- subtitlePen.setColor(highlightedSubtitleColor_);
- } else if (underMouse()) {
- p.fillRect(rect(), hoverBackgroundColor_);
- titlePen.setColor(hoverTitleColor_);
- subtitlePen.setColor(hoverSubtitleColor_);
- } else {
- p.fillRect(rect(), backgroundColor_);
- titlePen.setColor(titleColor_);
- subtitlePen.setColor(subtitleColor_);
- }
-
- // Description line with the default font.
- int bottom_y = wm.maxHeight - wm.padding - metrics.ascent() / 2;
-
- const auto sidebarSizes = utils::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("timestamp") + 4;
-#else
- const int msgStampWidth = QFontMetrics(tsFont).horizontalAdvance("timestamp") + 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(
- "Mentions",
- Qt::ElideRight,
- (width() - wm.iconSize - 2 * wm.padding - msgStampWidth) * 0.8);
- p.drawText(QPoint(2 * wm.padding + wm.iconSize, top_y), name);
-
- p.setFont(QFont{});
- p.setPen(subtitlePen);
-
- // The limit is the space between the end of the avatar and the start of the
- // timestamp.
- int usernameLimit =
- std::max(0, width() - 3 * wm.padding - msgStampWidth - wm.iconSize - 20);
- auto userName =
- metrics.elidedText("Show Mentioned Messages", Qt::ElideRight, usernameLimit);
-
- p.setFont(QFont{});
- p.drawText(QPoint(2 * wm.padding + wm.iconSize, bottom_y), userName);
-
- // 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), "timestamp");
- p.restore();
- }
-
- p.setPen(Qt::NoPen);
-
- if (unreadMsgCount_ > 0) {
- QBrush brush;
- brush.setStyle(Qt::SolidPattern);
-
- brush.setColor(mentionedColor());
-
- 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);
- }
-}
\ No newline at end of file
diff --git a/src/UserMentionsWidget.h b/src/UserMentionsWidget.h
deleted file mode 100644
index 179f0026..00000000
--- a/src/UserMentionsWidget.h
+++ /dev/null
@@ -1,164 +0,0 @@
-#pragma once
-
-#include <QColor>
-#include <QHBoxLayout>
-#include <QLabel>
-#include <QLayout>
-#include <QWidget>
-
-class FlatButton;
-class RippleOverlay;
-
-class UserMentionsWidget : public QWidget
-{
- Q_OBJECT
-
- Q_PROPERTY(QColor borderColor READ borderColor WRITE setBorderColor)
-
- 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 avatarBgColor READ avatarBgColor WRITE setAvatarBgColor)
- Q_PROPERTY(QColor avatarFgColor READ avatarFgColor WRITE setAvatarFgColor)
-
- 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:
- UserMentionsWidget(QWidget *parent = 0);
-
- void updateUnreadMessageCount(int count);
- void clearUnreadMessageCount() { updateUnreadMessageCount(0); };
- bool isPressed() const { return isPressed_; }
- int unreadMessageCount() const { return unreadMsgCount_; }
- QColor borderColor() const { return borderColor_; }
- void setBorderColor(QColor &color) { borderColor_ = color; }
- 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 avatarBgColor() const { return avatarBgColor_; }
- QColor avatarFgColor() const { return avatarFgColor_; }
-
- 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 setAvatarFgColor(QColor &color) { avatarFgColor_ = color; }
- void setAvatarBgColor(QColor &color) { avatarBgColor_ = 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; }
-
-signals:
- void clicked();
-
-public slots:
- void setPressedState(bool state);
-
-protected:
- void mousePressEvent(QMouseEvent *event) override;
- void paintEvent(QPaintEvent *event) override;
- void resizeEvent(QResizeEvent *event) override;
-
-private:
- void init(QWidget *parent);
-
- RippleOverlay *ripple_overlay_;
-
- bool isPressed_ = false;
-
- bool hasUnreadMessages_ = true;
-
- int unreadMsgCount_ = 0;
-
- QHBoxLayout *topLayout_;
-
- QColor borderColor_;
- 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 avatarBgColor_;
- QColor avatarFgColor_;
-
- QColor bubbleBgColor_;
- QColor bubbleFgColor_;
-};
\ No newline at end of file
diff --git a/src/UserSettingsPage.cpp b/src/UserSettingsPage.cpp
index 49cb2c1f..9fd033e9 100644
--- a/src/UserSettingsPage.cpp
+++ b/src/UserSettingsPage.cpp
@@ -22,9 +22,11 @@
#include <QLabel>
#include <QLineEdit>
#include <QMessageBox>
+#include <QProcessEnvironment>
#include <QPushButton>
#include <QScrollArea>
#include <QSettings>
+#include <QString>
#include <QTextStream>
#include "Config.h"
@@ -49,8 +51,9 @@ UserSettings::load()
isGroupViewEnabled_ = settings.value("user/group_view", true).toBool();
isTypingNotificationsEnabled_ = settings.value("user/typing_notifications", true).toBool();
isReadReceiptsEnabled_ = settings.value("user/read_receipts", true).toBool();
- theme_ = settings.value("user/theme", "light").toString();
+ theme_ = settings.value("user/theme", defaultTheme_).toString();
font_ = settings.value("user/font_family", "default").toString();
+ avatarCircles_ = settings.value("user/avatar/circles", true).toBool();
emojiFont_ = settings.value("user/emoji_font_family", "default").toString();
baseFontSize_ = settings.value("user/font_size", QFont().pointSizeF()).toDouble();
@@ -116,6 +119,10 @@ UserSettings::save()
settings.setValue("start_in_tray", isStartInTrayEnabled_);
settings.endGroup();
+ settings.beginGroup("avatar");
+ settings.setValue("circles", avatarCircles_);
+ settings.endGroup();
+
settings.setValue("font_size", baseFontSize_);
settings.setValue("typing_notifications", isTypingNotificationsEnabled_);
settings.setValue("read_receipts", isReadReceiptsEnabled_);
@@ -190,6 +197,15 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
groupViewLayout->addWidget(groupViewLabel);
groupViewLayout->addWidget(groupViewToggle_, 0, Qt::AlignRight);
+ auto avatarViewLayout = new QHBoxLayout;
+ avatarViewLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
+ auto avatarViewLabel = new QLabel(tr("Circular Avatars"), this);
+ avatarViewLabel->setFont(font);
+ avatarCircles_ = new Toggle(this);
+
+ avatarViewLayout->addWidget(avatarViewLabel);
+ avatarViewLayout->addWidget(avatarCircles_, 0, Qt::AlignRight);
+
auto typingLayout = new QHBoxLayout;
typingLayout->setContentsMargins(0, OptionMargin, 0, OptionMargin);
auto typingLabel = new QLabel(tr("Typing notifications"), this);
@@ -366,6 +382,7 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
mainLayout_->addLayout(startInTrayOptionLayout_);
mainLayout_->addWidget(new HorizontalLine(this));
mainLayout_->addLayout(groupViewLayout);
+ mainLayout_->addLayout(avatarViewLayout);
mainLayout_->addWidget(new HorizontalLine(this));
mainLayout_->addLayout(typingLayout);
mainLayout_->addLayout(receiptsLayout);
@@ -446,6 +463,10 @@ UserSettingsPage::UserSettingsPage(QSharedPointer<UserSettings> settings, QWidge
settings_->setGroupView(!isDisabled);
});
+ connect(avatarCircles_, &Toggle::toggled, this, [this](bool isDisabled) {
+ settings_->setAvatarCircles(!isDisabled);
+ });
+
connect(typingNotifications_, &Toggle::toggled, this, [this](bool isDisabled) {
settings_->setTypingNotifications(!isDisabled);
});
diff --git a/src/UserSettingsPage.h b/src/UserSettingsPage.h
index ffff1e20..28236e83 100644
--- a/src/UserSettingsPage.h
+++ b/src/UserSettingsPage.h
@@ -22,6 +22,7 @@
#include <QFrame>
#include <QLabel>
#include <QLayout>
+#include <QProcessEnvironment>
#include <QSharedPointer>
#include <QWidget>
@@ -85,7 +86,13 @@ public:
save();
}
- QString theme() const { return !theme_.isEmpty() ? theme_ : "light"; }
+ void setAvatarCircles(bool state)
+ {
+ avatarCircles_ = state;
+ save();
+ }
+
+ QString theme() const { return !theme_.isEmpty() ? theme_ : defaultTheme_; }
bool isTrayEnabled() const { return isTrayEnabled_; }
bool isStartInTrayEnabled() const { return isStartInTrayEnabled_; }
bool isGroupViewEnabled() const { return isGroupViewEnabled_; }
@@ -100,6 +107,11 @@ signals:
void groupViewStateChanged(bool state);
private:
+ // Default to system theme if QT_QPA_PLATFORMTHEME var is set.
+ QString defaultTheme_ =
+ QProcessEnvironment::systemEnvironment().value("QT_QPA_PLATFORMTHEME", "").isEmpty()
+ ? "light"
+ : "system";
QString theme_;
bool isTrayEnabled_;
bool isStartInTrayEnabled_;
@@ -107,6 +119,7 @@ private:
bool isTypingNotificationsEnabled_;
bool isReadReceiptsEnabled_;
bool hasDesktopNotifications_;
+ bool avatarCircles_;
double baseFontSize_;
QString font_;
QString emojiFont_;
@@ -156,6 +169,7 @@ private:
Toggle *typingNotifications_;
Toggle *readReceipts_;
Toggle *desktopNotifications_;
+ Toggle *avatarCircles_;
QLabel *deviceFingerprintValue_;
QLabel *deviceIdValue_;
diff --git a/src/Utils.cpp b/src/Utils.cpp
index d6b092b1..8c02b1c2 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -4,6 +4,7 @@
#include <QComboBox>
#include <QDesktopWidget>
#include <QGuiApplication>
+#include <QProcessEnvironment>
#include <QScreen>
#include <QSettings>
#include <QTextDocument>
@@ -99,13 +100,13 @@ utils::descriptiveTime(const QDateTime &then)
const auto days = then.daysTo(now);
if (days == 0)
- return then.toString("HH:mm");
+ return then.time().toString(Qt::DefaultLocaleShortDate);
else if (days < 2)
- return QString("Yesterday");
- else if (days < 365)
- return then.toString("dd/MM");
+ return QString(QCoreApplication::translate("descriptiveTime", "Yesterday"));
+ else if (days < 7)
+ return then.toString("dddd");
- return then.toString("dd/MM/yy");
+ return then.date().toString(Qt::DefaultLocaleShortDate);
}
DescInfo
@@ -147,7 +148,7 @@ utils::getMessageDescription(const TimelineEvent &event,
DescInfo info;
if (sender == localUser)
- info.username = "You";
+ info.username = QCoreApplication::translate("utils", "You");
else
info.username = username;
@@ -323,10 +324,25 @@ utils::linkifyMessage(const QString &body)
return doc;
}
+QByteArray escapeRawHtml(const QByteArray &data) {
+ QByteArray buffer;
+ const size_t length = data.size();
+ buffer.reserve(length);
+ for(size_t pos = 0; pos != length; ++pos) {
+ switch(data.at(pos)) {
+ case '&': buffer.append("&"); break;
+ case '<': buffer.append("<"); break;
+ case '>': buffer.append(">"); break;
+ default: buffer.append(data.at(pos)); break;
+ }
+ }
+ return buffer;
+}
+
QString
utils::markdownToHtml(const QString &text)
{
- const auto str = text.toUtf8();
+ const auto str = escapeRawHtml(text.toUtf8());
const char *tmp_buf =
cmark_markdown_to_html(str.constData(), str.size(), CMARK_OPT_DEFAULT);
@@ -366,16 +382,16 @@ utils::getQuoteBody(const RelatedInfo &related)
return markdownToHtml(related.quoted_body);
}
case MsgType::File: {
- return QString("sent a file.");
+ return QString(QCoreApplication::translate("utils", "sent a file."));
}
case MsgType::Image: {
- return QString("sent an image.");
+ return QString(QCoreApplication::translate("utils", "sent an image."));
}
case MsgType::Audio: {
- return QString("sent an audio file.");
+ return QString(QCoreApplication::translate("utils", "sent an audio file."));
}
case MsgType::Video: {
- return QString("sent a video");
+ return QString(QCoreApplication::translate("utils", "sent a video"));
}
default: {
return related.quoted_body;
@@ -387,14 +403,20 @@ QString
utils::linkColor()
{
QSettings settings;
- const auto theme = settings.value("user/theme", "light").toString();
+ // Default to system theme if QT_QPA_PLATFORMTHEME var is set.
+ QString defaultTheme =
+ QProcessEnvironment::systemEnvironment().value("QT_QPA_PLATFORMTHEME", "").isEmpty()
+ ? "light"
+ : "system";
+ const auto theme = settings.value("user/theme", defaultTheme).toString();
- if (theme == "light")
+ if (theme == "light") {
return "#0077b5";
- else if (theme == "dark")
+ } else if (theme == "dark") {
return "#38A3D8";
-
- return QPalette().color(QPalette::Link).name();
+ } else {
+ return QPalette().color(QPalette::Link).name();
+ }
}
uint32_t
diff --git a/src/Utils.h b/src/Utils.h
index 756dc1e3..225754be 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -9,6 +9,7 @@
#include "timeline/widgets/ImageItem.h"
#include "timeline/widgets/VideoItem.h"
+#include <QCoreApplication>
#include <QDateTime>
#include <QPixmap>
#include <mtx/events/collections.hpp>
@@ -79,7 +80,9 @@ event_body(const mtx::events::collections::TimelineEvents &event);
//! Match widgets/events with a description message.
template<class T>
QString
-messageDescription(const QString &username = "", const QString &body = "")
+messageDescription(const QString &username = "",
+ const QString &body = "",
+ const bool isLocal = false)
{
using Audio = mtx::events::RoomEvent<mtx::events::msg::Audio>;
using Emote = mtx::events::RoomEvent<mtx::events::msg::Emote>;
@@ -91,24 +94,41 @@ messageDescription(const QString &username = "", const QString &body = "")
using Video = mtx::events::RoomEvent<mtx::events::msg::Video>;
using Encrypted = mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>;
- if (std::is_same<T, AudioItem>::value || std::is_same<T, Audio>::value)
- return QString("sent an audio clip");
- else if (std::is_same<T, ImageItem>::value || std::is_same<T, Image>::value)
- return QString("sent an image");
- else if (std::is_same<T, FileItem>::value || std::is_same<T, File>::value)
- return QString("sent a file");
- else if (std::is_same<T, VideoItem>::value || std::is_same<T, Video>::value)
- return QString("sent a video clip");
- else if (std::is_same<T, StickerItem>::value || std::is_same<T, Sticker>::value)
- return QString("sent a sticker");
- else if (std::is_same<T, Notice>::value)
- return QString("sent a notification");
- else if (std::is_same<T, Text>::value)
+ // Sometimes the verb form of sent changes in some languages depending on the actor.
+ auto remoteSent = QCoreApplication::translate(
+ "message-description: ", "sent", "For when you are the sender");
+ auto localSent = QCoreApplication::translate(
+ "message-description:", "sent", "For when someone else is the sender");
+ QString sentVerb = isLocal ? localSent : remoteSent;
+ if (std::is_same<T, AudioItem>::value || std::is_same<T, Audio>::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 an audio clip")
+ .arg(sentVerb);
+ } else if (std::is_same<T, ImageItem>::value || std::is_same<T, Image>::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 an image")
+ .arg(sentVerb);
+ } else if (std::is_same<T, FileItem>::value || std::is_same<T, File>::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 a file")
+ .arg(sentVerb);
+ } else if (std::is_same<T, VideoItem>::value || std::is_same<T, Video>::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 a video clip")
+ .arg(sentVerb);
+ } else if (std::is_same<T, StickerItem>::value || std::is_same<T, Sticker>::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 a sticker")
+ .arg(sentVerb);
+ } else if (std::is_same<T, Notice>::value) {
+ return QCoreApplication::translate("message-description sent:", "%1 a notification")
+ .arg(sentVerb);
+ } else if (std::is_same<T, Text>::value) {
return QString(": %1").arg(body);
- else if (std::is_same<T, Emote>::value)
+ } else if (std::is_same<T, Emote>::value) {
return QString("* %1 %2").arg(username).arg(body);
- else if (std::is_same<T, Encrypted>::value)
- return QString("sent an encrypted message");
+ } else if (std::is_same<T, Encrypted>::value) {
+ return QCoreApplication::translate("message-description sent:",
+ "%1 an encrypted message")
+ .arg(sentVerb);
+ } else {
+ return QCoreApplication::translate("utils", "Unknown Message Type");
+ }
}
template<class T, class Event>
@@ -129,10 +149,12 @@ createDescriptionInfo(const Event &event, const QString &localUser, const QStrin
return DescInfo{
QString::fromStdString(msg.event_id),
- isEmote ? "" : (sender == localUser ? "You" : username),
+ isEmote ? ""
+ : (sender == localUser ? QCoreApplication::translate("utils", "You") : username),
sender,
(isText || isEmote)
- ? messageDescription<T>(username, QString::fromStdString(msg.content.body).trimmed())
+ ? messageDescription<T>(
+ username, QString::fromStdString(msg.content.body).trimmed(), sender == localUser)
: QString(" %1").arg(messageDescription<T>()),
utils::descriptiveTime(ts),
ts};
diff --git a/src/dialogs/MemberList.cpp b/src/dialogs/MemberList.cpp
index f4167143..9e973efa 100644
--- a/src/dialogs/MemberList.cpp
+++ b/src/dialogs/MemberList.cpp
@@ -9,7 +9,6 @@
#include "dialogs/MemberList.h"
-#include "AvatarProvider.h"
#include "Cache.h"
#include "ChatPage.h"
#include "Config.h"
@@ -28,17 +27,10 @@ MemberItem::MemberItem(const RoomMember &member, QWidget *parent)
textLayout_->setMargin(0);
textLayout_->setSpacing(0);
- avatar_ = new Avatar(this);
- avatar_->setSize(44);
+ avatar_ = new Avatar(this, 44);
avatar_->setLetter(utils::firstChar(member.display_name));
- if (!member.avatar.isNull())
- avatar_->setImage(member.avatar);
- else
- AvatarProvider::resolve(ChatPage::instance()->currentRoom(),
- member.user_id,
- this,
- [this](const QImage &img) { avatar_->setImage(img); });
+ avatar_->setImage(ChatPage::instance()->currentRoom(), member.user_id);
QFont nameFont;
nameFont.setPointSizeF(nameFont.pointSizeF() * 1.1);
@@ -97,7 +89,7 @@ MemberList::MemberList(const QString &room_id, QWidget *parent)
topLabel_->setAlignment(Qt::AlignCenter);
topLabel_->setFont(font);
- auto okBtn = new QPushButton("OK", this);
+ auto okBtn = new QPushButton(tr("OK"), this);
auto buttonLayout = new QHBoxLayout();
buttonLayout->setSpacing(15);
@@ -126,7 +118,7 @@ MemberList::MemberList(const QString &room_id, QWidget *parent)
qCritical() << e.what();
}
- auto closeShortcut = new QShortcut(QKeySequence(tr("ESC")), this);
+ auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
connect(closeShortcut, &QShortcut::activated, this, &MemberList::close);
connect(okBtn, &QPushButton::clicked, this, &MemberList::close);
}
diff --git a/src/dialogs/ReadReceipts.cpp b/src/dialogs/ReadReceipts.cpp
index dc4145db..58ad59c3 100644
--- a/src/dialogs/ReadReceipts.cpp
+++ b/src/dialogs/ReadReceipts.cpp
@@ -37,8 +37,7 @@ ReceiptItem::ReceiptItem(QWidget *parent,
auto displayName = Cache::displayName(room_id, user_id);
- avatar_ = new Avatar(this);
- avatar_->setSize(44);
+ avatar_ = new Avatar(this, 44);
avatar_->setLetter(utils::firstChar(displayName));
// If it's a matrix id we use the second letter.
@@ -56,10 +55,7 @@ ReceiptItem::ReceiptItem(QWidget *parent,
topLayout_->addWidget(avatar_);
topLayout_->addLayout(textLayout_, 1);
- AvatarProvider::resolve(ChatPage::instance()->currentRoom(),
- user_id,
- this,
- [this](const QImage &img) { avatar_->setImage(img); });
+ avatar_->setImage(ChatPage::instance()->currentRoom(), user_id);
}
void
@@ -78,13 +74,15 @@ ReceiptItem::dateFormat(const QDateTime &then) const
auto days = then.daysTo(now);
if (days == 0)
- return QString("Today %1").arg(then.toString("HH:mm"));
+ return tr("Today %1").arg(then.time().toString(Qt::DefaultLocaleShortDate));
else if (days < 2)
- return QString("Yesterday %1").arg(then.toString("HH:mm"));
- else if (days < 365)
- return then.toString("dd/MM HH:mm");
+ return tr("Yesterday %1").arg(then.time().toString(Qt::DefaultLocaleShortDate));
+ else if (days < 7)
+ return QString("%1 %2")
+ .arg(then.toString("dddd"))
+ .arg(then.time().toString(Qt::DefaultLocaleShortDate));
- return then.toString("dd/MM/yy");
+ return then.toString(Qt::DefaultLocaleShortDate);
}
ReadReceipts::ReadReceipts(QWidget *parent)
@@ -131,7 +129,7 @@ ReadReceipts::ReadReceipts(QWidget *parent)
layout->addWidget(userList_);
layout->addLayout(buttonLayout);
- auto closeShortcut = new QShortcut(QKeySequence(tr("ESC")), this);
+ auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
connect(closeShortcut, &QShortcut::activated, this, &ReadReceipts::close);
connect(okBtn, &QPushButton::clicked, this, &ReadReceipts::close);
}
diff --git a/src/dialogs/RoomSettings.cpp b/src/dialogs/RoomSettings.cpp
index b2344f23..00b034cc 100644
--- a/src/dialogs/RoomSettings.cpp
+++ b/src/dialogs/RoomSettings.cpp
@@ -350,12 +350,12 @@ RoomSettings::RoomSettings(const QString &room_id, QWidget *parent)
keyRequestsToggle_->hide();
}
- avatar_ = new Avatar(this);
- avatar_->setSize(128);
+ avatar_ = new Avatar(this, 128);
if (avatarImg_.isNull())
avatar_->setLetter(utils::firstChar(QString::fromStdString(info_.name)));
else
- avatar_->setImage(avatarImg_);
+ avatar_->setImage(room_id_,
+ QString::fromStdString(http::client()->user_id().to_string()));
if (canChangeAvatar(room_id_.toStdString(), utils::localUser().toStdString())) {
auto filter = new ClickableFilter(this);
@@ -438,7 +438,7 @@ RoomSettings::RoomSettings(const QString &room_id, QWidget *parent)
resetErrorLabel();
});
- auto closeShortcut = new QShortcut(QKeySequence(tr("ESC")), this);
+ auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
connect(closeShortcut, &QShortcut::activated, this, &RoomSettings::close);
connect(okBtn, &QPushButton::clicked, this, &RoomSettings::close);
}
@@ -487,7 +487,7 @@ RoomSettings::retrieveRoomInfo()
try {
usesEncryption_ = cache::client()->isRoomEncrypted(room_id_.toStdString());
info_ = cache::client()->singleRoomInfo(room_id_.toStdString());
- setAvatar(QImage::fromData(cache::client()->image(info_.avatar_url)));
+ setAvatar();
} catch (const lmdb::error &e) {
nhlog::db()->warn("failed to retrieve room info from cache: {}",
room_id_.toStdString());
@@ -633,14 +633,13 @@ RoomSettings::displayErrorMessage(const QString &msg)
}
void
-RoomSettings::setAvatar(const QImage &img)
+RoomSettings::setAvatar()
{
stopLoadingSpinner();
- avatarImg_ = img;
-
if (avatar_)
- avatar_->setImage(img);
+ avatar_->setImage(room_id_,
+ QString::fromStdString(http::client()->user_id().to_string()));
}
void
@@ -668,12 +667,12 @@ RoomSettings::updateAvatar()
QFile file{fileName, this};
if (format != "image") {
- displayErrorMessage(tr("The selected media is not an image"));
+ displayErrorMessage(tr("The selected file is not an image"));
return;
}
if (!file.open(QIODevice::ReadOnly)) {
- displayErrorMessage(tr("Error while reading media: %1").arg(file.errorString()));
+ displayErrorMessage(tr("Error while reading file: %1").arg(file.errorString()));
return;
}
@@ -733,7 +732,7 @@ RoomSettings::updateAvatar()
return;
}
- emit proxy->avatarChanged(QImage::fromData(content));
+ emit proxy->avatarChanged();
});
});
}
diff --git a/src/dialogs/RoomSettings.h b/src/dialogs/RoomSettings.h
index 6667b68b..e1807ba1 100644
--- a/src/dialogs/RoomSettings.h
+++ b/src/dialogs/RoomSettings.h
@@ -52,7 +52,7 @@ class ThreadProxy : public QObject
signals:
void error(const QString &msg);
- void avatarChanged(const QImage &img);
+ void avatarChanged();
void nameEventSent(const QString &);
void topicEventSent();
};
@@ -140,7 +140,7 @@ private:
void resetErrorLabel();
void displayErrorMessage(const QString &msg);
- void setAvatar(const QImage &img);
+ void setAvatar();
void setupEditButton();
//! Retrieve the current room information from cache.
void retrieveRoomInfo();
diff --git a/src/dialogs/UserMentions.cpp b/src/dialogs/UserMentions.cpp
deleted file mode 100644
index 8f56ec93..00000000
--- a/src/dialogs/UserMentions.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-#include <QTimer>
-
-#include "UserMentions.h"
-#include "timeline/TimelineItem.h"
-
-using namespace dialogs;
-
-UserMentions::UserMentions(QWidget *parent)
- : QWidget{parent}
-{
- top_layout_ = new QVBoxLayout(this);
- top_layout_->setSpacing(0);
- top_layout_->setMargin(0);
-
- scroll_area_ = new QScrollArea(this);
- scroll_area_->setWidgetResizable(true);
- scroll_area_->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
-
- scroll_widget_ = new QWidget(this);
- scroll_widget_->setObjectName("scroll_widget");
-
- // Height of the typing display.
- QFont f;
- f.setPointSizeF(f.pointSizeF() * 0.9);
- const int bottomMargin = QFontMetrics(f).height() + 6;
-
- scroll_layout_ = new QVBoxLayout(scroll_widget_);
- scroll_layout_->setContentsMargins(4, 0, 15, bottomMargin);
- scroll_layout_->setSpacing(0);
- scroll_layout_->setObjectName("timelinescrollarea");
-
- scroll_area_->setWidget(scroll_widget_);
- scroll_area_->setAlignment(Qt::AlignBottom);
-
- top_layout_->addWidget(scroll_area_);
-
- setLayout(top_layout_);
-}
-
-void
-UserMentions::pushItem(const QString &event_id,
- const QString &user_id,
- const QString &body,
- const QString &room_id)
-{
- TimelineItem *view_item = new TimelineItem(
- mtx::events::MessageType::Text, user_id, body, true, room_id, scroll_widget_);
- view_item->setEventId(event_id);
- setUpdatesEnabled(false);
- view_item->hide();
-
- scroll_layout_->addWidget(view_item);
- QTimer::singleShot(0, this, [view_item, this]() {
- view_item->show();
- view_item->adjustSize();
- setUpdatesEnabled(true);
- });
-}
\ No newline at end of file
diff --git a/src/dialogs/UserMentions.h b/src/dialogs/UserMentions.h
deleted file mode 100644
index e995b207..00000000
--- a/src/dialogs/UserMentions.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-#include <QScrollArea>
-#include <QScrollBar>
-#include <QVBoxLayout>
-#include <QWidget>
-
-namespace dialogs {
-
-class UserMentions : public QWidget
-{
- Q_OBJECT
-public:
- UserMentions(QWidget *parent = nullptr);
- void pushItem(const QString &event_id,
- const QString &user_id,
- const QString &body,
- const QString &room_id);
-
-private:
- QVBoxLayout *top_layout_;
- QVBoxLayout *scroll_layout_;
-
- QScrollArea *scroll_area_;
- QWidget *scroll_widget_;
-};
-
-}
\ No newline at end of file
diff --git a/src/dialogs/UserProfile.cpp b/src/dialogs/UserProfile.cpp
index b8040f9f..5ad3afa2 100644
--- a/src/dialogs/UserProfile.cpp
+++ b/src/dialogs/UserProfile.cpp
@@ -114,9 +114,8 @@ UserProfile::UserProfile(QWidget *parent)
btnLayout->setSpacing(8);
btnLayout->setMargin(0);
- avatar_ = new Avatar(this);
+ avatar_ = new Avatar(this, 128);
avatar_->setLetter("X");
- avatar_->setSize(128);
QFont font;
font.setPointSizeF(font.pointSizeF() * 2);
@@ -183,7 +182,7 @@ UserProfile::UserProfile(QWidget *parent)
qRegisterMetaType<std::vector<DeviceInfo>>();
- auto closeShortcut = new QShortcut(QKeySequence(tr("ESC")), this);
+ auto closeShortcut = new QShortcut(QKeySequence(QKeySequence::Cancel), this);
connect(closeShortcut, &QShortcut::activated, this, &UserProfile::close);
connect(okBtn, &QPushButton::clicked, this, &UserProfile::close);
}
@@ -210,8 +209,7 @@ UserProfile::init(const QString &userId, const QString &roomId)
displayNameLabel_->setText(displayName);
avatar_->setLetter(utils::firstChar(displayName));
- AvatarProvider::resolve(
- roomId, userId, this, [this](const QImage &img) { avatar_->setImage(img); });
+ avatar_->setImage(roomId, userId);
auto localUser = utils::localUser();
diff --git a/src/popups/PopupItem.cpp b/src/popups/PopupItem.cpp
index f905983a..c4d4327f 100644
--- a/src/popups/PopupItem.cpp
+++ b/src/popups/PopupItem.cpp
@@ -11,7 +11,7 @@ constexpr int PopupItemMargin = 3;
PopupItem::PopupItem(QWidget *parent)
: QWidget(parent)
- , avatar_{new Avatar(this)}
+ , avatar_{new Avatar(this, conf::popup::avatar)}
, hovering_{false}
{
setMouseTracking(true);
@@ -40,7 +40,6 @@ UserItem::UserItem(QWidget *parent)
: PopupItem(parent)
{
userName_ = new QLabel("Placeholder", this);
- avatar_->setSize(conf::popup::avatar);
avatar_->setLetter("P");
topLayout_->addWidget(avatar_);
topLayout_->addWidget(userName_, 1);
@@ -52,7 +51,6 @@ UserItem::UserItem(QWidget *parent, const QString &user_id)
{
auto displayName = Cache::displayName(ChatPage::instance()->currentRoom(), userId_);
- avatar_->setSize(conf::popup::avatar);
avatar_->setLetter(utils::firstChar(displayName));
// If it's a matrix id we use the second letter.
@@ -87,16 +85,7 @@ UserItem::updateItem(const QString &user_id)
void
UserItem::resolveAvatar(const QString &user_id)
{
- AvatarProvider::resolve(
- ChatPage::instance()->currentRoom(), userId_, this, [this, user_id](const QImage &img) {
- // The user on the widget when the avatar is resolved,
- // might be different from the user that made the call.
- if (user_id == userId_)
- avatar_->setImage(img);
- else
- // We try to resolve the avatar again.
- resolveAvatar(userId_);
- });
+ avatar_->setImage(ChatPage::instance()->currentRoom(), user_id);
}
void
@@ -116,7 +105,6 @@ RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res)
auto name = QFontMetrics(QFont()).elidedText(
QString::fromStdString(res.info.name), Qt::ElideRight, parentWidget()->width() - 10);
- avatar_->setSize(conf::popup::avatar + 6);
avatar_->setLetter(utils::firstChar(name));
roomName_ = new QLabel(name, this);
@@ -125,8 +113,7 @@ RoomItem::RoomItem(QWidget *parent, const RoomSearchResult &res)
topLayout_->addWidget(avatar_);
topLayout_->addWidget(roomName_, 1);
- if (!res.img.isNull())
- avatar_->setImage(res.img);
+ avatar_->setImage(QString::fromStdString(res.info.avatar_url));
}
void
@@ -141,10 +128,7 @@ RoomItem::updateItem(const RoomSearchResult &result)
roomName_->setText(name);
- if (!result.img.isNull())
- avatar_->setImage(result.img);
- else
- avatar_->setLetter(utils::firstChar(name));
+ avatar_->setImage(QString::fromStdString(result.info.avatar_url));
}
void
@@ -154,4 +138,4 @@ RoomItem::mousePressEvent(QMouseEvent *event)
emit clicked(selectedText());
QWidget::mousePressEvent(event);
-}
\ No newline at end of file
+}
diff --git a/src/popups/UserMentions.cpp b/src/popups/UserMentions.cpp
new file mode 100644
index 00000000..3480959a
--- /dev/null
+++ b/src/popups/UserMentions.cpp
@@ -0,0 +1,161 @@
+#include <QPainter>
+#include <QStyleOption>
+#include <QTabWidget>
+#include <QTimer>
+
+#include "Cache.h"
+#include "ChatPage.h"
+#include "Logging.h"
+#include "UserMentions.h"
+#include "timeline/TimelineItem.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 notif : item.notifications) {
+ const auto event_id = QString::fromStdString(utils::event_id(notif.event));
+
+ try {
+ const auto room_id = QString::fromStdString(notif.room_id);
+ const auto user_id = utils::event_sender(notif.event);
+ const auto body = utils::event_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::client()->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)
+{
+ 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);
+}
\ No newline at end of file
diff --git a/src/popups/UserMentions.h b/src/popups/UserMentions.h
new file mode 100644
index 00000000..d7dfc575
--- /dev/null
+++ b/src/popups/UserMentions.h
@@ -0,0 +1,51 @@
+#pragma once
+
+#include <mtx/responses.hpp>
+
+#include <QApplication>
+#include <QEvent>
+#include <QMap>
+#include <QPaintEvent>
+#include <QScrollArea>
+#include <QScrollBar>
+#include <QString>
+#include <QTabWidget>
+#include <QVBoxLayout>
+#include <QWidget>
+#include <Qt>
+
+#include "Logging.h"
+
+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_;
+};
+
+}
\ No newline at end of file
diff --git a/src/timeline/.TimelineItem.cpp.swp b/src/timeline/.TimelineItem.cpp.swp
new file mode 100644
index 00000000..75e03aeb
--- /dev/null
+++ b/src/timeline/.TimelineItem.cpp.swp
Binary files differdiff --git a/src/timeline/TimelineItem.cpp b/src/timeline/TimelineItem.cpp
index e52dce7b..7916bd80 100644
--- a/src/timeline/TimelineItem.cpp
+++ b/src/timeline/TimelineItem.cpp
@@ -282,6 +282,7 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(ty)
, room_id_{room_id}
{
init();
@@ -325,8 +326,7 @@ TimelineItem::TimelineItem(mtx::events::MessageType ty,
generateBody(userid, displayName, formatted_body);
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(userid);
} else {
generateBody(formatted_body);
setupSimpleLayout();
@@ -341,6 +341,7 @@ TimelineItem::TimelineItem(ImageItem *image,
const QString &room_id,
QWidget *parent)
: QWidget{parent}
+ , message_type_(mtx::events::MessageType::Image)
, room_id_{room_id}
{
init();
@@ -356,6 +357,7 @@ TimelineItem::TimelineItem(FileItem *file,
const QString &room_id,
QWidget *parent)
: QWidget{parent}
+ , message_type_(mtx::events::MessageType::File)
, room_id_{room_id}
{
init();
@@ -369,6 +371,7 @@ TimelineItem::TimelineItem(AudioItem *audio,
const QString &room_id,
QWidget *parent)
: QWidget{parent}
+ , message_type_(mtx::events::MessageType::Audio)
, room_id_{room_id}
{
init();
@@ -382,6 +385,7 @@ TimelineItem::TimelineItem(VideoItem *video,
const QString &room_id,
QWidget *parent)
: QWidget{parent}
+ , message_type_(mtx::events::MessageType::Video)
, room_id_{room_id}
{
init();
@@ -395,6 +399,7 @@ TimelineItem::TimelineItem(ImageItem *image,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Image)
, room_id_{room_id}
{
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Image>, ImageItem>(
@@ -426,6 +431,7 @@ TimelineItem::TimelineItem(FileItem *file,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::File)
, room_id_{room_id}
{
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::File>, FileItem>(
@@ -440,6 +446,7 @@ TimelineItem::TimelineItem(AudioItem *audio,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Audio)
, room_id_{room_id}
{
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Audio>, AudioItem>(
@@ -454,6 +461,7 @@ TimelineItem::TimelineItem(VideoItem *video,
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Video)
, room_id_{room_id}
{
setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Video>, VideoItem>(
@@ -470,6 +478,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Notice)
, room_id_{room_id}
{
init();
@@ -499,8 +508,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice
generateBody(sender, displayName, formatted_body);
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(sender);
} else {
generateBody(formatted_body);
setupSimpleLayout();
@@ -517,6 +525,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote>
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Emote)
, room_id_{room_id}
{
init();
@@ -547,8 +556,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote>
generateBody(sender, displayName, formatted_body);
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(sender);
} else {
generateBody(formatted_body);
setupSimpleLayout();
@@ -565,6 +573,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
const QString &room_id,
QWidget *parent)
: QWidget(parent)
+ , message_type_(mtx::events::MessageType::Text)
, room_id_{room_id}
{
init();
@@ -595,8 +604,7 @@ TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text>
generateBody(sender, displayName, formatted_body);
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(sender);
} else {
generateBody(formatted_body);
setupSimpleLayout();
@@ -781,9 +789,8 @@ TimelineItem::setupAvatarLayout(const QString &userName)
QFont f;
f.setPointSizeF(f.pointSizeF());
- userAvatar_ = new Avatar(this);
+ userAvatar_ = new Avatar(this, QFontMetrics(f).height() * 2);
userAvatar_->setLetter(QChar(userName[0]).toUpper());
- userAvatar_->setSize(QFontMetrics(f).height() * 2);
// TODO: The provided user name should be a UserId class
if (userName[0] == '@' && userName.size() > 1)
@@ -810,12 +817,12 @@ TimelineItem::setupSimpleLayout()
}
void
-TimelineItem::setUserAvatar(const QImage &avatar)
+TimelineItem::setUserAvatar(const QString &userid)
{
if (userAvatar_ == nullptr)
return;
- userAvatar_->setImage(avatar);
+ userAvatar_->setImage(room_id_, userid);
}
void
@@ -899,8 +906,7 @@ TimelineItem::addAvatar()
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(userid);
}
void
@@ -951,4 +957,4 @@ TimelineItem::openRawMessageViewer() const
"failed to serialize event ({}, {})", room_id, event_id);
}
});
-}
\ No newline at end of file
+}
diff --git a/src/timeline/TimelineItem.h b/src/timeline/TimelineItem.h
index c0dab6b8..356976e5 100644
--- a/src/timeline/TimelineItem.h
+++ b/src/timeline/TimelineItem.h
@@ -215,7 +215,7 @@ public:
void setBackgroundColor(const QColor &color) { backgroundColor_ = color; }
QColor backgroundColor() const { return backgroundColor_; }
- void setUserAvatar(const QImage &pixmap);
+ void setUserAvatar(const QString &userid);
DescInfo descriptionMessage() const { return descriptionMsg_; }
QString eventId() const { return event_id_; }
void setEventId(const QString &event_id) { event_id_ = event_id; }
@@ -277,7 +277,7 @@ private:
QFutureWatcher<QString> *colorGenerating_;
QString event_id_;
- mtx::events::MessageType message_type_;
+ mtx::events::MessageType message_type_ = mtx::events::MessageType::Unknown;
QString room_id_;
DescInfo descriptionMsg_;
@@ -336,8 +336,7 @@ TimelineItem::setupLocalWidgetLayout(Widget *widget, const QString &userid, bool
generateBody(userid, displayName, "");
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(userid);
} else {
setupSimpleLayout();
}
@@ -381,8 +380,7 @@ TimelineItem::setupWidgetLayout(Widget *widget, const Event &event, bool withSen
generateBody(sender, displayName, "");
setupAvatarLayout(displayName);
- AvatarProvider::resolve(
- room_id_, sender, this, [this](const QImage &img) { setUserAvatar(img); });
+ setUserAvatar(sender);
} else {
setupSimpleLayout();
}
diff --git a/src/timeline/TimelineView.cpp b/src/timeline/TimelineView.cpp
index 18b73eb0..ed783e90 100644
--- a/src/timeline/TimelineView.cpp
+++ b/src/timeline/TimelineView.cpp
@@ -306,7 +306,10 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEvent<mtx::events:
dummy.origin_server_ts = e.origin_server_ts;
dummy.event_id = e.event_id;
dummy.sender = e.sender;
- dummy.content.body = "-- Encrypted Event (No keys found for decryption) --";
+ dummy.content.body =
+ tr("-- Encrypted Event (No keys found for decryption) --",
+ "Placeholder, when the message was not decrypted yet or can't be decrypted")
+ .toStdString();
try {
if (!cache::client()->inboundMegolmSessionExists(index)) {
@@ -319,7 +322,10 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEvent<mtx::events:
}
} catch (const lmdb::error &e) {
nhlog::db()->critical("failed to check megolm session's existence: {}", e.what());
- dummy.content.body = "-- Decryption Error (failed to communicate with DB) --";
+ dummy.content.body = tr("-- Decryption Error (failed to communicate with DB) --",
+ "Placeholder, when the message can't be decrypted, because "
+ "the DB access failed when trying to lookup the session.")
+ .toStdString();
return {dummy, false};
}
@@ -335,7 +341,10 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEvent<mtx::events:
index.sender_key,
e.what());
dummy.content.body =
- "-- Decryption Error (failed to retrieve megolm keys from db) --";
+ tr("-- Decryption Error (failed to retrieve megolm keys from db) --",
+ "Placeholder, when the message can't be decrypted, because the DB access "
+ "failed.")
+ .toStdString();
return {dummy, false};
} catch (const mtx::crypto::olm_exception &e) {
nhlog::crypto()->critical("failed to decrypt message with index ({}, {}, {}): {}",
@@ -343,7 +352,12 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEvent<mtx::events:
index.session_id,
index.sender_key,
e.what());
- dummy.content.body = "-- Decryption Error (" + std::string(e.what()) + ") --";
+ dummy.content.body =
+ tr("-- Decryption Error (%1) --",
+ "Placeholder, when the message can't be decrypted. In this case, the Olm "
+ "decrytion returned an error, which is passed ad %1")
+ .arg(e.what())
+ .toStdString();
return {dummy, false};
}
@@ -365,7 +379,11 @@ TimelineView::parseEncryptedEvent(const mtx::events::EncryptedEvent<mtx::events:
if (events.size() == 1)
return {events.at(0), true};
- dummy.content.body = "-- Encrypted Event (Unknown event type) --";
+ dummy.content.body =
+ tr("-- Encrypted Event (Unknown event type) --",
+ "Placeholder, when the message was decrypted, but we couldn't parse it, because "
+ "Nheko/mtxclient don't support that event type yet")
+ .toStdString();
return {dummy, false};
}
diff --git a/src/ui/Avatar.cpp b/src/ui/Avatar.cpp
index 4b4cd272..501a8968 100644
--- a/src/ui/Avatar.cpp
+++ b/src/ui/Avatar.cpp
@@ -1,12 +1,14 @@
#include <QPainter>
+#include <QSettings>
+#include "AvatarProvider.h"
#include "Utils.h"
#include "ui/Avatar.h"
-Avatar::Avatar(QWidget *parent)
+Avatar::Avatar(QWidget *parent, int size)
: QWidget(parent)
+ , size_(size)
{
- size_ = ui::AvatarSize;
type_ = ui::AvatarType::Letter;
letter_ = "A";
@@ -61,35 +63,31 @@ Avatar::setBackgroundColor(const QColor &color)
}
void
-Avatar::setSize(int size)
+Avatar::setLetter(const QString &letter)
{
- size_ = size;
-
- if (!image_.isNull())
- pixmap_ = utils::scaleImageToPixmap(image_, size_);
-
- QFont _font(font());
- _font.setPointSizeF(size_ * (ui::FontSize) / 40);
-
- setFont(_font);
+ letter_ = letter;
+ type_ = ui::AvatarType::Letter;
update();
}
void
-Avatar::setLetter(const QString &letter)
+Avatar::setImage(const QString &avatar_url)
{
- letter_ = letter;
- type_ = ui::AvatarType::Letter;
- update();
+ AvatarProvider::resolve(avatar_url, size_, this, [this](QPixmap pm) {
+ type_ = ui::AvatarType::Image;
+ pixmap_ = pm;
+ update();
+ });
}
void
-Avatar::setImage(const QImage &image)
+Avatar::setImage(const QString &room, const QString &user)
{
- image_ = image;
- type_ = ui::AvatarType::Image;
- pixmap_ = utils::scaleImageToPixmap(image_, size_);
- update();
+ AvatarProvider::resolve(room, user, size_, this, [this](QPixmap pm) {
+ type_ = ui::AvatarType::Image;
+ pixmap_ = pm;
+ update();
+ });
}
void
@@ -103,6 +101,8 @@ Avatar::setIcon(const QIcon &icon)
void
Avatar::paintEvent(QPaintEvent *)
{
+ bool rounded = QSettings().value("user/avatar/circles", true).toBool();
+
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
@@ -116,7 +116,8 @@ Avatar::paintEvent(QPaintEvent *)
painter.setPen(Qt::NoPen);
painter.setBrush(brush);
- painter.drawEllipse(r.center(), hs, hs);
+ rounded ? painter.drawEllipse(r.center(), hs, hs)
+ : painter.drawRoundedRect(r, 3, 3);
}
switch (type_) {
@@ -129,7 +130,10 @@ Avatar::paintEvent(QPaintEvent *)
}
case ui::AvatarType::Image: {
QPainterPath ppath;
- ppath.addEllipse(width() / 2 - hs, height() / 2 - hs, size_, size_);
+
+ rounded ? ppath.addEllipse(width() / 2 - hs, height() / 2 - hs, size_, size_)
+ : ppath.addRoundedRect(r, 3, 3);
+
painter.setClipPath(ppath);
painter.drawPixmap(QRect(width() / 2 - hs, height() / 2 - hs, size_, size_),
pixmap_);
diff --git a/src/ui/Avatar.h b/src/ui/Avatar.h
index 41967af5..a643c8c4 100644
--- a/src/ui/Avatar.h
+++ b/src/ui/Avatar.h
@@ -15,13 +15,13 @@ class Avatar : public QWidget
Q_PROPERTY(QColor backgroundColor WRITE setBackgroundColor READ backgroundColor)
public:
- explicit Avatar(QWidget *parent = 0);
+ explicit Avatar(QWidget *parent = 0, int size = ui::AvatarSize);
void setBackgroundColor(const QColor &color);
void setIcon(const QIcon &icon);
- void setImage(const QImage &image);
+ void setImage(const QString &avatar_url);
+ void setImage(const QString &room, const QString &user);
void setLetter(const QString &letter);
- void setSize(int size);
void setTextColor(const QColor &color);
QColor backgroundColor() const;
@@ -41,7 +41,6 @@ private:
QColor background_color_;
QColor text_color_;
QIcon icon_;
- QImage image_;
QPixmap pixmap_;
int size_;
};
diff --git a/src/ui/InfoMessage.cpp b/src/ui/InfoMessage.cpp
index fa575d76..27bc0a5f 100644
--- a/src/ui/InfoMessage.cpp
+++ b/src/ui/InfoMessage.cpp
@@ -2,6 +2,7 @@
#include "Config.h"
#include <QDateTime>
+#include <QLocale>
#include <QPainter>
#include <QPen>
#include <QtGlobal>
@@ -61,14 +62,14 @@ DateSeparator::DateSeparator(QDateTime datetime, QWidget *parent)
{
auto now = QDateTime::currentDateTime();
- QString fmt;
+ QString fmt = QLocale::system().dateFormat(QLocale::LongFormat);
- if (now.date().year() != datetime.date().year())
- fmt = QString("ddd d MMMM yy");
- else
- fmt = QString("ddd d MMMM");
+ if (now.date().year() == datetime.date().year()) {
+ QRegularExpression rx("[^a-zA-Z]*y+[^a-zA-Z]*");
+ fmt = fmt.remove(rx);
+ }
- msg_ = datetime.toString(fmt);
+ msg_ = datetime.date().toString(fmt);
QFontMetrics fm{font()};
#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)
|