Cache room avatars (#139)

fixes #107
This commit is contained in:
Konstantinos Sideris 2017-12-22 00:00:48 +02:00
parent ece20dd917
commit 33f534c6f8
7 changed files with 107 additions and 14 deletions

View File

@ -48,6 +48,9 @@ public:
bool isFormatValid();
void setCurrentFormat();
QByteArray image(const QString &url) const;
void saveImage(const QString &url, const QByteArray &data);
private:
void setNextBatchToken(lmdb::txn &txn, const QString &token);
void insertRoomState(lmdb::txn &txn, const QString &roomid, const RoomState &state);
@ -56,6 +59,7 @@ private:
lmdb::dbi stateDb_;
lmdb::dbi roomDb_;
lmdb::dbi invitesDb_;
lmdb::dbi imagesDb_;
bool isMounted_;

View File

@ -98,7 +98,10 @@ signals:
void fileUploaded(const QString &roomid, const QString &filename, const QString &url);
void audioUploaded(const QString &roomid, const QString &filename, const QString &url);
void roomAvatarRetrieved(const QString &roomid, const QPixmap &img);
void roomAvatarRetrieved(const QString &roomid,
const QPixmap &img,
const QString &url,
const QByteArray &data);
void userAvatarRetrieved(const QString &userId, const QImage &img);
void ownAvatarRetrieved(const QPixmap &img);
void imageDownloaded(const QString &event_id, const QPixmap &img);

View File

@ -30,6 +30,7 @@
class LeaveRoomDialog;
class MatrixClient;
class Cache;
class OverlayModal;
class RoomInfoListItem;
class RoomSettings;
@ -45,6 +46,7 @@ public:
RoomList(QSharedPointer<MatrixClient> client, QWidget *parent = 0);
~RoomList();
void setCache(QSharedPointer<Cache> cache) { cache_ = cache; }
void setInitialRooms(const QMap<QString, QSharedPointer<RoomSettings>> &settings,
const QMap<QString, RoomState> &states);
void sync(const QMap<QString, RoomState> &states,
@ -52,6 +54,7 @@ public:
void syncInvites(const std::map<std::string, mtx::responses::InvitedRoom> &rooms);
void clear();
void updateAvatar(const QString &room_id, const QString &url);
void addRoom(const QMap<QString, QSharedPointer<RoomSettings>> &settings,
const RoomState &state,
@ -64,6 +67,7 @@ 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);
public slots:
void updateRoomAvatar(const QString &roomid, const QPixmap &img);
@ -96,4 +100,5 @@ private:
QMap<QString, QSharedPointer<RoomInfoListItem>> rooms_;
QSharedPointer<MatrixClient> client_;
QSharedPointer<Cache> cache_;
};

View File

@ -17,6 +17,7 @@
#include <stdexcept>
#include <QByteArray>
#include <QDebug>
#include <QFile>
#include <QStandardPaths>
@ -34,6 +35,7 @@ Cache::Cache(const QString &userId)
, stateDb_{0}
, roomDb_{0}
, invitesDb_{0}
, imagesDb_{0}
, isMounted_{false}
, userId_{userId}
{}
@ -54,7 +56,7 @@ Cache::setup()
bool isInitial = !QFile::exists(statePath);
env_ = lmdb::env::create();
env_.set_mapsize(128UL * 1024UL * 1024UL); /* 128 MB */
env_.set_mapsize(256UL * 1024UL * 1024UL); /* 256 MB */
env_.set_max_dbs(1024UL);
if (isInitial) {
@ -91,12 +93,60 @@ Cache::setup()
stateDb_ = lmdb::dbi::open(txn, "state", MDB_CREATE);
roomDb_ = lmdb::dbi::open(txn, "rooms", MDB_CREATE);
invitesDb_ = lmdb::dbi::open(txn, "invites", MDB_CREATE);
imagesDb_ = lmdb::dbi::open(txn, "images", MDB_CREATE);
txn.commit();
isMounted_ = true;
}
void
Cache::saveImage(const QString &url, const QByteArray &image)
{
if (!isMounted_)
return;
auto key = url.toUtf8();
try {
auto txn = lmdb::txn::begin(env_);
lmdb::dbi_put(txn,
imagesDb_,
lmdb::val(key.data(), key.size()),
lmdb::val(image.data(), image.size()));
txn.commit();
} catch (const lmdb::error &e) {
qCritical() << "saveImage:" << e.what();
}
}
QByteArray
Cache::image(const QString &url) const
{
auto key = url.toUtf8();
try {
auto txn = lmdb::txn::begin(env_, nullptr, MDB_RDONLY);
lmdb::val image;
bool res = lmdb::dbi_get(txn, imagesDb_, lmdb::val(key.data(), key.size()), image);
txn.commit();
if (!res)
return QByteArray();
return QByteArray(image.data(), image.size());
} catch (const lmdb::error &e) {
qCritical() << "image:" << e.what();
}
return QByteArray();
}
void
Cache::setState(const QString &nextBatchToken, const QMap<QString, RoomState> &states)
{

View File

@ -232,8 +232,7 @@ ChatPage::ChatPage(QSharedPointer<MatrixClient> client, QWidget *parent)
view_manager_->queueAudioMessage(roomid, filename, url);
});
connect(
client_.data(), &MatrixClient::roomAvatarRetrieved, this, &ChatPage::updateTopBarAvatar);
connect(room_list_, &RoomList::roomAvatarChanged, this, &ChatPage::updateTopBarAvatar);
connect(client_.data(),
&MatrixClient::initialSyncCompleted,
@ -353,6 +352,7 @@ ChatPage::bootstrap(QString userid, QString homeserver, QString token)
client_->getOwnProfile();
cache_ = QSharedPointer<Cache>(new Cache(userid));
room_list_->setCache(cache_);
try {
cache_->setup();

View File

@ -468,7 +468,7 @@ MatrixClient::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url)
QNetworkRequest avatar_request(endpoint);
QNetworkReply *reply = get(avatar_request);
connect(reply, &QNetworkReply::finished, this, [this, reply, roomid]() {
connect(reply, &QNetworkReply::finished, this, [this, reply, roomid, avatar_url]() {
reply->deleteLater();
int status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
@ -486,7 +486,7 @@ MatrixClient::fetchRoomAvatar(const QString &roomid, const QUrl &avatar_url)
QPixmap pixmap;
pixmap.loadFromData(img);
emit roomAvatarRetrieved(roomid, pixmap);
emit roomAvatarRetrieved(roomid, pixmap, avatar_url.toString(), img);
});
}

View File

@ -15,9 +15,11 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <QBuffer>
#include <QDebug>
#include <QObject>
#include "Cache.h"
#include "MainWindow.h"
#include "MatrixClient.h"
#include "OverlayModal.h"
@ -53,9 +55,17 @@ RoomList::RoomList(QSharedPointer<MatrixClient> client, QWidget *parent)
topLayout_->addWidget(scrollArea_);
connect(client_.data(),
SIGNAL(roomAvatarRetrieved(const QString &, const QPixmap &)),
&MatrixClient::roomAvatarRetrieved,
this,
SLOT(updateRoomAvatar(const QString &, const QPixmap &)));
[=](const QString &room_id,
const QPixmap &img,
const QString &url,
const QByteArray &data) {
if (!cache_.isNull())
cache_->saveImage(url, data);
updateRoomAvatar(room_id, img);
});
}
RoomList::~RoomList() {}
@ -79,12 +89,33 @@ RoomList::addRoom(const QMap<QString, QSharedPointer<RoomSettings>> &settings,
rooms_.insert(room_id, QSharedPointer<RoomInfoListItem>(room_item));
if (!state.getAvatar().toString().isEmpty())
client_->fetchRoomAvatar(room_id, state.getAvatar());
updateAvatar(room_id, state.getAvatar().toString());
int pos = contentsLayout_->count() - 1;
contentsLayout_->insertWidget(pos, room_item);
}
void
RoomList::updateAvatar(const QString &room_id, const QString &url)
{
if (url.isEmpty())
return;
QByteArray savedImgData;
if (!cache_.isNull())
savedImgData = cache_->image(url);
if (savedImgData.isEmpty()) {
client_->fetchRoomAvatar(room_id, url);
} else {
QPixmap img;
img.loadFromData(savedImgData);
updateRoomAvatar(room_id, img);
}
}
void
RoomList::removeRoom(const QString &room_id, bool reset)
{
@ -194,7 +225,7 @@ RoomList::sync(const QMap<QString, RoomState> &states,
auto new_avatar = state.getAvatar();
if (current_avatar != new_avatar && !new_avatar.toString().isEmpty())
client_->fetchRoomAvatar(room_id, new_avatar);
updateAvatar(room_id, new_avatar.toString());
room->setState(state);
}
@ -246,6 +277,9 @@ RoomList::updateRoomAvatar(const QString &roomid, const QPixmap &img)
}
rooms_.value(roomid)->setAvatar(img.toImage());
// Used to inform other widgets for the new image data.
emit roomAvatarChanged(roomid, img);
}
void
@ -308,10 +342,7 @@ RoomList::addInvitedRoom(const QString &room_id, const mtx::responses::InvitedRo
rooms_.insert(room_id, QSharedPointer<RoomInfoListItem>(room_item));
auto avatarUrl = QString::fromStdString(room.avatar());
if (!avatarUrl.isEmpty())
client_->fetchRoomAvatar(room_id, avatarUrl);
updateAvatar(room_id, QString::fromStdString(room.avatar()));
int pos = contentsLayout_->count() - 1;
contentsLayout_->insertWidget(pos, room_item);