From 5c1dcf3e9bdb317dd8b42aadb18657eb4bfa2e0f Mon Sep 17 00:00:00 2001 From: Dan J Miller Date: Fri, 12 Jan 2018 16:18:18 -0330 Subject: [PATCH] [NewUI-flat] New deposit ether modal UI. (#2642) * New deposit ether modal. * New deposit modal full screen on mobile, and other style fixes. * Hide shapeshift option from deposit modal for now. * Add shapeshift form to new deposit modal. * Store recipient address for shapeshift tx in background. * Use Simpledropdown to achieve desired styling in coin selector. * Lint fix * Fix typos and remove dead code. * Remove storage of shapeshift receiving address from background. * Fix typos --- app/images/coinbase logo.png | Bin 0 -> 9775 bytes app/images/shapeshift logo.png | Bin 0 -> 17537 bytes ui/app/actions.js | 5 +- ui/app/components/modals/buy-options-modal.js | 2 +- .../components/modals/deposit-ether-modal.js | 182 +++++++ ui/app/components/modals/modal.js | 32 ++ ui/app/components/shapeshift-form.js | 482 ++++++++---------- ui/app/components/shift-list-item.js | 50 +- ui/app/components/tx-list.js | 10 +- ui/app/components/tx-view.js | 2 +- ui/app/css/itcss/components/modal.scss | 254 ++++++++- ui/app/css/itcss/settings/variables.scss | 2 + ui/app/reducers/app.js | 5 +- yarn.lock | 23 +- 14 files changed, 724 insertions(+), 325 deletions(-) create mode 100644 app/images/coinbase logo.png create mode 100644 app/images/shapeshift logo.png create mode 100644 ui/app/components/modals/deposit-ether-modal.js diff --git a/app/images/coinbase logo.png b/app/images/coinbase logo.png new file mode 100644 index 0000000000000000000000000000000000000000..a23d7926d3b88bebb0726b68c1adb703fbaf06f3 GIT binary patch literal 9775 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 z�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 JPDHLkV1k-K*p>hQ literal 0 HcmV?d00001 diff --git a/app/images/shapeshift logo.png b/app/images/shapeshift logo.png new file mode 100644 index 0000000000000000000000000000000000000000..ac8faba5b1a1e9aad5ae9a71d7480684201aee5d GIT binary patch literal 17537 zcmXVYRahNO)9q&CzH!}n&;)mPcXxMpcXxMp2=2jyI|Kp*g1fsrC-3*4o0+-k>aOZl ztE#)|nJ7hh31kF(1ONblEF~$b3;;lo{rfhBgZcL)8{>2N_kwUymJkNi%@7^~0D=H1 zQ6W`Nh;uzyLp8BK`8xVCr0@`>l4u~+Y|!j*G;$dQW?T;`9K^6%X=L?>lO7$drlrzZ zS7&i*D4B7Tn+B<>kh+#&r>n9-$NuNcnumdue)6Nfu*;?3MjnsfOy2XsR2B{Po#mVy2Zt4)d2qcK7wI2N6H6%v#Df+gE=Rutf`Z&t%a$7C{Vx^Q1!C zjn;l6_0>;N2(I*r{J|Ne$5R5TnBv7Wcj#iV6fFh9|G=KlNaq9+p;sx?daK)?o>R!hD%NR%~X0WP3eDWGsJ>Eu@Hua(9o2i zgmkKD`IlXn!@`RxEGDb67mKA{I?aFZJ!JQ_{Oa|8bkI?N0X`7ruY$}@=@8p^z%!vS z=*jcwVoIjj!w8YmaPn~vCAvAYcGX=&#gGhRO}C4cuC<~@ zc@jk4SLY={jEsLAZM!+2}CT@LTM;^cr--6>F!r z-r)vsF&L#_KJb{!s z>oXKm?le^njJKXJRNQ$s4BbY*d8Je~-@Pwxb$03PLL+;cs94>W!ArtdzAHUR=O@dMlW}TGBgql z-w91af_G$P;v|qt&7_u!BtttKK0~2u=D}zu_!VnyGQv@koI{*RT@UH=WBTa;h?n@6t@%z93IvE*vbSYBXf>Pv=ltMolx5 zGrQ6|x%`4Bs?O zyCb?i=_XhW#D;0gBJnCy&Hgz&Z0Wz*_7?9qO@%g5<*CCg(YK=C3Qf9Qh#_&d>^+}5 zD7_()EoJwetX^OqV4T13xO|~(Y)T%C#jV?G83E9eztNKJZ8bQZ_i)3J6Z&*>M+;#tSqFgo8!+THyi;`3XVxVIW{R zm$Qj~;E)`J*vI4+k~?v!fL5spB`mI;8@F=_CA??PtA=&$yJ1I=KFwuXZo&5rK@*d+ z5i^oF(zP}c67BK{ikh-gF-+EHHmxr*6~SsTR$(|0hmcuz4Xz|)8~4QqiXGC34W-&l zw+_mPg9DJmh;cJf`{98e|BAWEU}RK_^S|{{L+`+lg<0i1-;Mo(cEYAKl*o%LBc&*U zEqAPnF7p}luTz9=n$Dnd1IDrv%91C<#%3R2<> zagqHrXS>TH!F8B6C=7WwQyVI6 zi7J!Wlbp=f3@Yhq{J0vxumoAp2B45={tX@=JBCX78gUnh#0wwJq8MI`$1l$PhGXO< zJR-Bjfjd(WjhE@7!@g(aLD>|2qr9inU*Q=?gqZ|ir|F<$w_b|K0`l}7~On~jOfq*wEiDA!9$-wiOMeet2s`Q2Ep_PtqOc2JGa~k zulkozRG#1+2&-_DyL#sK>GYvv$AzGs3&}{^4P`vaBoOUXFd+cQ&@Fde#ZQrnr74>A zzaivWA}qj1cB8B+%DInN=!w^lywON8avL60_ak@;GIQCf{0b8K%$s~ z0)%!z2eof$N0laFGc(HgZ2QNlL>mbvNfTDq2a8yCer0`ss{aSZOrRT(Y_avyL-yBN z87p*fzPz$Dw?IfFWORWHLLgF51hL?N;Fv2CsVD^q;rc-b(o%I1k)9&AT(0SRTsBkA z{IGNa;m`+LfkJAi9~!x4JT~-Kgj~k|7Mwd=VP;vCE#Gx4ML(FZAE}9j!8%oe2Y2aT zF~M&RcZ+w0swrE5pARLj2rVh+FcPT}B3y=Oz0PMHY2>B#A2$K16{$$jMQiKl)7D71 zYS9eJVTpHem|v;1loZA*4`2qMTyeeDq%q+G75kKF1Vc4aSghA#0q_x#a2XPCW?cU# zg|5+ zqm#l-X(jgKALI*({u5w#i(L>dgU|)L&lgGtlYC`xJL;=k1gVQc*x|EWGO@)OWZN|FL zNa_rHD|%!W>S#{z<^@OxhVxgF5zcaK;&JX946jU_a41`|S*)p1W!=J@YLEr=q1*s!gBbX+PXW1d167Ov#GJh(3ahC*KN0LLEuDk*H)7KZ7c4 z3Zb)JEPb|GH%YJ?Zuk!xy)+;e5Epv#%g%3++r*-V8z{k0T`tC~4a!PK)Ip{#%!bUM zcZKY9wL=XW%h-Bf?{JX?U8(U9<5+9x|FE@%tN1TR9Kf54Xq9x)hf$$Gk#f3s} zLDQe;j@8!NYKR^v-HSf0aRh=3$)TkS;QRdcw8H;fmq`T>Au?*t{`qvgf^w+W17O}{ zt+90j9zrs*ge}5&hAND|+4B+zv_mmc8p6K}j4vkzvChQ@Q|OM836q*SthN7$)l7XyJ#>1lKUrpe>4Ov7pZxZu1Em zIS)i@;QnJHLL7vlh5hE{=CUU93|`uposvxcsM0c9Y7LVxL~QLtG88BYJE(HC|2U=7 zpQK7>nh80D30cKg0wRF{BC7mXfPae{t;u?%^>7=tqbv=G4W!5A>>f?0OX_W^FRaFf0pOIDr9afl#e%?NzEuba^TKV0eDTB!X^*quQ=KQu8#3JQw!nE@fy@x zp+Y3AO1@`Sd1{GijmNOF9nS^x%HV>(9M>5B%e6S6fgnXask$DAKUvu#Z-U8Ze5zO^ zdj-K^z{G7B^&!KUE0PB|9Q-O;6~|Lr6o4^`270;vcC}&9RL7Sqbv-4rP7IvT=1M2d z4}bT&zWk6ftl&6jewAAX|NTK}kBULGN|iB?UT(^7W>ddl2KBz-8p{gB9+7>M|? zd)pxB>CIAZNI?ap3JthRrAV?Ov-cbJvv8fP0vnocyx|At1%62+tdn6eRT$$q!X1Y( zbjiqQAb<6^&M{M4>@psFuH|SfO9JZH|1XNvU{+#BHjV6?jiZcy6%=AwTR?*%1;9q& z6{6~Cj|EYMV(c)6c_4^pucp%Dw5*$A zYTvwItFdDfN4MX3G>?n?-hg7i&CuN*_)&Oo9fEnZr0mls|AXxWX5^SNl0WsOe9rm< zJ!?fyl3`~R#IM1i(0sDb5~~a&RJV{>{0h$BcneSltZuhzTlz0e#@!giSoV_0g>uc| zXT1dUM!WVGgWRol6TZDw&ng=dC}&0z{qRCkXvZ?8{0%xdcVD(?v>pDZBmySf=1$S+ zfa2pZYE1CESD#dHU6#fWELZxhG#W|h$MzS1csO`P96$;-gozUtz@Y%}c1yyLm{s&N zi?2WlTWtjsb?-C(X%pja9!Ec2spDF0F(F>{Xiue+Q{G73oD1<`1g}n?-_a*C>Z%=L znoa4CA&`?0inaV-s`3pQD7auTbyrFhcB&8XV8UCAEZsf5v4r$)9IK2Y=~2JF8)rTV646Z{A_%FqKEA z>%X6ic(6weB}EL~B@)7L_BgCvMn5p>`QN!iS}{;zx{mN!Ryum}qmxAD(fOSOE(W=G zAQ2)7E>eBq$r`t3VZaqP5bBAv1G(H5z1#x5rOwJ4+gft=tlxl49;?Gdkl|0y?s3t^ z+Qvx>>%&%UM&m)8iWZaL(beTP!2u2Gk(xbsP4uRElr7i)pHuKc;9?P_iM|)!eixX{ zkG2d-#2xX>b}%#q2))5>tR0|Y!)+(vd=%DVw}jTy<~yQxr(qEr;pDBR>&;b*U9MYPhe~FBM9v)eRg;q-&IBBz%R_Rko%ZMSERB5m*C2r)e^zQwCA(# zSXlc>&S_>J5j2VKeG&B2P9$S9A=vsAElVr9V7-z=aIj0PX$Jk3*E%muP~v!d<`sH7 zOtwznq9bZAz4;KlD5FWJh5T0dByJYqBEm(DbOXKIUZ7@UsQ3I&zTQHKNu!q&(FoxsCBiJ)rF(L_ClDsq_{qN-z4$fPJur?NO@Rt|BLzGmSF6 zfFAATU*jpG?l%I5uqxVG-5%2y2e!0efZXH6Qe9c7u^UJ^Zh)E(nTS zE3g&?UzQ2+va57s#j^9W75^k2%WD)KX)3#`MqU(QY;Xo9-B?hb7SL2(nS8hmeqz93 z^fNo}*{%kK4+sjilok2S8Ow$#-1s#;;aQv9+l2E!nKK9}@)8RKJa%d-6Y*}>x-+YI zTXLVq_ZrP-p2#WdDfGO)otrh5V&JP|Q_P9Y6G$~Nfzs$;o&TaRLO0nslEBRrK=HD+ zGWUmbk}cB^DkOb{&;is;O+68@1|}`(5e+zz;xcn8E6?bAHV6K-L+7cN@nJy-xL9j0 z;CtB7v0bg(+)e<0kcR(WP~BY8e=V9OopEb>m?B(9?PEc>zl2Pb-@a$Bg7FKT7{@?9 zQaFj~T$_Y)^ASJbL+s9e`28}>LOxluR)fdcr6F`Ho{GO*{muCK(xA^8LdonV-}Ews zs@EU7_W^ed{8ISNwnsN$`6x~hq);iO(?5=BQRsIk4B4RvN(&L8njY8anOF!@k*GyQ zwqAKGpODuw`L(sthf?A zK`L0&u$d>K_$O;AG!DP#?(1#aOiuV;enmhFo^>HFlKYoJ)Y8c&lT6be0qmwbxr9)R z_)7atCsQ#H=`dQ>=0r-+mr4odvj*iqhRkO@8o7G%s8>6eU5|-*t z0uyg3FTz(!uqZ1Y*x~p=cyPyL8+uRPmk*$FSqTDS-MWT{gzFS4`RbYqCn7W$U zcF)%v9fDIWCq6Nz`}nQ}d4ca&0xM|>u&H=?sPd%Y>8FoGoC8D!A83<3964nb6irn6 z{t30K?P*+u+qobya>GH1jZ>^17m?(s)sf*J4sr1Y;x`W6bmDzb6^ zl(7yTq|*m8Z%L`Htu4*16ZjA8#?t%Z69dk$T9aNoOIvG#U+ zi%}(8@9ac(^MY{bQYm2oG+-XF6}>JhAQWrujZFm)>ulR68{>pw2Ud@ClCfq$`^~Lc zNPuezdl5i*5FljHgXrL+#8Cg1T!p-Xk1S(rlP2*alXi5j4r2IJIf|Q2Iq`X3S%dbA z#3ea5wj^3j3SdcX*F&?_qD+!r$xNHU1vdtte3`<&ZR~y*uELrR zz|a$oEP$vR8Kl@a@s&!Tp3CNn-+r|omPI-a`$nO~po5rkacw_oW9w9I23*x8ZETE2 zbaCzoL=-?5l$T83yFh2N@PPH)EU*o)El@!S{d-bpU)nt|kvPy(pbrq%?+pnv2&De) ze}KVPtxcFNjY=#p!eUs{a;(oqs$^eO`5D%;^J21cgn9CZNXGab*Pi zb5o$s?j1uUG@t{?)kQUw3~U%@MHdwsN4DXSq?t+ z0IzH_J=Jm4j!S@ZyY1o808IxPPdWWcv(t?@kck3@YVWmQrTemTn1$+fU%iA38(dKw ztUJ4IG{xDQ7An1%dE)J@>6lDxq<>aWH`k4;0t$?W+nC>qM~bJm!5ik- zR9;>nUY}R)oP?8A7kc0B{Ig2s)3*b&Z>v`+fE;?-H@d67|k z#QQGIp#MF5FGTT0ulDalPrX?u!~jlV z+W08e~^zVpg9`e{O=vk7H)a(^za`@P&H#bC_f z$2U*#90ivfUGY{;QVyZx+Xis^M$ zKLAs;wUGMyN}S!JuF)D~<~Lf+KP9Q8UL2sIVRm7L_9o>4$I6u zYO4(WPA;YKvj#rZ*FA!u!U}>yWTkSBed=q%YqP?|U1@k(TeYiDH-5-X))7pUcUWOX zv--+xl*1M^;X??$C%^U;Qm+x)LEH5uMS4^|JyJC-0LJ?1ON5>e2Vg8!DCLG8KrjdI zCpdg2rm-eW>kcp+31k@Z$-U4u_*faUq1wwjebPjDto?Wz&rMqr`isF*)HXz;sHJi2Md_am}ao%48IV~DGQSPluuaf=;FVa&l3Eqi|T>`i?TYgT~B5UO+ z5Q;kljph@>U7`m1A-X-62~So{s-b29waC!R)aTe?7VBZ=Qd1Wi)C@m4y&fatdNzqk1 zK0AEOXXdN(U_XVn0BN!P#p_;>s#sU=iev?^eyP@v@)|faYQODM&Thgee$Hb$MC~-D zY@$&|QEp|<@FZ@AY!$6udtNbWDej3sEAill7M*VA=Z`#prL&lrc++C~CTfa=@6}s4 znGkHeAe2OQBIc}I0!bp!A6CUC{=On3`;t=l-DB%5leo(w-v6KXPenrdO5Nm>TVZ|k z5Q=J~!%D%B!MwQOc-Vi;=yl%oA3R_Fv=;Q3FdG|M3Xis5B@h0Zi#SWQ3kz%Ah6%jP zcFwEf`T!O^eHG(+6YfXcMNL$wq)GEVpNmh2%U%`)^@A)oibt@fU&yAE0%z=+&Zg#Y z)yJsOH3msRz;DPYV^IGp92m^58<|VhazSoi`kcNF(T7Ot4({H7B!NvP_JtZ@{GSxd zppA4VG}xO5J0-ndVj5oEQ*)r0Xi7Ov#(LVAP!xg;Y{pzFG6^2YJ^~^9c{GmQCuGH- zW+uSIqeaOYHKTg_cAA9;&OkkkKWTpZsZis9FV8x`fPs|NH!Sqi8-F^mX>}D8oi9e5 zKQSOc_7`GVR0QS9GorK(PhCIXoptsTWifExnojI7$m2fUwoXBG?tudADdBo}Op3aZ zJslp~5+@WoLU(OT-PN78ZO_`O;@$=>$xC1myb^=o@nkB5j;%{19u^~)bf#FgtL=k^ z@Gaz}`XvZyE8(_d*Iz(YkKsUo+XBJ?)1jRoQiken_-!3IA@vbT_G-)2EtAt(6!+N{ zkX!&%#VI@Ic2I(<0v{w-GYVlv_SudkpH()oN^j@z4lIxe-=Un`(wa$7Ai&ocv{ZP~ z7*=lXUnsueGW*M4PHZO(^E9a9_;teBTQJYWeS$Vid0d?XOT>SYz4KQwWGp2|BTqzv zA-=Q!{Yd=Ek-O?a*iuP{7dk53DX(!BNc}|KwHZhqx^Y04U-SSv1XZI&5Ymn!!(+}# zTd5G@XS&*bi>Qc%C~-ofv5ZW+OZeBhFbhp~9X{7CMH=0bjy!Ef?KT&QBrR%F*(mIP ztKYI+f*gVNMy!eaSN=uW^k{IvAL{_uz|=H8KUk^f^7(y)J(9ggb8uT^qEpWsg@hHo zl$it~FFZP!`%>(19eNTXbT=1{k4rHq9T5FBhyF88>`2m_Yi7E;D}$Q*FnEIxC>VYx zcQvdUGqb7}<6SSPzj~a5>;F`kjiNkel6P@O<7f|rW%+AeP#G8~hY*J!bq21SKq|i! zw+sAD!5F9~=I36FK742Yp@fTK+JYsFZg8}1|K@3DxZt;yAEDG|kz_$T%w{RNsl=ec zwofp9=!bJNX$6ApjuUcXx>+|#59wt&*%ibirjg=*os;zP5_{~D^nes3HcF!12|U0N z4BNZ4A8-hl&^_sd(w%XbC=QMRo=Y zJh-*KK6aNy)(B|U_`4!WPEjg?=h1zCM^dD`!QGHR&$AtUV^pgs(ffYgzw{}QSH&*k zG%NK{`z52c@|$hVM|H;3bM0uK(qnNzcMOdQ0vr1a#U7OfFw<387YM33x>eX$K1`hv#WTVD|(8W%w zSwUf23Z^@Y_%Hz)ebx?orO63nvBgf-r}j9)W~HvjnEA>?@D&j^U5hstl^_XXy5`>y zZr?(hfq(Uj^93;-Ey(d&Pc@5TbCX&(wGJ>bErAgM#<#>xzb%vo*a^uJXa1tgkIQFP zc3=IWeu(EHd)Po`=xn%eZc-~G8L!f+8AlB=#^j6VT|g4}j`-Oy#bp3J(qi#d29n3_ zx>)QqoZM#1;~2hMHT75m279p6RtscA;P=S%!<3;PW@3Lkm{l2(8!k2qT`*KXlwFe~ zASf+V^hG5iyNQmsr9REDOaeIOE2z%BiC<=O1{D)JaCLQ5U@N#uSu#RtkZ!%it+jXT zkM?bc!w)a#u$Jhu->MI8!OGx`5VyZ<4zof6$eUa2`U0%zuECIFpL%i{8;vya?^P&S z5$kb_#I<2NDh`mNK8OPq=!a6-ThuN;?@Y}os&0%eduUhv@}0@}Dl4Z{Q*X?c;fv5< z2ELPeU*A4`9IL{Bs#WK@2j_e`E>a4O>FO)rB1W-z6`ob^zoq7kF0w`K$tVay%|=@y zWl_CEIiRo1$t!}-T7Yn~(mms_54Tgzlrtw*DTI?jN#ExJfo2oP{Y^&KKM9Zr9PeI3 z1CiK_3w{Jcq6cB}^P{DEc#}iCu*o}o<#JBs0L|y^;Q=O`=n&22*w#;8;l29HnyBT( z$WP_1RABeQ0T`c00`FT`W_$r$=a`n&ZZxrcX-?gvO|q(O}5! zVant&S#NT1FnXl;2oxW(k~Bi|B)|J%Z_w&V*-&jh&v&|U094r+RXs%qy2#_Gt)8h* zGHf*L)%K*B>~ba$30`FY^l_9}3fuZ$qXQV(R$yC^xe>D7SkHJMm#89jRK+#}^X@jQ zBo+b|-8YFiYfLTw_@mO-x)i>X3j9QOB<1XWJ^Yy&q9tp}VmXk5GfC!y;a3e_j6vnD zY7^_iXqJ4G5J=O=p5mzNrB69|cmR?lvbEvh*02V0>+VdG3Ku(%XBn0j#NhBbUs zqN%zq3aMj}r<>}j`Ln3AQhT`oHYhq<-U-4kw1yRvJ7;@*?)s5^oTK2&H*!-jsr8-a zMKxPt)QtovvoY+bJZcl@d0BZM%q~{|3yTKQL$^dhx|W zc-4#sqFIb8Qvyl%I@T&0b*zD;(k|1yOIXX-;KS2Cp=l% zm;q^)@$yZ5hx8q_?4V-SKMdnK^&NamWc6yQy_&HQ6cn+l0yWg;f7yaSWip76)d)A` zK?L2f=jE}M3R@9qy=du&M3uQe_T&gd9;^dFsMQXS&v!b-5Tw#E>aPvs@06~8kO^76 z^o@Ng_9p6bae98`I4@NxyM3}1ZiWEf(^&nJ`3VompaQ$WDn19aG_0JFJ64?iq>uz+ z&5dd_IFM^(I@x~rZYi7I!6DkEvQqj@$?7Sy3t{Ru48Y}8WnQmtY41@r#-n(1!p z_EAICFumt|RA^d+ArK>f4@oOg0&Q?mapS_R1S?olLi3H!2%RbKG;*>Z#dlPI&UljfqPs=cC@V3wC>~roAt69l%ic#5>Yj@nfKU%{xEK2%uvN+ z9$OttYk$Lewhn#a4jbcB!N0pL)%LF0WXx-r21Rd6#ZZyL6v!brc)22Gmiy%Yc05R4 zTUorkSw!?`>g&5hm$&+n>i81Azw^xEobk9|_(>x5W4T9>LuosO87x5HaW;Obg5+$F z=u`6xa*5KP@pW`Bk$evQ*S1j2KNbB+7zZR9*1KA}^78!3A%s54S~6UP6I$Wp+yfnG z`FFu7LU19X$UDAAX^yp>q(xP%aH3F^tL9q%P;)X~^v`{bdyPnrIj3QiIxtuz4e(u& zNN#omzMB|!GU2v@Shv+uygxc@o>-dJ-Z@D_nMXLshp`+XU}R=j3l|S32tx}MKwE?s z!%dVF$ITZPYndtsfu4fl<|9;Ft&N#r6%z>=)n+Yz_$MPLFNqpqi)%DiVgAcl^NX^@ z7R*#`akEAtNAo%o#<`1~(8dqyO(1Z}68Qx6IOs!|_qk}bH+1Gl1f2A}NN-%$w`*w@ z2eB3p$mvz|CyWZ1I-9c6G+)z~(VNh>U75XmgHOZ2sXOVeBjXfU2k=!Gzj7QR#+7*5@M%JQF3S6_i`4$ ze#Vfsx%}b>ZEhV`hM3rQfJZ-lwN#wq%L)u+vehXiHz^8Tge`FY+SNoAf6tqhsE5qI z6B^yp0am6IMr^&&yp{BW#Kh1a}X3er);rgHNQp7?%h_HK-HbT^rVM>jJmKpFV^Z} zZW0fOMtesA2n-3bOZ{2v0lm6OL)@KFE*Dn8`B6IJjLA?x869s{=O%*W?5kq}Rx*-& zAKXjl^I<@6KXq*6#dD=qeITFF1x#8X3qE?8Z%adlM8AFizVl5$0(7Pka@+|MI8>3G zt1`eb3RH4%e5psC@pI`5UK`8)vw%4Jfv9P3ABwgz^=&`}>kq_3q|Bgv0D#>jF56zs z>i%nCAwKCJcozgnGTN|G&wN#=cZ7yS@YJnDF%8%uyT1TVX@G#?TtPET0}S}GU%dz|R}BXuH+9y{`{TzQ(--yOKd>p_ zgEDrE#JA|vPp}>@e?-Pe_KX~_h)dl*4@*83@f2ek*!8ORR&26dY_S7q6{_jD(XU>4bS65;AJX27kGx-ToI?_^qGZyJ z&zmGcdea$l82VP&KOftu+hE#(f9F(WVRk1YqEB^qoW#YAES=1h@C0zcBALtPrsFV7 z0@yXe50Nc|#IFXjyxa9{t_7h^*={#9zh0+_8#>DE8;V8;H2DUpAArLd)v%2sV}cz! zk)$J5w}?~vqsjkhvr&)6`1&ZyCMgJR(F(G9oE+(ij2ylb|JmFS@v3?!5m#X)D1BzN z20eslG?cA8eECC<3TO_+H6dK zCj6UM|4&-XGZP8dI&7>y5O03f()D}g*K$;56!tlL>&^PjfoYkHV2*m@J1DJ4Ef1h! zuLcf9CrLa9d&_EIFC2=9a78F0-Ur}io0Pz@eV(4S$h0_;Sp>9jyWbbg7%l0ItGNk7O0;-fEK{Np5gV$OT1_JwYE&;4 zMP~NFJBJqkQJ%FUo5+zT=hpUVDqT^Ka5{1Fk-kqG#3qFcmkp4$bL|Ov#{MFsE1`sl?qW+mO`D%zOL=e ztp20de4Yrl&De0~PIe?|K&hs1G5zNIDKTE8Vu6^7vp<;kx|OPd%idk{eijs0uK8U^ z-5WVZgz0J%cKeRYE~mK*MlVo@t7@8?x9a+!ZF2$*Jc}( z{)hUG`{udDZL@v-9R2v#uO*E^_P9>-C;uAdDuCbqO(E>^ zIeV#BhR7@r->5*%a-5D``;0L8hYR^|5(w(ND%}sbLb?Cmx z>LRHZ>od@1K3lt4EiUr;{651hTcG+?Ftg9^CWmrOu*F!n1J*Q=tT@KAzfZ*cQLk;^ zLBz~^oC1fZRh7PYAXDVWj%c~5c|T%nF*5JFd(QAQfGMkhUq;#JQ~xvd%WnQFr<}-8 z7is|Kvm$YVzzLxn`yuia-VU1Xrq_C##;c-5Eo?~?{z0qo^4gzjy0ZI3S5+y`?hOOb zRV5lOv9N_*7u6XTN7Wb_@O$ShWL__mB~ky6WDHdDw$r~1R2y$cv5_ATv*aM07C;8@ zF)qj$kj6-D_HdDRUZooC?KPdt*i}D47^4|%f}-cINgVZZr-yQ;>M+nDL&82YSk8Cx zq62ENL9qmec5zfS(*2cfz|g#cT6N?GXKS!QdFD zda|%`a2z;$B0}f^s}g60$H@fUy)EX41jY|%T<$~Iw^3?(-P$v}?Yx^oGpO%Vlt>9% zdetgFAjlcSe0U1>s3>$p1}X`hhaILP$y#FBrhg=yh*u{sZ@OjGE0yq<2l(;6#`&GQ zRyK9pWe_4NX2dis@LL*0pEtfp>ZGPHKzZJrb7>xng72AgZTo&0EJ(ai+2FVA8>AkU zsI##F(V!VJxt$@|1$%)5+utaVHK(PrkBN3{xUb~}kJn~@P@OJ*N7g!>2f?+*a|lsW zQ<`Y)I47$+bK#!5U=LOJn8!y|LuT!hOdGfs!p=Hz`H@6+~Yewrj_d~{4yWV%tdlky7E~fN;!gOI%{xg7Duh*b7d5*IAfA(qUMJIlR0uq z2Cd}vv4d${0?fj-pQWk|=7N5*uTU7JMY`~Zex7bP*qX4&wkULd@RV$hxu*?IqV!ev z(GQO3`8)C#2utmVM$!>Y4g2Y(4NAPq&-3Oou)&7_-KbGoED2w+>K$BKta53$9r3|& z10#~L{qP9s^kc&V=J4iv#blKSDf+{dzXIk*cm_j-;G?8!p$Om^;-bZ`YNoB;2f7Os4=PBkwuG!GBuYGti5M|P1E%_}iEFK8N}2-14|*)V zPkj)jpE=pb{ogU1hR4@!e+#S@c4cH|+!nE)5BcB*Fe|Z2b}YH2$IE#O)q69Q}3mC-pI=%8tYy zItHPV>}p|VAYIGR|Zq=HR?-W3oadL0&x_90L36cdS&)%aMwuQas3kE0gTamh^|gGxHL;grAr zZ8ke<*T|-eg95f=*q5Btde*NzjgFbz|JHceT}z`v61?-ERI)_<;3P%}e7v2V6jWV{@z(^ zC!cxrr#lST*8B(0&jHM5?Pfd0&Smvh#40R?(9}y*lj%tUv4Q$H^ROpYZP3LiJfzy4 zdu+XBc+K|nTr>GkV(#sGjY1t(rF{a`!5s5t0{ut~>&+(kE}t>zDXi^@Bm5@rI(=7j zC-H+2k7{XXcbJtI$kapuPVQtrIAc1gM5>){7BO9O>a16nN^EdJR=ogw*oY^=`66VU z)<@phF))C8++4Q`YVE2@jigqfUjOCbV^^Cgz+5cGVn(dO2cPOet~>>W^AX^fDC$RR ze#%v;0uk&V;e9sBQ!CFtfUp`CSlho0wG!PnZ1m$(8yD!loOtU1>^naCI~fCV|v~inmHzXan6tp^5@aliygEd+#_l@OO+P?nrV~a!m1EaFqEjB?3nt?>ZtU{aShl zR*)YR5tS49aq`F{6M{97b`B(;&QE1#HRTF_C$;g!hE zHR<?3Q|K^oSG%uaq)dH z!`PuPoII5T`RkQTVfY~-#osF-^!Fr;DvLUiQ8HBX zjy+^I_CyDLDD>w}&4G`gNLF7cH&*;=p+g>Ad=*drQwhc}{3No7L_iUlzxEEiMKQ8n z=&=4w$X56WeXjH4tND|_4vT=-o7PfFAH6s!3z2{n!Fk9GyR z>1Jz6v3|r76v4h%9ABul9Z;+^6g%>HencQDi7O4yVQhK%t<>k*-Sb^Y``>$cSNv@J z8VdXHzEFG=yb_b&TB4k!BzwOIztCXLLB%BxbUH=%M-WXfc|f~g-9icbHzcJf4|~0j zh0G_h*N)gxtzwp68eVq{1m=2wqXIk2 z!|ZSUMeA|0B`$9wwuz=f%rOY4$@XkV)YH}4&;oO!K<-k^bO_*Q}ZUMT!IkNELIUh!4N`64}Lf05(uRg>B4 zL$n=>aCn~`*Azj`-!d`7hRt=>7?i~`2EXNz^{F;yFpmO~mK7{()1>V}EYZz&5c9#k zpa_i~`y=dUQaJdOB7@lA<=(lF`wMRv51vJTfVjge)T-LnN8MPc}@ ztRnASLLWjaW&S%O_u#MPA9ui+%Z}1Y!;!dC#2d1UOLVpsVoF=F3f2!?P479Qt1~7L z(z4LN%5=Z$a$9!sE`7TOulDtRJKK>_Dptjp-)@6_1ON=EO5f6lQGwvjPsBK{%wH*y zIo1{*5h%Z4cbj1IW~iI%?4U8lRyame02yR+)5O&X<{!{K?+CKZ8zUsGh^|$Pw@8F4 zMOObd6gBi0Vpie3jbhXBh+2%n=U;>L6=Ih|@0c5lZW8N!87IttwQC{)pz}kP7c_cb z-hJh*Q0lIQ?RPIVC11Z}nQSEO6oJDo!A$(G!~#6Ug_&Nj!ixNgDyff2GwiNM&OoKk zX<_&F%-HI`$G>2BS}{_CW-qB>Y7A8CFTKuEI%H4&8XgVcNca=e&~d2NZGR9{uvj9$ zsw+jlOIthU8w%Wg?(o=`wJ3(46X_MV%-unuCR#QhN96dm`KS&iyon&Aw2*#XADIkk znH~Sk@ob+ux+;ROX<+cZsRP@i!Ja*LV8?ZVz7UgU>u}5r9Q%T!uUacrYGo=m~#^t2g?Czy(0fZIh*K&-FW$mr z{{5gmb|krr}yU9GfI4?uv>v zaRCwKF=S%iEucFc;*EGo>uv+vjM ze)*jxW4fQyHtV_p7p=s!j0@oEBYi6kivkt}P7DR=EK#;6rOon{7HcJCo&oC)A3cBL z*5A_>eY=6RX_f}9>ND6`SiWxSkHWq#+Ndr6Zrwi@7jDemwB%{pS#BAN z0u}}SAQY%G5YZG3w4k$KrsGC}v!P;iE-rN)x?$^{i`V{VN8^TR$wJE}DXeSz=IjEj zs5NVx8nvc%x!Vf>`jsvDx@hx?4gY=Q-+%w>N64rCS*<_F$F!XxikQ#xL#s-f*6->&+V&HL;}_nJ*x z-`%nO@Kf6gr){em(sDT&C}6FcoeW>$)GkAvIil*Szj*4HE%2kiIRhVfu?6^;Zf>tK zsA$91OIB^%(gCcJc=~B6?82_?Sl5m%SqrSXDcHlda^p6(;L9SmcH`C`j+PWZvf{&M zPU!ZC>gs2?TNJP;P=6FSW)YQ@n~g9UxR3nvP{ZsVU&!vO?Q6NSV)f%((57nhI9o;-bI_ zZ7lu9<~C5( zu6gz8eOE1>5&bre6H|06*OP<-ClrW6Y0z)%SjYDCbbUi^E(yOab81mQ=r%B?xkFPE z71^fAicdVGJAd1}^p$G&qMcgRvr21Gz@mUf0gD2EcnbVKnIPx_1iBeL00000NkvXX Hu0mjf^)vdu literal 0 HcmV?d00001 diff --git a/ui/app/actions.js b/ui/app/actions.js index 55773b3b5..0d96d2b59 100644 --- a/ui/app/actions.js +++ b/ui/app/actions.js @@ -1438,7 +1438,6 @@ function pairUpdate (coin) { function shapeShiftSubview (network) { var pair = 'btc_eth' - return (dispatch) => { dispatch(actions.showSubLoadingIndication()) shapeShiftRequest('marketinfo', {pair}, (mktResponse) => { @@ -1464,7 +1463,7 @@ function coinShiftRquest (data, marketData) { dispatch(actions.hideLoadingIndication()) if (response.error) return dispatch(actions.displayWarning(response.error)) var message = ` - Deposit your ${response.depositType} to the address bellow:` + Deposit your ${response.depositType} to the address below:` log.debug(`background.createShapeShiftTx`) background.createShapeShiftTx(response.deposit, response.depositType) dispatch(actions.showQrView(response.deposit, [message].concat(marketData))) @@ -1500,7 +1499,7 @@ function reshowQrCode (data, coin) { if (mktResponse.error) return dispatch(actions.displayWarning(mktResponse.error)) var message = [ - `Deposit your ${coin} to the address bellow:`, + `Deposit your ${coin} to the address below:`, `Deposit Limit: ${mktResponse.limit}`, `Deposit Minimum:${mktResponse.minimum}`, ] diff --git a/ui/app/components/modals/buy-options-modal.js b/ui/app/components/modals/buy-options-modal.js index d735983f9..74a7a847e 100644 --- a/ui/app/components/modals/buy-options-modal.js +++ b/ui/app/components/modals/buy-options-modal.js @@ -69,7 +69,7 @@ BuyOptions.prototype.render = function () { // h('div.buy-modal-content-option', {}, [ // h('div.buy-modal-content-option-title', {}, 'Shapeshift'), // h('div.buy-modal-content-option-subtitle', {}, 'Trade any digital asset for any other'), - // ]), + // ]),, this.renderModalContentOption( 'Direct Deposit', diff --git a/ui/app/components/modals/deposit-ether-modal.js b/ui/app/components/modals/deposit-ether-modal.js new file mode 100644 index 000000000..3e6d3fde1 --- /dev/null +++ b/ui/app/components/modals/deposit-ether-modal.js @@ -0,0 +1,182 @@ +const Component = require('react').Component +const h = require('react-hyperscript') +const inherits = require('util').inherits +const connect = require('react-redux').connect +const actions = require('../../actions') +const networkNames = require('../../../../app/scripts/config.js').networkNames +const ShapeshiftForm = require('../shapeshift-form') + +const DIRECT_DEPOSIT_ROW_TITLE = 'Directly Deposit Ether' +const DIRECT_DEPOSIT_ROW_TEXT = `If you already have some Ether, the quickest way to get Ether in +your new wallet by direct deposit.` +const COINBASE_ROW_TITLE = 'Buy on Coinbase' +const COINBASE_ROW_TEXT = `Coinbase is the world’s most popular way to buy and sell bitcoin, +ethereum, and litecoin.` +const SHAPESHIFT_ROW_TITLE = 'Deposit with ShapeShift' +const SHAPESHIFT_ROW_TEXT = `If you own other cryptocurrencies, you can trade and deposit Ether +directly into your MetaMask wallet. No Account Needed.` +const FAUCET_ROW_TITLE = 'Test Faucet' +const facuetRowText = networkName => `Get Ether from a faucet for the ${networkName}` + +function mapStateToProps (state) { + return { + network: state.metamask.network, + address: state.metamask.selectedAddress, + } +} + +function mapDispatchToProps (dispatch) { + return { + toCoinbase: (address) => { + dispatch(actions.buyEth({ network: '1', address, amount: 0 })) + }, + hideModal: () => { + dispatch(actions.hideModal()) + }, + showAccountDetailModal: () => { + dispatch(actions.showModal({ name: 'ACCOUNT_DETAILS' })) + }, + toFaucet: network => dispatch(actions.buyEth({ network })), + } +} + +inherits(DepositEtherModal, Component) +function DepositEtherModal () { + Component.call(this) + + this.state = { + buyingWithShapeshift: false, + } +} + +module.exports = connect(mapStateToProps, mapDispatchToProps)(DepositEtherModal) + +DepositEtherModal.prototype.renderRow = function ({ + logo, + title, + text, + buttonLabel, + onButtonClick, + hide, + className, + hideButton, + hideTitle, + onBackClick, +}) { + if (hide) { + return null + } + + return h('div', { + className: className || 'deposit-ether-modal__buy-row', + }, [ + + h('div.deposit-ether-modal__buy-row__back', { + onClick: onBackClick, + }, [ + + h('i.fa.fa-arrow-left.cursor-pointer'), + + ]), + + h('div.deposit-ether-modal__buy-row__logo', [logo]), + + h('div.deposit-ether-modal__buy-row__description', [ + + !hideTitle && h('div.deposit-ether-modal__buy-row__description__title', [title]), + + h('div.deposit-ether-modal__buy-row__description__text', [text]), + + ]), + + !hideButton && h('div.deposit-ether-modal__buy-row__button', [ + h('button.deposit-ether-modal__deposit-button', { + onClick: onButtonClick, + }, [buttonLabel]), + ]), + + ]) +} + +DepositEtherModal.prototype.render = function () { + const { network, toCoinbase, address, toFaucet } = this.props + const { buyingWithShapeshift } = this.state + + const isTestNetwork = ['3', '4', '42'].find(n => n === network) + const networkName = networkNames[network] + + return h('div.deposit-ether-modal', {}, [ + + h('div.deposit-ether-modal__header', [ + + h('div.deposit-ether-modal__header__title', ['Deposit Ether']), + + h('div.deposit-ether-modal__header__description', [ + 'To interact with decentralized applications using MetaMask, you’ll need Ether in your wallet.', + ]), + + h('div.deposit-ether-modal__header__close', { + onClick: () => { + this.setState({ buyingWithShapeshift: false }) + this.props.hideModal() + }, + }), + + ]), + + h('div.deposit-ether-modal__buy-rows', [ + + this.renderRow({ + logo: h('img.deposit-ether-modal__buy-row__eth-logo', { src: '../../../images/eth_logo.svg' }), + title: DIRECT_DEPOSIT_ROW_TITLE, + text: DIRECT_DEPOSIT_ROW_TEXT, + buttonLabel: 'View Account', + onButtonClick: () => this.goToAccountDetailsModal(), + hide: buyingWithShapeshift, + }), + + this.renderRow({ + logo: h('i.fa.fa-tint.fa-2x'), + title: FAUCET_ROW_TITLE, + text: facuetRowText(networkName), + buttonLabel: 'Get Ether', + onButtonClick: () => toFaucet(network), + hide: !isTestNetwork || buyingWithShapeshift, + }), + + this.renderRow({ + logo: h('img.deposit-ether-modal__buy-row__coinbase-logo', { + src: '../../../images/coinbase logo.png', + }), + title: COINBASE_ROW_TITLE, + text: COINBASE_ROW_TEXT, + buttonLabel: 'Continue to Coinbase', + onButtonClick: () => toCoinbase(address), + hide: isTestNetwork || buyingWithShapeshift, + }), + + this.renderRow({ + logo: h('img.deposit-ether-modal__buy-row__shapeshift-logo', { + src: '../../../images/shapeshift logo.png', + }), + title: SHAPESHIFT_ROW_TITLE, + text: SHAPESHIFT_ROW_TEXT, + buttonLabel: 'Buy with Shapeshift', + onButtonClick: () => this.setState({ buyingWithShapeshift: true }), + hide: isTestNetwork, + hideButton: buyingWithShapeshift, + hideTitle: buyingWithShapeshift, + onBackClick: () => this.setState({ buyingWithShapeshift: false }), + className: buyingWithShapeshift && 'deposit-ether-modal__buy-row__shapeshift-buy', + }), + + buyingWithShapeshift && h(ShapeshiftForm), + + ]), + ]) +} + +DepositEtherModal.prototype.goToAccountDetailsModal = function () { + this.props.hideModal() + this.props.showAccountDetailModal() +} diff --git a/ui/app/components/modals/modal.js b/ui/app/components/modals/modal.js index 2ff6accaa..afb2a2175 100644 --- a/ui/app/components/modals/modal.js +++ b/ui/app/components/modals/modal.js @@ -9,6 +9,7 @@ const isPopupOrNotification = require('../../../../app/scripts/lib/is-popup-or-n // Modal Components const BuyOptions = require('./buy-options-modal') +const DepositEtherModal = require('./deposit-ether-modal') const AccountDetailsModal = require('./account-details-modal') const EditAccountNameModal = require('./edit-account-name-modal') const ExportPrivateKeyModal = require('./export-private-key-modal') @@ -73,6 +74,37 @@ const MODALS = { }, }, + DEPOSIT_ETHER: { + contents: [ + h(DepositEtherModal, {}, []), + ], + mobileModalStyle: { + width: '100%', + height: '100%', + transform: 'none', + left: '0', + right: '0', + margin: '0 auto', + boxShadow: '0 0 7px 0 rgba(0,0,0,0.08)', + top: '0', + display: 'flex', + }, + laptopModalStyle: { + width: '900px', + maxWidth: '900px', + top: 'calc(10% + 10px)', + left: '0', + right: '0', + margin: '0 auto', + boxShadow: '0 0 6px 0 rgba(0,0,0,0.3)', + borderRadius: '8px', + transform: 'none', + }, + contentStyle: { + borderRadius: '8px', + }, + }, + EDIT_ACCOUNT_NAME: { contents: [ h(EditAccountNameModal, {}, []), diff --git a/ui/app/components/shapeshift-form.js b/ui/app/components/shapeshift-form.js index c5993e3d3..2270b8236 100644 --- a/ui/app/components/shapeshift-form.js +++ b/ui/app/components/shapeshift-form.js @@ -1,308 +1,242 @@ -const PersistentForm = require('../../lib/persistent-form') const h = require('react-hyperscript') const inherits = require('util').inherits +const Component = require('react').Component const connect = require('react-redux').connect -const actions = require('../actions') -const Qr = require('./qr-code') -const isValidAddress = require('../util').isValidAddress -module.exports = connect(mapStateToProps)(ShapeshiftForm) +const classnames = require('classnames') +const { qrcode } = require('qrcode-npm') +const { shapeShiftSubview, pairUpdate, buyWithShapeShift } = require('../actions') +const { isValidAddress } = require('../util') +const SimpleDropdown = require('./dropdowns/simple-dropdown') function mapStateToProps (state) { + const { + coinOptions, + tokenExchangeRates, + selectedAddress, + } = state.metamask + return { - warning: state.appState.warning, - isSubLoading: state.appState.isSubLoading, - qrRequested: state.appState.qrRequested, + coinOptions, + tokenExchangeRates, + selectedAddress, } } -inherits(ShapeshiftForm, PersistentForm) +function mapDispatchToProps (dispatch) { + return { + shapeShiftSubview: () => dispatch(shapeShiftSubview()), + pairUpdate: coin => dispatch(pairUpdate(coin)), + buyWithShapeShift: data => dispatch(buyWithShapeShift(data)), + } +} +module.exports = connect(mapStateToProps, mapDispatchToProps)(ShapeshiftForm) + +inherits(ShapeshiftForm, Component) function ShapeshiftForm () { - PersistentForm.call(this) - this.persistentFormParentId = 'shapeshift-buy-form' + Component.call(this) + + this.state = { + depositCoin: 'btc', + refundAddress: '', + showQrCode: false, + depositAddress: '', + errorMessage: '', + isLoading: false, + bought: false, + } } -ShapeshiftForm.prototype.render = function () { - return this.props.qrRequested ? h(Qr, {key: 'qr'}) : this.renderMain() +ShapeshiftForm.prototype.componentWillMount = function () { + this.props.shapeShiftSubview() } -ShapeshiftForm.prototype.renderMain = function () { - const marketinfo = this.props.buyView.formView.marketinfo - const coinOptions = this.props.buyView.formView.coinOptions - var coin = marketinfo.pair.split('_')[0].toUpperCase() - - return h('.flex-column', { - style: { - position: 'relative', - padding: '25px', - paddingTop: '5px', - width: '90%', - minHeight: '215px', - alignItems: 'center', - overflowY: 'auto', - }, - }, [ - h('.flex-row', { - style: { - justifyContent: 'center', - alignItems: 'baseline', - height: '42px', - }, - }, [ - h('img', { - src: coinOptions[coin].image, - width: '25px', - height: '25px', - style: { - marginRight: '5px', - }, - }), - - h('.input-container', { - position: 'relative', - }, [ - h('input#fromCoin.buy-inputs.ex-coins', { - type: 'text', - list: 'coinList', - autoFocus: true, - dataset: { - persistentFormId: 'input-coin', - }, - style: { - boxSizing: 'border-box', - }, - onChange: this.handleLiveInput.bind(this), - defaultValue: 'BTC', - }), - - this.renderCoinList(), - - h('i.fa.fa-pencil-square-o.edit-text', { - style: { - fontSize: '12px', - color: '#F7861C', - position: 'absolute', - }, - }), - ]), - - h('.icon-control', { - style: { - position: 'relative', - }, - }, [ - // Not visible on the screen, can't see it on master. - - // h('i.fa.fa-refresh.fa-4.orange', { - // style: { - // bottom: '5px', - // left: '5px', - // color: '#F7861C', - // }, - // onClick: this.updateCoin.bind(this), - // }), - h('i.fa.fa-chevron-right.fa-4.orange', { - style: { - position: 'absolute', - bottom: '35%', - left: '0%', - color: '#F7861C', - }, - onClick: this.updateCoin.bind(this), - }), - ]), - - h('#toCoin.ex-coins', marketinfo.pair.split('_')[1].toUpperCase()), - - h('img', { - src: coinOptions[marketinfo.pair.split('_')[1].toUpperCase()].image, - width: '25px', - height: '25px', - style: { - marginLeft: '5px', - }, - }), - ]), - - h('.flex-column', { - style: { - marginTop: '1%', - alignItems: 'flex-start', - }, - }, [ - this.props.warning ? - this.props.warning && - h('span.error.flex-center', { - style: { - textAlign: 'center', - width: '229px', - height: '82px', - }, - }, this.props.warning) - : this.renderInfo(), - - this.renderRefundAddressForCoin(coin), - ]), - - ]) +ShapeshiftForm.prototype.onCoinChange = function (e) { + const coin = e.target.value + this.setState({ + depositCoin: coin, + errorMessage: '', + }) + this.props.pairUpdate(coin) } -ShapeshiftForm.prototype.renderRefundAddressForCoin = function (coin) { - return h(this.activeToggle('.input-container'), { - style: { - marginTop: '1%', - }, - }, [ +ShapeshiftForm.prototype.onBuyWithShapeShift = function () { + this.setState({ + isLoading: true, + showQrCode: true, + }) - h('div', `${coin} Address:`), - - h('input#fromCoinAddress.buy-inputs', { - type: 'text', - placeholder: `Your ${coin} Refund Address`, - dataset: { - persistentFormId: 'refund-address', - - }, - style: { - boxSizing: 'border-box', - width: '227px', - height: '30px', - padding: ' 5px ', - }, - }), - - h('i.fa.fa-pencil-square-o.edit-text', { - style: { - fontSize: '12px', - color: '#F7861C', - position: 'absolute', - }, - }), - h('div.flex-row', { - style: { - justifyContent: 'flex-start', - }, - }, [ - h('button', { - onClick: this.shift.bind(this), - style: { - marginTop: '1%', - }, - }, - 'Submit'), - ]), - ]) -} - -ShapeshiftForm.prototype.shift = function () { - var props = this.props - var withdrawal = this.props.buyView.buyAddress - var returnAddress = document.getElementById('fromCoinAddress').value - var pair = this.props.buyView.formView.marketinfo.pair - var data = { - 'withdrawal': withdrawal, - 'pair': pair, - 'returnAddress': returnAddress, + const { + buyWithShapeShift, + selectedAddress: withdrawal, + } = this.props + const { + refundAddress: returnAddress, + depositCoin, + } = this.state + const pair = `${depositCoin}_eth` + const data = { + withdrawal, + pair, + returnAddress, // Public api key 'apiKey': '803d1f5df2ed1b1476e4b9e6bcd089e34d8874595dda6a23b67d93c56ea9cc2445e98a6748b219b2b6ad654d9f075f1f1db139abfa93158c04e825db122c14b6', } - var message = [ - `Deposit Limit: ${props.buyView.formView.marketinfo.limit}`, - `Deposit Minimum:${props.buyView.formView.marketinfo.minimum}`, - ] + if (isValidAddress(withdrawal)) { - this.props.dispatch(actions.coinShiftRquest(data, message)) + buyWithShapeShift(data) + .then(d => this.setState({ + showQrCode: true, + depositAddress: d.deposit, + isLoading: false, + })) + .catch(() => this.setState({ + showQrCode: false, + errorMessage: 'Invalid Request', + isLoading: false, + })) } } -ShapeshiftForm.prototype.renderCoinList = function () { - var list = Object.keys(this.props.buyView.formView.coinOptions).map((item) => { - return h('option', { - value: item, - }, item) - }) +ShapeshiftForm.prototype.renderMetadata = function (label, value) { + return h('div', {className: 'shapeshift-form__metadata-wrapper'}, [ - return h('datalist#coinList', { - onClick: (event) => { - event.preventDefault() - }, - }, list) -} + h('div.shapeshift-form__metadata-label', {}, [ + h('span', `${label}:`), + ]), -ShapeshiftForm.prototype.updateCoin = function (event) { - event.preventDefault() - const props = this.props - var coinOptions = this.props.buyView.formView.coinOptions - var coin = document.getElementById('fromCoin').value + h('div.shapeshift-form__metadata-value', {}, [ + h('span', value), + ]), - if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') { - var message = 'Not a valid coin' - return props.dispatch(actions.displayWarning(message)) - } else { - return props.dispatch(actions.pairUpdate(coin)) - } -} - -ShapeshiftForm.prototype.handleLiveInput = function () { - const props = this.props - var coinOptions = this.props.buyView.formView.coinOptions - var coin = document.getElementById('fromCoin').value - - if (!coinOptions[coin.toUpperCase()] || coin.toUpperCase() === 'ETH') { - return null - } else { - return props.dispatch(actions.pairUpdate(coin)) - } -} - -ShapeshiftForm.prototype.renderInfo = function () { - const marketinfo = this.props.buyView.formView.marketinfo - const coinOptions = this.props.buyView.formView.coinOptions - var coin = marketinfo.pair.split('_')[0].toUpperCase() - - return h('span', { - style: { - }, - }, [ - h('h3.flex-row.text-transform-uppercase', { - style: { - color: '#868686', - paddingTop: '4px', - justifyContent: 'space-around', - textAlign: 'center', - fontSize: '17px', - }, - }, `Market Info for ${marketinfo.pair.replace('_', ' to ').toUpperCase()}:`), - h('.marketinfo', ['Status : ', `${coinOptions[coin].status}`]), - h('.marketinfo', ['Exchange Rate: ', `${marketinfo.rate}`]), - h('.marketinfo', ['Limit: ', `${marketinfo.limit}`]), - h('.marketinfo', ['Minimum : ', `${marketinfo.minimum}`]), ]) } -ShapeshiftForm.prototype.activeToggle = function (elementType) { - if (!this.props.buyView.formView.response || this.props.warning) return elementType - return `${elementType}.inactive` -} +ShapeshiftForm.prototype.renderMarketInfo = function () { + const { depositCoin } = this.state + const coinPair = `${depositCoin}_eth` + const { tokenExchangeRates } = this.props + const { + limit, + rate, + minimum, + } = tokenExchangeRates[coinPair] || {} + + return h('div.shapeshift-form__metadata', {}, [ + + this.renderMetadata('Status', limit ? 'Available' : 'Unavailable'), + this.renderMetadata('Limit', limit), + this.renderMetadata('Exchange Rate', rate), + this.renderMetadata('Minimum', minimum), -ShapeshiftForm.prototype.renderLoading = function () { - return h('span', { - style: { - position: 'absolute', - left: '70px', - bottom: '194px', - background: 'transparent', - width: '229px', - height: '82px', - display: 'flex', - justifyContent: 'center', - }, - }, [ - h('img', { - style: { - width: '60px', - }, - src: 'images/loading.svg', - }), ]) } + +ShapeshiftForm.prototype.renderQrCode = function () { + const { depositAddress, isLoading } = this.state + const qrImage = qrcode(4, 'M') + qrImage.addData(depositAddress) + qrImage.make() + + return h('div.shapeshift-form', {}, [ + + h('div.shapeshift-form__deposit-instruction', [ + 'Deposit your BTC to the address below:', + ]), + + h('div', depositAddress), + + h('div.shapeshift-form__qr-code', [ + isLoading + ? h('img', { + src: 'images/loading.svg', + style: { width: '60px'}, + }) + : h('div', { + dangerouslySetInnerHTML: { __html: qrImage.createTableTag(4) }, + }), + ]), + + this.renderMarketInfo(), + + ]) +} + + +ShapeshiftForm.prototype.render = function () { + const { coinOptions, btnClass } = this.props + const { depositCoin, errorMessage, showQrCode, depositAddress } = this.state + const coinPair = `${depositCoin}_eth` + const { tokenExchangeRates } = this.props + const token = tokenExchangeRates[coinPair] + + return h('div.shapeshift-form-wrapper', [ + showQrCode + ? this.renderQrCode() + : h('div.shapeshift-form', [ + h('div.shapeshift-form__selectors', [ + + h('div.shapeshift-form__selector', [ + + h('div.shapeshift-form__selector-label', 'Deposit'), + + h(SimpleDropdown, { + selectedOption: this.state.depositCoin, + onSelect: this.onCoinChange, + options: Object.entries(coinOptions).map(([coin]) => ({ + value: coin.toLowerCase(), + displayValue: coin, + })), + }), + + ]), + + h('div.icon.shapeshift-form__caret', { + style: { backgroundImage: 'url(images/caret-right.svg)'}, + }), + + h('div.shapeshift-form__selector', [ + + h('div.shapeshift-form__selector-label', [ + 'Receive', + ]), + + h('div.shapeshift-form__selector-input', ['ETH']), + + ]), + + ]), + + h('div', { + className: classnames('shapeshift-form__address-input-wrapper', { + 'shapeshift-form__address-input-wrapper--error': errorMessage, + }), + }, [ + + h('div.shapeshift-form__address-input-label', [ + 'Your Refund Address', + ]), + + h('input.shapeshift-form__address-input', { + type: 'text', + onChange: e => this.setState({ + refundAddress: e.target.value, + errorMessage: '', + }), + }), + + h('divshapeshift-form__address-input-error-message', [errorMessage]), + ]), + + this.renderMarketInfo(), + + ]), + + !depositAddress && h('button.shapeshift-form__shapeshift-buy-btn', { + className: btnClass, + disabled: !token, + onClick: () => this.onBuyWithShapeShift(), + }, ['Buy']), + + ]) +} diff --git a/ui/app/components/shift-list-item.js b/ui/app/components/shift-list-item.js index 43973de63..111a77df4 100644 --- a/ui/app/components/shift-list-item.js +++ b/ui/app/components/shift-list-item.js @@ -16,6 +16,7 @@ module.exports = connect(mapStateToProps)(ShiftListItem) function mapStateToProps (state) { return { + selectedAddress: state.metamask.selectedAddress, conversionRate: state.metamask.conversionRate, currentCurrency: state.metamask.currentCurrency, } @@ -28,36 +29,39 @@ function ShiftListItem () { } ShiftListItem.prototype.render = function () { + const { selectedAddress, receivingAddress } = this.props return ( - h('div.tx-list-item.tx-list-clickable', { - style: { - paddingTop: '20px', - paddingBottom: '20px', - justifyContent: 'space-around', - alignItems: 'center', - }, - }, [ - h('div', { + selectedAddress === receivingAddress + ? h('div.tx-list-item.tx-list-clickable', { style: { - width: '0px', - position: 'relative', - bottom: '19px', + paddingTop: '20px', + paddingBottom: '20px', + justifyContent: 'space-around', + alignItems: 'center', }, }, [ - h('img', { - src: 'https://info.shapeshift.io/sites/default/files/logo.png', + h('div', { style: { - height: '35px', - width: '132px', - position: 'absolute', - clip: 'rect(0px,23px,34px,0px)', + width: '0px', + position: 'relative', + bottom: '19px', }, - }), - ]), + }, [ + h('img', { + src: 'https://info.shapeshift.io/sites/default/files/logo.png', + style: { + height: '35px', + width: '132px', + position: 'absolute', + clip: 'rect(0px,23px,34px,0px)', + }, + }), + ]), - this.renderInfo(), - this.renderUtilComponents(), - ]) + this.renderInfo(), + this.renderUtilComponents(), + ]) + : null ) } diff --git a/ui/app/components/tx-list.js b/ui/app/components/tx-list.js index 70722f43e..50e328dac 100644 --- a/ui/app/components/tx-list.js +++ b/ui/app/components/tx-list.js @@ -52,7 +52,7 @@ TxList.prototype.render = function () { TxList.prototype.renderTransaction = function () { const { txsToRender, conversionRate } = this.props return txsToRender.length - ? txsToRender.map((transaction, i) => this.renderTransactionListItem(transaction, conversionRate)) + ? txsToRender.map((transaction, i) => this.renderTransactionListItem(transaction, conversionRate, i)) : [h( 'div.tx-list-item.tx-list-item--empty', { key: 'tx-list-none' }, @@ -61,12 +61,16 @@ TxList.prototype.renderTransaction = function () { } // TODO: Consider moving TxListItem into a separate component -TxList.prototype.renderTransactionListItem = function (transaction, conversionRate) { +TxList.prototype.renderTransactionListItem = function (transaction, conversionRate, index) { // console.log({transaction}) // refer to transaction-list.js:line 58 if (transaction.key === 'shapeshift') { - return h(ShiftListItem, transaction) + return h('div', { + key: `shapeshift${index}`, + }, [ + h(ShiftListItem, transaction), + ]) } const props = { diff --git a/ui/app/components/tx-view.js b/ui/app/components/tx-view.js index e42a20c85..949d91f6f 100644 --- a/ui/app/components/tx-view.js +++ b/ui/app/components/tx-view.js @@ -73,7 +73,7 @@ TxView.prototype.renderButtons = function () { textAlign: 'center', }, onClick: () => showModal({ - name: 'BUY', + name: 'DEPOSIT_ETHER', }), }, 'DEPOSIT'), diff --git a/ui/app/css/itcss/components/modal.scss b/ui/app/css/itcss/components/modal.scss index 9b64564d6..2431e2f63 100644 --- a/ui/app/css/itcss/components/modal.scss +++ b/ui/app/css/itcss/components/modal.scss @@ -598,4 +598,256 @@ justify-content: center; font-size: 17px; color: $nile-blue; -} \ No newline at end of file +} + +// Deposit Ether Modal +.deposit-ether-modal { + border-radius: 8px; + font-family: Roboto; + display: flex; + flex-flow: column; + height: 100%; + + &__header { + width: 100%; + border-radius: 8px 8px 0 0; + background-color: $mid-gray; + display: flex; + position: relative; + padding: 25px; + flex-flow: column; + align-items: flex-start; + + &__title { + color: $white; + font-size: 24px; + line-height: 32px; + } + + &__description { + color: $white; + font-size: 16px; + line-height: 22px; + margin-top: 10px; + } + + &__close::after { + content: '\00D7'; + font-size: 2em; + color: $white; + position: absolute; + top: 20.8px; + right: 28px; + cursor: pointer; + } + } + + &__buy-rows { + width: 100%; + padding: 33px; + padding-top: 0px; + display: flex; + flex-flow: column nowrap; + flex: 1; + overflow-y: auto; + + @media screen and (max-width: 575px) { + height: 0; + } + } + + &__buy-row { + border-bottom: 1px solid $alto; + display: flex; + justify-content: space-between; + align-items: center; + flex: 1; + padding-bottom: 25px; + padding-top: 25px; + + @media screen and (max-width: 575px) { + min-height: 360px; + flex-flow: column; + justify-content: center; + padding-top: 45px; + } + + &__back { + position: absolute; + top: 10px; + left: 0px; + } + + &__shapeshift-buy { + padding-top: 25px; + position: relative; + @media screen and (max-width: 575px) { + display: flex; + justify-content: space-between; + align-items: center; + flex: 1; + padding-bottom: 25px; + flex-flow: column; + justify-content: center; + padding-top: 20px; + min-height: 240px; + border: none; + } + } + + &__logo { + display: flex; + justify-content: center; + flex: 0.3 1 auto; + + @media screen and (min-width: 575px) { + min-width: 215px; + } + } + + &__coinbase-logo { + height: 40px; + width: 180px; + } + + &__shapeshift-logo { + height: 60px; + width: 174px; + } + + &__eth-logo { + border-radius: 50%; + width: 68px; + height: 68px; + border: 3px solid $tundora; + z-index: 25; + padding: 4px; + background-color: #fff; + } + + &__right { + display: flex; + } + + &__description { + color: $cape-cod; + flex: 0.5 1 auto; + + @media screen and (min-width: 575px) { + min-width: 315px; + } + + &__title { + font-size: 20px; + line-height: 30px; + } + + &__text { + font-size: 14px; + line-height: 22px; + margin-top: 7px; + } + } + + &__button { + display: flex; + justify-content: flex-end; + + @media screen and (min-width: 575px) { + min-width: 300px; + } + } + } + + &__buy-row:last-of-type { + border-bottom: 0px; + } + + &__deposit-button, .shapeshift-form__shapeshift-buy-btn { + height: 54px; + width: 257px; + border: 1px solid $curious-blue; + border-radius: 4px; + display: flex; + justify-content: center; + font-size: 16px; + color: $curious-blue; + background-color: $white; + } + + .shapeshift-form-wrapper { + display: flex; + flex-flow: column; + justify-content: center; + align-items: center; + margin-top: 28px; + flex: 1 0 auto; + + .shapeshift-form { + width: auto; + + &__caret { + width: auto; + flex: 1; + } + } + } + + .shapeshift-form__shapeshift-buy-btn { + margin-top: 10px; + } + + .simple-dropdown { + color: #5B5D67; + font-size: 16px; + font-weight: 300; + line-height: 21px; + border: 1px solid #D8D8D8; + background-color: #FFFFFF; + text-align: center; + width: 100%; + height: 45px; + line-height: 44px; + font-family: Montserrat Light; + } + + .simple-dropdown__selected { + text-align: center; + } +} + +//Notification Modal + +.notification-modal-wrapper { + display: flex; + flex-direction: column; + justify-content: flex-start; + align-items: center; + position: relative; + border: 1px solid $alto; + box-shadow: 0 0 2px 2px $alto; + font-family: Roboto; +} + +.notification-modal-header { + background: $wild-sand; + width: 100%; + display: flex; + justify-content: center; + padding: 30px; + font-size: 22px; + color: $nile-blue; + height: 79px; +} + +.notification-modal-message { + padding: 20px; +} + +.notification-modal-message { + width: 100%; + display: flex; + justify-content: center; + font-size: 17px; + color: $nile-blue; +} diff --git a/ui/app/css/itcss/settings/variables.scss b/ui/app/css/itcss/settings/variables.scss index 387d14b5f..edc376c17 100644 --- a/ui/app/css/itcss/settings/variables.scss +++ b/ui/app/css/itcss/settings/variables.scss @@ -42,6 +42,8 @@ $malibu-blue: #7ac9fd; $athens-grey: #e9edf0; $jaffa: #f28930; $geyser: #d2d8dd; +$mid-gray: #5b5d67; +$cape-cod: #38393a; /* Z-Indicies diff --git a/ui/app/reducers/app.js b/ui/app/reducers/app.js index 3a4fb536d..e96dea0be 100644 --- a/ui/app/reducers/app.js +++ b/ui/app/reducers/app.js @@ -58,6 +58,7 @@ function reduceApp (state, action) { isLoading: false, // Used to display error text warning: null, + buyView: {}, }, state.appState) switch (action.type) { @@ -591,8 +592,8 @@ function reduceApp (state, action) { marketinfo: action.value.marketinfo, coinOptions: action.value.coinOptions, }, - buyAddress: appState.buyView.buyAddress, - amount: appState.buyView.amount, + buyAddress: action.value.buyAddress || appState.buyView.buyAddress, + amount: appState.buyView.amount || 0, }, }) diff --git a/yarn.lock b/yarn.lock index 3a22dcf98..f9c705843 100644 --- a/yarn.lock +++ b/yarn.lock @@ -508,7 +508,7 @@ async-eventemitter@^0.2.2: dependencies: async "^2.4.0" -"async-eventemitter@github:ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c": +async-eventemitter@ahultgren/async-eventemitter#fa06e39e56786ba541c180061dbf2c0a5bbf951c: version "0.2.3" resolved "https://codeload.github.com/ahultgren/async-eventemitter/tar.gz/fa06e39e56786ba541c180061dbf2c0a5bbf951c" dependencies: @@ -2210,10 +2210,6 @@ clone-stats@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" -clone@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/clone/-/clone-0.2.0.tgz#c6126a90ad4f72dbf5acdb243cc37724fe93fc1f" - clone@^1.0.0, clone@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" @@ -11141,12 +11137,12 @@ vinyl-fs@^3.0.0: vinyl "^2.0.0" vinyl-sourcemap "^1.1.0" -vinyl-source-stream@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vinyl-source-stream/-/vinyl-source-stream-1.1.2.tgz#62b53a135610a896e98ca96bee3a87f008a8e780" +vinyl-source-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/vinyl-source-stream/-/vinyl-source-stream-2.0.0.tgz#f38a5afb9dd1e93b65d550469ac6182ac4f54b8e" dependencies: through2 "^2.0.3" - vinyl "^0.4.3" + vinyl "^2.1.0" vinyl-sourcemap@^1.1.0: version "1.1.0" @@ -11166,13 +11162,6 @@ vinyl-sourcemaps-apply@^0.2.0: dependencies: source-map "^0.5.1" -vinyl@^0.4.3: - version "0.4.6" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.4.6.tgz#2f356c87a550a255461f36bbeb2a5ba8bf784847" - dependencies: - clone "^0.2.0" - clone-stats "^0.0.1" - vinyl@^0.5.0: version "0.5.3" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-0.5.3.tgz#b0455b38fc5e0cf30d4325132e461970c2091cde" @@ -11189,7 +11178,7 @@ vinyl@^1.1.0, vinyl@^1.2.0: clone-stats "^0.0.1" replace-ext "0.0.1" -vinyl@^2.0.0: +vinyl@^2.0.0, vinyl@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.1.0.tgz#021f9c2cf951d6b939943c89eb5ee5add4fd924c" dependencies: