From 85cd428dcb46763e626c1c9bae1c27b44d617d4f Mon Sep 17 00:00:00 2001 From: dsc Date: Sat, 8 Dec 2018 16:55:29 +0100 Subject: [PATCH] Merchant page --- LeftPanel.qml | 28 + MiddlePanel.qml | 24 +- components/Style.qml | 1 + components/TitleBar.qml | 58 +- images/merchant/arrow_right.png | Bin 0 -> 18203 bytes images/merchant/bg.png | Bin 0 -> 18041 bytes images/merchant/input_box.png | Bin 0 -> 15459 bytes js/TxUtils.js | 11 + js/Utils.js | 36 +- main.qml | 23 + monero-wallet-gui.pro | 5 +- pages/AddressBook.qml | 2 +- pages/Keys.qml | 2 +- pages/Mining.qml | 2 +- pages/Receive.qml | 486 +++------------- pages/SharedRingDB.qml | 2 +- pages/Sign.qml | 7 +- pages/Transfer.qml | 4 +- pages/TxKey.qml | 2 +- pages/merchant/Merchant.qml | 699 ++++++++++++++++++++++++ pages/merchant/MerchantCheckbox.qml | 63 +++ pages/merchant/MerchantTrackingList.qml | 231 ++++++++ pages/settings/SettingsInfo.qml | 2 +- pages/settings/SettingsLayout.qml | 2 +- pages/settings/SettingsLog.qml | 2 +- pages/settings/SettingsNode.qml | 2 +- pages/settings/SettingsWallet.qml | 2 +- qml.qrc | 6 + 28 files changed, 1250 insertions(+), 452 deletions(-) create mode 100644 images/merchant/arrow_right.png create mode 100644 images/merchant/bg.png create mode 100644 images/merchant/input_box.png create mode 100644 pages/merchant/Merchant.qml create mode 100644 pages/merchant/MerchantCheckbox.qml create mode 100644 pages/merchant/MerchantTrackingList.qml diff --git a/LeftPanel.qml b/LeftPanel.qml index d7cd0ee8..16b1a8f9 100644 --- a/LeftPanel.qml +++ b/LeftPanel.qml @@ -61,6 +61,7 @@ Rectangle { signal miningClicked() signal signClicked() signal keysClicked() + signal merchantClicked() function selectItem(pos) { menuColumn.previousButton.checked = false @@ -68,6 +69,7 @@ Rectangle { else if(pos === "History") menuColumn.previousButton = historyButton else if(pos === "Transfer") menuColumn.previousButton = transferButton else if(pos === "Receive") menuColumn.previousButton = receiveButton + else if(pos === "Merchant") menuColumn.previousButton = merchantButton else if(pos === "AddressBook") menuColumn.previousButton = addressBookButton else if(pos === "Mining") menuColumn.previousButton = miningButton else if(pos === "TxKey") menuColumn.previousButton = txkeyButton @@ -446,6 +448,32 @@ Rectangle { 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 --------------- MoneroComponents.MenuButton { diff --git a/MiddlePanel.qml b/MiddlePanel.qml index d47e3685..69d14d0f 100644 --- a/MiddlePanel.qml +++ b/MiddlePanel.qml @@ -38,6 +38,8 @@ import moneroComponents.Wallet 1.0 import "components" as MoneroComponents import "./pages" import "./pages/settings" +import "./pages/merchant" +import "components" as MoneroComponents Rectangle { id: root @@ -56,6 +58,7 @@ Rectangle { property Transfer transferView: Transfer { } property Receive receiveView: Receive { } + property Merchant merchantView: Merchant { } property TxKey txkeyView: TxKey { } property SharedRingDB sharedringdbView: SharedRingDB { } property History historyView: History { } @@ -72,11 +75,16 @@ Rectangle { signal getProofClicked(string txid, string address, string message); 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 { - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: parent.bottom + anchors.fill: parent + visible: currentView !== merchantView source: "../images/middlePanelBg.jpg" } @@ -123,6 +131,10 @@ Rectangle { name: "Receive" PropertyChanges { target: root; currentView: receiveView } PropertyChanges { target: mainFlickable; contentHeight: receiveView.receiveHeight + 100 } + }, State { + name: "Merchant" + PropertyChanges { target: root; currentView: merchantView } + PropertyChanges { target: mainFlickable; contentHeight: merchantView.merchantHeight + 100 } }, State { name: "TxKey" PropertyChanges { target: root; currentView: txkeyView } @@ -172,8 +184,8 @@ Rectangle { ColumnLayout { anchors.fill: parent - anchors.margins: 18 - anchors.topMargin: appWindow.persistentSettings.customDecorations ? 50 : 0 + anchors.margins: currentView !== merchantView ? 20 * scaleRatio : 0 + anchors.topMargin: appWindow.persistentSettings.customDecorations ? 50 * scaleRatio : 0 spacing: 0 Flickable { diff --git a/components/Style.qml b/components/Style.qml index 9c6fded4..b5926dab 100644 --- a/components/Style.qml +++ b/components/Style.qml @@ -12,6 +12,7 @@ QtObject { property string orange: "#FF6C3C" property string white: "#FFFFFF" property string green: "#2EB358" + property string moneroGrey: "#4C4C4C" property string defaultFontColor: "white" property string dimmedFontColor: "#BBBBBB" diff --git a/components/TitleBar.qml b/components/TitleBar.qml index a759adce..6c9bfb1e 100644 --- a/components/TitleBar.qml +++ b/components/TitleBar.qml @@ -47,7 +47,7 @@ Rectangle { property string title property int mouseX: 0 property bool containsMouse: false - property alias basicButtonVisible: goToBasicVersionButton.visible + property bool basicButtonVisible: false property bool customDecorations: persistentSettings.customDecorations property bool showWhatIsButton: true property bool showMinimizeButton: false @@ -56,6 +56,9 @@ Rectangle { property bool showMoneroLogo: false property bool small: false property alias titleBarGradientImageOpacity: titleBarGradientImage.opacity + property bool orange: false + property string buttonHoverColor: "#262626" + property string buttonHoverColorOrange: "#44FFFFFF" signal closeClicked signal maximizeClicked @@ -70,11 +73,19 @@ Rectangle { Image { id: titleBarGradientImage + visible: !titleBar.orange anchors.fill: parent height: titleBar.height width: titleBar.width source: "../images/titlebarGradient.jpg" } + + Rectangle { + visible: titleBar.orange + width: parent.width + height: parent.height + color: "#ff6600" + } } Item { @@ -82,11 +93,11 @@ Rectangle { width: 125 height: parent.height anchors.centerIn: parent - visible: customDecorations && showMoneroLogo + visible: customDecorations z: parent.z + 1 Image { - visible: !isMobile + visible: !isMobile && showMoneroLogo && !titleBar.orange anchors.left: parent.left anchors.top: parent.top anchors.topMargin: 11 @@ -94,6 +105,16 @@ Rectangle { height: 28 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 { @@ -115,7 +136,7 @@ Rectangle { color: "transparent" height: titleBar.height width: height - visible: isMobile + visible: !titleBar.orange && titleBar.basicButtonVisible z: parent.z + 2 Image { @@ -130,7 +151,7 @@ Rectangle { hoverEnabled: true anchors.fill: parent cursorShape: Qt.PointingHandCursor - onEntered: goToBasicVersionButton.color = "#262626"; + onEntered: { goToBasicVersionButton.color = titleBar.orange ? titleBar.buttonHoverColorOrange : titleBar.buttonHoverColor } onExited: goToBasicVersionButton.color = "transparent"; onClicked: { releaseFocus() @@ -166,7 +187,13 @@ Rectangle { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onEntered: minimizeButton.color = "#262626"; + onEntered: { + if(titleBar.orange){ + minimizeButton.color = titleBar.buttonHoverColorOrange; + } else { + minimizeButton.color = titleBar.buttonHoverColor; + } + } onExited: minimizeButton.color = "transparent"; onClicked: minimizeClicked(); } @@ -193,7 +220,13 @@ Rectangle { anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor - onEntered: maximizeButton.color = "#262626"; + onEntered: { + if(titleBar.orange){ + maximizeButton.color = titleBar.buttonHoverColorOrange; + } else { + maximizeButton.color = titleBar.buttonHoverColor; + } + } onExited: maximizeButton.color = "transparent"; onClicked: maximizeClicked(); } @@ -219,7 +252,13 @@ Rectangle { onClicked: closeClicked(); hoverEnabled: true cursorShape: Qt.PointingHandCursor - onEntered: closeButton.color = "#262626"; + onEntered: { + if(titleBar.orange){ + closeButton.color = titleBar.buttonHoverColorOrange; + } else { + closeButton.color = titleBar.buttonHoverColor; + } + } onExited: closeButton.color = "transparent"; } } @@ -227,6 +266,7 @@ Rectangle { // window borders Rectangle { + visible: !titleBar.orange anchors.bottom: parent.bottom anchors.right: parent.right anchors.left: parent.left @@ -236,10 +276,10 @@ Rectangle { } Rectangle { + visible: titleBar.small && !titleBar.orange anchors.top: parent.top anchors.right: parent.right anchors.left: parent.left - visible: titleBar.small height: 1 color: "#2F2F2F" z: parent.z + 1 diff --git a/images/merchant/arrow_right.png b/images/merchant/arrow_right.png new file mode 100644 index 0000000000000000000000000000000000000000..6d0d6aca09a98305911120f58c9b84e6cfc33da9 GIT binary patch literal 18203 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 Dzt0*! literal 0 HcmV?d00001 diff --git a/images/merchant/bg.png b/images/merchant/bg.png new file mode 100644 index 0000000000000000000000000000000000000000..b6687a4015aac445fc22d539deadc0c3cad9cd3d GIT binary patch literal 18041 zcmeI3do)z*`@pxOll!HMRMHuAk<69BOk>2j<`zciYA)NDFf(SR29-#n%SlqXMRake zi&CPJL|39yxs?h@9r_-WPKDndD%#bo*7|*a|NPeM#hU%R&-*;j`+nZ%efB%E*50|? z#ZE)pq&dxlMfK>OVLtd|z{)W;;(#O9T|*w>ZnZmNdi(+d z9R>Aeu{xPe8g5so*w{?cx{(npyLWHI9t}4``BNgZI0Co9drjB4pPX>4& zP9iPVt&qD6$b{Tik_RfBQ!6lYVn)OagpwGwAKWawOopaO;cvKt$U6e)hnr^FT$dy3!dyUKzl+#YQgzCT#+IZ|LcT0Nay+>9&1dq<|5&O6L)5B3H>HgXO%uP;>Qb9W)nDH=+GTOIeDl3{9Sei!@ey+J6TCGU7F$ z+4c{G)T$QM)|%`sQC)|8b!GC1E}h;!+*y~;TM7WrgyHqWhUnti5lbdV1PvdZ-#f{F zty0E@!QFmm)RvwDGP;|+`gvkDHktcMo0>X1IvST%S*@bo^4c=Ys$5s$H5B-7IO)^i z+ozYG&x^#ZiL{&a{%Pf%0jCpl>h>#ar?h7soTHp7py+f4MX;w<3e?BVa4-70O%{@Uj0j7 zUN*u%>RFv=PR2LS7gNP|LeS<~+Iu^ByENM8Am+?7p3e>Av3_P9LW>Ol`1j;JE8`8|J1O+t1cZxY^7M zI3q_mV2D_JBf2B4<64JNhgOG9-zvp4`qJRz?>(ODyWd*BY*wdwr*Y?GFbQYrc`U{K zaZV<63O;V7Rbgg{`$@N$oN2}zQV4gWW;i&WC_H-b{%n6u|3UW2m%@uv9v__Rl6cRn z;dZy(%fy$m10rRW9U4(Ju`K;rsWatfvSx{9o=kGenstBotQsoD6#F`>*;M! zVz5~w{+8mwWd|1=)G|O*bMvzDp5!Tc_Iuu@#^tW{*ivNcY2?wKtMJ#Me9yeJ<*z-o zJXpC)a$@pWIb5p6@o=A^os)g8IbCz59Cyn&y{^5y8ll9PRUBfd6QGj6V?!jqYQgEd zO`A6wlqC&mm+dys%grQEjFuUB6}$Hz@UxI>PYQm2Sm>hArJtSHAHM6!^f!y1FVe~~ zb}Ge!s{`^5B_8t5x6gOWw|jlbq^Ku5E$fm^na{^aRm+-X3s*0+W5w0}p4R2j+l#8$X&$Nf?O2+uQbAOI?Czb8+is&0$*5GV>c=-O zbu1IU4PNsstl@21YH@01Pfp9pUXxySZ%pfvJH|&ArTZ8O?p?brH5vqJ&+uGhHf z>!RBzTlGemBVi-;Kz77@8J6tMNXy7ozkPfkwpr)OUJGIi`0h+okL8|>QcDfXxNU{o zI=^Gf&W;l8|2K`Grw<|X#QTs z&`Rw}mM*&9q&~Khvf5y-OP{@gbJ+gBd%O~h!Y?fda}J1Hd-c|~tLJ0_UMqPjc1CY` z-TJ6!jjK1>r|lf=!;Um9vc}F`Mbld}6E(Ir>bHh;YQ=DGT}f^|a5gu;s+3vh*WlOv z;t4W+mFl6q#21OG9F?Z#vnp*X&i1^0XVZdCysO_*7F9wiJ$8k3p&ZPgReq z>(GwwL4~%#xhllwje9l~Jd?Zs$3|_IHhR5PN?(d?sz=JlRrFOCUGI?VAHI4L_Q|g0 z54FR_B%jb_{nVT6=I(9x6Igw9d(sZ3Z3##H+v?kLv}98kqqc3m8u!gbb>XuHn~~jJ zOUjqjuI{@a>^sxaInC>WSISA^x}aCWSES*D`>r)*hs#omN>>Mz_J&$0f(z-l4MNb-EduKzMG!AL{;;O{rtn zElAfKK0186NOJ}{{QZO1k810a>N6Aj5)-ZjZ9Xyds^aymlP%Si)pIZ33^Bs49a!

HAV)w9G1ndSON8#l&1hZ3Xcu9Cxvr%+Ai~>u zIfBCFg9tpz1j)c+u?SNF3X3;0!r=`OI1Cn##t_h03=)GSVlhM<2J!jQB`ZRABtFxR z=x%NMIUFRJ>-r0YJR%w`5{XbEBNUg>r$<_Xzc4npjgM(1u2n(OL{1AV&VtGKYRTKc z4;%%|WT3%7FbIk%fb6h8oP&_X3T6f6~#C-|z z12^A=vS$mRYzm!_Wl*k-Rvr&9_}kVmia_>g1>w>70#Lj$&2_)*%&#TqYef;;jBaou zjUlc*WQI6>K_&_PP3!ouzFK}ZV}HM#zlLDU{{-`&#}N6097*Xv=4!Op&z%UkenJtA z4_dOI4EdFvIp+0SGq`&aT3o9G+5ZO%A)syi-z~$h?W+G5%P^kB41XGj1v1I#FA4mm z;n!AfY(2l-SzmVR_dAg^URl0W0!yf}i8rj^+l(09uvRqIx2@t~f6kvD*DEXyPoRT% z0#vUs^s#zHAeb6MwFSh0OcOIafdN(jAEn?}>$ed}=wBP}glSQ@fn2^bmkE-MCR&d- zk7J!+1^rC8eC$_3vcTQW3gKWwF~Jf{OprJfcA}dJ{U-QfGKhX$eh^JaW(Uz&AezTv zkEAat?ILg@rItznjXln2#c<<%x5HQf&nhiYy1>#cv#RT5| zLpz^wJ0mfS(O{#N6EvSK$C7KpiFGus$6BHDFmx10e?5zTm+@a8Apgyq@hSZ`y~2D+ zgTR1D7Nxi(^MPrlxL`mei&9*Y`M|VNTreP#MJX=Hd|+BBE*KEWq7;{8J}|8m7YvAG zQHo14ADC8(3kF28D8(h24@@h?1p^{kl;V=i2d0(cf&q~%N^wc%1Jg=z!GK5>rMM*X zfoY|KYaz0%hh z?ms&uCd;_Tq)MIlT>XT@?3&=+p_^xBXUmv1`A4jGnmORA_IQ(1oW?2r8fLA|9bXY= zJ?N5j{wc2g-R5Q5r!^z>2D=9`9V;?%M{rTb_Oz{6*74tG)z1iDe9<&`Fw3{I@$bw$ x@TA4o&^AW6WkdZ)u>GPn|LNi`X?!9JT-inX<5+om6Erx$!N$e9$jW!y{{RYH#Qp#P literal 0 HcmV?d00001 diff --git a/images/merchant/input_box.png b/images/merchant/input_box.png new file mode 100644 index 0000000000000000000000000000000000000000..66a49e5541a1aa7f38bcaeb86dfa85e799eed4b2 GIT binary patch literal 15459 zcmeI3e{3AZ6~`xWF~KxS-AW~a6pnR4q+z|g_xrYY7aX61ZMej}oQoZxVQ+VAZ+*9W z+^&6RmnLqL(xiVB2|{Z-4irHu)Br&(qTmD!A+1o`q!EY&l34s`kZMztfcqm6g>?3Q zdu)#^mHt!BO7`8G@4WYRKJ(_?{dGnrz|yA1Py~IRMt>`oRHFZzw%&9v z`nW9F*lqxD`B$vJMPPVjIRL&XHQHvjg>MyQJy9zudJNWP5=k^00RNgyQj)u&X^X*5 zRSPg@4~#K3RS7V+xx#!nSr5C^#{LxC*uOq1_jk)ag;^7<@@GU8AOTIumPy1lL(Bx2 zY+Mn2w}v^!mYrgD2bel5p{*_4Vyo9v(B`Ujv$7xvHjl4XaCsee*NrwiFSt0~#|b>k z3!=b_cHWl1m|zw9=1(adVkERKpAK08rpq*wBFClE>DsiTR!?Y*t(;ruUcuhOrVAul%|a$zmeS$g@Kfxr~(Lgj$}P z$0-~pyCo8HdKzX!FS?Wb%H~XHMEjDEi$Fu~Ny)IG7iwnBTsNV}#W=~HR6NT^kvSNL z3FKm+n-}K1g=Cj*>PDA7&$^59ysJ0Wj?(7LSc0X@>jpFGJZ_efbIX=OZioD+{>1te zluSJp)%AEVS7j}QySLTXXRFF~!)>aj=xJk>pTk{B`(26#Ly`%Ds2;tnU}t%6ly`|v zUKE7Ys4#iHEJ$u979)&mUy)2{AyH<`#pCFT+R?Y5D5aS*V}!$ElV+HbCc~ys5Ussd zRTa_e@OXKbOJQY?hq0qU5ujn!g zUn~#NWp_GxyH{d6B+0=F0`#&mx68pQ9>p%8R8EJ>RY+78Y*wNTDHZi4DPA_mT8m<> zg?Bl5uN*_$WOuWY;&iZH$>U&SPN;ZfUh;Z%tf8)hKKiaR z8y0k`{x7znRK;?aq;*0i$Q25>FtD(l%kAgPlT|pavrnR5k;M)@m5|J!nvgmnm()7_ zTxqbB(_iXA^?F=Sg>?l69e!@E;W97l$4Nw80#&S(J)Nix&Kkq#w^^q>-jkj0vGeFc zk1*)UyOztiYdhJsT*h5Xo~@c{2JQK5*l?Mb^$RLyVQfSXdobvBxg4nZ;={N{i<*J) zwvdYYykYe#yB#eo>x3(FdbXE&7P4g>%Y?jRt`^Fk%d&?&H;SJ6&;ut|eBzvS{)=t$ z^ZZg;(dQWzi$#-QK*%P=MdpL46c+}BY*JigKA1{zVL-?x#YN_WsT3Cmgltk=WImWm zabZBnCdEbOgQ*l3283)d^!JIYK72F0?~R{?whvxd?bvLe zZf~A&I5s{QVL+xm6yHC-hk5Vi@w4Nf_PTd}`gNxHgMSQ%&a7+QKR7hmyz1I65ZHD_ z^_dS(!H($(vwx3!_mQ8EOh-bEt=*>*^*{buh4+c^lTSYwdG$|^?^=83iSNHYGQF&I z?}+35k%yPA1P256B;FYu8hxsU8I4x^j`&{hax$&ss?T}VO=G|8KJ{eB4b1x1)(xqn z(c85(Ns#oW-yZt%HMgG)HEf?e``b}IviOVL&;Rog`D;SM_Gj+hSGoT?@?&54>zc^% z(>q>$c53j#=64?21d@Z#{$lcjAMCkfYD}+xbkV`B=f8V&d0mBf%bVZWGW7~Lw`KXe zi_brI{FMi)2kwh|@3{mlSqT(aV+x=&Udi;gaPY#W9lixnqyKLE^%43{4Reky1 zUHxOnB7gYxF1zP9`_TBhTLwNmS@YGamsJmMxc-5?!z+a8p5}?4?s)O$!_ObsdZ;ow yAl1NC@835xKJ-@1e>Jyr$7hQHxOlMwy!=P@%*W?XJYl^C+|;l>^wiqBe)u1Cp%mf( literal 0 HcmV?d00001 diff --git a/js/TxUtils.js b/js/TxUtils.js index a6cca114..c78436d7 100644 --- a/js/TxUtils.js +++ b/js/TxUtils.js @@ -64,3 +64,14 @@ function isValidOpenAliasAddress(address) { // we can get an awful lot of valid domains, including non ASCII chars... accept anything 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 +} diff --git a/js/Utils.js b/js/Utils.js index dad1f143..8ba7fa85 100644 --- a/js/Utils.js +++ b/js/Utils.js @@ -48,4 +48,38 @@ function showSeedPage() { passwordDialog.open(); if(isMobile) hideMenu(); updateBalance(); -} \ No newline at end of file +} + +function ago(epoch) { + // Returns ' [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") + } + } + } +} diff --git a/main.qml b/main.qml index d23cb5e5..4cde64e3 100644 --- a/main.qml +++ b/main.qml @@ -86,6 +86,11 @@ ApplicationWindow { // true if wallet ever synchronized 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 showPageRequest(page) { @@ -1371,6 +1376,15 @@ ApplicationWindow { updateBalance(); } + onMerchantClicked: { + middlePanel.state = "Merchant"; + middlePanel.flickable.contentY = 0; + if(isMobile) { + hideMenu(); + } + updateBalance(); + } + onTxkeyClicked: { middlePanel.state = "TxKey"; middlePanel.flickable.contentY = 0; @@ -1825,6 +1839,15 @@ ApplicationWindow { onTriggered: checkUpdates() } + function titlebarToggleOrange(flag){ + // toggle titlebar orange style + if(flag !== undefined){ + titleBar.orange = flag; + } else { + titleBar.orange = !titleBar.orange; + } + } + function releaseFocus() { // Workaround to release focus from textfield when scrolling (https://bugreports.qt.io/browse/QTBUG-34867) if(isAndroid) { diff --git a/monero-wallet-gui.pro b/monero-wallet-gui.pro index 7213544a..2662c5c9 100644 --- a/monero-wallet-gui.pro +++ b/monero-wallet-gui.pro @@ -97,6 +97,7 @@ SOURCES = *.qml \ components/*.qml \ pages/*.qml \ pages/settings/*.qml \ + pages/merchant/*.qml \ wizard/*.qml \ wizard/*js } @@ -458,7 +459,9 @@ OTHER_FILES += \ DISTFILES += \ notes.txt \ monero/src/wallet/CMakeLists.txt \ - components/MobileHeader.qml + components/MobileHeader.qml \ + pages/merchant/Merchant.qml \ + pages/merchant/MerchantCheckbox.qml # windows application icon diff --git a/pages/AddressBook.qml b/pages/AddressBook.qml index 596210cb..36de37d5 100644 --- a/pages/AddressBook.qml +++ b/pages/AddressBook.qml @@ -39,7 +39,7 @@ Rectangle { ColumnLayout { id: columnLayout - anchors.margins: (isMobile)? 17 : 40 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right diff --git a/pages/Keys.qml b/pages/Keys.qml index b2ac4b1f..8993bc4b 100644 --- a/pages/Keys.qml +++ b/pages/Keys.qml @@ -53,7 +53,7 @@ Rectangle { anchors.top: parent.top anchors.right: parent.right - anchors.margins: (isMobile)? 17 : 20 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 40 * scaleRatio spacing: 30 * scaleRatio diff --git a/pages/Mining.qml b/pages/Mining.qml index e4bcbd76..92afaf6a 100644 --- a/pages/Mining.qml +++ b/pages/Mining.qml @@ -39,10 +39,10 @@ Rectangle { ColumnLayout { id: mainLayout + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right - anchors.margins: 40 * scaleRatio spacing: 20 * scaleRatio Layout.fillWidth: true diff --git a/pages/Receive.qml b/pages/Receive.qml index a65e3da0..2c840ab4 100644 --- a/pages/Receive.qml +++ b/pages/Receive.qml @@ -32,7 +32,7 @@ import QtQuick.Controls.Styles 1.4 import QtQuick.Layouts 1.1 import QtQuick.Dialogs 1.2 -import "../components" +import "../components" as MoneroComponents import moneroComponents.Clipboard 1.0 import moneroComponents.Wallet 1.0 import moneroComponents.WalletManager 1.0 @@ -46,115 +46,7 @@ Rectangle { id: pageReceive color: "transparent" property var model - property var current_address - property int current_subaddress_table_index: 0 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 += ": " + "
" + - qsTr("Expected") + ": " + amountToReceiveLine.text + "
" + - 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 + "
" + list.join("
")) - } function renameSubaddressLabel(_index){ inputDialog.labelText = qsTr("Set the label of the selected address:") + translationManager.emptyString; @@ -171,7 +63,7 @@ Rectangle { /* main layout */ ColumnLayout { id: mainLayout - anchors.margins: (isMobile)? 17 : 40 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 40 * scaleRatio anchors.left: parent.left @@ -188,24 +80,10 @@ Rectangle { id: addressRow spacing: 0 - LabelSubheader { + MoneroComponents.LabelSubheader { Layout.fillWidth: true textFormat: Text.RichText - text: "" + - qsTr("Addresses") + - " " + - qsTr("Help") + "" + - translationManager.emptyString - onLinkActivated: { - receivePageDialog.title = qsTr("Tracking payments") + translationManager.emptyString; - receivePageDialog.text = qsTr( - "

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.

" - ) - receivePageDialog.icon = StandardIcon.Information - receivePageDialog.open() - } + text: qsTr("Addresses") } ColumnLayout { @@ -245,9 +123,9 @@ Rectangle { anchors.rightMargin: 80 color: "transparent" - Label { + MoneroComponents.Label { id: idLabel - color: index === current_subaddress_table_index ? "white" : "#757575" + color: index === appWindow.current_subaddress_table_index ? "white" : "#757575" anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left anchors.leftMargin: 6 @@ -256,7 +134,7 @@ Rectangle { text: "#" + index } - Label { + MoneroComponents.Label { id: nameLabel color: "#a5a5a5" anchors.verticalCenter: parent.verticalCenter @@ -267,7 +145,7 @@ Rectangle { text: label } - Label { + MoneroComponents.Label { color: "white" anchors.verticalCenter: parent.verticalCenter anchors.left: nameLabel.right @@ -299,7 +177,7 @@ Rectangle { } } - IconButton { + MoneroComponents.IconButton { id: renameButton imageSource: "../images/editIcon.png" anchors.verticalCenter: parent.verticalCenter @@ -313,7 +191,7 @@ Rectangle { } } - IconButton { + MoneroComponents.IconButton { id: copyButton imageSource: "../images/copyToClipboard.png" anchors.verticalCenter: parent.verticalCenter @@ -329,20 +207,17 @@ Rectangle { } onCurrentItemChanged: { // reset global vars - current_subaddress_table_index = subaddressListView.currentIndex; - current_address = appWindow.currentWallet.address( + appWindow.current_subaddress_table_index = subaddressListView.currentIndex; + appWindow.current_address = appWindow.currentWallet.address( appWindow.currentWallet.currentSubaddressAccount, subaddressListView.currentIndex ); - - // reset tracking table - trackingModel.clear(); } } } // 'fake' row for 'create new address' - ColumnLayout{ + ColumnLayout { id: createAddressRow Layout.fillWidth: true spacing: 0 @@ -353,13 +228,13 @@ Rectangle { height: 1 } - Rectangle{ + Rectangle { id: createAddressRect Layout.preferredHeight: subaddressListRow.subaddressListItemHeight color: "transparent" Layout.fillWidth: true - Label { + MoneroComponents.Label { color: "#757575" anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left @@ -396,7 +271,8 @@ Rectangle { } RowLayout { - CheckBox2 { + Layout.topMargin: 22 * scaleRatio + MoneroComponents.CheckBox2 { id: showAdvancedCheckbox checked: persistentSettings.receiveShowAdvanced onClicked: { @@ -405,282 +281,72 @@ Rectangle { text: qsTr("Advanced options") + translationManager.emptyString } } - - GridLayout { - id: advancedRow - columns: (isMobile)? 1 : 2 - Layout.fillWidth: true - columnSpacing: 32 * scaleRatio + + RowLayout { + Layout.topMargin: 6 * scaleRatio visible: persistentSettings.receiveShowAdvanced + Layout.fillWidth: true - ColumnLayout { - Layout.alignment: Qt.AlignTop + MoneroComponents.LineEditMulti { + id: paymentUrl Layout.fillWidth: true - spacing: 20 * scaleRatio - LabelSubheader { - Layout.fillWidth: true - textFormat: Text.RichText - text: "" + - qsTr("QR Code") + - " " + - qsTr("Help") + "" + - translationManager.emptyString - onLinkActivated: { - receivePageDialog.title = qsTr("QR Code") + translationManager.emptyString; - receivePageDialog.text = qsTr( - "

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.

" - ) - 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 - } - } + labelText: qsTr("Payment URL") + translationManager.emptyString + text: TxUtils.makeQRCodeString(appWindow.current_address) + readOnly: true + copyButton: true + wrapMode: Text.WrapAnywhere } + } - ColumnLayout { - id: trackingRow - Layout.alignment: Qt.AlignTop + GridLayout{ + visible: persistentSettings.receiveShowAdvanced + Layout.topMargin: 10 * scaleRatio + columns: 2 + columnSpacing: 30 * scaleRatio + + RowLayout { + property int qrSize: 220 * scaleRatio Layout.fillWidth: true - spacing: 0 * scaleRatio - LabelSubheader { - Layout.fillWidth: true - textFormat: Text.RichText - text: "" + - qsTr("Tracking") + - " " + - qsTr("Help") + "" + - translationManager.emptyString - onLinkActivated: { - receivePageDialog.title = qsTr("Tracking payments") + translationManager.emptyString; - receivePageDialog.text = qsTr( - "

This is a simple sales tracker:

" + - "

Let your customer scan that QR code to make a payment (if that customer has software which " + - "supports QR code scanning).

" + - "

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.

" + - "

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).

" - ) - receivePageDialog.icon = StandardIcon.Information - receivePageDialog.open() - } - } + Rectangle { + id: qrContainer + radius: 4 * scaleRatio + color: "white" + Layout.preferredWidth: parent.qrSize + Layout.preferredHeight: parent.qrSize - ListModel { - id: trackingModel - } - - 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 + Image { + id: qrCode anchors.fill: parent - clip: true - boundsBehavior: ListView.StopAtBounds - model: trackingModel - delegate: Item { - id: trackingTableItem - height: 46 - width: parent.width - Layout.fillWidth: true + anchors.margins: 1 * scaleRatio - Rectangle{ - anchors.right: parent.right - anchors.left: parent.left - anchors.top: parent.top - height: 1 - color: "#404040" - visible: index !== 0 - } + smooth: false + fillMode: Image.PreserveAspectFit + source: "image://qrcode/" + TxUtils.makeQRCodeString(appWindow.current_address) - Image { - id: arrowImage - source: "../images/upArrow-green.png" - height: 18 * scaleRatio - width: 12 * scaleRatio - anchors.verticalCenter: parent.verticalCenter - anchors.left: parent.left - anchors.leftMargin: 8 - } - - 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; - } - } + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: { + if (mouse.button == Qt.RightButton){ + qrMenu.x = this.mouseX; + qrMenu.y = this.mouseY; + qrMenu.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 - } + onPressAndHold: qrFileDialog.open() } } - } - RowLayout { - visible: trackingTableRow.visible && x.text !== "" && amountToReceiveLine.text !== "" - Layout.topMargin: 14 * scaleRatio - Layout.fillWidth: true - Layout.preferredHeight: 40 * scaleRatio + Menu { + id: qrMenu + title: "QrCode" - Label { - id: toReceiveSatisfiedLine - color: "white" - 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 + MenuItem { + text: qsTr("Save As") + translationManager.emptyString; + onTriggered: qrFileDialog.open() + } } } } @@ -693,12 +359,12 @@ Rectangle { FileDialog { id: qrFileDialog - title: "Please choose a name" + title: qsTr("Please choose a name") folder: shortcuts.pictures selectExisting: false nameFilters: ["Image (*.png)"] 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) ) receivePageDialog.title = qsTr("Save QrCode") + 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() { console.log("Receive page loaded"); subaddressListView.model = appWindow.currentWallet.subaddressModel; 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) - current_subaddress_table_index = 0; - subaddressListView.currentIndex = 0; } - - update() - timer.running = true - - trackingEnabled.checked = false } function clearFields() { @@ -737,8 +390,5 @@ Rectangle { } function onPageClosed() { - timer.running = false - trackingEnabled.checked = false - trackingModel.clear() } } diff --git a/pages/SharedRingDB.qml b/pages/SharedRingDB.qml index 82d4fd96..b28b3ac6 100644 --- a/pages/SharedRingDB.qml +++ b/pages/SharedRingDB.qml @@ -79,7 +79,7 @@ Rectangle { /* main layout */ ColumnLayout { id: mainLayout - anchors.margins: (isMobile)? 17 : 20 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 40 * scaleRatio anchors.left: parent.left diff --git a/pages/Sign.qml b/pages/Sign.qml index 49b80166..1a7bd877 100644 --- a/pages/Sign.qml +++ b/pages/Sign.qml @@ -86,12 +86,9 @@ Rectangle { // sign / verify 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.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio + anchors.left: parent.left anchors.right: parent.right Rectangle { diff --git a/pages/Transfer.qml b/pages/Transfer.qml index 0a1693ef..056507c8 100644 --- a/pages/Transfer.qml +++ b/pages/Transfer.qml @@ -103,7 +103,7 @@ Rectangle { ColumnLayout { id: pageRoot - anchors.margins: (isMobile)? 17 : 20 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 40 * scaleRatio anchors.left: parent.left @@ -427,7 +427,7 @@ Rectangle { anchors.top: pageRoot.bottom anchors.left: parent.left anchors.right: parent.right - anchors.margins: (isMobile)? 17 : 20 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 32 * scaleRatio spacing: 26 * scaleRatio enabled: !viewOnly || pageRoot.enabled diff --git a/pages/TxKey.qml b/pages/TxKey.qml index 22174f3e..04da3c65 100644 --- a/pages/TxKey.qml +++ b/pages/TxKey.qml @@ -45,7 +45,7 @@ Rectangle { /* main layout */ ColumnLayout { id: mainLayout - anchors.margins: 40 * scaleRatio + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right diff --git a/pages/merchant/Merchant.qml b/pages/merchant/Merchant.qml new file mode 100644 index 00000000..5f816782 --- /dev/null +++ b/pages/merchant/Merchant.qml @@ -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( + "" + + "

This page will automatically scan the blockchain and the tx pool " + + "for incoming transactions using the QR code.

" + + "

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).

" + ); + } 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: "Currently selected address: " + addressLabel + " (Change)" + 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 } +} diff --git a/pages/merchant/MerchantCheckbox.qml b/pages/merchant/MerchantCheckbox.qml new file mode 100644 index 00000000..49ffbc7f --- /dev/null +++ b/pages/merchant/MerchantCheckbox.qml @@ -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 + } +} diff --git a/pages/merchant/MerchantTrackingList.qml b/pages/merchant/MerchantTrackingList.qml new file mode 100644 index 00000000..33a756fc --- /dev/null +++ b/pages/merchant/MerchantTrackingList.qml @@ -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" + } + } +} diff --git a/pages/settings/SettingsInfo.qml b/pages/settings/SettingsInfo.qml index 6cec2e04..8d1a98e5 100644 --- a/pages/settings/SettingsInfo.qml +++ b/pages/settings/SettingsInfo.qml @@ -46,7 +46,7 @@ Rectangle { anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right - anchors.margins: (isMobile)? 17 : 20 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 0 spacing: 30 * scaleRatio diff --git a/pages/settings/SettingsLayout.qml b/pages/settings/SettingsLayout.qml index 6914a438..1ae870e3 100644 --- a/pages/settings/SettingsLayout.qml +++ b/pages/settings/SettingsLayout.qml @@ -55,7 +55,7 @@ Rectangle { anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right - anchors.margins: (isMobile)? 17 : 20 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 0 spacing: 6 * scaleRatio diff --git a/pages/settings/SettingsLog.qml b/pages/settings/SettingsLog.qml index 23234862..1933ae93 100644 --- a/pages/settings/SettingsLog.qml +++ b/pages/settings/SettingsLog.qml @@ -47,7 +47,7 @@ Rectangle { anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right - anchors.margins: (isMobile)? 17 : 20 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 0 spacing: 10 diff --git a/pages/settings/SettingsNode.qml b/pages/settings/SettingsNode.qml index bdfc906c..3b62a43f 100644 --- a/pages/settings/SettingsNode.qml +++ b/pages/settings/SettingsNode.qml @@ -39,7 +39,7 @@ Rectangle{ /* main layout */ ColumnLayout { id: root - anchors.margins: (isMobile)? 17 : 20 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 0 anchors.left: parent.left diff --git a/pages/settings/SettingsWallet.qml b/pages/settings/SettingsWallet.qml index 004a0bec..7998a328 100644 --- a/pages/settings/SettingsWallet.qml +++ b/pages/settings/SettingsWallet.qml @@ -46,7 +46,7 @@ Rectangle { anchors.left: parent.left anchors.top: parent.top anchors.right: parent.right - anchors.margins: (isMobile)? 17 : 20 + anchors.margins: (isMobile)? 17 * scaleRatio : 20 * scaleRatio anchors.topMargin: 0 spacing: 0 diff --git a/qml.qrc b/qml.qrc index 2f00e2a2..f3cbd454 100644 --- a/qml.qrc +++ b/qml.qrc @@ -247,5 +247,11 @@ images/miningxmr@2x.png images/eyeHide.png images/eyeShow.png + pages/merchant/Merchant.qml + pages/merchant/MerchantCheckbox.qml + pages/merchant/MerchantTrackingList.qml + images/merchant/arrow_right.png + images/merchant/bg.png + images/merchant/input_box.png