mirror of
https://github.com/logos-storage/logos-storage-app-skeleton.git
synced 2026-06-15 12:59:29 +00:00
commit
da1621df7f
@ -66,6 +66,8 @@ LogosResult StorageBackend::init(const QString& configJson = "{}") {
|
||||
} else {
|
||||
setStatus(Running);
|
||||
debug("Storage module started.");
|
||||
QMetaObject::invokeMethod(this, &StorageBackend::downloadManifests, Qt::QueuedConnection);
|
||||
QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection);
|
||||
emit startCompleted();
|
||||
}
|
||||
})) {
|
||||
@ -155,6 +157,8 @@ LogosResult StorageBackend::init(const QString& configJson = "{}") {
|
||||
m_uploadStatus = "Upload completed!";
|
||||
emit uploadProgressChanged();
|
||||
emit uploadStatusChanged();
|
||||
|
||||
QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection);
|
||||
}
|
||||
})) {
|
||||
qWarning() << "StorageWidget: failed to subscribe to storageUploadProgress events";
|
||||
@ -549,11 +553,22 @@ void StorageBackend::remove(const QString& cid) {
|
||||
LogosResult result = m_logos->storage_module.remove(cid);
|
||||
|
||||
if (!result.success) {
|
||||
debug("StorageBackend::remove failed with error=" + result.getError());
|
||||
return;
|
||||
// Log but continue — manifest might not have local data, remove it from the list anyway
|
||||
debug("StorageBackend::remove: storage returned error=" + result.getError() + " (removing from list regardless)");
|
||||
} else {
|
||||
debug("Cid " + cid + " removed from storage.");
|
||||
}
|
||||
|
||||
debug("Cid " + cid + " removed.");
|
||||
// Always remove from manifests list
|
||||
for (int i = 0; i < m_manifests.size(); ++i) {
|
||||
if (m_manifests[i].toMap().value("cid").toString() == cid) {
|
||||
m_manifests.removeAt(i);
|
||||
emit manifestsChanged();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QMetaObject::invokeMethod(this, &StorageBackend::space, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
void StorageBackend::fetch(const QString& cid) {
|
||||
@ -631,18 +646,38 @@ void StorageBackend::downloadManifest(const QString& cid) {
|
||||
return;
|
||||
}
|
||||
|
||||
debug("Manifest tree cid: " + result.getString("treeCid"));
|
||||
debug(QString("Manifest datasetSize %1").arg(result.getInt("datasetSize")));
|
||||
debug(QString("Manifest blockSize %1").arg(result.getInt("blockSize")));
|
||||
debug("Manifest filename: " + result.getString("filename"));
|
||||
debug("Manifest mimetype: " + result.getString("mimetype"));
|
||||
QString treeCid = result.getString("treeCid");
|
||||
qint64 datasetSize = result.getInt("datasetSize");
|
||||
qint64 blockSize = result.getInt("blockSize");
|
||||
QString filename = result.getString("filename");
|
||||
QString mimetype = result.getString("mimetype");
|
||||
|
||||
debug("Manifest tree cid: " + treeCid);
|
||||
debug(QString("Manifest datasetSize %1").arg(datasetSize));
|
||||
debug(QString("Manifest blockSize %1").arg(blockSize));
|
||||
debug("Manifest filename: " + filename);
|
||||
debug("Manifest mimetype: " + mimetype);
|
||||
|
||||
// Add to manifests list
|
||||
QVariantMap manifest;
|
||||
manifest["cid"] = cid;
|
||||
manifest["treeCid"] = treeCid;
|
||||
manifest["filename"] = filename;
|
||||
manifest["mimetype"] = mimetype;
|
||||
manifest["datasetSize"] = datasetSize;
|
||||
manifest["blockSize"] = blockSize;
|
||||
|
||||
m_manifests.append(manifest);
|
||||
emit manifestsChanged();
|
||||
}
|
||||
|
||||
QVariantList StorageBackend::manifests() const { return m_manifests; }
|
||||
|
||||
void StorageBackend::downloadManifests() {
|
||||
qDebug() << "StorageBackend::downloadManifests called";
|
||||
|
||||
LogosResult result = m_logos->storage_module.manifests();
|
||||
QString error = result.getError();
|
||||
|
||||
if (!result.success) {
|
||||
debug("StorageBackend::downloadManifests failed with error=" + result.getError());
|
||||
return;
|
||||
@ -650,22 +685,25 @@ void StorageBackend::downloadManifests() {
|
||||
|
||||
QVariantList manifestsList = result.getList();
|
||||
int count = manifestsList.size();
|
||||
|
||||
debug(QString("Found %1 manifests").arg(count));
|
||||
|
||||
// for (const QVariant& manifestVariant : manifestsList) {
|
||||
// QVariantMap manifest = manifestVariant.toMap();
|
||||
m_manifests.clear();
|
||||
|
||||
// QString cid = manifest["cid"].toString();
|
||||
// QString treeCid = manifest["treeCid"].toString();
|
||||
// QString filename = manifest["filename"].toString();
|
||||
// qint64 datasetSize = manifest["datasetSize"].toLongLong();
|
||||
for (const QVariant& manifestVariant : manifestsList) {
|
||||
QVariantMap src = manifestVariant.toMap();
|
||||
|
||||
// debug(QString("Manifest: %1, treeCid: %2, size: %3")
|
||||
// .arg(filename)
|
||||
// .arg(treeCid.isEmpty() ? "EMPTY" : treeCid)
|
||||
// .arg(datasetSize));
|
||||
// }
|
||||
QVariantMap manifest;
|
||||
manifest["cid"] = src.value("cid").toString();
|
||||
manifest["treeCid"] = src.value("treeCid").toString();
|
||||
manifest["filename"] = src.value("filename").toString();
|
||||
manifest["mimetype"] = src.value("mimetype").toString();
|
||||
manifest["datasetSize"] = src.value("datasetSize").toLongLong();
|
||||
manifest["blockSize"] = src.value("blockSize").toLongLong();
|
||||
|
||||
m_manifests.append(manifest);
|
||||
}
|
||||
|
||||
emit manifestsChanged();
|
||||
}
|
||||
|
||||
void StorageBackend::space() {
|
||||
@ -678,12 +716,33 @@ void StorageBackend::space() {
|
||||
return;
|
||||
}
|
||||
|
||||
debug(QString("Space datasetSize %1").arg(result.getInt("totalBlocks")));
|
||||
debug(QString("Space quotaMaxBytes %1").arg(result.getInt("quotaMaxBytes")));
|
||||
debug(QString("Space quotaUsedBytes %1").arg(result.getInt("quotaUsedBytes")));
|
||||
debug(QString("Space quotaReservedBytes %1").arg(result.getInt("quotaReservedBytes")));
|
||||
qDebug() << "StorageBackend::space raw value:" << result.value;
|
||||
|
||||
static constexpr qint64 DEFAULT_QUOTA = 20LL * 1024 * 1024 * 1024; // 20 GB
|
||||
|
||||
// Check config for a quota-max-bytes override
|
||||
qint64 configQuota = 0;
|
||||
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
|
||||
if (!doc.isNull()) {
|
||||
configQuota = doc.object().value("quota-max-bytes").toVariant().toLongLong();
|
||||
}
|
||||
|
||||
qint64 apiQuota = result.getInt("quotaMaxBytes");
|
||||
m_quotaMaxBytes = apiQuota > 0 ? apiQuota : (configQuota > 0 ? configQuota : DEFAULT_QUOTA);
|
||||
m_quotaUsedBytes = result.getInt("quotaUsedBytes");
|
||||
m_quotaReservedBytes = result.getInt("quotaReservedBytes");
|
||||
emit quotaChanged();
|
||||
|
||||
debug(QString("Space totalBlocks %1").arg(result.getInt("totalBlocks")));
|
||||
debug(QString("Space quotaMaxBytes %1").arg(m_quotaMaxBytes));
|
||||
debug(QString("Space quotaUsedBytes %1").arg(m_quotaUsedBytes));
|
||||
debug(QString("Space quotaReservedBytes %1").arg(m_quotaReservedBytes));
|
||||
}
|
||||
|
||||
qint64 StorageBackend::quotaMaxBytes() const { return m_quotaMaxBytes; }
|
||||
qint64 StorageBackend::quotaUsedBytes() const { return m_quotaUsedBytes; }
|
||||
qint64 StorageBackend::quotaReservedBytes() const { return m_quotaReservedBytes; }
|
||||
|
||||
void StorageBackend::updateLogLevel(const QString& logLevel) {
|
||||
qDebug() << "StorageBackend::updateLogLevel called with logLevel=" << logLevel;
|
||||
|
||||
|
||||
@ -33,6 +33,10 @@ class StorageBackend : public QObject {
|
||||
Q_PROPERTY(QString configJson READ configJson NOTIFY configJsonChanged)
|
||||
Q_PROPERTY(int uploadProgress READ uploadProgress NOTIFY uploadProgressChanged)
|
||||
Q_PROPERTY(QString uploadStatus READ uploadStatus NOTIFY uploadStatusChanged)
|
||||
Q_PROPERTY(QVariantList manifests READ manifests NOTIFY manifestsChanged)
|
||||
Q_PROPERTY(qint64 quotaMaxBytes READ quotaMaxBytes NOTIFY quotaChanged)
|
||||
Q_PROPERTY(qint64 quotaUsedBytes READ quotaUsedBytes NOTIFY quotaChanged)
|
||||
Q_PROPERTY(qint64 quotaReservedBytes READ quotaReservedBytes NOTIFY quotaChanged)
|
||||
|
||||
public:
|
||||
enum StorageStatus { Stopped = 0, Starting, Running, Stopping, Destroyed };
|
||||
@ -44,6 +48,10 @@ class StorageBackend : public QObject {
|
||||
QString configJson() const;
|
||||
int uploadProgress() const;
|
||||
QString uploadStatus() const;
|
||||
QVariantList manifests() const;
|
||||
qint64 quotaMaxBytes() const;
|
||||
qint64 quotaUsedBytes() const;
|
||||
qint64 quotaReservedBytes() const;
|
||||
|
||||
Q_INVOKABLE static QString defaultDataDir();
|
||||
|
||||
@ -88,6 +96,8 @@ class StorageBackend : public QObject {
|
||||
void configJsonChanged();
|
||||
void uploadProgressChanged();
|
||||
void uploadStatusChanged();
|
||||
void manifestsChanged();
|
||||
void quotaChanged();
|
||||
|
||||
private slots:
|
||||
|
||||
@ -106,4 +116,8 @@ class StorageBackend : public QObject {
|
||||
QString m_uploadStatus = "";
|
||||
qint64 m_uploadTotalBytes = 0;
|
||||
qint64 m_uploadedBytes = 0;
|
||||
QVariantList m_manifests;
|
||||
qint64 m_quotaMaxBytes = 0;
|
||||
qint64 m_quotaUsedBytes = 0;
|
||||
qint64 m_quotaReservedBytes = 0;
|
||||
};
|
||||
|
||||
@ -2,13 +2,14 @@ import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import QtCore
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: 600
|
||||
implicitHeight: 400
|
||||
implicitHeight: 600
|
||||
color: "#000000"
|
||||
|
||||
property var backend: mockBackend
|
||||
@ -22,6 +23,7 @@ Rectangle {
|
||||
property url downloadCid: ""
|
||||
property string logLevel: ""
|
||||
property bool showDebug: false
|
||||
property var pendingDownloadManifest: null
|
||||
property url uploadCid: root.backend.cid
|
||||
property url configJson: root.backend.configJson
|
||||
|
||||
@ -124,6 +126,19 @@ Rectangle {
|
||||
function space() {}
|
||||
|
||||
function updateLogLevel(logLevel) {}
|
||||
|
||||
property var manifests: []
|
||||
property var quotaMaxBytes: 20 * 1024 * 1024 * 1024 // 20 GB default
|
||||
property var quotaUsedBytes: 0
|
||||
property var quotaReservedBytes: 0
|
||||
}
|
||||
|
||||
function formatBytes(bytes) {
|
||||
if (bytes <= 0) return "0 B"
|
||||
if (bytes < 1024) return bytes + " B"
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + " KB"
|
||||
if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + " MB"
|
||||
return (bytes / (1024 * 1024 * 1024)).toFixed(2) + " GB"
|
||||
}
|
||||
|
||||
Text {
|
||||
@ -213,36 +228,35 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: peerIdField
|
||||
placeholderText: "Enter the peer Id"
|
||||
placeholderTextColor: "#999999"
|
||||
color: "#000000"
|
||||
selectByMouse: true
|
||||
text: root.peerId
|
||||
onTextChanged: root.peerId = text
|
||||
anchors.top: uploadProgressColumn.bottom
|
||||
anchors.topMargin: 50
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Button {
|
||||
id: peerConnectButton
|
||||
objectName: "peerConnectButton"
|
||||
text: "Peer connect"
|
||||
onClicked: root.backend.tryPeerConnect(root.peerId)
|
||||
anchors.top: peerIdField.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
// TextField {
|
||||
// id: peerIdField
|
||||
// placeholderText: "Enter the peer Id"
|
||||
// placeholderTextColor: "#999999"
|
||||
// color: "#000000"
|
||||
// selectByMouse: true
|
||||
// text: root.peerId
|
||||
// onTextChanged: root.peerId = text
|
||||
// anchors.top: uploadProgressColumn.bottom
|
||||
// anchors.topMargin: 50
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
// }
|
||||
|
||||
// Button {
|
||||
// id: peerConnectButton
|
||||
// objectName: "peerConnectButton"
|
||||
// text: "Peer connect"
|
||||
// onClicked: root.backend.tryPeerConnect(root.peerId)
|
||||
// anchors.top: peerIdField.bottom
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
// enabled: root.isRunning
|
||||
// anchors.topMargin: 10
|
||||
// }
|
||||
Button {
|
||||
id: debugButton
|
||||
objectName: "debugButton"
|
||||
text: "Debug"
|
||||
onClicked: root.backend.tryDebug()
|
||||
anchors.top: peerConnectButton.bottom
|
||||
anchors.top: uploadProgressColumn.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 50
|
||||
@ -253,7 +267,7 @@ Rectangle {
|
||||
objectName: "peerIdButton"
|
||||
text: "Peer Id"
|
||||
onClicked: root.backend.showPeerId()
|
||||
anchors.top: peerConnectButton.bottom
|
||||
anchors.top: uploadProgressColumn.bottom
|
||||
anchors.right: debugButton.left
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 50
|
||||
@ -264,7 +278,7 @@ Rectangle {
|
||||
objectName: "dataDirButton"
|
||||
text: "Data dir"
|
||||
onClicked: root.backend.dataDir()
|
||||
anchors.top: peerConnectButton.bottom
|
||||
anchors.top: uploadProgressColumn.bottom
|
||||
anchors.right: peerIdButton.left
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 50
|
||||
@ -275,7 +289,7 @@ Rectangle {
|
||||
objectName: "sprButton"
|
||||
text: "SPR"
|
||||
onClicked: root.backend.spr()
|
||||
anchors.top: peerConnectButton.bottom
|
||||
anchors.top: uploadProgressColumn.bottom
|
||||
anchors.left: debugButton.right
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 50
|
||||
@ -286,91 +300,92 @@ Rectangle {
|
||||
objectName: "versionButton"
|
||||
text: "Version"
|
||||
onClicked: root.backend.version()
|
||||
anchors.top: peerConnectButton.bottom
|
||||
anchors.top: uploadProgressColumn.bottom
|
||||
anchors.left: sprButton.right
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 50
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: cidDownloadField
|
||||
placeholderTextColor: "#999999"
|
||||
placeholderText: "Enter the cid to download"
|
||||
color: "black"
|
||||
// text: root.downloadCid
|
||||
onTextChanged: root.downloadCid = text
|
||||
anchors.top: debugButton.bottom
|
||||
anchors.topMargin: 50
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
// TextField {
|
||||
// id: cidDownloadField
|
||||
// placeholderTextColor: "#999999"
|
||||
// placeholderText: "Enter the cid to download"
|
||||
// color: "black"
|
||||
// // text: root.downloadCid
|
||||
// onTextChanged: root.downloadCid = text
|
||||
// anchors.top: debugButton.bottom
|
||||
// anchors.topMargin: 50
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
// }
|
||||
|
||||
Button {
|
||||
id: openFile2
|
||||
text: "Open file"
|
||||
onClicked: fileDialog2.open()
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: cidDownloadField.bottom
|
||||
anchors.topMargin: 15
|
||||
enabled: root.isRunning
|
||||
}
|
||||
// Button {
|
||||
// id: openFile2
|
||||
// text: "Open file"
|
||||
// onClicked: fileDialog2.open()
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
// anchors.top: cidDownloadField.bottom
|
||||
// anchors.topMargin: 15
|
||||
// enabled: root.isRunning
|
||||
// }
|
||||
|
||||
Button {
|
||||
id: cidDownloadButton
|
||||
objectName: "cidDownloadButton"
|
||||
text: "Download"
|
||||
onClicked: root.backend.tryDownloadFile(root.downloadCid,
|
||||
root.downloadDestination)
|
||||
anchors.top: openFile2.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
// Button {
|
||||
// id: cidDownloadButton
|
||||
// objectName: "cidDownloadButton"
|
||||
// text: "Download"
|
||||
// onClicked: root.backend.tryDownloadFile(root.downloadCid,
|
||||
// root.downloadDestination)
|
||||
// anchors.top: openFile2.bottom
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
// enabled: root.isRunning
|
||||
// anchors.topMargin: 10
|
||||
// }
|
||||
|
||||
Button {
|
||||
id: existsButton
|
||||
objectName: "existsButton"
|
||||
text: "Exists"
|
||||
onClicked: root.backend.exists(root.downloadCid)
|
||||
anchors.top: openFile2.bottom
|
||||
anchors.left: cidDownloadButton.right
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
// Button {
|
||||
// id: existsButton
|
||||
// objectName: "existsButton"
|
||||
// text: "Exists"
|
||||
// onClicked: root.backend.exists(root.downloadCid)
|
||||
// anchors.top: openFile2.bottom
|
||||
// anchors.left: cidDownloadButton.right
|
||||
// enabled: root.isRunning
|
||||
// anchors.topMargin: 10
|
||||
// }
|
||||
|
||||
Button {
|
||||
id: fetchButton
|
||||
objectName: "fetchButton"
|
||||
text: "Fetch"
|
||||
onClicked: root.backend.fetch(root.downloadCid)
|
||||
anchors.top: openFile2.bottom
|
||||
anchors.left: existsButton.right
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
// Button {
|
||||
// id: fetchButton
|
||||
// objectName: "fetchButton"
|
||||
// text: "Fetch"
|
||||
// onClicked: root.backend.fetch(root.downloadCid)
|
||||
// anchors.top: openFile2.bottom
|
||||
// anchors.left: existsButton.right
|
||||
// enabled: root.isRunning
|
||||
// anchors.topMargin: 10
|
||||
// }
|
||||
|
||||
Button {
|
||||
id: removeButton
|
||||
objectName: "removeButton"
|
||||
text: "Remove"
|
||||
onClicked: root.backend.remove(root.downloadCid)
|
||||
anchors.top: openFile2.bottom
|
||||
anchors.right: cidDownloadButton.left
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
// Button {
|
||||
// id: removeButton
|
||||
// objectName: "removeButton"
|
||||
// text: "Remove"
|
||||
// onClicked: root.backend.remove(root.downloadCid)
|
||||
// anchors.top: openFile2.bottom
|
||||
// anchors.right: cidDownloadButton.left
|
||||
// enabled: root.isRunning
|
||||
// anchors.topMargin: 10
|
||||
// }
|
||||
|
||||
Button {
|
||||
id: downloadManifestButton
|
||||
objectName: "downloadManifestButton"
|
||||
text: "Download manifest"
|
||||
onClicked: root.backend.downloadManifest(root.downloadCid)
|
||||
anchors.top: openFile2.bottom
|
||||
anchors.right: removeButton.left
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
// Button {
|
||||
// id: downloadManifestButton
|
||||
// objectName: "downloadManifestButton"
|
||||
// text: "Download manifest"
|
||||
// onClicked: root.backend.downloadManifest(root.downloadCid)
|
||||
// anchors.top: openFile2.bottom
|
||||
// anchors.right: removeButton.left
|
||||
// enabled: root.isRunning
|
||||
// anchors.topMargin: 10
|
||||
// }
|
||||
|
||||
Button {
|
||||
|
||||
/*Button {
|
||||
id: downloadManifestsButton
|
||||
objectName: "downloadManifestsButton"
|
||||
text: "Manifests"
|
||||
@ -379,6 +394,297 @@ Rectangle {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 10
|
||||
}*/
|
||||
|
||||
// ── Manifests section ──────────────────────────────────────────────────
|
||||
Text {
|
||||
id: manifestsTitle
|
||||
text: "Manifests"
|
||||
color: "white"
|
||||
font.pixelSize: 14
|
||||
font.bold: true
|
||||
anchors.top: versionButton.bottom
|
||||
anchors.topMargin: 30
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
// ── Disk space bar ────────────────────────────────────────────────────
|
||||
Item {
|
||||
id: spaceBarSection
|
||||
anchors.top: manifestsTitle.bottom
|
||||
anchors.topMargin: 10
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width - 40
|
||||
height: root.backend.quotaMaxBytes > 0 ? 36 : 20
|
||||
|
||||
readonly property real total: root.backend.quotaMaxBytes
|
||||
readonly property real used: root.backend.quotaUsedBytes
|
||||
readonly property real reserved: root.backend.quotaReservedBytes
|
||||
|
||||
// No quota configured
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "No quota configured"
|
||||
color: "#555555"
|
||||
font.pixelSize: 11
|
||||
visible: spaceBarSection.total <= 0
|
||||
}
|
||||
|
||||
// Background track
|
||||
Rectangle {
|
||||
id: spaceBarTrack
|
||||
visible: spaceBarSection.total > 0
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
height: 14
|
||||
radius: 7
|
||||
color: "#2a2a2a"
|
||||
border.color: "#3a3a3a"
|
||||
border.width: 1
|
||||
clip: true
|
||||
|
||||
// Used (green)
|
||||
Rectangle {
|
||||
width: Math.min(parent.width * (spaceBarSection.used / spaceBarSection.total), parent.width)
|
||||
height: parent.height
|
||||
radius: parent.radius
|
||||
color: "#4CAF50"
|
||||
}
|
||||
|
||||
// Reserved (orange), stacked after used
|
||||
Rectangle {
|
||||
x: parent.width * (spaceBarSection.used / spaceBarSection.total)
|
||||
width: Math.min(parent.width * (spaceBarSection.reserved / spaceBarSection.total),
|
||||
parent.width - x)
|
||||
height: parent.height
|
||||
color: "#FF9800"
|
||||
}
|
||||
}
|
||||
|
||||
// Labels
|
||||
Row {
|
||||
visible: spaceBarSection.total > 0
|
||||
anchors.top: spaceBarTrack.bottom
|
||||
anchors.topMargin: 4
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
spacing: 16
|
||||
|
||||
Text {
|
||||
text: "Used: " + root.formatBytes(spaceBarSection.used)
|
||||
color: "#4CAF50"
|
||||
font.pixelSize: 10
|
||||
}
|
||||
Text {
|
||||
text: "Reserved: " + root.formatBytes(spaceBarSection.reserved)
|
||||
color: "#FF9800"
|
||||
font.pixelSize: 10
|
||||
}
|
||||
Text {
|
||||
text: "Free: " + root.formatBytes(spaceBarSection.total - spaceBarSection.used - spaceBarSection.reserved)
|
||||
color: "#888888"
|
||||
font.pixelSize: 10
|
||||
}
|
||||
Text {
|
||||
text: "Total: " + root.formatBytes(spaceBarSection.total)
|
||||
color: "#555555"
|
||||
font.pixelSize: 10
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: manifestInputRow
|
||||
spacing: 8
|
||||
anchors.top: spaceBarSection.bottom
|
||||
anchors.topMargin: 16
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
TextField {
|
||||
id: manifestCidField
|
||||
width: 380
|
||||
placeholderText: "Enter CID to download manifest"
|
||||
placeholderTextColor: "#999999"
|
||||
color: "#000000"
|
||||
selectByMouse: true
|
||||
}
|
||||
|
||||
Button {
|
||||
id: addManifestButton
|
||||
text: "Download Manifest"
|
||||
enabled: root.isRunning() && manifestCidField.text.length > 0
|
||||
onClicked: {
|
||||
root.backend.downloadManifest(manifestCidField.text)
|
||||
manifestCidField.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Table header
|
||||
Rectangle {
|
||||
id: manifestTableHeader
|
||||
anchors.top: manifestInputRow.bottom
|
||||
anchors.topMargin: 8
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: parent.width - 40
|
||||
height: 28
|
||||
color: "#222222"
|
||||
radius: 2
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 6
|
||||
|
||||
Text {
|
||||
width: 150
|
||||
text: "CID"
|
||||
color: "#aaaaaa"
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Text {
|
||||
width: 120
|
||||
text: "Filename"
|
||||
color: "#aaaaaa"
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Text {
|
||||
width: 85
|
||||
text: "MIME type"
|
||||
color: "#aaaaaa"
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Text {
|
||||
width: 75
|
||||
text: "Size (bytes)"
|
||||
color: "#aaaaaa"
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Text {
|
||||
width: 110
|
||||
text: ""
|
||||
color: "#aaaaaa"
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: manifestTableContainer
|
||||
anchors.top: manifestTableHeader.bottom
|
||||
anchors.left: manifestTableHeader.left
|
||||
anchors.right: manifestTableHeader.right
|
||||
height: 280
|
||||
color: "#111111"
|
||||
border.color: "#333333"
|
||||
border.width: 1
|
||||
clip: true
|
||||
|
||||
ListView {
|
||||
id: manifestListView
|
||||
anchors.fill: parent
|
||||
model: root.backend.manifests
|
||||
clip: true
|
||||
|
||||
delegate: Rectangle {
|
||||
width: manifestListView.width
|
||||
height: 36
|
||||
color: index % 2 === 0 ? "#181818" : "#1e1e1e"
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 6
|
||||
anchors.rightMargin: 4
|
||||
spacing: 0
|
||||
|
||||
Text {
|
||||
width: 150
|
||||
text: modelData["cid"] ?? ""
|
||||
color: "#dddddd"
|
||||
font.pixelSize: 11
|
||||
font.family: "monospace"
|
||||
elide: Text.ElideMiddle
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: modelData["cid"] ?? ""
|
||||
HoverHandler {}
|
||||
}
|
||||
Text {
|
||||
width: 120
|
||||
text: modelData["filename"] ?? ""
|
||||
color: "#dddddd"
|
||||
font.pixelSize: 11
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Text {
|
||||
width: 85
|
||||
text: modelData["mimetype"] ?? ""
|
||||
color: "#dddddd"
|
||||
font.pixelSize: 11
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Text {
|
||||
width: 75
|
||||
text: modelData["datasetSize"] ?? ""
|
||||
color: "#dddddd"
|
||||
font.pixelSize: 11
|
||||
elide: Text.ElideRight
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
Row {
|
||||
spacing: 4
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
Button {
|
||||
width: 50
|
||||
height: 26
|
||||
text: "↓"
|
||||
enabled: root.isRunning()
|
||||
onClicked: {
|
||||
root.pendingDownloadManifest = modelData
|
||||
var filename = modelData["filename"]
|
||||
|| modelData["cid"] || "download"
|
||||
manifestSaveDialog.currentFile = StandardPaths.writableLocation(
|
||||
StandardPaths.HomeLocation) + "/" + filename
|
||||
manifestSaveDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
width: 50
|
||||
height: 26
|
||||
text: "🗑"
|
||||
enabled: root.isRunning()
|
||||
onClicked: root.backend.remove(
|
||||
modelData["cid"] ?? "")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "No manifests yet"
|
||||
color: "#555555"
|
||||
font.pixelSize: 12
|
||||
visible: manifestListView.count === 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
@ -386,34 +692,35 @@ Rectangle {
|
||||
objectName: "spaceButton"
|
||||
text: "Space"
|
||||
onClicked: root.backend.space()
|
||||
anchors.top: cidDownloadButton.bottom
|
||||
anchors.right: downloadManifestsButton.left
|
||||
anchors.top: manifestTableContainer.bottom
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: logLevelField
|
||||
placeholderTextColor: "#999999"
|
||||
placeholderText: "Enter the log level to download"
|
||||
color: "black"
|
||||
// text: root.downloadCid
|
||||
onTextChanged: root.logLevel = text
|
||||
anchors.top: downloadManifestsButton.bottom
|
||||
anchors.topMargin: 50
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
Button {
|
||||
id: logLevelButton
|
||||
objectName: "logLevelButton"
|
||||
text: "Log level"
|
||||
onClicked: root.backend.updateLogLevel(root.logLevel)
|
||||
anchors.top: logLevelField.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
enabled: root.isRunning
|
||||
anchors.topMargin: 10
|
||||
}
|
||||
// ── Log level section ──────────────────────────────────────────────────
|
||||
// TextField {
|
||||
// id: logLevelField
|
||||
// placeholderTextColor: "#999999"
|
||||
// placeholderText: "Enter the log level to download"
|
||||
// color: "black"
|
||||
// // text: root.downloadCid
|
||||
// onTextChanged: root.logLevel = text
|
||||
// anchors.top: manifestTableContainer.bottom
|
||||
// anchors.topMargin: 30
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
// }
|
||||
|
||||
// Button {
|
||||
// id: logLevelButton
|
||||
// objectName: "logLevelButton"
|
||||
// text: "Log level"
|
||||
// onClicked: root.backend.updateLogLevel(root.logLevel)
|
||||
// anchors.top: logLevelField.bottom
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
// enabled: root.isRunning
|
||||
// anchors.topMargin: 10
|
||||
// }
|
||||
|
||||
// TextEdit {
|
||||
// id: selectableText
|
||||
@ -469,6 +776,22 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: manifestSaveDialog
|
||||
fileMode: FileDialog.SaveFile
|
||||
onAccepted: {
|
||||
if (root.pendingDownloadManifest) {
|
||||
root.backend.tryDownloadFile(
|
||||
root.pendingDownloadManifest["cid"],
|
||||
manifestSaveDialog.selectedFile)
|
||||
root.pendingDownloadManifest = null
|
||||
}
|
||||
}
|
||||
onRejected: {
|
||||
root.pendingDownloadManifest = null
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: debugPanel
|
||||
anchors.left: parent.left
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user