From 29bd8b71d183e852c43c1bac7cd4c9020f1a4877 Mon Sep 17 00:00:00 2001 From: Konstantinos Sideris Date: Sat, 21 Apr 2018 21:18:57 +0300 Subject: [PATCH] Implement media cache --- include/AvatarProvider.h | 11 +++++++---- include/ChatPage.h | 4 ++-- src/AvatarProvider.cc | 35 +++++++++++++++++++++++------------ src/Cache.cc | 5 ++++- src/ChatPage.cc | 40 ++++++++++++++++++++++++---------------- src/MatrixClient.cc | 3 +++ 6 files changed, 63 insertions(+), 35 deletions(-) diff --git a/include/AvatarProvider.h b/include/AvatarProvider.h index dabd609c..fd5f15c4 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 client) { client_ = client; } + static void init(QSharedPointer client, QSharedPointer 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 callback); - //! Remove all saved data. - static void clear() { avatars_.clear(); }; private: static QSharedPointer client_; - static QHash avatars_; + static QSharedPointer cache_; }; diff --git a/include/ChatPage.h b/include/ChatPage.h index b3b379cb..d0be9ce1 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); +Q_DECLARE_METATYPE(mtx::responses::Rooms) +Q_DECLARE_METATYPE(std::vector) class ChatPage : public QWidget { diff --git a/src/AvatarProvider.cc b/src/AvatarProvider.cc index 9f861fdb..c7745239 100644 --- a/src/AvatarProvider.cc +++ b/src/AvatarProvider.cc @@ -15,12 +15,15 @@ * along with this program. If not, see . */ +#include +#include + #include "AvatarProvider.h" #include "Cache.h" #include "MatrixClient.h" QSharedPointer AvatarProvider::client_; -QHash AvatarProvider::avatars_; +QSharedPointer AvatarProvider::cache_; void AvatarProvider::resolve(const QString &room_id, @@ -28,21 +31,22 @@ AvatarProvider::resolve(const QString &room_id, QObject *receiver, std::function 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 2afd3080..1155a40a 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 a37cc9f0..280ae399 100644 --- a/src/ChatPage.cc +++ b/src/ChatPage.cc @@ -399,8 +399,6 @@ ChatPage::ChatPage(QSharedPointer 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 17a34d96..e95f8b97 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 url_parts = avatar_url.toString().split("mxc://"); if (url_parts.size() != 2) {