Merge pull request #1793

85cd428 Merchant page (xmrdsc)
This commit is contained in:
luigi1111 2019-01-09 17:43:31 -06:00
commit 5a682b37ec
No known key found for this signature in database
GPG Key ID: F4ACA0183641E010
28 changed files with 1250 additions and 452 deletions

View File

@ -61,6 +61,7 @@ Rectangle {
signal miningClicked() signal miningClicked()
signal signClicked() signal signClicked()
signal keysClicked() signal keysClicked()
signal merchantClicked()
function selectItem(pos) { function selectItem(pos) {
menuColumn.previousButton.checked = false menuColumn.previousButton.checked = false
@ -68,6 +69,7 @@ Rectangle {
else if(pos === "History") menuColumn.previousButton = historyButton else if(pos === "History") menuColumn.previousButton = historyButton
else if(pos === "Transfer") menuColumn.previousButton = transferButton else if(pos === "Transfer") menuColumn.previousButton = transferButton
else if(pos === "Receive") menuColumn.previousButton = receiveButton else if(pos === "Receive") menuColumn.previousButton = receiveButton
else if(pos === "Merchant") menuColumn.previousButton = merchantButton
else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton
else if(pos === "Mining") menuColumn.previousButton = miningButton else if(pos === "Mining") menuColumn.previousButton = miningButton
else if(pos === "TxKey") menuColumn.previousButton = txkeyButton else if(pos === "TxKey") menuColumn.previousButton = txkeyButton
@ -446,6 +448,32 @@ Rectangle {
height: 1 height: 1
} }
// ------------- Merchant tab ---------------
MoneroComponents.MenuButton {
id: merchantButton
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("Merchant") + translationManager.emptyString
symbol: qsTr("U") + translationManager.emptyString
dotColor: "#FF4F41"
under: receiveButton
onClicked: {
parent.previousButton.checked = false
parent.previousButton = merchantButton
panel.merchantClicked()
}
}
Rectangle {
visible: merchantButton.present
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 16
color: "#313131"
height: 1
}
// ------------- History tab --------------- // ------------- History tab ---------------
MoneroComponents.MenuButton { MoneroComponents.MenuButton {

View File

@ -38,6 +38,8 @@ import moneroComponents.Wallet 1.0
import "components" as MoneroComponents import "components" as MoneroComponents
import "./pages" import "./pages"
import "./pages/settings" import "./pages/settings"
import "./pages/merchant"
import "components" as MoneroComponents
Rectangle { Rectangle {
id: root id: root
@ -56,6 +58,7 @@ Rectangle {
property Transfer transferView: Transfer { } property Transfer transferView: Transfer { }
property Receive receiveView: Receive { } property Receive receiveView: Receive { }
property Merchant merchantView: Merchant { }
property TxKey txkeyView: TxKey { } property TxKey txkeyView: TxKey { }
property SharedRingDB sharedringdbView: SharedRingDB { } property SharedRingDB sharedringdbView: SharedRingDB { }
property History historyView: History { } property History historyView: History { }
@ -72,11 +75,16 @@ Rectangle {
signal getProofClicked(string txid, string address, string message); signal getProofClicked(string txid, string address, string message);
signal checkProofClicked(string txid, string address, string message, string signature); signal checkProofClicked(string txid, string address, string message, string signature);
Rectangle {
// grey background on merchantView
visible: currentView === merchantView
color: MoneroComponents.Style.moneroGrey
anchors.fill: parent
}
Image { Image {
anchors.left: parent.left anchors.fill: parent
anchors.right: parent.right visible: currentView !== merchantView
anchors.top: parent.top
anchors.bottom: parent.bottom
source: "../images/middlePanelBg.jpg" source: "../images/middlePanelBg.jpg"
} }
@ -123,6 +131,10 @@ Rectangle {
name: "Receive" name: "Receive"
PropertyChanges { target: root; currentView: receiveView } PropertyChanges { target: root; currentView: receiveView }
PropertyChanges { target: mainFlickable; contentHeight: receiveView.receiveHeight + 100 } PropertyChanges { target: mainFlickable; contentHeight: receiveView.receiveHeight + 100 }
}, State {
name: "Merchant"
PropertyChanges { target: root; currentView: merchantView }
PropertyChanges { target: mainFlickable; contentHeight: merchantView.merchantHeight + 100 }
}, State { }, State {
name: "TxKey" name: "TxKey"
PropertyChanges { target: root; currentView: txkeyView } PropertyChanges { target: root; currentView: txkeyView }
@ -172,8 +184,8 @@ Rectangle {
ColumnLayout { ColumnLayout {
anchors.fill: parent anchors.fill: parent
anchors.margins: 18 anchors.margins: currentView !== merchantView ? 20 * scaleRatio : 0
anchors.topMargin: appWindow.persistentSettings.customDecorations ? 50 : 0 anchors.topMargin: appWindow.persistentSettings.customDecorations ? 50 * scaleRatio : 0
spacing: 0 spacing: 0
Flickable { Flickable {

View File

@ -12,6 +12,7 @@ QtObject {
property string orange: "#FF6C3C" property string orange: "#FF6C3C"
property string white: "#FFFFFF" property string white: "#FFFFFF"
property string green: "#2EB358" property string green: "#2EB358"
property string moneroGrey: "#4C4C4C"
property string defaultFontColor: "white" property string defaultFontColor: "white"
property string dimmedFontColor: "#BBBBBB" property string dimmedFontColor: "#BBBBBB"

View File

@ -47,7 +47,7 @@ Rectangle {
property string title property string title
property int mouseX: 0 property int mouseX: 0
property bool containsMouse: false property bool containsMouse: false
property alias basicButtonVisible: goToBasicVersionButton.visible property bool basicButtonVisible: false
property bool customDecorations: persistentSettings.customDecorations property bool customDecorations: persistentSettings.customDecorations
property bool showWhatIsButton: true property bool showWhatIsButton: true
property bool showMinimizeButton: false property bool showMinimizeButton: false
@ -56,6 +56,9 @@ Rectangle {
property bool showMoneroLogo: false property bool showMoneroLogo: false
property bool small: false property bool small: false
property alias titleBarGradientImageOpacity: titleBarGradientImage.opacity property alias titleBarGradientImageOpacity: titleBarGradientImage.opacity
property bool orange: false
property string buttonHoverColor: "#262626"
property string buttonHoverColorOrange: "#44FFFFFF"
signal closeClicked signal closeClicked
signal maximizeClicked signal maximizeClicked
@ -70,11 +73,19 @@ Rectangle {
Image { Image {
id: titleBarGradientImage id: titleBarGradientImage
visible: !titleBar.orange
anchors.fill: parent anchors.fill: parent
height: titleBar.height height: titleBar.height
width: titleBar.width width: titleBar.width
source: "../images/titlebarGradient.jpg" source: "../images/titlebarGradient.jpg"
} }
Rectangle {
visible: titleBar.orange
width: parent.width
height: parent.height
color: "#ff6600"
}
} }
Item { Item {
@ -82,11 +93,11 @@ Rectangle {
width: 125 width: 125
height: parent.height height: parent.height
anchors.centerIn: parent anchors.centerIn: parent
visible: customDecorations && showMoneroLogo visible: customDecorations
z: parent.z + 1 z: parent.z + 1
Image { Image {
visible: !isMobile visible: !isMobile && showMoneroLogo && !titleBar.orange
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: 11 anchors.topMargin: 11
@ -94,6 +105,16 @@ Rectangle {
height: 28 height: 28
source: "../images/titlebarLogo.png" source: "../images/titlebarLogo.png"
} }
Image {
visible: !isMobile && showMoneroLogo && titleBar.orange
anchors.left: parent.left
anchors.top: parent.top
anchors.topMargin: 11
width: 132
height: 22
source: "../images/moneroLogo_white.png"
}
} }
Label { Label {
@ -115,7 +136,7 @@ Rectangle {
color: "transparent" color: "transparent"
height: titleBar.height height: titleBar.height
width: height width: height
visible: isMobile visible: !titleBar.orange && titleBar.basicButtonVisible
z: parent.z + 2 z: parent.z + 2
Image { Image {
@ -130,7 +151,7 @@ Rectangle {
hoverEnabled: true hoverEnabled: true
anchors.fill: parent anchors.fill: parent
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onEntered: goToBasicVersionButton.color = "#262626"; onEntered: { goToBasicVersionButton.color = titleBar.orange ? titleBar.buttonHoverColorOrange : titleBar.buttonHoverColor }
onExited: goToBasicVersionButton.color = "transparent"; onExited: goToBasicVersionButton.color = "transparent";
onClicked: { onClicked: {
releaseFocus() releaseFocus()
@ -166,7 +187,13 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onEntered: minimizeButton.color = "#262626"; onEntered: {
if(titleBar.orange){
minimizeButton.color = titleBar.buttonHoverColorOrange;
} else {
minimizeButton.color = titleBar.buttonHoverColor;
}
}
onExited: minimizeButton.color = "transparent"; onExited: minimizeButton.color = "transparent";
onClicked: minimizeClicked(); onClicked: minimizeClicked();
} }
@ -193,7 +220,13 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onEntered: maximizeButton.color = "#262626"; onEntered: {
if(titleBar.orange){
maximizeButton.color = titleBar.buttonHoverColorOrange;
} else {
maximizeButton.color = titleBar.buttonHoverColor;
}
}
onExited: maximizeButton.color = "transparent"; onExited: maximizeButton.color = "transparent";
onClicked: maximizeClicked(); onClicked: maximizeClicked();
} }
@ -219,7 +252,13 @@ Rectangle {
onClicked: closeClicked(); onClicked: closeClicked();
hoverEnabled: true hoverEnabled: true
cursorShape: Qt.PointingHandCursor cursorShape: Qt.PointingHandCursor
onEntered: closeButton.color = "#262626"; onEntered: {
if(titleBar.orange){
closeButton.color = titleBar.buttonHoverColorOrange;
} else {
closeButton.color = titleBar.buttonHoverColor;
}
}
onExited: closeButton.color = "transparent"; onExited: closeButton.color = "transparent";
} }
} }
@ -227,6 +266,7 @@ Rectangle {
// window borders // window borders
Rectangle { Rectangle {
visible: !titleBar.orange
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
@ -236,10 +276,10 @@ Rectangle {
} }
Rectangle { Rectangle {
visible: titleBar.small && !titleBar.orange
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.left: parent.left anchors.left: parent.left
visible: titleBar.small
height: 1 height: 1
color: "#2F2F2F" color: "#2F2F2F"
z: parent.z + 1 z: parent.z + 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
images/merchant/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@ -64,3 +64,14 @@ function isValidOpenAliasAddress(address) {
// we can get an awful lot of valid domains, including non ASCII chars... accept anything // we can get an awful lot of valid domains, including non ASCII chars... accept anything
return true return true
} }
function makeQRCodeString(addr, amount) {
var XMR_URI_SCHEME = "monero:"
var XMR_AMOUNT = "tx_amount"
var qrCodeString =""
qrCodeString += (XMR_URI_SCHEME + addr)
if (amount !== undefined && amount !== ""){
qrCodeString += ("?" + XMR_AMOUNT + "=" + amount)
}
return qrCodeString
}

View File

@ -48,4 +48,38 @@ function showSeedPage() {
passwordDialog.open(); passwordDialog.open();
if(isMobile) hideMenu(); if(isMobile) hideMenu();
updateBalance(); updateBalance();
} }
function ago(epoch) {
// Returns '<delta> [seconds|minutes|hours|days] ago' string given an epoch
var now = new Date().getTime() / 1000;
var delta = now - epoch;
if(delta < 60) {
return delta + " " + qsTr("seconds ago")
} else if (delta >= 60 && delta <= 3600) {
if(delta >= 60 && delta < 120){
return 1 + " " + qsTr("minute ago")
} else {
return parseInt(Math.floor(delta / 60)) + " " + qsTr("minutes ago")
}
} else if (delta >= 3600 && delta <= 86400) {
if(delta >= 3600 && delta < 7200) {
return 1 + " " + qsTr("hour ago")
} else {
return parseInt(Math.floor(delta / 60 / 60)) + " " + qsTr("hours ago")
}
} else if (delta >= 86400){
if(delta >= 86400 && delta < 172800) {
return 1 + " " + qsTr("day ago")
} else {
var _delta = parseInt(Math.floor(delta / 24 / 60 / 60));
if(_delta === 1) {
return 1 + " " + qsTr("day ago")
} else {
return _delta + " " + qsTr("days ago")
}
}
}
}

View File

@ -86,6 +86,11 @@ ApplicationWindow {
// true if wallet ever synchronized // true if wallet ever synchronized
property bool walletInitialized : false property bool walletInitialized : false
// Current selected address / subaddress (Receive page)
property var current_address
property var current_address_label: "Primary"
property int current_subaddress_table_index: 0
function altKeyReleased() { ctrlPressed = false; } function altKeyReleased() { ctrlPressed = false; }
function showPageRequest(page) { function showPageRequest(page) {
@ -1371,6 +1376,15 @@ ApplicationWindow {
updateBalance(); updateBalance();
} }
onMerchantClicked: {
middlePanel.state = "Merchant";
middlePanel.flickable.contentY = 0;
if(isMobile) {
hideMenu();
}
updateBalance();
}
onTxkeyClicked: { onTxkeyClicked: {
middlePanel.state = "TxKey"; middlePanel.state = "TxKey";
middlePanel.flickable.contentY = 0; middlePanel.flickable.contentY = 0;
@ -1825,6 +1839,15 @@ ApplicationWindow {
onTriggered: checkUpdates() onTriggered: checkUpdates()
} }
function titlebarToggleOrange(flag){
// toggle titlebar orange style
if(flag !== undefined){
titleBar.orange = flag;
} else {
titleBar.orange = !titleBar.orange;
}
}
function releaseFocus() { function releaseFocus() {
// Workaround to release focus from textfield when scrolling (https://bugreports.qt.io/browse/QTBUG-34867) // Workaround to release focus from textfield when scrolling (https://bugreports.qt.io/browse/QTBUG-34867)
if(isAndroid) { if(isAndroid) {

View File

@ -97,6 +97,7 @@ SOURCES = *.qml \
components/*.qml \ components/*.qml \
pages/*.qml \ pages/*.qml \
pages/settings/*.qml \ pages/settings/*.qml \
pages/merchant/*.qml \
wizard/*.qml \ wizard/*.qml \
wizard/*js wizard/*js
} }
@ -458,7 +459,9 @@ OTHER_FILES += \
DISTFILES += \ DISTFILES += \
notes.txt \ notes.txt \
monero/src/wallet/CMakeLists.txt \ monero/src/wallet/CMakeLists.txt \
components/MobileHeader.qml components/MobileHeader.qml \
pages/merchant/Merchant.qml \
pages/merchant/MerchantCheckbox.qml
# windows application icon # windows application icon

View File

@ -39,7 +39,7 @@ Rectangle {
ColumnLayout { ColumnLayout {
id: columnLayout id: columnLayout
anchors.margins: (isMobile)? 17 : 40 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right

View File

@ -53,7 +53,7 @@ Rectangle {
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.margins: (isMobile)? 17 : 20 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 40 * scaleRatio anchors.topMargin: 40 * scaleRatio
spacing: 30 * scaleRatio spacing: 30 * scaleRatio

View File

@ -39,10 +39,10 @@ Rectangle {
ColumnLayout { ColumnLayout {
id: mainLayout id: mainLayout
anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.margins: 40 * scaleRatio
spacing: 20 * scaleRatio spacing: 20 * scaleRatio
Layout.fillWidth: true Layout.fillWidth: true

View File

@ -32,7 +32,7 @@ import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.1 import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2 import QtQuick.Dialogs 1.2
import "../components" import "../components" as MoneroComponents
import moneroComponents.Clipboard 1.0 import moneroComponents.Clipboard 1.0
import moneroComponents.Wallet 1.0 import moneroComponents.Wallet 1.0
import moneroComponents.WalletManager 1.0 import moneroComponents.WalletManager 1.0
@ -46,115 +46,7 @@ Rectangle {
id: pageReceive id: pageReceive
color: "transparent" color: "transparent"
property var model property var model
property var current_address
property int current_subaddress_table_index: 0
property alias receiveHeight: mainLayout.height property alias receiveHeight: mainLayout.height
property alias addressText : pageReceive.current_address
function makeQRCodeString() {
var XMR_URI_SCHEME = "monero:"
var XMR_AMOUNT = "tx_amount"
var qrCodeString =""
var amount = amountToReceiveLine.text
qrCodeString += (XMR_URI_SCHEME + current_address)
if (amount !== ""){
qrCodeString += ("?" + XMR_AMOUNT + "=" + amount)
}
return qrCodeString
}
function update() {
const max_tracking = 3;
if (!appWindow.currentWallet || !trackingEnabled.checked) {
trackingLineText.text = "";
trackingModel.clear();
return
}
if (appWindow.currentWallet.connected() == Wallet.ConnectionStatus_Disconnected) {
trackingLineText.text = qsTr("WARNING: no connection to daemon");
trackingModel.clear();
return
}
var model = appWindow.currentWallet.historyModel
var count = model.rowCount()
var totalAmount = 0
var nTransactions = 0
var blockchainHeight = null
var txs = []
for (var i = 0; i < count && txs.length < max_tracking; ++i) {
var idx = model.index(i, 0)
var isout = model.data(idx, TransactionHistoryModel.TransactionIsOutRole);
var subaddrAccount = model.data(idx, TransactionHistoryModel.TransactionSubaddrAccountRole);
var subaddrIndex = model.data(idx, TransactionHistoryModel.TransactionSubaddrIndexRole);
if (!isout && subaddrAccount == appWindow.currentWallet.currentSubaddressAccount && subaddrIndex == current_subaddress_table_index) {
var amount = model.data(idx, TransactionHistoryModel.TransactionAtomicAmountRole);
totalAmount = walletManager.addi(totalAmount, amount)
nTransactions += 1
var txid = model.data(idx, TransactionHistoryModel.TransactionHashRole);
var blockHeight = model.data(idx, TransactionHistoryModel.TransactionBlockHeightRole);
var in_txpool = false;
var confirmations = 0;
var displayAmount = 0;
if (blockHeight == 0) {
in_txpool = true;
} else {
if (blockchainHeight == null)
blockchainHeight = appWindow.currentWallet.blockChainHeight()
confirmations = blockchainHeight - blockHeight - 1
displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole);
}
txs.push({
"amount": displayAmount,
"confirmations": confirmations,
"blockheight": blockHeight,
"in_txpool": in_txpool,
"txid": txid
})
}
}
// Update tracking status label
if (nTransactions == 0) {
trackingLineText.text = qsTr("No transaction found yet...") + translationManager.emptyString
return
}
else if(nTransactions === 1){
trackingLineText.text = qsTr("Transaction found") + ":" + translationManager.emptyString;
} else {
trackingLineText.text = qsTr("%1 transactions found").arg(nTransactions) + ":" + translationManager.emptyString
}
toReceiveSatisfiedLine.text = "";
var expectedAmount = walletManager.amountFromString(amountToReceiveLine.text)
if (expectedAmount && expectedAmount != amount) {
var displayTotalAmount = walletManager.displayAmount(totalAmount)
if (amount > expectedAmount) toReceiveSatisfiedLine.text += qsTr("With more Monero");
else if (amount < expectedAmount) toReceiveSatisfiedLine.text = qsTr("With not enough Monero")
toReceiveSatisfiedLine.text += ": " + "<br>" +
qsTr("Expected") + ": " + amountToReceiveLine.text + "<br>" +
qsTr("Total received") + ": " + displayTotalAmount + translationManager.emptyString;
}
trackingModel.clear();
txs.forEach(function(tx){
trackingModel.append({
"amount": tx.amount,
"confirmations": tx.confirmations,
"blockheight": tx.blockHeight,
"in_txpool": tx.in_txpool,
"txid": tx.txid
});
});
//setTrackingLineText(text + "<br>" + list.join("<br>"))
}
function renameSubaddressLabel(_index){ function renameSubaddressLabel(_index){
inputDialog.labelText = qsTr("Set the label of the selected address:") + translationManager.emptyString; inputDialog.labelText = qsTr("Set the label of the selected address:") + translationManager.emptyString;
@ -171,7 +63,7 @@ Rectangle {
/* main layout */ /* main layout */
ColumnLayout { ColumnLayout {
id: mainLayout id: mainLayout
anchors.margins: (isMobile)? 17 : 40 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 40 * scaleRatio anchors.topMargin: 40 * scaleRatio
anchors.left: parent.left anchors.left: parent.left
@ -188,24 +80,10 @@ Rectangle {
id: addressRow id: addressRow
spacing: 0 spacing: 0
LabelSubheader { MoneroComponents.LabelSubheader {
Layout.fillWidth: true Layout.fillWidth: true
textFormat: Text.RichText textFormat: Text.RichText
text: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 14px;}</style>" + text: qsTr("Addresses")
qsTr("Addresses") +
"<font size='2'> </font><a href='#'>" +
qsTr("Help") + "</a>" +
translationManager.emptyString
onLinkActivated: {
receivePageDialog.title = qsTr("Tracking payments") + translationManager.emptyString;
receivePageDialog.text = qsTr(
"<p>This QR code includes the address you selected above and" +
"the amount you entered below. Share it with others (right-click->Save) " +
"so they can more easily send you exact amounts.</p>"
)
receivePageDialog.icon = StandardIcon.Information
receivePageDialog.open()
}
} }
ColumnLayout { ColumnLayout {
@ -245,9 +123,9 @@ Rectangle {
anchors.rightMargin: 80 anchors.rightMargin: 80
color: "transparent" color: "transparent"
Label { MoneroComponents.Label {
id: idLabel id: idLabel
color: index === current_subaddress_table_index ? "white" : "#757575" color: index === appWindow.current_subaddress_table_index ? "white" : "#757575"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
anchors.leftMargin: 6 anchors.leftMargin: 6
@ -256,7 +134,7 @@ Rectangle {
text: "#" + index text: "#" + index
} }
Label { MoneroComponents.Label {
id: nameLabel id: nameLabel
color: "#a5a5a5" color: "#a5a5a5"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -267,7 +145,7 @@ Rectangle {
text: label text: label
} }
Label { MoneroComponents.Label {
color: "white" color: "white"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: nameLabel.right anchors.left: nameLabel.right
@ -299,7 +177,7 @@ Rectangle {
} }
} }
IconButton { MoneroComponents.IconButton {
id: renameButton id: renameButton
imageSource: "../images/editIcon.png" imageSource: "../images/editIcon.png"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -313,7 +191,7 @@ Rectangle {
} }
} }
IconButton { MoneroComponents.IconButton {
id: copyButton id: copyButton
imageSource: "../images/copyToClipboard.png" imageSource: "../images/copyToClipboard.png"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@ -329,20 +207,17 @@ Rectangle {
} }
onCurrentItemChanged: { onCurrentItemChanged: {
// reset global vars // reset global vars
current_subaddress_table_index = subaddressListView.currentIndex; appWindow.current_subaddress_table_index = subaddressListView.currentIndex;
current_address = appWindow.currentWallet.address( appWindow.current_address = appWindow.currentWallet.address(
appWindow.currentWallet.currentSubaddressAccount, appWindow.currentWallet.currentSubaddressAccount,
subaddressListView.currentIndex subaddressListView.currentIndex
); );
// reset tracking table
trackingModel.clear();
} }
} }
} }
// 'fake' row for 'create new address' // 'fake' row for 'create new address'
ColumnLayout{ ColumnLayout {
id: createAddressRow id: createAddressRow
Layout.fillWidth: true Layout.fillWidth: true
spacing: 0 spacing: 0
@ -353,13 +228,13 @@ Rectangle {
height: 1 height: 1
} }
Rectangle{ Rectangle {
id: createAddressRect id: createAddressRect
Layout.preferredHeight: subaddressListRow.subaddressListItemHeight Layout.preferredHeight: subaddressListRow.subaddressListItemHeight
color: "transparent" color: "transparent"
Layout.fillWidth: true Layout.fillWidth: true
Label { MoneroComponents.Label {
color: "#757575" color: "#757575"
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left anchors.left: parent.left
@ -396,7 +271,8 @@ Rectangle {
} }
RowLayout { RowLayout {
CheckBox2 { Layout.topMargin: 22 * scaleRatio
MoneroComponents.CheckBox2 {
id: showAdvancedCheckbox id: showAdvancedCheckbox
checked: persistentSettings.receiveShowAdvanced checked: persistentSettings.receiveShowAdvanced
onClicked: { onClicked: {
@ -405,282 +281,72 @@ Rectangle {
text: qsTr("Advanced options") + translationManager.emptyString text: qsTr("Advanced options") + translationManager.emptyString
} }
} }
GridLayout { RowLayout {
id: advancedRow Layout.topMargin: 6 * scaleRatio
columns: (isMobile)? 1 : 2
Layout.fillWidth: true
columnSpacing: 32 * scaleRatio
visible: persistentSettings.receiveShowAdvanced visible: persistentSettings.receiveShowAdvanced
Layout.fillWidth: true
ColumnLayout { MoneroComponents.LineEditMulti {
Layout.alignment: Qt.AlignTop id: paymentUrl
Layout.fillWidth: true Layout.fillWidth: true
spacing: 20 * scaleRatio
LabelSubheader { labelText: qsTr("Payment URL") + translationManager.emptyString
Layout.fillWidth: true text: TxUtils.makeQRCodeString(appWindow.current_address)
textFormat: Text.RichText readOnly: true
text: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 14px;}</style>" + copyButton: true
qsTr("QR Code") + wrapMode: Text.WrapAnywhere
"<font size='2'> </font><a href='#'>" +
qsTr("Help") + "</a>" +
translationManager.emptyString
onLinkActivated: {
receivePageDialog.title = qsTr("QR Code") + translationManager.emptyString;
receivePageDialog.text = qsTr(
"<p>This QR code includes the address you selected above and " +
"the amount you entered below. Share it with others (right-click->Save) " +
"so they can more easily send you exact amounts.</p>"
)
receivePageDialog.icon = StandardIcon.Information
receivePageDialog.open()
}
}
ColumnLayout {
id: amountRow
Layout.fillWidth: true
Layout.minimumWidth: 200
spacing: parent.spacing
LineEdit {
id: amountToReceiveLine
Layout.fillWidth: true
labelText: qsTr("Amount") + translationManager.emptyString
placeholderText: qsTr("Amount to receive") + translationManager.emptyString
fontBold: true
inlineIcon: true
onTextChanged: {
if(amountToReceiveLine.text.indexOf('.') === 0){
amountToReceiveLine.text = '0' + amountToReceiveLine.text;
}
}
validator: RegExpValidator {
regExp: /^(\d{1,8})?([\.]\d{1,12})?$/
}
}
Rectangle {
color: "white"
Layout.fillWidth: true
Layout.maximumWidth: mainLayout.qrCodeSize
Layout.preferredHeight: width
radius: 4
Image {
id: qrCode
anchors.fill: parent
anchors.margins: 1
smooth: false
fillMode: Image.PreserveAspectFit
source: "image://qrcode/" + makeQRCodeString()
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
if (mouse.button == Qt.RightButton)
qrMenu.open()
}
onPressAndHold: qrFileDialog.open()
}
}
Menu {
id: qrMenu
title: "QrCode"
y: parent.height / 2
MenuItem {
text: qsTr("Save As") + translationManager.emptyString;
onTriggered: qrFileDialog.open()
}
}
}
LineEditMulti {
id: paymentUrl
Layout.fillWidth: true
labelText: qsTr("Payment URL") + translationManager.emptyString
text: makeQRCodeString()
readOnly: true
copyButton: true
wrapMode: Text.WrapAnywhere
}
}
} }
}
ColumnLayout { GridLayout{
id: trackingRow visible: persistentSettings.receiveShowAdvanced
Layout.alignment: Qt.AlignTop Layout.topMargin: 10 * scaleRatio
columns: 2
columnSpacing: 30 * scaleRatio
RowLayout {
property int qrSize: 220 * scaleRatio
Layout.fillWidth: true Layout.fillWidth: true
spacing: 0 * scaleRatio
LabelSubheader { Rectangle {
Layout.fillWidth: true id: qrContainer
textFormat: Text.RichText radius: 4 * scaleRatio
text: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 14px;}</style>" + color: "white"
qsTr("Tracking") + Layout.preferredWidth: parent.qrSize
"<font size='2'> </font><a href='#'>" + Layout.preferredHeight: parent.qrSize
qsTr("Help") + "</a>" +
translationManager.emptyString
onLinkActivated: {
receivePageDialog.title = qsTr("Tracking payments") + translationManager.emptyString;
receivePageDialog.text = qsTr(
"<p><font size='+2'>This is a simple sales tracker:</font></p>" +
"<p>Let your customer scan that QR code to make a payment (if that customer has software which " +
"supports QR code scanning).</p>" +
"<p>This page will automatically scan the blockchain and the tx pool " +
"for incoming transactions using this QR code. If you input an amount, it will also check " +
"that incoming transactions total up to that amount.</p>" +
"<p>It's up to you whether to accept unconfirmed transactions or not. It is likely they'll be " +
"confirmed in short order, but there is still a possibility they might not, so for larger " +
"values you may want to wait for one or more confirmation(s).</p>"
)
receivePageDialog.icon = StandardIcon.Information
receivePageDialog.open()
}
}
ListModel { Image {
id: trackingModel id: qrCode
}
RowLayout{
Layout.topMargin: 14
Layout.bottomMargin: 10
visible: trackingTableRow.visible
Label {
id: trackingLineText
color: "white"
fontFamily: Style.fontLight.name
fontSize: 16 * scaleRatio
text: ""
}
}
ColumnLayout {
id: trackingTableRow
visible: trackingListView.count >= 1
Layout.fillWidth: true
Layout.minimumWidth: 240
Layout.preferredHeight: 46 * trackingListView.count
ListView {
id: trackingListView
Layout.fillWidth: true
anchors.fill: parent anchors.fill: parent
clip: true anchors.margins: 1 * scaleRatio
boundsBehavior: ListView.StopAtBounds
model: trackingModel
delegate: Item {
id: trackingTableItem
height: 46
width: parent.width
Layout.fillWidth: true
Rectangle{ smooth: false
anchors.right: parent.right fillMode: Image.PreserveAspectFit
anchors.left: parent.left source: "image://qrcode/" + TxUtils.makeQRCodeString(appWindow.current_address)
anchors.top: parent.top
height: 1
color: "#404040"
visible: index !== 0
}
Image { MouseArea {
id: arrowImage anchors.fill: parent
source: "../images/upArrow-green.png" acceptedButtons: Qt.RightButton
height: 18 * scaleRatio onClicked: {
width: 12 * scaleRatio if (mouse.button == Qt.RightButton){
anchors.verticalCenter: parent.verticalCenter qrMenu.x = this.mouseX;
anchors.left: parent.left qrMenu.y = this.mouseY;
anchors.leftMargin: 8 qrMenu.open()
}
Label {
id: trackingConfirmationLine
color: "white"
anchors.top: parent.top
anchors.topMargin: 6
anchors.left: arrowImage.right
anchors.leftMargin: 10
fontSize: 14 * scaleRatio
text: {
if(in_txpool){
return "Awaiting in txpool"
} else {
if(confirmations > 1){
if(confirmations > 100){
return "100+ " + qsTr("confirmations") + translationManager.emptyString;
} else {
return confirmations + " " + qsTr("confirmations") + translationManager.emptyString;
}
} else {
return "1 " + qsTr("confirmation") + translationManager.emptyString;
}
}
} }
} }
onPressAndHold: qrFileDialog.open()
Label {
id: trackingAmountLine
color: "#2eb358"
anchors.top: trackingConfirmationLine.bottom
anchors.left: arrowImage.right
anchors.leftMargin: 10
fontSize: 14 * scaleRatio
fontBold: true
text: amount
}
IconButton {
id: clipboardButton
imageSource: "../images/copyToClipboard.png"
onClicked: {
console.log("tx_id copied to clipboard");
clipboard.setText(txid);
appWindow.showStatusMessage(qsTr("Transaction ID copied to clipboard"),3);
}
anchors.right: parent.right
anchors.top: undefined
anchors.verticalCenter: parent.verticalCenter
}
} }
} }
}
RowLayout { Menu {
visible: trackingTableRow.visible && x.text !== "" && amountToReceiveLine.text !== "" id: qrMenu
Layout.topMargin: 14 * scaleRatio title: "QrCode"
Layout.fillWidth: true
Layout.preferredHeight: 40 * scaleRatio
Label { MenuItem {
id: toReceiveSatisfiedLine text: qsTr("Save As") + translationManager.emptyString;
color: "white" onTriggered: qrFileDialog.open()
fontFamily: Style.fontLight.name }
fontSize: 14 * scaleRatio
textFormat: Text.RichText
text: ""
height: 40 * scaleRatio
}
}
RowLayout {
Layout.fillWidth: true
Layout.minimumWidth: 200
Layout.topMargin: trackingTableRow.visible ? 20 * scaleRatio : 32 * scaleRatio
CheckBox {
id: trackingEnabled
text: qsTr("Enable") + translationManager.emptyString
} }
} }
} }
@ -693,12 +359,12 @@ Rectangle {
FileDialog { FileDialog {
id: qrFileDialog id: qrFileDialog
title: "Please choose a name" title: qsTr("Please choose a name")
folder: shortcuts.pictures folder: shortcuts.pictures
selectExisting: false selectExisting: false
nameFilters: ["Image (*.png)"] nameFilters: ["Image (*.png)"]
onAccepted: { onAccepted: {
if(!walletManager.saveQrCode(makeQRCodeString(), walletManager.urlToLocalPath(fileUrl))) { if(!walletManager.saveQrCode(TxUtils.makeQRCodeString(appWindow.current_address), walletManager.urlToLocalPath(fileUrl))) {
console.log("Failed to save QrCode to file " + walletManager.urlToLocalPath(fileUrl) ) console.log("Failed to save QrCode to file " + walletManager.urlToLocalPath(fileUrl) )
receivePageDialog.title = qsTr("Save QrCode") + translationManager.emptyString; receivePageDialog.title = qsTr("Save QrCode") + translationManager.emptyString;
receivePageDialog.text = qsTr("Failed to save QrCode to ") + walletManager.urlToLocalPath(fileUrl) + translationManager.emptyString; receivePageDialog.text = qsTr("Failed to save QrCode to ") + walletManager.urlToLocalPath(fileUrl) + translationManager.emptyString;
@ -709,27 +375,14 @@ Rectangle {
} }
} }
Timer {
id: timer
interval: 2000; running: false; repeat: true
onTriggered: update()
}
function onPageCompleted() { function onPageCompleted() {
console.log("Receive page loaded"); console.log("Receive page loaded");
subaddressListView.model = appWindow.currentWallet.subaddressModel; subaddressListView.model = appWindow.currentWallet.subaddressModel;
if (appWindow.currentWallet) { if (appWindow.currentWallet) {
current_address = appWindow.currentWallet.address(appWindow.currentWallet.currentSubaddressAccount, 0) appWindow.current_address = appWindow.currentWallet.address(appWindow.currentWallet.currentSubaddressAccount, 0)
appWindow.currentWallet.subaddress.refresh(appWindow.currentWallet.currentSubaddressAccount) appWindow.currentWallet.subaddress.refresh(appWindow.currentWallet.currentSubaddressAccount)
current_subaddress_table_index = 0;
subaddressListView.currentIndex = 0;
} }
update()
timer.running = true
trackingEnabled.checked = false
} }
function clearFields() { function clearFields() {
@ -737,8 +390,5 @@ Rectangle {
} }
function onPageClosed() { function onPageClosed() {
timer.running = false
trackingEnabled.checked = false
trackingModel.clear()
} }
} }

View File

@ -79,7 +79,7 @@ Rectangle {
/* main layout */ /* main layout */
ColumnLayout { ColumnLayout {
id: mainLayout id: mainLayout
anchors.margins: (isMobile)? 17 : 20 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 40 * scaleRatio anchors.topMargin: 40 * scaleRatio
anchors.left: parent.left anchors.left: parent.left

View File

@ -86,12 +86,9 @@ Rectangle {
// sign / verify // sign / verify
ColumnLayout { ColumnLayout {
id: root
spacing: 20 * scaleRatio
anchors.margins: (isMobile ? 17 : 20) * scaleRatio
anchors.topMargin: 40 * scaleRatio
anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
Rectangle { Rectangle {

View File

@ -103,7 +103,7 @@ Rectangle {
ColumnLayout { ColumnLayout {
id: pageRoot id: pageRoot
anchors.margins: (isMobile)? 17 : 20 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 40 * scaleRatio anchors.topMargin: 40 * scaleRatio
anchors.left: parent.left anchors.left: parent.left
@ -421,7 +421,7 @@ Rectangle {
anchors.top: pageRoot.bottom anchors.top: pageRoot.bottom
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
anchors.margins: (isMobile)? 17 : 20 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 32 * scaleRatio anchors.topMargin: 32 * scaleRatio
spacing: 26 * scaleRatio spacing: 26 * scaleRatio
enabled: !viewOnly || pageRoot.enabled enabled: !viewOnly || pageRoot.enabled

View File

@ -45,7 +45,7 @@ Rectangle {
/* main layout */ /* main layout */
ColumnLayout { ColumnLayout {
id: mainLayout id: mainLayout
anchors.margins: 40 * scaleRatio anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right

699
pages/merchant/Merchant.qml Normal file
View File

@ -0,0 +1,699 @@
import QtQuick 2.7
import QtQuick.Layouts 1.1
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
import QtQuick.Controls.Styles 1.4
import QtQuick.Dialogs 1.2
import moneroComponents.Clipboard 1.0
import moneroComponents.Wallet 1.0
import moneroComponents.WalletManager 1.0
import moneroComponents.TransactionHistory 1.0
import moneroComponents.TransactionHistoryModel 1.0
import moneroComponents.Subaddress 1.0
import moneroComponents.SubaddressModel 1.0
import "../../js/Windows.js" as Windows
import "../../js/TxUtils.js" as TxUtils
import "../../js/Utils.js" as Utils
import "../../components" as MoneroComponents
import "../../pages"
import "."
Item {
id: root
anchors.margins: 0
property int minWidth: 900 * scaleRatio
property int qrCodeSize: 220 * scaleRatio
property bool enableTracking: false
property string trackingError: "" // setting this will show a message @ tracking table
property alias merchantHeight: mainLayout.height
property string addressLabel: ""
property var hiddenAmounts: []
function onPageCompleted() {
appWindow.titlebarToggleOrange();
appWindow.hideMenu();
// prepare tracking
trackingCheckbox.checked = root.enableTracking
root.update();
timer.running = true;
// set currently selected account indication
var _addressLabel = appWindow.currentWallet.getSubaddressLabel(
appWindow.currentWallet.currentSubaddressAccount,
appWindow.current_subaddress_table_index);
if(_addressLabel === ""){
root.addressLabel = "#" + appWindow.current_subaddress_table_index;
} else {
root.addressLabel = _addressLabel;
}
}
function onPageClosed() {
appWindow.titlebarToggleOrange();
// reset component objects
timer.running = false
root.enableTracking = false
trackingModel.clear()
appWindow.showMenu();
}
Image {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
height: 300 * scaleRatio
source: "../../images/merchant/bg.png"
smooth: false
}
ColumnLayout {
id: mainLayout
visible: parent.width >= root.minWidth
spacing: 0
// emulates max-width + center for container
property int maxWidth: 1200 * scaleRatio
property int defaultMargin: 50 * scaleRatio
property int horizontalMargin: {
if(appWindow.width >= maxWidth){
return ((appWindow.width - maxWidth) / 2) + defaultMargin;
} else {
return defaultMargin;
}
}
anchors.leftMargin: horizontalMargin
anchors.rightMargin: horizontalMargin
anchors.margins: defaultMargin
anchors.left: parent.left
anchors.top: parent.top
anchors.right: parent.right
Item {
height: 220 * scaleRatio
anchors.left: parent.left
anchors.right: parent.right
Rectangle {
id: tracker
anchors.left: parent.left
anchors.top: parent.top
height: 220 * scaleRatio
width: (parent.width - qrImg.width) - 50 * scaleRatio
radius: 5
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
RowLayout {
spacing: 0
height: 56 * scaleRatio
RowLayout {
Layout.alignment: Qt.AlignLeft
Layout.preferredWidth: 260 * scaleRatio
Layout.preferredHeight: parent.height
Layout.fillHeight: true
spacing: 8 * scaleRatio
Item {
Layout.preferredWidth: 10 * scaleRatio
}
Text {
font.pixelSize: 16 * scaleRatio
font.bold: true
color: "#767676"
text: qsTr("Sales")
}
Item {
Layout.fillWidth: true
}
}
Item {
Layout.fillWidth: true
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1 * scaleRatio
color: "#d9d9d9"
}
MerchantTrackingList {
Layout.fillWidth: true
Layout.preferredHeight: 400 * scaleRatio
model: trackingModel
message: {
if(!root.enableTracking){
return qsTr(
"<style>p{font-size:14px;}</style>" +
"<p>This page will automatically scan the blockchain and the tx pool " +
"for incoming transactions using the QR code.</p>" +
"<p>It's up to you whether to accept unconfirmed transactions or not. It is likely they'll be " +
"confirmed in short order, but there is still a possibility they might not, so for larger " +
"values you may want to wait for one or more confirmation(s).</p>"
);
} else if(root.trackingError !== ""){
return root.trackingError;
} else if(trackingModel.count < 1){
return qsTr("Currently monitoring incoming transactions, none found yet.");
} else {
return ""
}
}
onHideAmountToggled: {
if(root.hiddenAmounts.indexOf(txid) < 0){
root.hiddenAmounts.push(txid);
} else {
root.hiddenAmounts = root.hiddenAmounts.filter(function(_txid) { return _txid !== txid });
}
}
}
}
}
DropShadow {
anchors.fill: source
cached: true
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 16
color: "#20000000"
smooth: true
source: tracker
}
Rectangle {
id: qrImg
color: "white"
anchors.right: parent.right
anchors.top: parent.top
height: root.qrCodeSize
width: root.qrCodeSize
Layout.maximumWidth: root.qrCodeSize
Layout.preferredHeight: width
radius: 5
Image {
id: qrCode
anchors.fill: parent
anchors.margins: 1 * scaleRatio
smooth: false
fillMode: Image.PreserveAspectFit
source: "image://qrcode/" + TxUtils.makeQRCodeString(appWindow.current_address, amountToReceive.text)
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: {
if (mouse.button == Qt.RightButton){
qrMenu.x = this.mouseX;
qrMenu.y = this.mouseY;
qrMenu.open()
}
}
onPressAndHold: qrFileDialog.open()
}
}
Menu {
id: qrMenu
title: "QrCode"
MenuItem {
text: qsTr("Save As") + translationManager.emptyString;
onTriggered: qrFileDialog.open()
}
}
}
DropShadow {
anchors.fill: source
cached: true
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 16
color: "#30000000"
smooth: true
source: qrImg
}
}
Item {
Layout.preferredHeight: 40 * scaleRatio
anchors.left: parent.left
anchors.right: parent.right
Item {
width: (parent.width - qrImg.width) - (50 * scaleRatio)
height: 32 * scaleRatio
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: 12 * scaleRatio
font.bold: false
color: "white"
text: "<style type='text/css'>a {text-decoration: none; color: #FF6C3C; font-size: 12px;}</style>Currently selected address: " + addressLabel + " <a href='#'>(Change)</a>"
textFormat: Text.RichText
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: appWindow.showPageRequest("Receive")
}
}
}
Item {
anchors.right: parent.right
anchors.top: parent.top
width: 220 * scaleRatio
height: 32 * scaleRatio
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
font.pixelSize: 12 * scaleRatio
font.bold: false
color: "white"
text: qsTr("(right-click, save as)")
}
}
}
Item {
Layout.preferredHeight: 120 * scaleRatio
Layout.topMargin: 20 * scaleRatio
Layout.fillWidth: true
Rectangle {
id: payment_url_container
anchors.left: parent.left
anchors.top: parent.top
implicitHeight: 120 * scaleRatio
width: (parent.width - qrImg.width) - (50 * scaleRatio)
radius: 5
ColumnLayout {
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
spacing: 0
RowLayout {
spacing: 0
height: 56 * scaleRatio
RowLayout {
Layout.alignment: Qt.AlignLeft
Layout.preferredWidth: 260 * scaleRatio
Layout.preferredHeight: parent.height
Layout.fillHeight: true
spacing: 8
Item {
Layout.preferredWidth: 10 * scaleRatio
}
Text {
font.pixelSize: 14 * scaleRatio
font.bold: true
color: "#767676"
text: qsTr("Payment URL")
}
Item {
Layout.fillWidth: true
}
}
// @TODO: PaymentURL explanation
// Rectangle {
// // help box
// Layout.alignment: Qt.AlignLeft
// Layout.preferredWidth: 40 * scaleRatio
// Layout.fillHeight: true
// color: "transparent"
// Text {
// anchors.verticalCenter: parent.verticalCenter
// anchors.right: parent.right
// anchors.rightMargin: 20 * scaleRatio
// font.pixelSize: 16 * scaleRatio
// font.bold: true
// color: "#767676"
// text:"?"
// }
// MouseArea {
// anchors.fill: parent
// cursorShape: Qt.PointingHandCursor
// onClicked: {
// merchantPageDialog.title = qsTr("Payment URL") + translationManager.emptyString;
// merchantPageDialog.text = qsTr("payment url explanation")
// merchantPageDialog.icon = StandardIcon.Information
// merchantPageDialog.open()
// }
// }
// }
Item {
Layout.fillWidth: true
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 1
color: "#d9d9d9"
}
Text {
property string _color: "#767676"
Layout.fillWidth: true
Layout.margins: 20 * scaleRatio
Layout.topMargin: 10 * scaleRatio
wrapMode: Text.WrapAnywhere
elide: Text.ElideRight
font.pixelSize: 12 * scaleRatio
font.bold: true
color: _color
text: TxUtils.makeQRCodeString(appWindow.current_address, amountToReceive.text)
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onEntered: {
parent.color = MoneroComponents.Style.orange
}
onExited: {
parent.color = parent._color
}
onClicked: {
console.log("Copied to clipboard");
clipboard.setText(parent.text);
appWindow.showStatusMessage(qsTr("Copied to clipboard"), 3);
}
}
}
}
}
DropShadow {
anchors.fill: source
cached: true
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 16
color: "#20000000"
smooth: true
source: payment_url_container
}
Item {
anchors.right: parent.right
anchors.top: parent.top
width: 220 * scaleRatio
height: 32 * scaleRatio
ColumnLayout {
anchors.left: parent.left
anchors.right: parent.right
Text {
font.pixelSize: 14 * scaleRatio
font.bold: false
color: "white"
text: qsTr("Amount to receive") + " (XMR)"
}
Image {
height: 28 * scaleRatio
width: 220 * scaleRatio
source: "../../images/merchant/input_box.png"
TextField {
id: amountToReceive
topPadding: 0
leftPadding: 10 * scaleRatio
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
anchors.topMargin: 3 * scaleRatio
font.pixelSize: 16 * scaleRatio
font.bold: true
horizontalAlignment: TextInput.AlignLeft
verticalAlignment: TextInput.AlignVCenter
selectByMouse: true
color: "#424242"
selectionColor: "#3f3fe3"
selectedTextColor: "white"
background: Rectangle {
color: "transparent"
}
onTextChanged: {
if (amountToReceive.text.indexOf('.') === 0) {
amountToReceive.text = '0' + amountToReceive.text;
}
}
validator: RegExpValidator {
regExp: /^(\d{1,8})?([\.]\d{1,12})?$/
}
}
}
Item {
height: 2 * scaleRatio
width: 220 * scaleRatio
}
Text {
// @TODO: When we have XMR/USD rate avi. in the future.
visible: false
font.pixelSize: 14 * scaleRatio
font.bold: false
color: "white"
text: qsTr("Amount to receive") + " (USD)"
opacity: 0.2
}
Image {
visible: false
height: 28 * scaleRatio
width: 220 * scaleRatio
source: "../../images/merchant/input_box.png"
opacity: 0.2
}
}
}
}
Item {
Layout.topMargin: 32 * scaleRatio
Layout.preferredHeight: 40 * scaleRatio
anchors.left: parent.left
anchors.right: parent.right
ColumnLayout {
spacing: 16 * scaleRatio
MerchantCheckbox {
id: trackingCheckbox
checked: root.enableTracking
text: qsTr("Enable sales tracker")
onChanged: {
root.enableTracking = this.checked;
}
}
Text {
id: content
font.pixelSize: 14 * scaleRatio
font.bold: false
color: "white"
text: qsTr("Leave this page")
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: appWindow.showPageRequest("Receive")
}
}
}
}
}
Rectangle {
// Shows when the window is too small
visible: parent.width < root.minWidth
anchors.top: parent.top
anchors.topMargin: 100 * scaleRatio;
anchors.horizontalCenter: parent.horizontalCenter
height: 120 * scaleRatio
width: 400 * scaleRatio
radius: 5
Text {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 14 * scaleRatio
font.bold: true
color: MoneroComponents.Style.moneroGrey
text: qsTr("The merchant page requires a larger window")
}
}
function update() {
const max_tracking = 3;
if (!appWindow.currentWallet || !root.enableTracking) {
root.trackingError = "";
trackingModel.clear();
return
}
if (appWindow.currentWallet.connected() == Wallet.ConnectionStatus_Disconnected) {
root.trackingError = qsTr("WARNING: no connection to daemon");
trackingModel.clear();
return
}
var model = appWindow.currentWallet.historyModel
var count = model.rowCount()
var totalAmount = 0
var nTransactions = 0
var blockchainHeight = null
var txs = []
// Currently selected subaddress as per Receive page
var current_subaddress_table_index = appWindow.current_subaddress_table_index;
for (var i = 0; i < count && txs.length < max_tracking; ++i) {
var idx = model.index(i, 0)
var isout = model.data(idx, TransactionHistoryModel.TransactionIsOutRole);
var timeDate = model.data(idx, TransactionHistoryModel.TransactionDateRole);
var timeHour = model.data(idx, TransactionHistoryModel.TransactionTimeRole);
var timeEpoch = new Date(timeDate + "T" + timeHour + "Z") .getTime() / 1000;
var subaddrAccount = model.data(idx, TransactionHistoryModel.TransactionSubaddrAccountRole);
var subaddrIndex = model.data(idx, TransactionHistoryModel.TransactionSubaddrIndexRole);
if (!isout && subaddrAccount == appWindow.currentWallet.currentSubaddressAccount && subaddrIndex == current_subaddress_table_index) {
var amount = model.data(idx, TransactionHistoryModel.TransactionAtomicAmountRole);
totalAmount = walletManager.addi(totalAmount, amount)
nTransactions += 1
var txid = model.data(idx, TransactionHistoryModel.TransactionHashRole);
var blockHeight = model.data(idx, TransactionHistoryModel.TransactionBlockHeightRole);
var in_txpool = false;
var confirmations = 0;
var displayAmount = 0;
if (blockHeight == 0) {
in_txpool = true;
} else {
if (blockchainHeight == null)
blockchainHeight = appWindow.currentWallet.blockChainHeight()
confirmations = blockchainHeight - blockHeight - 1
displayAmount = model.data(idx, TransactionHistoryModel.TransactionDisplayAmountRole);
}
txs.push({
"amount": displayAmount,
"confirmations": confirmations,
"blockheight": blockHeight,
"in_txpool": in_txpool,
"txid": txid,
"time_epoch": timeEpoch,
"time_date": timeDate + " " + timeHour,
"hide_amount": root.hiddenAmounts.indexOf(txid) >= 0
})
}
}
// Update tracking status label
if (nTransactions == 0) {
root.trackingError = qsTr("Currently monitoring incoming transactions, none found yet.") + translationManager.emptyString
return
}
trackingModel.clear();
txs.forEach(function(tx){
trackingModel.append({
"amount": tx.amount,
"blockheight": tx.blockheight,
"confirmations": tx.confirmations,
"blockheight": tx.blockHeight,
"in_txpool": tx.in_txpool,
"txid": tx.txid,
"time_epoch": tx.time_epoch,
"time_date": tx.time_date,
"hide_amount": tx.hide_amount
});
});
}
ListModel {
id: trackingModel
}
Timer {
id: timer
interval: 3000; running: false; repeat: true
onTriggered: update()
}
MessageDialog {
id: merchantPageDialog
standardButtons: StandardButton.Ok
}
FileDialog {
id: qrFileDialog
title: "Please choose a name"
folder: shortcuts.pictures
selectExisting: false
nameFilters: ["Image (*.png)"]
onAccepted: {
if(!walletManager.saveQrCode(TxUtils.makeQRCodeString(appWindow.current_address, amountToReceive.text), walletManager.urlToLocalPath(fileUrl))) {
console.log("Failed to save QrCode to file " + walletManager.urlToLocalPath(fileUrl) )
receivePageDialog.title = qsTr("Save QrCode") + translationManager.emptyString;
receivePageDialog.text = qsTr("Failed to save QrCode to ") + walletManager.urlToLocalPath(fileUrl) + translationManager.emptyString;
receivePageDialog.icon = StandardIcon.Error
receivePageDialog.open()
}
}
}
Clipboard { id: clipboard }
}

View File

@ -0,0 +1,63 @@
import QtQuick 2.7
import QtQuick.Layouts 1.1
import QtGraphicalEffects 1.0
RowLayout {
id: root
spacing: 10 * scaleRatio
property bool checked: false;
property alias text: content.text
signal changed;
Rectangle {
id: checkbox
anchors.left: parent.left
anchors.top: parent.top
implicitHeight: 22 * scaleRatio
width: 22 * scaleRatio
radius: 5
Image {
id: imageChecked
visible: root.checked
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
source: "../../images/checkedBlackIcon.png"
opacity: 0.6
width: 12 * scaleRatio
height: 8 * scaleRatio
}
}
Text {
id: content
font.pixelSize: 14 * scaleRatio
font.bold: false
color: "white"
text: ""
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.checked = !root.checked;
changed();
}
}
DropShadow {
anchors.fill: source
cached: true
horizontalOffset: 3
verticalOffset: 3
radius: 8.0
samples: 16
color: "#20000000"
smooth: true
source: checkbox
}
}

View File

@ -0,0 +1,231 @@
import QtQuick 2.0
import QtQuick.Controls 2.0
import QtQuick.Controls.Styles 1.4
import QtQuick.Layouts 1.1
import QtQuick.Dialogs 1.2
import "../../js/Utils.js" as Utils
import "../../components" as MoneroComponents
ListView {
id: trackingListView
// items will not be drawn when a message is set
property string message: ""
boundsBehavior: ListView.StopAtBounds
Layout.fillWidth: true
clip: true
signal hideAmountToggled(string txid)
function viewTx(txid){
// @TODO: implement blockexplorer-like page. Redirect to history for now
appWindow.showPageRequest("History");
}
TextEdit {
// message box
visible: parent.message !== ""
anchors.fill: parent
anchors.margins: 20 * scaleRatio
anchors.topMargin: 10 * scaleRatio
wrapMode: Text.Wrap
font.pixelSize: 14 * scaleRatio
font.bold: false
color: "#767676"
textFormat: Text.RichText
text: parent.message
selectionColor: MoneroComponents.Style.dimmedFontColor
selectByMouse: true
readOnly: true
onFocusChanged: {if(focus === false) deselect() }
}
delegate: Item {
id: trackingTableItem
visible: trackingListView.message === ""
height: 53 * scaleRatio
width: parent.width
Layout.fillWidth: true
RowLayout {
id: container
height: parent.height
width: parent.width
spacing: 0
Item {
Layout.preferredHeight: parent.height
Layout.preferredWidth: 20 * scaleRatio
}
ColumnLayout {
spacing: 0
Layout.preferredHeight: 40 * scaleRatio
Layout.preferredWidth: 240 * scaleRatio
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 18 * scaleRatio
TextEdit {
id: dateString
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 12 * scaleRatio
font.bold: false
color: "#707070"
text: time_date + " (" + Utils.ago(time_epoch) + ") "
selectionColor: MoneroComponents.Style.dimmedFontColor
selectByMouse: true
readOnly: true
onFocusChanged: {if(focus === false) deselect() }
}
Rectangle {
anchors.left: dateString.right
anchors.leftMargin: 2
width: hideAmount.width + 2
height: 20
color: 'transparent'
TextEdit {
id: hideAmount
anchors.top: parent.top
anchors.topMargin: 1 * scaleRatio
anchors.left: parent.left
anchors.verticalCenter: parent.verticalCenter
readOnly: true
font.pixelSize: 12 * scaleRatio
font.bold: false
color: "#707070"
text: hide_amount ? "(" + qsTr("show") + ")" : "(" + qsTr("hide") + ")"
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
trackingListView.hideAmountToggled(txid);
hide_amount = !hide_amount;
}
}
}
}
Item {
Layout.preferredWidth: parent.width
Layout.preferredHeight: 18 * scaleRatio
TextEdit {
id: amountText
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 14 * scaleRatio
font.bold: true
color: hide_amount ? "#707070" : "#009F1E"
text: hide_amount ? '-' : '+' + amount
selectionColor: MoneroComponents.Style.dimmedFontColor
selectByMouse: true
readOnly: true
onFocusChanged: {if(focus === false) deselect() }
}
}
}
Item {
Layout.fillWidth: true
}
RowLayout {
spacing: 0
Layout.preferredHeight: parent.height
Layout.preferredWidth: 240 * scaleRatio
Item {
Layout.fillWidth: true
Layout.preferredHeight: parent.height
}
Item {
Layout.preferredWidth: 150 * scaleRatio
Layout.preferredHeight: parent.height
TextEdit {
anchors.horizontalCenter: parent.horizontalCenter
anchors.verticalCenter: parent.verticalCenter
font.pixelSize: 12 * scaleRatio
font.bold: false
color: "#a8a8a8"
text: {
if(in_txpool){
return qsTr("Awaiting in txpool") + translationManager.emptyString;
} else {
if(confirmations > 1){
if(confirmations > 100){
return "100+ " + qsTr("confirmations") + translationManager.emptyString;
} else {
return confirmations + " " + qsTr("confirmations") + translationManager.emptyString;
}
} else {
return "1 " + qsTr("confirmation") + translationManager.emptyString;
}
}
}
selectionColor: MoneroComponents.Style.dimmedFontColor
selectByMouse: true
readOnly: true
onFocusChanged: {if(focus === false) deselect() }
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
viewTx(txid);
}
}
}
Item {
Layout.preferredWidth: 30 * scaleRatio
Layout.preferredHeight: parent.height
Image {
anchors.verticalCenter: parent.verticalCenter
anchors.horizontalCenter: parent.horizontalCenter
Layout.preferredWidth: 12 * scaleRatio
Layout.preferredHeight: 21 * scaleRatio
source: "../../images/merchant/arrow_right.png"
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
viewTx(txid);
}
}
}
Item {
Layout.preferredWidth: 10 * scaleRatio
Layout.preferredHeight: parent.height
}
}
}
Rectangle {
anchors.right: parent.right
anchors.left: parent.left
anchors.top: container.bottom
height: 1
color: "#F0F0F0"
}
}
}

View File

@ -46,7 +46,7 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.margins: (isMobile)? 17 : 20 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 0 anchors.topMargin: 0
spacing: 30 * scaleRatio spacing: 30 * scaleRatio

View File

@ -55,7 +55,7 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.margins: (isMobile)? 17 : 20 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 0 anchors.topMargin: 0
spacing: 6 * scaleRatio spacing: 6 * scaleRatio

View File

@ -47,7 +47,7 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.margins: (isMobile)? 17 : 20 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 0 anchors.topMargin: 0
spacing: 10 spacing: 10

View File

@ -39,7 +39,7 @@ Rectangle{
/* main layout */ /* main layout */
ColumnLayout { ColumnLayout {
id: root id: root
anchors.margins: (isMobile)? 17 : 20 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 0 anchors.topMargin: 0
anchors.left: parent.left anchors.left: parent.left

View File

@ -46,7 +46,7 @@ Rectangle {
anchors.left: parent.left anchors.left: parent.left
anchors.top: parent.top anchors.top: parent.top
anchors.right: parent.right anchors.right: parent.right
anchors.margins: (isMobile)? 17 : 20 anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio
anchors.topMargin: 0 anchors.topMargin: 0
spacing: 0 spacing: 0

View File

@ -247,5 +247,11 @@
<file>images/miningxmr@2x.png</file> <file>images/miningxmr@2x.png</file>
<file>images/eyeHide.png</file> <file>images/eyeHide.png</file>
<file>images/eyeShow.png</file> <file>images/eyeShow.png</file>
<file>pages/merchant/Merchant.qml</file>
<file>pages/merchant/MerchantCheckbox.qml</file>
<file>pages/merchant/MerchantTrackingList.qml</file>
<file>images/merchant/arrow_right.png</file>
<file>images/merchant/bg.png</file>
<file>images/merchant/input_box.png</file>
</qresource> </qresource>
</RCC> </RCC>