nsR5eIfEH2?TL036H=n5)uFH5&Sb#0Ey!jw_?m%#n_%;! z!TVdd83qZ15*)S`gA0YcjSLPf2 EI2iY7x=IAvOa#<6y$ng0rSp^IVa6`op7H;?_ zYyNoCU~qxbf2UP;q@L3w8UPds0F(i6P&gd?qvHn9?c-z5gMQzKR>uVg1%s>w3Y(wL zm>cC{_KErN?8nh^d=0XNbN3z8dazH=8@=d-;LE2rOAuy{G+qDojZW`1TlLzvI*vOQ z_}+vNA@MIFILQ3S@EO$d*<22Cxhxb4S!8@mK4vw@XLQJ*mbXdUvy~VyHaR*J|APEq zKBq@bQx_ G0oeeDsj{Nf_wS_sK;GSpZ_qu5*x#0kn=W1I7?p zWc(a{FIk*Ip#Zsj9!kX`KNj)@jx%!TdfBEF6biB>$~5d&p|^w<0>DLq=;9wOv `oZVrDos0 zD{}oeHrl=KZ#3GsI8L`9P;(MPQDB=#X3H0l`N}0ImvXqF_Rm|8vpvv}=>ul^4%Dkv zXx8emxw#3AMjh(48Zv(udc7X{=6wuUU1a(IEr4WvLURyw!V3~fXDD`k$RbdHOaj4Z zlEoH?7(ScLLa9=LVzC6{ bjR{iD9B=vFp*`^2A4dx6aj9?|L7ux zu+{d_I=E18dw8xsAy&0g)_MTrozI%!Kbk9rPrv@eTn*1Dgnha$^r8`hGfTZgOD*r) zmbcpP+Nd_))N0ldGz?G+12ca%n}-Z;V-w@ZXa$&@$U>!HK*1udA3}3;1F9Pvu)4eq z>+7qqf{)d$D%4OIy1g!Hd}8`YF|;Aw7; zixIH hIoc zw7$9Cs82_}BQ-y{Jz3KHMHrtL!>tIjQ^;^Oe$Yj&jv9V#aS@i6mf+0kQ?RkVhMIp1 zx~TCnD~$fRj&4d33aF`zr4nl8Ji9I&K;uTjMZP@c2XtK~#|R!Jl0mVil48Kt0s~m| zu+UxcJ%mF0;Pf46w_9>+wb;U7euDQWp(vw8n3|qqK{$j$F+V>KhYlY`ff!>qijgc` z8$zC9LQe_$xUDooSgSj*QFR!0#r^7&CHtWhv$@L2$eB(Eyjy5IgYMJ4&QKE(5i02>ZOaY zi~_K@a0xay)}e~-PRIc>2Fn~Plu&pIECiM LQlTdUzZD_UI&}=r)a>x=Q61oanf=P6djvP4x z^K)}Be_$S_Ca3VXjX{ k~Ey3~dyKwN?Ndbq^YC&%vbNQqF|GC}s68oIEo5rK4k79ro#(>3J7|&p%#0 z44(Fi&35}A)Z6{ Gr!7aBQhv}(lc9rrZz?ju$H!UTQ z9)7Nk|5Q66ENnT@M9Uz;XwlNeR}dWd__2BWYbPf2i?C1EM9-TLeDRCI%pB;?ZnQez zyVXSGr`h_}Zl|5qgwGk?Ji0B#3TjF6{l^jY$pqlFs<6Ir9v04@h4W|5z}ng>x*8qa zKMQ4K7IHC&X^5c^!xxri2xRsUg(*TN_Y1{hP%ISOjGgf_ITQlh4p8HV1~NXPEfF%A zWPF{9rgg#bFuFy=^#;DC%OYEr2!qN beDlfua_+08=S>KzRZ)Jr z+WGM(FKqmDt $edhiO^rSr6wm-WvT)*6KD;r0BVb8;q22-!P8GX0gIOwU~6lO zYj)=552O1qk8tD|*ZxrEH-nHR`u;(=QuZpPvWG(9=JEyC&R7BZ rvDR!h;nKnaT*BYAwzkGac!VU)qDyqk@ndlG=n*)6{5Z_u zYXrB-oPz;FZW>YpWAMdB04r4wY7H+iAn4yRW&a|E?;n5d$;l mkHE(A{qVNZ8q z{DCu#|8RD3{X5$2# b{dmirVst<3M`*J4G(_(eq{bj==V1e z!n9y&W)5cO4 O&_@Z$eG=$4M3YlfF1kYASf$d#&W!F7E)-7 zJTG( EaN$6ppE=xC52rgU@*+sAa zvcBW~Pfwm-dxz6&j|HJ?5&TKeXR%y?0|(F#F6v-~7({L?!`UYugL7w|LB=@GXiuSp z?$P8l!;Q#h80=bNe5?`><70BlEtkqpp^$emaphqg;FH$JuriX5sO=5Z{6OvwF}*Ix zpO9tCg!qvEAM=Pl`bX#kL0}OepNR@W4P8~38XCn7WE%`LOPpFm3xTqMHY(E4I+%{* znwX)s5dpKS^?JVBZe?BHx9B}`tEk%a{Dt#M= q zJ6_}x4m2x#T6Ff5eQvlBthOS!u+oRn57tT<@#(oS^G{xJwEX$~#M r1}dq77CqRCw833WkRQkELChaP#6UI zT|j}d*=n}3h!)xPMk9wYLdJESY!nHz*LU=Ky#Wgt{I9OALcP&sX!XdE!;ByuKYk1j zVxECeB+4-ml4L5YKpP=QT?`g3)_io2xP*Htqep*txMKasyN?zB`@SITP6pfnqT;uw zYrSu-b=|i%TD`Z{n~lS$o;U!e#8m|Y!6}44$4eS`^<`K*`xKmd{4uz2_8h{AZ7}UD z%pN$zet)4@5QSVe8Y`DQ6o5YB?Cq(^$u2^Ty?j3Bu=|5TfJ_%sKQOsK%={eQVkQo? zKji@!X;B4nx@i&|su>h?%4{*gr(6g}+MM$Vrfj~6U!%|BF*Hu9fga}&WWN~;2mNvk zol)Q@cp*Cz%IZ1#ZiZv=d!l?U7a)S=+nFo|=VmBB9MSja7|3hL;F^U(@3}4zvP3AD zL&biSrN(5Pp@L*XSKV-iO7c;!0^UvZwD1cJ=y_4u3&ofphS|R7ul@E1|J^d|(@47R zmVjb_?)l!!F08ly$!5LvCiI8rA@tanr2u$x3h{qLe~P99etQE>J@hs7hfl%MrA2N& zsf fYNd@ex0- {26_hi$g9CJ;s%$Tx_QvQIl)jafX&o;;IBP84~TME;Sil>rCiA zQNtP{MyBh!W~0%_qE*OlZLXJnKg^(~W%eCMUte9(&Yn95=gwbX7i LL_Mn-ASsUqDtCytbnmHEYxH^q22X25Nr7BiY|Vkbm!RG{t$qjWWseO3W66~ z&W~-?+uwy+odtjxFqfA_WiwNV DIXABRueTeKuT0SC_hyG^P(}eW>RT(t5OPaPIzN17~ z?EWa`-nE@^lxBz-jD(1YECe3}1%U*ES}Oz0CVPR8IR!4S;PWu(5>pV(IX4ngln0a- zv@W7(6+(1*!V< kYo^=O+%vP;D11y`M+eM%T>}ATyYlt}J}z zvD)vQS=@Skx80mV&R`9d%a@S3=U{3aT@J4XRfIpEeE0!)=JaXUK>vRn5u$@fj&Y(4 z167eh2D$x?+dI?KGo7(gxtq<| Q*}D&h82j{-*; y>y6CX z+IpeUs2953P7%e$rsf<13Lbs*QCP=omBHKr)gqldc>)9GgK!Xo=LtlQCb)XF!tck_ z#X#XwD}-}v9{63SKVLCFFkXp1e8=4Q1=y#AuA3$J+jISSQv^SBe!2E~r`N3zj7b7O zWej7O1DHbfr1h$>a`9=naQaC&ho+u_zv;O{Fg-KNCDJA`_r%zQH#axy9GE}QEENj< zY%b%XPmit-LZ0aBllCW0BSI?>$-oe`9{SOY;iDkPSXJ_79Mf()-RmU)JB~##5m_A@ zv%Z)((75UpEd*HyF4(3B83S_?iHLkT5_=BRHF9m}QDh6bU~8)yc3Z6i1VQfbkt5{k z!NSFhaN*)5SYO{@7bqf2K!ttuh{qw Ish;m(iy~Kaz6iT)F`vF?lwV6%*sSK#d-d!E3jh z82jkCnb}!`N;e2iI*Wo(#Voqbae&4#0=>@Hg#-bQoxx-z31b;|nMX_PxuM otn0sHjagn-5yUcA`7x7zIfaIM{cgYR_N54W>9uI-tqWFf1yp|!REPd)k| zV&E4U0xc9PFo)S+3 ~IY*8jX<;&`m(LY?r2X-x2BiIULVkd=QEHO{VA>e) z>e~?B6tuGow~Ba|D7E`FaQ1G&^sa}{7=;oTr%<{AVVgjc&?$uvWF17nXM($N{gTd2 zJsJu?R#31^J8o@SLA73^WJm76!NXPoEkeK7h0SV%JLf3wpzBUua&j<)Ra!s@i-$ zPgS%qhY3l~w_q^{X98b*+gja4{nh&5!E$*EZgP4~LNMO%Pd&ELM>oiQL$BA(;O)iK zewG8=u}ThdrU!k@0$+Uk2{?c5EHqI7$OW37nL`063(6fFK78Ccbm(Afa$>BFn$Jb; z@0&Umx#^*n41LoQ3t*yv86)awh-B60=yQ$Zf|&WALkw`uMAnKjGJ%l!s| zvpuU(HW=f8vGGYvgdq&7b)d7p0OuZm7*0R^G}i!02=oxg3WaPYI(+y<|HO$~YlVEa zo3$|jM&!q`^gwES&O$|8<3l4-Fc37Y3~Za!HeCAyN#Zw+1nr9bUq{+5dc$86w{&V* z#0xM#LX$6~v=Y2a`7|vI(DR8xj1wZxXG9Y6r3wsGD3yqiv8)v55daX1}v|va$rotK&_xup-Qm=eK%F9a$}FB!$EY5s5fH07M(c1=KK=Ia9=4D z3e>ag!%a%hS_mGy*m>Kfjm{f;-TndD4`6aPSfzqKzTrZ*x&$j1&cG#ffv6s4Y EYmYBg92be1P3k =)Ko6Lze`kB&7;g6+&nF46??oE2ir5N(3Wpg=yVsfk>M!rFD z0%nrSlT)+$Ew`ND(hU-Xt!j-+DyT)2vd&};GPbQc>SW%eh=MRtFloX?uGRE!U95*c z =iotdfyO(TjOAFI{L(|h{dKNr6I|@ZzQ187$d97LujlH8 zZOk9LJv{3NxXyV{29)AW5sBoL9j;aCnDSt9grc5`LA1o=h}4c6Xrm=hA+<8eMraY& zg=-A Z0m5H1V&Ae&!duCy!;r!V2SoYke5Uptq1Gt-zu1N?;1AT27 zCbfF^4O`9rYY+p^k$1xRfIJ4nMT8-o8Z_1~!Rpc_4CppF2&7~f#R>UbCYqj_a_8pe zy5r-O9- j4kgd!P? zMP$e#XcaW*1Gq>H-X*_Y2C=HpZif)WuS3!VGm1E_LANmsT@Gf$ a%1zJC8HX`m==RQVJ11FNYA+@2Pi~OOB`bM! zvrKN($+0y+*tBl|T7!GCUGF<{8_j3%+TE1n $#W!8i$#=d79NDCMGAmLzoAcnx5?BviZJk8a^e$ zWTT7ZNv{2&UIUB)D5(^GH}NI$v9S~XPqb;sSUlCEh0MG%s)$CWhdJa>*ekssk_3dR zsx%B$)hHjGj~kI)NrXf)Gq+um51)!U(vvHO6VtrT0dOM@BU}gv1wzg_lDmnxK1o&O zNsGaU(W%!hP7VgeVnNJIPq*>QwIC9DsXUfFa{RdNIz2djVFBv(x@^xhWXmeMK?p^Y zB_qM$*ez>7w`3w%q2*jpeAmXN_rQb+oA_-v-SmoU5(1~}9*R2A8&(_6w~4@Gf1EO- z`8;YK>R0JkVQt|Yw-C~VH# ioeLkI$F~@&j3Sf=rCKIkmt$RotU`ss*U9qiWG1ToZ4}(n5P*ov zMW{qbmlvW^DZ7CmbY0K0!@xJnm5I!WlP6(mWd)pG51G5g^N -})I*V75b$RLxKig_qv z%qseuu)cT-&YwTeL$9bGV1k=rD#9{#xc#= $0=P2~cq?5`344ua84Yuj8P0L31m;EGAE*bk);uzw^50dD*^ z`{fuen!3p@0s0+M J z(n9ctM%Vuq*Y&gHqS9=jeBK5d`rtO#VRLm6RuQYFGT#d33d)r-jYARR6XPzzp`B78 z-^W~lXHX^>?;r)jTouM{gT*-ji2_OD*pzuZM8`u&AwUa2BM`+lhP)h+2NS`-_m7DM zC>p|x)Z-3lNF68jh1)tZC9g*kt`(z$; tblq!18vT0pm`l%WaUI@CO< zppm+3z0-Lue&?B+sW(}_nw_GyeX`N_UWLBnab5+6`UA2V$Y2~0ISuGE)}gk!0nKJh zA`zIbCo6$D0PY1wLFgfF?V*nxP!52SVoJ>l3VwCA09yK_xX6sEFL@RyZnOeT6opcE zC;YhEeUitDRIR_1HThFjtjiFoNRE
1z^xpnZ6$Je<}u%CmrXZ2m9=LJx6Tu zsqxwGM7`K*(iul#07=M>PbscKKi@Bx%e_J&-$jKIRmP{pBqhj@>1hBlSp;-pxn`@? zX+x*m;n%5qEn{ksCu@$k0^P#duIqm@;-nLB6VVlgfa-&qo#6G3 (3BcMi7jrpyQA-sy1S^+|#>y3cer8q{#8Eex&Tdc=1e1BE5@-}0!EO+h8I#jP zw;PcsT2FwgE;*YjN#6bl6ax;60ej6c11T%Q^*S8<%KdrBZY0#oGD5``FkpPe^zkOw z6^Jf>)e?)45{yV%h-j27>>>q<(uoI^As2By5T6!Z5Bv`D49S-aQ4j)*wVcWE@is!E zPOealrlx0L0 N_@>pr-N=!!y+ ztM#X=UH^4{7|qhnC1{U?AY f7W7 z8QcvP=~`R|M7aQi-Js-T@HMWzN39 inL#;amf4ICfP`CIerwLqALPn<(KS zFDrI)cG8H;7F|a3Unj?c&r<~nz%x-pD#Byf6kVMF5hJHuD)bG*Mr$x3Xn>cEn!ZbG zGoYJFooQ{nPi?daG!~MqL+E>86O?LkzqIB1(VXXm-wrnsT~P??-ROr~{qSWNeU-># zq4}P9L=_Cb37zUHY^|^ISVXE-p}HZo02maP! O|4y62TtTd7sMH5_bhu)!3#tacuGr&ZcJ3TeoDiyOm%npa+ z6H_oZJ;^KckR@;&*}UIqG}$7M;_9@Tm{s>MlT7Qk@qpo}^+xA=F*cZ`b>iSApk41S zjZELz5XIG2_(snQ#>q#gnsrLQ+j;;&rv|OY77v?s9FLKmVzJB^c%@tk#w+7}ng@i~ zbYK}KO@Cngk@%Bb9}}~|lnaQI<`3BK50tkHI}~%wpHewTeBv~DVb^NV#MK$NI}-WP z)bkW{xsB3nyIlk#h_0V%zYM7}-AN+G!D5L}7mM6XO%D+*a?0gW-?FnoCR-3RvZk1` zS?fEl!;>p|Xc6Km7PNW~2?@poVDSoYQKs8>kGS3N6(}f0xCv-iA$YU_rKZ=qd#mH$ z=Z9gDi?T=vFc>xi^d}o@P)9eY-EK28@$yNy6$^!Ma%!AzgpokOQK7WI{FnZ3MYpn z830r1!tlHCpK3 HEy`xS@v*TU-UkQoM@Z!==$3-( z^qJww8uU3xZbP@ vlNl;#`p^lKAu0j-xn8;y?dvo+{S>wtd)jm8vIJR;Xz==kov zuBXk=00r3NH1ZbX57z_XO;>y3yIenHk0Ty_tA%~=y5tJgp@%*tC2=ymUT~2nLW`-X zDW_5@_w9_uNipsvR}P47m>2`_ydM!mpOOXwpvmzmU}yj%iaV3n68?`}8;E5 % >ZaYi=@CUejQ=~;9&Sh>uFv6H~iYQ9)q{Uts=UL zv({v_k^EO(8eJ+Hngga|K8E74QmN#idlXQz43Q&N47~0dDj(PNc!3iV2p0u_R@)KC z{A&IM3Whs}V8?CUepG=yP9yJHqvy^BLHO!cFFHUjH@P_$A_sba36}OIG;8bJD@@}= z$lT^=X(hA*)6)}P1tC!s2yT+W%yJaFLK;^>g2L;AVK7|XN?nfgOcG}zN})KK!i&;n z5)v{@w4+e6MR99dukNZ}Qzqh?ti@FVM!Uz)BMuOyLh _`*r0;e0FgSEYO&Bl^E%A5<{B+xNW#$T^;ww_eCncQ2;DvycyDOIL~@bP%D+ta z!x`A)G}7Ozdf}KK!HFP}RzQvqLEqg+M4{Van9ajjAg+xkO@m3As8TMw8JZGj+JVk% z0SM)%%J^01T(3T40Z8L>v74$B8bo=qa|1~^&k*DDpMxMb=^c&}S1EjFSAKoA@oX$O z`nDH!7$Hge@Q1b^ Ty|(w3dI669hy3#cqH?PuanIu za*%AyxUvO6BpCm|5l$)mg#QOFPb>h#(5CwD3hF)m`76qV+F9S`!6f;FNFQ +n$8TKH~V|plm6mx)NkdAMs=dg}{+X z7t+F1`AJ1txlBe*TgF&`&>)T01W SgHJn47B*$zgvoMZ8usgAr6tfpMCL*1#v1D2Kr@&nHq->!=)r zVgp(%M8*af7hukYh9*#a;JI!IcRAz=Z%?~KJFaxsGv<*qPq-nBb8dhZpB9Mcd*}l7 z&`#&_L;^srP$rWRrE dM6djxD!Q=>t8_IdCxJg4erH>@ydLD|GLou4k z<8XkhLSuhB*>-AI`7%01$`nND*G4e+jw3|d9xrnNqg~$_ zJYM z9tcAJ0PJDf5fdO9FpqA=OelD4EHf -)NEyvYoYtN|#}fY2N(^qhHfUy>TVwKE(T% z6M)_5@{*T8rI)FzqJj!iMtFZhbcrx7Fhg`z4UE~ezR_m^pld`z;Hk}GtFN b2~^budYqxNXGcmS?4C}9%YpX^cq!ih7C>CjCra9n^O23rHV zcU+-MmH;=8T_T^8Xp}Dy_!#-9>^zJYT<1{N?H|2>!7=Pn8oKPYTCErgeLfH{&CTGn z3JML1A|XAmAQaIbnig0?ZMFxm?aijpNe_k+Nw%bw5-PX>bF5yta!3JE{Il*7uCA z)JX#0D0Q$zyAGE#$Px!gl`;>&(e&qs1t7)!>1qhrg&$Sy%f?{%>ml7%pl?6^Wod^v z4S(SkC~Z|?hg-z!w1uW^2PgyqqC}B}?yHrtWiBNQlP$IgvR^xpLV~eE;Cp4)_2&wk zn`5v?Y3M3 lS%+P-3wLUlf0y-anc0M+39b~&qpy= z!_Kc`rdRO?f734f8l~PC Kz>JgQ&W6s9U z7Yek#gFx6+(7mHQd6gX!fH*$Ddm98k2gN8AoKrw6QTF{{HZ+V$*rPNg1ic8xG5bD_ zS21rGvTjD85JbfMs1-tAE>RRuQjpUN><}+1%?^#UxJF3bJQfGgcwsbtBa&TVkr?IU z4{N<0jHF-HPZ;sCiWyR645N5?7td*A9J>pcIpjX=et=Q5Jqq9TY(`QFF-zsuWp5_E zP9yEJ89P9CHKM!@!KFqrX+w73aiC0$uhu-Eeua>rRM)j#CxsxMzi6C@{v~KZ!qo z3_&)N0%Aj_nYld_fUYdQQl-uuE07RarocdpvXj1y2a?RD%H->^1_;eGXL3c-8YycZ z6G}M{(J1dbmGF{{>G966O(nyQW`hWnrO>w@a-j87N7>Gfkltr|C=tbmXGA=5Rm?w% zq7i4a{e31Oeaj?3=H~qLG(DxT7%4)RH83qJL~D(fAVVwWuvO<-O_UrXOMt(_rv@AP zECtSZ{}Cb5L)R~g$e4vaN<()_gJ=@3YgRVDQzjRg7K5XZ-5|LZ7hPhL+#4QeV$%3< zT0g|m6-)L0NG1{fThwXaD2N`3wA0x)2%-CiF-J6a*P9U~p|raw0Fj7IM}>TBdN zM!9!a*5sp(H2Rw_cdQ+@H3lVkOba4IKCPCCc>=+!a$-zJy&T+YowS999P`nAh9Hg$ z5E+buyyy3)V2{#}CFu6UIW(RnGL<}*kS0A~JP=So%#)5~$pSBHDu=zIB{0K`o$-@Z zBw=tnVwjZM8l%gNvWa%Bv)KuK!T^p*eDM)%nvPVX!S_JQC4wQBYqXy;S`3G+!l3L& zAHlyPMF_4;yTugS&L~m*1B 99)_TLT|qQL+XB-Fq5r zfbkr~f#?Mk+I@FgEn2=iF8dOtVRuM~JYF`Ni`aP4_mF#i!$6Muj^hF8U-RlHg4X86 zgc>csCDIes;!UpSJ5p^ztUI^Uu%g#f8g@HN#xBD)&WpA&P?|Z@hl$;mXvc}EUG$;4 zw)oh3>Leo=j>8kkDAIStCD(^`ahC=Hpeu2R=d&<^YG6EoF Qu8={lkUUzVDHz`;I}r0}ieE=L4pPDI;*jtM)TfdUrx{|z zQT5%la0ENQIDThasGI_(62TbkJmC4{& ;8*X5 -iochI$V6ASHLDUWYthh!Rl0wJzfU z9$Df$oJx >1KA-DLBU}@0C}~FV0O+#Ui0)TBlz=P&$JK)=1BqKC7bYhw z5O7RD c=EB?PprYCIb! zO+wZ9h*T($DD%sliqxplHFyx06M)!Kj8NO#UG6J C6Brcd@i>}P{hQ?GQPJbyUdci zQ?W}qypDNF6jT2TgFL+cuCI^-d08p>m1q;19>)-z7VQj}#Lh2KG?ZP{2mM)5`pv65 z?aR ULwWsbNE`7|92#hBwE=kksYnJDFl-&A8j@t7OadX)v~P?D zO-!ie8l6rF_8=vJF+!p?GCqx-(FJ=qq#5P-Lu$Z?6s{k4e#r_^T5VAmaqd#i$`qib zU89|`s>~tisW;^w&q4d|hT&{`ZD$mlyq_e}(z%9|uyQQGyGO}!QlB32wb2#M>-v!# z;5&LkOB^%M*-GWbHwaJP 3RTC&@*(FmQErDv;DlT}iRhq?v4M>*?MZhi5dsW` zXc{i9Fs ;#gpqZ;OohHvPu~6ZxRez z=86(#LJmZrMaV4-s`q3OT_OZ-7-FJOP&KVCtz^W3u^fXgwS;VP*SMEJO@2sqI_U$= zp8ym)12=uhGCWuA7-bPggtSBalGe`K&D02uuIqzCMEdC6wkv!c25F2@n&G<8kZIN6 z8cE(mEt$$8R5qWMqzX_>3<`>UYN_RFQyt?23wa^~dyoc}Kw3r|!(czRoUIPIX< z5!8i?w8RYaJbgpnn=9Os8|NHcDGtzg*?pIM!>GgRyL@d5N*$sk7ja` dxa;u1!2#dP9zF#y*>+Y@KQZ^D%$;nYJaVU|#u$+x>#Lx;W!*X@RojMs?y@m^~2 zj3!MR77xx9#1;WGH4!|L;0TY(L~9WF2JAsf?o8G&T)bGGT&hQw{m6?KB{>33xkGv~ z?SU%el0>l#z;uAPifd%!(+%^FpDk^7pN2AHSM|-qiQ(-*5j^|fe62rAyZjsE)<$8Z z#d<5Dq_kZjM4`+V2$ewNX@L^!zGk=Ur9g=#sCD}eUI9nb!-!lW(&;in9?bA(T|vU2 z%a{OzB^Ck}gk8{y5i~MZcy4sPBx_rYz+O^14v0o4-gFqiywT+keWTo>k#X_hHOipr ztjz_@W|0;lQWl=>uP$Vt2PJlRY5PFt3p6z;FbbYe>-GFnA_NZR#nA|QR6R{Lj~Bvb zAfKKoz7;T-?u^`k7fBByy0FQt5iQN)+KX0i=p&d?kE7q={f7LZZO4dUxI_rjF>(rO zeYSL!e3x|6r2DhuSxrJUd<*vJb*b@Hdn;*q^}$GGH)%n}P$7c^k?Nc);tmih!}eeg zQX&KuyV&WN-IfP}Y9x5QVD2o_WlIdZMRbdWtR$s@DR_~zz$agE5Fl$Znz+uI{sz!) z!HK?`@2>BD96f!wffX5z3+W?{{oH7{!B~r58#t*EA)bNed%lkMjA6V;)eF&mjAysf zedobj#P~jy&dU{pXfY6-#?on;3ww}~JA3F*xr<`oMor*rB1($c;zg^a!ceG@Oo&li zo*tqV5DGgS9+mPOwcrh LI=x2k~@Y>?PAKffi97&fm}+_G-Q8p?Bj@J zDui6H11}P47mrfvreK#yXmKt<7ATVM`BDj{Vz(!P9R+dQyVcTvk0_l;N 2NB{!4P$DfL&XuGDkwzi^5|e-v zP3yrPq~TklMSYTZ9c1Al_Zd^Qp%&xKp ~F>bZnNqZsJs6fq{g49_EoQ(_oYAu8g*;As>{ zb(V!uj0#5dkr2TU*RrfG>_Hm3dqvx-8bb8QFQk=wcxfhD2^|?e4!lGp7jdBOGKvXY z$1y#$1|$fn%8FPU?Y1_?3ojHg5}Ncc?4s6*lvPNouA^;AK4EZGmk%QxuInEP3@9BC zLu$CIe1*_E-RXDDexEFX&`G;{PG6Nw$D>yQ-hxn0r6X$)MsobUq3d2glc|r7k5^$2 zlD_TP7UID@c84M%2WF+#7}kU|on$KGh77z|y63tEp$`$q2Aq(O-5}N1Azplu@@1jQ zxKtvw&k)$NU0}9ln|GCf?fYIY5^*LtHHC$~(@5Y^$LXQlDlGx|`@ZMOnxZHb4{%JN z=9H> !Zca8l{9W>FkEhZIcWRFQo3Sv=bKk}9#b4cY;&u2izRL#Y1=Pld}^qe zP@5ab{ARn^v;(1Wo`6~=JzAND?@g!zVwn%1f;nok*T~xI1`k{?Y@?UW<;V)`{g@zY z*maBrTDZB45aG3@rI@G{Pu8AC921baj5s)^u^m1Mff)p$si`2Ck} 1$h(+^xqiz|nVL;T{$Le%?w$M$3+#Q-JOFlm&qZlJ@te5@8kwS)K^{_#4VJ6clm&aCM zkJ1o_wA<|_UduH!k}Zl0NC?OsVj<8Ci6<+ck%n eJVgTB8)* zAZdu1{YIFb=rYgs-QUx F`BJrhJ4R7RH~G)hF3p^?%;+|3ZWUo`jatn>3w(6$lLP+I}Sdg z&~C>*>ODv)|Er2Uo*gOhO|53gp*-6J8Vw#Gw&C_&yHVfDI)P?}_(reShHkg5%BRC* zodQCI;sw C*&1@ ;!+6|EY6L$1O$pIlsZgzq zjbF-U!!6jOwBwFsbHx>8+7-cll_4*}k +x*YI3lzrUHclax|b(uRTjj!IZoH!e>h(4s@kRIoeJ7t`AJA#%;*}K-aaz z98ile8gg_~F&1cM8m+c%p%C=DE!^517*l{31YrV)hT&lD5I5M9D^$oqsaRU9Opadw znAnqvGNK)KHqryK1WSglwYWS2J((N|Kvr3UxO{@pg-*B2)`3R)xt?z}n$0X;M9vu~ z)gMEq$%KmMhtl}|^ U)Ga*2c-cm{D?0Ey4~5~#Jl9&{sXB|y1C1}D9gjpA4UGo8BcI##dS zwmZGPW#-DzZPlRLZt-++?jKMk60!D=RBnK*1SS&@0KPy$zZSJ&YG!)9kk3CeJ$+~u z_Arf9Q*P*N=$iNj{Y`~)m@ a6OoI#HD@qLGg%qN=Pef^5}2=4`qU?M2*1n z?Ph&5zg4Z~`@S%(Ocqub&R{&yk(Hv11Pw~2xq?&`&*6)RY(y}1U>0WQ4?H-3_{eGe zo(}9`8hKZWrr9!0{TaNj8 }e}N zG*Sr8oH^4(C;u7L+>Zhm(ixCN<}aa3R6xXlm^e~1D?1o$H5yH>Cz_g^LJYcVRcp08 zxkY$^O_?*I$?ei{H3-E}PSWsdHG7dp1k#twV(+9~6#@wWW#ffN$|SUqI>>_1YP71k zTD_4WD?m`_=E?$E>z=X#YKsO5G43}G6)cLkhVJq4+fSZ0E&V~X_BGh!H1ck|;SFyH zkki*q(|Qnih9#9%Tad#5xsuO87E|e5bApz@bzEqnH9%wt !Kp*xv?W&R{3x#$*YyT~E z6B%-%G1n25%LOQ+rl4ytS3dL^CED2B#N2@cQ&Usm;EmYYs^w7-2r@O5Ag~RP_fXSf zL6B~d7(~ni+Cvher~bWa(sp?b-5`^%BQZTyit@0CkgR~PT8+*8`o?C?2`NLYL49il zn)NM^-DFAtR8lOl0*nfU9KhxCndrzZ$NEQ4ocP@B_uRI&7gwg)PP^Wha=F|=#(xYw z&t>FZ7ttbgi84^axS&$ZDU?Vs%tirnz21PewKbTYo+e1tK%^*Juh(-l!%~;nsHQ?> z%}`4|nMP`sli2q7QYQT}HAT<)&=^+@uA3aCLhX}?$YB!9-dtbK*W0}em>G>~gsQ7c znA1@+gyI^Zc;N^&ZiU<<8m(gKbl*$vt#{qAJTWuz)l%s|3vL41RS1w*y_uPrHOwG? z8XcG|3tb>enjvHcGm|AKqH9F=nu0A%%0RtQhiA^6 IeS#`QtCgBZS8^lRs?icVw3-3Xu zRa-BfJ9nX|<;Kizr=@Kyp92h#`2buK6h@#Xk0u9ZR|s=Hm`H#!-rL!O^P6|S>b{R_ zT49BF0B!=hqJ_3|nJ=O@vx-`qmTBe8F}Xuy#VpvSY$gwtHE3d7u&}T|aRC%dMGX^a z*6P}NzKf8l9*?fj;ywckm37UOi$!q4XP5EfFI}`qAJw39#f0I|Z;h`B1;SLd3sU e{-P^P>A z3WK%2xluriP(okSMr4R`Z-y2Vh6OgcMe63#KS`V5PTB*|>8@oLm;23fdIb+WkX|J% zS4?gYO;ATa-|BYS+0Bjh{ObBv-YQM&brgWj)kQ9o;O3YhO!|pqb1MX>K7xSK%TPee zJ~=&An?HK^aXUBm5Zpv`Wg#dQtEQRx8b)Iqro{_)gJs#6Jj-ExV53Vj92;z6fQ(EJ zCR&YBxup4iU@a{#7tvkHqfcrX24kf%$4|G!(79SsuBa$|-5XkR{e04MK!JpiqY@bj zGEGhntGo)$3|Advq3QO!nP$D3Cm_Pb+&--@SKC~b* uMn z1pqBT?a;B~PtrYyn~1I`1k|Ay==#_2uO|)NYLOMlAQW0aq-dg&ha$R1hKda^4uB?F zfs2 KEbSBR_tr9USo#>2_k+2sSrZhb`K5~Q1lt_rnlY;2s?jO>HDX &4@-JD6a^=f@1@!QZ)=C z?HcS+x9IAKpitekq~R2@6id*Ir!X=Cw6Lzz&8{vl7OIU_E--TVngbWlJ_$W4k6;Ki z8U(@VDqblCY^h8X5*n>DK0V#3OinBn3Y9%wEH{d-N(k<|?>^toX8+2xvVUpWxo~29 z9PBJw0ThNQv 5z5OL z()Te`-hkjDodkj64%&Et)a5P0UJ!;IC{Sz#&;lS3l;NT{<^E8Au;KZU=@J^`^z#en zPF0pS>!qHsZA?tU B1QO^u_RK2H-P$&DL7tUS%nK1FY;?A zPn@701bb;|skpebRK_G*M%OusM&4AS`AIfN2)JWSy{@LI?M5#}Vc34oal(*(KjIKJ zCBzTaCTf(RGjXz()&!aR6S&?CRp#(%NPt9~wE+$Lqdy?kS7~HaYNRHz>6j2M5+# zdYw7o34 dQOpa4& z1mqDR8bipmRdS*4p(XGFzSszha`KD{? &`o=UWJlq`NgG0t&lGS=qklM2C4#v zyeb<0stHWYi2!d9`n{TZJ@%7#i<)DmdND`UBZV~%fSFk-cY?54B(v!Z2O+Y?QzI}T zW+CE}$E h#~TqSYv55a z9ble-!>-ceM?K zLe~~ApwelBT6Ij#j+I)UhotF)$sZhJV}6I?IGQeqjx5-vG8oys6={a8+_9TV5UyGX zh$bc`nuzi|qMNxtw6lfV@p-1zZ0h}<1Erh;(-pJ`Jr@Nb9F$Wu8eFmX$YYPeD={{h zo}Qtknz^~PSwzSba|xL+m&;RNOj^jN`K2JOcq9^kmgh)GL69J~&|W%%u*-?Y6TD@Q z1u~>YHZW2PYVe9O5urfBK*eG?PA}J}ZWNZ67t8ClP62Y0+SY}qVe#D4(8g;lhx|p! zx5*`1b%ve!QJ0St8&J1k$*>?#JL6+!d=j*5zS0k13O`EAwC?$$xinpq5D=kTvRNus zKGt+QKUJ=b9T*!M%TiAP1!Q9dsxr+$t=$KYnz-@KIQ>4G=%-Je<|Ny_uX+_6Ja|BJ zUB^TOsaz-&f@8;yL#b5aFitTySp$yZFjFne_(r0?OaWk=`MiRA^g`2Rq*M%&U?w6! zx aadoy@DwbZd4dH%0tm7JN|k6S zhL_Awf}oH$psar$E%QVU<2tkiftkTn@i?AGb~+5TL%kl1!@dw)o(>;AOc&_<>eABt z(MkE2vvc!zhJmj!DuEh*ZX(Y;t929vpCD6YTB;{nSzY4{B|&%h+ (7_u=3qg=;6mY)?NbiSSBU5FUDNgXoDoFoVOId|cvrb37ccbWv z0eajJgTXJCLC~B^9+VaGT4Fj6%nCx?qX0NSB*^XOS1+9_UszZwbps; !OpTita1tt;P& !``-6H{p8`3hc{ZaV{5hQWTVl|PFG6qJ@?#x0q@a8+^6~Wx4(T)G~YjK zROQm8O9u?m{{_F-{9ZKEhtHlp1B;8xh*9^T+Umnbvk#q~2LVBGg!oVeDxwfajvR*D z5Gi`a%U=ew)6-mZSFhKCa=Fr-pPlI*IdY_hNj4G$T0f4IT%__lLY4ip<>w}GT4)+y zr1Ibx0oSgrN8x%2#$~@WhCP96rVJVU>Zwu@kgiX}>x!WOU?R--Jlk`7*-Pi1sjOi( zxEGpP9}{IOm(Ih5Q{R9_y_T#$EDtOpP#TR%%x|c?3te|Y+YtKXxw3f_fHK~P>4|Ze z7#oAa rv+kz6K$Lg{#7f?Q!G;#vS^2oYP5s}u^c?RtpoajkYTze( Gt7Uab`xCUx ziAP(G$f06JYML+P@;otwT42Y~3QSE-!o>I(Ow1gBJ_h3rM?*M!$@%hM{l(UoKKIE5 z$MFt|P>h99P~t19ehHqxhk!;KEy^ B77|F5kmnndL1b-ixisMhL?4i&Q&GN|Anx$pThfrE$< 4P)*{}PS*TM{1 z1iY&z!lxR!P3Q+3M-Ly-FkW!*Mp8L%yoheh+f;>zXKxCstv9K;2VH< N6_*@j9&sJA+HedupNYYEG!hhb0>1QLJ%lVII$C);Y(avPiL#m%io z-bFp$!)vy-bRJeNo<#xJ;JTmm8S)HX5;9H20dfQm3xQ=@G69K#Ku|U1cQB5FN~KIa zBQQQzh6 i>k9nTgW}4<2m4)ZC=!Bm{&OA$_i=q01?v!$*$5@vT}FlVnm|2VANivL$da ze-Pqph#je#mL=%+x*X8G{GPjEdTNr!x)_*S$gQlb+Njx$nVH${_}Ew%5hRz;Bap(t z^ C-aLv%lk=^}1quR6fCVPCqV`Y#wzgIa8|$0-de_N?TGn(>aV(sD5-y!R z&E*hk>YO~O7z&M%AdZ DRJ&zcc9Mn9s(z4Sp@3GQ09kaAs4O0{`Ec>+Z@~Ru z{5)KI<|!(g iL3&)Ngh4JwT{rrUs`nd}iD~)E;I(qboJ~uOqo $Qt+Mvo;C>W{_Q}^>cV5$QlGZ)?KywsYZR;uUf6a`o=oDN|RF)aOWMj zqi~ID&1TD9T3XIxfR?4n6{xv%ym2Zt7`ZX=m?MN!abJzJA! G#* z$+GQMy|}S_q4JGKAD-A~yG5fsk50X v1ppNXa2dL(%Fr$D zIpEmKQmBw6<1ka9LNqQXkJcd1z2YUH8n7>X=_~M?|IdGg$DcgKgA%6DBGWOWNZudd zHOq|TIPvT#>DtAWuJZdfHnwJ)?e?qdwXGw+{oB8}@SzWV=p`}E{kjOj```b5_!qzM z3oiO07m&s8v`lRb^+lGLz1!`p7(jaIp;`fj3%t}^(?CI~H156AZL gfOLa }$uE?ziaURv8MLSfpraz(AawXR(_^%$Hx^(1EFo2u9i;<<}S=2utC>l(Uz zq~{tp-{@+YB?*(7YReE4Q=d2q0U|{7nNw#jz<>I$AAyBSmv|p?fe3M<+)kX2Axo0M z*U5FtGSX!6h(=x0lVylv4MM`_d%j(3G{?X3 6`#*lM_Q!wxiA(Szr|TjFM8Ep0 zzv>rCrLA7CyM-5fHfxzvwr%9doDy0@{-v2T%}9$7sA@;bFVM<2G~kzJ7*)5bc=JOU z$W OV Wxu+k4)k_P|ZZ_1AtZhNCDt(YaundUf zJ^|h(DVdOnkxVk`AT6RCqDFDrQ>V|ur$6%-@aSWYgG7Ba6aaK%F R;1Xiol$ztH#$6(LHV9&(>n&JhjJxkXi z2@MAEqzYc;>Iw8B-AMHw?Toh a{0+Nc+xNIVu_3>C4z-tDLt~>AeqYr=h z!;il6o$q{6j(5LqmLR6z{LSBV|My>f|AHU-0t2#4K4Z oZiXr6@CcrB zB&WL!M#L+9nLLNom`KI=nn=YMgJo#dbL2N3aiWgHCHcTh*BTi76H-g{{uTy`w&OZk zr`OFk8})o`YrU|swpLnOUoTdhoxFoArP&4g4O{{G%o7j7*{2_eCTag5Of9QAJcniw z^e7KcON3MIB~yyYP^~zQpof@=mrww{{=j4K;3JPCBRf1>mYCmH@f%q~$|oT52V)pB zjEzr_HAZVt!JxT_Zc&zzCN9G;W#6+N7kWu$pvpCLom``>+Sc88Z|!=kwe{YA{`1XG ze)5wqI@IX8S%Nen4qabZSop(S!Q3hs#`NKg{VlXG#JP)$5N%3;oU8#=GSZdnA)4eR zvvDFdEiGbXqSb8i1Pk(8Z@cX_xc61Bgu{mp0pihGz1}dFmzU8+s^*thS96%W@unsw z9267>v!QM-pASsA;^L5HP$8yJYtIZ1gA{VGTMQ&63>Ha25voonloKDQ(v!h|YIval z*z;T~2wbyO-^$f$oA%bmR;Jx`ET5u@(u6jCWDc3H!}b3c5RP0pcZMNKf>7gT_yLNg z4NRAMjZp#q6(OTq^cjsrkvuW_JO#<|zN)^84nxXi83o|sZ{YVXEb(eJIrQ;q4Kr@A z(T!BhkY_7UDwWw4$`^_pG!t{Pl5+dd<3QO?iu9uRyFm7v(?!Z*?16D!@$C5vZ=aYP z@8W(W0QbX-jIIS3yk^3ikgcsRzaj{nf9$t6f1LUY&Yr&rYwKIkXtvomZTB2#cKT?h z(Y#_@K!tZi(u^An^U>cl7Cw(z&qId}!m;DW;G6Eelj@il%^*#*hFRxz+-mija2yBm zNa5$RId5`u!Yh|5Ua4I4(3*sJlLORXp+4|{_;F}evJ@Xi(mKfc9wCEXa&;(=5k(;_ zWNy-G@tscF>NFcxy +xU0ixAbhm$c;^D`O+Bs+?{3>o_^$hxODC;Y$6)wq+ri5 zQ0-uFA^BA_XKuc1tO*z}e+o zhMAtT`m-}=0Vt_9Mw3HOFbW(eF|>{DP#>)`jplE6n$RT|t6pa}mikC&@(5 Ycy-5C7pm{7ZNd(G9T#De^SieHxMKUk@`yZG3$4O~;OgbGd9* zTUuFXL11l6y#shtx_G17J(ml^LNz&=rl!dOz_bdx&(@(@t-=as s(;UkA(er^u4 zmD6w(^9% bgMhzt pRLUatnm7-!BeLbcXp0Wf%6R3HUNeHpddsOG zGQVKxrdqE~<0c>)>%xsY0EtB)go*AWSr@!-nWdH0yZ_UF{?Jc-{No>oAN;`|e9;KP z4HW`4wV;VNsJ*h{{zkSmetrt{x8Oy&12O9&S%kICDkpPj4TK!x*t8%B2}2jP30aCz zt*J}CGHE~3LT%JIHPk%Rx_RpKX`X+2^axpnX^a^rD1nCpH%=QBqyYt{n0bvn#}&wY zqTTC9^-hP&kMNKo@et*(RH0t%gv3kM(gN5t@tM(|5QAUAlk_MLKyXuDw@Q{~BLu_c z542D=#xyIJ&Or@>+|BhBXw=a1H5x$W4~|00WHF#nE>Ow>q==JjB2xQBiFqPzZ&PiM zu8OjSMA#@3N9`Df3T7xt*=~1Xv))82NyDY(0_{T9S1F5OGI~V#KW}C{`bO|drE+$u zya&KFV3eE4sIrWt#9el*ae@#OY9xh$dP-D~kBY*d$E@;q{fGbfdzFuV^rIaFO&)(S z1j{$t5-=%Ycw}|qjGHS77ccg=qo(<0ypa=hff~phcGpO dr22GUwFZy8}G%f2hYu_zcKkivz^ zkv6K#ru+rOCE6SuaGN7Bg(=qsBjqd9kPNaKv@5Ot;-mQ|e7#w(A=7Wb+VUbk)_5aE z2$X9uSRqnc03n7m*-0!C56ex4=E?|{7eds?a>_(mT-PHfOGpbM)Z}T`$3$ekjV@4) zl?gGhx*y!kqs5IjhOVrOOsfcW3|j3n3_M=wkA9;WnE1M^fVDW#mh+2v02?7g{3v5S z=?ckyXS&`h*^DuhH@2{_{OzCq^k;wMu9v^;W54xVzxC+P{oK#JpjD{Pix9*#fAr*2 zosEr}W7VEU)4Q3;=3bw%%!BLInzdD}YoxU@mV;T%46lbk7NJ8>oJ+X|pEJ&UaY-}j zI=e<5VcY0lv=CWYMi=Sa*|VJ3p^PXMh|z#x%8X7-OmeVCbuolAA?8i%D^m|U7qf+= zafQ68aWaqj4x(gG6+Mz=ApvyIA9kEB)DXdH)VCPgWTq$IxZPICCKZ8;NHM=q>jK09 z@Zj1E*EOkzcmfZ&Fb AAht-fdZiE2qFL ThzyH`f-tmq)yg>B43IS1ZW@hb)C!YB8$x`J^IafS` z7xI5b_h=m5AbZPh;014iiO?NYFJ?1zvn>wldOa$`@Yy;j4bI0?>p?+C{wu~3p2|E^ z!i`%AX>=q9hdD$m@+<^Ju8%2m@`wJW_hZP9{9zUbWqruCq2v(-eT3q)TPYAG6XRwDpQ$KdL)fZ%vyuk4N#~{Ty|`zIl)PmHTK1&W{ib_v$tHPttuq3)9zYw)vc{uO$iF7Wr@AY$f;n;tSp&;XG4j<-js _*Ehq@$@(Ol_eCuui}`d_&!o%V1|n0Hk?W}3xNQ*6w_Gk#{-^kruRide<)!7@ z{_qd~@C8GSu6wfVTIr{L`e)myU7p5(?xJO9YIsq{vl(lQ2NId8E`~0wp|KXEz&XoW z(%{KyvXd&l(ZF|W;73QPm08rIQ1OLYx+v~YBTdfQ&zBo%fBD?!ed6iPu|FRlJ5W6g zz8@qENi#r$WQF*lerQ$dpe2YH{#Ga<169feC>L`)7?*x$DCD9VoEK0KC<#J_CWB{W zL7ZtxD?!(RaxQcZPo6r1F~AnPLvq@LNdaM2s$_$GYgcs#O_`v*Kl-h{PcN;rXb9QsKE%C2hCg%bz`aZu-}L^;a4n z``E|M!}Clph$TqV$&)8((eU=>=EbJnb{{nj`@vkX@Ga=KzrNjU&CzP?9mJUtilha@ zQA5gHLcz`Qco&Z>0{XE1zMOIqPr3+HAA|JgRZYAQgUrYk%cn)+( !=?C(#jIE>V`$`6#VUTAvUxe9YPzek3N7t`o5KE){cOF`x5G#r&q7vAbv`B20hV zc-%462bC}iXw3&cKf>V`H@D!KbC)owxXker_3RteL{AogHjqoR62nIj4pqo<|7v8g zm6M7e5hg-@K$ezBA?CmMH>i-F_ov-yFb)eO2{p!Ba+Z`VkKwWo6}!YS1AWGc =vKT3cE(gME6kVtVWu@uKOuFrUda3x#aGP%JKwmy27aV&N>J zWo^T@0;(y*7-$BC >q>z0oqyU0i}Q7cO#%3F&*o%5ZOirVol1MdWiw zzbZ>jesAK-izw0ZTq&v=c0jdmO2#$@E+u e$AV0svw`{y@-k)#Wcvq0te~J= zO`?1r-p9D$5v}gnMTHu@{CP%=UKBzQ6U{uuhjrQ(7oT`|GRWLhE3ABHyWMzsp^(43 z*XvfAt?qcw>6;iNYrbN5LPqjIhTRZ)PC`Lef`r871Y5}QL%<7IM=JRhaiwasy|g=u zM_55pV38m>VmR*56qCokvrIk>n4bM&^_*+`l@P@73YQvSXpeCTYW!Sva puQRlaulEr(f_O0;i5h(kQ6miO3kRP^)aXSe1Zg^a_-=}2 zza+%s*Q?d=nA7aMq2F$N=lbg68|(FExz+Ai }A5tw1(fmXUI3}nVPb(vK)q~C8S|DP;`kH;s3iv*Lb z+5Ak!yY<-N&3j+@vd>J-On#|Snf}W7f%>YZ-8ZWAm^g+85Kyzq!@u@x|K9rC7aurr z>C(asnI!7FrdWck0J%aG4=}q5N_LSDaFOi*DUv^oe~$=LwIamqDt8bIxuFEtG-UiG z{eO`J&K#G;<=cjgRWyuS!l;l~QcyuPQCwdnXQql2YSb<~fv8bFpMTy^qZh3Z#H49F z=jds*{dQw>;oe@S_3GNj#_L !_Q(LAPXMB}lh-3llFb-&= zC`n$)27MS_+DN%iMCpGAa3dt;1#Hu`v^yj_uUHWAn6gMP;2?H>W->f+{P6i>M-Dx7 z^5m_bKXmNaSF`1Vr{Nmt>tFx++ H3um=afq0a&k zq3Z!r$_m7S46#6SVU%LnC`p=;Zzw`(DZ|*Mq4IaWwi1$!f=KleNZ^1GBTHI@TrQu~ zHOZy944#8T6r)CNUk67x3N v$(W!+wcGWAO89~-f?45qZhppY^RFI zoo*S`RlI9o&F1n4=I3+w1XJP3POp6zLZOE+h@S5Eooo-2YyrAUu5>vd 9 z)qju_>8anxmji_ * >LT+spIdH5{T zQm~c3!p!f3WYWYUKpYe3Nv oSjaI(vKm7LZRzC5GPjoQmxgn_0OHK%4QuRnZwXNWdc&fFuRB5!f<^}k3U8g_S zZq(=7?bfZn=TCKd{YlUD3SJPDP|IX7HppQdkVRL;#v5V;L1dy9q6QglC|FiOV8lZb zAt~;cee|@FstE90Jk*eVv@$N5`X)a1N|oYT-p(w~&Q6`1ot?WlF*CDp>m4T-G_AY^ z*MS<%<~+ir87aSpO7=-;k|E)g0Dy>>Zj~hYUX6^2pC@p!rraRe;H0Vv6YQTfJ0zDu z<`ZNfow69Iz&gWmh0NvHnw 4G?f~ z$MHhv9nUF*ew0DK-9i+JhT&>q5a}W*Wo8X0kiog+8kycRG{ec}G95Jc^?WY7RjHIV zCnm>Ni)L 7!Ud*M`6XO~IY<>xm1=Xp%yWEC5pt1df|?BvhJq zv6O`n!>AF-xa+vNuRi$byL*mPIC=w6qdg-8+ev%>`y-%DA}O>znSOtH`N4d-e0w44 z_s4|REA^X=iXZqTr{@$r-?dRyW(f7B@rYyx$XW8Kq%w-UOs?Q$kohqf?TpVHtm@f( zZF%`jtJyUB3g*4xG&ND_nmWp<3dp&fgNxr}=`#xfzZRtuV=yEL66@Dw-cZPvIxTKx zlY2{x$buk&<0MazWRN8zbNMu=MjxszG8AfL#Ib{>dKT#Y3?DZggh#5%R@NFRSej8I zgV)GwwOWP8pFDL#P@_F71XrSi2k(nrsZDqZ((Lr);}hfMF0Bngj82U@z8ZHaQ5$Ii z;$8)*r3WeUB+!qAgM>*8C(@&M`YV-iz+e@ebUBKM7X$}c6BG&&kHX~Vv8AB>tSBR= zUeF{9%p=6(VoV-TEXPI?Nz@44C_;_ERj5&!GULUsKJf5+mRDD=Ypu}>UOTi;&yL>n zp7%s&&t7 0wYRk#S+AeJE_z(RiKpxG#1i h-%Z`EG_aUM{;C(A)#J9?z4z!+kN9sVFQw17dKk+Ke& zsx*yTFyrb|E^X(WLkznr*DHSHJcod|PBrj@OskD~p;pV!=PR}E`@VPAKKt1}^59v} zz7V{G=>s45mHOiH>Wqb!02$+8u~gEytlJBc67U@YkWTdNMsZSk@R39ihL0!I|51`8 zOMMFDB7(X~(4_@Q)mm`_k)ft!sDZ*Nh-RTtqdVEQ#0ZxP->JWqma &wB*+6U& zC^MUC;@ORcn|{=5AoFPajwS}~k*Wq2!618%DUzhAhDcX_K24>FEda}bdSCW{#B)^# zwMH@lHv% e_8*rANjEj%vBV+ zedm?sa)oCN5mAi~HUD#~qhiXqWFk$eLGS#pNI6SFOZ^RlB$-e_wyeBW2vii8o2cZR z7-ZY|^QwefL~&rP95zjkeu) Tyr z+&}r*)xY|}7cRgx(7q7t0ea7Se!}y^sAbvKG-k$c<&lfJoHXfr2r@d(KoAnYUmMC2 zr)f}5p`K~!y|f(&l1QURunjeezf+5Qn$=O%=;SzFh|9~97y)7=DHf!`oWOV*#t>>0 zMS9 Mqo4n;FoT9H;kkRI6ol4J_(MmQ}PN zT7;gge}XvJjTv&-Rnk)bLX7Ang0MYUh$&r$JwT0;F_18*K}sx=I(FP|9Yd#@K4_^m z)VO&=1=(>|o2IhSAO;1FK&Vj|7W;koz}X86NB;e<{@STGz3G2w{@I`X*%eWveIeKb zMC(KRz~BG-{-+x2;@USgcUHA_~G7x)mlf4l;kY+u0T<52^PQr33)dsF9X33Tdsb zrFK{Dkx=g?Og SZ!XxK7PuXr3R(_!$pXFA>PL4;79>Dj6E zU;4#gZ2a!;{_bVVR`vtqJwj@+yzc$?-~T&bfA|~ZF#Lc9rQ2q;gL$gkp~-#gt1HlK zHhHW{?CK1>m{bCQUsGig=`W!Hz`Eek+=+n@FqF`W53#e>NNlS$(v#d>6hp2&T$t*N z;-$9?$kJ#RO;0-1h>WCYPxfUGcc{@=c(?fIBai+RN{F$wy7kfJ<>e<29z59F?b_`N z!5*X6zV@~2pa1;lKlJ#ivlAkW#-n`ks5Ni!;v2+kv`$>N(@v^6K{2M-1w2X%NK$JA zFbHfDiy&(*;*yEj%^B1hNu94N%V4N=A!HA;szTM|ATM6{PzB3_4mBBU$L>!UraRP9 zfx0kww0yl@A3OETGw-OBikmegeDmCH*KFS%+Jp3wk9?%{v;X)XJ>GVlP1MGBYMMDw zP(#Lu2zHdHQIuM8X}ez}27&VsW~$g<4Bi*oAU`0KiAax-9F%d01U;gC;uvLML85^` z!C*l`Bem(OnV+nKq08p~7&T&x1oC^RF{jaJ7dN)HzUlA2a{t7?{pAll`H4^bVPn+w z+82U7O5gq6-|hEB)Pg{3BXb=`j+%(4#S!!Rt|v!DMk3MD!$4^&WKIRgi5zT;5kO1H z%D^j1tc;d^4k~jTe@0mo&Npa-1py^$G$^x(&mB_1c7hu15IVs^ <)WfBA17dcQ2OPiS8V_Antl7*RXoM7gxsY_+F^(5KOr zpq^wSUe}=4=_d0+nbL{0p(==&*$45#uC+$nl5w#KNNSCe&kW2*y2VmU1?@?OSs&CI zrKkA~yI3H5DpVPF96Y1Io84}| |?LYW~R9=xFM*Bjr$LWv$=#N6&+CTAQ zKYFU`xH;61Q } z$9gez=8PbBC^vK#iQf9D|Y?syj*) zAQhm3HZTcd+y1igemGZ vN8MJ)LzvC{r7_#Ng zHq4h)t2O$uM>n^s=YRLV{LaPS`JLbK_l4jlqqn~Gt)A!jEum|u1;b-PGbU)5ZoJ+Y zug5@ZjnN?8SP-;93@}7#cSuVKh6XzVWx0PG6NsUM?LduSU>SyLjS`{LQt^nworD;2 zf8 ~oKS zsF7*Ls8Pr(X|hF73vR@XH9K<&%I{CVGgND|eRoZ{MUZI!#5%-Bicu*I2K@*CLy0}f z^f2U>4W@#~V+L`bb2wP@j#tysXdShlZCh5h-|x3R`u~08(S0Gf35igncOhz&t&|q~ z?e-M%&9q^d5;anY3oX$ayAn*=Zb#b|IHz#`(Wp@hW{vX+ahZjBL({gQMq=>!r1Vcq z1;jAO7{@~Z;(-Gc^vCN^MBH{NU-yHcfN*N-#I463*%yMFln6EYV}%-Z`c4jY?38IC zYLv-X8MFwzSP3sU;wSLqkbAOSDzxMuRXa3zKH4>kAH<;SAu$2NkwsiNnn+2Ud=s(d z7`Q|6fPs*iKj6g&ndxZ~M-;Fy@T9wbKZ8QwpPQZCcZY6vqG0)5@BGp8y>5RVnd7jg zn~WOCl|p@9pfFyKZBQ0H!YpW7N B_E;hHm^AQ+ z!Ky@5BuGL)LL7L56*%dUK0yVAmTmdPVs>d?2=*yKje4#}BO>q642c?1j6j@5?t~Yk zM%&gc7?3vnr`nQYJ8jgyVyM b&->)LwCnnVPTN&N_Qj`fx`1B{EO!ma&^b9i*uX2mNbhBP(++9=I z=0wAbI4q4xOY#D`9#=(ZAjiL_(IAQJ47$efl|T&wPNT4NoMaldmIw|v*5Cm{)8>pa zy(h=*2Rx8iev?oLt{PIzXpwppp%r*RdEXN36H|;D @l6!IHe!N>Hc;9LsquD7eo{d*4BR4-&N(QJPbJyZQ;>?JMe2*B5UI5%XnhG< z- NpW1ha_GyUT{`R+1i`Mdg{_uxCxU#(2iG=pnVyS%V zoTgC)D*M$N>ub
d0VKp88ylm2DL`CYA9(~nORnBog}Z+K~fc=vGbB(LrG!{V%bPgq_`k1L5u)W zHAXTQ5Kp`2d_bs}pIji+{>1z=T%e5>tJ`j|5Ret4&(nUEWr?w|iN}8E?LY7b`|i*_ zU6xEljq->ZX&H-AqcDr8(c}y#pEy{>V6@e!%k>80(gx5JzCXz1$|nP>AU!1MN=2G% zsgbV@VgU)4BH{xQ@{}|P!XpKx2A0KJTyjAcfyzFdUZ4A^>2J<;5VD2;k&w*IA6Pwf z_|W4=j~;zy-xBQ8WhtpO%G%4JfK4qD_h7J$sF7vRy2X)#Bwfc%GRxW!zPXcBTMi6b z_J{XNtO@@kwjoD@3RLbOQyp@| lG=OVEZsx eBS(*X@-1(9%U|Ak=bhDk zcW9rkiel90!-yJPSX}QQ`@K~wm2O35C9A*;$fyze-=Ts~6CM_^W=}aoVvsCLD4A SKO3z2G)QG$ThAnrP2_b3Ya9DF4#@!{!n9XzjFZ@sm4%ZXe51O?!%S^;&x zvuvNPonq9;>$_XXXm@DTp@yiDow50SS!?7bt5 5>oYcgBT%- z1{OgU$H_Gr;#@%d9rPNyMFR5(;+KE pLx(>9wzs`4SxR@`6718nB0`N!3%-=g z+9lBZpA5t7iHXT+S*0mQoj|MEkc;|h!%a8zcf`)nFlv Fs=Tk^Pp}y%yDeK{@Y*wH~7+LQDAH7-?DEB_UTy>p+-OS{omin+UAlUicRFW zdo)AOnWm*`jfAWU;jj@A<8t`?;Tcih3#1->kzv?bEZQpZ@8e_7Q1WI&t*Cr*m2Jk2AJ)u~00! zW8;%BH+KMLX6J$2BsuwR*eOb*r%AY+G{&Uf5$VL4fOPR(Vh7SqHL2qeGK3bOIXg4^ z#LHgxvd_Kuz3+Ws)bH501pD-yh_vb_KJkh6JKpsZPkVjO4ufb41J#mg%k>d>ZR0?8 zbVX{Vnq>VmK0>M#V$>JJt`NvNq>(ftA3-h<#ti*ijvs&cu6yqKgP-}?pZUb$!-q#s zrrsBVeY!66C!hRe^XLD`&z|k{x>I2&4h2CtLA6FigyVZ&pM%#Z5|CaIbDQ%6>8jKC z#iXoa+k5fz?I;vkg+`-+S>d@$w;aFaKfU8e-|?q^@9+J+>Tc)0F9iE^-RPqq{b=jG z|NNgliZQ? 7?YB`GI 1k<4tdR)4xIC`^p!; z_{G+hPIzAk_UXFPo8SCq_hTRX*rm!H$No$|6RlJFY;|?>H3eE^S>gT^C|9?(lB&*u zOEXy4U^{x0B)*hokhA9GKU}+nF@R;6o$;~phcO%c$)ErEpZ{#L+3ehX_ub)@&Us%5 z_UXD4EggkSTm9
A!7GPBKPq$iQ#$+B^XpRr+Ndj8GGxt z+itu6 Drj`qO{ ^^9^yyE3_LUDj{J{5}dgkmqH#fJAQX-8k zLA_Rk4nDf7IVUOi5yN9wh;)50+sLOPIj7G-_ji29cYOLauX)XHzvV4&x&O7VeeD%v zfbFy|1pD;7(MLY=k@Di|>T5REHh=UhU;XMkd%d1U)t)}OME!nWRepvk3oyJsa;O^Q zNq`E4eDC(#?^yo6@B7{lpgZ)L_rL%BYw#>--yPbg=Z(7EE+q>d@*!Nh=kB{MuC1-T zerf5_%QrjiNh)uU^MVpnz_CK8%BABuLis|WKRrFYbn?WBzdmu|#9yKSd>P-kYX5%} z?OTF 4|JM`zXc$kG%Wc@4j%~efKrtInurm?9&TI2&LL9D=Wv(pFe-crAwFYSy@>< z*=n^9J5Dd-`+=cpdSu#GKcCGt$HyzD5%D>T@aH$s0-VPLSQV}lr3>=%_~2-AJj_1r z)ALE%pZv+6 만나서 반가워요! 당신의 성별을 무엇인가요? +성별을 선택해 주세요 + +좋은 %s분 소개시켜 드릴께요! +당신의 나이는 무엇인가요? +년생 +가입 연령 확인하기 +%s년 기준으로 %s년생(만 20살)부터 %s년생(만 35살)까지 가입할 수 있어요 +나이를 입력해 주세요 +가입이 불가한 연령이예요 diff --git a/3days/feat/intro/src/main/java/com/weave/intro/MobileSendAuthScreen.kt b/3days/feat/intro/src/main/java/com/weave/intro/MobileSendAuthScreen.kt index e5936da..254d7e4 100644 --- a/3days/feat/intro/src/main/java/com/weave/intro/MobileSendAuthScreen.kt +++ b/3days/feat/intro/src/main/java/com/weave/intro/MobileSendAuthScreen.kt @@ -101,7 +101,7 @@ fun MobileSendAuthScreen( message = stringResource(id = R.string.next_button_message), type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, isEnabled = isEnabled, - onClick = { onNextBtnClicked(phoneNumber.text) }, + onEnabledClick = { onNextBtnClicked(phoneNumber.text) }, modifier = Modifier .align(Alignment.BottomCenter) .padding( diff --git a/3days/feat/intro/src/main/java/com/weave/intro/TermsAgreementScreen.kt b/3days/feat/intro/src/main/java/com/weave/intro/TermsAgreementScreen.kt index cf55147..6ad4d3c 100644 --- a/3days/feat/intro/src/main/java/com/weave/intro/TermsAgreementScreen.kt +++ b/3days/feat/intro/src/main/java/com/weave/intro/TermsAgreementScreen.kt @@ -73,7 +73,7 @@ fun TermsAgreementScreen( message = stringResource(id = R.string.next_button_message), type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, isEnabled = true, - onClick = { onNextBtnClicked() }, + onEnabledClick = { onNextBtnClicked() }, modifier = Modifier .align(Alignment.BottomCenter) .padding( diff --git a/3days/feat/my-profile/src/androidTest/java/com/weave/my_profile/ExampleInstrumentedTest.kt b/3days/feat/my-profile/src/androidTest/java/com/weave/my_profile/ExampleInstrumentedTest.kt index d33379f..4f09b2f 100644 --- a/3days/feat/my-profile/src/androidTest/java/com/weave/my_profile/ExampleInstrumentedTest.kt +++ b/3days/feat/my-profile/src/androidTest/java/com/weave/my_profile/ExampleInstrumentedTest.kt @@ -1,13 +1,11 @@ package com.weave.my_profile -import androidx.test.platform.app.InstrumentationRegistry import androidx.test.ext.junit.runners.AndroidJUnit4 - +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals import org.junit.Test import org.junit.runner.RunWith -import org.junit.Assert.* - /** * Instrumented test, which will execute on an Android device. * diff --git a/3days/feat/my-profile/src/main/AndroidManifest.xml b/3days/feat/my-profile/src/main/AndroidManifest.xml index a5918e6..44008a4 100644 --- a/3days/feat/my-profile/src/main/AndroidManifest.xml +++ b/3days/feat/my-profile/src/main/AndroidManifest.xml @@ -1,4 +1,4 @@ -+ \ No newline at end of file diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileBirthYearScreen.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileBirthYearScreen.kt new file mode 100644 index 0000000..249a1ad --- /dev/null +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileBirthYearScreen.kt @@ -0,0 +1,379 @@ +package com.weave.my_profile + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.material3.TooltipState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.focus.FocusManager +import androidx.compose.ui.focus.FocusRequester +import androidx.compose.ui.focus.focusRequester +import androidx.compose.ui.focus.onFocusChanged +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.key.Key +import androidx.compose.ui.input.key.KeyEvent +import androidx.compose.ui.input.key.key +import androidx.compose.ui.input.key.onKeyEvent +import androidx.compose.ui.platform.LocalFocusManager +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.KeyboardType +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.withStyle +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel +import com.weave.design_system.DaysTheme +import com.weave.design_system.R +import com.weave.design_system.component.BtnType +import com.weave.design_system.component.DaysNextButton +import com.weave.design_system.component.DaysOnlyBackAppbar +import com.weave.design_system.component.DaysSnackBar +import com.weave.design_system.component.DaysStepIndicator +import com.weave.design_system.component.SnackBarType +import com.weave.design_system.component.tooltip.DaysTooltip +import com.weave.design_system.component.tooltip.TooltipDirection +import com.weave.design_system.extension.addFocusCleaner +import com.weave.design_system.extension.noRippleClickable +import com.weave.utils.Keyboard +import com.weave.utils.keyboardAsState +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import java.time.Year + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MyProfileBirthYearScreen( + sharedViewModel: MyProfileSharedViewModel = hiltViewModel(), + onBackBtnClicked: () -> Unit, + onNextBtnClicked: () -> Unit +) { + val focusManager = LocalFocusManager.current + val isKeyboardVisible by keyboardAsState() + var isEnabled by remember { mutableStateOf(sharedViewModel.birthYear.all { it.isNotEmpty() }) } + val snackState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + val noInputMessage = stringResource(id = R.string.my_profile_birth_year_empty_input_message) + val tooltipState = remember { TooltipState() } + + Scaffold( + modifier = Modifier.fillMaxSize(), + topBar = { + DaysOnlyBackAppbar(onBackPressed = onBackBtnClicked) + }, + snackbarHost = { + SnackbarHost( + modifier = Modifier + .padding(bottom = 110.dp) + .imePadding(), + hostState = snackState, + ) { snackData -> + DaysSnackBar( + message = snackData.visuals.message, + type = if (snackData.visuals.actionLabel == SnackBarType.DEFAULT.toString()) SnackBarType.DEFAULT else SnackBarType.ERROR + ) + } + } + ) { innerPadding -> + Box( + modifier = Modifier + .fillMaxSize() + .addFocusCleaner(focusManager) + .padding( + top = innerPadding.calculateTopPadding(), + ) + ) { + Image( + modifier = Modifier + .fillMaxSize() + .background(DaysTheme.colors.bgDefault), + painter = painterResource(id = R.drawable.texture_bg), + contentDescription = stringResource(id = R.string.background_description) + ) + + Column( + modifier = Modifier + .matchParentSize() + .padding(horizontal = 26.dp) + ) { + Spacer(modifier = Modifier.height(12.dp)) + + DaysStepIndicator(currentStep = 2, totalStep = 5) + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = stringResource(id = R.string.my_profile_birth_year_sub_title, if(sharedViewModel.genderState == "남성") "여성" else "남성"), + style = DaysTheme.typography.regular14.toTextStyle(), + color = DaysTheme.colors.grey200 + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = stringResource(id = R.string.my_profile_birth_year_title), + style = DaysTheme.typography.semiBold24.toTextStyle(), + color = DaysTheme.colors.grey500 + ) + + Spacer(modifier = Modifier.height(40.dp)) + + BirthYearInputRow( + focusManager = focusManager, + birthYear = sharedViewModel.birthYear, + onNumChanged = { index, newValue -> + sharedViewModel.birthYear[index] = newValue + isEnabled = sharedViewModel.birthYear.all { it.isNotEmpty() } + }, + unSupportedYear = false + ) + + Spacer(modifier = Modifier.height(24.dp)) + + DaysTooltip( + tooltipState = tooltipState, + direction = TooltipDirection.Top, + tooltipText = boldBirthYearMessage(), + content = { + Row( + modifier = Modifier + .fillMaxWidth() + .noRippleClickable { + scope.launch { tooltipState.show() } + }, + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + modifier = Modifier.size(18.dp), + painter = painterResource(id = R.drawable.ic_question_mark), + tint = DaysTheme.colors.grey200, + contentDescription = "" + ) + + Spacer(modifier = Modifier.width(4.dp)) + + Text( + text = stringResource(id = R.string.my_profile_birth_year_description), + style = DaysTheme.typography.regular14.toTextStyle(), + color = DaysTheme.colors.grey200 + ) + } + + } + ) + } + + DaysNextButton( + modifier = Modifier + .align(Alignment.BottomCenter) + .padding( + bottom = if (isKeyboardVisible == Keyboard.Closed) innerPadding.calculateBottomPadding() else 0.dp + ), + message = stringResource(id = R.string.next_button_message), + type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, + isEnabled = isEnabled, + onEnabledClick = onNextBtnClicked, + onDisabledClick = { + scope.launch { + val job = launch { + snackState.showSnackbar( + message = noInputMessage, + actionLabel = SnackBarType.ERROR.toString(), + duration = SnackbarDuration.Indefinite + ) + } + delay(3000L) + job.cancel() + } + } + ) + } + } +} + +fun boldBirthYearMessage(): AnnotatedString { + val currentYear = Year.now().value + val twentyYearsOld = currentYear - 20 + val thirtyFiveYearsOld = currentYear - 35 + + return buildAnnotatedString { + append("${currentYear}년 기준으로 ") + withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) { + append("${twentyYearsOld}년생(만 20살)부터 ${thirtyFiveYearsOld}년생(만 35살) ") + } + append("까지 가입할 수 있어요") + } +} + +@Composable +fun BirthYearInputRow( + modifier: Modifier = Modifier, + focusManager: FocusManager, + birthYear: SnapshotStateList , + onNumChanged: (Int, String) -> Unit, + unSupportedYear: Boolean +) { + val focusRequesters = List(4) { FocusRequester() } + val isFocused = remember { mutableIntStateOf(-1) } + val showHint = birthYear.all { it.isEmpty() } + + Row( + modifier = modifier + .fillMaxWidth() + .aspectRatio(314f / 92f), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.Bottom + ) { + birthYear.forEachIndexed { index, _ -> + BasicTextField( + modifier = Modifier + .weight(1f) + .fillMaxSize() + .background( + color = if (unSupportedYear) DaysTheme.colors.pink50 else DaysTheme.colors.yellow50, + shape = RoundedCornerShape(20.dp) + ) + .border( + width = 4.dp, + color = if (unSupportedYear) { + DaysTheme.colors.red300 + } else { + if (isFocused.intValue == index) Color(0xFFDFDBA5) else DaysTheme.colors.white + }, + shape = RoundedCornerShape(20.dp) + ) + .focusRequester(focusRequesters[index]) + .onFocusChanged { focusState -> + if (focusState.isFocused) { + isFocused.intValue = index + onNumChanged(index, "") + } else if (isFocused.intValue == index) { + isFocused.intValue = -1 + } + } + .onKeyEvent { event: KeyEvent -> + if (event.key == Key.Backspace) { + if (birthYear[index].isEmpty()) { + if (index > 0) { + focusRequesters[index - 1].requestFocus() + } + } + return@onKeyEvent true + } + false + }, + value = birthYear[index], + onValueChange = { newValue -> + if (newValue.length == 1 && newValue.all { it.isDigit() }) { + onNumChanged(index, newValue) + + val nextIdx = index + 1 + if (nextIdx < birthYear.size) { + focusRequesters[nextIdx].requestFocus() + } else if (birthYear.all { it.isNotBlank() }) { + focusManager.clearFocus() + } + } + }, + textStyle = DaysTheme.typography.semiBold28.copy( + fontSize = 40.dp, lineHeight = 60.dp, textAlign = TextAlign.Center + ).toTextStyle(), + singleLine = true, + maxLines = 1, + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), + decorationBox = { innerTextField -> + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + if (showHint) { + Text( + modifier = Modifier.fillMaxWidth(), + text = "2000"[index].toString(), + style = DaysTheme.typography.semiBold28.copy( + color = Color(0xFFF1EFCC), + fontSize = 40.dp, + lineHeight = 60.dp, + textAlign = TextAlign.Center + ).toTextStyle() + ) + } + innerTextField() + } + } + ) + } + + Text( + modifier = Modifier.padding(bottom = 4.dp), + text = "년생", + style = DaysTheme.typography.semiBold18.toTextStyle(), + color = DaysTheme.colors.grey300 + ) + } +} + +@Preview(showBackground = true) +@Composable +fun BirthYearInputRowPreview() { + val focusManager = LocalFocusManager.current + val birthYear = remember { mutableStateListOf("", "", "", "") } + + val onNumChanged: (Int, String) -> Unit = { index, newValue -> + birthYear[index] = newValue + } + + BirthYearInputRow( + focusManager = focusManager, + birthYear = birthYear, + onNumChanged = onNumChanged, + unSupportedYear = false + ) +} + +@Preview +@Composable +fun MyProfileBirthYearScreenPreview() { + MyProfileBirthYearScreen( + onNextBtnClicked = {}, + onBackBtnClicked = {} + ) +} \ No newline at end of file diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileGenderScreen.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileGenderScreen.kt index acb17b3..139d4af 100644 --- a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileGenderScreen.kt +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileGenderScreen.kt @@ -7,13 +7,18 @@ import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.padding import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarDuration +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -21,31 +26,54 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp +import androidx.hilt.navigation.compose.hiltViewModel import com.weave.design_system.DaysTheme import com.weave.design_system.R import com.weave.design_system.component.BtnType import com.weave.design_system.component.DaysGenderSelector import com.weave.design_system.component.DaysNextButton import com.weave.design_system.component.DaysOnlyBackAppbar +import com.weave.design_system.component.DaysSnackBar import com.weave.design_system.component.DaysStepIndicator +import com.weave.design_system.component.SnackBarType import com.weave.utils.Keyboard import com.weave.utils.keyboardAsState +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch @Composable fun MyProfileGenderScreen( + sharedViewModel: MyProfileSharedViewModel = hiltViewModel(), onBackBtnClicked: () -> Unit, onNextBtnClicked: () -> Unit ) { val isKeyboardVisible by keyboardAsState() - var isEnabled by remember { mutableStateOf(false) } - var genderState by remember { mutableStateOf("") } + var isEnabled by remember { mutableStateOf(sharedViewModel.genderState.isNotEmpty()) } + val snackState = remember { SnackbarHostState() } + val scope = rememberCoroutineScope() + val noSelectedMessage = stringResource(id = R.string.my_profile_gender_no_selected_message) Scaffold( - modifier = Modifier.fillMaxSize(), + modifier = Modifier + .fillMaxSize() + .imePadding(), topBar = { DaysOnlyBackAppbar(onBackPressed = onBackBtnClicked) + }, + snackbarHost = { + SnackbarHost( + modifier = Modifier + .padding(bottom = 110.dp) + .imePadding(), + hostState = snackState, + ) { snackData -> + DaysSnackBar( + message = snackData.visuals.message, + type = if (snackData.visuals.actionLabel == SnackBarType.DEFAULT.toString()) SnackBarType.DEFAULT else SnackBarType.ERROR + ) + } } - ) { innerPadding -> + ) { innerPadding -> Box( modifier = Modifier .fillMaxSize() @@ -89,24 +117,37 @@ fun MyProfileGenderScreen( Spacer(modifier = Modifier.height(40.dp)) DaysGenderSelector( - genderState = genderState, + genderState = sharedViewModel.genderState, onChangedGender = { newValue -> - genderState = newValue - isEnabled = genderState.isNotEmpty() + sharedViewModel.genderState = newValue + isEnabled = sharedViewModel.genderState.isNotEmpty() } ) } DaysNextButton( - message = stringResource(id = R.string.next_button_message), - type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, - isEnabled = isEnabled, - onClick = { onNextBtnClicked() }, modifier = Modifier .align(Alignment.BottomCenter) .padding( bottom = if (isKeyboardVisible == Keyboard.Closed) innerPadding.calculateBottomPadding() else 0.dp - ) + ), + message = stringResource(id = R.string.next_button_message), + type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, + isEnabled = isEnabled, + onEnabledClick = onNextBtnClicked, + onDisabledClick = { + scope.launch { + val job = launch { + snackState.showSnackbar( + message = noSelectedMessage, + actionLabel = SnackBarType.ERROR.toString(), + duration = SnackbarDuration.Indefinite + ) + } + delay(3000L) + job.cancel() + } + } ) } } @@ -114,7 +155,7 @@ fun MyProfileGenderScreen( @Preview @Composable -fun MyProfileGenderScreenPreview(){ +fun MyProfileGenderScreenPreview() { MyProfileGenderScreen( onNextBtnClicked = {}, onBackBtnClicked = {} diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileInitScreen.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileInitScreen.kt index 183fc93..8f50585 100644 --- a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileInitScreen.kt +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileInitScreen.kt @@ -2,18 +2,24 @@ package com.weave.my_profile import androidx.compose.foundation.Image import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign @@ -21,31 +27,26 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.weave.design_system.DaysTheme import com.weave.design_system.R -import com.weave.design_system.component.BtnType -import com.weave.design_system.component.DaysNextButton -import com.weave.design_system.component.DaysOnlyBackAppbar -import com.weave.utils.Keyboard -import com.weave.utils.keyboardAsState +import kotlinx.coroutines.delay @Composable fun MyProfileInitScreen( - onBackBtnClicked: () -> Unit, onNextBtnClicked: () -> Unit ) { - val isKeyboardVisible by keyboardAsState() + val screenHeight = + LocalDensity.current.run { androidx.compose.ui.platform.LocalContext.current.resources.displayMetrics.heightPixels.toDp() } + + LaunchedEffect(Unit) { + delay(3000) + onNextBtnClicked() + } Scaffold( modifier = Modifier.fillMaxSize(), - topBar = { - DaysOnlyBackAppbar(onBackPressed = onBackBtnClicked) - } ) { innerPadding -> Box( modifier = Modifier .fillMaxSize() - .padding( - top = innerPadding.calculateTopPadding(), - ) ) { Image( modifier = Modifier @@ -55,30 +56,39 @@ fun MyProfileInitScreen( contentDescription = stringResource(id = R.string.background_description) ) + + Box( + modifier = Modifier + .fillMaxWidth() + .height(screenHeight / 2) + .align(Alignment.BottomCenter) + .background( + brush = Brush.verticalGradient( + colors = listOf(Color(0x00F3DDE5), Color(0xFFF3DDE5)) + ) + ) + ) + Column( - modifier = Modifier.matchParentSize(), - horizontalAlignment = Alignment.CenterHorizontally + modifier = Modifier + .matchParentSize() + .padding(bottom = innerPadding.calculateBottomPadding()), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center ) { - Spacer(modifier = Modifier.height(54.dp)) + Image( + modifier = Modifier.size(48.dp), + painter = painterResource(id = R.drawable.ic_reading_glasses), + contentDescription = "" + ) + Spacer(modifier = Modifier.height(24.dp)) Text( text = stringResource(id = R.string.my_profile_init_title), - style = DaysTheme.typography.semiBold24.toTextStyle(), + style = DaysTheme.typography.semiBold20.toTextStyle(), color = DaysTheme.colors.grey500, textAlign = TextAlign.Center ) } - - DaysNextButton( - message = stringResource(id = R.string.next_button_message), - type = if (isKeyboardVisible == Keyboard.Opened) BtnType.Short else BtnType.Tall, - isEnabled = true, - onClick = { onNextBtnClicked() }, - modifier = Modifier - .align(Alignment.BottomCenter) - .padding( - bottom = if (isKeyboardVisible == Keyboard.Closed) innerPadding.calculateBottomPadding() else 0.dp - ) - ) } } } @@ -87,7 +97,6 @@ fun MyProfileInitScreen( @Composable fun MyProfileInitScreenPreview() { MyProfileInitScreen( - onBackBtnClicked = {}, onNextBtnClicked = {} ) } \ No newline at end of file diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileNavGraph.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileNavGraph.kt index bba2e16..3ae67c5 100644 --- a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileNavGraph.kt +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileNavGraph.kt @@ -1,5 +1,10 @@ package com.weave.my_profile +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.ViewModel +import androidx.navigation.NavBackStackEntry import androidx.navigation.NavController import androidx.navigation.NavGraphBuilder import androidx.navigation.compose.composable @@ -9,15 +14,35 @@ fun NavGraphBuilder.navGraphMyProfile(navController: NavController) { navigation(startDestination = "my_profile_init", route = "my_profile") { composable("my_profile_init") { MyProfileInitScreen( - onBackBtnClicked = { navController.popBackStack() }, onNextBtnClicked = { navController.navigate("my_profile_gender") }, ) } composable("my_profile_gender") { + val sharedViewModel = it.sharedViewModel (navController = navController) + MyProfileGenderScreen( + sharedViewModel = sharedViewModel, + onBackBtnClicked = { navController.popBackStack() }, + onNextBtnClicked = { navController.navigate("my_profile_birth") } + ) + } + composable("my_profile_birth") { + val sharedViewModel = it.sharedViewModel (navController = navController) + + MyProfileBirthYearScreen( + sharedViewModel = sharedViewModel, onBackBtnClicked = { navController.popBackStack() }, - onNextBtnClicked = { navController.navigate("my_profile_age") } + onNextBtnClicked = { navController.navigate("next_screen") } ) } } +} + +@Composable +inline fun NavBackStackEntry.sharedViewModel(navController: NavController): T { + val navGraphRoute = destination.parent?.route ?: return hiltViewModel() + val parentEntry = remember(this) { + navController.getBackStackEntry(navGraphRoute) + } + return hiltViewModel(parentEntry) } \ No newline at end of file diff --git a/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileSharedViewModel.kt b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileSharedViewModel.kt new file mode 100644 index 0000000..c5e7197 --- /dev/null +++ b/3days/feat/my-profile/src/main/java/com/weave/my_profile/MyProfileSharedViewModel.kt @@ -0,0 +1,18 @@ +package com.weave.my_profile + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import javax.inject.Inject + +@HiltViewModel +class MyProfileSharedViewModel @Inject constructor( + +): ViewModel() { + + var genderState by mutableStateOf("") + val birthYear = mutableStateListOf("", "", "", "") +} \ No newline at end of file diff --git a/3days/feat/my-profile/src/test/java/com/weave/my_profile/ExampleUnitTest.kt b/3days/feat/my-profile/src/test/java/com/weave/my_profile/ExampleUnitTest.kt index 07019af..d0b6243 100644 --- a/3days/feat/my-profile/src/test/java/com/weave/my_profile/ExampleUnitTest.kt +++ b/3days/feat/my-profile/src/test/java/com/weave/my_profile/ExampleUnitTest.kt @@ -1,9 +1,8 @@ package com.weave.my_profile +import org.junit.Assert.assertEquals import org.junit.Test -import org.junit.Assert.* - /** * Example local unit test, which will execute on the development machine (host). *