From a866d97b70ec346f1ff07c8e382395cde60ab9b3 Mon Sep 17 00:00:00 2001 From: Vus Khoza Date: Thu, 26 Jun 2025 14:43:25 +0200 Subject: [PATCH] progress on home page --- analysis_options.yaml | 3 + android/app/src/main/AndroidManifest.xml | 1 + assets/.env | 2 + assets/icon.png | Bin 0 -> 50012 bytes ios/Runner/Info.plist | 10 + lib/http/http.dart | 30 ++ lib/main.dart | 77 ++-- lib/screens/home/home_controller.dart | 75 ++++ lib/screens/home/home_controller.freezed.dart | 157 ++++++++ lib/screens/home/home_controller.g.dart | 21 ++ lib/screens/{ => home}/home_screen.dart | 208 ++++++----- pubspec.lock | 344 ++++++++++++++++++ pubspec.yaml | 13 +- 13 files changed, 802 insertions(+), 139 deletions(-) create mode 100644 assets/.env create mode 100644 assets/icon.png create mode 100644 lib/http/http.dart create mode 100644 lib/screens/home/home_controller.dart create mode 100644 lib/screens/home/home_controller.freezed.dart create mode 100644 lib/screens/home/home_controller.g.dart rename lib/screens/{ => home}/home_screen.dart (60%) diff --git a/analysis_options.yaml b/analysis_options.yaml index 0d29021..d54b87e 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -26,3 +26,6 @@ linter: # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options +analyzer: + errors: + invalid_annotation_target: ignore \ No newline at end of file diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index b5228b8..cf3acc5 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + BO~LHhsX#aI~CcItnj-} zeZH^X@893|bzXgZ^Yol^-sgVb_jO(Ob)R@$Z8dTdMiMM6EOPa0%KBJXIGb2l*g8Z6 z@HfYP?40l)JTFCc10wieFp>2=`2P&vDn{Nn+-$x5EIn|EVkY%Y6QdD_^xdfB^q zZ{oJe!iO#+A5!wPvGjIub7ePhaIwLDR`A#SBqUq z!GQfS${7ob9ZOwV!NC8?+Kinald5pRTU;FmdCd}%kD|QIFp<*NIG9HpK$&@4KCKFO%$!>7c9KP*_D+hABnlW5Gq9? z4r{Jl-|X+=8~tpg9AAINKRU)D;)qTwcNLenV+&i(n-QdW>c#9gzlgqLMR7{TVcnv; zr464j<)pnAl;`&qgU+uF8uwWUU=^e(*pX(No>)T9S+Ug)Dqu@R4k;TQ6Y9M z;#H)Ip6l331bRWx;AYH=7xz?)J3>&jO46)q@TUkPdE~)DzYK@9h~D2D zdQ!Y#;*)8!>D&C=u)Jq!r^mLo=bI-l4T*i7iG9P=vbW@r$mO2K=1NkG$9w>*={Wg& z+u%7lU7_X*8gjMOlsPXKqd23({CY5dR(Vfta&z~7zjParR4nN3l#NAfT z`xIxQn1?a84Naw`k&^yyp*RM9$tWYKB0N;bmC<7e

mJ&zF_GJMb2M^y~ zgb~)HcSG#5!7+dN%Xej?_kLM7exSu}~Ka*KLA{GrU?AhRKXYPYqQa@aV$Zu`Cv)UUT_oSkHbK#D|2gn#@!!Uo zbf#@J7wBnBz5eM~aB#;Wo0~D2011z=v70m`F&4w?jj{rY-3&H=FZl-J27gAc_14#3 zYG+s1B9oT{i8nA3?KV8zGp=;a&CUMDhoY>~{#0w9-q&mz$SU=`EA0Qfok-A$2De|* zie0_B-6zn-p7TL*$jzY_o1WwE>^B0v39VtG%-7?T0BhjR)^?~_`iiIZEvbsZb}K3 zO@%|XIKuqZRSyOS+7>@ambLYDWdEAHObOAF<5;}s`0u7_T$pmT3E|YdkNT=mPG4~R zVqLzDI@YBPE=4W%nW*Z|NO90b3H{A;7pnsOv_{hUzC~*OdOF&~_RfQuJ~jd(3f5vo zWT9!pMh>M|N%QXsQ{U~7=xe#fckZ>x%Xsq79?vh^)=nxH56d@9J)6%>BUSyEYe<+e z_{}fGav%(4hmHN19g`zzs!I(jc@1S zw>2n}Dvh2C1^<>iraUjeuH)!weK(i2!GC*j$bIP?R0U(hw1~GP*e$C~3NuoW_Dxa$ zZu5pkN6@SBs6}z=IfqzF(Np-&dKdWB|1PPteMg#0T@f-Lm->9biN>1Hd*2)9RId2X zMyDkzsWBt@7S=!^ohuq`*;7NNKo$8)M^b@`J%N)(rSR`U{riw2e25Bm*weKy8Z%K!-`_rjx*RXwxu{7S(<)tz8V2?H>`|qh( zh|Zz>Q{UYsibLOeagsB3sK$Bb@8#aIdH~m=I6y@@uT)5t-pd8`3Dwsz&clH5NV8$M ze+_Tg{+EWC2fH=Rzhe6&olE`RyStC`@>Hc+^Y{CuR;w~Jx$kQ{OS{I!uKuwrzluso zgFEQox88@(Vit`|O_li`W-s?kIZ-lD#9a{^J(tUmq_xs^p<&2V4i3bmq@-{; z?q*jfe7w_rO}_=O!ZpKR8kw7S_xF?GDLmfS=TKB7XYu%(3aW+DV`A`4n|;aQGt}b| zF+QK}a}f&*3-ABz6Y$vit_Af2zxloX_gr{R|HUeLozu?F4k-+SvPWb$Zrn)Y`ccC+ zo5fJ+(-yG9eR4c^%F^IAr^&HfMZH<&RT%oJ809%oMXQ%~=`dV5F`uZ&ZJ8d1Ed$WQ$58Q0Egp37wYhGNNswb6?wrWS3Qh^GozyfdgaZY~re<9GDR42FJqM`E#_*VhpK zGjTy(k?U;7pFh&bcbxtiJnizE3yBNb+u&eKR!rw2nf||p(Yi6%*3uF??9U?G6&ac2 zckfiB1AgK`Jwj5Vi`4VDy^zz+sQrcP-wP@Y$eA~$iG?=c{OZoG z33t7>Q`S@FrBM`Nd^b8u^JQDy79ysuE(WqOJU7>dpoQHnlm(s2nS>&CW(t5`mbSJ8 zeSLj@Z~Vj6x%t&sZ4f~6TxW?7Ki&A0o7RUpD0csl0_7k}5LLV(PCMRVL@upFYpkcxzpp z0@X(nd7#N1Wy&9?SQxJHEHbb)#kQ7V^j&i$({yd_ZR2+S1Wr5!BBAv{T_VUQ@RhZ- zwU2Xh&_90)WoBjyUcDOUF3vpqy_WnD?}&EK^1pC=V^Q&>d#8o{UV{r$c8S8c4-{Y5 z22!mN2t|vj-w_2fJJJA>|Hx)K*om%0vNIbcF(#`5Y6XzYd7lWFMMQonq>62Ye;KBO z!fqi>&Q_sywW$h4TH-(uapc}U80zbPYjmN8L63%`&oj+T&XmNfQ8kZBi1pu^s&?r; z6II)dlyh8>WqwRa_)86cnWlOLwBtA56vC%6C6`i*2kzO6uoxH7vT()UsG4~W@$htT+zt;O` z&9~^qt~i{%#S}IX-z2&LIfoZC&f{+ zVAg`#L|vIr7%?7dDNJp#Y^ljDL!qG|4V*A3j5{=5eqWxK; zj<U6}O9Wvb&t4az7p#{5+rBnItLTwq%O zXp9ZFgxB)$e@hP%QloO5ZH!}4eMB*ex-|F@nGoP`ym&z{XLnUhg6*M)6koFrk%Sx? zGrh1Kg}im;}VAHiN?DSh)=`eVna^b{W;V>Mg)nOD;Mi2bDrT% z_&Gbs;`2pk@!hwg`D3HteOatIpc+IJuTRE9=2qiYmF$|1qlUxi-g!;WJFy;h&Yw#B zI>lf!kDofa2RLoDc3{@*CN`z^-Q+uD4hr;Mr7@NEh96&!6E;IrS1yVJbpzCvtctq} zbh^&uvVC@}YXg&JCra1u)Ybo|O~;XYY%c!KIG($-UF9zl{H0SM-D~RTAov~1E2MU) zu} zkw%YdUjMMv)7etl;QhCRbG!2a6w-q+n*ql`;}NTpTQYQcOPyZ19j&BuUJK~WxEblW z)4bJRVQzxzB=ZwY`F4c3lqpLcJ5ih4o%2@M(gKl7_*;=!u3K09DYoX`?b38*pl5S+ zQ|k5D{={9k44$LJ`DOl!&iWlF2F)|7Z~z5+rcdnZe0IaPnUH%H{itmcgMKx&r7XeR zPf57o97z$Q2vFzRGyTW8x$mZ@nW4no2db2cIg|K~3i3;`YT*mVwAM#94ANm$QCrX3 zxQiW4Q{H40>~;3CRH{QglTWK$m28^fhjW&WD8M=4&sKA_G6ThGBjq|16`&QBAIVc0 z&^!lf4+ku^nvkDue4YE#xQL-}F(z|9)Zoa~wu0K_<>dtcB+zXQo|eM=E8h1JKN(|Z z1(xC>{pQVVL$G|fb<8ij90Ed~%3kzs@^ zv&^uT36~R!g z=T`dZ>{!9D=#vfx(Kx!eYW0tTWUoywk2;DP@SUScRQc*DVJ*b4wz*ksz*jiqN@(nx zX~}ld8W*o+>niBG8L6|v=lk0Lcfyhdwn}I#|SRFu?n*YmMOwK+f@&Wbtk` z`xV$)fcL#&_68>86-y{fYLgwq!8I;G@(_Agpt67zV+H5Ee*HS~K!5!b0Rct7j7IQ* zh4@O_Rle?Re`(K(wwf8+iIMNIvb%%~zH?D7Sye%jsN$%+%0fHuAz=|CRl>~45W446 z$2qG;?kvsuS$-`3m4tI%92!Cou8}!<(%LrQzcgRATQOG6TebUqrKfR6`fB5*-DhJ9 zfqOsM?@=n~bEcg;r@=*VfjNd*pCiuTTH(p~g$p?5R8dK??RthMB%WqhpTD%VVJrro zK@=J8E}mqBiiPDxH8P)D!<(&lf&C3Q2P$l9s|>6GtpDh$4~QVZ0oiv3aDFfJea>6L z#~#)@&0Q3Cow}O06ji}ko(fb|fgst8F+V4Vl-q{3pC?28TDtfAPw@lsxxL|a-zZV? zOdEXCR1DAv1ql@qMqnn7j?+|Uw>PfXy%Q1P3i?W}Ry#xAapo9@pql47QqNUJQt=F7{ulD`b`n?8k?)eSM> zZTRYJ60TN~4|*K#%@$~W*L~T1HXcH}%OCsgQv0%)btj zfGSX+%WL{uN%HLA4lp#>hm9VK1O_)*;w`Q;e)-$8BwkUMjodz(Df;I;^4>00Rsa0TG7=Y?IX#!Envyd|o7&}EWo162*+CSYtQ_*9>7CDlEs)Vo+YNx_KRTNC26r2o)Mx?4-gr7U@MXCD>ViS@Y zUor4ShrNU4KYiJC>rL4T!n!9;FE(TBV%KUG(D>sUj86XR+Y8QwI=@e z--PmHLlWBld=o%a)!o2H$6MMli{b#^gAcwv0`YicWCV)JXznr*dUq|;29hQ67)<%k zXY^WFS>X!YdD}HR%lcw$CE{?GGMPy&B-A`LY(K!A*^j+SX8KveQh-7EP1y8^s)^8U zW8)6w{%$w8E_5d|A=Dq_XAwr3nyE1r26N^ogG<{hW0#!W+=^#h(e>-3VbW}!C$hk= zfJf{fuD$}A?Ku{?7iD%gcrb3WxvJk)u6DLk(QVwqo}7M$w%?UnrZqWFwx^16WpXld zbiq}a@cP$O$*N%Yd5-s%(N-6q$1@n&(s^EsvGHAKn!a+VUSkr>_I8ll^p_5qG+sxG z6=YC?epjYk)ie4WWEU#n;T^pH}_W9&Ic1evN2)&d$yt+u@P+Ktll4Ct1}< zL&yA-4daH7vu0#BI9~4pWC4($$;()f*HM=*FC6Z!BCEDO)!?z%O^!mLfFQChlWHw? zOTFDB+{}ff`at6Xee4y_&BRno9>0p)dgWt`Z{+Q0$m}pKQwVO5&By%yE#~X%8(HoJ zG!8HkNMBZ#UhXv>ta z(h6NqMsGqL*i7>BW~`yWF0TJe>>F-qA<>Bv3cEC$yFQ$*qDRF$9p%DrQIF_m)ap`? z5UJfDQu#vlO*gC%CD6%McqsDIVZy?Oo&Z*QW%sa$V6#GOPUOS&H%SuHGO?ExKiJkz zOiZ-zEDc7T&GLN1Z2G19o>o1N&%Sk(nFD}j0I0xrLBpN+{J9e}X;{ClNxS?7&|`qn zD)K#4MA4FRTMtcOlbz(PDFa8vw>h);+nk{?WW!ssW9-7mtHIOsvDjK8AdaMcu&sXk zHYGWk9AcZ$#wS;MN&e!DI8*4xTGyNil)}qU77j47z!Fe%2XdtpFy*?u-WON1 zgXiK(9H7TDF+HtZZN%J5?Ub@uRaF(P%Nwm)jNMl=dz2MVHXEf++s4+oSNrj@HzPic zpLv0Cue{Su3kH+0;WDd^9nWBDsYLt#0qqeQG$sC<{G+{0dr*mpB{ z_BASM;|)b%2|?c)Y^!!sW1@5%bz&uA43*Zv-N0=M+sw`L*-v85#w$EP&j(mO7nRZK z%CK%Uy=y>FuD5(i_KC+;EozXnUfpTyo;&${RXXq&(WJAOk#aY`hgCoB-Xg$%P!u2n zp=Jn}x3mMI+L&!klsnl@?f5qBFC98B`qZO%bE~qEzId;H;Vf~W)|>=$F!bI4JwciH zf?*5m8Y$I5)>F0kjCX=W3xTbEHT5{G!(So#uXGM#P21`5<~E`l?F`DPN#@2D@jm)F z%Q$<<%HS0vtKF@&c;Z>(_Kg4v=Bdb;)y~l=!PnePAB37dki51%^U{$b_7goyvTkmy ziftnJ2Wo3R;Eet?74ht7-o2>r?kIdciikygtR){}&qh402FOAZr=Hz zIq_Dz=OxGXXiQY)>htl{iK!`ccQ?|rIG4oWoQr$=m&8RECAnA`IWfbUAZU|4v)zCfF=6>R2E&dACUFen}!d*2~ z#r5@d_SN8rHS7IbJIu2$JsO#<8*hFGP179~pnq_Xa2-!gwA^R7QgO)l9GSpfHxWkY zhAIr5eE+}%X%_D?n=xHRxtQy`E@$l6?(NKT-c9e9c3=#9=~s`=*D2N-e(ojwisU*wi!eG%rdmsTERoBp@D5? z4{6GaPbq39&TJLYJSgXN#8IqGQqVhoBb$O7DXdACw^?ZS>$+3ZI%D=XGDSz`?NQE> z@3+#-8z# z%LcHE1laF2hLe-MHY)%RAm7cn?zLW7SDCOd4MRMM3>?OE7s*?%~@@{UDBquKt}USY%PGP&4|pQeOct$v9z z0$3PQUCy_*8I}L@0(kH)(Br9NV~BM-^}0&F7j)~}Us|yGlet6vqqwr?oUcn5%JJA(wcnNCmA*2#x31-s0|VVmU~*M%t74VJ8f!Y;vl= zNE@bF&+e9$E>OBO%bIGB04d0|X@eCBKHeQyFuR1mW&NajPckS~F6@Mvl1;X&?~%Yi zxt!Q+JSyY1>2|?!75bUT8rL-%6`*90hJ%mR8<2`;aFhJ~#sGT3hj&q$Kl$d?i7Uqo zQq0CKot5v(@+gUY=ALfv%VM(+3Y((~5}(*Lm-tZ@VR5*Fq(mxbk7Ekb$x@csNjRd5 zZrtlCEsQRn$FIKg)~h#oE^704Uy5@rY3qo5skwZ^nAlWTBHToE4t5+sBt9xwD7yZT69*CD~}@5qS>x}r**j3iqi3=DMqo`Tz=5L*tqwu z<8R52*ZtDWn-cd&6;BhOlH=~5$hCYW)b1W$pt!Ikfn7sx;BAHh?z_F1D)(hD>_iyq zu5Ot@4$hHKA4d}84mEUL7L0gd$geCW- zBdE>ky?Y-fZ&tCqDOJ^hE?J)-H;^^6IRev6-YICP2e(*D1h_^t{G`(GgQ)-C=xH~$&--Nq@asj4^Yup(i{83-= zNbl|HCaJ&fSg6~6cqqv#7n%hA1*?z{7AOm`mZk^0g8A=#W-K&AkA7bU@KD6NUmAAE zgV-T}Jf(Bx8WV)3YuE~YhP!2*+B0jckI9>j_{0YxU6_4at@ugICtkSlc}c^z%42;E zYl;PN_m-he5$YcerZjl}=pd*4Brc|EkcdV@NAvewzy1yV# z+M_qU<~mK*kdC4ufesDPsES_KOtatMvuNA1IDSdqTLVHHDY$(@LoaOg-nS@RI3h^? zXf9{p;IdD@w%_exH(h${EuMlaKt%P8>Z>kIIx_(#5$n)R7VJKhhplQ~E<);KZuXm` zJVFPv0jM3y7Tl%y<;#+*N<3qD&g<%1dC%XHqx&Mtyy5J>l=g_l#e4RdDsRus&0#Wl zbhhM9cjSz2-&QI!D2gewOTrh8wX_HelUrV&wif}D0r-eOc;t79qE(cQ9g{``$c6@fa zz~%AM-fhoMBvIOO*gU+=F&Z8?e?O3N(`g`_HY!5bZw*m*$$(MbnBX=AP zKp5mV!WnD(3UM~)7O*YB+C=dtfRc#x<&uu}HVK&|a>a1N{1=PWiRRV#{5Kyt`d7VV zMHS<|G*-NDMEh;+w#fS+kXk@G46~->q+k{&D#E+?RFR~NiYq{ubO4{hK~JA5XV8=#2%+ z9sE@QClH>)DjR%{PQV;!FQMp0u|!Ie9Bq3J;+i4R8Yx(n6w!)eCxVbI;$@w95|ftL z-HkO>56g1D@7op<+Hq{5?TeVyVb_MJ4RsOoCj!1>#YU*ky^t;G)N5Rkpl?IZ86*l& zw^pj}HcE7IC0#vF-EtH~n^x)4u{=6dVXZqI62EdHX)?y_+!ro2Veeq=aXfFDu0&L} ztS4uQ5H5OzR{7+!!AjaH=Zh_;}MmI9zl3?`mP z_I4jL-h{|+-)y4M$1(!;PuS@k{Hgi0MyeVb;?z=E7hZ)OI}Pb|*SoxLypy(&yaT2n zL}IWOiU;jRJzY>r>Zn&{-gsV?I7lZ+eoNL!_q!x$makPAcE8ZRc;jQ>wYpsISk=!j zJ;*XC#8AT4yp#5TsMGOm(bMon{7f&(_t(Tjw}gj7f6pwBTJCu_&Jv*-Wg zBQvvqc0DRQY~)|nY(6e%+Ph9MYrN^pWZOXgQcqE|Edn0g$~z!rDvc>J6OSDsLH%wc#SOnhmC#m+1IuV^J^XnGZ6~3vpzEZ=542M0e0<@`KQP< zX1AZGv25=D*rh4EaU<1-miPNk08-uCP7W`@vx3UF>)FPII@)OB|NZ+nct!9O|xI2QNy^NXqo0^1>oGSaM+ z+9x_zkUC9Ww`7+WHUiw8opiZt6FzS6N$%qGyLzR$?5Pzc_HX~1Qqb_kvlPWvco25p zV)tlrFD+s84~fZG$mmZdcNf>`yL;iAN8wAIL)lfDEWSE%HopnmNB>-o#y@j{$1M0Y znCwnxH;-nv^E@DU|AU_wLJz*tLG~jc9q9S?GlZ?F64WMe%)l1Gl7|LqpyUX>!T=K! zk7M26ZZ})lpEEjn(h6@(W1)XGnAV5>9gmb2d<7=S5&vWuraoRM@6z0=2)B>ae` zg_1V@7un+(ZUE*QL9g^C!xFVy@8uPCkn-P39>nhQU{;pEyKpwDh?`yq=wxvC!E2Act z@N#yqG~n}x5{%>!z;UmqTqf$ZNC#B0Zuh)jF=3Ml#52z1NZ@??=jV0kT7$NedfcU< zN_U$w1G2}Lcch3ZPg0jpNoHDLZfoE}c8MhbbtH>IrwZ;Wz1P}(cY~{qG7iAYj=0x$ zNAo6{X>GQwq1--Gn0KR9QM4h&(1*=;FnmeUimuU0m*V^-OWyn9iudvS6;3{|KXjR( z(Wk!m?EC%4=CPA59d#~^c|HrN$NglqthZxheSO$6%R2U$8|(4inMeH@J$XU-FFVP7 z!eDp{I_Nc%DDxKo2qZZgp+1)Ioxk@|9bkHR5pSA}#Pgv1{QOc1k*}szGDPsKkeq^G zcL3tbg}UJ-4kFq~RKF#`e|We09=Pj3b)0_VMHtBrDtx|Vl(vm9PnU)T2M8kE@bC4}{(HKHpZc2_rWtC&362 zu1|IE%rA&jFbl|2fC+@FAlmA9?WddVti=9&2e$60_?I6Slry{p>$n3|mpa)_1GYi{ zSOWLZyX3rk-SJOoV(-^ApRE?6yr3`z0S=yTO-K`uJEH@10I1iIo(w{PMrmIISfK*~ zn|ynJ+v=(QMX-E;my>nD-JjF(M(;N(o+QAS&<#MC5_ozbr4ynBm+``)tFhjW`~Y2Q zzBE4+QN1+mtc^?Nk6Wrt>@n4r-j4PUeD$USA{;|;b4{=_pQR2LjTY}7H6*5?^?DX~ zxxOt@>&S&K9Zv3*8F%TEE{h}#nLG@=-GAjY2e0)gND@>RX$-|@)dYPaV8vV#c;-~k zqho{hKh4xs%ESZ zjBy2qWBzJ{K+`JRW#6#OU~6qs*t9K_<*M5($jsz{ZTTYMn8&G?mRMVA^%(~1hDWBJ zfB&R65t{O4mM=*gH)V!t-1>-$jafkP0UB8AH4S^JMz&X?MZ9rT@z1DqE*BWjDqT-M zvf)0l#$yHVlaC9%3GTCgC-F*hHrem4!4dX%X*o;7mn7c5&7jsQaPG8WsDs`gVv0qi zyV8-u!?7!ZEutYH$^tAb;3X+3EserDxpCE&ATGEETU_EzY(EK|e62h?mEaMLlbe+I@}nmp038CoPi4`dm8K#1o;{Q5;< z@(tymLO4jgMau%8i6Z+$w8o&?O;Vma06++m7>HDQd=D**X|;N+?l$6nI609;>_$*c z!QX)F-4`!He|Dq3u7F({7z=34ke7*lbwsr?w6`$#HjhC21K}?yC#gVC#fB>B<|n=v5Cq~-Fwe!YUxSVQ;2Lugv*fUxU5=Ov zweUS10S@!qs)K8tJ}mo{Uapi!iq&Tsn+aRZVwNVLKRl;Sb#z(yLJXmr6w2HW4RDul3IfY>D-%!!vI;J9Z8`Oqw&50yah82fZ3{ zpt0O*qV^fu$MUb9li;R_S4daF2rPU*ln7LyDtzw2Wt&;(=NQ$NL4on&;Xb0k0wY2F z;kx|wW`Ttue%K5O584yY;-v^Vf89s*fd`-i=pInuL~ts-j^$d6d1(ez3Q>5FUeEex zQOHod?}uE+qM30?e*-9}Z)n(Fk~?KsT}8w}ZyVR7>Q-qnF)`$pbFR8UZ~MTgW1kCe zz5f7j^DX6|4IVgX71>#w^ZrGoJ4r z+2Y)T&N}R9L?CYrJwA*u%a)6NZyT!AR+~iFTv|NESN!0|=9AAA))qf5Y~${u*Ic~u0Lj5%5c1u;P_Ca+gc1)Ia>o=3ia?*Skd zFpv{%!DhMNYYqIsryv^(21;gJt>EKO85;dI&8x0_( zsM^7KsX<#mx!xf6M4ow2A&sWTvTlx3|AF>i5CIQ1XvsIct`580f@9&0*fcSj25@^9 zP*Hx)I^8Quy5>ycc$XIB=FomheotAWPcwh@1w{8Trfi!tEb+=}!4KCYF^K2LVXd^znAOLMP-b?cV3*Lhd7u&a`m5mjo zTcf*v32|u!b@SWrOTfB;z8 zl{BNsr&`+`^O2dLJ^qk$6=d8|j_6>jpiSsbgKCZlWJpp0zQK$s+#86txMe=IE(Soj zs=6Bb0x3S8sy4!ETV{lrg@wkwMWAtrHef3`wb#9jzql`MLDjeuNu)=iKv71ed@!<7 zCqWU(uB6q^ex)_q(C197VVu=%_gvy1(!ww?v9BFQ8Ez;_s_@|Of=D`6AWfW{53I|p+8I0678yIRZpeNEpRTKJj5&!a7M3+Kj3=Dj>J;Si10jvcZ0w zy& zqV&IuWea&DqJ(7n zg909&^v}?1;p$2OKJEme$y?`pd&yFf-qFP5oQ%$asbjc2JP`f*`-Y)S4*bMNEbi&IzO{x3SFLcM~dAqV%gu! zekBcb5j-Y@!7=P~<_!VEo1J!_X44k2%pb7F6(c%zgBggx%H`SX0w+E^J6%0He@>3d zGNafvbQedcZ1BNS^n^V!r@frw>85x``DzpR@Zf=b^OWdm%C}4GS-=kU@3eC1ooIzQ zG(2o%a0>5U5fONPHZCILW9t@rV_RRZN)xl5oi{&N7PeamS0 zb|E9qW(MRaWQRi~gDnGoIn!rv5>iOCjok;jFBxZ^A&EhZZzM5*vaF3Me8p(-7FxI8 z*PwuMfVEO<1avoM%z=eq*r$n2mRXx-{W6#gfpHYd%0z~xg4~0r^SQe#pQJ<$Je@%B zFYsH5lHBVh8r;m4@U%V%zILviccwDg60C5&b(ykTI27e^D zz?QdEQ0fOztGK3(LOq_HCO+yBdLA#OX0udeC2MAp?78!qMSxK&yMa|XE5>pS=&WB0 zNe{r?8Hd)rc@BmZ7Tm>`m007IBYSl+ZSED(LVE2M6l0Z0p-!@a9uhbY;eh^7t~L+RcXD*Njj=+zT#M0u#huiJ z3E?uPPyjx`68qNL?fRA=?Yd+B5+H$E$~7KULZz~-A5XALaJ!F;sLn(64i zWp|j*MBc$^&-Buf_WlG-iS4T>x(lF>MwHHnoIN)54cK?8IJzdAuhu+6p`knAs7OBm z);~%WJuq<~Hhrh@O8f@697L3W?6^B#5o{|yBKn8f=y!sa1uAOHUIe-_y8Mq|#G(p9 z3o6*mj2S41ucy`vetqYI@kc=VV4#4Z&Bm}(*)QAs5r?LpbyeXXJ($0GzOlGEW?1fH z^U9FiZpy|OW*geSeBp#?7=~$>6F|&jpxi{G7ueAQ-Bpw=L53qwG`Y(*kIr?^ zQvYlBf)pV;WcmFC2ny+39N=oq)n5F)J_T(}!!&mzBcpaW^OtS^G#ffR4)IV5!e+K? zf!MwSFf)Pl2eZK7$bbruR7(6 zJHrzCgXAtb?vcHA3J|%@@Y)GqJd=3Ni>D@e!EGU+noaWsaZe$Uzqxo^c)CyhvOtoS z<{hQMp`q3KBdPHwe$%*@R~{F1I96G`TACVyWb4IrPwO(7`5wi1RQKI0Pe&Y30s@OC$4CDgm4dhdb>{gC#?jZ5;~k55i3PE2 z0?kizuW?a~X81OniR65+INAic3COe+;1)2T8?pP2MfzhrbYT+d5;Y`GFj2EjKZw!f zH`Km~qKzV-nyB`;NoMz11^n^%{~A_cCJPw@202UO`_j^VO>V?wO%Vr1`k=*ey*4@? z-Z3j4a_F?dw<$QVOPSNr(ILHCI4O9JJHTqiwRNLg(D&xYzW^N^T%ZiXbze*WHM_|g zcd#X{P1{o2cR`!7rA?-yzU3}*kp?~MA>Gg|%ObT(U&@-uea{c!{y<37-& z06wq#($JE=o5#*|A{tY(4ij0JrTnlI=tb@!8`pKZF~VE&*xb0kbZy9Qe{TOR+i1z* z+f;jz1aHaYXU{H7H~ElKvP!oj{|FhO9xhh%-(5KeI=RRzAAh_5c>%PfGJ4%i$pkc^ zJ%_-^WTvZCH8nWu>HJ3v_pi5WFZ=oWruACG#SFfz52PU{{5N#-<OA_=aAaTwA7%2t*4x5OO z_D4P~>AbOLsrf5pjNru!)`Q@QIhQB6`!KFAdzfhO!IgB~gaTxg*M@KMecEscHNqF$ z$%Pg)#0m8D4~_k*w1t;;Vwc@!xxj|YDdC6f_4(134CPqfm1SAsEYa#pC-mqPNUpkw zE^(Qi`Fg}6dS3ig!)!gvnvGFuqG-^zHlQH*>+8jVEvx~@&zo~?snAw9i$oA&qJ;@R zgt#YoTI=l9dhQE}Itg4%_!2>#2&3J2zYw1#%ywx!`}pwoV>@G77)h?LuLto2hVCLS zaV$zRWMo)%9Dz~>+e1XFS*UIUov44KjHdEVTMBZTJHgxdFozu8?6=7YdHLBDy8AA& z(yBF=vYi*&U|0}ln!s*~Xt}_v0nQ6FCM4*XK6te%3C|ID0Zco=P{dp_-&NuWDG1HB z+sYGj%63dIS7ZZceituf^9DzuK2l#0TS_iX>n>`Wb|6N`>~hMj0~(_ z5<9yQGSN*Fu^BS0YK`N~%;2_6A>=UK2(e?H+k^L1#G*OCIUOXh<=sK6at4fg zZ3iFRw*GFu@jGF#Xu`@L2H7dlg8lO#W^Zz zH6?a1=FX#od)v-GPoG&of7$_qO7bMNQzw=+ntDT;5kV5wg${47m4SbcYx zRb>cc79BA-omRn%gEJTNOh=Gqt+322O8-R^wX*x`08zWbfU)X}GB~9d&x4;TqxWlT zjMQ$dtE=l5Ffb!dvhA^3cm8wfs%Kmu`20mX6$1lW>_m<33yO^Eh5(UJwBR}d$T!k) zvqEv?0vWm;AU~^2U^JLdL6}*hzV!&t#Anl65-K+g-9%W@_wR)biE5@l^(H@p*;mBy z7W{kC4syW{M&Hk!$12b5OD4C=%gcZG71qmt9<+k@lN@I%P{HWBX6cQ`C0Voq=Asvl zD$MfzzD<0QAqp1Hq;R1j_3Im=+Gq9R+!m+mn)OoPljnOr@4ua47^?o|4NtUE^+#oD z4(97|n-cEp63jQ$sH$kE7&ypqCNe2PKE}xYC^T?V;?G=kJ`Q#taWclET%P)qQn`9p zklsR&9$rMSceXT=UH;}xWUv(A24dIVMrKz#V@Tn`(`*h#QyqN!5LYcZq+OccBeIS5A69?XEurs;gP3|PZ2s0_%C*SUz;3MP7 z9#B5c*H^2s2c`GyNP}1A;qPrKMW7apxrtUQ^orLG^`8(_4Sz zwS?sCKT~bTOrd+tbCzIFdhsI9(Q4&LQK)z@=L0v?r@X+~DE4;fg}@s-q|C}yIHbP5 z7fkX-9Xg9C(Z_B+Y0j?|9}VSN!sC)RYMU-fcTxciVn-diV;N zM@L>uqhIzq&52$}jQ!?KSQ-f=pTI<>^x$6~hP6$-?2F9mX=&+w zl)>B_=WK?C5q;+sQAi(2>Xel-cUB7Nqp@1p6&{dM{ctb2;blhcE=~DHSJma4w0KIK z)rWB2NZJi_?Glk zHsCFso^C&UI2S=h5wWSr=0_#TeV$?KjC^`oU^h+o)5OVTs&^|STq^j3@d4kDA{ApM z>_s|ZKA~j9AuxI+fE7X=#$pOq5364YP8wvv=okWE!VX9B^~=d#HVPa{W+t~j72wUZ zc@}~#;t^5D?BDd`Bj4?YDXoyf(Hl8v+F!=Ih-(0el;cpg?2>0np+-%Z`vGsSKRa7# z`X-&0t)??%c+=;w`6t47-4A}+?7vMtkmf%-5*9M`LqjayQJncMi1pA2mHtQyuS=q! zq#W^|QOWB%3fyK@k*9e_%+^hF_>|D9`%# zQAv0@{7UHOoDR#L^13n|>cNFCU%oa|q3eql)1m83^PHFRV2eq@`-3OsN{thSk|ADp zexm+>>C^LF-k18WEonU0+Cpj@t3ME?#W3N zUyT4FIy?>-x+lgP)72AeG*a%yObl8Sz;$6cxiYV4R=Ti~22{rOav-)en{Q?p;KFqn z;&5_eq`@_ZQf6(O3*jS#R;~l82ez@hhiur2RGL%3YBJ?=Ej8Y(>1shlaxhx3vU!jYmH$j{M*EnZ!_6k2O|hbG zl{kDQO@YozVvB9tl#QJJn<6b2<~gpj2a!u-LX(6->E{SN-rj}mr-YlE4w#iZCvsq@;}3G?t5XnvPWP^McYF z8C)XU6-s5O%8jM=)45h1O7nRgoFBB$$|rF`i#cN)fR&-D{Ys9I9o|d_I2_f00$Ym~ z{-wbssOy1GO^ZQ`TGU0WO4*GUAe31Wy%00OVRb$!sPS5W{+e}x$Qr-!U!GYatGb4r@9`E7Il-Y#Z2d zoo+|UD@=&5Hd9VFOhgzMP4!;KX61jFb#I{o@c+_aY(6k`aCdao?JaC4C-Y z)QwcZd8AL6mP5^Ana@jet@D(*aX9<9U{Me3tNG#VS-A-So&&zz)Uq!BJ!c5!9ij}ooA zdY|XEp{ex9io7+))^|3PGHa!+LXM|M4#kIUlkHHHtEc_M*&`kI8Pfe%Z6lFB-x`H= z=%CTv0V5P-V0f?)D+FbEn$*3(^Wv>}h5`$}ehfBxtWf^OVlAgZlVv3u5FsHh~FhDz|$ri2?% z)MxMx>y&HVZ^ly@>8*LZEaQhk)AsS~Ul!zAbc6lM8&fk@O5V@)cBcZUeaK}45GNG* z5#96EOo0s18_YOA<6t)a@$7bRhJfe-Ly5Cj9Fo|4q@WFvvtPjR*6W8C_221=j z;6L+1K8OYRN2p+tH)iy?IwRQ#z4~CGNP6Cv`8Lj^gvH3Usyz{zewCiE9}C@V1ku7N z3uV&Y29fw68-8}{Rb2-eP1jf?@0qdLCMxCiZV%0{&V@&W1-ws~%JxOzgkT=T5z>hA zVW%t}z-|*o{veX<9E4IY7nTHwgIH+S=;5h_9TGMYLkf8S%;2p==Ew_q;LvJp9G+d&?R+S z_LFn-S_5TeKJUMU2T}v%STe3iA;sE$+1u_SP&_B%4NgNyo`E1H(kAIyRz+Zif zA^=#Im91e-Qf9MNS0Ah%2R`Z=$H2nl+t&N;m}asQuA5CLUCCaOk6afvnz{jTN- zVujb}hEGgPi~waOlc4gvacR!{(o8o_+j_@1k8nM^|D+-v!3}dSHM$Vma7o*1{K2$6 z;>RaLDZV7Q>@q%2q0EiHB;OJxOp2?7CSdU&H8O;vC*0vdgxs1^MFY}e`8;yU`7q11 zqB08SW%R0g14A>Wy9a1cF{aUOB8gIz{&NF{V9o zQ4&fcP3_MCOs`8@BDYP*cE6${B%;c<1_i$*YlW_;Ue_Q=G9eg^-C-5zP*z3ZN>UQm z69p=Vy`*CwP$dYVvd+*P-e+)Q=ZwOWzyE3rli&7;q*sFst$pnA!=HbZ+NsH;ujodq zEAkqjI zRsh&zHb#A*?QsK006KmEu_sOqOGr28fkbuy`pIn|nsj@84`;2}%WF|;>0ewi9Skl? zusoaW&wE>gz$}sNBEL!zw2^7TN5~c0$Q8C7!|YH}kP$Hm=a6BZQU_DJos}f9k_{IV!*Lnc6VjQ7afD!7v*6 zXke>1w6ZIHCH?tN>7<_m_4=CJv_9Fysya81t$)10*?V(7p&zP+w9%FR3~Uwe7#@L3 zLp!{KksSF1tLjF*6DgsjP22o6Y4d>nGq7agM`rZKrr2QLMJ5A$a3~mozDsghfF9#4 zbAeTbPIZnDgtnzELTNq;m&B`sQ=9mImZ!355WWki;Z*HSV-T4<+M)Z-YsAQ4;r65p zoo#JZNny!E+t@(TWQg0p^7N^Ia=3JbV2-pm<=;K+zt3*!m@XIGBdsU@5Ql;5M^Z+n zr?XShQQATkM<-+wgT1{o^4|OILwjcX*ONEI+KrA~))MQXAfAJ307`C6O6x~=R?mU) z4{#3T{{Hch3H-x|P==i)tZO1yB$=BoE=0XUw*lN2QMFS01;Y~Flh0B= zEI=0>yh2F#@f6pKhfZf z_VL6Ie@**JzqjA}V9F5zC!K|d2W^djP^;@GM+^sWClQ}85HA%NlovH&1x$D{R2G^A zBf2fmU1)rA=4#5j;vq2I0~&;VMxWj&Q2-Gj)#abM5UDlX&3VQZg8KuODH{Vl;baU5 zAO+3AWi6V!*UXMSTh4XXuLu_hNG_8r&f${DkF9V<=}td+`4Y{^H9^-DjK+&!VMh}> z))U4rvh%B=lAge7(>#7#rA6^{oh@dh!6hb}zA2u>NiM{C4TZ-V#xOFnH!X%k#`HZF z>>!}qh-DBeX8B-Mop$HDIh!9R6cN6_rGz6|%xg*W!iACvK48$hOb^*k30)M|S_KQo zG4VLQ*J5XiVekYkk%PF6S+FGOejAvZ*_XhRF^=14otpI{2%Q-=7b+be2Z{XdD+j^B!FUy)9usNm?8wtii^V!*AC-xV0@7 zNHyoKRt=wJc8cim=p++G

2D8PaY8y7j>xSrv`$KS%*cZ245oqnLyHv<{VeZDa@Bi_SZs@EML%;OW6$s{%={Mp4&iu|w)y z{fm!u5!zv3`{+=FTBd>4gr-NX(c%3;4LVieMHV}B)d(yH-ky3>3&Lg~e1Pv`_9#zS zdpvK|;RPgbz~rSifAQ8=UZ z^HUy;znLxPRs!!_0~8mgd?1{=Q)!}D{z-$W<=5=-FpPI!z&Q&D_6>6%6gye_nk}uJ z=0sTKy6?hJsB+25R0G9e&735qhXF4X-9W3Xt`4cMum3oi_1gM%ghb_dp8v{d4mel9 zV036ti8)fQMa?oyczf=Udd;6DSGIAf^hWj-$x->NSQ5Dc9q278Y~m4j{@`<5)6ACRhgiFKl% zx{hqoYXwiIZD*8zFV%yk2^`5#yg=h~~mK-N_ zZL~{70d?4oJX(~b8MJMWc^PsI?%Z;Gm2c>vm>4^D6wNSGPaP4M@j6f{=Na3csgGa) z?taqy<5K?@FA`AwZVvTjpB#&l#XWXT@7ccwtPmOez{iGfUTs*YhA|&+W>>~A#jA%k zt2V7gy`>f>i+ReiehhS1f~gbogW7TCR26L#KEr@5FYYY%5A9)?BMX1Au#@6LtuR|p z%YIGOP;eHPf5sPm^Lo-9#@8)8i8lOBDyW{qQhl`TI}H-YqG-#@4IiMSa21ek^A4Su z623qXLOiR*k7&FkP5^AjL4u>VOhd z`Ieb4y4pvZyPOCMa*+l2Wu_T~5QG9P3=7A8frzbC^)yOuPZ zJ&kg?*Thn_GW?3bKkr!#)~YC6x0RxPstelx0DshtoUZD)LK;j^&1p1mjJc9*zslTO z)MK0$g$Hv#j#>@FSW@Xp2|*QOWo><-qH&2i;gwghaYeq_Yq8y5$@L>pv|XTx7w~Vl zeH`OGSd&JBxt^`8ssmcQDmYHy_|uLEB(~pksW(P^WuT&> z*603)H`Ss%JKJF8_mH}U{>Qg=M%*k!?=MSo#*$@?7}Kcm{y67&kwJKpD2;+jSkfm- zJ&3rkk!h5CyR`uCw1?P4_V3#+wx~_>?6@&3bu@LyQ(M1DD@tRp5{guSTgu9olTov{ zj#+sW=5TdX7_49KO$gUYkf?xDV-~x6Y^?~WY#WAkVAFi5@27ycNI48^VYbw3C1Seh z9#0GddOAWV31DJ>X{qX0D3)7yT9c7z+msBqeuz!!*X&i2oy6jUpcLJ@_Ukoc=$nXF zm@3yuq|h_=a@%K2>tkw>!gcHd)_DCN=ce3LWi+g=YyZM62)gYrG!1y1;_44IHF8CU zbztad6yX`A>{>5lgIT^#I@XKdOXm78*DMIqWKXGZTceT_H}@IZM=?rbGe*=~A0=*s zG+?Lp19_*SMmm?1oeU-Qa-j=wOoIcE@XkbPoecIbIv05-Cq*7m2{U=;X-mOaYN($5#UWiK?MhXoB^qlI?6^VIz615* z%=Z-|49~W{$4%CZ5rr!DQCPepB4qQAo&K^D^-aS!xypemD#~H75u}XZhOi z&__D|_+BGW%wHCU1|JQW(JXy#_vW1bcq21#ExSr!a>rNC2F>QW`DMnna3C>`_=hq( zo7<N{Z?>t0I6dOkTMZ*gT5g7Q;N-rqVaVNR(Bg zTrQ7JE_=6Nlm7b9g>MI3*=d=0g_XgukpG1Qg8woCvIy~?`Oumo9eyNt}tGuA&HKUEh%4C+J0WhC)0v18ArL*6&Aka zP04&Iensbjd5^Py6ih<<>fm|0Z)DuXpT$p92?;S6O7N>lu8Zcp%71k)eqjH{!t0axxz6qTxBT;(J7%zoD@7ySAuO=i>US8X_?7TwOH z*D_c`uCJd?OI2rF?r=`*%(N{h6YW~_mP61B+Q)K1bsv)Ivy-(|A5QUWy~q=jH}w7B zcV3MjqnP-GJb!zsTY;VhFZhQ4mhX>~Mw?z7+>RZjR2I5^>mzhy4D*IWfc$$@P+5;h z%B@++F?ER+r@vGC`z8AmFS)gRz}!m7b@If0OR~_!Bc@WHQD1rc3DBhnbE>b^+gMHv z-ZZvV-@8O6P8}WN-S3u`o^qpns}8NGjfOjJ%O<%NAXR9#;DUS2MdtK%-Djri99L+h zfs}{tbzRNkt1H!U+>;m#K09ah)KX_Ghl*;rZdrlh{ah3C@b4L2AN$kR3k=6bBdX%~ zm6KT+Zwxr0 z7p2u@sHl$fmG7tw`Pcl6m3b3_=Ktm~Lp5cB!kOzNqYk?s0XP54%g=t6Z!hSd87WDR~8h93471cE=l~3@Z65+HQa7 ze~&fhUR@(`dhY$xyF_UCu=UWg#V6*vwy5EKlYnfBYX#`iC}YQ_T|CZXp+nc=gd6@V zMaqmmEMbcaB$e*eI$m5XT}w^d%(Rs*$lKW?(F}M3X9euZ(Rv&ung{;wHY}=#0|TK- z3^33$Dx=9EGQ!o~ovB^O3a1ddIMXdWR6#<9+BD65?oIDg{p)6V4}6BxF(M1&FGdmT zXJfHFluD9AA2#xcz7Mk57}KDipp0 zFY*j4c~}lfmXxkiv+vAi5UUCbehnkiq1&av5ae=X9jTsCwJiG;U+8|LLhNvGWD=yQYRK4s3BKR8b2iU{W}t=*n3>lbDt&sJ@uWLCXn{Xo zi%7&Q8QHdF4k{`7Gdd;24f_ZpB*JW;%o|D-Dv{_gF1L?|CVw9zP`rMnRfhXl_rP-N z!~}T?fl9-#ws)ZqjPkO*4aV>B)}h%^cpgGSIT>~DQeW5$3|=1gnGvgbwu$*;)+39% zbU2_A_*9OXx{Fs~wf}U#!<*WN@>`GSV%j6;bdf&ceGvg`(n$$45er>?3~x@=dx8fw z!&Pn9gPiScIGv0pJ_@GBVtctVC2Ch2(q9p0#&~D1q(54|8S8AbRP*9JOGS%^cwfR& zeEJ=Q>(@F~Ua);&8fU&hke%u?Z3pD}MJt zFISfgDfGVOjnOoS)_%NkgZjpVqq%10z#<1j0>h$QZToz_GNFG%wdl%;Z#8bv+22IC zYiJHUrGqCf+*exLNgEzSYEzuq)R8%JnVdR84sSAK#TXFDO4(8I!uX^Xtb=HydZb*nKWEk-*{RZ|9nmt`E(>$GuqclQICs-Io^S2pao_h1Q z%5;~)HpI0QP~~6RdKt^uLdnIPCNsStFICoe+&fTyPEpZ z+L;U&F#=Cr3~jw=?atCq+)H#YHjNhWWy9oSLQ4X``D(N0B;zAHR_ zrOE$-kDcxZ77o(xk8iiqq%>ve*HYx-!{5q-0+NB%Nn^ z^*+>LD;`gG`SVxuX}@YbF#8Z7mQEoS!=aK#1qQrQ+{+Vkd+JxRUzF`zUwm@)@p>A( z{WiM`+u!u+7Qd)j>>s!KR3*uUIE_;K+|;#<9=Ft@)}QS+X(YjMW%f~w@Ab>h?O`6Qk_^%?+?%jh)^G%|Z&sA3xSAKch7cDhSSyAwYS~k#n%c&hvLNm&q?$t%MB@V%~-LjP@S+M5lazm>?0d{rc z>79z3M32swzD_H0I?DGM4gaw4)TQ{|+8!sS_n@(=F1|IMb0v$jAzfwLRmok(Yl;;k zK+Uenk9oJqaXY_ooP|gZzPZae7L^pjTh5jrzjqbB!JizTe|z%<9W9~yB1Ww_kh;T7 z228Ak-bcIK^EFp7nnwR}NaxXhh^BO!-0!vj@S-@kT;3kRS6+;w$&Z&Gr5^;3m7m3jwJe4LK#iY&;b?ICY;i>{o7z>w$ z1&5^8$~KIct=fARu~;f7fAwcN?E_DY*!)Z#EF+7!R|N2eJ$a&i7&r+-3xi$pP&;XV z#xcszmRcf9e^?(@-gq&=!a*(rII~AZLg(p)CsKFEh@=4mWcH(+t)(VR@0-!aQ5uNFP3^&Hfo%~I3`^8&snzoh`m|k&R6q8gvMQ}#C8t-t4_P4uYO9WJ%FM+- zG}@*5M0T={0E-!!wOCoeX!#fJx%)-c0hV?Yuy5NadM+*t}NlYFgWnZqg#e zEpSa$H_yR{-g!D=#ka|286QA~6kAAT06A~b7p8HN@;XO;J}$cV6!3t|qbpbFUfM<& zz)1 zgTCIkmW-~ZsfUC|)=9E$IioA3KIWdQ+0V~x#hR3w-ZPAgDKxk|-LUJmfuF~77C5jI zYjhK9d+*4o%O@qo0!8V!brn_fOd>fMcGsR6jlX}Jx}1_jnE6hfdUgp{X*SY9Q<&+z zh35fhWdqjRa^xD^X_jd%AF`W>Iq*;+Kyqc!NF;Oo=9$$^uGhkZIFCXgi&^y}?NzT` znFOPDX|ROmBnf3(S_n{_myA!ZJKKUDcAS35Ka<*7a3b=~OuJhc5PuPtgj5cl63Ibv7vp?Rf2X(le zp9fEvLQ?Fl7~AU_eCd@LwX0)(@o2n%Sz0i`cAl1-z%CkbO*5}FgZ@gR_01~?| ztM!20s~~ymE5OmsurS;+D@%?~5E99@z0l%K#C=(H;F;c}h$YkQCvUYsOrH{d-!!Lr z@b-D(`ip%bK>DfFqs?WwU@m8ES3MfN6Tk|SXwmeJnpZ6j`}nzdUE*5H1Yv9_z@7ee z{8wZD1EFdJl<9gWP9=G+F-DXUj)@N!ZNo===>x81z~B_t^5FX&&d?)`+fSBAcc$p* z+>+H986nPSjzL+c%fDNWM`bY|tnSQA{JhK9QBQ8)9Y9W%llvw>sCs~$y3lCK&ZLo+ zMw?_=^F#zGs7|1Z&JHkW9$%N)w1Mmr>GK@9|br!2*<3deWqA> zR%EFOa3SMOU;mz2R}Fp!5C@ALTum7ui)TOd7hkSZI}FLmiPDDemCj~)#YcH3Ewz#* zg<2Ki78w!=y?1+TRVAYwZKma^(s9AWWYo{UJ968Usi731o5R%a31yDO)NHtHPM<_N z;aL?`1t4-Pg`S&z<9Nn|hBkfhYQqK|AUEgYgmXqAW|2;Hp!Zl>DIgzSszfHFDc+Ni zRYF1TRg=lJ3Io90@sx6GkuXJf#~`a-QpHRI;E@qM#&w>)R;u@Yvf7(%-5U)3{!#eO zI~drer#eHlo@*#dwE~$S(VYod!880r7fN+OiSZiBUeBg6P3P%Xhtg%|ur&*Ytcn~F zr;#iaijakX_;y>2FLJFWP8FTVg~bZk z#p1)BJw78ld52rz2qeiYy5d{XklymE?F(9Iyl+t{Z`lk1nX`lNx2gzNeON1Y!KJ)m!N>~LJKoWaFWoa@GJ6dtunmQah6j#qoa z^7L#tjqfh;{xE%Vrmrp)lsW=erau&5s;a-}?CCYD>OzaUx6-|)o&_N0aOpIm-8au|= zwO}R&KcQ@eLOZd7WBI(H4jF4=_C}wdGOYel-5tI)_@vkY*o3ghZg_?6WW1Lq2Ex;= z@>>^&eJqmM1W$%lP>QR5u9s-DtvOIF6Q|c7gp+#D){CsU6zuNtY{(z56pYXs_Hn8Y z8_h9gxz4Gc)hLbESZ6L&;yXK{G7_k9bHq>a66Ca2XvSApmTl@Mxb;*@nk>8~ZY-iqww?G_yqKqf%^P@V*ES++11G9qv9usl~GX z&sjL!l5uUVS9Ci@|I%*~+MB_^|& z=#Z7J7jaNThK_umg|$Ik{8;wO?peGC)tf|6`ugCy=RL714Y6vXRD8DhES=E&i-#GN ztl%d07&Pmh@U;?4T>5qLr!@EM1ZY9J=g}F(B03fMq4CATFUv@EhGK8V!qd(9%2hxo zc+8W;?D8R`B3KTs^podRX5}5 z=qO4CmNMaxpzws~F_p3j7cPW|r4P}BQ+IVdwmv&XBJf+JA^0S>`KhsN@8>S`6NGoYAVdwMe$ke;N}Hx1v&!IPP94oKF97UN;6GzvAq1z zEg}BIZ*=VIk@S&YJFvg!jNRy#l>EMl-5S(vYKyUIN;mjE+9&jMR|FO#WO9n1Lt;T; z&j6$7un$Z0a<1Bi3Enpd&1K8%=0Ap#%7TnafPSJ7Io=w_X6tV80u-;LLs!g?(F=>e zb^*jv0f~0R>9oyZwgIqq^cD?>6o3Izzl7l z2sWH9VTHN3;0Kb2>2rH(w-gxNg|Dzo+AAJCs<7x`qTwrOG?wbnU2e~$4^8mo7d%PE-NTmX6; z;^OTr@p#4eM*V5~L;Zp{`4-+hLkDiWKniHD&<|()X9+f6%6ev>o(Ho2XgZHX`o!NCtmN!$%*0)8iDYL!8jKX^-FPWLCm6Dvi?gZ0kB_ipm40uh{X~{{!D~lbxS>aNPCs? zBG7mU)8*0B;~M9gO)PS-fZ`IU)NAAPE;BV{I_sJwj`eZw*5@^_(z4Yr&|H!zgd--&UaLk6W}rp+h50LBLOfDG)eUVxZ&>S zLwiP6H+ zUi8_{e_~Y>0-wBBLyTVI#>2cC6&-?Ek0kZ-5&^n=lnz}NkZ|kS`@Ub=dx)|A=11fQ zq6len@e-DBG^Iv$sC#RiqX(^fHS?9SWr6eX@gy?;nFYbpbd@w|epj`qqzXi0pr+vh z`DsxS;op#FkU$;^446SLmQLe4NXpKcmG-uwfd~dWN2Y7vNRLd$PqV%q2hwE-TW4?s zEggxSh5cQmwd#B9%P~BB>6uR1>%8$VaCl!Ty3DSjfuZIgsKlT3KyyCFP8=$@sn{|e zfZ}NtBik|a_-}XNF?oZCmaeTKtuR3t0~|%UQ&Oz$nxyVDR89War5aiSTo`h{neGLO z{mxt`K6#|_as*`nZ=KcUtec=oaP#p=BPB54pU^`xUG?~%*C`FY$KJiBdn`Cw0Q7I5 z>w;awy12a>_=q6l z+0QvbheHC|fW&-M8-~p_OVjMABpML`cM0wq(1{w9|2T^whtI61-@CkgFN32;V}jo_$7He>Ru$MDBZg&~+{tx8cIlH< z8{(h3yOj|SI>gSlZ*fPLXP5czzH1otPC5?vxw{>hWQH=+M&8>Kvc9ZKX|*~u<^z2Z{&aZR40&|rATd=gs*VpeChjLWRQbde zDPiT95rJ62BBFedRh(mO*s!TB{0pxS-h0A8ge3pW9RmaRoe{==%w!PwxHplj>ZD2O zLu(EiMqzT0d0B(aC6JXN3UsX%RY$PUuq)1cB{($`6lk# zE28WK`6eJKxn_OBAM@!XVrC04`oaDP z@h64bTvXqyWK8`9&;g%oaA{{)MlsOZ0kMKW(c)$YB)<>Xgo5EGqGwDV2rDiA<;^us zwY@#B{O4dX0d$^yHljw&CRm264Mu6)qd=>4TxSODrNkFd6h&kFd}k;xsG$5ADq&DZ z5oVcK*rvkjTYEjpR)#o5q;GqU`pAz;cenbTbF z!K&xk%o2}yng~ndkf2{nB#3YYnC9+HFYYNn-uW`bFc5EELeUCT$}W(NZAoTJl=H_R z5tu+0xl%5w^6DfHqzW=%+48NZQ?#!1JCJF(YqNHnvM?acN@kSrpWj=rEXY~JE0QLB0 ztV4!DflpVtt)|G@RxWmf!T$00FQAiW;+^+FC538u;O~FB0RwyX_kjAWv;b)sNLUDs z{d6VhlxH~5ykUvx$TVOUdW9}zk$`hN^hiZoX0J>VHe8G0k$~KzlvkzaM>9AU2;(xS z>46sr)X|{LX?0xlF>Z}bqY8t0Z}u3 zE^EtQiH4nv;npkh4~FeA8Xo7}tH}kDF_JOph(HtHD_#UMCDcHr?2x$puf1onl#mK` z89@^&T1{K-cG9by1!8&qLxFEU@amm@WO|GYj!9NS4(&X+JA#57+KL;OTcYm=0!DQA z?%jU|gak8#tu;LKy62xZz3uSK#whMa|4Bojw=mZMYkjgdPRfn|7b)?Kr zkUxzA;m3vH^jFE}d9N^%gKhW)#48P1fk0`#fAYwDW@m(ljR5jZ-19>FesT?!$PU%x z!od|RabGmZ@`8#b&TREun%F^hEN}HlunKEl$Q6y2as=^N+@ilmoM5>|OOQ=F`%cr$ z1Fwr3*jQy19*FX-^z%(Cja{vDTeJOR7Vl2VZgg`cvaJ!JIQShs8XDP^+0Bn9#hLUH zN!Q$l`vuYL!lu`}R<0xO31n!9C>gUCkGE;5{c~4{w(h-Ig~3FhzSabA(F5qf4Dt13 zHym|{%mrL$_QE~SeioVqNeVs4(A}_G-|Hd=yk;+{W0ZeCtsRkWZ=g{^&M~svw&f6R1T3vj*I{r7yz-BvzFij#buHPCl zh>Xccc)nq1U_C3Q*e+j@&j<)|bq$8^8#&9MOyv3Y^cyRf)_`II(e(k-Ohw1WY)ODN zEU~WSm5E>h9Qa@#D9Nle=@rbb6EU>33d-p}Ah%Km+CE6ojQuSxt;mtdipT)n{Vya= z|1!Af~>5CF9@DL;SQP|bQ%6ea-(%}+B+^^)&1UM0GS z?WFf_SEf+-@~7B=f%oryF8sFqX|4HaR9MD`#@r5=$tOc*&Uo<*fNa18rtGn6ETg5b z=RQBe`|+)Sx^4#N(=%IbnC{({H%F7DKJ@~~Ar9~vFi9;LK{WydHc_SvX)d}&`sGOK znJ{CE$0tV}xM6|S^B=4F`o(8gopl9MVS6GGKX8V+GWUPG!6$n)^JWRM>wznR9AxEp zUR!~gPE5XVQy}j3-EV)7CfgU_?z|oVdF#wK8o|v7Nlah3(ZK)6)O6C+=XlufTptS6 zAHu`&(UB~!esGKjd_(a^8_?G_t{AB3M0I$~8A^d}X|7+ZoO5A;^bd)Qp$^@&2i|Om z$R^+kI1@9JKmuUfR$x3+O@PzCi!Q{2lH=o~VBTk`RF&kKA8tdwl=bpJWC8}{g*Mwq zPo%PLLPG_qzjwDwgmAq=*P|mj(f*vmz#G6O%l6<&SpNH27X>dhJ;(@26?Q);%s`rb)H|RaY55tBX7l|3V?*jtA4#MNrcL+ZO2>Es8Ae`ZSO3o0>{P^-)6oo=3kd6%!pVIcbWe^ zy7LF_WDuYX8GOGQ{}@Q@Z>Hf#?bmms11%4;h`tbDrGinv7{l3gE^=isdP6n}a!o+j zRaSOO|M^+mzg7t&h&MPAdxvDIKp{KJXCG|L`2ey65sM~B$$?h63oLpUA?yqB%zM;2 zqr~$d!1ZkM3ZSy2j+?P#JhEv=DC4yrkYaehocEJKj$QMLF!70LDcT7nK=5QcnX`Y0 zz)T}7aSwGhh68{|PpK^HOU>fazEW8WP<{SKfy`c+2j;B<(I1Wnp`N=1)rrWhjAWT3 zh5#Ve9J=Yi8CJLx0z5g`SG`tG9pqgp0TF@%7dWgR9P4w-9F5aWRj7YUt$Ods2)W3Z zo0>w8stB1nz{|s6Rn6XpvR&sy3=5@0J>YTnjt9u?li0G_l+OVRfyfX|JdZ^`QHr)6$~ky(ZMdJ@%&KAv-fbZX6o zHW#;%%MU)Ki&(KkKGe(5b0U_i=dyk`T;@jM5hw;jp!!k{yE+Zx7Xo$wV=Uv&`VLU@ z%m4i&h6E-dMH%2JwV@*E`Qk?C~YJ4ecel1Zy& z-#7$^7q*y>vK_qk1~75Os1iV zx}Mw9OQ~Phu*)++kFxJo_@-UH;;ltvXC)oIKx0(H&-IXoO#x?DA=~{v1#DZ8V^Kz{GmdOPOlZr zuVsw1hc*p?`>dJh;tv7No%^uq+h-96IjAYY#}TZ7cB;$P^KIELy!dJc>IpfsXP+T|E?PhUTdG+MC{rJzZOO^85%1e+> zO;((jcYC&beuTD;@{P#q8@1aeCEG3pIEv`J17%dN==D^C#22L;bXv;4OF6#@k!;x4 z8k8e9v@oOV{79f}oFjNbXB6+aEnx7RaLPY9BI^MhIEoz*nI9s)TiQLe#tR&0Q2PAr z?(0d!-V#|h2dBrFo%Pr4`5)|Ey@_56-hA-5NdxTW<=n(*qPJfi7n(~iEN&nAgJ&bi zV8O7lvp_9Dn3=LfaCgLYx*beu@D{PqKYeQpU@Fy%xq`WQ)H-UV3zZaDh=A9_&<$I} zuNyS*=X+=L8^uyZ8{{}?q9^@#py2_HfmGIr^p%dAqINdlMSiIX=W?YZA=0| zg+mAPp5^q!9gl%;7UZqO%9u{^y{8>4L7U2QD)C~!JSU|n>KyM%PWi?Jmc-|k=5$|7 zBPUP{L)xFoCiNQ zu*X&Z22EvUNYpbp2zFYyT4ip3j~9L1i8DPteQ>yvvy=1pFy~*_@n=_iwRwa|Z+8D& z4ap#Qbb`%cuhmYy8TT8qJFh1*V~AG7cE2-y=f$(J{pg$~SYaV4Zm}x6XMCBl<%4@O zh<#EVI>wsEj$I6uX=^a}>vrdf-)J5=TcHrMa0A&-{S4zskzyuv?_SX%2rfOP0i=@&((i9a_i=QyC6*?q@bXn z)GNiZQOpH08X-csW#&XVnLEte)%-8azIYOAuA(QJ9m3b^K$e>w;>}EqxGNw!U;gK4 z6hWh_t;v#IXzxSsu8_mVzsUa-iTr_W4YRiFDBszG-uIE;GOuF!UVw24m>zxqu3G-- zmuD0CqNY}@(czQ)2(;E2VR7PB@1Hn0Z`)$?uFj>eIdp`G{q|#Z(Iq{(qBT&}3$=N89)aERl)xJ?XhwE}Fnn?sj|@bKm(Idi1y<$g2t z77|~?2Is+=0t46y@jPDAzbyLtzGCa(=RlM*N<1MxdvSWXf!panOLfhOrTyP`UYqs) zb4-*B?PSAJw7+ye*RcJyVfbY0O+y2suK0W0{?D3jd-_59{PuQYj)CS;{VAi0PT1%Q zSSlh$kMQcL6Z+?O4+W1(sdG@v;yv#9HN!=%Sr7G8gSq4xOUe)v)9 zK>O^QZv#YwHQ@gOr~}}Hh&dYqt009CYQfww?HYRB9$daUz@(j zI4$0?Uievk2}#PiFO&jeKx>(>2MzUj&i=%rz$L8zm{K5O`(prW7lZY z$&$7mlgmiP^&6M>+wiHK-pq&4USV~WvqL)I`Q zcEor1-o53IeO@U$cIjA$sOKx9mAYuc>ord-Bt!gvNNwbi_}SWNP6>(mETSJtX=njpWXPnqESlW^yaTVj=`D(ydKd?NalrShkf3%b)qafB9QqV zgZ)oZm;ixVNca4oCCNF&v=Jhg6xdt^GCi`fs{=#N0S|zQ0wCSnv(O+O{75z521gl? zf|&jNx!=2Lx&znBwE}=^!SABQrjKm+QP0;7xyT`d-%x2H+D|ZifaN+DqSD*nW%U>g zwZDFyH>gH}gaC6vGD+c31^YcvRsN+M(dl_R>yZ-m6+Ck>?SJEmsaWQ~7gFZ_{-f4} zb4fj#^7$J9OeA&S+%GJxVh$8wtpPvo68~Z*n zfZr-zRP@KicSB~XoKGY3Bl^4?>u_?2&Cn78_z)Heb+8$eFlP)0>}v>60*x=!$N%{P zlP`8B;ca~RS|b2M2B$5iI3mPn8ZcN8i~Z*?3M=E_a z1B+G`M?AvEozS=^J7}z-gQ~)-cU{C>Or;vdJ+lw~_d^1O0@y!NEy?ajuUg!xq z?I*vP5Xcg7H3f_X5V`(^&yjtC-k)VNhmn+*HP4KG+eQgVm`zm}#J;ATKnsnkw}{Q& zhvR`GgCB>n>E)M(@cG6keKnD;&Y1RS=iG6H^{z2Lj91OvlT1CeWaEUfGMD_e_K=Xz z;nsfb7mgT1J0c$ua;V$by}lstjX&@Q@1q4{KIc={_MWO>$7u;T+Td;n_JKE%tDW(} zjwApZa1g=}0PL`QE=)%LF-cK>oET zddYh^`@GOwC*^e75l~$tXQBM*0X{P7gT5F}6LGCYruad;x2N;2jACM9NMttHfs$Zv zJYhqDmo{=7Ha{S_TbA4>mTZSe-lmMb3hA<$fpZF1Xr%Nlpa_U)EzHzu_RdEyN5Jz zVAwrM6mn6dTY|Q^Qd_HL@lTJmaAgR?+--am&#!mGU#m9RnWvQ&=LmDL7FUZ`Y6lCF zQgxOCa=k3lAFwie&`h8E)cj`stv%~z?yOvx?Bya=>n~{T-h^8mu~3;GHtBQz^0;1gLj|eJ`M!J8&80LA2{ECTMT8&qvYfhZNK@@YNqvGmsL( zYC_I6FqDT%6#_6r6mZJQ+*2dp9LBv3qC7T&;&rkGmOH=;RdAXnY zvfp{^-gl_h4F#5)=EZxyLW0!;D@c1%1KBgoib{&b7fjvSZ#Gne^EMb5`vCk5wHW+( zTMw-4eq+k+txQff*3Rb57WiRZ^7y}bDWnh}=L~$G33mb902S5$mG>R~RQ}=nlIbfh{&WRpG0-bKS!_8zIoNEy+wM;XT`hpg}Y)bICCe7|1L ztCwEY$@4t#_xrx@>%Q*mQkdS$ag!>5N^aLzX?_mR>=9*&SO-=PbFN1yz{tucvj#Ljyj&w<#U(N@rW?>GE+M;!zbBhe2#Kzw7XJ0MD zo0$7>8e22|!R2FC!vd&36!39%@qu}>%^LgEh#7^dyH@$3IQDUm7Tw%;8Bz9~=>=Zv z<2wtSmnK-nA0;#~j}Kx7{gQkPAejWIqI3_DvHO=kAA zrepR4y?j@Bg^LLok3tIpP7_cYMm`844;;c`!#jJ5kPefS8zUvIhJ&y*K-Tapn16S( z#8h9G7)r602pbtOL(JTgy4H*eyEe^iB)c*g0QW_Fp`k?t%H47L1#vphk#FGoU<;D1 zF=wi4r;l9^r?fYDxp)q?BkYwJ6Nqp~G=L)=gq~x@G0b=^K1R&cXT3QF=Y8 z)ya6{Hsha#GfaJo-~0z%qYHTp@_lR-E_D>N@;v5^%eqi}=j3)x+NCdO0k2;+{0_$t z6}wQ&2Q1&O8~YO#?p-y2i1h`LkWX)!-hpyYQ8Sn1&4bME)igA9mG1#op3D*kj7@LKeiPp!J4Fd|dAcr!vtYI=U>({$O1aByXN2P4&AN1`M ztv&nNbSIC4?(iu0nOB4OK3DGMKJNkkvCijGxdQxLy?jQEkEfITjrq9n$^V2b77)&HUksg0EiXh1@4<8lc8f zMmKB7v#u0!Te4{oP+jaO(v4w=k030RDx^1{K*?1*e;KQq*;6Z4BIZZ4Z?BF$ z#DuKKb6kwQ=O=Mei#>m0hc)+Sy!(hGh<9*VdHY(hSHoe$rGL<1BUlNAOZ*t-^M7ceOVcsJJ6J*X-=< z%|KJ?Lu4f2Y%Fs^I)S&X?S5|WHdfZPOb;nXOo!NJibv938KzNwvqtx>{yXV(kIyP5 zCSb?5lHax@Vesy7SGi@{@Oe=m)ip&`ltx$p(=CYrbvMR%1Amci9Qrhl%Ufo_(Xsiy zi@}qAY;@FQU%T?6HN*|ZKd5$zb~$R}&imF(7Z)02YojcnCeIsk^YS~Fj;2Q09e>QJ zj*eG%+dOyYiSzr!E7>7au}2R)jnsT->#K2P;hxZJ0y8Oq^GYtBJ*vSB3kT?KAYqQc zv(QcGd06I~R_dE@GjQi1>;5VHx2D;S#ruP$m=hk!W)>D55P2XYClw2c$*m-$z~Kj* z04|5#Oo&8%np^4>Kb#)STuhrtSX;Vyuwu2>R3XI0n0HBJ;^ULk5O=}ZxKqb|*aP_F zql^ijN{1;_DAdXKK@QM7AeUldW1Cl73igT`u?nig%-4Z<7rKza;6Ybq;#>5#rkx5Z zeW;uWXhaC{En{CXF=w>U#*tCMHRHprAr0-2pE2mC|M6OwtJv5ul-;G-U#&j-BuIXZ zeQeVbxo;UxpzU=^5n+xNj!uvnM@(vW*So}bJxTR37sl--Z$!*~U`g)62oR{QjHY(Y z;$}Y{g(%7wr*&yC0~_Kkou%^}DKJ3d0z_-mk~~oS1QT2q*Mc04WP~8jwA;$m6bvjq zT?v%l#V#jppqTj9ZejZKMqoyBazTiEYrpNBgC?^S0C0tQmi}yH)&l%^M!vq#>i&^6`C_F07P z`@|A-W=gOUTd=_CX{KYMwOR2^A2ap%T<%Qt_cK>r__ecF(m;9T989l+6ng-ir;vo@ zj=1RSWoHJ>PiTz2ErM}-C@Itxv*yiY>jiIWT~{2|1Z`3qCnp+sW=LEw>Ca?OiZkeV z)^HWW4aYO^gCJW4Q3r5u7z&d2hQkPuxGS$aybJP!>CWSA2V@_rqSlr$UoO$_U*dYK zL_z+@mFdE#tjaSzS_YdZh*()Yi$H?23Q^YPIc&aQX*+pFl;VHB z)3rK!@I~I5ITS}Rm15Rye4E$ETtz`z{+xg8vo)a*_ECcmb%_poe9_aZpHtkbz%86P zT-|a<(VoXtgsl|x;h}e=Iv@l0|MK`d_v7y3UqGo}S63G?e$UFLhC|^$iE$D{Ua-iZH zx04Nn2lY%@4Ac*#jZ9cYCVXnoAi9v>C=@3_0ET zl23FR=CU8H1)QV}hKPn}?ye;Grep}^lY^m$mH=|gVCdm4K0m-YK;7!f-=*V#Xj`p> z-SPDF>^F$pq8=;2!9EvxqC-t_sANQ~*5U3#Z8U@sXso{6*lWVADc;Fx8P>e4p{%Ej z(s=)vR(oBALA`I-&)>IzaiGtdY2-+9dxQkHk-~z?;-I9=@3oM_yW3JpDgJ|+RbFHb zqd`uNaB}fk_5X-09&x(LXry@3=d2$mwv?}cGd18k8gI>K3vj|zlVw&ZC++k4C))P& zf7BX0^5l{Hc19QF!xs=I;B$-Yl@k3Kl)nK(ZX-$fK@sOK%M+D$(HR25i>g=`t`dS; zlQavl^1K3Jav571Lixsx_4_++`XcF`KKabDPOc+bH1P&15~-RBS5*L4*Yi5kZ%giv z;|dT_K{>i?3ru`{)S=@re7C;ph!vO?MQI`q2C(>`2>~pV%pZM1Q@b`(x`u4F6=sO~ z9dx#l!WoT1PwLknefsbi1ys9We2K7np@8%WLv&Nr5< zbJXrKyBxmix4R9`0knTjnc{q3*f3xYMC=>(p#)(a` zMGrWWv1^Q#-cN1A;)Al20|Z{jKK%GzPJ!!!$H9>YMQRJ?CkFe{vvc(G?r=LhHh*~_ z@rE1YA<_+Bd-csEK>*JL*HD?jQGAJzp zo4v#q+%CV?Q*D3)?626e;W46V2`y{ortz#=md8%P9ls%S1lJu9@MQ(kV!vZz=gxrM zahQmJbuK79aD^DHeU#{?ZWh8B8ToUv&0XE-2$wqdG;aRC)VHDwd+eBjN3IOQ`Rs&B zz2w6Thl_STieBLO8a_IRdQcMVyW{xf%Pl5@-~L(j?=HmVQfP0g(0~zjer)PXJ|lC@ zSJgqFY?;LwB(jZuoUp}7UD6t@Sod8ltNc&p5=~cgl8zuEKm)ZefX0v~C~Qc%lKZ4u z-zx%FGVl$VPmtR4ReI8eB1YBu*h7f0^4mKDG)0`J=X>Fk2G?Zor&7R?{ffnu-qUs zFFE4v*ld&cKwPI?p3$YNweO2$-#7k)`_9-@k)PD1dWRbZng3(}_NH_{FGZohYw?@i zk#bITY^DarLY40-goy|(KBbS9JDMZ+Ri5P=Pb~rAI5j_Kbh$C%YZ98;=jTrZ&w|Pf zcthqz?+z4cwmt-&jOv_r2dHwa!TT5l3P8$aV#BwrlkeHW76V)nsaM{Ziz432MbNJyr@+76{h5Lm58Cb>jyMU$_DUNn<~b^4#cs#+nbPc{2I^ zFER|!I;bt?kzesOTD4~n1a-G9kcRcz43=_D2=|V&G&gTWI9_nhvPE+bMec&rJ$lM9F`wJ-BG?8It2+1hy3R^5?qd@m;{F@rRQN)uFf+e1GjHx<82r&zE@O& zqW$J|VzKi{Q?CKMg!1Tg^e`WcBLHyhjyQ{u zzT*+sG^rGT?epS~PX)TmRqXgE0&52*X8tjCVET-cAY)TnSc~Zb=Me-|0#P>jaOg2z zoAw`_Zh)2)$c_W#W7kcJ_2g-xs?cJw-D0K|j!XN^pL!~LU|8+-CQDJ9=o_Z0erFz4 z;#;xyly_yK3mHNXR@82iWpTDEJLg%9YczQTO)95lySzQDz1{-LgR2W2|DHcpj4xA+ zKfarvO}A~HNhSU$^)x0q=?L#6!x(?@nTzs5gz=EkGF#Yuut&G{NaH~1H+=n7WI)yi zI$jV#5JD0{I03^|NQy+J_0wsVxZ?$ApoazGppXSi&>sf#l`F3=KCj-8a%sB<-Abgb zYie$;$``i~d+-06B-rKU=*Yly(dP-{%d>~Eg8+b7c;&#RFI$cGdb5*PIS?kmmBwK6BPyo>8?cq{VpJ~EMSNvz|yk5 zE`=D&BZn5;kiYUHeNve8{7j<$fDA};K=e2PfNlH zT^#WI&@E$W#1uiA23SH%%GNMw#wZ&$Ur<6=K5dx8M@JsFynDY`GXAW*n5!S+zNq{* z1jm(TfSqkR(BkS0aaw>IT-cL6q)B_Yh^}a~xsL;yAs(V>HmVtNiL_HMB)jKBIjQ+k z`snocQG_fPyDwp!`pTOxV{ON)6*ReGp7MnmFtPp+?%1i5$&dmQFrlkpK>hmbR&&9F z2K~*rV?hGiK93mX*$+m?_d|ebaY;nIH3KUL43JzAIHIpN%W*R7f*Lj$vyZ99-)Joe zJ*zL6pbavHKHIZN$co0WPdA9)j-czFu*Px$nFMzF$*y{X6Gr@mmLsnzBEMf2$fC_s zqCO*WCqFztVnLSF?|d?MTv5FV7zD1-<s*y zQNhnU?tBxg5VO|Z6F!fDT3)N17G5tWbsNuPev~$vLm$m?k)N##&DIMII@RNKonbdy zh<}oLw!B!{>)P7ooy{L!U5jS@6blZTL&X+qdHv1M2=qPJ!vm{*qjyqHy3w# zQc_Y#r*duv?)}-@FT4E0DUxU;NQmJj%N0;XT^KOio20mr!a`}z6u1`qY_nfB6&ASS zH}0Vhyba?Y{@2>`iF7>&-EO&ZE*$nJgR~3U)TDAD>#$P!5w zkYpC>hE#UWUbNYZ8+}CUzT0`DV;AZg<%@NRtz3?IpHp*ax;R_QLZ)xt-}#+&cYW>- z#OEk~#aA9fwZF@Vb(Edut1FvwJ>j=c9@t!G=MQIr&zvzr?`WFc7er}F#H(U6)dbE4 zDhrVNNzi`f2{B+$yu!eJ!K&2RGLj`HWAARpb9|WK%n0} z{CBQsx+CJ4k-VaJy!C}C=y!m)9sBp-#llmVP8A&gB`-nM5Em zc=JXKWh1uoj4u}FS@Y@rRRXzn3gs_Ly6^T352|RK9U~ht)F_D_si=+$+!Cp^?7=-1 zHeOmj3c~=AZ~!xN3!4kJCXE6v9pZh_LJipSZVnb|v4DQIfH*dveYCAvu!#1w(~Id0 z%z<)WfVQ3(2JIK-iJw+H)dWERkaRL@2L~{rPoUU6+kD*)+KpgrI5x(Gl)cF2uLgJy zKs#d-!E0Jm32piT6e^&EJ131kr}2fEQdsz4P0JpURTAQm%#l-xb{ zNq2!DELPo7xAW~Hf0AZ>%$SqJt8gkZnpo3@(_C16wiYdxGMD3OYQQs6TRf9EMc$9PYksYHoLuhHyxavYkh$?MtZ4yDzq^2*2q_Dw*5=={+GD+FZs9-If9mq ztZF){)4j9`SuF#P%rF(%*z(2Od4NaIVc@+xagu?;&wb{yU*N6+sX?s&oYi2L2am><&Imy;n7)QHE+G+QEi_fj*QH zGtk`ECn<9`IfT-qFH~}&<;#u))kFFy@?#rqlh$Lqx0KlgAq|8$vSb8Uxy@*yOURSv z$fXm4<01)Sr}xViBWOPqfXJ_D>FHZ!<(!WUXjX~dzxO^$dUsxRlSa4bdvbP_S9_CH zczRn68`k8C0KkaqJmIKeXl=$0u4M0|cLq|6wvDUhbO9YlgweriYZc7AY@k~T@W88A zmn$=>ai)}tY_vezMivb`34kcKU_xgV#%q9PKxjV~rMhY~RqWn7zky^Q$R3=6!51)! z2Ox##E(=U;gYtw8hffipszxWe#H&2wUeh;?dk^ z%JH*n*{8g&U*5PC%c7#ks#jmJv=b_pt!Esf$_v~I@B&ygz2XY|#|(XkSJJZcQFW4U zuZvCp{Q6G|9$NR{p~=q@4&Gh2^aX_|=g7siS)f_RgU%PBWz#@9dR~;7H(Ep6ZjC{K zXYb&Eymy2P2}X`;AoMp*oPVyz6W}cd#vn%W*)#%#aeAKTr>Rj#Xi%DJ*%Rwo_jlT2 zB5uEndX-l)GU$j(JTI0*ez2dQL^o}|8K-{tqTdEMZqE8s`KeK@rQTR!Qa(^R0vKb4KX4-`?=45;?GRA)N)Ung+0ZZNyE;Ew4vV) zHVTqYj_$@9(Yqfnbxo7wEz{fXY}IiDW&x^?xPLBRA|JCi;IJe=^809NM75dtS0xAow_#hlMQcEB}ozEX3e888f*U1;` z`)9@^pLT>m>iyRi4*XXTa2@m%FtR=v%!coAO4m5g1N0d8I&ppZcEU`_L0!Q?I9icj zT>O+<30{bB&q7J)nUXQR)Z|MMS`ylUPKIvxsU3I^u6Hzd5ogWX$`hAF6ymh3@84%f zAO~bp&l*4`Kp2Lq5*&r=1M1{7|F&3wbP5-EsspyRw6p{SLKv#}oL$9Qo#6w`K5%nJ zdXK=3nY!(_a{^KY$fSD4Tt8aKN-y`=2oRLczInvlzSH{A-gIscW0QONsa^XGNx-z= zfB?uFnXudXQILfYMW8{f*K9S|k=kHYxFo_I1M`qKK+G1wC!u7_ieA{*)~aUB40hGm zFl+~E0z@eAmPLqiKxsV=wn6QH(F5+kA=SXfUf|pLFA*63K?E6V)-1W73;{;MvyD{E=-h1JDA&$+_7A9(Vz)H4U|A2 zNE&1*!rY+{!U%A@JIeZCxwc7s#XG1Y^j~`xfxQ5#MCeH183}$n0LT)&@BFO0Va5)@ znW&v?uT{_5o+!;gS2~SV5IGUzUQCw@bUBy|4Ge;0M-p-pWRRr;nP7t6uUl1@uvD$C zXTE!m{j4O0?nCNj7$QXU)xpyAhP!**P)VlA#)!;>7!S%KaJ&8yuKWAaoi@VZxm@ts zsAl*H*0HMt21=7KTCG6W%jJ|DM*4iuk4K*{UXIxyIYPngcd#>#Fw&3~dx{<;m%fQd zXPm0Lgv}au>>Nde*7Q;B>TTsum$be}M0)I*aU}Ig;MoS>r$W;mEb4)`3);+wfxVhV zQC;C`X(ci2Jz*JpRAchqGCVLy(vYVC?;dzYz=eT4U6 z09E{BMgckMFQ=~^;bQ`~0FYqrwDq&Ds)X;6AE(qr*q|6!h5{DKc*GS&QS!;f;`6uD zO#iI|4i~acF9+kXLzeLZ(39ET_G-j}!Y*?9h+Eb-0~;8=K%#j25~_qdrS3?ocd`!r zk3tEcW1tEb&^Vx%2YUe+ou1Rj!|Sh>e&#bVfz0rH(mjXL5Vb*5&ykWMZ=DKE*KR|& zIv@DQBgWFe-GG^Zwe?wDz_}(D78a7j@X=;k-c+P~!g6@;lo}kxf2RyOfV>B9877^X zsVRE8FV2+!0XN@i-IPA2V}j)0F0X>hY_4>*e07IVee4t&5S}6MBE$;NuLeH}gmnpC z!#4qn1jHNW-a0k!$VX3y+lZ}nB%f!4NB$ZLUZkVryLS$dWn?u09pjP+MOH6}6=^O@l{Y2SQ~- zPQ`@<>J0tR2%X9gq{m_Sq|WM!We7lU7{jJSHWDP!h=n=WCVx||G+^d24d^^?anGC= zR#1>bby!EV-9@u}(WEY3XnsN<2AdMk?R7!Kj1Q(Nj==b<_ASHBNNSgx@9a{N~xtC~BYqK!DXA;ff~o&;134#SOj&Z9!w)c{q zlH^GQnsGyfM-3Mw67W+(<;An~#Zd<4`LmrwI8f7vZMm|_hC zet0QBp+VT~fUSX2dHDzi?oOP)HcVqg7r1gz>q3x$R;7UhnSt!-iUjs4?2`05{i57{ zkzO4ba+K9^`riy1f0&*W*0DC-6z8DlgNX$VEN2uS8!az) z%tr9A=x4PYRBx$dOON`F0H&K_QZPCB7zKHpRuCx`eh@`mF|gHO&=EW_;jtkd1{qFh z2_OSre@9z^DSrJ+oyM*e&33}?xs~LWk7WFD@5?U8aDw-@IvicHX&UgW0{6V2Bt{>K zm5Swr^0J%7w#Kq*C<;m5>R@?R;;{zD>MmM%ZQ?3r08n=W+Y>lZfP?`L$|@2JNe}!m z{Jp6bBY~poD|#%~*?pHCkXem}@ankb_gXq$}98ZN_5R_5JJYAOw_ z3yS_)0)C*rhdL?FvHpNA3e@g!`72K(n>rNGC0tRwU?&Z6{YV0vY40Jtb!Otkk{d?e zjK90xM=VKR=si^+d2cNU;`?4-u}mH*tJ&Eat6aXxV7t^J%UG!RHA5q!b6O#7rTF%F zr)AI2w9#9h3l9bK35VUt0r-aXXSL`DCXg^kHBRIiUB%Z~5Y*ak&3vE5caM&p&r&f= z9MZW7SRxuVF_lhpuW6hK&d*!vxTZ|)DCR^jVd@*FlG*%s%1V!X5`84R{I;fp_`wK# z1)^&Pw|o>=m@+=uZ}TnnWY`!{yTwgRY2@Lg`{Zx z2idh}6_N7#knTRa{L(1+kJH*A;h)6sDR{hQX1F)L5a&0ZmPr)o4AZvc>EUD{QmA-_ zB#gY&o_%v<rO4Wd6LS6oEnG2lw&BdANXCCdNQg#XTDDW^_uk20Vab8*4{=nh> zEz96tdqUP;g5<=r6JJM#_`L=7tEGtv^@{071PC-YKBnZO=bp4&5cV?4kNO&|ueu?s zf;!eVDmysek5MdIo139omQkC^KvPczcnxXXH9uw%fL6>*DDb!)FcnJTJrEB-QwN~K z{_}6&{-u_Jhu_60M@Xrl;E&s8{iWdNMj_WEGaM%Wz7M*9Y`K5mbN>x}f4}#nKyLE? z)L*W3xynaIcF0v*UBwu=@&Bz)W+uWH#uU?GsjP;IJO=00+^o;uz@b$)RMX~B}Z ru8--OViMoP*SM1&l-=w7ba40;r^J_FlPizO;7eOWU%gn>I^cf*qr!Ok literal 0 HcmV?d00001 diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index a21c575..560b073 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -45,5 +45,15 @@ UIApplicationSupportsIndirectInputEvents + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSAppTransportSecurity + + NSAllowsArbitraryLoadsInWebContent + + diff --git a/lib/http/http.dart b/lib/http/http.dart new file mode 100644 index 0000000..6cf8da9 --- /dev/null +++ b/lib/http/http.dart @@ -0,0 +1,30 @@ +import 'package:dio/dio.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:logger/logger.dart'; + +var logger = Logger(printer: PrettyPrinter()); + +class Http { + final Dio dio = Dio(); + + final String baseUrl = dotenv.env['BASE_URL'] ?? ''; + + Http() { + dio.options.baseUrl = baseUrl; + dio.options.connectTimeout = const Duration(seconds: 60); + dio.options.receiveTimeout = const Duration(seconds: 60); + dio.interceptors.add(LogInterceptor( + request: true, + responseBody: true, + requestBody: true, + error: true, + )); + } + + Future get(String url) async { + Response response; + response = await dio.get(baseUrl + url); + logger.i(response.data.toString()); + return response.data; + } +} diff --git a/lib/main.dart b/lib/main.dart index f87b093..ca0a3a4 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,13 +2,18 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:go_router/go_router.dart'; import 'providers/transaction_provider.dart'; -import 'screens/home_screen.dart'; +import 'screens/home/home_screen.dart'; import 'screens/make_payment_screen.dart'; import 'screens/history_screen.dart'; import 'screens/profile_screen.dart'; import 'theme/app_theme.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; -void main() { +const String testEnv = "assets/.env"; +const String liveEnv = "assets/.env"; + +void main() async { + await dotenv.load(fileName: testEnv); runApp(const MyApp()); } @@ -17,49 +22,43 @@ class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { - return ChangeNotifierProvider( - create: (_) => TransactionProvider(), - child: MaterialApp.router( - title: 'QPay', - theme: AppTheme.lightTheme, - routerConfig: GoRouter( - initialLocation: '/', - routes: [ - ShellRoute( - builder: (context, state, child) { - return ScaffoldWithNavBar(child: child); - }, - routes: [ - GoRoute( - path: '/', - builder: (context, state) => const HomeScreen(), - ), - GoRoute( - path: '/make-payment', - builder: (context, state) => const MakePaymentScreen(), - ), - GoRoute( - path: '/history', - builder: (context, state) => const HistoryScreen(), - ), - GoRoute( - path: '/profile', - builder: (context, state) => const ProfileScreen(), - ), - ], - ), - ], - ), + return MaterialApp.router( + title: 'QPay', + theme: AppTheme.lightTheme, + routerConfig: GoRouter( + initialLocation: '/', + routes: [ + ShellRoute( + builder: (context, state, child) { + return ScaffoldWithNavBar(child: child); + }, + routes: [ + GoRoute( + path: '/', + builder: (context, state) => const HomeScreen(), + ), + GoRoute( + path: '/make-payment', + builder: (context, state) => const MakePaymentScreen(), + ), + GoRoute( + path: '/history', + builder: (context, state) => const HistoryScreen(), + ), + GoRoute( + path: '/profile', + builder: (context, state) => const ProfileScreen(), + ), + ], + ), + ], ), ); } } class ScaffoldWithNavBar extends StatelessWidget { - const ScaffoldWithNavBar({ - super.key, - required this.child, - }); + const ScaffoldWithNavBar({super.key, required this.child}); final Widget child; diff --git a/lib/screens/home/home_controller.dart b/lib/screens/home/home_controller.dart new file mode 100644 index 0000000..ba6fe78 --- /dev/null +++ b/lib/screens/home/home_controller.dart @@ -0,0 +1,75 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:logger/logger.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:qpay/http/http.dart'; + +part 'home_controller.freezed.dart'; +part 'home_controller.g.dart'; + +class HomeScreenModel { + List transactions = []; + List categories = []; + dynamic account = {}; + dynamic profile = {}; + bool isLoading = false; + bool isLoggedIn = false; + String? errorMessage; +} + +@freezed +abstract class Category with _$Category { + const factory Category({ + required String name, + required String description, + required String? image, + required String label, + }) = _Category; + + factory Category.fromJson(Map json) => + _$CategoryFromJson(json); +} + +var logger = Logger(printer: PrettyPrinter()); + +class HomeController extends ChangeNotifier { + + final HomeScreenModel model = HomeScreenModel(); + final Http http = Http(); + + Future getCategories() async { + // activate skeletonizer + model.categories = getFakeCategoryData(); + // Simulate fetching categories + try { + model.isLoading = true; + List response = await http.get('/categories'); + model.categories = response.map((e) => Category.fromJson(e)).toList(); + } on DioException catch (e) { + logger.e(e); + model.errorMessage = e.type.name; + model.categories = []; + } + + model.isLoading = false; + notifyListeners(); + } + + // required by skeletonizer + List getFakeCategoryData() { + return [ + Category( + name: 'Electricity', + description: 'Pay your electricity bills easily', + image: 'assets/images/electricity.png', + label: 'Electricity', + ), + Category( + name: 'Internet', + description: 'Pay your internet bills easily', + image: 'assets/images/internet.png', + label: 'Internet', + ), + ]; + } +} diff --git a/lib/screens/home/home_controller.freezed.dart b/lib/screens/home/home_controller.freezed.dart new file mode 100644 index 0000000..33e182d --- /dev/null +++ b/lib/screens/home/home_controller.freezed.dart @@ -0,0 +1,157 @@ +// dart format width=80 +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'home_controller.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; + +/// @nodoc +mixin _$Category { + + String get name; String get description; String? get image; String get label; +/// Create a copy of Category +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$CategoryCopyWith get copyWith => _$CategoryCopyWithImpl(this as Category, _$identity); + + /// Serializes this Category to a JSON map. + Map toJson(); + + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is Category&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.image, image) || other.image == image)&&(identical(other.label, label) || other.label == label)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,name,description,image,label); + +@override +String toString() { + return 'Category(name: $name, description: $description, image: $image, label: $label)'; +} + + +} + +/// @nodoc +abstract mixin class $CategoryCopyWith<$Res> { + factory $CategoryCopyWith(Category value, $Res Function(Category) _then) = _$CategoryCopyWithImpl; +@useResult +$Res call({ + String name, String description, String? image, String label +}); + + + + +} +/// @nodoc +class _$CategoryCopyWithImpl<$Res> + implements $CategoryCopyWith<$Res> { + _$CategoryCopyWithImpl(this._self, this._then); + + final Category _self; + final $Res Function(Category) _then; + +/// Create a copy of Category +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? description = null,Object? image = freezed,Object? label = null,}) { + return _then(_self.copyWith( +name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable +as String?,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String, + )); +} + +} + + +/// @nodoc +@JsonSerializable() + +class _Category implements Category { + const _Category({required this.name, required this.description, required this.image, required this.label}); + factory _Category.fromJson(Map json) => _$CategoryFromJson(json); + +@override final String name; +@override final String description; +@override final String? image; +@override final String label; + +/// Create a copy of Category +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$CategoryCopyWith<_Category> get copyWith => __$CategoryCopyWithImpl<_Category>(this, _$identity); + +@override +Map toJson() { + return _$CategoryToJson(this, ); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _Category&&(identical(other.name, name) || other.name == name)&&(identical(other.description, description) || other.description == description)&&(identical(other.image, image) || other.image == image)&&(identical(other.label, label) || other.label == label)); +} + +@JsonKey(includeFromJson: false, includeToJson: false) +@override +int get hashCode => Object.hash(runtimeType,name,description,image,label); + +@override +String toString() { + return 'Category(name: $name, description: $description, image: $image, label: $label)'; +} + + +} + +/// @nodoc +abstract mixin class _$CategoryCopyWith<$Res> implements $CategoryCopyWith<$Res> { + factory _$CategoryCopyWith(_Category value, $Res Function(_Category) _then) = __$CategoryCopyWithImpl; +@override @useResult +$Res call({ + String name, String description, String? image, String label +}); + + + + +} +/// @nodoc +class __$CategoryCopyWithImpl<$Res> + implements _$CategoryCopyWith<$Res> { + __$CategoryCopyWithImpl(this._self, this._then); + + final _Category _self; + final $Res Function(_Category) _then; + +/// Create a copy of Category +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? description = null,Object? image = freezed,Object? label = null,}) { + return _then(_Category( +name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable +as String,description: null == description ? _self.description : description // ignore: cast_nullable_to_non_nullable +as String,image: freezed == image ? _self.image : image // ignore: cast_nullable_to_non_nullable +as String?,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable +as String, + )); +} + + +} + +// dart format on diff --git a/lib/screens/home/home_controller.g.dart b/lib/screens/home/home_controller.g.dart new file mode 100644 index 0000000..6c26abc --- /dev/null +++ b/lib/screens/home/home_controller.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'home_controller.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_Category _$CategoryFromJson(Map json) => _Category( + name: json['name'] as String, + description: json['description'] as String, + image: json['image'] as String?, + label: json['label'] as String, +); + +Map _$CategoryToJson(_Category instance) => { + 'name': instance.name, + 'description': instance.description, + 'image': instance.image, + 'label': instance.label, +}; diff --git a/lib/screens/home_screen.dart b/lib/screens/home/home_screen.dart similarity index 60% rename from lib/screens/home_screen.dart rename to lib/screens/home/home_screen.dart index 9850937..73e60ab 100644 --- a/lib/screens/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:qpay/screens/home/home_controller.dart'; import 'package:skeletonizer/skeletonizer.dart'; import 'dart:async'; @@ -17,13 +18,19 @@ class _HomeScreenState extends State final List> _animations = []; final List> _alignListAnimations = []; - late bool _enabled = true; + late final bool _enabled = true; late bool _loading = true; Timer? _loadingTimer; + bool get _isLoggedIn => true; + + final HomeController homeController = HomeController(); + @override void initState() { super.initState(); + + homeController.getCategories(); _controller = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, @@ -68,6 +75,23 @@ class _HomeScreenState extends State }); } + void _showErrorSnackBar(String? message) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text(message.toString()), + behavior: SnackBarBehavior.floating, + ), + ); + } + + int _getCategoryIndex(String name) { + return homeController.model.categories.indexWhere((e) => e.name == name); + } + + bool _isCategoryOdd(String name) { + return _getCategoryIndex(name) % 2 != 0; + } + @override void dispose() { _loadingTimer?.cancel(); @@ -84,7 +108,7 @@ class _HomeScreenState extends State slivers: [ SliverAppBar( floating: true, - title: const Text('QPay'), + title: Image(image: AssetImage('assets/icon.png'), width: 85), actions: [ IconButton( icon: const Icon(Icons.notifications_outlined), @@ -98,110 +122,100 @@ class _HomeScreenState extends State child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - AlignTransition( - alignment: _alignAnimation, - child: Skeletonizer( - enabled: _loading, - child: Text( - "USD 4.02", - style: TextStyle( - fontSize: 30, - fontWeight: FontWeight.bold, + if (_isLoggedIn) + AlignTransition( + alignment: _alignAnimation, + child: Skeletonizer( + enabled: _loading, + child: Column( + children: [ + Text( + "Vusumuzi Khoza", + style: TextStyle(fontSize: 15), + ), + Text( + "USD 4.02", + style: TextStyle( + fontSize: 30, + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 20), + ], ), ), ), - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.spaceAround, - children: [ - SlideTransition( - position: _animations[0], - child: FilledButton( - style: ButtonStyle( - backgroundColor: WidgetStateProperty.all( - Theme.of( - context, - ).colorScheme.primary.withValues(alpha: 0.9), - ), - padding: WidgetStateProperty.all( - const EdgeInsets.symmetric( - vertical: 17.5, - horizontal: 30, - ), - ), - ), - onPressed: onPressed, - child: Row( + ListenableBuilder( + listenable: homeController, + builder: (context, child) { + if (homeController.model.errorMessage != null) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _showErrorSnackBar( + homeController.model.errorMessage, + ); + }); + } + return Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ - Icon(Icons.electric_bolt, color: Colors.white), - const SizedBox(width: 8), - const Text( - 'Electricity', - style: TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, + ...homeController.model.categories.map( + (e) => Skeletonizer( + enabled: homeController.model.isLoading, + child: SlideTransition( + position: _animations[0], + child: FilledButton( + style: ButtonStyle( + backgroundColor: + WidgetStateProperty.all( + _isCategoryOdd(e.name) + ? Theme.of(context) + .colorScheme + .secondary + .withValues(alpha: 0.9) + : Theme.of(context) + .colorScheme + .primary + .withValues(alpha: 0.9), + ), + padding: WidgetStateProperty.all( + const EdgeInsets.symmetric( + vertical: 17.5, + horizontal: 30, + ), + ), + ), + onPressed: onPressed, + child: Row( + children: [ + Icon( + Icons.electric_bolt, + color: Colors.white, + ), + const SizedBox(width: 8), + Text( + e.name, + style: TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ], + ), + ), + ), ), ), ], ), - ), - ), - SlideTransition( - position: _animations[1], - child: OutlinedButton( - style: ButtonStyle( - side: WidgetStateProperty.all( - BorderSide( - color: Theme.of(context).colorScheme.primary, - ), - ), - foregroundColor: WidgetStateProperty.all( - Theme.of(context).colorScheme.primary, - ), - padding: WidgetStateProperty.all( - const EdgeInsets.symmetric( - vertical: 17.5, - horizontal: 30, - ), - ), - ), - onPressed: onPressed, - child: Row( - children: [ - Icon(Icons.phone_android), - const SizedBox(width: 8), - const Text( - 'Airtime', - style: TextStyle(fontWeight: FontWeight.bold), - ), - ], - ), - ), - ), - SlideTransition( - position: _animations[2], - child: OutlinedButton( - style: ButtonStyle( - side: WidgetStateProperty.all( - BorderSide( - color: Theme.of(context).colorScheme.primary, - ), - ), - padding: WidgetStateProperty.all( - const EdgeInsets.symmetric( - vertical: 17.5, - horizontal: 30, - ), - ), - ), - onPressed: onPressed, - child: Icon(Icons.add), - ), - ), - ], + homeController.model.categories.isNotEmpty + ? const SizedBox(height: 30) + : SizedBox(), + ], + ); + }, ), - const SizedBox(height: 30), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ diff --git a/pubspec.lock b/pubspec.lock index c433588..6300f5e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: ef06759dc1bc07f900a323b5a4c667aaa23c62704c26d8bdd22a18fd3ec04f6a + url: "https://pub.dev" + source: hosted + version: "84.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: c1e56e205548fa834b96925837dc45d463d433a40597e7004273af5db0d4c52e + url: "https://pub.dev" + source: hosted + version: "7.5.0" args: dependency: transitive description: @@ -25,6 +41,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + build: + dependency: transitive + description: + name: build + sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_config: + dependency: transitive + description: + name: build_config + sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33" + url: "https://pub.dev" + source: hosted + version: "1.1.2" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa" + url: "https://pub.dev" + source: hosted + version: "4.0.4" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62 + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792" + url: "https://pub.dev" + source: hosted + version: "9.1.2" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27" + url: "https://pub.dev" + source: hosted + version: "8.10.1" characters: dependency: transitive description: @@ -33,6 +113,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" clock: dependency: transitive description: @@ -41,6 +129,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.2" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e" + url: "https://pub.dev" + source: hosted + version: "4.10.1" collection: dependency: transitive description: @@ -49,6 +145,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" + convert: + dependency: transitive + description: + name: convert + sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 + url: "https://pub.dev" + source: hosted + version: "3.1.2" crypto: dependency: transitive description: @@ -65,6 +169,30 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af" + url: "https://pub.dev" + source: hosted + version: "3.1.0" + dio: + dependency: "direct main" + description: + name: dio + sha256: "253a18bbd4851fecba42f7343a1df3a9a4c1d31a2c1b37e221086b4fa8c8dbc9" + url: "https://pub.dev" + source: hosted + version: "5.8.0+1" + dio_web_adapter: + dependency: transitive + description: + name: dio_web_adapter + sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" + url: "https://pub.dev" + source: hosted + version: "2.1.1" fake_async: dependency: transitive description: @@ -102,6 +230,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_dotenv: + dependency: "direct main" + description: + name: flutter_dotenv + sha256: b7c7be5cd9f6ef7a78429cabd2774d3c4af50e79cb2b7593e3d5d763ef95c61b + url: "https://pub.dev" + source: hosted + version: "5.2.1" flutter_lints: dependency: "direct dev" description: @@ -128,6 +264,38 @@ packages: description: flutter source: sdk version: "0.0.0" + freezed: + dependency: "direct dev" + description: + name: freezed + sha256: "6022db4c7bfa626841b2a10f34dd1e1b68e8f8f9650db6112dcdeeca45ca793c" + url: "https://pub.dev" + source: hosted + version: "3.0.6" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: c87ff004c8aa6af2d531668b46a4ea379f7191dc6dfa066acd53d506da6e044b + url: "https://pub.dev" + source: hosted + version: "3.0.0" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 + url: "https://pub.dev" + source: hosted + version: "4.0.0" + glob: + dependency: transitive + description: + name: glob + sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de + url: "https://pub.dev" + source: hosted + version: "2.1.3" go_router: dependency: "direct main" description: @@ -144,6 +312,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.2.1" + graphs: + dependency: transitive + description: + name: graphs + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" + url: "https://pub.dev" + source: hosted + version: "2.3.2" http: dependency: transitive description: @@ -152,6 +328,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.4.0" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 + url: "https://pub.dev" + source: hosted + version: "3.2.2" http_parser: dependency: transitive description: @@ -168,6 +352,38 @@ packages: url: "https://pub.dev" source: hosted version: "0.19.0" + io: + dependency: transitive + description: + name: io + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b + url: "https://pub.dev" + source: hosted + version: "1.0.5" + js: + dependency: transitive + description: + name: js + sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc" + url: "https://pub.dev" + source: hosted + version: "0.7.2" + json_annotation: + dependency: "direct main" + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c + url: "https://pub.dev" + source: hosted + version: "6.9.5" leak_tracker: dependency: transitive description: @@ -200,6 +416,14 @@ packages: url: "https://pub.dev" source: hosted version: "5.1.1" + logger: + dependency: "direct main" + description: + name: logger + sha256: "2621da01aabaf223f8f961e751f2c943dbb374dc3559b982f200ccedadaa6999" + url: "https://pub.dev" + source: hosted + version: "2.6.0" logging: dependency: transitive description: @@ -232,6 +456,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.16.0" + mime: + dependency: transitive + description: + name: mime + sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" + url: "https://pub.dev" + source: hosted + version: "2.0.0" nested: dependency: transitive description: @@ -240,6 +472,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.0" + package_config: + dependency: transitive + description: + name: package_config + sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc + url: "https://pub.dev" + source: hosted + version: "2.2.0" path: dependency: transitive description: @@ -328,6 +568,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" provider: dependency: "direct main" description: @@ -336,6 +584,22 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.5" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" + url: "https://pub.dev" + source: hosted + version: "1.5.0" shared_preferences: dependency: "direct main" description: @@ -392,6 +656,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.4.1" + shelf: + dependency: transitive + description: + name: shelf + sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 + url: "https://pub.dev" + source: hosted + version: "1.4.2" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" + url: "https://pub.dev" + source: hosted + version: "3.0.0" skeletonizer: dependency: "direct main" description: @@ -405,6 +685,22 @@ packages: description: flutter source: sdk version: "0.0.0" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c" + url: "https://pub.dev" + source: hosted + version: "1.3.5" source_span: dependency: transitive description: @@ -437,6 +733,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.4" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 + url: "https://pub.dev" + source: hosted + version: "2.1.1" string_scanner: dependency: transitive description: @@ -461,6 +765,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.4" + timing: + dependency: transitive + description: + name: timing + sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe" + url: "https://pub.dev" + source: hosted + version: "1.0.2" typed_data: dependency: transitive description: @@ -517,6 +829,14 @@ packages: url: "https://pub.dev" source: hosted version: "14.3.1" + watcher: + dependency: transitive + description: + name: watcher + sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a" + url: "https://pub.dev" + source: hosted + version: "1.1.2" web: dependency: transitive description: @@ -525,6 +845,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + web_socket: + dependency: transitive + description: + name: web_socket + sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 + url: "https://pub.dev" + source: hosted + version: "3.0.3" xdg_directories: dependency: transitive description: @@ -541,6 +877,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce + url: "https://pub.dev" + source: hosted + version: "3.1.3" sdks: dart: ">=3.7.2 <4.0.0" flutter: ">=3.27.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8808aea..a979eba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,6 +42,11 @@ dependencies: google_fonts: ^6.1.0 uuid: ^4.3.3 skeletonizer: ^1.4.3 + dio: ^5.8.0+1 + freezed_annotation: ^3.0.0 + json_annotation: ^4.9.0 + flutter_dotenv: ^5.2.1 + logger: ^2.6.0 dev_dependencies: flutter_test: @@ -53,6 +58,9 @@ dev_dependencies: # package. See that file for information about deactivating specific lint # rules and activating additional ones. flutter_lints: ^5.0.0 + build_runner: ^2.5.4 + freezed: ^3.0.6 + json_serializable: ^6.9.5 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec @@ -66,9 +74,8 @@ flutter: uses-material-design: true # To add assets to your application, add an assets section, like this: - # assets: - # - images/a_dot_burr.jpeg - # - images/a_dot_ham.jpeg + assets: + - assets/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/to/resolution-aware-images