diff --git a/include/AvatarProvider.h b/include/AvatarProvider.h index dabd609c7e0f58f9e461880fb50cc786a8f1b246..fd5f15c457c367b4dea94266fad262e3e99e4a33 100644 --- a/include/AvatarProvider.h +++ b/include/AvatarProvider.h @@ -24,23 +24,26 @@ class MatrixClient; class TimelineItem; +class Cache; class AvatarProvider : public QObject { Q_OBJECT public: - static void init(QSharedPointer<MatrixClient> client) { client_ = client; } + static void init(QSharedPointer<MatrixClient> client, QSharedPointer<Cache> cache) + { + client_ = client; + cache_ = cache; + } //! The callback is called with the downloaded avatar for the given user //! or the avatar is downloaded first and then saved for re-use. static void resolve(const QString &room_id, const QString &userId, QObject *receiver, std::function<void(QImage)> callback); - //! Remove all saved data. - static void clear() { avatars_.clear(); }; private: static QSharedPointer<MatrixClient> client_; - static QHash<QString, QImage> avatars_; + static QSharedPointer<Cache> cache_; }; diff --git a/include/ChatPage.h b/include/ChatPage.h index b3b379cb72c07374b8142ec9997035ad6a9e4f6f..d0be9ce1d77433ad75f05e40a54b9dcc9060e660 100644 --- a/include/ChatPage.h +++ b/include/ChatPage.h @@ -51,8 +51,8 @@ constexpr int CONSENSUS_TIMEOUT = 1000; constexpr int SHOW_CONTENT_TIMEOUT = 3000; constexpr int TYPING_REFRESH_TIMEOUT = 10000; -Q_DECLARE_METATYPE(mtx::responses::Rooms); -Q_DECLARE_METATYPE(std::vector<std::string>); +Q_DECLARE_METATYPE(mtx::responses::Rooms) +Q_DECLARE_METATYPE(std::vector<std::string>) class ChatPage : public QWidget { diff --git a/src/AvatarProvider.cc b/src/AvatarProvider.cc index 9f861fdb6ab9bd0be7e01aa626276c18b46a73b8..c77452396979e540f7c253c57c54491dd5b7d4e5 100644 --- a/src/AvatarProvider.cc +++ b/src/AvatarProvider.cc @@ -15,12 +15,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include <QBuffer> +#include <QtConcurrent> + #include "AvatarProvider.h" #include "Cache.h" #include "MatrixClient.h" QSharedPointer<MatrixClient> AvatarProvider::client_; -QHash<QString, QImage> AvatarProvider::avatars_; +QSharedPointer<Cache> AvatarProvider::cache_; void AvatarProvider::resolve(const QString &room_id, @@ -28,21 +31,22 @@ AvatarProvider::resolve(const QString &room_id, QObject *receiver, std::function<void(QImage)> callback) { - const auto key = QString("%1 %2").arg(room_id).arg(user_id); + const auto key = QString("%1 %2").arg(room_id).arg(user_id); + const auto avatarUrl = Cache::avatarUrl(room_id, user_id); - if (!Cache::AvatarUrls.contains(key)) + if (!Cache::AvatarUrls.contains(key) || cache_.isNull()) return; - if (avatars_.contains(key)) { - auto img = avatars_[key]; + if (avatarUrl.isEmpty()) + return; - if (!img.isNull()) { - callback(img); - return; - } + auto data = cache_->image(avatarUrl); + if (!data.isNull()) { + callback(QImage::fromData(data)); + return; } - auto proxy = client_->fetchUserAvatar(Cache::avatarUrl(room_id, user_id)); + auto proxy = client_->fetchUserAvatar(avatarUrl); if (proxy.isNull()) return; @@ -50,9 +54,16 @@ AvatarProvider::resolve(const QString &room_id, connect(proxy.data(), &DownloadMediaProxy::avatarDownloaded, receiver, - [user_id, proxy, callback, key](const QImage &img) { + [user_id, proxy, callback, avatarUrl](const QImage &img) { proxy->deleteLater(); - avatars_.insert(key, img); + QtConcurrent::run([img, avatarUrl]() { + QByteArray data; + QBuffer buffer(&data); + buffer.open(QIODevice::WriteOnly); + img.save(&buffer, "PNG"); + + cache_->saveImage(avatarUrl, data); + }); callback(img); }); } diff --git a/src/Cache.cc b/src/Cache.cc index 2afd3080b14db15b447e8b864a81d77825f699a3..1155a40a1b9485823de4c1624792876faed150da 100644 --- a/src/Cache.cc +++ b/src/Cache.cc @@ -144,6 +144,9 @@ Cache::saveImage(const QString &url, const QByteArray &image) QByteArray Cache::image(const QString &url) const { + if (url.isEmpty()) + return QByteArray(); + auto key = url.toUtf8(); try { @@ -160,7 +163,7 @@ Cache::image(const QString &url) const return QByteArray(image.data(), image.size()); } catch (const lmdb::error &e) { - qCritical() << "image:" << e.what(); + qCritical() << "image:" << e.what() << url; } return QByteArray(); diff --git a/src/ChatPage.cc b/src/ChatPage.cc index a37cc9f0d1f5669c9f5824345c13508f8ef63fc9..280ae399ac3a678f71d9d8eb280f271f7b7377f5 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -399,8 +399,6 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, this, &ChatPage::setGroupViewState); - AvatarProvider::init(client); - connect(this, &ChatPage::continueSync, this, [this](const QString &next_batch) { syncTimeoutTimer_->start(SYNC_RETRY_TIMEOUT); client_->setNextBatchToken(next_batch); @@ -461,7 +459,6 @@ ChatPage::resetUI() top_bar_->reset(); user_info_widget_->reset(); view_manager_->clearAll(); - AvatarProvider::clear(); showUnreadMessageNotification(0); } @@ -497,6 +494,8 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token) room_list_->setCache(cache_); text_input_->setCache(cache_); + AvatarProvider::init(client_, cache_); + try { cache_->setup(); @@ -584,21 +583,30 @@ ChatPage::updateOwnProfileInfo(const QUrl &avatar_url, const QString &display_na user_info_widget_->setUserId(userid); user_info_widget_->setDisplayName(display_name); - if (avatar_url.isValid()) { - auto proxy = client_->fetchUserAvatar(avatar_url); + if (!avatar_url.isValid()) + return; - if (proxy.isNull()) + if (!cache_.isNull()) { + auto data = cache_->image(avatar_url.toString()); + if (!data.isNull()) { + user_info_widget_->setAvatar(QImage::fromData(data)); return; - - proxy->setParent(this); - connect(proxy.data(), - &DownloadMediaProxy::avatarDownloaded, - this, - [this, proxy](const QImage &img) { - proxy->deleteLater(); - user_info_widget_->setAvatar(img); - }); + } } + + auto proxy = client_->fetchUserAvatar(avatar_url); + + if (proxy.isNull()) + return; + + proxy->setParent(this); + connect(proxy.data(), + &DownloadMediaProxy::avatarDownloaded, + this, + [this, proxy](const QImage &img) { + proxy->deleteLater(); + user_info_widget_->setAvatar(img); + }); } void @@ -661,8 +669,8 @@ ChatPage::loadStateFromCache() try { cache_->populateMembers(); - emit initializeRoomList(cache_->roomInfo()); emit initializeEmptyViews(cache_->joinedRooms()); + emit initializeRoomList(cache_->roomInfo()); } catch (const lmdb::error &e) { std::cout << "load cache error:" << e.what() << '\n'; // TODO Clear cache and restart. diff --git a/src/MatrixClient.cc b/src/MatrixClient.cc index 17a34d968fd67e5a34d625f46d40bf6aa24ffb2c..e95f8b970e58bde4cef20aec5de013b9c075abf5 100644 --- a/src/MatrixClient.cc +++ b/src/MatrixClient.cc @@ -621,6 +621,9 @@ MatrixClient::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url) void MatrixClient::fetchCommunityAvatar(const QString &communityId, const QUrl &avatar_url) { + if (avatar_url.isEmpty()) + return; + QList<QString> url_parts = avatar_url.toString().split("mxc://"); if (url_parts.size() != 2) {