From 078cabd745c031b73ff2a134ef0c5717a0ccec0a Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 9 Feb 2016 12:48:25 +0100 Subject: [PATCH] kivy: store contacts as invoices --- gui/kivy/main_window.py | 2 +- gui/kivy/theming/light/contact_avatar.png | Bin 6305 -> 0 bytes gui/kivy/theming/light/save.png | Bin 0 -> 946 bytes gui/kivy/uix/screens.py | 92 ++++++++-------------- gui/kivy/uix/ui_screens/send.kv | 16 ++-- lib/paymentrequest.py | 11 ++- 6 files changed, 51 insertions(+), 70 deletions(-) delete mode 100644 gui/kivy/theming/light/contact_avatar.png create mode 100644 gui/kivy/theming/light/save.png diff --git a/gui/kivy/main_window.py b/gui/kivy/main_window.py index 7de28877..2d6d4662 100644 --- a/gui/kivy/main_window.py +++ b/gui/kivy/main_window.py @@ -258,8 +258,8 @@ class ElectrumWindow(App): panel.switch_to(tab) def show_request(self, addr): - self.receive_screen.screen.address = addr self.switch_to('receive') + self.receive_screen.screen.address = addr def scan_qr(self, on_complete): if platform != 'android': diff --git a/gui/kivy/theming/light/contact_avatar.png b/gui/kivy/theming/light/contact_avatar.png deleted file mode 100644 index 0a4daa27a69cec1ce43ea035aafca8bb6dea6e25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6305 zcmV;S7+&XzP)WFU8GbZ8()Nlj2>E@cM*02l>HL_t(&-ffy$kX+|= z-+$-3OW$6nyJsI@0B|tCkXR@zq$m{^k&>*{mP&G2DaTHwg1E{_IZBltTuCJ*fNv?; z%3{ZHDS1dzvgFtbRSvC4rbtoZ0+J#LfDnm&n+-FVt-Ggtdb@p>lZWnEK&Pvw>)zY< z&OPURXZ`;_iNmuIAfSNszEKoWKm-*)6;xG_-Zc?a5miM*5Ku)$Ra8(Ex>p5Ml~`j? zgY701EFhM)-z17dO(s%ei~|}H1|2m9r2Em$_X?uY6--pO-w$B-4mRag1nKG_y;~4P zmF^+ipWY>UB7>l!paNY*Kzqsyww(k?5XEh|diypPuiRn!?kbLpJbHMXlZPH4pC83` zbE0vRI1JPnBZvT^x@l;Yp0&3nw?($3QxMr&lCJcdN4?*5hZ5V@Wb;IN8g>7tYOI0? zmg53OXI2;Z$IgUh8M@1vH zDE4Lm_vGyvmiv7FK9B1y zfNVLEZaueiZMHqn0)%#@EzrTjqu7X04e2<7@jT6r`v(X0Pb`*XQ){ zlLzGSM<0=5VUS)>^i17NCYx&5+cL`zZ;?&eRYY|A_FlTV-{H2FXUBS}YKRaDq~yqf z-Aql5viPf6B3d*9Fyf#Io3v|@Y)3kO;T~5TP3c&|@SZ-xSZMp#n4MqK<430C_>ses z$z(JNJKNFFjX~`DTwU)=Iy=POp~jB57Pj-&W=`v-qgceaKnkU;rnRm|_hz{Cz%Eu7 z>eA>$pfNOjXmsdQ8_)>}h%ki=70+fyjV#Vo`Q`L~%RoR9SF-}~VvT!C8+1gmQ_5PUqq-G~o5_l;N?_lgV0WBoFA;YiknLKy zd9$*WhBZb+R7^q;SHP&>T4D9puXN>u^C}x{naCA*cy~s>@Q6!OjWi-yZbn>Ntn1D94520!-TOc7(2*!wrNl9tF#rgB^GylOk;)Pp8z>U1`7jn`PL$T`p18v({G&T*mz!shVnRGLIw&cQt5;^t|f_q6q%u{U|BRad|K5O zt@Sp+Mq86fi;2BCM*BRuIM-xxaRWPPYax}8V6h_C=WpTImOTB;NlAJ`5{6C0C?ZCz zXHTDccGL5Ff3h>Yd%a#)G0PFi$d#+}eDhm>t?ypFE)O0W#UvBVPS3J-?FQC*OUDNC z3=bwLByEYC0XHtM>8oe&a^psszQHU5kM7ZIe_G;=7D3b3POF17S|k!iCWo?w7%r}~ zwbqWvL?Nk8NHb{Bs)ZamG0Eh_ez8nQ9Cg$fgZ1p`Q)h&2Ewme=?9geen29k!f>x`} z*^BSX!g`rceEeaCb7_71^>-xEs`JojQ6fJS2_kXYF_$lu9bZO<4YuuJq9nskDF65QU-E+=Ut`a3ABCjN zt=m<8b*;*4@0Yl^*rIJG1UDhqXKV7_yLWLnnmn|(fH&HQG<{jQw;}ByR@X98ZARQ( z@32s6N@Kkxwk49vcr@#6Szcb%u{{|%c5G5C%OwhZtkd88Cug=h!mZ*hvfV$(rUGId zX}8;QbNYt%7g9X)_$RnKSLV6@{UX13bB@_LpN<%<)B}0#N}02_s+^y%@xgqRVmis! zzjBCEUz#FBc>S#fmhY}}aLA)C?Xp&CQK|X(Dsr#d;qu)E(+f2kYjx~SOu`5fDfN0= zQU!;PoEWFj*Do5!80mF*vYDWIW03Zm-rjhzSzQRCi1k)X(<Dd(oR(+fFf%aCKn* zrmp|&|4@4S93L&@m>A9L^$%9!cdk|9kzz(4-knq0p)RjB^xW%2lT|l!?Z!#aYQ0vYbzkGg_iaW<+M1KQ_Uq+GoF~NCqaz*?Y3rf ziFjnRpx_XNG3G<_X17Mm=2!`6ti-X3WvN(2poqV?q-(Fdqug9ls~NMpv_bXUf?m0} zqPy~G{pw@m`p9UOv2;Q&TwK%N{Q8f1=_gm^^Uq9Dwi}!}{ayVpfApf-ZX$l_<9l>u zD2Z4G5lcfHZ5v0|TODSnODwN8k+h5ETxu#Lyp$e4GN$`RJcTBzy?H|Jx4Y49-R`zf zag10-#AJaaP}1d@Y2JL}uH@|mPkwqYZW5-iu9~paVyckjsgH~>;2Ev2`~2jcW!`;j zhTs0Zztj^Ck4U|#vJuw!#-IFD*V>_c{dW#={OAbHMu+BxO0^YJZus1522{&UY-3PM zv>nB$>qyQ^NZ5(Fa`g_jx)cX?W1T*I>hz9oRkk|Wtwzyc?1TiJCYR5@#k(*4lOimR!@c20Eb5%L}@{E*gzU-Rp=jgF9Ikqn^M}|_SkWX;#c8P13X2h|Q z96vNA%^={%uUs)<5cAp39MsH2Ka024<);@{L)eL= zN+ij=!@+%H^bZVUi+s4<-kPBR#n>s(Sl{~DFXV^+^M_QHYJ_oybE_@xl**j`-Z_#{ zOy^z$6UUq=rm$wpTt1)B-Mg}K;LrfiI1sr?r8=U+xuhI9IINmHEPwV_XXQV9_ciV7 z^EmyDPjIIi@ZOy=>k$~oLQPDnFNt+vh*A_wZFxg|0r4F2*_3#W1H|3QZgXz#_1Hb= zVpErt_{}xF^Q%i*zOyJ%qQJ$)7SokB$-X>G%WeJh@0^pV!7PuS7~!!8`>|Y$(rT48 z-zUl?C=Pj?`oaYHq|18E*HDqk{rw!-`&s?Qw_fDm{n;B_yS0H7v%0(yh?j9mCLLOS zfQAA2;WV9~pXKG6^cT`R{fUP-ad?kx#q*Zmd=|fxp_2`g#i|j?_)N8iRB?AwvaE;!Z4YyuYl!yiw9Ze@^0+4cz6j zIHelt{4R{I+E{P1zUE7=kYcQuqL{QHj=RHIZ@{+|7efFVi-H=<1#zgBZtJ<%&gu() z`>tNP(UHECFDY+BMg~1Sw!cU+>*{nnkn`8eocaC*Ddrq~;wy(F41BZ|5cwUJ=c}^1 zvOyTd8aD!vCYBB9j4Q+aY1YD!v)9VJ@Ww1br$c5StNz-$RNlWU%Lj_uaU2$=*JuaA z#6(dK@9V>}ZCR^!WMDMF>NZW-J?IpO5#tJCbYc1$&wc+#`oaqrWvPs~lEMmbAL zk7hY}peSz27QfWs{LDIwK`8Ywg0&5;H+)>rq1A|FX}(Ic6KE}nnOmxhV~Oq?OCiSa z=5mGCua))n50<3SY)c|#V-MzWlCIjV4ojC;+33qqZ3f~xq7!2&`TSFd$))l{;t)iU zSjJKS+lVi!s$h#T30jR6UU~Udo_qcsxjo;ZknPu@z7)IiNhzj18BDtr3m(fgpQW1> zjBWF=!^4^{c-*Bf(HPqRfFQsNj7OFm_T8DwGBT@^vu+ZYAiz~c;V@(3T z&2WE`<5NTW;M5=)$C7ZVM!6X??K*1jDKgaW%9F?Ua%5^skQ|+`hG?W3$ASp9ZlWN@ zR)|@cpXb8m1=ds5Am@mpgi6sYS6b#Yc~hu+R+Tty?t` zDTiD|xv|{j?HeoHt2D`GlZ+J8)SAi%*Vgs+OhsmIu2J_Jq?1WL{nS4G;7bqE?-|yX z8q_^QXRX1!3LlJS89O%4$4*Xyct8i&292a!1w^n>1F9G!5Vt9ns#K*?1g&;JP;00OBgtevnrjWp)h1C#$)O~J4)+lDJutvO|MElZ zKR7_;@~Xb~v)d%nuI&EkxY}1sn1z<^8BKFw7g*GZJ6XhXP>s7oLXS!4Ay7&j$7+l# zBP0Dfxi8NzudH+aR+W|22DMTHCkQZYABil%pkzEO*QK=7q}~W1R&0=1fhf`xF^Qyu z-Rcn3TG&BMRs}DPXxXrLqF;Xhi<3Os=W6x&4`{~1Gmq`Ts<*UxZidF48pV%KF!);s zNcTGuHJXYe7$az`f|^aVPwg(GBoGB4!rr~RIecK3{Kem1<*nCO!ExzxA(3%JOeAU< z?WB{cwnJPG2`f#UAi^Xpi~(W=*Ty6hs0~5g$HYoPjM}PHj3L{fl2e~Q$iMilL&O_C zGq=`tc)Y-_hj+31>J6Fx(G`*pPcr^5pVZ7z7j0FQI48!kL1XO_UtMn!5wSO6a@AO( zC{WAJ%h96`^QBMT)w%g~7A}{mS`Mj{OiwnNcc6FUmAJsV9WAdHkCLX!#9 zMWU#K$tAFo36084f`pAb)+Yx?(mXbtQzxH;$uxU&B`Ljni^bck8pJWF;|Cc1>ZiyY z+AT1rFf1l91frsGq#pv~vCe$+)R`Wtu?d=yOcmsjhsT+k$gt9CFuhzRC^u;PN{j=Y z2-EH$u?n^Yjt$13o`d94i0dFxgz;?bjEDFUZ9kO!t~|#d9%00@XfDx`S%l#>V3CY`lXUK~*uvR!maB=jzQH z`h&lFSHJt*d)zsD2ehGJj?&;C{|Jqva5ik6KJK55mmcC1?w$YpDhasM?d+o zaeaEK$i2l1YiliebYg&_hQz8o`WsKM|A`Y~7qb}41_IGAM8)cf8)=uH?Ln2gMeL}( zlg836Y$h59UEbA7%0mZE$eGCpdF14A{@ed}fg9(q(W)s6GYv|eDrsyST#$rVK?tP| zezi$xz%+cqhA%!)9L{odPfn%=T#9y<=Kg{#td;r6d$WA<^G`DVxu8gy;FkU)#^EnL%ED zbCu=Ub>eobi!L}G>3ou*yesK^LdQq4oYPcfQ`Stw6=How(K3@ap`cRqEW7nQK+~ z;eUN4o*MS~x6eKfV)NbaUFBzQ+@rP;h;18kIhO;wGVC2rk+4Mi5XRCD1BC=?lvds6 zUa2h??ymEG;~l=58)o;ABh~5(nN*)Y+9$ zU|Awo`>pv>znLAW$JQTw>x()xlu}A<{p!g94i!_B8cN2qrI<0AP8f`c z(uu?t!SP(Iq*1@oqI9n%S7+<;_Qh2i7k;FR3s*RFY&>q%I%*c!(>n5++xx;^%E2YALd1+0Q-fS162>d<8U#W#-n3)yAo;O;1$@(i`hcg<=o zrV~lLcPPoQk0eu$rjr(}cBqY3fRnUHr9D!8DRpdRzSZLDLXAsz)-?{5eg~#Mm}9BF zVR^pKtbr>tSDMA)k!zoR>`7B7P8}3cM~PMMhsQ*A)Cv3l1D^FZQ3!DuVeHsGI#3w= z-Z%bX>*8lV;oP|RtDx4o)0(?mahn~y<&qkW9r`jJcE+ZO5ZRVkZlnlUNtNp2Tp95XD+qZ?wYpT0E4TpE!1SX>8ZPgcx--ih7B&+wQSN z9ZI)->g_1F4?OE>(%al%Gzt}wMkebeGf$q}$D>Dv{PMzV$1lw_S_^mW_3BD;ZM|-f zjienni3O25w!!nd$;7q-TJ^GAyu2c>ygki@ON+YUcNiS{5nROcnQxP zn|L4>?jCIwcI{t}lXqt0>vNu+GW*0!W!$u_j$_nz9Fj>}920YIv7)zE7>%Zsho_cTs%Q*`Eu!aE&G56C}A7;~g6+1W^>lTIv=7j1DC0;*kQR8@>IVr&<1YEcl}SzTVUTaD%e35T@jN@RrCSQcI~VQHXpdwL_d zd~>~Q*+G!WF`%wVrc!P*-{)N$8y%YN&kr5|GX)(d2--%7BO?Y8v(xpv$UXOog#!N% XL)WKU6p7mJ00000NkvXXu0mjfH#=<` diff --git a/gui/kivy/theming/light/save.png b/gui/kivy/theming/light/save.png new file mode 100644 index 0000000000000000000000000000000000000000..43859c85fd65d672ea1108f1b7d9922c627742e1 GIT binary patch literal 946 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H1|$#LC7uRSY)RhkE)4%caKYZ?lYt_f1s;*b z3=A$RAk65Hek24a$X?><>&pIsiIc-X*Ynb}Zww5~_MR?|Ar-gY-nI4@Nt9@N_}o>b zvp2_u`R}ooBYgjxTf@SnxV5#d>KOvFSLQ6ZG$SK%g@(o-&LB?%!!Hv$61n-09QhDo za4O3o?Xdag;vMVVnomFceQ);Mxp&`aFaoVa04b%ee{9=!PnxD0o4i}qGhFqnQquB| zj5p$@d}){|pXa&lxwT;7%`%r3Y4L`)oSwVS{BAkh&v9E>aO3?g40pnRXVwL)yzE@E z*>u+Ha?gJDh{vxxc0{*UH>Z^U_EY#BHFws#gRZyz*(2V$8S@pCnIC*pp4)%3kL}Iz z*^Jlz8vULVbX~Sn{Eywi#ec;G3->b}uGY&`dww=tIq9E$`{6y;lpa;hd@bYq=WEcM zS94dM(Erf0WY6IrGhS4myA{{j5uK`A)N=gM)Z)wEydv9~nO5Ynygy~3tJdI zi6N>+BVbp$Ea!rC?MzGzj13G93*wB3{dWg>@7|9m(O?ok;$I4@5!FG?e+En z)4%^(T9+Qs#achL#Pw3#-TOM3)uESqpE!4Wm~GdRW76~ZrIvZ;+s;{hb+^84R%*N7 zR?YD8!`jRFs=3#4{#8$s6SLaV@|_`+mqFK#rQxyzl7$!UFx>j{h0i3+lu(t zGOlOUG5E8eZI^E5LSKHS^Tyw@-f%8&oWs_H={YpN;^I1-W{Nmpxm)x0;++4DIr__l z-hT`byCnM{Dg1Hn9;+LI)nYf}o?Kn~t4XX_C+Yup(W5!#Q8s`4zCE9BYk4r~)qF*+ zs2Qa{{{NGy-n_5AUL|u)>gWH8ng2TWT9mC1ij!OSfeD@f*rsL^U8ISH?*u;6v=(ep|@w<{-2UW-W1Al8!)+|F}fac;0Pcm(2ly85}S Ib4q9e0FH8my#N3J literal 0 HcmV?d00001 diff --git a/gui/kivy/uix/screens.py b/gui/kivy/uix/screens.py index 29f5ae13..55e4d74c 100644 --- a/gui/kivy/uix/screens.py +++ b/gui/kivy/uix/screens.py @@ -202,12 +202,32 @@ class SendScreen(CScreen): self.screen.amount = self.app.format_amount_and_units(amount) self.screen.message = pr.get_memo() + def do_save(self): + if not self.screen.address: + return + if self.payment_request: + # it sould be already saved + return + # save address as invoice + from electrum.paymentrequest import make_unsigned_request, PaymentRequest + req = {'address':self.screen.address, 'memo':self.screen.message} + amount = self.app.get_amount(self.screen.amount) if self.screen.amount else 0 + req['amount'] = amount + pr = make_unsigned_request(req).SerializeToString() + pr = PaymentRequest(pr) + self.app.invoices.add(pr) + self.app.update_tab('invoices') + self.app.show_info(_("Invoice saved")) + def do_paste(self): contents = unicode(self.app._clipboard.paste()) + if not contents: + self.app.show_info(_("Clipboard is empty")) + return try: uri = parse_URI(contents) except: - self.app.show_info("Invalid URI", contents) + self.app.show_info(_("Clipboard content is not a Bitcoin URI")) return self.set_URI(uri) @@ -324,20 +344,14 @@ class ReceiveScreen(CScreen): self.app._clipboard.copy(uri) self.app.show_info(_('Request copied to clipboard')) - def do_save(self): + def on_amount_or_message(self): addr = str(self.screen.address) amount = str(self.screen.amount) message = str(self.screen.message) #.ids.message_input.text) - if not message and not amount: - return False amount = self.app.get_amount(amount) if amount else 0 req = self.app.wallet.make_payment_request(addr, amount, message, None) self.app.wallet.add_payment_request(req, self.app.electrum_config) self.app.update_tab('requests') - return True - - def on_amount_or_message(self): - self.do_save() Clock.schedule_once(lambda dt: self.update_qr()) def do_new(self): @@ -345,34 +359,6 @@ class ReceiveScreen(CScreen): self.app.show_info(_('Please use the existing requests first.')) -class ContactsScreen(CScreen): - kvname = 'contacts' - - def add_new_contact(self): - dlg = Cache.get('electrum_widgets', 'NewContactDialog') - if not dlg: - dlg = NewContactDialog() - Cache.append('electrum_widgets', 'NewContactDialog', dlg) - dlg.open() - - def update(self): - contact_list = self.screen.ids.contact_container - contact_list.clear_widgets() - child = -1 - children = contact_list.children - for key in sorted(self.app.contacts.keys()): - _type, value = self.app.contacts[key] - child += 1 - try: - if children[child].label == value: - continue - except IndexError: - pass - ci = Factory.ContactItem() - ci.address = key - ci.label = value - contact_list.add_widget(ci) - pr_text = { PR_UNPAID:_('Pending'), @@ -401,11 +387,16 @@ class InvoicesScreen(CScreen): ci = Factory.InvoiceItem() ci.key = pr.get_id() ci.requestor = pr.get_requestor() - ci.memo = pr.memo - ci.amount = self.app.format_amount_and_units(pr.get_amount()) - status = self.app.invoices.get_status(ci.key) - ci.status = pr_text[status] - ci.icon = pr_icon[status] + ci.memo = pr.get_memo() + amount = pr.get_amount() + if amount: + ci.amount = self.app.format_amount_and_units(amount) + status = self.app.invoices.get_status(ci.key) + ci.status = pr_text[status] + ci.icon = pr_icon[status] + else: + ci.amount = _('No Amount') + ci.status = '' exp = pr.get_expiration_date() ci.date = format_time(exp) if exp else _('Never') ci.screen = self @@ -444,8 +435,9 @@ class RequestsScreen(CScreen): expiration = req.get('exp', None) status = req.get('status') signature = req.get('sig') + ci = Factory.RequestItem() - ci.address = req['address'] + ci.address = address ci.memo = self.app.wallet.get_label(address) if amount: status = req.get('status') @@ -476,22 +468,6 @@ class RequestsScreen(CScreen): d.open() -class CSpinner(Factory.Spinner): - '''CustomDropDown that allows fading out the dropdown - ''' - - def _update_dropdown(self, *largs): - dp = self._dropdown - cls = self.option_cls - if isinstance(cls, string_types): - cls = Factory.get(cls) - dp.clear_widgets() - def do_release(option): - Clock.schedule_once(lambda dt: dp.select(option.text), .25) - for value in self.values: - item = cls(text=value) - item.bind(on_release=do_release) - dp.add_widget(item) class TabbedCarousel(Factory.TabbedPanel): diff --git a/gui/kivy/uix/ui_screens/send.kv b/gui/kivy/uix/ui_screens/send.kv index 71402557..cf995625 100644 --- a/gui/kivy/uix/ui_screens/send.kv +++ b/gui/kivy/uix/ui_screens/send.kv @@ -75,27 +75,27 @@ SendScreen: height: '48dp' IconButton: id: qr - size_hint: 0.5, 1 + size_hint: 0.6, 1 on_release: app.scan_qr(on_complete=app.set_URI) icon: 'atlas://gui/kivy/theming/light/camera' Button: - id: paste_button text: _('Paste') on_release: s.parent.do_paste() Button: text: _('Clear') - size_hint: 1, None - height: '48dp' on_release: s.parent.do_clear() + IconButton: + size_hint: 0.6, 1 + on_release: s.parent.do_save() + icon: 'atlas://gui/kivy/theming/light/save' BoxLayout: size_hint: 1, None height: '48dp' Widget: - size_hint: 1, 1 + size_hint: 2, 1 Button: - text: _('Send') - size_hint: 1, None - height: '48dp' + text: _('Pay') + size_hint: 1, 1 on_release: s.parent.do_send() Widget: size_hint: 1, 1 diff --git a/lib/paymentrequest.py b/lib/paymentrequest.py index c7f41a21..fa039785 100644 --- a/lib/paymentrequest.py +++ b/lib/paymentrequest.py @@ -186,8 +186,13 @@ class PaymentRequest: def get_amount(self): return sum(map(lambda x:x[2], self.outputs)) + def get_address(self): + o = self.outputs[0] + assert o[0] == TYPE_ADDRESS + return o[1] + def get_requestor(self): - return self.requestor if self.requestor else 'unknown' + return self.requestor if self.requestor else self.get_address() def get_verify_status(self): return self.error @@ -196,7 +201,7 @@ class PaymentRequest: return self.memo def get_id(self): - return self.id + return self.id if self.requestor else self.get_address() def get_outputs(self): return self.outputs[:] @@ -421,7 +426,7 @@ class InvoiceStore(object): for k, pr in self.invoices.items(): l[k] = { 'hex': str(pr).encode('hex'), - 'requestor': pr.get_requestor(), + 'requestor': pr.requestor, 'txid': pr.tx } path = os.path.join(self.config.path, 'invoices')