mirror of https://github.com/layters/testshop
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
457 lines
23 KiB
457 lines
23 KiB
// requires Qt version 5.12.8. See https://doc.qt.io/qt-5/qt5-intro.html
|
|
import QtQuick 2.12
|
|
import QtQuick.Controls 2.12 // (requires at least Qt 5.12 where QtQuick.Controls 1 is deprecated. See https://doc.qt.io/qt-5/qtquickcontrols-index.html#versions) // TextField, TextArea (multi-lined TextField), TextFieldStyle
|
|
import QtQuick.Layouts 1.12 // RowLayout, ColumnLayout, GridLayout, StackLayout, Layout
|
|
import QtGraphicalEffects 1.12 // LinearGradient
|
|
import Qt.labs.platform 1.1 // FileDialog (since Qt 5.8) // change to "import QtQuick.Dialogs" if using Qt 6.2
|
|
|
|
import neroshop.CurrencyExchangeRates 1.0
|
|
import FontAwesome 1.0
|
|
|
|
//import neroshop.Wallet 1.0
|
|
|
|
import "components"
|
|
import "components" as NeroshopComponents
|
|
import "pages"
|
|
import "pages/subpages"
|
|
|
|
ApplicationWindow {
|
|
id: mainWindow
|
|
visible: true
|
|
//visibility: (Script.getJsonRootObject()["window_mode"] == 1) ? "FullScreen" : "Windowed"
|
|
title: qsTr("neroshop" + " v" + neroshopVersion)
|
|
width: 1280//Script.getJsonRootObject()["window_width"]
|
|
height: 900//Script.getJsonRootObject()["window_height"]
|
|
minimumWidth: 1024
|
|
minimumHeight: 768
|
|
color: NeroshopComponents.Style.getColorsFromTheme()[0]
|
|
|
|
header: Rectangle {
|
|
color: NeroshopComponents.Style.getColorsFromTheme()[1]
|
|
height: 80//100 // width should be set automatically to the parent's width
|
|
visible: (!(pageStack.currentItem instanceof MainPage)) ? true : false;
|
|
|
|
Button {
|
|
id: neroshopLogoImageButton
|
|
visible: !settingsDialog.hideHomepageButton
|
|
property real iconSize: 30
|
|
icon.source: (NeroshopComponents.Style.darkTheme) ? "qrc:/assets/images/appicons/Vector_Illustrator Files/LogoLight.svg" : "qrc:/assets/images/appicons/Vector_Illustrator Files/LogoDark.svg"
|
|
icon.color: icon.color
|
|
icon.width: iconSize; icon.height: iconSize
|
|
display: AbstractButton.IconOnly
|
|
hoverEnabled: true
|
|
background: Rectangle {
|
|
color: "transparent"
|
|
radius: 5
|
|
////border.color: parent.hovered ? "#ffffff" : "transparent"
|
|
}
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
anchors.left: parent.left
|
|
anchors.leftMargin: 20
|
|
onClicked: {
|
|
navBar.uncheckAllButtons()
|
|
pageStack.pushPage("qrc:/qml/pages/HomePage.qml", StackView.Immediate)//pageStack.push(Qt.createComponent("qrc:/qml/pages/HomePage.qml"))//pageLoader.source = "qrc:/qml/pages/HomePage.qml"
|
|
console.log("Number of items in StackView: " + pageStack.depth)
|
|
}
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onPressed: mouse.accepted = false
|
|
cursorShape: Qt.PointingHandCursor
|
|
}
|
|
}
|
|
|
|
NeroshopComponents.SearchBar {
|
|
id: searchBar
|
|
anchors.left: (neroshopLogoImageButton.visible) ? neroshopLogoImageButton.right : parent.left
|
|
anchors.leftMargin: 20
|
|
anchors.top: parent.top; anchors.topMargin: 20
|
|
}
|
|
|
|
NeroshopComponents.NavigationalBar {
|
|
id: navBar
|
|
anchors.left: parent.right
|
|
anchors.leftMargin: (-this.width - 20)
|
|
anchors.top: parent.top
|
|
anchors.topMargin: 20
|
|
}
|
|
}
|
|
|
|
StackView {
|
|
id: pageStack
|
|
anchors.fill: parent
|
|
initialItem: MainPage {}
|
|
property bool canGoBack: (depth > 1)
|
|
property var lastPushedSource: ""
|
|
property var lastPushedProperties: {}
|
|
|
|
function pushPage(pageUrl, operation = StackView.Transition) {
|
|
if (lastPushedSource === pageUrl) {
|
|
console.log("Same page with same properties as the last pushed one. Not pushing.");
|
|
return;
|
|
}
|
|
|
|
let pageComponent = Qt.createComponent(pageUrl);
|
|
if (pageComponent.status !== Component.Ready) {
|
|
console.log("Component creation error:", pageComponent.errorString());
|
|
return;
|
|
}
|
|
|
|
lastPushedSource = pageUrl;
|
|
lastPushedProperties = null;
|
|
|
|
pageStack.push(pageComponent, null, operation);
|
|
}
|
|
|
|
function pushPageWithProperties(pageUrl, properties, operation = StackView.Transition) {
|
|
// Check if the new page is the same as the last pushed one
|
|
if (lastPushedSource === pageUrl && JSON.stringify(lastPushedProperties) === JSON.stringify(properties)) {
|
|
console.log("Same page with same properties as the last pushed one. Not pushing.");
|
|
return;
|
|
}
|
|
|
|
let component = Qt.createComponent(pageUrl);
|
|
if (component.status !== Component.Ready) {
|
|
console.log("Component creation error:", component.errorString());
|
|
return;
|
|
}
|
|
|
|
lastPushedSource = pageUrl;
|
|
lastPushedProperties = properties;
|
|
|
|
pageStack.push(component, properties, operation);
|
|
}
|
|
|
|
function goBack() {
|
|
if(!pageStack.canGoBack) return;
|
|
pageStack.pop();
|
|
if(pageStack.currentItem instanceof HomePage) {
|
|
console.log("Current page is Home (on back clicked)")
|
|
lastPushedSource = "qrc:/qml/pages/HomePage.qml"
|
|
lastPushedProperties = null
|
|
} else if(pageStack.currentItem instanceof CatalogPage) {
|
|
console.log("Current page is Catalog (on back clicked)")
|
|
lastPushedSource = "qrc:/qml/pages/CatalogPage.qml"
|
|
lastPushedProperties = pageStack.get(depth - 1).model // still pushes to StackView (:/) but that's ok // Note: pageStack.currentItem.model returns false when compared to `lastPushedProperties` whereas pageStack.get(depth - 1) returns true
|
|
} else if(pageStack.currentItem instanceof MessagesPage) {
|
|
console.log("Current page is Messages (on back clicked)")
|
|
navBar.checkButtonByIndex(2)
|
|
lastPushedSource = "qrc:/qml/pages/subpages/MessagesPage.qml"
|
|
} else {
|
|
lastPushedSource = ""
|
|
lastPushedProperties = null
|
|
}
|
|
}
|
|
|
|
function goToMain() {
|
|
pageStack.pop(null); // unwinding
|
|
}
|
|
}
|
|
|
|
NeroshopComponents.SettingsDialog {
|
|
id: settingsDialog
|
|
anchors.centerIn: Overlay.overlay//parent: Overlay.overlay; anchors.centerIn: parent
|
|
dim: true
|
|
enter: Transition {
|
|
NumberAnimation { property: "opacity"; from: 0.0; to: 1.0 }
|
|
}
|
|
exit: Transition {
|
|
NumberAnimation { property: "opacity"; from: 1.0; to: 0.0 }
|
|
}
|
|
}
|
|
|
|
NeroshopComponents.MessageBox {
|
|
id: messageBox
|
|
title: "message"
|
|
x: mainWindow.x + (mainWindow.width - this.width) / 2
|
|
y: mainWindow.y + (mainWindow.height - this.height) / 2
|
|
buttonRow.state: "centered"; buttonRow.width: 300
|
|
}
|
|
|
|
NeroshopComponents.MessageBox {
|
|
id: monerodMessageBox
|
|
title: "prompt"
|
|
x: mainWindow.x + (mainWindow.width - this.width) / 2
|
|
y: mainWindow.y + (mainWindow.height - this.height) / 2
|
|
buttonModel: ["Cancel", "OK"]
|
|
buttonRow.state: "centered"; buttonRow.width: 300
|
|
Component.onCompleted: {
|
|
buttonAt(0).onClickedCallback = function() { close() }
|
|
buttonAt(1).color = "#4169e1"//"#4682b4"
|
|
buttonAt(1).onClickedCallback = function() {
|
|
console.log("Now connecting to a local node ...")
|
|
Wallet.daemonConnect();//(moneroDaemonRpcLoginUser.text, moneroDaemonRpcLoginPwd.text)
|
|
close();
|
|
}
|
|
}
|
|
}
|
|
// navigating between different pages: https://stackoverflow.com/a/15655043
|
|
// The footer item is positioned to the bottom, and resized to the width of the window
|
|
// Custom ToolBar
|
|
footer: Rectangle {
|
|
height: 40//; width: parent.width// width is automatically set to parent's width by default so no need to manually set the footer width
|
|
color: NeroshopComponents.Style.getColorsFromTheme()[1]
|
|
|
|
Row {
|
|
anchors.fill: parent//anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.rightMargin: 20 // use leftMargin only if using layoutDirection is Qt.LeftToRight
|
|
spacing: 20 // Spacing between each Row item
|
|
layoutDirection: Qt.RightToLeft
|
|
|
|
Rectangle {
|
|
width: themeSwitcher.width + settingsButton.width
|
|
height: footer.height//themeSwitcher.height
|
|
color: "transparent"
|
|
//border.color: "blue"
|
|
|
|
NeroshopComponents.ThemeSwitch {
|
|
id: themeSwitcher
|
|
width: 40
|
|
anchors.left: settingsButton.right
|
|
}
|
|
|
|
Button {
|
|
id: settingsButton
|
|
display: AbstractButton.IconOnly
|
|
checkable: true
|
|
checked: settingsDialog.visible
|
|
icon.source: "qrc:/assets/images/cog.png"//"/tools.png"
|
|
icon.color: (NeroshopComponents.Style.darkTheme) ? "#8fa4ff" : "#001677"////hovered ? "#001677" : "#ffffff"//(!checked && hovered) ? "#001677" : "#ffffff"
|
|
//icon.width: parent.width//footer.height
|
|
//icon.height: parent.height//32//footer.height
|
|
hoverEnabled: true
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
background: Rectangle {
|
|
color: "transparent"//(parent.checked) ? "#001677" : "transparent"
|
|
radius: 3
|
|
border.color: parent.hovered ? parent.icon.color : this.color//"#001677"
|
|
//border.width: (!parent.checked && parent.hovered) ? 1 : 0
|
|
}
|
|
onClicked: {
|
|
settingsDialog.open()
|
|
}
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onPressed: mouse.accepted = false
|
|
cursorShape: Qt.PointingHandCursor
|
|
}
|
|
NeroshopComponents.Hint {
|
|
visible: parent.hovered
|
|
x: parent.x + (parent.width - this.width) / 2 // Popups don't have anchors :(
|
|
height: contentHeight + 20; width: contentWidth + 20
|
|
bottomMargin : footer.height + 5
|
|
text: qsTr("Settings")
|
|
pointer.visible: false
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: daemonSyncBar.width
|
|
height: footer.height
|
|
color: "transparent"
|
|
//border.color: "red"
|
|
|
|
NeroshopComponents.ProgressBar {
|
|
id: daemonSyncBar
|
|
radius: 20
|
|
foregroundColor: "#564978"
|
|
backgroundColor: "#d9d9d9"
|
|
//textObject.visible: true
|
|
hoverEnabled: true
|
|
anchors.verticalCenter: parent.verticalCenter//anchors.top: parent.top; anchors.topMargin: (parent.height - this.height) / 2 // center vertically on footer (height)
|
|
value: DaemonManager.daemonProgress//0.5 // placeholder value
|
|
barWidth: 200
|
|
|
|
NeroshopComponents.Hint {
|
|
visible: parent.hovered
|
|
x: parent.x + (parent.width - this.width) / 2 // Popups don't have anchors :(
|
|
height: contentHeight + 20; width: (contentWidth > parent.width) ? 300 : parent.width
|
|
bottomMargin : footer.height + 5
|
|
text: qsTr("neromon\n%1 %2").arg(DaemonManager.daemonStatusText/*(parent.value <= 0.0) ? "Disconnected" : ((parent.value > 0.0 && parent.value < 1.0) ? "Synchronizing" : "Connected")*/).arg((parent.value > 0.0 && parent.value < 1.0) ? ("(" + (parent.value * 100).toString() + "%)") : "")
|
|
pointer.visible: false
|
|
}
|
|
}
|
|
}
|
|
Rectangle {
|
|
width: moneroDaemonSyncBar.width
|
|
height: footer.height
|
|
color: "transparent"
|
|
//border.color: "plum"
|
|
|
|
NeroshopComponents.ProgressBar {
|
|
id: moneroDaemonSyncBar
|
|
radius: daemonSyncBar.radius
|
|
foregroundColor: NeroshopComponents.Style.moneroOrangeColor
|
|
backgroundColor: daemonSyncBar.backgroundColor//(NeroshopComponents.Style.darkTheme) ? "#8c8c8c" : NeroshopComponents.Style.moneroGrayColor
|
|
//textObject.visible: true
|
|
//textObject.text: "wallet sync: " + (this.value * 100).toString() + "%"
|
|
//textObject.color: "#ffffff"
|
|
hoverEnabled: true
|
|
anchors.verticalCenter: parent.verticalCenter//anchors.top: parent.top; anchors.topMargin: (parent.height - this.height) / 2
|
|
////value: Wallet.opened ? Wallet.getSyncPercentage() : 0.0 // this does not work (fails to update value so we use Timer instead)
|
|
barWidth: daemonSyncBar.barWidth
|
|
property string title: "monerod"
|
|
Timer {
|
|
interval: 1 // trigger every x miliseconds
|
|
running: true
|
|
repeat: true // If repeat is true the timer is triggered repeatedly at the specified interval
|
|
onTriggered: {
|
|
moneroDaemonSyncBar.value = Wallet.getSyncPercentage()
|
|
|
|
if(settingsDialog.hideWalletSyncBarOnFull && (moneroDaemonSyncBar.value >= 1.0)) {
|
|
moneroDaemonSyncBar.parent.visible = false
|
|
}
|
|
}
|
|
}
|
|
|
|
NeroshopComponents.Hint {
|
|
visible: parent.hovered
|
|
x: parent.x + (parent.width - this.width) / 2
|
|
height: contentHeight + 20; width: (contentWidth > parent.width) ? 300 : parent.width
|
|
bottomMargin : footer.height + 5
|
|
text: qsTr("%1\n%2 %3%4").arg(moneroDaemonSyncBar.title).arg((!Wallet.opened || parent.value <= 0.0) ? ((moneroDaemonSyncBar.title != "monerod") ? "Waiting" : "Disconnected") : ((parent.value > 0.0 && parent.value < 1.0) ? Wallet.getSyncMessage() : "Connected")).arg((parent.value > 0.0 && parent.value != 1.0) ? ("(" + (parent.value * 100).toFixed(2) + "%)") : "").arg((!Wallet.opened || parent.value <= 0.0) ? "" : ((parent.value > 0.0 && parent.value != 1.0) ? ("\nBlocks remaining: " + Wallet.getSyncHeight() + " / " + Wallet.getSyncEndHeight()) : ""))
|
|
pointer.visible: false
|
|
}
|
|
}
|
|
}
|
|
//Rectangle {
|
|
//}
|
|
Rectangle {
|
|
id: networkMonitor
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: networkMonitorRow.width; height: networkMonitorRow.height
|
|
color: "transparent"
|
|
//border.color: "plum"
|
|
property var networkStatus: null
|
|
|
|
Row {
|
|
id: networkMonitorRow
|
|
anchors.centerIn: parent
|
|
spacing: 5
|
|
Text {
|
|
id: peerCounterText
|
|
visible: (Number(text) > 0)
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
text: "0"
|
|
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
|
|
font.pointSize: 10
|
|
|
|
Timer {
|
|
interval: 5000
|
|
running: true
|
|
repeat: true
|
|
onTriggered: {
|
|
networkMonitor.networkStatus = Backend.getNetworkStatus()
|
|
if(networkMonitor.networkStatus === null) return;
|
|
if(networkMonitor.networkStatus.hasOwnProperty("connected_peers")) {
|
|
peerCounterText.text = networkMonitor.networkStatus.connected_peers
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: networkStatusIndicator
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: 14; height: width
|
|
color: {
|
|
let nodeCount = Number((networkMonitor.networkStatus == null) ? "-1" : (networkMonitor.networkStatus.hasOwnProperty("active_peers") ? networkMonitor.networkStatus.active_peers : "-1"))//Number(peerCounterText.text)
|
|
// Apply the color-coded scale based on the number of online nodes
|
|
if (nodeCount >= 30) {
|
|
return "#228b22";//"#2e8b57" // Darker shade of green for 30 or more nodes
|
|
} else if (nodeCount >= 20) {
|
|
return "#81ac2a"; // Green for 20 to 29 nodes (Optimal network status)
|
|
} else if (nodeCount >= 10) {
|
|
return "#ffa500"; // Yellow for 10 to 19 nodes (Mildly compromised network status)
|
|
} else if (nodeCount >= 5) {
|
|
return "#ff4500"; // Orangered for 5 to 9 nodes (Moderately compromised network status)
|
|
} else if (nodeCount >= 0) {
|
|
return "#b22222"; // Bright red for 0-4 nodes (Severely compromised network status)
|
|
} else {
|
|
return "#808080"; // Gray for -1 nodes (Unknown network status)
|
|
}
|
|
}
|
|
radius: width / 2
|
|
}
|
|
}
|
|
|
|
HoverHandler {
|
|
id: networkMonitorHoverHandler
|
|
}
|
|
|
|
NeroshopComponents.Hint {
|
|
visible: networkMonitorHoverHandler.hovered
|
|
height: contentHeight + 20; width: contentWidth + 20
|
|
bottomMargin : footer.height + 5
|
|
text: qsTr("Network peers: %1\nActive peers: %2\nIdle peers: %3").arg(peerCounterText.text).arg((networkMonitor.networkStatus == null) ? "0" : (networkMonitor.networkStatus.hasOwnProperty("active_peers") ? networkMonitor.networkStatus.active_peers : "0")).arg((networkMonitor.networkStatus == null) ? "0" : (networkMonitor.networkStatus.hasOwnProperty("idle_peers") ? networkMonitor.networkStatus.idle_peers : "0"))
|
|
pointer.visible: false
|
|
}
|
|
}
|
|
}
|
|
/*Row {
|
|
width: priceDisplayText.contentWidth
|
|
height: footer.height
|
|
anchors.left: footer.left
|
|
anchors.leftMargin: 20
|
|
anchors.top: footer.top
|
|
anchors.topMargin: 0*/
|
|
Rectangle {
|
|
anchors.left: footer.left
|
|
anchors.leftMargin: 20
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
width: priceDisplayRow.width
|
|
height: childrenRect.height
|
|
color: "transparent"
|
|
visible: !settingsDialog.hidePriceDisplay
|
|
//border.color: NeroshopComponents.Style.moneroOrangeColor
|
|
|
|
Row {
|
|
id: priceDisplayRow
|
|
spacing: 10
|
|
Text {
|
|
id: priceDisplayText
|
|
property string scriptCurrency: settingsDialog.currency.currentText
|
|
property string currency: Backend.isSupportedCurrency(scriptCurrency) ? scriptCurrency : "usd"
|
|
readonly property double price: CurrencyExchangeRates.getXmrPrice(priceDisplayText.currency)
|
|
text: qsTr(FontAwesome.monero + " %1%2").arg(Backend.getCurrencySign(currency)).arg(price.toFixed(Backend.getCurrencyDecimals(currency)))
|
|
color: (NeroshopComponents.Style.darkTheme) ? "#ffffff" : "#000000"
|
|
font.bold: true
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
Text {
|
|
id: priceChangePercentageText
|
|
property double percent: 0.00
|
|
visible: false // hide this for now until I can get the actual price change percent value
|
|
text: qsTr("%1%2%").arg((percent >= 0) ? "+" : "").arg(percent.toFixed(2))
|
|
color: (percent >= 0) ? "green" : "red"
|
|
anchors.verticalCenter: priceDisplayText.verticalCenter
|
|
font.pointSize: 10
|
|
font.bold: true
|
|
}
|
|
|
|
HoverHandler {
|
|
id: priceDisplayHoverHandler
|
|
}
|
|
|
|
NeroshopComponents.Hint {
|
|
visible: priceDisplayHoverHandler.hovered // <- uncomment this to make the tooltip visible on hover
|
|
height: contentHeight + 20; width: contentWidth + 20
|
|
bottomMargin : footer.height + 5
|
|
text: qsTr("XMR / %1").arg(priceDisplayText.currency.toUpperCase())
|
|
pointer.visible: false
|
|
}
|
|
}
|
|
}
|
|
//}
|
|
}
|
|
}
|
|
// error: module "QtQuick.Layouts" version 1.15 is not installed (fix by changing the version specified in /usr/lib/x86_64-linux-gnu/qt5/qml/QtQuick/Layouts/plugins.qmltypes)
|
|
// error: module "Qt.labs.platform" is not installed (fix: sudo apt install qml-module-qt-labs-platform)
|
|
// error: gtk errors on "wallet_upload_button" clicked (fix: https://forums.wxwidgets.org/viewtopic.php?t=47187)
|
|
|
|
// install: qtbase5-dev for "../cmake/Qt5/Qt5Config.cmake"
|
|
// error: /usr/lib/x86_64-linux-gnu/cmake/Qt5/Qt5Config.cmake will set Qt5_FOUND to FALSE so package "Qt5" is considered to be NOT FOUND due to Qml and Quick configuration files (/usr/lib/x86_64-linux-gnu/cmake/Qt5Qml/Qt5QmlConfig.cmake; /usr/lib/x86_64-linux-gnu/cmake/Qt5Quick/Qt5QuickConfig.cmake) not being found (fix: sudo apt install qtdeclarative5-dev)
|
|
// error: module "QtQuick" is not installed (fix: qml-module-qtquick-controls qml-module-qtquick-controls2). This will also install qml-module-qtgraphicaleffects, qml-module-qtquick-layouts, qml-module-qtquick-window2, and qml-module-qtquick2
|
|
// error: module "QtQuick.Shapes" is not installed (fix: qml-module-qtquick-shapes)
|