offline avatars in mucs
This commit is contained in:
parent
29c7d31c89
commit
21c7d65027
16 changed files with 225 additions and 45 deletions
|
@ -1063,6 +1063,7 @@ void Core::Account::onContactHistoryResponse(const std::list<Shared::Message>& l
|
|||
|
||||
void Core::Account::onClientError(QXmppClient::Error err)
|
||||
{
|
||||
qDebug() << "Error";
|
||||
QString errorText;
|
||||
QString errorType;
|
||||
switch (err) {
|
||||
|
@ -1140,6 +1141,9 @@ void Core::Account::onClientError(QXmppClient::Error err)
|
|||
case QXmppStanza::Error::UnexpectedRequest:
|
||||
errorText = "Unexpected request";
|
||||
break;
|
||||
case QXmppStanza::Error::PolicyViolation:
|
||||
errorText = "Policy violation";
|
||||
break;
|
||||
}
|
||||
|
||||
errorType = "Client stream error";
|
||||
|
@ -1367,7 +1371,8 @@ void Core::Account::addNewRoom(const QString& jid, const QString& nick, const QS
|
|||
{"autoJoin", conf->getAutoJoin()},
|
||||
{"joined", conf->getJoined()},
|
||||
{"nick", conf->getNick()},
|
||||
{"name", conf->getName()}
|
||||
{"name", conf->getName()},
|
||||
{"avatars", conf->getAllAvatars()}
|
||||
};
|
||||
|
||||
Archive::AvatarInfo info;
|
||||
|
|
|
@ -675,7 +675,7 @@ bool Core::Archive::dropAvatar(const std::string& resource)
|
|||
}
|
||||
}
|
||||
|
||||
bool Core::Archive::setAvatar(const QByteArray& data, bool generated, const QString& resource)
|
||||
bool Core::Archive::setAvatar(const QByteArray& data, AvatarInfo& newInfo, bool generated, const QString& resource)
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("setAvatar", jid.toStdString());
|
||||
|
@ -726,7 +726,9 @@ bool Core::Archive::setAvatar(const QByteArray& data, bool generated, const QStr
|
|||
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
QByteArray value;
|
||||
AvatarInfo newInfo(ext, newHash, generated);
|
||||
newInfo.type = ext;
|
||||
newInfo.hash = newHash;
|
||||
newInfo.autogenerated = generated;
|
||||
newInfo.serialize(&value);
|
||||
lmdbKey.mv_size = res.size();
|
||||
lmdbKey.mv_data = (char*)res.c_str();
|
||||
|
@ -802,6 +804,33 @@ bool Core::Archive::readAvatarInfo(Core::Archive::AvatarInfo& target, const std:
|
|||
}
|
||||
}
|
||||
|
||||
void Core::Archive::readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const
|
||||
{
|
||||
if (!opened) {
|
||||
throw Closed("readAllResourcesAvatars", jid.toStdString());
|
||||
}
|
||||
|
||||
int rc;
|
||||
MDB_val lmdbKey, lmdbData;
|
||||
MDB_txn *txn;
|
||||
MDB_cursor* cursor;
|
||||
mdb_txn_begin(environment, NULL, MDB_RDONLY, &txn);
|
||||
mdb_cursor_open(txn, avatars, &cursor);
|
||||
rc = mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_FIRST);
|
||||
|
||||
do {
|
||||
std::string sId((char*)lmdbKey.mv_data, lmdbKey.mv_size);
|
||||
QString res(sId.c_str());
|
||||
if (res != jid) {
|
||||
data.emplace(res, AvatarInfo());
|
||||
data[res].deserialize((char*)lmdbData.mv_data, lmdbData.mv_size);
|
||||
}
|
||||
} while (mdb_cursor_get(cursor, &lmdbKey, &lmdbData, MDB_NEXT) == 0);
|
||||
|
||||
mdb_cursor_close(cursor);
|
||||
mdb_txn_abort(txn);
|
||||
}
|
||||
|
||||
Core::Archive::AvatarInfo Core::Archive::getAvatarInfo(const QString& resource) const
|
||||
{
|
||||
if (!opened) {
|
||||
|
|
|
@ -56,9 +56,10 @@ public:
|
|||
std::list<Shared::Message> getBefore(int count, const QString& id);
|
||||
bool isFromTheBeginning();
|
||||
void setFromTheBeginning(bool is);
|
||||
bool setAvatar(const QByteArray& data, bool generated = false, const QString& resource = "");
|
||||
bool setAvatar(const QByteArray& data, AvatarInfo& info, bool generated = false, const QString& resource = "");
|
||||
AvatarInfo getAvatarInfo(const QString& resource = "") const;
|
||||
bool readAvatarInfo(AvatarInfo& target, const QString& resource = "") const;
|
||||
void readAllResourcesAvatars(std::map<QString, AvatarInfo>& data) const;
|
||||
|
||||
public:
|
||||
const QString jid;
|
||||
|
|
|
@ -25,7 +25,8 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
|
|||
nick(p_nick),
|
||||
room(p_room),
|
||||
joined(false),
|
||||
autoJoin(p_autoJoin)
|
||||
autoJoin(p_autoJoin),
|
||||
exParticipants()
|
||||
{
|
||||
muc = true;
|
||||
name = p_name;
|
||||
|
@ -44,6 +45,8 @@ Core::Conference::Conference(const QString& p_jid, const QString& p_account, boo
|
|||
if (autoJoin) {
|
||||
room->join();
|
||||
}
|
||||
|
||||
archive->readAllResourcesAvatars(exParticipants);
|
||||
}
|
||||
|
||||
Core::Conference::~Conference()
|
||||
|
@ -140,8 +143,8 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
|
|||
resource = "";
|
||||
}
|
||||
|
||||
Archive::AvatarInfo info;
|
||||
bool hasAvatar = readAvatarInfo(info, resource);
|
||||
std::map<QString, Archive::AvatarInfo>::const_iterator itr = exParticipants.find(resource);
|
||||
bool hasAvatar = itr != exParticipants.end();
|
||||
|
||||
if (resource.size() > 0) {
|
||||
QDateTime lastInteraction = pres.lastUserInteraction();
|
||||
|
@ -158,12 +161,12 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
|
|||
};
|
||||
|
||||
if (hasAvatar) {
|
||||
if (info.autogenerated) {
|
||||
if (itr->second.autogenerated) {
|
||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::valid));
|
||||
} else {
|
||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::autocreated));
|
||||
}
|
||||
cData.insert("avatarPath", avatarPath(resource) + "." + info.type);
|
||||
cData.insert("avatarPath", avatarPath(resource) + "." + itr->second.type);
|
||||
} else {
|
||||
cData.insert("avatarState", static_cast<uint>(Shared::Avatar::empty));
|
||||
cData.insert("avatarPath", "");
|
||||
|
@ -179,14 +182,14 @@ void Core::Conference::onRoomParticipantAdded(const QString& p_name)
|
|||
case QXmppPresence::VCardUpdateNotReady: //let's say the photo didn't change here
|
||||
break;
|
||||
case QXmppPresence::VCardUpdateNoPhoto: { //there is no photo, need to drop if any
|
||||
if (!hasAvatar || !info.autogenerated) {
|
||||
if (!hasAvatar || !itr->second.autogenerated) {
|
||||
setAutoGeneratedAvatar(resource);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case QXmppPresence::VCardUpdateValidPhoto:{ //there is a photo, need to load
|
||||
if (hasAvatar) {
|
||||
if (info.autogenerated || info.hash != pres.photoHash()) {
|
||||
if (itr->second.autogenerated || itr->second.hash != pres.photoHash()) {
|
||||
emit requestVCard(p_name);
|
||||
}
|
||||
} else {
|
||||
|
@ -285,30 +288,46 @@ void Core::Conference::handlePresence(const QXmppPresence& pres)
|
|||
|
||||
bool Core::Conference::setAutoGeneratedAvatar(const QString& resource)
|
||||
{
|
||||
bool result = RosterItem::setAutoGeneratedAvatar(resource);
|
||||
Archive::AvatarInfo newInfo;
|
||||
bool result = RosterItem::setAutoGeneratedAvatar(newInfo, resource);
|
||||
if (result && resource.size() != 0) {
|
||||
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
||||
if (itr == exParticipants.end()) {
|
||||
exParticipants.insert(std::make_pair(resource, newInfo));
|
||||
} else {
|
||||
itr->second = newInfo;
|
||||
}
|
||||
emit changeParticipant(resource, {
|
||||
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
||||
{"avatarPath", avatarPath(resource) + ".png"}
|
||||
{"avatarPath", avatarPath(resource) + "." + newInfo.type}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Core::Conference::setAvatar(const QByteArray& data, const QString& resource)
|
||||
bool Core::Conference::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource)
|
||||
{
|
||||
bool result = RosterItem::setAvatar(data, resource);
|
||||
bool result = RosterItem::setAvatar(data, info, resource);
|
||||
if (result && resource.size() != 0) {
|
||||
if (data.size() > 0) {
|
||||
QMimeDatabase db;
|
||||
QMimeType type = db.mimeTypeForData(data);
|
||||
QString ext = type.preferredSuffix();
|
||||
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
||||
if (itr == exParticipants.end()) {
|
||||
exParticipants.insert(std::make_pair(resource, info));
|
||||
} else {
|
||||
itr->second = info;
|
||||
}
|
||||
|
||||
emit changeParticipant(resource, {
|
||||
{"avatarState", static_cast<uint>(Shared::Avatar::autocreated)},
|
||||
{"avatarPath", avatarPath(resource) + "." + ext}
|
||||
{"avatarPath", avatarPath(resource) + "." + info.type}
|
||||
});
|
||||
} else {
|
||||
std::map<QString, Archive::AvatarInfo>::iterator itr = exParticipants.find(resource);
|
||||
if (itr != exParticipants.end()) {
|
||||
exParticipants.erase(itr);
|
||||
}
|
||||
|
||||
emit changeParticipant(resource, {
|
||||
{"avatarState", static_cast<uint>(Shared::Avatar::empty)},
|
||||
{"avatarPath", ""}
|
||||
|
@ -333,3 +352,12 @@ Shared::VCard Core::Conference::handleResponseVCard(const QXmppVCardIq& card, co
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
QMap<QString, QVariant> Core::Conference::getAllAvatars() const
|
||||
{
|
||||
QMap<QString, QVariant> result;
|
||||
for (const std::pair<QString, Archive::AvatarInfo>& pair : exParticipants) {
|
||||
result.insert(pair.first, avatarPath(pair.first) + "." + pair.second.type);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -19,9 +19,15 @@
|
|||
#ifndef CORE_CONFERENCE_H
|
||||
#define CORE_CONFERENCE_H
|
||||
|
||||
#include "rosteritem.h"
|
||||
#include <QDir>
|
||||
|
||||
#include <QXmppMucManager.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "rosteritem.h"
|
||||
#include "shared/global.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
||||
|
@ -46,8 +52,8 @@ public:
|
|||
void setAutoJoin(bool p_autoJoin);
|
||||
void handlePresence(const QXmppPresence & pres) override;
|
||||
bool setAutoGeneratedAvatar(const QString& resource = "") override;
|
||||
bool setAvatar(const QByteArray &data, const QString &resource = "") override;
|
||||
Shared::VCard handleResponseVCard(const QXmppVCardIq & card, const QString &resource) override;
|
||||
QMap<QString, QVariant> getAllAvatars() const;
|
||||
|
||||
signals:
|
||||
void nickChanged(const QString& nick);
|
||||
|
@ -58,11 +64,16 @@ signals:
|
|||
void changeParticipant(const QString& name, const QMap<QString, QVariant>& data);
|
||||
void removeParticipant(const QString& name);
|
||||
|
||||
protected:
|
||||
bool setAvatar(const QByteArray &data, Archive::AvatarInfo& info, const QString &resource = "") override;
|
||||
|
||||
private:
|
||||
QString nick;
|
||||
QXmppMucRoom* room;
|
||||
bool joined;
|
||||
bool autoJoin;
|
||||
std::map<QString, Archive::AvatarInfo> exParticipants;
|
||||
static const std::set<QString> supportedList;
|
||||
|
||||
private slots:
|
||||
void onRoomJoined();
|
||||
|
|
|
@ -410,28 +410,37 @@ bool Core::RosterItem::isMuc() const
|
|||
|
||||
QString Core::RosterItem::avatarPath(const QString& resource) const
|
||||
{
|
||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
path += "/" + account + "/" + jid + "/" + (resource.size() == 0 ? jid : resource);
|
||||
QString path = folderPath() + "/" + (resource.size() == 0 ? jid : resource);
|
||||
return path;
|
||||
}
|
||||
|
||||
bool Core::RosterItem::setAvatar(const QByteArray& data, const QString& resource)
|
||||
QString Core::RosterItem::folderPath() const
|
||||
{
|
||||
bool result = archive->setAvatar(data, false, resource);
|
||||
QString path(QStandardPaths::writableLocation(QStandardPaths::CacheLocation));
|
||||
path += "/" + account + "/" + jid;
|
||||
return path;
|
||||
}
|
||||
|
||||
bool Core::RosterItem::setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource)
|
||||
{
|
||||
bool result = archive->setAvatar(data, info, false, resource);
|
||||
if (resource.size() == 0 && result) {
|
||||
if (data.size() == 0) {
|
||||
emit avatarChanged(Shared::Avatar::empty, "");
|
||||
} else {
|
||||
QMimeDatabase db;
|
||||
QMimeType type = db.mimeTypeForData(data);
|
||||
QString ext = type.preferredSuffix();
|
||||
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + ext);
|
||||
emit avatarChanged(Shared::Avatar::valid, avatarPath(resource) + "." + info.type);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
|
||||
{
|
||||
Archive::AvatarInfo info;
|
||||
return setAutoGeneratedAvatar(info, resource);
|
||||
}
|
||||
|
||||
bool Core::RosterItem::setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource)
|
||||
{
|
||||
QImage image(96, 96, QImage::Format_ARGB32_Premultiplied);
|
||||
QPainter painter(&image);
|
||||
|
@ -453,7 +462,7 @@ bool Core::RosterItem::setAutoGeneratedAvatar(const QString& resource)
|
|||
stream.open(QBuffer::WriteOnly);
|
||||
image.save(&stream, "PNG");
|
||||
stream.close();
|
||||
bool result = archive->setAvatar(arr, true, resource);
|
||||
bool result = archive->setAvatar(arr, info, true, resource);
|
||||
if (resource.size() == 0 && result) {
|
||||
emit avatarChanged(Shared::Avatar::autocreated, avatarPath(resource) + ".png");
|
||||
}
|
||||
|
@ -468,6 +477,7 @@ bool Core::RosterItem::readAvatarInfo(Archive::AvatarInfo& target, const QString
|
|||
Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, const QString& resource)
|
||||
{
|
||||
Archive::AvatarInfo info;
|
||||
Archive::AvatarInfo newInfo;
|
||||
bool hasAvatar = readAvatarInfo(info, resource);
|
||||
|
||||
QByteArray ava = card.photo();
|
||||
|
@ -477,13 +487,10 @@ Shared::VCard Core::RosterItem::handleResponseVCard(const QXmppVCardIq& card, co
|
|||
QString path = "";
|
||||
|
||||
if (ava.size() > 0) {
|
||||
bool changed = setAvatar(ava, resource);
|
||||
bool changed = setAvatar(ava, newInfo, resource);
|
||||
if (changed) {
|
||||
type = Shared::Avatar::valid;
|
||||
QMimeDatabase db;
|
||||
QMimeType type = db.mimeTypeForData(ava);
|
||||
QString ext = type.preferredSuffix();
|
||||
path = avatarPath(resource) + "." + ext;
|
||||
path = avatarPath(resource) + "." + newInfo.type;
|
||||
} else if (hasAvatar) {
|
||||
if (info.autogenerated) {
|
||||
type = Shared::Avatar::autocreated;
|
||||
|
|
|
@ -69,8 +69,8 @@ public:
|
|||
void requestHistory(int count, const QString& before);
|
||||
void requestFromEmpty(int count, const QString& before);
|
||||
QString avatarPath(const QString& resource = "") const;
|
||||
QString folderPath() const;
|
||||
bool readAvatarInfo(Archive::AvatarInfo& target, const QString& resource = "") const;
|
||||
virtual bool setAvatar(const QByteArray& data, const QString& resource = "");
|
||||
virtual bool setAutoGeneratedAvatar(const QString& resource = "");
|
||||
virtual Shared::VCard handleResponseVCard(const QXmppVCardIq& card, const QString& resource);
|
||||
virtual void handlePresence(const QXmppPresence& pres) = 0;
|
||||
|
@ -89,6 +89,10 @@ public:
|
|||
const QString jid;
|
||||
const QString account;
|
||||
|
||||
protected:
|
||||
virtual bool setAvatar(const QByteArray& data, Archive::AvatarInfo& info, const QString& resource = "");
|
||||
virtual bool setAutoGeneratedAvatar(Archive::AvatarInfo& info, const QString& resource = "");
|
||||
|
||||
protected:
|
||||
QString name;
|
||||
ArchiveState archiveState;
|
||||
|
@ -103,7 +107,7 @@ protected:
|
|||
std::list<std::pair<int, QString>> requestCache;
|
||||
std::map<QString, Shared::Message> toCorrect;
|
||||
bool muc;
|
||||
|
||||
|
||||
private:
|
||||
void nextRequest();
|
||||
void performRequest(int count, const QString& before);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue