|
|
|
@ -20,6 +20,9 @@ |
|
|
|
#include <QDesktopServices> |
|
|
|
#include <QDesktopServices> |
|
|
|
#include <QUrl> |
|
|
|
#include <QUrl> |
|
|
|
#include <QMenu> |
|
|
|
#include <QMenu> |
|
|
|
|
|
|
|
#include <QTimer> |
|
|
|
|
|
|
|
#include <QLocale> |
|
|
|
|
|
|
|
#include <limits> |
|
|
|
#include <cstring> |
|
|
|
#include <cstring> |
|
|
|
extern "C" { |
|
|
|
extern "C" { |
|
|
|
#include "member_files/member_files.h" |
|
|
|
#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 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 quint64 requestId() { quint64 id; do { id = QRandomGenerator::global()->generate64(); } while (!id); return id; } |
|
|
|
static bool copyPath(char* out, const QString& value) { |
|
|
|
static bool copyPath(char* out, const QString& value) { |
|
|
|
QByteArray bytes = value.toUtf8(); |
|
|
|
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); |
|
|
|
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("Media server:"), this)); selectors->addWidget(m_servers, 1); |
|
|
|
selectors->addWidget(new QLabel(text("Member:"), this)); selectors->addWidget(m_owners, 1); layout->addLayout(selectors); |
|
|
|
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* navigation = new QHBoxLayout; |
|
|
|
auto* up = new QPushButton(text("Up"), this); auto* reload = new QPushButton(text("Refresh"), this); |
|
|
|
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")); |
|
|
|
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() { |
|
|
|
MemberFileBrowser::~MemberFileBrowser() { |
|
|
|
cancel(); cancelRequest(m_listId); |
|
|
|
cancel(); cancelRequest(m_listId); |
|
|
|
|
|
|
|
cancelRequest(m_quotaId); |
|
|
|
for (auto id : m_actions.keys()) cancelRequest(id); |
|
|
|
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); |
|
|
|
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() { |
|
|
|
void MemberFileBrowser::refresh() { |
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_DEBUG, "member_files: GUI refresh old_id=%llu group=%llx server=%llx owner=%llx path=%s", |
|
|
|
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)); |
|
|
|
(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_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; } |
|
|
|
if (!server()) { m_status->setText(text("No media server in this group")); return; } |
|
|
|
|
|
|
|
requestQuota(); |
|
|
|
mf_message q{}; q.command = MF_LIST; q.owner = owner(); |
|
|
|
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; } |
|
|
|
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…")); |
|
|
|
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", |
|
|
|
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.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); |
|
|
|
(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.request_id == m_listId && r.owner == owner() && QString::fromUtf8(r.path) == m_path) { |
|
|
|
if (r.status) { |
|
|
|
if (r.status) { |
|
|
|
m_listId = 0; |
|
|
|
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; } |
|
|
|
if (r.status == MF_STALE && m_staleRetries++ < 1) { refresh(); return; } |
|
|
|
m_entries->clear(); m_status->setText(QString::fromUtf8(mf_status_text(r.status))); 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(); |
|
|
|
if (m_group == m_transferGroup && server() == m_transferServer) refresh(); |
|
|
|
} |
|
|
|
} |
|
|
|
m_openAfter = false; |
|
|
|
m_openAfter = false; |
|
|
|
|
|
|
|
if (!m_quotaId) requestQuota(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void MemberFileBrowser::mutate(uint8_t command) { |
|
|
|
void MemberFileBrowser::mutate(uint8_t command) { |
|
|
|
|