Implement media cache

This commit is contained in:
Konstantinos Sideris 2018-04-21 21:18:57 +03:00
parent 54091cf403
commit 29bd8b71d1
6 changed files with 63 additions and 35 deletions

View file

@ -24,23 +24,26 @@
class MatrixClient; class MatrixClient;
class TimelineItem; class TimelineItem;
class Cache;
class AvatarProvider : public QObject class AvatarProvider : public QObject
{ {
Q_OBJECT Q_OBJECT
public: 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 //! The callback is called with the downloaded avatar for the given user
//! or the avatar is downloaded first and then saved for re-use. //! or the avatar is downloaded first and then saved for re-use.
static void resolve(const QString &room_id, static void resolve(const QString &room_id,
const QString &userId, const QString &userId,
QObject *receiver, QObject *receiver,
std::function<void(QImage)> callback); std::function<void(QImage)> callback);
//! Remove all saved data.
static void clear() { avatars_.clear(); };
private: private:
static QSharedPointer<MatrixClient> client_; static QSharedPointer<MatrixClient> client_;
static QHash<QString, QImage> avatars_; static QSharedPointer<Cache> cache_;
}; };

View file

@ -51,8 +51,8 @@ constexpr int CONSENSUS_TIMEOUT = 1000;
constexpr int SHOW_CONTENT_TIMEOUT = 3000; constexpr int SHOW_CONTENT_TIMEOUT = 3000;
constexpr int TYPING_REFRESH_TIMEOUT = 10000; constexpr int TYPING_REFRESH_TIMEOUT = 10000;
Q_DECLARE_METATYPE(mtx::responses::Rooms); Q_DECLARE_METATYPE(mtx::responses::Rooms)
Q_DECLARE_METATYPE(std::vector<std::string>); Q_DECLARE_METATYPE(std::vector<std::string>)
class ChatPage : public QWidget class ChatPage : public QWidget
{ {

View file

@ -15,12 +15,15 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <QBuffer>
#include <QtConcurrent>
#include "AvatarProvider.h" #include "AvatarProvider.h"
#include "Cache.h" #include "Cache.h"
#include "MatrixClient.h" #include "MatrixClient.h"
QSharedPointer<MatrixClient> AvatarProvider::client_; QSharedPointer<MatrixClient> AvatarProvider::client_;
QHash<QString, QImage> AvatarProvider::avatars_; QSharedPointer<Cache> AvatarProvider::cache_;
void void
AvatarProvider::resolve(const QString &room_id, AvatarProvider::resolve(const QString &room_id,
@ -28,21 +31,22 @@ AvatarProvider::resolve(const QString &room_id,
QObject *receiver, QObject *receiver,
std::function<void(QImage)> callback) 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; return;
if (avatars_.contains(key)) { if (avatarUrl.isEmpty())
auto img = avatars_[key]; return;
if (!img.isNull()) { auto data = cache_->image(avatarUrl);
callback(img); if (!data.isNull()) {
return; callback(QImage::fromData(data));
} return;
} }
auto proxy = client_->fetchUserAvatar(Cache::avatarUrl(room_id, user_id)); auto proxy = client_->fetchUserAvatar(avatarUrl);
if (proxy.isNull()) if (proxy.isNull())
return; return;
@ -50,9 +54,16 @@ AvatarProvider::resolve(const QString &room_id,
connect(proxy.data(), connect(proxy.data(),
&DownloadMediaProxy::avatarDownloaded, &DownloadMediaProxy::avatarDownloaded,
receiver, receiver,
[user_id, proxy, callback, key](const QImage &img) { [user_id, proxy, callback, avatarUrl](const QImage &img) {
proxy->deleteLater(); 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); callback(img);
}); });
} }

View file

@ -144,6 +144,9 @@ Cache::saveImage(const QString &url, const QByteArray &image)
QByteArray QByteArray
Cache::image(const QString &url) const Cache::image(const QString &url) const
{ {
if (url.isEmpty())
return QByteArray();
auto key = url.toUtf8(); auto key = url.toUtf8();
try { try {
@ -160,7 +163,7 @@ Cache::image(const QString &url) const
return QByteArray(image.data(), image.size()); return QByteArray(image.data(), image.size());
} catch (const lmdb::error &e) { } catch (const lmdb::error &e) {
qCritical() << "image:" << e.what(); qCritical() << "image:" << e.what() << url;
} }
return QByteArray(); return QByteArray();

View file

@ -399,8 +399,6 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client,
this, this,
&ChatPage::setGroupViewState); &ChatPage::setGroupViewState);
AvatarProvider::init(client);
connect(this, &ChatPage::continueSync, this, [this](const QString &next_batch) { connect(this, &ChatPage::continueSync, this, [this](const QString &next_batch) {
syncTimeoutTimer_->start(SYNC_RETRY_TIMEOUT); syncTimeoutTimer_->start(SYNC_RETRY_TIMEOUT);
client_->setNextBatchToken(next_batch); client_->setNextBatchToken(next_batch);
@ -461,7 +459,6 @@ ChatPage::resetUI()
top_bar_->reset(); top_bar_->reset();
user_info_widget_->reset(); user_info_widget_->reset();
view_manager_->clearAll(); view_manager_->clearAll();
AvatarProvider::clear();
showUnreadMessageNotification(0); showUnreadMessageNotification(0);
} }
@ -497,6 +494,8 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
room_list_->setCache(cache_); room_list_->setCache(cache_);
text_input_->setCache(cache_); text_input_->setCache(cache_);
AvatarProvider::init(client_, cache_);
try { try {
cache_->setup(); cache_->setup();
@ -584,21 +583,30 @@ ChatPage::updateOwnProfileInfo(const QUrl &avatar_url, const QString &display_na
user_info_widget_->setUserId(userid); user_info_widget_->setUserId(userid);
user_info_widget_->setDisplayName(display_name); user_info_widget_->setDisplayName(display_name);
if (avatar_url.isValid()) { if (!avatar_url.isValid())
auto proxy = client_->fetchUserAvatar(avatar_url); 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; 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 void
@ -661,8 +669,8 @@ ChatPage::loadStateFromCache()
try { try {
cache_->populateMembers(); cache_->populateMembers();
emit initializeRoomList(cache_->roomInfo());
emit initializeEmptyViews(cache_->joinedRooms()); emit initializeEmptyViews(cache_->joinedRooms());
emit initializeRoomList(cache_->roomInfo());
} catch (const lmdb::error &e) { } catch (const lmdb::error &e) {
std::cout << "load cache error:" << e.what() << '\n'; std::cout << "load cache error:" << e.what() << '\n';
// TODO Clear cache and restart. // TODO Clear cache and restart.

View file

@ -621,6 +621,9 @@ MatrixClient::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url)
void void
MatrixClient::fetchCommunityAvatar(const QString &communityId, const QUrl &avatar_url) MatrixClient::fetchCommunityAvatar(const QString &communityId, const QUrl &avatar_url)
{ {
if (avatar_url.isEmpty())
return;
QList<QString> url_parts = avatar_url.toString().split("mxc://"); QList<QString> url_parts = avatar_url.toString().split("mxc://");
if (url_parts.size() != 2) { if (url_parts.size() != 2) {