Browse Source

Share avatar metadata downloads and cancel closed desktop galleries

master
evgeny 5 days ago
parent
commit
27fd9ee62d
  1. 45
      tools/chatgui/src/avatars/avatarmanager.cpp
  2. 1
      tools/chatgui/src/avatars/avatarmanager.h

45
tools/chatgui/src/avatars/avatarmanager.cpp

@ -154,6 +154,10 @@ QImage AvatarManager::fetch(const Ref& ref, bool original, bool gallery) {
Job job; job.ref = ref; job.original = original; job.gallery = gallery; job.generation = m_generation; Job job; job.ref = ref; job.original = original; job.gallery = gallery; job.generation = m_generation;
const auto record = m_records.value(ref.hash); const auto record = m_records.value(ref.hash);
job.file = record.original.isEmpty() ? ref.hash + ".json" : (original ? record.original : record.preview); job.file = record.original.isEmpty() ? ref.hash + ".json" : (original ? record.original : record.preview);
if (record.original.isEmpty()) {
for (const auto& active : m_jobs) if (!active.upload && active.ref.hash == ref.hash && active.file == job.file) return {};
for (const auto& queued : m_queue) if (!queued.upload && queued.ref.hash == ref.hash && queued.file == job.file) return {};
}
enqueue(job); pump(); return {}; enqueue(job); pump(); return {};
} }
@ -168,20 +172,41 @@ void AvatarManager::openGallery(const QString& channel, quint64 owner) {
} }
void AvatarManager::closeGallery() { void AvatarManager::closeGallery() {
m_galleryEpoch++;
if (m_listId && gui_bridge_is_uasync_ready()) { if (m_listId && gui_bridge_is_uasync_ready()) {
auto* q = (mf_cancel_request*)u_calloc(1, sizeof(mf_cancel_request)); auto* q = (mf_cancel_request*)u_calloc(1, sizeof(mf_cancel_request));
if (q) { q->inst = gui_bridge_get_inst(); q->id = m_listId; gui_bridge_post_uasync_fn(member_files_gui_cancel, q); } if (q) { q->inst = gui_bridge_get_inst(); q->id = m_listId; gui_bridge_post_uasync_fn(member_files_gui_cancel, q); }
else DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "avatar: gallery cancel allocation failed"); else DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "avatar: gallery cancel allocation failed");
} }
m_listId = 0; m_galleryRef = {}; m_gallery.clear(); m_catalog.clear(); m_listRetries = 0; m_listId = 0; m_galleryRef = {}; m_gallery.clear(); m_catalog.clear(); m_listRetries = 0;
QQueue<Job> keep; while (!m_queue.isEmpty()) { const auto job = m_queue.dequeue(); if (!job.gallery) keep.enqueue(job); } auto needed = [this](const Job& job) {
const QString context = key(job.ref.group, job.ref.owner);
return !job.original && m_wanted.contains(context) && m_refs.value(context).hash == job.ref.hash;
};
for (auto id : m_jobs.keys()) {
auto& job = m_jobs[id];
if (!job.gallery) continue;
if (needed(job)) { job.gallery = false; continue; }
if (gui_bridge_is_uasync_ready()) {
auto* q = (mf_cancel_request*)u_calloc(1, sizeof(mf_cancel_request));
if (q) { q->inst = gui_bridge_get_inst(); q->id = id; gui_bridge_post_uasync_fn(member_files_gui_cancel, q); }
else DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "avatar: cancel allocation failed id=%llu", (unsigned long long)id);
}
finish(id, MF_CANCELLED);
}
QQueue<Job> keep; while (!m_queue.isEmpty()) {
auto job = m_queue.dequeue(); if (job.gallery && needed(job)) job.gallery = false;
if (!job.gallery) keep.enqueue(job);
}
m_queue = keep; m_queue = keep;
} }
void AvatarManager::listGallery(quint64 cursor, const QByteArray& version) { void AvatarManager::listGallery(quint64 cursor, const QByteArray& version) {
if (!gui_bridge_is_uasync_ready() || !m_galleryRef.server) return;
auto* q = (mf_gui_request*)u_calloc(1, sizeof(mf_gui_request)); auto* q = (mf_gui_request*)u_calloc(1, sizeof(mf_gui_request));
if (!q) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "avatar: catalog allocation failed"); emit galleryError(text("Cannot load avatars")); return; } if (!q) { DEBUG_ERROR(DEBUG_CATEGORY_MEDIA, "avatar: catalog allocation failed"); emit galleryError(text("Cannot load avatars")); return; }
do { m_listId = QRandomGenerator::global()->generate64(); } while (!m_listId || m_jobs.contains(m_listId)); do { m_listId = QRandomGenerator::global()->generate64(); } while (!m_listId || m_jobs.contains(m_listId));
m_listCursor = cursor;
q->inst = gui_bridge_get_inst(); q->group = m_galleryRef.group; q->server = m_galleryRef.server; q->inst = gui_bridge_get_inst(); q->group = m_galleryRef.group; q->server = m_galleryRef.server;
q->message.kind = MF_REQUEST; q->message.command = MF_LIST; q->message.request_id = m_listId; q->message.kind = MF_REQUEST; q->message.command = MF_LIST; q->message.request_id = m_listId;
q->message.owner = m_galleryRef.owner; q->message.cursor = cursor; memcpy(q->message.path, "avatars", 8); q->message.owner = m_galleryRef.owner; q->message.cursor = cursor; memcpy(q->message.path, "avatars", 8);
@ -312,7 +337,7 @@ void AvatarManager::enqueue(const Job& job) {
for (const auto& old : m_queue) if ((old.upload == job.upload && old.ref.group == job.ref.group && old.ref.owner == job.ref.owner && for (const auto& old : m_queue) if ((old.upload == job.upload && old.ref.group == job.ref.group && old.ref.owner == job.ref.owner &&
(job.upload || (old.ref.hash == job.ref.hash && old.original == job.original))) || (job.upload || (old.ref.hash == job.ref.hash && old.original == job.original))) ||
(!job.upload && !old.upload && old.ref.hash == job.ref.hash && old.original == job.original)) return; (!job.upload && !old.upload && old.ref.hash == job.ref.hash && old.original == job.original)) return;
if (job.upload) m_queue.prepend(job); else m_queue.enqueue(job); if (job.upload || job.original) m_queue.prepend(job); else m_queue.enqueue(job);
} }
void AvatarManager::pump() { void AvatarManager::pump() {
@ -378,9 +403,9 @@ void AvatarManager::onResponse(const uint8_t* data, int length) {
response.owner != m_galleryRef.owner || response.command != MF_LIST || strcmp(response.path, "avatars")) return; response.owner != m_galleryRef.owner || response.command != MF_LIST || strcmp(response.path, "avatars")) return;
m_listId = 0; m_listId = 0;
if ((response.status == MF_STALE || response.status == MF_BUSY) && m_listRetries++ < 3) { if ((response.status == MF_STALE || response.status == MF_BUSY) && m_listRetries++ < 3) {
m_catalog.clear(); const Ref source = m_galleryRef; m_catalog.clear(); const quint64 epoch = m_galleryEpoch;
QTimer::singleShot(1000, this, [this, source] { QTimer::singleShot(1000, this, [this, epoch] {
if (source.group == m_galleryRef.group && source.owner == m_galleryRef.owner && source.server == m_galleryRef.server) listGallery(); if (epoch == m_galleryEpoch) listGallery();
}); return; }); return;
} }
if (response.status) { if (response.status) {
@ -400,7 +425,13 @@ void AvatarManager::onResponse(const uint8_t* data, int length) {
m_catalog[QString::fromLatin1(file).left(64)] = mf_get64(p + 11); m_catalog[QString::fromLatin1(file).left(64)] = mf_get64(p + 11);
at += MF_ENTRY_HEADER + n; at += MF_ENTRY_HEADER + n;
} }
if (response.more) { listGallery(response.cursor, QByteArray((const char*)response.version, 16)); return; } if (response.more) {
if (response.cursor <= m_listCursor) {
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "avatar: catalog cursor stalled=%llu", (unsigned long long)response.cursor);
emit galleryError(text("Invalid directory response")); return;
}
listGallery(response.cursor, QByteArray((const char*)response.version, 16)); return;
}
m_gallery = m_catalog.keys(); m_gallery = m_catalog.keys();
std::sort(m_gallery.begin(), m_gallery.end(), [this](const QString& a, const QString& b) { std::sort(m_gallery.begin(), m_gallery.end(), [this](const QString& a, const QString& b) {
return m_catalog[a] != m_catalog[b] ? m_catalog[a] > m_catalog[b] : a < b; return m_catalog[a] != m_catalog[b] ? m_catalog[a] > m_catalog[b] : a < b;
@ -445,7 +476,7 @@ void AvatarManager::onTransfer(const uint8_t* data, int length) {
if (snapshot.file.endsWith(".json")) { if (snapshot.file.endsWith(".json")) {
m_records[snapshot.ref.hash] = result.record; m_records[snapshot.ref.hash] = result.record;
auto& job = m_jobs[snapshot.id]; job.file = snapshot.original ? result.record.original : result.record.preview; auto& job = m_jobs[snapshot.id]; job.file = snapshot.original ? result.record.original : result.record.preview;
transfer(job); return; transfer(job); notify(snapshot.ref.hash); return;
} }
const QString cacheKey = snapshot.ref.hash + (snapshot.original ? ":original" : ""); const QString cacheKey = snapshot.ref.hash + (snapshot.original ? ":original" : "");
m_images.insert(cacheKey, new QImage(result.image), qMax(1, int(result.image.sizeInBytes() / 1024))); m_images.insert(cacheKey, new QImage(result.image), qMax(1, int(result.image.sizeInBytes() / 1024)));

1
tools/chatgui/src/avatars/avatarmanager.h

@ -80,6 +80,7 @@ private:
QStringList m_gallery; QStringList m_gallery;
QMap<QString, quint64> m_catalog; QMap<QString, quint64> m_catalog;
quint64 m_listId = 0; quint64 m_listId = 0;
quint64 m_galleryEpoch = 0, m_listCursor = 0;
unsigned m_listRetries = 0; unsigned m_listRetries = 0;
QThreadPool m_pool; QThreadPool m_pool;
QTimer m_timer; QTimer m_timer;

Loading…
Cancel
Save