diff --git a/src/AvatarProvider.cpp b/src/AvatarProvider.cpp index 57b61c750139e2879b2c077dd4a1a1ac0b073a91..277a403059b06612eb6a2377a8270f6bfabf7fa2 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 4b4e15e9d228aef2ed214a4b8e3e0e486fef01b3..47ed028ee1c92cf1aa678c1eabe9a5fd047dbf8d 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 ef0f951e480532e8c29097d0b51c060fa5f31fde..083dbe8920c54cb1d6dec146b757cd41803da74e 100644 --- a/src/Cache.cpp +++ b/src/Cache.cpp @@ -1830,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(); diff --git a/src/Cache.h b/src/Cache.h index 302bb65b9d3e4d071d4cbd7b5327cf9a8fd60b4a..0da49793995bc5b3e4c600bef4a0e3c7b6a701bf 100644 --- a/src/Cache.h +++ b/src/Cache.h @@ -153,7 +153,6 @@ struct RoomSearchResult { std::string room_id; RoomInfo info; - QImage img; }; Q_DECLARE_METATYPE(RoomSearchResult) diff --git a/src/ChatPage.cpp b/src/ChatPage.cpp index 2ed64b6bb192c5a98ad11f475ebfef9d8ff26464..21ded4b3af4bccf43ead05cf19bd660a2a106618 100644 --- a/src/ChatPage.cpp +++ b/src/ChatPage.cpp @@ -774,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 @@ -807,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()); @@ -1337,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 189af387db1e9b70b9ce31e65da3057ba431fc87..e41ae1ae4ac97fb72b1dad96e9db12360335a2fd 100644 --- a/src/ChatPage.h +++ b/src/ChatPage.h @@ -129,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(); @@ -159,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); diff --git a/src/Logging.cpp b/src/Logging.cpp index 686274d8a828d564399b521aa8ebd31e1c6b6886..322875826f05af709140ec76288f4e5f7b1e3f4c 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 2feae60d75ba6d7d40aa3d60a6ef5e815bf36260..e54f3c3f3b861062858bac8118b6669ab4108372 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 9bcce1347e55e5ce884a3c445e72430c51244d4c..dbcd6806ffdcbc2131776de448f3caed30c61c23 100644 --- a/src/RoomInfoListItem.cpp +++ b/src/RoomInfoListItem.cpp @@ -21,6 +21,7 @@ #include <QPainter> #include <QtGlobal> +#include "AvatarProvider.h" #include "Cache.h" #include "Config.h" #include "RoomInfoListItem.h" @@ -434,10 +435,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 40c938c11e54f4204806c0a30e888e1734752474..54e02a7637ab410d366719588706ce6cbf93dc1b 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 1abf35336bf3b22317b09d9ec706b04047c1ce84..c5e056214335d76224a8b95ef45d401f9df44a02 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 155a969c981571d26fe2d13a3d26aefdec9f3a84..95fc0d9b4451a6ddb6a76046d7d72bba25c8888f 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 a8049e3ad7b353fad6e6d8e1ad2ff4c3f4d6a0d7..712fe9aa1921c7787e8efb4aaaa7d4917496f0bd 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 5f2c936ea8b1af636e920021047b029652230916..3243064e843b4afccd95fbed89ac051c26264bfe 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/UserInfoWidget.cpp b/src/UserInfoWidget.cpp index 5345fb2a319415332ea895b1f77b157be4c4e0ab..19d7478e1d04c20e62f0643aae2da7eec531aeee 100644 --- a/src/UserInfoWidget.cpp +++ b/src/UserInfoWidget.cpp @@ -52,10 +52,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); @@ -134,14 +133,6 @@ UserInfoWidget::reset() userAvatar_->setLetter(QChar('?')); } -void -UserInfoWidget::setAvatar(const QImage &img) -{ - avatar_image_ = img; - userAvatar_->setImage(img); - update(); -} - void UserInfoWidget::setDisplayName(const QString &name) { @@ -160,6 +151,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 65de7be9202dadf18405ed5c69bbb3811fbd8285..263dd0c22823cc97746a65a19d133003e0c45739 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/dialogs/MemberList.cpp b/src/dialogs/MemberList.cpp index 88a95403802ce2abd10b1bbeb12bbe51629b1d57..9e973efa5d715b032e2aa068b41f1faea990c5a6 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); diff --git a/src/dialogs/ReadReceipts.cpp b/src/dialogs/ReadReceipts.cpp index 5a0d98c7843fc159ed4e75a938ae20c6d6fea34b..58ad59c3aacfe7a05c512c8986c04e1ef858cb3b 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 diff --git a/src/dialogs/RoomSettings.cpp b/src/dialogs/RoomSettings.cpp index 1fe5904bf4be7b9ae3a3c21b301033f972fdab7a..00b034ccd71f747d34d41b3efd6c3e03cf34719f 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); @@ -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 @@ -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 6667b68b931b3895b6752c0815b2ee3e45c4ac43..e1807ba1ee924e04bd2222ef0af0dffc5f9e3b77 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/UserProfile.cpp b/src/dialogs/UserProfile.cpp index 6aea96a8cbd0a0b850baf313e2bd9df196a039cc..5ad3afa2eb731a0881d2d1194a9acebc664a950b 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); @@ -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 f905983ad254e5e1c38defeb1a64a8e04fdeb951..c4d4327f43363337ecc91a0f54524bf27e35ec2d 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/timeline/.TimelineItem.cpp.swp b/src/timeline/.TimelineItem.cpp.swp new file mode 100644 index 0000000000000000000000000000000000000000..75e03aebe8a2b20a05aec339ab9cab819ed7fe2e --- /dev/null +++ b/src/timeline/.TimelineItem.cpp.swp @@ -0,0 +1,186 @@ +b0VIM 8.1������€c]7B[�'"��nicolas���������������������������������gentoo-neko�����������������������������~nicolas/Dokumente/devel/open-source/nheko/src/timeline/TimelineItem.cpp�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������utf-8 �3210����#"! U�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������tp�����������/����������������������������0�������1����������������������������a����������������������������x���������������������@�������~����������������������������¿�������������� ��������������Ó����������������������������à���������������������;�������ö���������������������B�������0��������������������-�������s���������������������������Ÿ���������������������������¿��������������������I�������Á��������������������3������� ��������������������;�������=������������� ������� +�������w��������������������<�������‚���������������������������¾���������������������������Ì���������������������������ß��������������������K�������ë���������������������������6������������� +�������4�������M��������������������9�������‚���������������������������º�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ad�� ��á �����/�������ý��¸��µ��m��%��à��¹��¶��s��1��ð ��À ��½ ��x ��/ ��+ �� �� ��÷��Û��Â��±��Ÿ��‹��Š��t��`��K��3��"����ó��Ù��Ø��µ����f��>������õ +��Ö +��Õ +�� +��d +��c +��á �� +��C +�� +��Ñ �� ��; ��9 ��8 ��3 ��þ��ü��Ð��Ï��¬��d��*��)��à��Þ��Ý��Ø����«��x��`��_��F������þ��ý��ã��¸��Ž��w��m��?����ÿ��Ð��¢��‹����V��"������×��À��¶��´��³��®��x��v��^��]��D����é��Ò��¥��x��a��8����ù��Ð��¨��‘��g��G��0��&��%�������������}� update();�� }� break;� setToolTip("");� case StatusIndicatorState::Empty:� break;� setToolTip(tr("Sent"));� case StatusIndicatorState::Sent:� break;� setToolTip(tr("Seen"));� case StatusIndicatorState::Read:� break;� setToolTip(tr("Delivered"));� case StatusIndicatorState::Received:� break;� setToolTip(tr("Encrypted"));� case StatusIndicatorState::Encrypted:� switch (state) {�� state_ = state;�{�StatusIndicator::setState(StatusIndicatorState state)�void��}� }� break;� case StatusIndicatorState::Empty:� }� break;� paintIcon(p, doubleCheckmarkIcon_);� case StatusIndicatorState::Read: {� }� break;� paintIcon(p, checkmarkIcon_);� case StatusIndicatorState::Received: {� break;� paintIcon(p, lockIcon_);� case StatusIndicatorState::Encrypted:� }� break;� paintIcon(p, clockIcon_);� case StatusIndicatorState::Sent: {� switch (state_) {�� p.setPen(iconColor_);�� PainterHighQualityEnabler hq(p);� Painter p(this);�� return;� if (state_ == StatusIndicatorState::Empty)�{�StatusIndicator::paintEvent(QPaintEvent *)�void��}� QIcon(pixmap).paint(&p, rect(), Qt::AlignCenter, QIcon::Normal);�� painter.fillRect(pixmap.rect(), p.pen().color());� painter.setCompositionMode(QPainter::CompositionMode_SourceIn);� QPainter painter(&pixmap);�� auto pixmap = icon.pixmap(width());�{�StatusIndicator::paintIcon(QPainter &p, QIcon &icon)�void��}� doubleCheckmarkIcon_.addFile(":/icons/StatusIndicator::StaStatusIndicator::StaStatusIndicator::StatusIndicator(QWidget *parent)�������úw.�������������?¬StatusIndicator::StatusIndicator(QWidget *parent)�������úw.�������������úw. �������"������úw.�������+������úw.��������constexpr int MSG_PADDING = 20;�������úw.�������constexpr int MSG_RIGHT_MARGIN = 7;�������úw.��������#include "mtx/identifiers.hpp"�#include "dialogs/RawMessage.h"��#include "timeline/widgets/VideoItem.h"�#include "timeline/widgets/ImageItem.h"�#include "timeline/widgets/FileItem.h"�#include "timeline/widgets/AudioItem.h"�#include "timeline/TimelineItem.h"��#include "ui/TextLabel.h"�#include "ui/Painter.h"�#include "ui/Avatar.h"�#include "Olm.h"�#include "MainWindow.h"�#include "Logging.h"�#include "Config.h"�#include "ChatPage.h"��#include <QtGlobal>�#include <QTimer>�#include <QMenu>�#include <QFontDatabase>�#include <QDesktopServices>�#include <QContextMenuEvent>��#include <functional>� */� * along with this program. If not, see <http://www.gnu.org/licenses/>.� * You should have received a copy of the GNU General Public License� *� * GNU General Public License for more details.� * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the� * but WITHOUT ANY WARRANTY; without even the implied warranty of� * This program is distributed in the hope that it will be useful,� *� * (at your option) any later version.� * the Free Software Foundation, either version 3 of the License, or� * it under the terms of the GNU General Public License as published by� * This program is free software: you can redistribute it and/or modify� *� * nheko Copyright (C) 2017 Konstantinos Sideris <siderisk@auth.gr>�/*�ad��x��°������������™��X��*��Ô��À��²��°��Ã��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������}� });� }� });� }� "failed to serialize event ({}, {})", room_id, event_id);� nhlog::net()->warn(� } catch (const nlohmann::json::exception &e) {� emit proxy->eventRetrieved(utils::serialize_event(res));�������úw. �������ad��ç��/�����K�������Þ����†��8����Ú��Ù��H����ã ��â ��œ ��o ��I ��" �� �� ��Î��¡��z��S��G��F��÷��«��Ÿ����œ��—������ç +��º +�� +��B +��@ +��? +��: +��± ��¯ ��‡ ��3 ��2 ��ù��Ð��Ï��‰��K��J�� ��Ò��Œ��‹��Y������ô����›��š��•��K��I����ç��æ��Ÿ��T����å��º��¸��·��²��/��Ô��������Ó��Ñ��Ð��Ë��B��@��&��î��ì��ë��æ��‚��€��R��:��ø��µ��³��²�������������void��}� style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);� QPainter p(this);� ������?¬…±�������������?¬…±������� opt.init(this);� QStyleOptTimelineItem::setUseTimelineItem::setUserAvatar(const QString &userid)�������úw.�������������?TimelineItem::setUserAvatar(const QString &userid)�������úw.�������������?TimelineItem::setUserAvatar(const QString &userid)�������úw.�������������?TimelineItem::setUserAvatar(const QStrinTimelineItem::setUserAvatar(const QStrinTimelineItem::setUserAvatar(const QStrinTimelineItem::setUserAvatar(const QString &userid)�������úw.�������������?TimelineItem::setUserAvatar(const QString &userid)�������úw.���������� ���úw.�������#������úw.�������,������úw.�������void��}� 0);� 0,� conf::timeline::msgTopMargin,� QFontMetrics(f).height() * 2 + 2,� topLayout_->setContentsMargins(conf::timeline::msgLeftMargin +�� f.setPointSizeF(f.pointSizeF());� QFont f;� ������úw.�������������úw.�������{�TimelineItem::setupSimpleLayout()�������úw.�������������úw.�������void��}� mainLayout_->insertWidget(0, userName_, Qt::AlignTop | Qt::AlignLeft);� if (userName_)�� topLayout_->setAlignment(userAvatar_, Qt::AlignTop | Qt::AlignLeft);� topLayout_->insertWidget(0, userAvatar_);�� userAvatar_->setLetter(QChar(userName[1]).toUpper());� if (userName[0] == '@' && userName.size() > 1)� // TODO: The provided user name should be a UserId class�� userAvatar_->setLetter(QChar(userName[0]).toUpper());� userAvatar_ = new Avatar(this, QFontMetrics(f).height() * 2);�� f.setPointSizeF(f.pointSizeF());� QFont f;� ������úw.�������������úw.�������� conf::timeline::msgLeftMargin, conf::timeline::msgAvatarTopMargin, 0, 0);� topLayout_->setContentsMargins(�{�TimelineItem::setupAvatarLayout(const QString &userName)�������úw.�������������úw.�������'������úw.�������0������úw.�������void��}� QString("<span style=\"color: #999\"> %1 </span>").arg(time.toString("HH:mm")));� timestamp_->setText(� timestamp_->setFont(timestampFont_);� timestamp_ = new QLabel(this);�{�TimelineItem::generateTimestamp(const QDateTime &time)�������úw.�������������úw.�������'��� ���úw.�������2������úw.�������void��}� });� MainWindow::instance()->openUserProfile(user_id, room_id_);� connect(filter, &UserProfileFilter::clicked, this, [this, user_id]() {�� });� userName_->setFont(f);� f.setUnderline(false);� QFont f = userName_->font();� connect(filter, &UserProfileFilter::hoverOff, this, [this]() {�� });� userName_->setFont(f);� f.setUnderline(true);� QFont f = userName_->font();� connect(filter, &UserProfileFilter::hoverOn, this, [this]() {�� userName_->setCursor(Qt::PointingHandCursor);� userName_->installEventFilter(filter);� auto filter = new UserProfileFilter(user_id, userName_);�������úw.�������������úw.�������-������úw.�������6��� ���úw. +�������� refreshAuthorColor();� ������úw.������� // otherwise this will just set it.� // Set the user color asynchronously if it hasn't been generated yet,�#endif� QFontMetrics(userName_->font()).horizontalAdvance(userName_->text()));� userName_->setFixedWidth(�ad��õ��= ������������Ú��°��S��+��þ��Ï����<��ï ��k ��= ��› ��B ��; ��í��Á��£��¢��a��2��ü��û��µ��ˆ��b��;��/��.��ç +��º +��“ +��l +��` +��_ +�� +��Ä ��¸ ��¶ ��µ ��° ��y ��w ��P ��# �� ��«��©��¨��£��j��h��@��ì��ë��Ú��±��°��j��,��+��ê��³��m��l��:��í��ì��Õ��~��|��{��v��T��R��A������Ð��…��@����ë��é��è��ã��°��®��Š��r��q��@��>��=��8��ÿ��ý��ã��«��©��¨��£��{��y��_��G��-��ê��è��ç��â�� ����������������void��}� style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);� QPainter p(this);� opt.init(this);� QStyleOption opt;�{�TimelineItem::paintEvent(QPaintEvent *)�void��}� contextMenu_->exec(event->globalPos());� if (contextMenu_)�{�TimelineItem::contextMenuEvent(QContextMenuEvent *event)�void��}� userAvatar_->setImage(room_id_, userid);�� return;� if (userAvatar_ == nullptr)�{�TimelineItem::setUserAvatar(const QString &userid)�void��}� 0);� 0,� conf::timeline::msgTopMargin,� QFontMetrics(f).height() * 2 + 2,� topLayout_->setContentsMargins(conf::timeline::msgLeftMargin +�� f.setPointSizeF(f.pointSizeF());� QFont f;�{�TimelineItem::setupSimpleLayout()�void��}� mainLayout_->insertWidget(0, userName_, Qt::AlignTop | Qt::AlignLeft);� if (userName_)�� topLayout_->setAlignment(userAvatar_, Qt::AlignTop | Qt::AlignLeft);� topLayout_->insertWidget(0, userAvatar_);�� userAvatar_->setLetter(QChar(userName[1]).toUpper());� if (userName[0] == '@' && userName.size() > 1)� // TODO: The provided user name should be a UserId class�� userAvatar_->setLetter(QChar(userName[0]).toUpper());� userAvatar_ = new Avatar(this, QFontMetrics(f).height() * 2);�� f.setPointSizeF(f.pointSizeF());� QFont f;�� conf::timeline::msgLeftMargin, conf::timeline::msgAvatarTopMargin, 0, 0);� topLayout_->setContentsMargins(�{�TimelineItem::setupAvatarLayout(const QString &userName)�void��}� QString("<span style=\"color: #999\"> %1 </span>").arg(time.toString("HH:mm")));� timestamp_->setText(� timestamp_->setFont(timestampFont_);� timestamp_ = new QLabel(this);�{�TimelineItem::generateTimestamp(const QDateTime &time)�void��}� });� MainWindow::instance()->openUserProfile(user_id, room_id_);� connect(filter, &UserProfileFilter::clicked, this, [this, user_id]() {�� });� userName_->setFont(f);� f.setUnderline(false);� QFont f = userName_->font();� connect(filter, &UserProfileFilter::hoverOff, this, [this]() {�� });� userName_->setFont(f);� f.setUnderline(true);� QFont f = userName_->font();� connect(filter, &UserProfileFilter::hoverOn, this, [this]() {�� userName_->setCursor(Qt::PointingHandCursor);� userName_->installEventFilter(filter);� auto filter = new UserProfileFilter(user_id, userName_);�� refreshAuthorColor();� // otherwise this will just set it.� // Set the user color asynchrono#else��������ZhÑø�������������oÁ€�#else��������ZhÑø�������������oÁ€������ userName_->setFixedWidth(QFontMetrics(userName_->font()).width(userName_->text()));����\���ZhÑø���������\���oÁ€������ // width deprecated in 5.13:����%���ZhÑø���������%���oÁ€������#if QT_VERSION < QT_VERSION_CHECK(5, 11, 0)���� +���úw. �������������úw. ������� userName_->setAlignment(Qt::AlignLeft | Qt::AlignTop);� userName_->setAttribute(Qt::WA_Hover);� userName_->setToolTipDuration(1500);� userName_->setToolTip(user_id);� userName_->setText(utils::replaceEmoji(fm.elidedText(sender, Qt::ElideRight, 500)));� userName_->setFont(usernameFont);� userName_ = new QLabel(this);�ad��Ù��©�����-�������À��e����ý��Ê��´��²��Ž����G��E��D��Ç ��m ��- ��Ò��~��j��7��!����û��ú��´��²��±��4��Ç +��† +��+ +��× ��à �� ��z ��x ��! ��û��ú��É��È��}��{��z��û��©��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� const mtx::ev const mtx::ev const mtx::events::Sticker &event,�8������úw.����� const mtx::ev const mtx::ev const mtx::ev const mtx::events::Sticker &event,�8������úw.����� const mtx::ev const mtx::ev const mtx::ev const mtx::events::Sticker &event,�8������úw.����� const mtx::events::Sticker &event,�8������úw.�������TimelineItem::TimelineItem(StickerItem *image,�������úw.�������������úw. �������������úw.�������)������úw.��������}� addSaveImageAction(image);� ������úw.�������������úw.�������� markOwnMessagesAsReceived(event.sender);�� image, event, with_sender);� setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Image>, ImageItem>(�{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Image)� : QWidget(parent)� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool with_sender,�!������úw.������� const mtx::events::RoomEvent<mtx::events::msg::Image> &event,�S������úw.�������TimelineItem::TimelineItem(ImageItem *image,�������úw.�������������úw. ���������� ���úw.�������'������úw.��������}� setupLocalWidgetLayout<VideoItem>(video, userid, withSender);�� init();� ������úw.�������{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Video)� : QWidget{parent}� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool withSender,�!��� +���úw.������� const QString &userid,�"������úw.�������+������úw.�������TimelineItem::TimelineItem(VideoItem *video,�������úw.�������������úw. ���������� ���úw.�������'������úw.��������}� setupLocalWidgetLayout<AudioItem>(audio, userid, withSender);�� init();� ������úw.�������{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Audio)� : QWidget{parent}� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool withSender,�!��� +���úw.�������ad��Q��u�����B�������²����Z��/��Ý��—��h��W��&��ú ��Ï ��¦ ��` ��1 ��' ��& ��æ��³��²��d��c��5��ò��Â��Á��r��a��3��ú +��ð +��ï +��¼ +��º +��¹ +��< +��â ��¢ ��G ��ó��ß��¬��–��”��p��o��)��(��Ý��Û��Ú��_����Å��j������Ð��º��¸��”��“��O��M��L��Ï��u��;��"��ö��â��¯��™��—��‡��†��@��>��=����Þ��²����S��?�� const QString const QString const QString &userid,�"������úw.�������"������?¬ const QString const QString const QString const QString const QString &userid,�"������úw.�������"������?¬ const QString const QString const QString const QString &userid,�"������úw.�������"������?¬ const QString const QString &userid,�"������úw.�������+������úw.�������TimelineItem::TimelineItem(AudioItem *audio,�������úw.�������������úw. ���������� ���úw.�������'������úw.��������}� setupLocalWidgetLayout<FileItem>(file, userid, withSender);�� init();� ������úw.�������{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::File)� : QWidget{parent}� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool withSender,�!��� +���úw.������� const QString &userid,�"������úw.�������+������úw.�������TimelineItem::TimelineItem(FileItem *file,�������úw.�������������úw. �������������úw.�������&������úw.��������}� addSaveImageAction(image);� ������úw.�������������úw.�������� setupLocalWidgetLayout<ImageItem>(image, userid, withSender);�� init();� ������úw.�������{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Image)� : QWidget{parent}� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool withSender,�!��� +���úw.������� const QString &userid,�"������úw.�������+������úw.�������TimelineItem::TimelineItem(ImageItem *image,�������úw.�������������úw. ���������� ���úw.�������'������úw.��������}� adjustMessageLayout();� ������úw.�������� }� setupSimpleLayout();�������úw.������� generateBody(formatted_body);� } else {� setUserAvatar(userid);���� ���úw.�������������úw.�������� setupAvatarLayout(displayName);� generateBody(userid, displayName, formatted_body);� if (withSender) {� ��� +���úw.�������� generateTimestamp(timestamp);� ������úw.���������� ���úw.�������� formatted_body.replace("mx-reply", "div");� formatted_body = utils::linkifyMessage(formatted_body);�� }� timestamp};� utils::descriptiveTime(timestamp),� body,� userid,� "You: ",� descriptionMsg_ = {emptyEventId,� } else {� timestamp};� utils::descriptiveTime(timestamp),� QString("* %1 %2").arg(displayName).arg(body),� userid,� "",� descriptionMsg_ = {emptyEventId,� formatted_body = QString("<em>%1</em>").arg(formatted_body);�ad��‹��ÿ������������®��\����ä��ã��ª��„��ƒ��[�� ��ê ��¾ ��½ ��i ��E ��D �� ��î��³��B��A��ÿ��å��À��w��#��"��ö +��Æ +��‰ +�� +�� +��å �� ��q ��& ��ú��ù��°����J��I��ö��õ��¿��v��,��+��Ö��‘����Ž��‹��j��f��ò��˜��G����¬��l��X��B��,��*����Ø��×��‚�� ����Ú��‹��Š��T����ß��Þ��˜��—��b�������� if (ty == mtx::events::MessageType::Emote) {�� QString emptyEventId;� ������?¬…±�������������?¬…±�������� formatted_body = body.toHtmlEscaped();� if (formatted_body == body.trimmed().toHtmlEscaped())� // Escape html if the input is not formatted.�� auto formatted_body = utils::markdownToHtml(body);�������?¬…±������� // Generate the html body to be rendered.�� auto timestamp = QDateTime::currentDateTime();���� ���?¬…±���������� ���?¬…±�������'������?¬…±������� auto displayName = Cache::displayName(room_id_, userid);�������?¬…±�������� addReplyAction();� ������?¬…±������� init();� ������?¬…±�������{� , room_id_{room_id}� , message_type_(ty)� : QWidget(parent)� QWidget *parent)�%������?¬…±������� const QString &room_id,�"������?¬…±�������+������?¬…±������� bool withSender,�!��� +���?¬…±������� QString body,�������?¬…±�������$������?¬…±������� const QString &userid,�"������?¬…±�������+������?¬…±�������TimelineItem::TimelineItem(mtx::events::MessageType ty,�������?¬…±�������������?¬…± �������5������?¬…±������� */� * For messages created locally.�/*��}� setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);� parentWidget()->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);�� statusIndicator_->setFixedHeight(tsFm.height() - tsFm.leading());� statusIndicator_->setFixedWidth(tsFm.height() - tsFm.leading());� statusIndicator_ = new StatusIndicator(this);�� QFontMetrics tsFm(timestampFont_);� ������?¬…±�������������?¬…±�������� timestampFont_.setStyleHint(QFont::Monospace);� timestampFont_.setFamily("Monospace");� timestampFont_.setPointSizeF(timestampFont_.pointSizeF() * 0.9);�� contextBtn_->setMenu(contextMenu_);� contextBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));� contextBtn_->setIcon(context_icon);� context_icon.addFile(":/icons/icons/ui/vertical-ellipsis.png");� QIcon context_icon;� ������?¬…±�������� contextBtn_->setCornerRadius(buttonSize_ / 2);� ������?¬…± +�������������?¬…±�������&������?¬…±������� contextBtn_->setFixedSize(buttonSize_, buttonSize_);� contextBtn_->setToolTip(tr("Options"));� contextBtn_ = new FlatButton(this);�� connect(replyBtn_, &FlatButton:: QIcon reply_icon;� ������úw.� QIcon reply_icon;� ������úw.� QIcon reply_icon;� ������úw.������� ������?¬…±� QIcon reply_icon;� ������úw.� QIcon reply_icon;� ������úw.���������� +���úw.�������� replyBtn_->setCornerRadius(buttonSize_ / 2);� ��� ���úw. +�������������úw.�������$������úw.������� replyBtn_->setFixedSize(buttonSize_, buttonSize_);� replyBtn_->setToolTip(tr("Reply"));� replyBtn_ = new FlatButton(this);�� mainLayout_->setSpacing(0);� mainLayout_->setContentsMargins(conf::timeline::headerLeftMargin, 0, 0, 0);�� topLayout_->addLayout(mainLayout_);� topLayout_->setSpacing(0);� conf::timeline::msgLeftMargin, conf::timeline::msgTopMargin, 0, 0);� topLayout_->setContentsMargins(�� actionLayout_->setSpacing(0);� actionLayout_->setContentsMargins(13, 1, 13, 0);�� messageLayout_->setSpacing(MSG_PADDING);� messageLayout_->setContentsMargins(0, 0, MSG_RIGHT_MARGIN, 0);� actionLayout_ = new QHBoxLayout;� ��� ���úw. +�������������úw.������� messageLayout_ = new QHBoxLayout;� ������úw. +�������������úw.�������ad��/ ��{ ��������������D��â��á��£����M��7��þ ��ý ��Í ��{ ��e ��O �� ��×��Ö����w��v��N�����Ý��±��°��\��8��7�� ��á +��¦ +��q +��p +��V +�� +��ï ��¦ ��R ��Q ��% ��õ��¸����€��d����ð��¥��y��x��/�����É��È����œ��f����Ó��Ò��}��8��6��5��2���� ��Õ��£��z��N����ï��Û��Å��¯������ƒ��‚��A������Õ��š��™��c��%��î��í��Ï��Î��™��˜����������� if (ty == mtx::events::MessageType::Emote) {�� QString emptyEventId;�� formatted_body = body.toHtmlEscaped();� if (formatted_body == body.trimmed().toHtmlEscaped())� // Escape html if the input is not formatted.�� auto formatted_body = utils::markdownToHtml(body);� // Generate the html body to be rendered.�� auto timestamp = QDateTime::currentDateTime();� auto displayName = Cache::displayName(room_id_, userid);�� addReplyAction();� init();�{� , room_id_{room_id}� , message_type_(ty)� : QWidget(parent)� QWidget *parent)� const QString &room_id,� bool withSender,� QString body,� const QString &userid,�TimelineItem::TimelineItem(mtx::events::MessageType ty,� */� * For messages created locally.�/*��}� setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);� parentWidget()->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);�� statusIndicator_->setFixedHeight(tsFm.height() - tsFm.leading());� statusIndicator_->setFixedWidth(tsFm.height() - tsFm.leading());� statusIndicator_ = new StatusIndicator(this);�� QFontMetrics tsFm(timestampFont_);�� timestampFont_.setStyleHint(QFont::Monospace);� timestampFont_.setFamily("Monospace");� timestampFont_.setPointSizeF(timestampFont_.pointSizeF() * 0.9);�� contextBtn_->setMenu(contextMenu_);� contextBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));� contextBtn_->setIcon(context_icon);� context_icon.addFile(":/icons/icons/ui/vertical-ellipsis.png");� QIcon context_icon;�� contextBtn_->setCornerRadius(buttonSize_ / 2);� contextBtn_->setFixedSize(buttonSize_, buttonSize_);� contextBtn_->setToolTip(tr("Options"));� contextBtn_ = new FlatButton(this);�� connect(replyBtn_, &FlatButton::clicked, this, &TimelineItem::replyAction);� replyBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));� replyBtn_->setIcon(reply_icon);� reply_icon.addFile(":/icons/icons/ui/mail-reply.png");� QIcon reply_icon;�� replyBtn_->setCornerRadius(buttonSize_ / 2);� replyBtn_->setFixedSize(buttonSize_, buttonSize_);� replyBtn_->setToolTip(tr("Reply"));� replyBtn_ = new FlatButton(this);�� mainLayout_->setSpacing(0);� mainLayout_->setContentsMargins(conf::timeline::headerLeftMargin, 0, 0, 0);�� topLayout_->addLayout(mainLayout_);� topLayout_->setSpacing(0);� conf::timeline::msgLeftMargin, conf::timeline::msgTopMargin, 0, 0);� topLayout_->setContentsMargins(�� actionLayout_->setSpacing(0);� actionLayout_->setContentsMargins(13, 1, 13, 0);�� messageLayout_->setSpacing(MSG_PADDING);� messageLayout_->setContentsMargins(0, 0, MSG_RIGHT_MARGIN, 0);� mainLayout_ = new QVBoxLayout mainLayout_ = new QVBoxLayout;� ������úw. +�������������úw.������� topLayout_ = new QHBoxLayout(this);�� &TimelineItem::finishedGeneratingColor);� this,� &QFutureWatcher<QString>::finished,� connect(colorGenerating_,� colorGenerating_ = new QFutureWatcher<QString>(this);�� connect(viewRawMessage_, &QAction::triggered, this, &TimelineItem::openRawMessageViewer);� connect(markAsRead_, &QAction::triggered, this, &TimelineItem::sendReadReceipt);� ChatPage::instance(), &ChatPage::themeChanged, this, &TimelineItem::refreshAuthorColor);�ad��`��L�����4�������}��{��K��õ��Á��À��l��b��`��_��Z������á ��Ž ��X ��W ��ú��ð��î��í��è��¤��¢��z��b��a����í��·����F������} +��{ +��z +��u +��) +��' +��÷ ��’ ��\ ��[ �� ��´�� ��–��”��“��Ž��L�� +��% +�� +�� +��Ê ��— ��V ��U ��& ��% ��ý��ü��Ý��Û��Ú��Õ��¯����‹��H����µ��w��(��Ê��u����é��º��¸��·��²��‡��…��:������Î��n����ð��ä��ã��À����™��`����Ó��Ò��µ��‡��0����ú��ù��á��������������������� try {�� }� return;� "failed to retrieve event {} from {}", event_id, room_id);� nhlog::net()->warn(� if (err) {�� using namespace mtx::events;� const mtx::events::collections::TimelineEvents &res, mtx::http::RequestErr err) {� [event_id, room_id, proxy = std::move(proxy)](� event_id,� room_id,� http::client()->get_event(�� });� Q_UNUSED(dialog);� auto dialog = new dialogs::RawMessage{QString::fromStdString(obj.dump(4))};� connect(proxy.get(), &EventProxy::eventRetrieved, this, [](const nlohmann::json &obj) {� auto proxy = std::make_shared<EventProxy>();�� const auto room_id = room_id_.toStdString();� const auto event_id = event_id_.toStdString();�������?¬…±�������{�TimelineItem::openRawMessageViewer() const�void��}� });� }� TimelineItem::addAvatar()�������úw.�������������?¬…±���������� ���úw.�TimelineItem::addAvaTimelineItem::addAvaTimelineItem::addAvatar()�������úw.�TimelineItem::addAvaTimelineItem::addAvaTimelineItem::addAvatar()�������úw.�TimelineItem::addAvaTimelineItem::addAvatar()�������úw.�TimelineItem::addAvatar()�������úw.�������������?¬…±���������� ���úw.���������� ���?¬…±�TimelineItem::addAvatar()�������úw.�TimelineItem::addAvaTimelineItem::addAvaTimelineItem::addAvatar()�������úw.�TimelineItem::addAvatar()�������úw.���������� ���úw.�������void��}� }� });� olm::request_keys(room_id_.toStdString(), event_id_.toStdString());� connect(requestKeys, &QAction::triggered, this, [this]() {�� contextMenu_->addAction(requestKeys);� auto requestKeys = new QAction("Request encryption keys", this);�������úw.������� if (contextMenu_) {� ������úw. +�������{�TimelineItem::addKeyRequestAction()�������úw.�������������úw.�������void��}� emit ChatPage::instance()->messageReply(related);� ������úw. �������������úw.�������������úw.�������$������úw.�������1������úw.�������� related.room = room_id_;� related.related_event = eventId().toStdString();� related.quoted_user = descriptionMsg_.userid;� related.quoted_body = body_->toPlainText();� related.type = message_type_;� RelatedInfo related;� ������úw.�������������úw.�������� return;� if (!body_)�������úw. +�������{�TimelineItem::replyAction()�������úw.�������������úw.�������void��}� }� connect(replyAction, &QAction::triggered, this, &TimelineItem::replyAction);�� contextMenu_->addAction(replyAction);� auto replyAction = new QAction("Reply", this);�������úw.������� if (contextMenu_) {� ������úw. +�������{�TimelineItem::addReplyAction()�������úw.�������������úw.�������void��}� }� connect(saveImage, &QAction::triggered, image, &ImageItem::saveAs);�� contextMenu_->addAction(saveImage);� auto saveImage = new QAction("Save image", this);���� ���úw.������� if (contextMenu_) {� ������úw. +�������{�TimelineItem::addSaveImageAction(ImageItem *image)�������úw.�������������úw.�������"��� ���úw.�������-������úw.�������ad��‹ ��# +������������¿��d����ü��æ��ä��‹��Š��Y��X��5��3��2��· ��K �� + ��¯��[��G����ÿ��ý��¨��ƒ��‚��Q��O��N��Ñ +��d +��# +��r��T��@�� ��÷ +��õ +��ž +��x +��w +��F +��D +��C +�� +��½ �� ��] ��1 �� ��ê��Ô��Ò��{��U��T��#��!�� ����ò��î��”��g��4����ô��À��ª��¨��˜��~��}��L��K����¿��h��g�� +��¦��¥����>����è��ª��ƒ��‚��\��[��@��÷��ö��³��ƒ��‚��[��J����÷��í��ì��Í��Ë��Ê��Ç��Ú�����/*��}� adjustMessageLayout();�� }� setupSimpleLayout();� generateBody(formatted_body);� } else {� setUserAvatar(sender);�� setupAvatarLayout(displayName);� generateBody(sender, displayName, formatted_body);�� auto displayName = Cache::displayName(room_id_, sender);� if (with_sender) {�� generateTimestamp(timestamp);�� timestamp};� utils::descriptiveTime(timestamp),� " sent a notification",� sender,� Cache::displayName(room_id_, sender),� descriptionMsg_ = {event_id_,�� auto body = QString::fromStdString(event.content.body).trimmed().toHtmlEscaped();� auto formatted_body = utils::linkifyMessage(utils::getMessageBody(event).trimmed());�� const auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);� const auto sender = QString::fromStdString(event.sender);� event_id_ = QString::fromStdString(event.event_id);�� markOwnMessagesAsReceived(event.sender);�� addReplyAction();� init();�{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Notice)� : QWidget(parent)� QWidget *parent)� const QString &room_id,� bool with_sender,�TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice> &event,� */� * Used to display remote notice messages.�/*��}� markOwnMessagesAsReceived(event.sender);�� video, event, with_sender);� bool with_sen bool with_sen bool with_sender,�!������úw.�������!������?¬…±�� bool with_sen bool with_sen bool with_sender,�!������úw.�������!������?¬…±�� bool with_sender,�!������úw.������� const mtx::events::RoomEvent<mtx::events::msg::Audio> &event,�S������úw.�������TimelineItem::TimelineItem(AudioItem *audio,�������úw.�������������úw. ���������� ���úw.�������'������úw.��������}� markOwnMessagesAsReceived(event.sender);�� file, event, with_sender);� setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::File>, FileItem>(�{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::File)� : QWidget(parent)� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool with_sender,�!������úw.������� const mtx::events::RoomEvent<mtx::events::msg::File> &event,�R������úw.�������TimelineItem::TimelineItem(FileItem *file,�������úw.�������������úw. �������������úw.�������&������úw.��������}� addSaveImageAction(image);�� markOwnMessagesAsReceived(event.sender);�� setupWidgetLayout<mtx::events::Sticker, StickerItem>(image, event, with_sender);�{� , room_id_{room_id}� : QWidget(parent)� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool with_sender,�!������úw.�������ad��;��§������������¼��º��¹��´��é��ç��°��¯��„��J����» ��± ��° ��l ��' ��ø��÷��¨��§���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� QFontMetrics fm(usernameFont);�� QFontMetrics fm(usernameFont);� ������úw.������� ������?¬…±�������������úw.�������������?¬…±�������� QFontMetric� QFontMetrics fm(usernameFont);�� QFontMetrics fm(usernameFont);�� QFontMetrics fm(usernameFont);� ������úw.�������������úw.�������� usernameFont.setWeight(QFont::Medium);� usernameFont.setPointSizeF(usernameFont.pointSizeF() * 1.1);� QFont usernameFont;� ������úw.�������������úw.�������� }� sender = displayname.split(":")[0].split("@")[1];� if (displayname.split(":")[0].split("@").size() > 1)� // TODO: Fix this by using a UserId type.� if (displayname.startsWith("@")) {�� auto sender = displayname;�������úw.�������{�TimelineItem::generateUserName(const QString &user_id, const QString &displayname)�������úw.�������������úw.�������&������úw.�������/������úw.�������>������úw.�������G������úw.�������void��}� generateBody(body);� ������úw.�������������úw.�������ad�� ��T ����� +�������¼��m��Ñ��Ï��Î��É��ˆ��†��ì ��T ��Ì����˜��ó��ñ����¬��k��¦ +��™ +��Õ ��Ô ��¥ ��£ ��¢ ��ƒ ��~ ��Ö��Ô��”��4��3��×��–��8��,��*��)��$��Å��Ã��€��S��'��ò��è��§��v��7��ç��œ��r��_��^��Ö��Õ��‹��`����õ��š��ˆ��~��|��{��v��������9��Ñ����¿ QString userColor = colorGenerating_->result();� ������?¬…±���������� ���úw.���������� ���?¬…±������� nhlog::ui()->debug("finishedGeneratingColor for: {}", userName_->toolTip().toStdString());�{�TimelineItem::finishedGeneratingColor()�������úw.�������������?¬…±�������������?¬…±�������void��}� }� }� userName_->setStyleSheet("QLabel { color : " + userColor + "; }");� } else {� colorGenerating_->setFuture(QtConcurrent::run(generate));� if (userColor.isEmpty()) {� // If the color is empty, then generate it asynchronously�� QString userColor = Cache::userColor(userName_->toolTip());�������?¬…±���������� ���úw.���������� ���?¬…±�������� };� return userColor;� userName_->toolTip(), backgroundColor().name());� QString userColor = utils::generateContrastingHexColor(� std::function<QString()> generate = [this]() {� // generate user's unique color.� if (userName_) {� ��� ���úw. +������� ��� ���?¬…± +������� }� colorGenerating_->waitForFinished();� colorGenerating_->cancel();� if (colorGenerating_->isRunning()) {� // Cancel and wait if we are already generating the color.�{�TimelineItem::refreshAuthorColor()�������úw.�������������?¬…±�������������?¬…±�������void��}� });� ChatPage::instance()->currentRoom());� MainWindow::instance()->openUserProfile(user_id,� connect(body_, &TextLabel::userProfileTriggered, this, [](const QString &user_id) {�� body_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction);� body_ = new TextLabel(utils::replaceEmoji(body), this);�{�TimelineItem::generateBody(const QString &body)�������úw.�������������?¬…±�������������?¬…±�������"������?¬…±�������+������úw.�������+������?¬…±�������void�// Only the body is displayed.��}� sendReadReceipt();� ������?¬…±�������� statusIndicator_->setState(StatusIndicatorState::Received);�������úw. +�������������?¬…± +�������#������?¬…±�������,������?¬…±�������B������úw.�������B������?¬…±������� else� statusIndicator_->setState(StatusIndicatorState::Encrypted);�������úw. +�������������?¬…± +�������#������?¬…±�������,������?¬…±�������B��� ���úw.�������B��� ���?¬…±������� if (isEncrypted)� ������úw.������� ������?¬…±�������� isReceived_ = true;� �� statusIndicator_->setState(StatusIndicatorState::Read);� statusIndicator_->setSta statusIndicator_->setState(StatusIndicatorState::Read);������� statusIndicator_->setSta statusIndicator_->setState(StatusIndicatorState::Read);������� statusIndicator_->setState(StatusIndicatorState::Read);������� statusIndicator_->setState(StatusIndicatorState::Read);�������úw. +�������#������úw.�������,������úw.�������B������úw.������� if (statusIndicator_->state() != StatusIndicatorState::Encrypted)� ������úw. +�������������úw.�������*������úw.�������@��� ���úw.�������{�TimelineItem::markRead()�������úw.�������������úw.�������void��}� statusIndicator_->setState(StatusIndicatorState::Received);�������úw. +�������#������úw.�������,������úw.�������B������úw.������� if (sender == settings.value("auth/user_id").toString().toStdString())� QSettings settings;� ��� ���úw.�������������úw.�������ad��-��Q������������¥��Q��™��Î��¸��¶��_��9��8��������7 ��¶��a��Þ��v��b��/������À +��š +��™ +��h +��f +��e +��b +��7 +��3 +��a �� ��‰��!�� ��Ù��Ã��Á��‰��G��F������Í��`��á��à��[��Ï��Î��¨��g��D����Ó��¬��«��…��„��A��Ð��Ï��Œ��\��[��4��#��õ��¨��ž����V��T��S��P��������������/*��}� adjustMessageLayout();� ������úw.������� ������?¬…±�������� }� setupSimpleLayout();�������úw.�������������?¬…±������� generateBody(formatted_body);� } else {� setUserAvatar(sender);�� setupAvatarLayout(displayName);� generateBody(sender, displayName, formatted_body);�� auto displayName = Cache::displayName(room_id_, sender);�������úw.�������������?¬…±������� if (with_sender) {� ������úw.������� ������?¬…±�������� generateTimestamp(timestamp);�� timestamp};� utils::descriptiveTime(timestamp),� " sent a notification",� sender,� Cache::displayName(room_id_, sender),� descriptionMsg_ = {event_id_,�� auto body = QString::fromStdString(event.content.body).trimmed().toHtmlEscaped();�������úw.�������������?¬…±������� auto formatted_body = utils::linkifyMessage(utils::getMessageBody(event).trimmed());�������úw.�������������?¬…±�������� const auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);���� ���úw.���������� ���?¬…±������� const auto sender = QString::fromStdString(event.sender);�������úw.�������������?¬…±������� event_id_ = QString::fromStdString(event.event_id);�� markOwnMessagesAsReceived(event.sender);�� addReplyAction();� ������úw.������� ������?¬…±������� init();� ������úw.������� ������?¬…±�������{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Notice)� : QWidget(parent)� QWidget *parent)�������?¬…±�������%������úw.�������%������?¬…±������� const QString &room_id,�"������úw.�������"������?¬…±�������+������úw.�������+������?¬…±������� bool with_sender,�!������úw.�������!������?¬…±�������TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice> &event,�������úw.�������������?¬…±�������������úw.�������������?¬…±�������T������úw.�������T������?¬…±������� */� * Used to display remote notice messages.�/*��}� markOwnMessagesAsReceived(event.sender);�� video, event, with_sender);� setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Video>, VideoItem>(�{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Video)� : QWidget(parent)� QWidget *parent)�������?¬…±�������%������úw.�������%������?¬…±������� const QString &room_id,�"������úw.�������"������?¬…±�������+������úw.�������+������?¬…±������� bool with_sender,�!������úw.�������!������?¬…±������� const mtx::events::RoomEvent<mtx::events::msg::Video> &event,�S������úw.�������S������?¬…±�������TimelineItem::TimelineItem(VideoItem *video,�������úw.�������������?¬…±�������������úw. �������������?¬…± ���������� ���úw.���������� ���?¬…±�������'������úw.�������'������?¬…±��������}� markOwnMessagesAsReceived(event.sender);�� audio, event, with_sender);� setupWidgetLayout<mtx::events::RoomEvent<mtx::even QWidget *parent)�������úw.�������������?¬…±�������%������úw.������� QWidget *pare QWidget *pare QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.�������ad��r��Z�����3�������Ö��Ò��=��ü��¡��M��9����ð ��î ��Ê ��œ ��› ��j ��i ��% ��Ï��Î��]��å��ä��}��(��á +��à +��º +��› +��x +��. +��ð ��É ��È ��¢ ��¡ ��r ��/ ��ÿ��þ��×��Æ��˜��_��U��T��!��������ò��î��Z��’��ž��r��^��,��������ê��é��¸��·��s��1��0��Ó��o��n����Ú��Ù��©��ƒ��(����Ï��‘��j��i��C��B��'��ä��´��³��Œ��{��M��(������þ��ü��û��Ý��Û��·��Š��ˆ��‡��‚��i��g��'��%��$����Ü��Ú��í��������{�TimelineItem::markOwnMessagesAsReceived(const std::string &sender)�void��}� statusIndicator_->setState(StatusIndicatorState::Sent);�{�TimelineItem::markSent()�void��}� colorGenerating_->waitForFinished();� colorGenerating_->cancel();�{�TimelineItem::~TimelineItem()��}� adjustMessageLayout();�� }� setupSimpleLayout();� generateBody(formatted_body);� } else {� setUserAvatar(sender);�� setupAvatarLayout(displayName);� generateBody(sender, displayName, formatted_body);� if (with_sender) {�� generateTimestamp(timestamp);�� timestamp};� utils::descriptiveTime(timestamp),� QString(": %1").arg(body),� sender,� sender == settings.value("auth/user_id") ? "You" : displayName,� descriptionMsg_ = {event_id_,� QSettTimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx:TimelineItem::TimeliTimelineItem::TimelineItem(const mtx::evTimelineItem::TimelineItem(const mtx::evTimelineItem::TimeliTimelineItem::TimeliTimelineItem::TimeliTimelineItem::TimeliTimelineItem::TimeliTimelineItem::TimeliTimelineItem::TimeliTimelineItem::TimeliTimelineItem::TimeliTimelineItem::TimeliTimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx:TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Text> &event,�������úw.�������������úw. �������R������úw.������� */� * Used to display remote text messages.�/*��}� adjustMessageLayout();� ������úw.�������� }� setupSimpleLayout();�������úw.������� generateBody(formatted_body);� } else {� setUserAvatar(sender);�� setupAvatarLayout(displayName);� generateBody(sender, displayName, formatted_body);� if (with_sender) {� ������úw.�������� generateTimestamp(timestamp);�� timestamp};� utils::descriptiveTime(timestamp),� QString("* %1 %2").arg(displayName).arg(body),� sender,� "",� descriptionMsg_ = {event_id_,�� formatted_body = QString("<em>%1</em>").arg(formatted_body);� auto displayName = Cache::displayName(room_id_, sender);�������úw.������� auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);���� ���úw.�������� auto body = QString::fromStdString(event.content.body).trimmed().toHtmlEscaped();�������úw.������� auto formatted_body = utils::linkifyMessage(utils::getMessageBody(event).trimmed());�������úw.�������� const auto sender = QString::fromStdString(event.sender);�������úw.������� event_id_ = QString::fromStdString(event.event_id);�� markOwnMessagesAsReceived(event.sender);�� addReplyAction();� ������úw.������� init();� ������úw.�������{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Emote)� : QWidget(parent)� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool with_sender,�!������úw.�������TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Emote> &event,�������úw.�������������úw.�������S������úw.������� */� * Used to display remote emote messages.�ad��¤��¬�����;�������¿��d����ü��Ê��´��²��Ž��`��_��.��-��é ��“ ��’ ��! ��©��¨��A��ì��ë��§����&����Í +�� +��h +��g +��A +��@ +�� +��Î ��ž �� ��v ��e ��7 ��þ��ô��ó��À��¾��½��c��a��=������ ����Ç��Å��5��3��2��-��®��¬������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������{�TimelineItem::mark{�TimelineItem::markOwnMessagesAsReceive{�TimelineItem::markOwnMessagesAsReceive{�TimelineItem::mark{�TimelineItem::mark{�TimelineItem::mark{�TimelineItem::mark{�TimelineItem::mark{�TimelineItem::mark{�TimelineItem::mark{�TimelineItem::markOwnMessagesAsReceive{�TimelineItem::mark{�TimelineItem::mark{�TimelineItem::mark{�TimelineItem::markOwnMessagesAsReceived(const std::string {�TimelineItem::markOwnMessagesAsReceive{�TimelineItem::markOwnMessagesAsReceived(const std::string &sender)�������úw.{�TimelineItem::markOwnMessagesAsReceived(const std::string {�TimelineItem::markOwnMessagesAsReceived(const std::string &sender)�������úw.�������������úw.�������<������úw.�������void��}� statusIndicator_->setState(StatusIndicatorState::Sent);� ������úw. +�������������úw.�������$������úw.�������:������úw.�������{�TimelineItem::markSent()�������úw.�������������úw.�������void��}� colorGenerating_->waitForFinished();� colorGenerating_->cancel();�{�TimelineItem::~TimelineItem()�������úw.�������������úw.�������������úw.��������}� adjustMessageLayout();� ������úw.�������� }� setupSimpleLayout();�������úw.������� generateBody(formatted_body);� } else {� setUserAvatar(sender);�� setupAvatarLayout(displayName);� generateBody(sender, displayName, formatted_body);� if (with_sender) {� ������úw.�������� generateTimestamp(timestamp);�� timestamp};� utils::descriptiveTime(timestamp),� QString(": %1").arg(body),� sender,� sender == settings.value("auth/user_id") ? "You" : displayName,� descriptionMsg_ = {event_id_,� QSettings settings;� ��� ���úw.�������������úw.�������� auto displayName = Cache::displayName(room_id_, sender);�������úw.������� auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);���� ���úw.�������� auto body = QString::fromStdString(event.content.body).trimmed().toHtmlEscaped();�������úw.������� auto formatted_body = utils::linkifyMessage(utils::getMessageBody(event).trimmed());�������úw.�������� const auto sender = QString::fromStdString(event.sender);�������úw.������� event_id_ = QString::fromStdString(event.event_id);�� markOwnMessagesAsReceived(event.sender);�� addReplyAction();� ������úw.������� init();� ������úw.�������{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Text)� : QWidget(parent)� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool with_sender,�!������úw.�������ad��J��&�����0�������ì��ê��²��x��6��â��à��ß��Ú��-��+��ë ��ê ��Ÿ ��W �� �� ��Ó��Ñ��Ð��Ë��d��b��ó��Û��Ú��™��H��G��)��(��ú +��§ +��} +��f +��\ +�� +��Ý ��Æ ��o ��A ��* �� ��Í��™��‚��x��&��h��Á��¿��¾��¹��÷��õ����Œ��K��á��´����4����ð��‹��c��L��ç��¿��¨��B��T��ƒ��y��x case StatusIndicatorState::Empty:�������?¬…±�������$������úw.�������$������?¬…±������� break;� setToolTip(tr("Sent"));� case StatusIndicatorState::Sent:�������?¬…±�������$������úw.�������$������?¬…±������� break;� setToolTip(tr("Seen"));� case StatusIndicatorState::Read:�������?¬…±�������$������úw.�������$������?¬…±������� break;� setToolTip(tr("Delivered"));� case StatusIndicatorState::Received:�������?¬…±�������$������úw.�������$������?¬…±������� break;� setToolTip(tr("Encrypted"));� case StatusIndicatorState::Encrypted:�������?¬…±�������$��� ���úw.�������$��� ���?¬…±������� switch (state) {�������úw.�������������?¬…±�������� state_ = state;� ������úw. +������� ������?¬…± +�������������úw.�������������?¬…±�������{�StatusIndicator::setState(StatusIndicatorState state)�������úw.�������������?¬…±�������������úw.������ case StatusIndicatorState::Empty:�������úw.�������������?¬…±�������$������úw.������� case StatusIndicatorState::Empty:�������úw.�������������?¬…±�������$������úw.�������$������?¬…±����� case StatusI case StatusIndicatorState::Empty case StatusIndicatorState::Empty:�������úw.����� case StatusIndicatorState::Empty:�������úw.����� case StatusIndicatorState::Empty case StatusIndicatorState::Empty case StatusI case StatusIndicatorState::Empty case StatusIndicatorState::Empty case StatusIndicatorState::Empty case StatusIndicatorState::Empty case StatusIndicatorState::Empty case StatusIndicatorState::Empty:�������úw.�������$������úw.������� }� break;� paintIcon(p, doubleCheckmarkIcon_);� case StatusIndicatorState::Read: {�������úw.�������$������úw.������� }� break;� paintIcon(p, checkmarkIcon_);� case StatusIndicatorState::Received: {�������úw.�������$������úw.������� break;� paintIcon(p, lockIcon_);� case StatusIndicatorState::Encrypted:�������úw.�������$��� ���úw.������� }� break;� paintIcon(p, clockIcon_);� case StatusIndicatorState::Sent: {�������úw.�������$������úw.������� switch (state_) {�������úw. +�������� p.setPen(iconColor_);�� PainterHighQualityEnabler hq(p);� ������úw.�������#������úw.������� Painter p(this);� ������úw.�������������úw.�������� return;� if (state_ == StatusIndicatorState::Empty)� ������úw. +�������������úw.�������-������úw.�������{�StatusIndicator::paintEvent(QPaintEvent *)�������úw.���������� +���úw.�������������úw.�������void��}� QIcon(pixmap).paint(&p, rect(), Qt::AlignCenter, QIcon::Normal);�� painter.fillRect(pixmap.rect(), p.pen().color());� painter.setCompositionMode(QPainter::CompositionMode_SourceIn);� QPainter painter(&pixmap);� ������úw.�������������úw.�������� auto pixmap = icon.pixmap(width());�������úw.�������{�StatusIndicator::paintIcon(QPainter &p, QIcon &icon)�������úw.���������� ���úw.�������������úw.�������&������úw.�������)������úw.�������0������úw.�������void��}� doubleCheckmarkIcon_.addFile(":/icons/icons/ui/double-tick-indicator.png");� checkmarkIcon_.addFile(":/icons/icons/ui/checkmark.png");� clockIcon_.addFile(":/icons/icons/ui/clock.png");� lockIcon_.addFile(":/icons/icons/ui/lock.png");�{� : QWidget(parent)�ad��±��±�����9�������þ��Ñ��¹��¸��v����²��±��‚����Y��X��9��7��6��1��ã ��á ��¿ ��| ��8 ��é��«��\��þ��©��R����î +��ì +��ë +��æ +��“ +��‘ +��F +��ü ��û ��² ��R ��ö��À��´��³����}��i��0��Ò��£��¢��…��W�����Þ��Ê��É��±��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� tr try {�� }� tr try {�� try {�� tr tr tr tr try {�� }� return;� "failed to retrieve event {} from {}", event_id, room_id);� nhlog::net()->warn(� if (err) {�� using namespace mtx::events;� const mtx::events::collections::TimelineEvents &res, mtx::http::RequestErr err) {� [event_id, room_id, proxy = std::move(proxy)](� event_id,� room_id,� http::client()->get_event(�� });� Q_UNUSED(dialog);�������úw. ������� auto dialog = new dialogs::RawMessage{QString::fromStdString(obj.dump(4))};� connect(proxy.get(), &EventProxy::eventRetrieved, this, [](const nlohmann::json &obj) {� auto proxy = std::make_shared<EventProxy>();�������úw.�������� const auto room_id = room_id_.toStdString();�������úw.������� const auto event_id = event_id_.toStdString();�������úw.�������{�TimelineItem::openRawMessageViewer() const�������úw.�������������úw.�������void��}� });� }� event_id_.toStdString());� room_id_.toStdString(),� "failed to read_event ({}, {})",� nhlog::net()->warn(� if (err) {� [this](mtx::http::RequestErr err) {� event_id_.toStdString(),� http::client()->read_event(room_id_.toStdString(),� if (!event_id_.isEmpty())�{�TimelineItem::sendReadReceipt() const�������úw.�������������úw.�������void��}� setUserAvatar(userid);�� setupAvatarLayout(displayName);�� generateUserName(userid, displayName);�� auto displayName = Cache::displayName(room_id_, userid);�������úw.������� auto userid = descriptionMsg_.userid;�������úw.�������������úw. +�������,������úw.������� // TODO: should be replaced with the proper event struct.�� return;� if (userAvatar_)� ������úw. +�������{�ad��Ò��î�����@�������û��¦��¤��o��B����á��¬��}��|��-��Ü ��– ��V �� �� ��â��à��ß��Ú��Ž��Œ��_��2����Ñ��œ��m��l����Ì +��† +��F +�� +�� +��Ò ��Ð ��Ï ��Ê �� ��‹ ��S �� ��ã��«��x��w��J�� +��Ë��ˆ��G����á��³��†��…��<����¿��³��²��V��î��N��U������Å��›��e��3�����¡��t����´��U��#��ÿ��þ��½��Ÿ��“��‚��©�������������������� connect(� });� });� emit eventRedacted(event_id_);�� }� return;� err->matrix_error.error)));� .arg(QString::fromStdString(� emit redactionFailed(tr("Message redaction failed: %1")� if (err) {� [this](const mtx::responses::EventId &, mtx::http::RequestErr err) emit ChatPage::instance( emit ChatPage::instance( emit ChatPage::instance( emit emit emit emit emit emit emit ChatPage::instance()->removeTimelineEvent(room_id_, event_id);�������úw. ������� connect(this, &TimelineItem::eventRedacted, this, [this](const QString &event_id) {�� });� MainWindow::instance()->openReadReceiptsDialog(event_id_);� if (!event_id_.isEmpty())� connect(showReadReceipts_, &QAction::triggered, this, [this]() {�� contextMenu_->addAction(redactMsg_);� contextMenu_->addAction(markAsRead_);� contextMenu_->addAction(viewRawMessage_);� contextMenu_->addAction(showReadReceipts_);� redactMsg_ = new QAction("Redact message", this);� viewRawMessage_ = new QAction("View raw message", this);� markAsRead_ = new QAction("Mark as read", this);� showReadReceipts_ = new QAction("Read receipts", this);� contextMenu_ = new QMenu(this);�� auto buttonSize_ = 32;�������úw.������� body_ = nullptr;� ������úw. +������� userName_ = nullptr;� ��� ���úw. +������� timestamp_ = nullptr;� ��� +���úw. +������� userAvatar_ = nullptr;� ������úw. +�������{�TimelineItem::init()�������úw.�������������úw.�������void��}� mainLayout_->addLayout(messageLayout_);�� messageLayout_->setAlignment(actionLayout_, Qt::AlignTop);� messageLayout_->setAlignment(timestamp_, Qt::AlignTop);� messageLayout_->setAlignment(statusIndicator_, Qt::AlignTop);� actionLayout_->setAlignment(contextBtn_, Qt::AlignTop | Qt::AlignRight);� actionLayout_->setAlignment(replyBtn_, Qt::AlignTop | Qt::AlignRight);�� messageLayout_->addWidget(timestamp_);� messageLayout_->addWidget(statusIndicator_);� messageLayout_->addLayout(actionLayout_);� actionLayout_->addWidget(contextBtn_);� actionLayout_->addWidget(replyBtn_);� messageLayout_->addWidget(body_, 1);�{�TimelineItem::adjustMessageLayout()�������úw.�������������úw.�������void��}� mainLayout_->addLayout(messageLayout_);�� messageLayout_->setAlignment(actionLayout_, Qt::AlignTop);� messageLayout_->setAlignment(timestamp_, Qt::AlignTop);� messageLayout_->setAlignment(statusIndicator_, Qt::AlignTop);� actionLayout_->setAlignment(contextBtn_, Qt::AlignTop | Qt::AlignRight);� actionLayout_->setAlignment(replyBtn_, Qt::AlignTop | Qt::AlignRight);�� messageLayout_->addWidget(timestamp_);� messageLayout_->addWidget(statusIndicator_);� messageLayout_->addLayout(actionLayout_);� actionLayout_->addWidget(contextBtn_);� actionLayout_->addWidget(replyBtn_);� messageLayout_->addLayout(widgetLayout_, 1);�{�TimelineItem::adjustMessageLayoutForWidget()�������úw.�������������úw.�������void�ad�� ��������������ô��Ÿ��I��=��û��Ñ��›��i��6��× ��ª ��4 ��Ö��w��E��!�� ��Ë����¡������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ connect(� connect(� connect(� connect(� });� });� emit eventRedacted(event_id_);�#������úw. �������� }� return;� err->matrix_error.error)));� .arg(QString::fromStdString(� emit redactionFailed(tr("Message redaction failed: %1")�+������úw. ������� if (err) {� [this](const mtx::responses::EventId &, mtx::http::RequestErr err) {� event_id_.toStdString(),� room_id_.toStdString(),� http::client()->redact_event(� if (!event_id_.isEmpty())� connect(redactMsg_, &QAction::triggered, this, [this]() {� });� emit ChatPage::instance()->showNotification(msg);�������úw. ������� connect(this, &TimelineItem::redactionFailed, this, [](const QString &msg) {� });�ad��u��É������������ÿ��Û��Ž��F��õ��ã����†��„��@��;��7 ��5 ��É����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� generateUserName(user_id, displayname);� ������úw.������� ������?¬…±�������������úw.�������������?¬…±�������#������úw.�������#������?¬…± generateUserName(user_id, displayname);� ������úw. generateUserName(user_id, displayname);� ������úw.�������������úw.�������#������úw.�������{�TimelineItem::generateBody(const QString &user_id, const QString &displayname, const QString &body)�������úw.�������������úw.�������"������úw.�������+������úw.�������:������úw.�������C������úw.�������V������úw.�������_������úw.�������void�// The username/timestamp is displayed along with the message body.�}� }� userName_->setStyleSheet("QLabel { color : " + userColor + "; }");� }� Cache::insertUserColor(userName_->toolTip(), userColor);� if (Cache::userColor(userName_->toolTip()).isEmpty()) {� // another TimelineItem might have inserted in the meantime.� if (!userColor.isEmpty()) {��ad��Æ��B ������������þ��Æ��®����|��z��y��t��ë��é��Ï��—��•��”����+��)��ç ��Ï �� ��J ��H ��G ��B ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������void��}� stylvoid��}� style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);� void��}� style()->drawPrimitive(QStyle::PE_Widget, &ovoid��}� style()->drawPrimitive(Qvoid��}� style()->drawPrimitive(Qvoid��}� style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this);� QPainter p(this);� ������úw.�������������úw.������� opt.init(this);� QStyleOption opt;� ������úw.�������������úw.�������{�TimelineItem::paintEvent(QPaintEvent *)�������úw.���������� +���úw.�������������úw.�������void��}� contextMenu_->exec(event->globalPos());� if (contextMenu_)�{�TimelineItem::contextMenuEvent(QContextMenuEvent *event)�������úw.�������������úw.������� ������úw.�������3������úw.�������void��}� userAvatar_->setImage(room_id_, userid);�� return;� if (userAvatar_ == nullptr)� ������úw. +�������{�ad���������;�������Á��™��P��ü��û��Ï��Ÿ��b��ï ��î ��ª ��b ��6 ��ë��¿��¾��u��F������»��º��„��;��ñ +��ð +��› +��V +��T +��S +��P +��/ +��+ +��· ��] �� ��Ì��q���� ��ó��Ý��Û��·��‰��ˆ��3��¾��½��‹��<��;����Ç������I��H��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� if (ty == mtx::events::MessageType::Emote) {�� if (ty == mtx::events::MessageTy if (ty == mtx::events::MessageTy if (ty == mtx::events::MessageType::Emote) {�� if (ty == mtx::events::MessageTy if (ty == mtx::events::MessageTy if (ty == mt if (ty == mtx::events::MessageTy if (ty == mtx::events::MessageTy if (ty == mt if (ty == mt if (ty == mt if (ty == mtx::events::MessageType::Emote) {�� if (ty == mt if (ty == mtx::events::MessageTy if (ty == mtx::events::MessageType::Emote) {�� QString emptyEventId;� ������úw.�������������úw.�������� formatted_body = body.toHtmlEscaped();� if (formatted_body == body.trimmed().toHtmlEscaped())� // Escape html if the input is not formatted.�� auto formatted_body = utils::markdownToHtml(body);�������úw.������� // Generate the html body to be rendered.�� auto timestamp = QDateTime::currentDateTime();���� ���úw.���������� ���úw.�������'������úw.������� auto displayName = Cache::displayName(room_id_, userid);�������úw.�������� addReplyAction();� ������úw.������� init();� ������úw.�������{� , room_id_{room_id}� , message_type_(ty)� : QWidget(parent)� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool withSender,�!��� +���úw.������� QString body,�������úw.�������$������úw.������� const QString &userid,�"������úw.�������+������úw.�������TimelineItem::TimelineItem(mtx::events::MessageType ty,�������úw.�������������úw. �������5������úw.������� */� * For messages created locally.�/*��}� setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);� parentWidget()->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Maximum);�� statusIndicator_->setFixedHeight(tsFm.height() - tsFm.leading());� statusIndicator_->setFixedWidth(tsFm.height() - tsFm.leading());� statusIndicator_ = new StatusIndicator(this);�� QFontMetrics tsFm(timestampFont_);� ������úw.�������������úw.�������� timestampFont_.setStyleHint(QFont::Monospace);� timestampFont_.setFamily("Monospace");� timestampFont_.setPointSizeF(timestampFont_.pointSizeF() * 0.9);�� contextBtn_->setMenu(contextMenu_);� contextBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));� contextBtn_->setIcon(context_icon);� context_icon.addFile(":/icons/icons/ui/vertical-ellipsis.png");� QIcon context_icon;� ������úw.�������������úw.�������� contextBtn_->setCornerRadius(buttonSize_ / 2);� ������úw. +�������������úw.�������&������úw.������� contextBtn_->setFixedSize(buttonSize_, buttonSize_);� contextBtn_->setToolTip(tr("Options"));� contextBtn_ = new FlatButton(this);�� connect(replyBtn_, &FlatButton::clicked, this, &TimelineItem::replyAction);� replyBtn_->setIconSize(QSize(buttonSize_ / 2, buttonSize_ / 2));� replyBtn_->setIcon(reply_icon);� reply_icon.addFile(":/icons/icons/ui/mail-reply.png");�ad��Í��Ù�����<�������þ��ý��ø������]��\��/��’��…��é ��è ��¹ ��· ��¶ ��— ��’ �� �� ��Ð��p��o����Ò��t��h��f��e��`������Ð +��£ +��w +��B +��8 +�� +��Ú ��› ��K ��� ��Ö��Ã��Â��N��M����Ø��†��m�������ö��ô��ó��î��ž��œ��9��Ù����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� QString userColor = colorGenerating_->result();� ��� QString user QString user QString userColor = colorGenerating_->result();� ������úw.������� ��� QString userColor = colorGenerating_->result();� ������úw.������� ��� QString user QString userColor = colorGenerating_->result();� ������úw.������� ��� QString userColor = colorGenerat QString user QString userColor = colorGenerat QString userColor = colorGenerat QString userColor = colorGenerat QString userColor = colorGenerating_->result();� ������úw.���������� ���úw.������� nhlog::ui()->debug("finishedGeneratingColor for: {}", userName_->toolTip().toStdString());�{�TimelineItem::finishedGeneratingColor()�������úw.�������������úw.�������void��}� }� }� userName_->setStyleSheet("QLabel { color : " + userColor + "; }");� } else {� colorGenerating_->setFuture(QtConcurrent::run(generate));� if (userColor.isEmpty()) {� // If the color is empty, then generate it asynchronously�� QString userColor = Cache::userColor(userName_->toolTip());�������úw.���������� ���úw.�������� };� return userColor;� userName_->toolTip(), backgroundColor().name());� QString userColor = utils::generateContrastingHexColor(� std::function<QString()> generate = [this]() {� // generate user's unique color.� if (userName_) {� ��� ���úw. +������� }� colorGenerating_->waitForFinished();� colorGenerating_->cancel();� if (colorGenerating_->isRunning()) {� // Cancel and wait if we are already generating the color.�{�TimelineItem::refreshAuthorColor()�������úw.�������������úw.�������void��}� });� ChatPage::instance()->currentRoom());� MainWindow::instance()->openUserProfile(user_id,� connect(body_, &TextLabel::userProfileTriggered, this, [](const QString &user_id) {�� body_->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextBrowserInteraction);� body_ = new TextLabel(utils::replaceEmoji(body), this);�{�TimelineItem::generateBody(const QString &body)�������úw.�������������úw.�������"������úw.�������+������úw.�������void�// Only the body is displayed.��}� sendReadReceipt();� ������úw.�������� statusIndicator_->setState(StatusIndicatorState::Received);�������úw. +�������#������úw.�������,������úw.�������B������úw.������� else� statusIndicator_->setState(StatusIndicatorState::Encrypted);�������úw. +�������#������úw.�������,������úw.�������B��� ���úw.������� if (isEncrypted)� ������úw.�������� isReceived_ = true;� ������úw. +�������{�TimelineItem::markReceived(bool isEncrypted)�������úw.�������������úw.�������!������úw.�������void��}�ad��q��©������������à��É��¿��¾��¬��ª��©������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������}� update();�� }� break;� setToolTip("");�ad����C�����I�������ì��¹��£��¡��J��$��#��ò��ð��ï��r����Ä ��i �� �� ��Î��¸��¶��_��9��8����������Ö��Ò��<��û +�� +��L +��8 +�� +��î ��ì ��È ��š ��™ ��h ��g �� ��Ç��\��[��ê��r��q��K�� +��ç��´��v��O��N��(��'��ø��›��š��W��'��&��ÿ��î��À��‡��}��|��I��G��F��C�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/*��}� adjustMessageLayout();� ������úw.������� ������?¬…±�������� /*��}� adjust/*��}� adjust/*��}� adjustMessageLayout();� ��/*��}� adjustMessageLayout();� ��/*��}� adjustMessageLayout();� ������úw.������� ��/*��}� adjust/*��}� adjustMessageLayout();� ��/*��}� adjustMessageLayout();� ��/*��}� adjust/*��}� adjust/*��}� adjust/*��}� adjust/*��}� adjust/*��}� adjust/*��}� adjust/*��}� adjust/*��}� adjust/*��}� adjust/*��}� adjustMessageLayout();� ������úw.�������� }� setupSimpleLayout();�������úw.������� generateBody(formatted_body);� } else {� setUserAvatar(sender);�� setupAvatarLayout(displayName);� generateBody(sender, displayName, formatted_body);�� auto displayName = Cache::displayName(room_id_, sender);�������úw.������� if (with_sender) {� ������úw.�������� generateTimestamp(timestamp);�� timestamp};� utils::descriptiveTime(timestamp),� " sent a notification",� sender,� Cache::displayName(room_id_, sender),� descriptionMsg_ = {event_id_,�� auto body = QString::fromStdString(event.content.body).trimmed().toHtmlEscaped();�������úw.������� auto formatted_body = utils::linkifyMessage(utils::getMessageBody(event).trimmed());�������úw.�������� const auto timestamp = QDateTime::fromMSecsSinceEpoch(event.origin_server_ts);���� ���úw.������� const auto sender = QString::fromStdString(event.sender);�������úw.������� event_id_ = QString::fromStdString(event.event_id);�� markOwnMessagesAsReceived(event.sender);�� addReplyAction();� ������úw.������� init();� ������úw.�������{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Notice)� : QWidget(parent)� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool with_sender,�!������úw.�������TimelineItem::TimelineItem(const mtx::events::RoomEvent<mtx::events::msg::Notice> &event,�������úw.�������������úw.�������T������úw.������� */� * Used to display remote notice messages.�/*��}� markOwnMessagesAsReceived(event.sender);�� video, event, with_sender);� setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Video>, VideoItem>(�{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Video)� : QWidget(parent)� QWidget *parent)�������úw.�������%������úw.������� const QString &room_id,�"������úw.�������+������úw.������� bool with_sender,�!������úw.������� const mtx::events::RoomEvent<mtx::events::msg::Video> &event,�S������úw.�������TimelineItem::TimelineItem(VideoItem *video,�������úw.�������������úw. ���������� ���úw.�������'������úw.��������}� markOwnMessagesAsReceived(event.sender);�� audio, event, with_sender);� setupWidgetLayout<mtx::events::RoomEvent<mtx::events::msg::Audio>, AudioItem>(�{� , room_id_{room_id}� , message_type_(mtx::events::MessageType::Audio)� : QWidget(parent)�ad��Ä��<������������é��ß��Ý��Ü��×��Q��O������á��‹��^��G��ò ��Å ��® ��] ��5 �� ��Í��¥��Ž��<�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� case StatusIndicatorState::Empty:�������úw.�������������?¬…±����� case StatusIndicatorState::Empty case StatusI case StatusIndicatorState::Empty case StatusIndicatorState::Empty case StatusIndicatorState::Empty case StatusIndicatorState::Empty case StatusIndicatorState::Empty case StatusIndicatorState::Empty:�������úw.�������$������úw.������� break;� setToolTip(tr("Sent"));� case StatusIndicatorState::Sent:�������úw.�������$������úw.������� break;� setToolTip(tr("Seen"));� case StatusIndicatorState::Read:�������úw.�������$������úw.������� break;� setToolTip(tr("Delivered"));� case StatusIndicatorState::Received:�������úw.�������$������úw.������� break;� setToolTip(tr("Encrypted"));� case StatusIndicatorState::Encrypted:�������úw.�������$��� ���úw.������� switch (state) {�������úw.�������� state_ = state;� ������úw. +�������������úw.�������{�StatusIndicator::setState(StatusIndicatorState state)�������úw.�������������úw.�������������úw.�������0������úw.�������void��}� }� break;� \ No newline at end of file diff --git a/src/timeline/TimelineItem.cpp b/src/timeline/TimelineItem.cpp index dd3b48c3b2642031109320a4e9a1db7d632eaa77..7916bd80c2efee1efef09b1d3f70772d3ad18802 100644 --- a/src/timeline/TimelineItem.cpp +++ b/src/timeline/TimelineItem.cpp @@ -326,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(); @@ -509,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(); @@ -558,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(); @@ -607,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(); @@ -793,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) @@ -822,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 @@ -911,8 +906,7 @@ TimelineItem::addAvatar() setupAvatarLayout(displayName); - AvatarProvider::resolve( - room_id_, userid, this, [this](const QImage &img) { setUserAvatar(img); }); + setUserAvatar(userid); } void diff --git a/src/timeline/TimelineItem.h b/src/timeline/TimelineItem.h index fe354000e23669a4f2d7a50efcfaab132b49ff15..356976e5931f140a6ec37d714f221b04af233619 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; } @@ -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/ui/Avatar.cpp b/src/ui/Avatar.cpp index 4b4cd27237e8ba5ce734fc837d361e27252b9a02..98bf21c61d4f9a5ffdaf1414fae4ab5fae24b409 100644 --- a/src/ui/Avatar.cpp +++ b/src/ui/Avatar.cpp @@ -1,12 +1,13 @@ #include <QPainter> +#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 +62,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 diff --git a/src/ui/Avatar.h b/src/ui/Avatar.h index 41967af559b3752082524bee06a6650853a41a1f..b9225dd1a667eb1a1eeb07ca8d0fd2763cf0fb10 100644 --- a/src/ui/Avatar.h +++ b/src/ui/Avatar.h @@ -15,13 +15,14 @@ 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 setSize(int size); void setTextColor(const QColor &color); QColor backgroundColor() const; @@ -41,7 +42,8 @@ private: QColor background_color_; QColor text_color_; QIcon icon_; - QImage image_; QPixmap pixmap_; + const std::string room; + const std::string user; int size_; };