mirror of
https://github.com/logos-storage/logos-storage-app-skeleton.git
synced 2026-06-14 04:19:25 +00:00
Temporary commit
This commit is contained in:
parent
2855adeb86
commit
fa7664720c
@ -130,7 +130,7 @@ endif()
|
||||
# Discover the required dependencies.
|
||||
# Without this discovery part, the dependencies cannot be found.
|
||||
# COMPONENTS is kind of generic for Qt modules.
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Widgets RemoteObjects Quick QuickWidgets)
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Widgets RemoteObjects Quick QuickWidgets Network)
|
||||
|
||||
# Get the real path to handle symlinks correctly
|
||||
#get_filename_component(REAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPATH)
|
||||
@ -419,6 +419,7 @@ target_link_libraries(storage_ui PRIVATE
|
||||
Qt6::RemoteObjects
|
||||
Qt6::Quick
|
||||
Qt6::QuickWidgets
|
||||
Qt6::Network
|
||||
component-interfaces
|
||||
)
|
||||
|
||||
|
||||
@ -8,6 +8,8 @@
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QLocale>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
|
||||
// StorageBackend is responsible for managing the interaction with the storage module.
|
||||
// It is mocked in the QML.
|
||||
@ -866,6 +868,55 @@ QString StorageBackend::buildConfig(const QString& dataDir, int discPort, int tc
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Indented);
|
||||
}
|
||||
|
||||
QString StorageBackend::buildUpnpConfig(const QString& dataDir) {
|
||||
debug("StorageBackend::buildUpnpConfig called with dataDir=" + dataDir);
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
|
||||
QJsonObject obj = doc.object();
|
||||
|
||||
obj["data-dir"] = dataDir;
|
||||
|
||||
QJsonArray bootstrapArray;
|
||||
for (const QString& node : BOOTSTRAP_NODES) {
|
||||
bootstrapArray.append(node);
|
||||
}
|
||||
obj["nat"] = "upnp";
|
||||
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Indented);
|
||||
}
|
||||
|
||||
QString StorageBackend::buildNatExtConfig(const QString& dataDir, int tcpPort) {
|
||||
debug("StorageBackend::buildUpnpConfig called with dataDir=" + dataDir +
|
||||
" and tcpPort=" + QString::number(tcpPort));
|
||||
|
||||
QJsonDocument doc = QJsonDocument::fromJson(m_configJson.toUtf8());
|
||||
QJsonObject obj = doc.object();
|
||||
|
||||
obj["data-dir"] = dataDir;
|
||||
|
||||
QJsonArray bootstrapArray;
|
||||
for (const QString& node : BOOTSTRAP_NODES) {
|
||||
bootstrapArray.append(node);
|
||||
}
|
||||
|
||||
debug("Retrieving the public IP");
|
||||
|
||||
QNetworkAccessManager manager;
|
||||
QEventLoop loop;
|
||||
QNetworkReply* reply = manager.get(QNetworkRequest(QUrl("https://echo.codex.storage/")));
|
||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||
loop.exec();
|
||||
|
||||
QString ip = reply->readAll().trimmed();
|
||||
reply->deleteLater();
|
||||
|
||||
debug("Public IP detected:" + ip);
|
||||
|
||||
obj["nat"] = "extip:" + ip;
|
||||
|
||||
return QJsonDocument(obj).toJson(QJsonDocument::Indented);
|
||||
}
|
||||
|
||||
QString StorageBackend::buildConfigFromFile(const QString& path) {
|
||||
qDebug() << "StorageBackend::buildConfigFromFile called";
|
||||
|
||||
|
||||
@ -85,6 +85,8 @@ class StorageBackend : public QObject {
|
||||
void reloadIfChanged(const QString& configJson);
|
||||
void status(StorageStatus status);
|
||||
QString buildConfig(const QString& dataDir, int discPort, int tcpPort);
|
||||
QString buildUpnpConfig(const QString& dataDir);
|
||||
QString buildNatExtConfig(const QString& dataDir, int tcpPort);
|
||||
QString buildConfigFromFile(const QString& path);
|
||||
void saveUserConfig(const QString& configJson);
|
||||
|
||||
|
||||
@ -132,6 +132,9 @@ qt_add_qml_module(appqml
|
||||
StartNode.qml
|
||||
LogosTextField.qml
|
||||
LogosStorageButton.qml
|
||||
Nat.qml
|
||||
LogosStorageLayout.qml
|
||||
PortForwarding.qml
|
||||
)
|
||||
|
||||
# Set up QML module directory for runtime
|
||||
|
||||
109
src/qml/Main.qml
109
src/qml/Main.qml
@ -1,50 +1,28 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtCore
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
// qmllint disable unqualified
|
||||
Item {
|
||||
id: root
|
||||
implicitWidth: 600
|
||||
implicitHeight: 400
|
||||
implicitWidth: 800
|
||||
implicitHeight: 800
|
||||
|
||||
property var backend: mockBackend
|
||||
|
||||
// Timer {
|
||||
// readonly property int running: 2
|
||||
|
||||
// id: timer
|
||||
// interval: 2000
|
||||
// repeat: false
|
||||
// onTriggered: {
|
||||
// console.log("timer triggered")
|
||||
// // root.backend.status = running
|
||||
// // root.backend.startCompleted()
|
||||
// // console.info(root.backend.status)
|
||||
// }
|
||||
// }
|
||||
QtObject {
|
||||
id: mockBackend
|
||||
|
||||
readonly property bool isMock: true
|
||||
property int status
|
||||
|
||||
signal startCompleted
|
||||
signal startFailed
|
||||
signal stopCompleted
|
||||
|
||||
function updateBasicConfig(dataDir, discPort) {
|
||||
console.log("updateBasicConfig", dataDir, discPort)
|
||||
}
|
||||
signal initCompleted
|
||||
|
||||
function start() {
|
||||
// timer.start()
|
||||
console.log("mock start callde")
|
||||
}
|
||||
|
||||
function stop() {
|
||||
root.backend.stopCompleted()
|
||||
console.log("mock start called")
|
||||
}
|
||||
|
||||
function defaultDataDir() {
|
||||
@ -53,9 +31,15 @@ Item {
|
||||
|
||||
function buildConfig() {}
|
||||
|
||||
function saveUserConfig() {}
|
||||
|
||||
function reloadIfChanged() {}
|
||||
|
||||
function init() {}
|
||||
function buildUpnpConfig() {}
|
||||
|
||||
function buildNatExtConfig() {}
|
||||
|
||||
function stop() {}
|
||||
}
|
||||
|
||||
Settings {
|
||||
@ -66,59 +50,90 @@ Item {
|
||||
property int tcpPort: 0
|
||||
property string dataDir: ""
|
||||
property bool onboardingCompleted: false
|
||||
property string natStrategy: "any"
|
||||
}
|
||||
|
||||
StackView {
|
||||
id: stackView
|
||||
anchors.fill: parent
|
||||
initialItem: onboarding
|
||||
initialItem: onboardingComponent
|
||||
}
|
||||
|
||||
Component {
|
||||
id: onboarding
|
||||
id: onboardingComponent
|
||||
|
||||
OnBoarding {
|
||||
id: onboardingInstance
|
||||
backend: root.backend
|
||||
discoveryPort: settings.discoveryPort
|
||||
tcpPort: settings.tcpPort
|
||||
dataDir: settings.dataDir.length > 0 ? settings.dataDir : root.backend.defaultDataDir()
|
||||
// discoveryPort: settings.discoveryPort
|
||||
// tcpPort: settings.tcpPort
|
||||
dataDir: settings.dataDir
|
||||
|
||||
onCompleted: {
|
||||
settings.discoveryPort = discoveryPort
|
||||
// settings.discoveryPort = discoveryPort
|
||||
settings.dataDir = dataDir
|
||||
settings.tcpPort = tcpPort
|
||||
// settings.tcpPort = tcpPort
|
||||
settings.onboardingCompleted = true
|
||||
|
||||
let config = root.backend.buildConfig(dataDir,
|
||||
discoveryPort, tcpPort)
|
||||
root.backend.saveUserConfig(config)
|
||||
root.backend.reloadIfChanged(config)
|
||||
root.backend.start()
|
||||
|
||||
stackView.push(startNodeView)
|
||||
stackView.push(natComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: storageView
|
||||
id: natComponent
|
||||
|
||||
Nat {
|
||||
onCompleted: function (enabled) {
|
||||
if (enabled) {
|
||||
settings.natStrategy = "upnp"
|
||||
let config = root.backend.buildUpnpConfig(settings.dataDir)
|
||||
root.backend.reloadIfChanged(config)
|
||||
root.backend.start()
|
||||
stackView.push(startNodeComponent)
|
||||
} else {
|
||||
stackView.push(portForwardingComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: storageComponent
|
||||
|
||||
StorageView {
|
||||
backend: root.backend
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: startNodeView
|
||||
id: startNodeComponent
|
||||
|
||||
StartNode {
|
||||
backend: root.backend
|
||||
|
||||
onBack: {
|
||||
root.backend.stop()
|
||||
stackView.pop()
|
||||
}
|
||||
|
||||
onNext: {
|
||||
stackView.push(storageView)
|
||||
stackView.push(storageComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: portForwardingComponent
|
||||
|
||||
PortForwarding {
|
||||
onPortTcpSelected: function (port) {
|
||||
settings.tcpPort = port
|
||||
settings.natStrategy = "extip"
|
||||
let config = root.backend.buildNatExtConfig(settings.dataDir,
|
||||
port)
|
||||
root.backend.reloadIfChanged(config)
|
||||
root.backend.start()
|
||||
stackView.push(startNodeComponent)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -133,7 +148,7 @@ Item {
|
||||
function onInitCompleted() {
|
||||
if (settings.onboardingCompleted) {
|
||||
root.backend.start()
|
||||
stackView.replace(storageView, StackView.Immediate)
|
||||
stackView.replace(storageComponent, StackView.Immediate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,22 +1,18 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import Logos.Theme
|
||||
import Logos.Controls
|
||||
|
||||
Rectangle {
|
||||
LogosStorageLayout {
|
||||
id: root
|
||||
color: Theme.palette.background
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: 600
|
||||
implicitHeight: 400
|
||||
|
||||
property int discoveryPort: 8090
|
||||
property int tcpPort: 0
|
||||
property var backend: mockBackend
|
||||
property var local: false
|
||||
property string dataDir: backend.defaultDataDir()
|
||||
|
||||
signal completed
|
||||
|
||||
QtObject {
|
||||
@ -30,79 +26,52 @@ Rectangle {
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacing.medium
|
||||
width: 400
|
||||
|
||||
LogosText {
|
||||
id: titleText
|
||||
font.pixelSize: Theme.typography.titleText
|
||||
text: "Logos Storage"
|
||||
// anchors.verticalCenter: parent.verticalCenter
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: discoveryPortColumn
|
||||
spacing: Theme.spacing.tiny
|
||||
Layout.fillWidth: true
|
||||
|
||||
LogosText {
|
||||
text: "Discovery port"
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.text
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
isValid: acceptableInput && text.length > 0
|
||||
id: discoveryPortTextField
|
||||
placeholderText: "Enter the discovery port"
|
||||
text: root.discoveryPort
|
||||
validator: IntValidator {
|
||||
bottom: 1
|
||||
top: 65535
|
||||
}
|
||||
onTextChanged: {
|
||||
if (isValid) {
|
||||
root.discoveryPort = parseInt(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
LogosText {
|
||||
id: questionText
|
||||
font.pixelSize: Theme.typography.titleText
|
||||
text: "First, let's choose the storage folder"
|
||||
Layout.alignment: Qt.AlignCenter
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: tcpPortColumn
|
||||
spacing: Theme.spacing.tiny
|
||||
Layout.fillWidth: true
|
||||
// ColumnLayout {
|
||||
// id: discoveryPortColumn
|
||||
// spacing: Theme.spacing.tiny
|
||||
// Layout.fillWidth: true
|
||||
|
||||
LogosText {
|
||||
text: "TCP port"
|
||||
font.pixelSize: Theme.typography.secondaryText
|
||||
color: Theme.palette.text
|
||||
}
|
||||
|
||||
LogosTextField {
|
||||
isValid: acceptableInput && text.length > 0
|
||||
id: tcpPortTextField
|
||||
placeholderText: "Enter the TCP port"
|
||||
text: root.tcpPort
|
||||
validator: IntValidator {
|
||||
bottom: 0
|
||||
top: 65535
|
||||
}
|
||||
onTextChanged: {
|
||||
if (isValid) {
|
||||
root.tcpPort = parseInt(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// LogosText {
|
||||
// text: "Discovery port"
|
||||
// font.pixelSize: Theme.typography.secondaryText
|
||||
// color: Theme.palette.text
|
||||
// }
|
||||
|
||||
// LogosTextField {
|
||||
// isValid: acceptableInput && text.length > 0
|
||||
// id: discoveryPortTextField
|
||||
// placeholderText: "Enter the discovery port"
|
||||
// text: root.discoveryPort
|
||||
// validator: IntValidator {
|
||||
// bottom: 1
|
||||
// top: 65535
|
||||
// }
|
||||
// onTextChanged: {
|
||||
// if (isValid) {
|
||||
// root.discoveryPort = parseInt(text)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
ColumnLayout {
|
||||
spacing: Theme.spacing.tiny
|
||||
Layout.fillWidth: true
|
||||
|
||||
LogosText {
|
||||
text: "Data dir"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Theme.spacing.tiny
|
||||
|
||||
@ -130,6 +99,20 @@ Rectangle {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Column {
|
||||
// CheckBox {
|
||||
// text: "Do you want to connect to a local network ?"
|
||||
// checked: false
|
||||
// onCheckedChanged: root.local = checked
|
||||
// }
|
||||
|
||||
// LogosText {
|
||||
// font.pixelSize: Theme.typography.secondaryText
|
||||
// text: "You will not "
|
||||
// Layout.alignment: Qt.AlignCenter
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
LogosStorageButton {
|
||||
@ -138,8 +121,11 @@ Rectangle {
|
||||
anchors.right: parent.right
|
||||
anchors.bottomMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
enabled: discoveryPortTextField.acceptableInput
|
||||
&& tcpPortTextField.acceptableInput && dataDirTextField.isValid
|
||||
onClicked: root.completed()
|
||||
enabled: dataDirTextField.isValid
|
||||
// enabled: discoveryPortTextField.acceptableInput
|
||||
// && tcpPortTextField.acceptableInput && dataDirTextField.isValid
|
||||
onClicked: function () {
|
||||
root.completed()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import Logos.Controls
|
||||
import Logos.Theme
|
||||
|
||||
@ -11,40 +12,67 @@ Rectangle {
|
||||
implicitWidth: 600
|
||||
implicitHeight: 400
|
||||
|
||||
property var backend
|
||||
property var backend: mockBackend
|
||||
property string status: ""
|
||||
property string title: "Starting your node...."
|
||||
property bool starting: true
|
||||
property bool success: false
|
||||
|
||||
signal back
|
||||
signal next
|
||||
|
||||
function onNodeStarted() {
|
||||
root.starting = false
|
||||
root.status = "Logos Storage started successfully."
|
||||
root.title = "Success"
|
||||
root.success = true
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: mockBackend
|
||||
|
||||
readonly property bool isMock: true
|
||||
property string configJson: "{}"
|
||||
|
||||
signal startCompleted
|
||||
signal startFailed
|
||||
signal nodeStarted
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 2000
|
||||
running: root.backend && root.backend.isMock === true
|
||||
onTriggered: {
|
||||
console.log("timer triggered")
|
||||
root.onNodeStarted()
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: root.backend
|
||||
|
||||
function onStartCompleted() {
|
||||
console.log("onStartCompleted received")
|
||||
root.starting = false
|
||||
root.status = "Logos Storage started successfully."
|
||||
root.success = true
|
||||
console.log("onStartCompleted")
|
||||
root.onNodeStarted()
|
||||
}
|
||||
|
||||
function onStartFailed(error) {
|
||||
console.log("onStartFailed received")
|
||||
root.starting = false
|
||||
root.title = "Error"
|
||||
root.status = "Failed to start: " + error
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.centerIn: parent
|
||||
anchors.fill: parent
|
||||
anchors.margins: 20
|
||||
anchors.bottomMargin: 60
|
||||
spacing: Theme.spacing.medium
|
||||
width: 400
|
||||
|
||||
LogosText {
|
||||
id: titleText
|
||||
font.pixelSize: Theme.typography.titleText
|
||||
text: "Starting your node...."
|
||||
text: root.title
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
|
||||
@ -4,12 +4,13 @@ import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import QtCore
|
||||
|
||||
// qmllint disable unqualified
|
||||
Rectangle {
|
||||
id: root
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
implicitWidth: 600
|
||||
implicitHeight: 600
|
||||
implicitWidth: 800
|
||||
implicitHeight: 800
|
||||
color: "#000000"
|
||||
|
||||
property var backend: mockBackend
|
||||
@ -63,9 +64,12 @@ Rectangle {
|
||||
property var status: root.stopped
|
||||
property var debugLogs: "Hello !"
|
||||
property var configJson: "{}"
|
||||
property url cid: ""
|
||||
property string uploadStatus: ""
|
||||
property int uploadProgress: 0
|
||||
property var manifests: []
|
||||
property var quotaMaxBytes: 20 * 1024 * 1024 * 1024 // 20 GB default
|
||||
property var quotaUsedBytes: 0
|
||||
property var quotaReservedBytes: 0
|
||||
|
||||
function start(newConfigJson) {
|
||||
status = root.running
|
||||
@ -74,63 +78,6 @@ Rectangle {
|
||||
function stop() {
|
||||
status = root.stopped
|
||||
}
|
||||
|
||||
function tryPeerConnect(peerId) {
|
||||
console.log("Attempting peer connection...")
|
||||
}
|
||||
|
||||
function tryDebug() {
|
||||
console.log("Attempting peer connection...")
|
||||
}
|
||||
|
||||
function spr() {}
|
||||
|
||||
function showPeerId() {}
|
||||
|
||||
function version() {}
|
||||
|
||||
function dataDir() {}
|
||||
|
||||
function tryUploadFinalize() {
|
||||
console.log("Attempting upload finalize")
|
||||
}
|
||||
|
||||
function tryUploadFile(file) {
|
||||
console.log("Attempting upload file")
|
||||
}
|
||||
|
||||
function tryDownloadFile(cid, file) {
|
||||
console.log("Attempting download a file", cid, file)
|
||||
}
|
||||
|
||||
function exists(cid) {
|
||||
console.log("Attempting exists", cid)
|
||||
}
|
||||
|
||||
function fetch(cid) {
|
||||
console.log("Attempting fetch", cid)
|
||||
}
|
||||
|
||||
function remove(cid) {
|
||||
console.log("Attempting remove", cid)
|
||||
}
|
||||
|
||||
function downloadManifest(cid) {
|
||||
console.log("Attempting downloadManifest", cid)
|
||||
}
|
||||
|
||||
function downloadManifests() {
|
||||
console.log("Attempting downloadManifests")
|
||||
}
|
||||
|
||||
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) {
|
||||
@ -157,13 +104,15 @@ Rectangle {
|
||||
}
|
||||
|
||||
Button {
|
||||
property var isStopped: root.backend.status == root.stopped
|
||||
|
||||
id: startStopButton
|
||||
objectName: "startStopButton"
|
||||
anchors.leftMargin: 50
|
||||
text: root.startStopText()
|
||||
enabled: root.canStartStop()
|
||||
onClicked: root.backend.status == root.stopped ? root.backend.start(
|
||||
jsonEditor.text) : root.backend.stop()
|
||||
onClicked: isStopped ? root.backend.start(
|
||||
jsonEditor.text) : root.backend.stop()
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: statusTextElement.bottom
|
||||
anchors.topMargin: 10
|
||||
@ -216,7 +165,7 @@ Rectangle {
|
||||
implicitHeight: 6
|
||||
|
||||
Rectangle {
|
||||
width: parent.width * parent.parent.visualPosition
|
||||
width: parent.width * control.visualPosition
|
||||
height: parent.height
|
||||
radius: 3
|
||||
color: "#4CAF50"
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
#include <QGuiApplication>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QtQml>
|
||||
#include <QQmlDebuggingEnabler>
|
||||
|
||||
static QQmlTriviallyDestructibleDebuggingEnabler enabler;
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user