Browse Source

Show live user storage quota in the member file browser

master
evgeny 6 days ago
parent
commit
4fdecb697b
  1. 59
      tools/chatgui/src/member_files/browser.cpp
  2. 3
      tools/chatgui/src/member_files/browser.h

59
tools/chatgui/src/member_files/browser.cpp

@ -20,6 +20,9 @@
#include <QDesktopServices>
#include <QUrl>
#include <QMenu>
#include <QTimer>
#include <QLocale>
#include <limits>
#include <cstring>
extern "C" {
#include "member_files/member_files.h"
@ -29,6 +32,10 @@ extern "C" {
}
static QString text(const char* value) { return QCoreApplication::translate("ChatGui", value); }
static QString quotaSize(quint64 bytes) {
return bytes <= (quint64)std::numeric_limits<qint64>::max() ? QLocale().formattedDataSize((qint64)bytes, 2) :
text("%1 bytes").arg(QLocale().toString(bytes));
}
static quint64 requestId() { quint64 id; do { id = QRandomGenerator::global()->generate64(); } while (!id); return id; }
static bool copyPath(char* out, const QString& value) {
QByteArray bytes = value.toUtf8();
@ -43,6 +50,10 @@ MemberFileBrowser::MemberFileBrowser(DbManager* db, QWidget* parent) : QDialog(p
m_servers = new QComboBox(this); m_owners = new QComboBox(this);
selectors->addWidget(new QLabel(text("Media server:"), this)); selectors->addWidget(m_servers, 1);
selectors->addWidget(new QLabel(text("Member:"), this)); selectors->addWidget(m_owners, 1); layout->addLayout(selectors);
m_quota = new QLabel(text("Select a member to see storage quota"), this);
m_quota->setObjectName("memberFilesQuota"); layout->addWidget(m_quota);
auto* quotaTimer = new QTimer(this); quotaTimer->setInterval(2000);
connect(quotaTimer, &QTimer::timeout, this, [this]() { if (isVisible()) requestQuota(); }); quotaTimer->start();
auto* navigation = new QHBoxLayout;
auto* up = new QPushButton(text("Up"), this); auto* reload = new QPushButton(text("Refresh"), this);
m_location = new QLineEdit(this); m_location->setPlaceholderText(text("Path inside the member directory"));
@ -91,6 +102,7 @@ MemberFileBrowser::MemberFileBrowser(DbManager* db, QWidget* parent) : QDialog(p
MemberFileBrowser::~MemberFileBrowser() {
cancel(); cancelRequest(m_listId);
cancelRequest(m_quotaId);
for (auto id : m_actions.keys()) cancelRequest(id);
}
@ -162,11 +174,25 @@ void MemberFileBrowser::cancelRequest(quint64 id) {
q->inst = gui_bridge_get_inst(); q->id = id; gui_bridge_post_uasync_fn(member_files_gui_cancel, q);
}
void MemberFileBrowser::requestQuota() {
if (m_quotaId || !owner() || !server() || !gui_bridge_is_uasync_ready()) return;
mf_message q{}; q.command = MF_QUOTA; q.owner = owner();
if (post(q)) m_quotaId = q.request_id;
else {
m_quota->setText(text("Quota unavailable"));
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: GUI quota request failed owner=%llu", (unsigned long long)owner());
}
}
void MemberFileBrowser::refresh() {
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: GUI refresh old_id=%llu group=%llx server=%llx owner=%llx path=%s",
(unsigned long long)m_listId, (unsigned long long)m_group, (unsigned long long)server(), (unsigned long long)owner(), qPrintable(m_path));
cancelRequest(m_listId); m_listId = 0; m_entries->clear(); m_location->setText(m_path); updateActions();
cancelRequest(m_quotaId); m_quotaId = 0; m_refreshGeneration++;
m_quota->setToolTip(QString());
m_quota->setText(owner() ? text("Loading quota…") : text("Select a member to see storage quota"));
if (!server()) { m_status->setText(text("No media server in this group")); return; }
requestQuota();
mf_message q{}; q.command = MF_LIST; q.owner = owner();
if (!copyPath(q.path, m_path) || !post(q)) { m_status->setText(text("Unable to request directory")); return; }
m_listId = q.request_id; m_entries->setSortingEnabled(false); m_status->setText(text("Loading directory…"));
@ -200,9 +226,41 @@ void MemberFileBrowser::onResponse(const uint8_t* data, int length) {
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: GUI response id=%llu list_id=%llu cmd=%s status=%s bytes=%u owner=%llx/%llx path=%s",
(unsigned long long)r.request_id, (unsigned long long)m_listId, mf_command_text(r.command), mf_status_text(r.status), r.data_len,
(unsigned long long)r.owner, (unsigned long long)owner(), r.path);
if (r.command == MF_QUOTA) {
if (r.request_id != m_quotaId || r.owner != owner()) {
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: GUI stale quota response id=%llu", (unsigned long long)r.request_id);
return;
}
m_quotaId = 0;
if (r.status) {
m_quota->setText(r.status == MF_BUSY ? text("Loading quota…") : text("Quota unavailable"));
m_quota->setToolTip(QString::fromUtf8(mf_status_text(r.status)));
if (r.status != MF_BUSY) DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: GUI quota failed status=%s", mf_status_text(r.status));
return;
}
if (r.data_len != MF_QUOTA_BYTES || !mf_get64(r.data + 8)) {
m_quota->setText(text("Quota unavailable"));
DEBUG_WARN(DEBUG_CATEGORY_MEDIA, "member_files: GUI malformed quota response bytes=%u", r.data_len);
return;
}
quint64 used = mf_get64(r.data), limit = mf_get64(r.data + 8), reserved = mf_get64(r.data + 16);
QString label = text("Storage: %1 used / %2 total").arg(quotaSize(used), quotaSize(limit));
if (reserved) label += text(" · Reserved for uploads: %1").arg(quotaSize(reserved));
m_quota->setText(label);
m_quota->setToolTip(text("Used: %1 bytes\nLimit: %2 bytes\nReserved: %3 bytes").arg(used).arg(limit).arg(reserved));
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: GUI quota owner=%llu used=%llu limit=%llu reserved=%llu",
(unsigned long long)owner(), (unsigned long long)used, (unsigned long long)limit, (unsigned long long)reserved);
return;
}
if (r.request_id == m_listId && r.owner == owner() && QString::fromUtf8(r.path) == m_path) {
if (r.status) {
m_listId = 0;
if (r.status == MF_BUSY) {
quint64 generation = m_refreshGeneration;
m_status->setText(text("Loading directory…"));
QTimer::singleShot(1000, this, [this, generation]() { if (generation == m_refreshGeneration) refresh(); });
return;
}
if (r.status == MF_STALE && m_staleRetries++ < 1) { refresh(); return; }
m_entries->clear(); m_status->setText(QString::fromUtf8(mf_status_text(r.status))); return;
}
@ -300,6 +358,7 @@ void MemberFileBrowser::onTransfer(const uint8_t* data, int length) {
if (m_group == m_transferGroup && server() == m_transferServer) refresh();
}
m_openAfter = false;
if (!m_quotaId) requestQuota();
}
void MemberFileBrowser::mutate(uint8_t command) {

3
tools/chatgui/src/member_files/browser.h

@ -26,6 +26,7 @@ public:
void onTransfer(const uint8_t* data, int length);
private:
void refresh();
void requestQuota();
void navigate(const QString& path);
void enter(QTreeWidgetItem* item);
void updateActions();
@ -42,6 +43,7 @@ private:
void cancelRequest(quint64 id);
DbManager* m_db;
quint64 m_group = 0, m_listId = 0, m_transferId = 0;
quint64 m_quotaId = 0, m_refreshGeneration = 0;
quint64 m_transferGroup = 0, m_transferServer = 0;
QString m_path, m_local;
bool m_openAfter = false;
@ -53,5 +55,6 @@ private:
QTreeWidget* m_entries;
QPushButton *m_upload, *m_download, *m_mkdir, *m_rename, *m_remove, *m_cancel;
QLabel* m_status;
QLabel* m_quota;
QProgressBar* m_progress;
};

Loading…
Cancel
Save