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 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_;
};

View File

@ -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
{

View File

@ -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);
});
}

View File

@ -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();

View File

@ -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.

View File

@ -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) {