| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241 |
- #include "AvatarCache.h"
- #include "AppConfig.h"
- #include "HttpFetch.h"
- #include "ImClient.h"
- #include "LocalStore.h"
- #include "Protocol.h"
- #include "UiHelpers.h"
- #include <QCoreApplication>
- #include <QDir>
- #include <QFile>
- #include <QFileInfo>
- #include <QLabel>
- #include <QPointer>
- #include <QVariant>
- AvatarCache &AvatarCache::instance()
- {
- static AvatarCache s;
- return s;
- }
- AvatarCache::AvatarCache(QObject *parent)
- : QObject(parent)
- {
- }
- QString AvatarCache::cacheDir() const
- {
- return LocalStore::userSubdir(ImClient::instance().uid(), QStringLiteral("avatars"));
- }
- QString AvatarCache::filePath(qint64 uid, int ver) const
- {
- const QString dir = cacheDir();
- if (dir.isEmpty())
- return {};
- return dir + QStringLiteral("/%1_%2.jpg").arg(uid).arg(ver);
- }
- int AvatarCache::diskVer(qint64 uid) const
- {
- QDir dir(cacheDir());
- int best = 0;
- const QString prefix = QString::number(uid) + QLatin1Char('_');
- const QStringList files = dir.entryList(QStringList() << (prefix + QStringLiteral("*.jpg")), QDir::Files);
- for (const QString &f : files)
- {
- const QString stem = QFileInfo(f).completeBaseName();
- const int us = stem.lastIndexOf(QLatin1Char('_'));
- if (us < 0)
- continue;
- bool ok = false;
- const int v = stem.mid(us + 1).toInt(&ok);
- if (ok && v > best)
- best = v;
- }
- return best;
- }
- int AvatarCache::resolveVer(qint64 uid, int ver)
- {
- int best = m_ver.value(uid, 0);
- if (best <= 0)
- {
- best = diskVer(uid);
- if (best > 0)
- m_ver.insert(uid, best);
- }
- if (ver > best)
- return ver;
- if (best > 0)
- return best;
- return ver;
- }
- void AvatarCache::invalidate(qint64 uid)
- {
- const QString prefix = QString::number(uid) + QLatin1Char('_');
- for (auto it = m_pix.begin(); it != m_pix.end(); )
- {
- if (it.key().startsWith(prefix) || it.key().startsWith(QString::number(uid) + QLatin1Char('/')))
- it = m_pix.erase(it);
- else
- ++it;
- }
- m_ver.remove(uid);
- }
- void AvatarCache::remember(qint64 uid, int ver, const QByteArray &bytes)
- {
- if (uid <= 0 || ver <= 0 || bytes.isEmpty())
- return;
- const QString path = filePath(uid, ver);
- QDir().mkpath(QFileInfo(path).absolutePath());
- QFile f(path);
- if (f.open(QIODevice::WriteOnly))
- {
- f.write(bytes);
- f.flush();
- f.close();
- }
- invalidate(uid);
- m_ver.insert(uid, ver);
- emit updated(uid);
- }
- void AvatarCache::ensure(qint64 uid, int ver)
- {
- if (uid <= 0 || ver <= 0 || m_fetching.contains(uid))
- return;
- int have = m_ver.value(uid, 0);
- if (have <= 0)
- have = diskVer(uid);
- if (have >= ver && QFile::exists(filePath(uid, have)))
- return;
- if (QFile::exists(filePath(uid, ver)))
- return;
- m_fetching.insert(uid);
- QPointer<AvatarCache> self(this);
- auto takeHttp = [self, uid](const QString &path, int gotVer) {
- if (!self)
- return;
- const QString http = AppConfig::instance().resourceUrl(path);
- if (http.isEmpty())
- {
- self->m_fetching.remove(uid);
- return;
- }
- HttpFetch::instance().get(http, [self, uid, gotVer](bool ok, const QByteArray &bytes, const QString &) {
- if (!self)
- return;
- if (ok && !bytes.isEmpty() && gotVer > 0)
- self->remember(uid, gotVer, bytes);
- self->m_fetching.remove(uid);
- });
- };
- ImClient::instance().getAvatar(uid, [self, uid, ver, takeHttp](const QJsonObject &reply) {
- if (!self)
- return;
- if (!ImClient::ok(reply))
- {
- self->m_fetching.remove(uid);
- return;
- }
- int got = static_cast<int>(Proto::jsonI64(reply, QStringLiteral("ver")));
- if (got <= 0)
- got = static_cast<int>(Proto::jsonI64(reply, QStringLiteral("avatar_ver")));
- if (got <= 0)
- got = ver;
- const QString url = reply.value(QStringLiteral("url")).toString();
- if (!url.isEmpty())
- {
- takeHttp(url, got);
- return;
- }
- const QByteArray bytes = QByteArray::fromBase64(
- reply.value(QStringLiteral("data")).toString().toLatin1());
- if (!bytes.isEmpty() && got > 0)
- self->remember(uid, got, bytes);
- self->m_fetching.remove(uid);
- });
- }
- void AvatarCache::probe(qint64 uid)
- {
- if (uid <= 0 || m_probing.contains(uid) || m_fetching.contains(uid))
- return;
- if (resolveVer(uid, 0) > 0)
- return;
- m_probing.insert(uid);
- QPointer<AvatarCache> self(this);
- ImClient::instance().getProfile(uid, [self, uid](const QJsonObject &reply) {
- if (!self)
- return;
- self->m_probing.remove(uid);
- if (!ImClient::ok(reply))
- return;
- const int ver = static_cast<int>(Proto::jsonI64(reply, QStringLiteral("avatar_ver")));
- if (ver <= 0)
- return;
- self->m_ver.insert(uid, ver);
- self->ensure(uid, ver);
- emit self->updated(uid);
- });
- }
- void AvatarCache::applyTo(QLabel *label, qint64 uid, int ver, const QString &name, int size)
- {
- if (!label)
- return;
- label->setAlignment(Qt::AlignCenter);
- label->setProperty("avatarUid", QVariant::fromValue(uid));
- label->setProperty("avatarVer", resolveVer(uid, ver));
- label->setProperty("avatarName", name);
- label->setProperty("avatarSize", size);
- if (!label->property("avatarBound").toBool())
- {
- label->setProperty("avatarBound", true);
- connect(this, &AvatarCache::updated, label, [this, label](qint64 id) {
- if (!label || id != label->property("avatarUid").toLongLong())
- return;
- const qint64 u = label->property("avatarUid").toLongLong();
- const int v = resolveVer(u, label->property("avatarVer").toInt());
- label->setProperty("avatarVer", v);
- const QString n = label->property("avatarName").toString();
- const int s = label->property("avatarSize").toInt();
- label->setPixmap(pixmap(u, v, n, s));
- });
- }
- label->setPixmap(pixmap(uid, ver, name, size));
- if (uid > 0 && resolveVer(uid, ver) <= 0)
- probe(uid);
- }
- QPixmap AvatarCache::pixmap(qint64 uid, int ver, const QString &name, int size)
- {
- ver = resolveVer(uid, ver);
- if (uid <= 0 || ver <= 0)
- return UiHelpers::avatarPixmap(name, size);
- const QString key = QStringLiteral("%1/%2/%3").arg(uid).arg(ver).arg(size);
- if (m_pix.contains(key))
- return m_pix.value(key);
- const QString path = filePath(uid, ver);
- QPixmap disk(path);
- if (disk.isNull())
- {
- QFile f(path);
- if (f.open(QIODevice::ReadOnly))
- disk.loadFromData(f.readAll(), "JPEG");
- }
- if (disk.isNull())
- {
- ensure(uid, ver);
- return UiHelpers::avatarPixmap(name, size);
- }
- const QPixmap rounded = UiHelpers::roundAvatar(disk, size);
- m_pix.insert(key, rounded);
- m_ver.insert(uid, ver);
- return rounded;
- }
|