From f07773ff0306c91a5fc4c1da568c09622b284815 Mon Sep 17 00:00:00 2001 From: P Date: Wed, 20 Nov 2024 18:25:13 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A90%=20=E5=AE=8C=E6=88=90=E6=96=B0?= =?UTF-8?q?=E5=95=86=E5=9C=BA/=E6=88=91=E7=9A=84=E8=A3=85=E6=89=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- YuMi.xcodeproj/project.pbxproj | 12 + .../mall_item_not_used.imageset/Contents.json | 21 + .../组 8240@3x.png | Bin 0 -> 10738 bytes .../Contents.json | 21 + .../矩形 2941@3x-2.png | Bin 0 -> 21063 bytes .../Contents.json | 21 + .../矩形 2941@3x.png | Bin 0 -> 16995 bytes .../RoomType/RoomType/Contents.json | 6 - .../ShoppingMall/MyDressingDataModel.h | 51 ++ .../ShoppingMall/MyDressingDataModel.m | 77 +++ .../ShoppingMall/MyDressingDataPresent.h | 51 ++ .../ShoppingMall/MyDressingDataPresent.m | 152 +++++ .../ShoppingMall/MyDressingViewController.m | 440 +++++++++++- .../ShoppingMallCategoryListView.h | 12 +- .../ShoppingMallCategoryListView.m | 644 +++++++++++++++--- .../ShoppingMall/ShoppingMallItemPreview.h | 6 +- .../ShoppingMall/ShoppingMallItemPreview.m | 143 +++- .../ShoppingMall/ShoppingMallTagView.h | 7 + .../ShoppingMall/ShoppingMallTagView.m | 47 +- .../ShoppingMall/ShoppingMallViewController.m | 73 +- .../YMMine/View/DressUp/Api/Api+DressUp.h | 9 + .../YMMine/View/DressUp/Api/Api+DressUp.m | 8 + .../View/DressUp/Model/DressUpShopModel.h | 5 + .../View/DressUp/Model/DressUpShopModel.m | 65 +- .../View/DressUp/Model/NameplateModel.h | 2 +- .../View/XPDressSearchViewController.h | 4 +- .../View/XPDressSearchViewController.m | 105 ++- .../View/XPDressUpShopCardViewController.m | 4 +- .../View/XPDressUpShopListViewController.m | 7 +- .../View/XPMineDressUpListViewController.m | 1 - .../YMMine/View/XPMineViewController.m | 15 +- .../Presenter/XPHomeContainerPresenter.m | 1 - .../View/Search/Model/HomeSearchResultModel.h | 4 + .../View/Search/Model/HomeSearchResultModel.m | 11 + .../Search/Presenter/XPHomeSearchPresenter.h | 7 +- .../Search/Presenter/XPHomeSearchPresenter.m | 35 +- .../Search/Protocol/XPHomeSearchProtocol.h | 5 + .../Search/View/SubView/XPHomeSearchNavView.h | 2 + .../View/XPHomePagingViewController.m | 8 - .../CardManager/XPSkillCardPlayerManager.h | 5 +- YuMi/ar.lproj/Localizable.strings | 4 + YuMi/en.lproj/Localizable.strings | 4 + YuMi/tr.lproj/Localizable.strings | 5 + YuMi/zh-Hant.lproj/Localizable.strings | 4 + 44 files changed, 1885 insertions(+), 219 deletions(-) create mode 100644 YuMi/Assets.xcassets/1.0.30/mall_item_not_used.imageset/Contents.json create mode 100644 YuMi/Assets.xcassets/1.0.30/mall_item_not_used.imageset/组 8240@3x.png create mode 100644 YuMi/Assets.xcassets/1.0.30/mu_dressing_card_normal_bg.imageset/Contents.json create mode 100644 YuMi/Assets.xcassets/1.0.30/mu_dressing_card_normal_bg.imageset/矩形 2941@3x-2.png create mode 100644 YuMi/Assets.xcassets/1.0.30/mu_dressing_card_selected_bg.imageset/Contents.json create mode 100644 YuMi/Assets.xcassets/1.0.30/mu_dressing_card_selected_bg.imageset/矩形 2941@3x.png delete mode 100644 YuMi/Assets.xcassets/RoomType/RoomType/Contents.json create mode 100644 YuMi/Modules/ShoppingMall/MyDressingDataModel.h create mode 100644 YuMi/Modules/ShoppingMall/MyDressingDataModel.m create mode 100644 YuMi/Modules/ShoppingMall/MyDressingDataPresent.h create mode 100644 YuMi/Modules/ShoppingMall/MyDressingDataPresent.m diff --git a/YuMi.xcodeproj/project.pbxproj b/YuMi.xcodeproj/project.pbxproj index 38571c52..e37e8b13 100644 --- a/YuMi.xcodeproj/project.pbxproj +++ b/YuMi.xcodeproj/project.pbxproj @@ -499,6 +499,8 @@ 4C6E1F752CEAEC3C0073D0A3 /* ShoppingMallTagView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6E1F742CEAEC3C0073D0A3 /* ShoppingMallTagView.m */; }; 4C6E1F792CEB12780073D0A3 /* UIView+GradientLayer.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6E1F782CEB12780073D0A3 /* UIView+GradientLayer.m */; }; 4C6E1F7C2CEB25B10073D0A3 /* ShoppingMallItemPreview.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6E1F7B2CEB25B10073D0A3 /* ShoppingMallItemPreview.m */; }; + 4CC6195A2CEC7770008C1EE8 /* MyDressingDataPresent.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC619592CEC7770008C1EE8 /* MyDressingDataPresent.m */; }; + 4CC6195D2CEC996E008C1EE8 /* MyDressingDataModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CC6195C2CEC996E008C1EE8 /* MyDressingDataModel.m */; }; 540EC1D02C89925F00F3BF0D /* GiftComboView.m in Sources */ = {isa = PBXBuildFile; fileRef = 540EC1CF2C89925F00F3BF0D /* GiftComboView.m */; }; 540EC1D32C89998500F3BF0D /* GiftComboManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 540EC1D22C89998500F3BF0D /* GiftComboManager.m */; }; 5412E0F42C4E460300FDD668 /* XPMineCenterAgencyView.m in Sources */ = {isa = PBXBuildFile; fileRef = 5412E0F32C4E460300FDD668 /* XPMineCenterAgencyView.m */; }; @@ -2584,6 +2586,10 @@ 4C6E1F782CEB12780073D0A3 /* UIView+GradientLayer.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIView+GradientLayer.m"; sourceTree = ""; }; 4C6E1F7A2CEB25B10073D0A3 /* ShoppingMallItemPreview.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ShoppingMallItemPreview.h; sourceTree = ""; }; 4C6E1F7B2CEB25B10073D0A3 /* ShoppingMallItemPreview.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ShoppingMallItemPreview.m; sourceTree = ""; }; + 4CC619582CEC7770008C1EE8 /* MyDressingDataPresent.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyDressingDataPresent.h; sourceTree = ""; }; + 4CC619592CEC7770008C1EE8 /* MyDressingDataPresent.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyDressingDataPresent.m; sourceTree = ""; }; + 4CC6195B2CEC996E008C1EE8 /* MyDressingDataModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MyDressingDataModel.h; sourceTree = ""; }; + 4CC6195C2CEC996E008C1EE8 /* MyDressingDataModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MyDressingDataModel.m; sourceTree = ""; }; 540EC1CE2C89925F00F3BF0D /* GiftComboView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GiftComboView.h; sourceTree = ""; }; 540EC1CF2C89925F00F3BF0D /* GiftComboView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = GiftComboView.m; sourceTree = ""; }; 540EC1D12C89998500F3BF0D /* GiftComboManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GiftComboManager.h; sourceTree = ""; }; @@ -6600,8 +6606,12 @@ 54283CE22CE48884009729B5 /* ShoppingMall */ = { isa = PBXGroup; children = ( + 4CC6195B2CEC996E008C1EE8 /* MyDressingDataModel.h */, + 4CC6195C2CEC996E008C1EE8 /* MyDressingDataModel.m */, 54283CEC2CE48B97009729B5 /* ShoppingMallDataPresent.h */, 54283CED2CE48B97009729B5 /* ShoppingMallDataPresent.m */, + 4CC619582CEC7770008C1EE8 /* MyDressingDataPresent.h */, + 4CC619592CEC7770008C1EE8 /* MyDressingDataPresent.m */, 54283CE32CE48A69009729B5 /* ShoppingMallViewController.h */, 54283CE42CE48A69009729B5 /* ShoppingMallViewController.m */, 54283CE62CE48ABB009729B5 /* MyDressingViewController.h */, @@ -12284,6 +12294,7 @@ E84A2E992A52817E00D6AF8A /* XPIncomeRecordView.m in Sources */, E801274B27E327DA00BAC3F2 /* XPRoomPKTypeTableViewCell.m in Sources */, 2331C1B02A60F32D00E1D940 /* XPCandyTreeMoreView.m in Sources */, + 4CC6195D2CEC996E008C1EE8 /* MyDressingDataModel.m in Sources */, E8412FB02779CB4D006E1101 /* XPRoomSettingPresenter.m in Sources */, E87DF4B02A429B32009C1185 /* XPFirstRechargeView.m in Sources */, E878893C273A54C300BF1D57 /* Api+Gift.m in Sources */, @@ -12861,6 +12872,7 @@ 239D0FA82BFCB88D002977CE /* XPRoomBackContainerView.m in Sources */, 23D321DF2ADFCEB2006B259C /* PIRedPacketChooseTypeView.m in Sources */, E81AF32827F1EE69003B9E43 /* XPRoomPKPanelUserView.m in Sources */, + 4CC6195A2CEC7770008C1EE8 /* MyDressingDataPresent.m in Sources */, E84150BF27747BD300A7F548 /* Api+FirstRecharge.m in Sources */, E84B0E422727EE0A008818C6 /* XPRoomMessageHeaderView.m in Sources */, 238B37AC2AC55A2C00BFC9D5 /* TreasureFailryMessageModel.m in Sources */, diff --git a/YuMi/Assets.xcassets/1.0.30/mall_item_not_used.imageset/Contents.json b/YuMi/Assets.xcassets/1.0.30/mall_item_not_used.imageset/Contents.json new file mode 100644 index 00000000..fa9dd572 --- /dev/null +++ b/YuMi/Assets.xcassets/1.0.30/mall_item_not_used.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "组 8240@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/YuMi/Assets.xcassets/1.0.30/mall_item_not_used.imageset/组 8240@3x.png b/YuMi/Assets.xcassets/1.0.30/mall_item_not_used.imageset/组 8240@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..13ff6a1f3eb90f5d58f2bcb6077b6cc4961d8fd9 GIT binary patch literal 10738 zcmVPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@ z1ONa40RR91rJw@<1ONa40RR91r2qf`0GpzEbpQYQ(aD+GSpA87rft&eIe z{sn)EqV1>PPpU$cr}9!U+0E|Wncw%^WaB2;y^qaivzy%E6Lxpz+?g|HzIPsH&Ya=Q z3yl((IB`)yQQsbgVJQQKGzyuf7YHc_ajtqR&X4De_2A0SYtF2|-SnZMeW}P=o zc=%yoU|;Y=!Pz&YQb#kUPJ}>;Ipaqu&iX+zy_jNJ#wEm=!5H6-ymxb^K1TjKC09GR z!QVGD`C-q<#~+rJmBlh%Zk}Y==|DN<{#8L0id@K;yd1sV_s~wg0m8%ZJ%H~8?bvQH z^rnXZ(ytg7dz4}u1XF+I!Qk43H;)LlBVSoBx zVn~^W+=zt!ihQYnTRuA^C~0oUj_Q7z(9~e2)ESt| zor4y{@SOKyCwld_P?vSmU~jX%pTDtq@ua%EuT`@ObSO}oLvuKM4S@I?%n1iz&ej9< z^);(=NB9}#oE=23|8K#>5*93Ywj&Mkc@IhgqO3VIKnZ;)8frR#cw|RFotrwK=iG+D zd}%Dkp6tbf)n#QPB5t0~HQKxd<=C>v0$qLj`Haiq0OAnLx%%TX^DE_CYq1pk0GLWJ z_K{NRImW~~9y4C5C>!^A3i6ze&P!1Kw!G?F(3i}CWY5E@xo1vGxP6%W61vfM@RBa5 zoKf;(`>>FHsd)p+dR0!%iHvCz0mNUVU**(KQf~u+gt3^YK^b9CV&%&}!}lG~E50uj z-=i4cZm8%%DZ~N4WHl-#YZ7~;(z3^n;JPqZ_c12>YuwijYM%Z|@Loz8g8-CckT1=3 zi%?z>$}7aDAD?a27B31e%2}Y`?R=Scwv`}Kx4g>~D3SGp{$tMoUarTZ!_cU`U1niJ z&c8z5P-64<6<3=vH=`Ywj{(Hpv1p+Bp|TN3&uqA{Y*iQC7cS;f6$6R`m|*?6;)9^F zIu?tU<4_vuOfwfagWgj;1=Z4uZjr4Ub6qi?)1ds_U5_3Xj`^mcrx}G7>YuSz;;asR z;zp>7o&p&Eztq`h5v3hEs_uhB?a^TL;(c0h!eP2lMMAPZTpB~sBM${g&%jHVDTTg- z{B~m~`0s!gE4zE@^@lPh&kSdjlc4hJD>{rQ-Ky;n9Fk}2kqjBgr+&s2%CT&^m_E3$Ks)& zqX%0Og)oP$5G=H~V#W=-TP9~Wrrkj~dH%|?m^9`?BQh9ot)52p!%*A206?$C_TsDg zFjq{Yj+T$m+*4}oN-W;6k)X~6aT@bZ>NVx7oa?hIrjJ^0SzsF{wi_rXOnBHA92oc| zh7QahdAciWOxU$MSS0##McLRdkPoIxoiktsFZTOS?jdCOLWtpZNa0&)NrJOaAjdmH z)uE~-OWMcqxLrU=3TCK_Cn&{dLU4z<6dvAa4AY7|Uf_!@=nTWOOZhO<9v~FLd{bdM z#A(%z9Y_}eDl@3d?qE^%`}UZRXC0Ict83aK9OXhx^`xW(X6$hl6IF|EAN?=a&v&v1 z>ZeBXT2y;90GiHr>`E@=tcq#ls3KW>70=QbWJ{71fQ zmu0gdQ0!p#~?{{YC z3?s(=gaQXTt_Dzue+RIlUgYz!`*Kmy>$ooMOlWRMMEERhNQ?*2xEk2vU!rd|wAF^?lFFGk)~D-Dko7e1 z?|c!2_Q6isu(lk^+hq7o9I{bi=Q>Ev-va$DUqc~kbYU`oAO?t{HcP<+wHNM6ivK>#TF@Kb7LdC3-|8*i6k0x^xD4kRF zO&F~#fkxy!N4eH7K(`8=ja)c&%mzo#xFE_nC`s)jg!(%&9_Ww{Qaii=JHlUe#w|jJ zGHP=&bE{IYKVYgg6oon3?xj_^l>FjMtBW$a&>_V*=^PhYLh-*4?*_{?aAi z9(fx`VExO_LJMcC2Rvlw@4t>#k1* zAg4I12#cY*FfohW;sMgGiL#r%{^iwm7hE!Iox#L07|Ks}(4GX`Z*!d;{PuB|ZP>W+ zFLdmpRl&iVV4r`mI9LMX+}ZY&CPn>_En+u%#uN@EmSr+(mjbkpo1vVHYfP)J4DgC+ zLF+>4yp`uFW;~9!3f9oJfrjkg(~y1FZ6jc6=7n6Az|;k+y4K0i9OyEC4L%(#e$;j* z^s6hUjUv@v+M!jTEG>Vy4-VU{z_5L>BMvzG3r(iI9@3Gta)O9Xl-3zKbvi$G$PhFy zVsyof(P>+-w`%y7EI1K!;la+BDQ_A+vFGLPJcImv`YGsisLqI54JuOu*O?((a!Cio+mc5=M88V15YNXJ0f z_3%x0c`Ok;I%1gPDyM}8uhbf!jx7d$$@Z{o#e&0K>r z7L}(0kRu&gj!RZ$?Ed1x_Hc#bQVt%ug#-YsBO?*{JcjO>Aai!6s@^T*DDre-k#*7} zSHrit5{v1tBPZ*Va@erPko_jv?X_kVmXu>JGHVi`UM?18KSg6!pwRAV70lV2(6|h7 zv%q!SQ<4*=u%VZMF>62)=QzTEVY__c^ig~0rx)^10u%3C@ts&i-VZ>+dMHgmQnld= ziky@Yew<3dF(J9*!;0!>PBKOhxwNK7?Ta1x1_LK@$VVF5GA=qc@FQi&`LH^qIi%x` zD^-0laE=0g)*$`OR5Xe4j)reKM{I;@@4=ZF((GF^$=SO;A3xta?uv3Kb+l`_(1CK{K3{*hosKiOUa}JNVRvsH;_&-u1OD&YCBhQCIERErfQ#= zE_C4A$qA3>PUp~dGgh>gpj5gx1R6&t%Fj(A7+hpkke8Ml5+GGiT}*?ocXm5CDt5~i z{M!8k7n%;Q?G<@2aqU3P#*mj~57j%gQX==fF=W_iHae;rmjjJm4|}+d|=AO3^jgNkR)ylH()zFr;lJyHMlv zldBIej2yi*GeT-M2{;HR;K0m)D9pl`1Hgr1&yqs_w6`fQUH7Wpx3sKD$ zosCV^L$GW3hpyOd_s+6bY}@2xCe!M?bnf!IaOK{8R`f_8z>exp-i6;tq-(-FR!tmk z-p&LdyCo7O`Hrn6;YeAUF6FMhWCF6LmN?B9bWuU**NGSGZ*NlH<+y1-N)m@G~nqDfeVLe%_dRar+T4+2=u9nyZy;sLBupDFMe zlkT26##(*z^)s%%(N!mt=P&;olcpE97W2kkWJi~@3Ly@HPXb-i z0L&dN6+Z`w|A`Yc8NTuGuy}&S=gyj5p?_JVdr5610rNstDPFGUPcbQu6td z1x75KIr=Y-=vzrdw*~`rNq23UWX;u8iN7(0tPc&hRPDtt%gO@z6BkY!{XqOV@3ZYC zKniJtt7%vv-+xbtyAVqB8%H8AMu6<4G; zR#1gP7lPOlkf>N4Go_c0G_Y%ho99l}%#7C}ZOZrS*^ z!)G!|CX`$$1-k>IaLRcl3M^8g$c6ZPo(gXqzHyA$%`>f#Ir}cqY#Fe` zf5DQ#OKi0S=)4S$C!8LMs(IMAz9@|hVMnW8k%o3_uH0XV74}n5l+$7+Wx$K~J*qq1 z^<(yw`~1fo&y-BLLTbn*cp}l5J*8%d<8ft0m&R33lBc2d|Hu$*cakJ8DgzQ|aE^a1%H!%S%pb>hk8XRfY(^P+%iIaN!9N3bc0kF0d@8OmXFd!V`v%fCnV?jfI*BWO zSgHric*VEIB=`2ZErsMhG42`#y2lDMOm|l~U_OO^=7p+QS>NgE*(?&*YV6Ab$^RF9 zW=c?^A_0Kb)R7As$A#kkz@rJ!WV~WXz^=I3d{o^BZKC~8R%U02YprV(bR=}4>iAHP z(EMDM7vYBRLpZYF6RUG|B3TW=r#~wMq2(?PhV=-xi?(AylvBfkMFn#@DFd!i&^hoW zxKQ<;UGE0=tzJD2J2kl*2sfqW%Rd5#G_OqQWEVIqF6`T*P%ktJ3(<8RV+CTsyB22d zpX6?}^MkWQ*C^6R%QcEb!)@Ujn^vc*$&U(48PIjV3Phu< zk5$JCLDVS0tMjAjbCXM&CKc|lxg1N;o6#`PJGu^GG-{P$Xb&x(F*@(Ic~NI9l9lBO z92$py(l843x~K>WX7sTUQB5owsJ5Z;k}V`q?;1tXnVIfZ9YBgK;98NF>OTJGid#qL zhlZe z4~vr$Sw@m;6m`F^Kzk(76NTj?Ii83ie1{#2IEvF-$MtpC&9R0h=h|kw%)IokB~b4g z#ehFg_5;MGRJDEmMAL8N8U>fHPOt45dWW{%nx^LSFaEyOf|_bZc*O9{dRRje$_J_T z=CdfW#rxN;QRG}m?IhRbHn>Ld>#1F%xXv?&9)Lc451qlV3WP@;y`=3f0^AI5x5}uq zr)AYON*)W_363yj!SKykf4h1bDtge4rZcCTu2B^3I_K&H2aCq$qB32hpck26F|%Z6 z^Tdwyn{Xt!X#@~c%o>U zS$_PILL9)ML(720P+!-RaLXJ!Q6jlUfdjCc;Tq+9yO%nNSjaWXO1MT@&>Bw^N%GC< zD8FPiv?XO1FsHOPlTtBhr}VTdr^BS1i|@tf~)9w=>EkX)m{UT*=m z$1VbCN(+}1*^pXpt&m!2&5oM)^U10wMSZdoO`wFcD(m~3>6Qp`!;uw^a-@)aaE+29 zqH@dz;mCHb2t1NXh*p9}Smn%hL1#uf4Jh-;HHuZcyNzFHAsze3M7 z0i?@MzPUz8x1PED9QMBXE+{N5k3$G4-K#!wxzHryJ?6;Ew9J{B}V0PKITY5)yjnZzybWt$QdG*4nSj#|oq~ZHRTl6xOj>6co z@b*eHRXux+Un(wd#wd}_2v7}q{V|{$6?e}V>z#vl(U7OmmwY`u))`!**fn;0 zrxSh{3-z$%16mB653dFuV|5_3JMK`V`@+SR8RlqWXK{^^qvCI5s{uc>31boX?gF0- z>6*}L;d*Wd2Jn=N84TY6oH?Ph;hQXS%&DZ#<{BlZgr9^NobxJ%XTKs#b2G(u$09te z2ZRWRr12?0mn5tDC@9yF8M$`J{m_|RqvUk!;C3+}jQm!m48YjOwOXiF)a=?{i(axi zB_$G&9)L@Y2vPz6JbPyEkmfFu6uqY8c|$g}04a<5~5z z6i_-bCT;|-Q)bL>^ZZU6&Z!lP7f-^Rl5YVqK9dI^9B0J&pic`{#5OtbM9u(Cph1U?l6pDx**j< zNIS{T4FY9Ooow`6qoiuF+=+)9mLlwY9ZN%Qs(_?J+#qKK4Yxx`#r^}(*(ic}9}!FI z%C%H{v^w5&jp7?P4H>P0YlXAiXGpG5| z=JSU+AV6s`cB4r2q#H05i&5zb#%dmoBx57vRS3dBId8{u)(0Jm_DGtMKR?a%!zRt}>Hc zi~`MFc7@m(E+h2Hoft@x)}G&{w`1 z?hzi69T#;8u2Hy}nT#LHxCGZI6`jO2ilscVoM@T|?hCw*VlAhIxI5^His_?A#@S04 zz7eJP)A58LTRTgf|CylWUZjk*G1gGyCdsAg8R5aGx9iKn}4f zEQ(Qi-NM;pzeuEO((rUAv^4mu0LA`9qL$xPPidI`yjv!v5ksy~{61|d7Fl!gxJy+J zPJKWTE(~o%ZFP9@L!PgmG+Ll79+9+gv37F7*+JmwZP*+%QDQb3zD?FXJ@78?-Srbt zrL?akI@rcd#oX_a*68~@*C?jMJKTTNuDAeol3m5n3H_Vx{k+B6q)AUnnV42XoIhZf zA$q4H>kQu%Te=hk4X#n{1Gtw_UMa|uN0X|D^R;lvJhpPi*m13SF_IVH#kNKQ(FmJ> z=Tqyu8#0Guf+h7zcmi}E#bWHqRJ^AIhbTJi6B!P@1YnZ)+LQsuZ-;Wf?}s2rT8OC#_gtf- zlwU_m5Hfrr224_zTLxg}xn8WWIu%iqz3=8k)Vsm>@^P57*=glB73i^Z5*HkC*C;r} zHV?h&Ma}be@Ec}K>J@2dw|d7D97wC3Nv7PtDkwvt4PfJ2HmrP!-gJEB%o3cVN*+=U z-$edJH1--k)+K+mN$j`kqWW#|7*Yuo?)BeAm<5JD-PXLls zKz4w{ypX@cW5!EXwXy6e=|NI#N16J!RVEE-)zT0@DF~|Fk%)W}+efD(-c~KpB44iF zqBp5qblZDzqt6yJUPMkwVDkKxXE7Oj3W#&yn~4vtoKfU%J0qZN~bd4p}ybVc75uCZw~Tf@(Jo+xbP zm2(87T`AgEI7c!$)d6!mF*UmyQ?oZ6(Aspl2|09&Y~6^Rn3V}p>3;QGqogaj9C;4C z9uMvhTna#rcft}hF#k>XaY9QpR$GWo@>sietsH#mk2e@fT!e&vO%k2|AlE22>3&{O zF}=iYx5#;}9%WV~@P+^QK3JJPh;8T-P_W%-yD6Z@MwidLIqZ~4y9`_8GiBb2^QDrs z)#patQ-eyp15Xs!c%CR)h%~PvCQe*bAdc!v3t!8fKB^RS&A4=44XcfU+rQ(Uu8P&?i!TDOvSWu z)$rnv`)5+S4@_YC2rl@v(m7S%Oo;Qo=9vVj{iq$mww?NiY8GNrOpn=QnrVTO z3Zk>rSXDiUc`mwXo1oNLurObejzO?Z&R%jBBtY#(?FW_MS@y;87_>AMm9xe^V^5o$ zB5hDoc0!4JF>iP^dDhKi)p=5|KaeR~^AzuQ?n;2#i`otCe4aBd<*R~)7CWO&PSq+< zR?HZ^n+XkFx^gzZRBUL((95STSY>GxoSaoJHro=QcB1ye9MX2b+T@M1PdL|SSIoF! zcWZcctfx#>$eVxv^RBapTvZ3g$#9@-Xu1V?x0#(&#<22jyo*Gc^$Zz#8s zR68Y8yHNX3JAs9O6E?8$y(3f|s&YbWN0SjyntRs~wFHG>8`8nThtB9o_H%D2w`-p4 zr#BASXdjfW+jC&H_evI3zt5BnL#v1HIMZ*eT@(KPkSpKC^59QiX`|yRx!z{9%lS8Rss;+7{*H_X+R-+o$1opx0nXZgwQ%N` zoerPz$TBPeQu~;z?D|EGgB}vgTgJ9Y$uE9jR>`(ZvXhbFJCPw`P}9w>|0946eA@;c zR6oY;rGN4i?6$IM69SSu$)DN*1=s_!zFj}k3^gp%fTRj$Z1_%;R{w4z_jKtS*#e95 z?<2l@Vw%PG0dSD{-S7S2C;KkD;M5PvD#?P@OUi%*=sf=5OMW^Oa<4$YM{VUaY*j-3 zX4k)pHoW)VY9}3!(|k?3HUT9uI-vV?+q(9P{SQ`fSE3}R7DY$H9O~jay<7CGlP`K_ z!-nUb8hyB`v`5UC=?k>G?}i31DBo}_6S(scI=g1q#=>>vrO)wmE(kL|r)*yKl)ILH zSH{@m7|Q9!DQD%8&P%c?_`;sOaQdh{&0WPcFT?xoTmp?3s;XEy4o6+@_{sXieOm#@ zkqf7eq4V8s7BW7kY|iP9w}Tt0s|O&rHcw0Wo2*b^uLsNKKbOw0x|~e!Q_^|~jwL{H zY`jp_4Irrmv>&x2wWs3((G{UuNk77tmKU5zfZVC5Q0+ySIV}J^oym6*pcv=uWy4@M zw#6zt6(d;%B{4zHQk4{Q@O4xwrc%fAeMnougm|KI+Nifu(|IW*0lL4E>#_umJQ5v2 z?=+;c5{mIzWIvn6vuQt)bx;zgxLM)|=HtJHYrxqMmYoqt^U6V0q@ZNy4~k%LQ7RvY z%^&JazsI7$cQLusB!(fZYyY4sgP3 z5}Hl$J9MWMfwes%@9oDK5&*Wd2E|Q3p>WHnlrbApa5+@R7b_XR1q_q6uK9U6|VzFI{pO#)=>jjwf{}6_&t>`%)_4(P7 z&ahH9hiSSD0lw!Uk$(6~*Q&pcj<(QA0@H9isd%#&nG>LlS9kK9WmjP6fGIuin`W;W z&x+2OFGBD8xQdET7MG3sGM7*zkfRr><`gJNUX$+r zuM=1_IvKzm3Ff*b$}o-ZpQHDDNhr1+J?Q$HUZH>GqZ2JsdebGXbSEZVK?wB@TsYf0 zm9i684G3s;y&&7GYdgkr4wS?&DSBg3c`97i;J{M46ZR%mkKz}plXeOrmNP>>oey(Q z)0_x7vy(f-rw@_9iwYZY9SOxj4Ty$Wk8S4PxMcpjIcf9avGE*7;3PC{$tNjN{)tM#?@$~bd*=5VO2#7mNZt+7M#>OyoG;J{wR4rYmXPtg1Z(;IS$FyC>!gXiDxz-HC zT^f(i0t)p=O4o)k zCeOpL{v&MV9M@L8OR{8g`P_Yhc zXpFt%QK5nHv`+I0YjDg(=h4-LbW>cu4AZIFC+|U-STS?n8LArwj2_LY4BIsrw86mc3*bB)-TR?hXiH3a^x_6${owP@&dglR=8(D)dQ!W@(QFo zJ;)Z%sy#nz(ChyldV#0ms`>S3MBB4xv%e-A7U1nw=?)FbWJ0aC#135uEB4FL*x$wd zk-eej=>kEJF@syX!(>II8&`)_a~Qok?iONu@EzBCh`;ine{Dw^nwK9vtb8G^+E5N`D4iS{^IvyPPhXg{1m--HPn<_1m`=Y zV*d&E3~kBBJ^Do1w&r`M24!=>nWrWXvcf{6uu%7_KwVS>vEx!KlsXP`x#J-$T(YjZ z14R7++InLVWljYWul^$=5K_A1gYSYjvM=%M9z27EiTV&Tiak<@&CH0^X+o`*}WsbZ%?x*>z+W6BE-O&FgA+n3$Ls zn3!1P*mnZoQ4(k-0e>+&-_f|rglOO!2mYgGVW|1^)-9&fz`xmL@D%xmLpylShDn!L_jRE?(D%_WpJ;MR-S0UUuWmx` z#rh8&p~~FS^|H#UEc&v}0yZ3(_ICK(ixkPQw%($p!uPc%t3Ps~eB7ebE&~1cqwOcH zjDL06N4^iiWfsE>DJ(NbMP0j{b_ldhoA64s~5Wk`9|kS(Os{8 z7We49r)k!D6jbIe93g#liW??&8!7g31v;ka9z8?sN{Cn zqu@^QHN@q-;ewxr9l3Vh`K!kH0j}bs4^Gl{m zj=6B!{7AV%Y|&li1Z-O&zUb2>!MIBqT$!%l+{mN`E)v46ffnHCvD_szGhdIwj+}~{ zpThbK&D+p&J$xXe7aUC=`phXV8sHya*H)-J@4R&Wy7{T6WBqfFUIiOT=BTYY3a4{O z-{4XK{vWk~6z>!OG}(RBU=g|AbdZTy5`5=W1 zzFx-;4x*>LhevE~B@aZp@%5^Mo3vqAGNrU+-te=11|z*iPjRj zIOJS@ZUr$PqAX;JyHxpUu<5#ntJY|6JML6fyJB~ZR#+X6aogkW23q`Z(}W1gUYzHs z^!*8w!kIGbi?m-hj-KmYPGoOK8e0}}Xot`1yhTs#0kuo$33;rw&-r!V*hzKqnN#A_ znv`d9VDDEPd|u94=OgdREcshkM~+L}{$cVl$6qk%tuVUTgU8V~H;a6i64Dw@^9rpC znYlW2G$Fj+tkz7TArZoV6h+my4{Qy`O}SwwA^AnOFxuGlGW|62ou^JJ@_JBsZIgET z^N-FG!V>t`*2`>0si$4=P^Skaaq%r?>63abY@agTk*2Rpn;KZm+>MyC!gGm1+gque z7X(X~oU(6BK&yhhv`W66vkT7r*y@fH=-#Ctk_K(34Stlw^0%x@7NREyv)WukvB|VD zldc=5z#ne~{35j6-k){ftkg8!p7RHuDr|Zz7v&;;RetfDU<)&8u(KPZ^-9jk4)wfi z)tqp`6t^guf;=uLopSqr!+44`TfSYgl(P16`Dt##u#M7Kg$%#Bz^7V=tiz~+g}OfJ zomR_5okn;(=RnGWg#8a2S;6)5wOntxD`kNXvGj(|A`3r|N1-kj!PvY>lEfpf7^+~l zN88$a4Lg$CVoJNRPft-vukm8{==oTivvHFgrc24|lS{?;V7E!K_IgW*w;#>eH6=sv znomtK&HAzYaz1ZeeO#e+lE^{$1-@rYY{1vr;N@)RCzf&xpN$o2@$;*rH>(^kTd&`~ zc?sKJINllbr#0sf?mmP#c=BhqWJ6DbvNGwnqinGf1uh8vW1GMBHKkE3HGZv*KpV8C zv?v!CKr<^J6CX*pV)UvJJDfQCV%{TW^CHR|7L79Uoy@^b!#OR z-7dnKz=@%kiD?PGPGv_9MLbVZvwy4bhy1T`G*qND6hQC@*TNsf71Z+MM3p|Lx< z9(2aa@KV0)h~qUb4ijb-V%oeoCyPc1e=I`YgEl`HiG6~ctD72kIQzRJ6YbIIVCPlU zxP~_vi$_DL6Vbl-59QroCB=S{=*COi_~BH3_sEa=Af;iO3h)j%W@ZQo1JQAS(z8f zh(1*BU@}P^wabp13O#=Xcx+!LR-x&S?0o2{P8aGZtYiK?4)Yv2yramH*oj$`S?~4d zL{Zj$7QtpdRYBoZdnSM+i|;Myn!Hc+T1&sCQq&>KOq-d`<=?v05c}tn@wWL&vjvOR zVAB;@r~Y4rtyxP`t*tf``$hCz*4%m93MBp#p@=&A zf$K@5KJvDzoK!|X6B}PCGhZn@Wu$cGsJ2lsZEAeZlylOI^nKL!FBL4IqFa}> zV`EKe7k%vIJZTWoN$R8{av&;T3Yz88SErgUKtbMLzeQDFl^$jmXo7IzMId5BSHALd z+++dq_4|S@`+^$ZfwER7aJQrjm|fONk3iqn+pNLk*Ivo{d@Xrk^27^qIK^ziBrA`c zLi=K&P<5UZ^o%+x9RVGKT-v|6Wk6g#IKhJJQCyU!j$kk1SALFK)UA}!(93l`BQd5c z=4LOi>EM#glp!-eV@N0`!bADYZ>!zWao{S}zwz!{nz--sLa*^RoEObtg?s$_UYSSU zYc)wR+yv@lM=xFPuamdUkPCBDYb+>^tdZ4I3yEWi=UNdgo;x8Xpm!m4Coh*WZ{+E- z{%+Z41Czz$+=9z_<2$66KU;gxSJxh&ZRnfGAz;Y7gxQ%l!x=ZNii^l6@MirUo-h&>)x zhDbe`7nQtPwqNAgMpQBL{pbKsn&d8(7~N{5*-=d{LPOqE4$GS@A5>oGp!$#kD6~3< z6a!+U{bCBvDubX^L{5tQhPJeT7hLTQD?N6mlo`Bn^Uv79tIgh6W&F(R$lIc~nEw>wR!d9beEUy!KD? zyD#|(#!3NM)fq(0JQD-OOgHfMrS_dL67V+Z>Q|)k}#`P(fIP?u|%0R0l69&$=T?PNT&4X8!(42c<(FeXqJLBBcF1t$%75UwrH{g}}aY zX*lLQL+tt;@#Pq1_k_1>Qb!cy zI%nV>eM27}MM^@{u^O3s6l(D0f9H9f~o0}>*J<#q^3mwaOXj`3pf9g zk5{eit~{Rl|EyPPY?SnimE$iKpkc3og1*5z%-vb)5==Fk#-r6@<)W{b z|HW!1Hg!mUI-cwzqY=J!Bfg-XKPnyVaOi{!_R<-8Z1$)FFxoIiNmWw+@bm9`&5il0 zQNuBQ*p^>oly`=1A-(cd`aMEGqS&obwJIbfC+l=-9tkYxp1TAo|fqwNh9W%A; zR8>Zb?7n4QNKVx-H`@uk$GBkJQ= zU{>4+h|mAdTu$c`Y~Rv< zqV6hWQ#Im(H8^l`3pRVSR7^NJcAMn4(cF4gwOcL*kq6|%cn(AbvhcZ^ZPpJ%a9cBZ zkpsPZ!bJw!x$B|Uei1sjbiy$;#k(hHxBT8%OC0;l&-WgRP+2^h>KmM}5%K4lYl%ny z0izNK;_qUr_U<0r#?&zW!0sUT0``eu_6TUsOL|m@vrG7S!r=HM)REa}7w@Y_Hc0}2 zQb3`21EU0omzdXthshg=8Mp1gYH8#0mF0uS*L`z&?KtjO$&PR8L=RT0!LQ9P!+MIN`qqK(zu4Zn(wH&V_C(ImYg*n;T8jUHa-9oBn!Eya2@QD! z&WNjG(mraz`FDLm4qMyzV7ZH&_h_}6fA>epFQ@7Cun7qB3zUeJNoITpqC~qV<^a7m zl<%KTz~uB0kDROmc2E@ZeQ05ib=1bin83S9zIZu<-**nOyloTTH8{(h@tXx4>E*k~6yUE9R1@*o$z6uz1A#p!&?6g_%! z6wmesMn*KLkLe`t);+dGV7!djY9WzR-+L4xBpbL%U7Yb}IHQb}a|f0^wdx6xn;J1LxUG zeSjn!rXg0^77R;|ZSg~RGk-NXRorE{-12$$g98fE_tdBC66|!fwr){@$X`vA0$rg% zJ4?5KC_9$b#l(Px`h^;6iPi9aean>k{?%rv^p{qH+l)u`uajX0tatZ5{=Y@B>jut0 zKv5UL>Ky(<8uAa;s*5Ca+V3;rT)z@I_NGX0k3S`C@=7;*>B)OrG|gO%&4s9tQs7oB z{tEhrqSmD@r?SPYeL*MW_ZB3{Sh--U+`5k=BJCyU%&BufOLEQ>o~$&1X93nKX|_Tn ztA+t{R&0n~8bT$4zSaCT<$sH=FX-{*y~){iU~2pnCI9^vu{9UxP*XAB^x7zRZtdSz zgxi|G1kr7Xoo)#)CGb~^B0OD@8BHsZ{}PKglPYPkZ(*K#q)Yxn? zn`oxG?>6)~08(~mnTZ;@6sSvzyQ4xQj52Xg&_C7cIDNBo_zjh)CT<<}(cXv0_2FS( z&I>SL=NpJ7%$7vR3o*RQ(2VE7z>_qi=zn?94U+QWw;;#@jCBQL|lg)H$C%uuBs@Wj_z1#SW)QtjUoKXBlxQs$OHxC z2bQu5ZtxcK2W$XVg{8A*JQzovy3TfR>mduv;XlR#<4u#PiW6_PvcsIn{zUo(1e=lK z@fc6egJ6L|VROfQw)-O;Q~+Y8Ks6~ikVMd8{eR++=Faw?3=vL%y^P}7s4Bb%OhSc) zS`s<7z$8ho$&)}zF`x~cPZ#HU8W;Gyb!In1TlK+Ri!Tx>Z3i2?d7RJ(;&;#=_YZd< zW;iNK?*VN({rE=VpM9~D0Sjbb!a4nG_a%xF97yvm#EoK7@}k^;5tmQcCeEA*9$n}n zN76of{*xuIMEh?+3feBFf%*`t6MN-MK2(66l49vG_zXyrK_1VS_CWqAgd!+m@`uD? zq_9_0uZL|_W0n0n5}{LWL@4E5S5-ZdmxEqLUWw?)>2?@3bdWGPr5iVN&hm~;==N%E zmfb%C=k?8Oz5xmz9ChQJog9RoqN4gN9pU@&%jn2Ap%!(>f zBDQHz3e-Lkr^aDrfxX5Z=lxcNULHarl7g`Np#W@=A5}>}JRQqiIo7ZhD6|cn4hm{*NpB^;rMP zE)^#sUgUJ(6vdsTem25$s*+(G3~;1gIf5s5xKPL$I1w*596rnc!1~27E=KCtz>gqF z`ko42A~!6Z_3qvN>bI@+b3t9xQj!qI9-*4cK_=aW$XK;3aL@})e`z3iDATz;WXD3w4TIH1rYVk=AZ8K&it1SH zkCLZ3z|TtImPw%d#Ki}lvy*PL`Sj~U5eYvT04^!C*ITTEhN!T^PaWl)?0RXitcYDb z_7z0mvCC&ZgBCi9fkOON0NXagKcj0bLq1CS3gTn{OEgIL#M#h|@{P8-QU0I( z2z#sly+r-1UE99D?%+>mpbmbljyEm%dW1OKGjUtoFHrxw{Qk?VJLz@dc`3-fIBP02 zD0Uns-A)&v*%quT@aLyJ6_)Yce;|I{BR0LZbPff$bRF>hh=X;+G0K>hZt{P^6eOSU z#lu`0+cJVD*G|^)UED5)VH}1k(%6C{%IFF4qw3hvi*KdeuUGjCZY`9bypBVxxU1v_ zLl=mAbSnGbtyj?TprBydcr7DXTk`*+%{nZ|*FP3xht5%{?Q#6AWLtb0^#PH^PW2?JOf=TfDL=1==C7G7i~zNU76EfjL_NhRM?huNT=&bRajD{_reAQw$aBcCn8EWIIyar=O_<39nslt+;C?0HT)(I=T{zJX}j zCE5R0Ctu}#9U0e^(Kf>MrIe)fLb;q4ubxSWvVB+#a3ii$zF_f@%AFxr!6F2l#}8?bLy z0-ga9&IBl<3nB{t+Z6d4Z~7MI=D6W4Wk2~(*s8RJj4Zci>@qp+WHwRA%dl8piR`Uk zC?MP~nxfPo2_B0mo0|qQU6EJh_+I_>-^}l^P2>?WeVb_rxOmwPW65|4@G~|9ffYlzF@lSYlQ=L(#n084P|p$;=h&P!bz!s z7tJ&Q1YQ-)N0du!5;023g`CtreZ3@8?&Ym;EQN>hO#@ZzhD2(lMujIw z_=bfPeM z&i}c3{ojJ#sKl1raIo5zg0gI(nRYQm^&WW3x&UAzboU(hbgexkVSsFrf2vIiN1Gml=O! z3{NgB{6w00Uifp&O)myvw|^hd*5gpxw9QJvZH|a=ODwLDbnPzMvG9Yd`ZUMxt<>8+ zq;l%DWQVSF2XodBeAU!{BDPd6PChB1tZ}gsIiQu;-mD@gH@FqCrSOORV< z#^Xi4h(mbnY0;|0hYiOmJ8fMcSkc-Ty zj#OoIx)UJgLnodLDR0e*6js6~gK*>@-{Lt zexZotIadT-s_fQM*)zLT(2k(uHR{@>v&E5?L!$f_Ip~d>H=`;cFoQNzzpK8-b$C2P^B-aHSZ;4}%L71dwU_qcqGZm{2Q#vTSx49>x@dq&s^^Hz)eZRYZ}S8I zlInIOLGLNoBcNN)XoU6jtJuD7p~WrGMNhUr>k5oj9UHmp$xBP$t*)JU9bak^A(M(% zuep|j^7jO8>e@&Yz(rr&++7zj`l5G+&ewxhJS?9&H@aq zgDG`<5G89miGt_wr(#>4CfWihc10pRblbq-F<;&{P85gC}ZuBa{u_t zAnwjoJyT4EDT*LUgf;%QihtT`0F=wW?NET~wZQi`9ZUym_+a5xw+2PcDra*>JM)#R zP=*FT=a|uI3xSgs;*5TM;R|24FJmbNk~ule1_}C8oc7AP% zHNHpLm0s{wZAx9voY5@&Bl}Lcr3C;47aVASNWqaFUXc9u!8PdNoxVCc&3O-68Hrwb zzhG=Hql3tA2~R!~|6kkG7Z<`py^vY*dDo=38kv)f3O~HOQ6c)ho4HE^;&#S!EjJASxiv(J2rAVf zWE8^ol}NjrO=E;i`5H!j*Byd=cBLhoMvr0)FSMTzBxc@_Q_5!SS2K_RooJ1RVeZSN z#|TnOI@hN$>_F0%7lgz-ms-28WdahKtUa-oC3u(SY{i2U#@~o+vz}-Zu93di&%IGM zlmuUchV!m)_VM~_ifTl@)OH$9j}!BateHv^PoAQ@|DaW%rdj2^i{Xjuu&uy0l;5p1 z`L0W1Q6In_j1pDGKP^%mt_%0d6!9!m(oo71$Pwl30_=)atxK1Jtz%ue+)#C*KKv+=@Or( zQra>F2D`{zJIr=6KUocN`<@vlcyK4%U9xB}* z0@_z&D&C8zaC=gwbHQ&tz2qwb1-}?det^S;_ee9%6w}IcR`CRC@i_kNuJZzCIEv|f z^B1$fjb6a5X4=#V-is^v=XNJcw6|-8alh~Bnn9!_qK_8$)`g^LbfAClfvu1|R1H6; z-k=5CRe_RzLoTm&L|s*VSiBy4{8bk1EpO*p6!=M$J{3CJfL)WMq|4n)P+r9o?!IT3 zLz2gy;rEEzY631h%>N26luoRGD%#8p)X@5N2ftYjt7b*VSNVs^D%eZmlpPx)|ILW6 z&tMO>Q~Wwv{d zUqnNue&kSjO0gf;Vnjdf=cwvsVB(U`w0GVYu{U#yVwGH#a+hFa_RSvpshSH2dOx4Q z8O-IT99(MtM=4mhkye=5Je-T1G{rwVMBsJOw640JWD6jDaNalj?VfX#%5u0K>XE5! zcAwpVcM<|(JdcYrHi z*A{>7ct=%)QmnkKrjplmqNp#I;vOYIAVfV`aK8c;U&FLbG8XmQP&t?r;nmgJMlV^w zH~aq%-kgaB?MuEVT>z+Ii4ViynN8$7Zno6y5%FhrU}fJ--TrC5R4n09c=i~qaVP4u zMrGYAc?M?%c6wArEj6l#d-ys=unq%t0>Sx4IT6U{p`*~H-w)!5eLrPM zb4=UAHIt>`8#YC=Qo!G-1SYUuI4E7p#JMvc_&6ns1IjYoTa)cP14It7olD5Lv6^D= z$5LPNBY=QAok%MG3Hy5Z@R*y4ZK^fGdM35A`Vm9il(HhH7W69Shw}*Me-X8X_zyC; z;_h)3Try@7CLK${y8;cUhm6m=&eq-U2EF;cdvG4fd-pz_7{5_jPr3teD{*vviB{sG zxL6no(xQuHJhwZz^TX5cM#rB*DJXTcqeONFN7k+G)cWnzeJtP4jDJx$=t_-4zHo2* zoO~_;n#=0e!Y>f@61;UCzFbf+bYiBL0;rx{zZg>(B%y?zDJ%OA)EL+xfX;zL{>a_n zRg7%j-13FVKmF$ZqJItHfJtAs5^?>WF-q~#lSN}N@=A0~=q^9{Xo|XXLWN64RDJ$z zr@ioZ{^{B=7SCHB%x=C4^#ej|FMRd;-$?5}b9so`)c4MI;dqr>Z|x=gq^RNJ*OE>< z%&lklaPmz986VH(1)i2Y(=ob%bO*%&&;KDF#eF0G#ZON!vZPdnCS!W(my3{8?%;Ej zg07U&XqP%pq=lKCk;z@mT%8jN*{1}Q{ya40iqGkl@EA9lMqihI^&;rPrbGhJbjbH~ zx#QldF*mcjXUukw&-F%GBy4+J{iT<|AHN~fzbfiBblC8p+6PxY_wT2dvJ@J;t?#&D z|McjZc~Xy-E9V#zTyx{3B+FSKzy7Y9-(?*Qtnpi50;S+nT>SzjJ6)f_03>_8c* zQxA2YUM{5$p6TP2GG!%&uTQ26C?!$HuU+@q`?jU_xFA@HMQIvS-AJ6hUpA`h_4Fq* zcx}c#kQ|KA@*c*M&t@3J_U$nD zjDXq@RGq!BWJydVUY^!Vw1=(*8Z4tT@kWS4t+p=?Aa5*n949#G1rshy&>)O-J=*S zX|~3a?0j5#!~8Fijs|%tsS!l^`ri%AM*Apra`xCF=1@g_*R$}`PesPvp}=GRUCs@M z_|IEsK^lVX_mb+<+E3^2ony_@&mrJlQ%SbUV6ruiNL1FHZrBV-O zc|D|$LjaT?2S;QOkc5M1Grj^2-_{VzKhoKYuO7hAOUou#nw}<9R;mv%3M}n4b1TT5CH^0{O`0|>u zs-lCw0^GJAkobe0d39(AhT361K3B*_J;@XGFP{+*Q@O5e)%(V9kyIUo&w^STPT4xO*zs8 zX>aAfVx(gCf@4c&t3MO6x#H}YHp)HxF|0BvR_e%u$ib~quuiaA{%a3b2;i2cU~JPg z^{#JMA`D32hj*d=>Y^ovnrvtqYJGIPbgN4TfIrYg3>g`YL#mhd7@f-FSxb_Rn9g6! z5AwR)uUfKigC%k;EAS0FC%EK5{l9x#6i$V|J(#uXW$f+cD{Ry4nq@s({uJ0X%C(IW zJ?(J_%*PY(C;_mSjLMA$j3j`mwVK><1pkB@ECG-=6EQ#c0}O4cX+vfFI={M{&>Cr- zd>Nrpu`)-{dde|ZtZnjMPz+#YE8ntNH#9>93aFHoiD70LRo+u5Lgvj~D`94?61<0p zj&d!vsh?8n_w%_ry}VcOBR~UWb>vI361kX_Mr3pv_S>Y{;w^I$dETrt0lMN4b02Ra zGhe?-iI`vzsG|PPeltS~iUk>-?i2YQv&zrOy_Jh^CVkr*PVX!+R=%<-c<||`3E62z zm-?BxA=?-8vt}+>X(<)pv|FD)rpn*tA!|I-)>v?9fm=es&xJ|1;2}VQ@7N9s*ZqcR zon2jsc++|Zw-((*1~62E)kqayBopCeg7=Kc0zx`&b$fVqW5)RUZ-XvcAMg8^;T`)f z1=OO7?(Y0MOb0*9pM7UpJ1f1_gwk$1ii!ip^Ur)unq|cBG5$cCW?-sjN;|$r{LI16 z`6fN3We~V(iS6b~Hlu&WgLZ{C6H?S{tU}p?ar}L*=>s95l=aeImk3-3=XomvU4(|X z6{)AS+!wR>5Q3sx1O26`V3#Z2!vx*N-O|}l7o|C~?1$KnZAOEa&<14}2$}OZBs{a6 z4h|9jl%6vAic-}wgu_?6U7Zfnb_QP97_ijz62zyzr0~Y5X^hfM72x2S6%KVVXAEF& zjvoTJ$X&O#7@@@&vnC1k6YhpKbLsbw>E>UfHpq>S6*3G2b(aKe?-&qz7T3JH4DvKN z_w!w)PrdAP6?#1~*K)_8J9<1hdCK^@{LZNDwiP7dfL8ExZIfH`EjJCMm`(nn27)^vBW&`-sC>5b8(C>G8T=Ek@&~ zQ-o!4lEOY#ch#~e@VO@2QN}|*VVewkE-P^R1i`rKs+LDns8PP`GJLz|Q_rj@B4blI z2zlT=bTM#Nv&v#OL%yej4*x26knyfhy{MvnDs5D-MKJZk@)dw>v$H(&Rn=w^^gQBq z+w`a)_zldgurX^Wx_E9a ztRY{!=xj%#vyhFNGcZF@(;Z=Q7&Y`IICeFG!Gr<9Wb>5>Gr$F|g?o&j3gT-F$pn_z z1qjMoQNT~}x*ZMT8R&m@ov+q1jHiIAC?JY6adWA0Sa%gS53?4Z6jTFHZwNs3x)<9{bm;o^JaOGyH-3 zU4cevw)l46z3_ty5oY!g8#?tBw1=^FxuBIBF_;9?wMqa+YA3Jki{Ss~yg@2ht@x3t z@xXdZkn! zKf#k=FTcDs`dbP=#$RpIoLksdaL1S!A;ho3K&mkQ?C!L?&@Z2JZPGLiuAbqb6Q?)h zDj#b{fv9;9?6BmiY;trqZpXOqcKzBogVBg-jDcem^ri`UmiHe z*T0t)*dou-+%NreK*0+y+mEXaSi6XG((-;R?XbQ;*^OjbU=xeoA-paGHw@t7^E?ko zxW97lZ3=PjSYvU66vd0_*4xgGds#(2?RJd@BFdeBvsWUW4$!efy8vNW{+jbj-{{Qr z;To|M=bzbz=Zlpd69cexvwfKkx-VtiJw|z8t6M^TCCcyh&jRX@+NLNxy_xk0>5j-C z7vF6(bX6(HODD}m2j19jKe+-N1$nFMj5ORNSSi@0_r^bAo`9XS0#xZKEzKS`oNa`l zDl7pI9kaJ2H%qq;U!Z@ZhuMR2%C#!=+Km{WYiRNdax0!WSa)WK!g_qqrOWgBK;Z`L zyN%4##*M1F-R7FW|M=m+t;v9^pwiRoxq@w(OicTdH+~nu?ETTOYl~ic->xyW=)V1D zBY@(l0PypC2xv?<=RYlq&BCbwB)nKCPb<}%ae4-50(X#LNQhMDjbD&TkCwA5f(&eG zhpz#=WTNY4kx-oQ6bsUIDp!-&^9%>Qr12p@GZSZv5)c#BrF{msutymHt1fDeCpQlx zvim}(j-vZNa{ZmSV1f(7A+{++)bTnKLqGb|+)?(5JOY}E{|O~|J0`BpPAy|H;x2go zt*OuYpYdBo9tDOF`<$l^bwlw)cWdKy(TLENq^2Ix(eebf{n^ z(Q+z@aK1bq6kiWf41bkx^1Qr^=BrwAYcp=P>ObyI(ZcS*ZnbXaGRB!Ypi!P|Xe8l> zeEuE#^_O$Yah4C0s^x%j0kjG?lm8Yly(-w3^Qq3=jp!<3AJuQp+FuIYJg4#sq7vVH z76RzMH{EKuUv_a}Jn$&Sr1u8nZuUBunH`bXW>uN2psUQ->~_ArpH`+1b4@9IX_Wu` z_^F0EhNMZKyT5!U4&)U3XHHJ1VIoMx^M%tP3x7tCo8M&na!-jOTAOxJ0HSAele3^Z zoM=rb`dY&!0$iTV30T#9{XmPM5q^hN(rzC5ecaMuAwB?7$EkN>pLI{D*U~*UKnP!} z2A1z7=-Fin@V?5&n7ENt_RK{;LOAKJ_4mk&gjlb~JK~!FV^!#*udrR-d zN~#Otfv9rRCiMJd-XLP-^~rf|IzQR3Blt> z#CS8(^F7Tf^hdnJ_Kfdh7m?y{sAliuEs+MIf2WhsY2eTmRndqj{4>&@d{yo`qxEq@ z-n`&3Fi{eDB)ayv{`S;|J?mr~(!_NrT*HGeWDp8Ci7hjhz(k=}Q;e{~z4oF#WL}@g zfao6u#d8JBE$Q{}@z& zsIaM0$5!RnkdrNUZjA#04cgZ#9$!B1tm~>!T*ypU*v0vGSaytKX^ir_I&9j=fLFCo z^%%c_JR?i2z)yDL&KXVI=#pG0_5zH-!%dn2rHmf~KEh2|;PlknFk47?&vtM08mrwr zN^buoHg{eGMPsi0PXw+-CjqV2-Q=UhuOM!E&87bG8pq`z!61=&K4?if z+agBaubyNoic?z&tLu#*7N1n+s5nsRr#FslF)N7D1h?Wnu*`K9K^=U3|<5mUHUt72l%M?g#ZY)ag_97;>QcBjiqIG zPfuRGa1-@~3*fbGsc_(^rBvl9C)%oK97Qm@6c%c9?9%5F54pLTP`9)3C+U+yNx<(i zD3H$S+JOTtMs>D>n$H!PS%~i=K=E~ED*v3XNRr)qv1KH~qgy^}>VTsANZZ?@6`oKM z($f4N8WshfoD`TbB+Yonk&Vf?_O&W~$vmGY65|OB9<+O?w1fcrS!pybXnHG|YGShM z(&ryt;$%^LrW>vi^A5TXz&3 z(BwV6jZ1vdAo<9UZYD}MZ4JR06C2PWh;?7(P6<(Tjc>0bhVbPiIQR_`$H$njr2-%e;CY_B}fi(rn; zXRl!8-Wr7Cdk>m>YS~@vF0rhfd!Hm8qAk!C4<*`@^Q6D}+|iDgg-s64-!JlkO{RMA zKeZGFTo3VtT2`eEOCR8ZjciJ6apR5f6TBhD)iMn-U8`;llVpsxwIHX@dKMb8K4-S> zcENlqgG306$t+mw4}IP4-vQ|DvzSc3^7(5wRefSV4K8bEZuri@ucj z^$IAgSAZgLmvYj*=LdG(d#<5jPVxuAnK;gSGsWQt#B-=QfR^29qL-q}{7A!US_)ky2`qcvB6g7kM*@W(fBs(;@O7tW>_^5wkZCB*kv4+=%gEJGyM z`!5mJdg9lSj`{KSG2|RjsHqP=ZG6>VBSoXJ$Dm8G%@SnxFy7_C+7|cr1!)(77TGCt zqTe3#w4LZ{u#}Wp&p1z6Z`0NmEXLBBY=Q0611O|IYeS#72`htkd&J=yAJ#0jv>_0_Uf@_YOiIQIej3G<0? zmXw7h{f4fSK4qY5526Upqsl9>>+a)oYZFJ;Llu)o7VArvd)qINKg%tD9`}S!^q#O3 zjFuvq2AV*u_m&GKmEC4oh|`Bzif+ zBUpEYr@(XpWi7ImH)Xuf+SD6`e_RA(g6ga$aorE5c`pV#2*yc~y7U>7;BotzX`j>8 zi^*-|FGPWr`KOR#BAc-Qc}QCEzSQKKn%MO0)XV+vl6Bh}V~ zQWvV=N^_ZPK8mD`n4spD6D&&V)r;4!7Y#Ulzz>)HnIg)Uti@nPxf_6Zig#?{zM;l|#Xi#H$CENYu8F?PRb zP~Ba=r`cNk@f*AFZt8b&fZf;Cpd1@bc>LpfrU-`(4sox=jRWzEv$w2 zO{2*_Z2P)a6KH%?+evaZ!LO%6ySnp+eTtw}jMcDW@t}G2t{kS~4*9zg^&>wFbE!IEZSu91(&0Q8?Ysk55Su|Ml+!t(JRmlAy zC~=qG`Vqp#Me_R`4f(fO(zPT}Yx#;CJW}{imc54@1~XOm2Zhf!=;@WoSH^1(Q7d4D z0@8)QGi-m_VT+#-VTC&pC3o;Tp<~6?l;NBlW18{mSC27fwi}6&sJ0VzLERl|5bq@W z*O?ZAX5{qKPqMaHIHA|Dr|(ZEc55fuya@aL4Zv#wTn-GHhf8-0iGhr5%$FMUBQCl2psyM2T@nl^j!OZHhZzu{W_aHr?s z^^cKKDS_TzmG2hgw2l^hLf$PNL;_=h+riGLJDFx^iJ@nPpcxfA=eZX~r2RMYcsg_$; zME)xBlqv3&?Z;MIzx(azjzI`bc%lpzUAM-g?A;@3W?PeJ@8}a8p9U??4UNyD_H-xK zJ)g)b`cQd4-{+G=mcU?;A@O}-Qs$|0*$P)>%rnpPzL(d1zwg)UE$o=t(KW07 zSS2DNvc~AI!^cHLL;(>IvHdH417AVNH;#e-h&BARPb7&nDDovK%%(tXR-Z)H!1{dX%i7K5HUJ@ z;2(d{v2KYE=Pp>bvvW&%f$8;K3acty4GuUf*j;^gBYJ~^;VVy#KcruOimzO??Z}7c zCYBBljBYtPsBO5lVRu-#ok{GmOp{3E$`i^P);;i2F&vZ z4Lg_Bl8G{7!g@*rd$ehOPjox6Z8t1iN803&0InrlGU=LCYg4Sct!b)NGC*$ya!Ocl zIlu8&iLFq$A|e)KW~cx75%d)LY+4&E{uG2icYc2$Wzyv}jdTV>eJL^7p!Sz}$d?%y zR{{coWVrF${~Q6lU3FD-Jhto~qRl^=K`&Dk2Y7qtJ{q^lI92NH9|-uJA{GZDviYQ9 z8qLStHsli|BY3u48ij;E;@OOp!7lO9p-q%{OO#)No_@CvBX(GZ7QmcNknE<}*qEY2 z`0olbh#_Ba!{wn3b(4B~G}5Xx46Yq=ymAP;(Y*{O&K@n>5R;J(3QA%<{Z!=*YPp zA;B0nzRR1{-sfjF0<`783Vh}=B}4k33|qJ%bx0%H<8jzAab~?ChX=t&$LMbbO}0W| zV!3JWY~MxJdn+>&Tc0V%q@mzs&fwL`xw>6YpzMsE}084C6o+Jdw<0ZsssnLC@y$s#q=_H zua?Z=h;$?Z(-grko9+(bRd*Yw4yUZ?!qUH-2g1o{UMr!4G|}K6eXzT*`uAM%{|=|g5sG~tc&|+$Iof8Gf4cB;UMg6 zq*f&_1xwN4olKQlhz7eYQWX>GoI`D9Apiwo&#Iu*&3ztIe>bjsCWGeA`8q&MOPI+j zigu+iXDuUGEPb?pLu>`W%xz*IOH{v%1H(86 zDCES~WV4X|#bj=X^|_ z68SWGNB`*J2T7Rq0!Vh)@PO-a7Lpekmox(N^7pB8y#(lBDXv4Tb9nd(Je7pt^jyo( zg`eizk|jB{eD35hI(O4}W@Xv?uZQ|Kt=t|a3Xz4Ei9&-DWM^?`41M}Na_V}0X^;G= zrc^v~DuYwzW_ZLH)02+$VUO6tmrv700YKksKqP3Ek#~rS+ zE%)}=>sjC9-132=E;c!Za}QvnMaRMeyI4RoM%kul|3jNbC=uG9?yk-cg*`+}QzZeU z;*rI5MVRzjq6$1&$uwYF6AWA3qTzro?AGKFMv@htLR_QO1!IZFMm@Wc?DRS&8 zCou`JypB?K`}7n1(F59X8tVIduZV*|D+;y#1JXk+IXW1`o0DtW%%6!dwGFPAtjIqZ z6bv!m{gGi4sqUG;`P`9NF-)~O5aOW6g_UAy)6qm4DPWt~mzbzPkAvl+5U3y!^7EmU zYiRvu>2ZUt^)athDZ~eX;c<2G2;b@5V-M!2tu`z=GY@7R&hiafvdBR(*WWqQBS?Vr z0wO6ZCx;;qYM1uYD#PKG=UpZ;bg?XP2>Nol@xa`r5c;zw8vh;s@^0+veRZ3w^ZJj7 zKoIjGSoXrJH>h;(-I%f6XB@D@hzLV?af@e<5a%ATUP``Bz`U^u@mW7wgi46TvW5^H z;j{PC&mO=Ny3#g4@Td2b;}*g=NgT~Vm1=@0Ax|vWS?_JyJQ%0!m+$C-x=&O~{cqrLjl^-s%neJlhbD*)Y)uXN23 zi0#Ml*|2B6uXyRSd~_kRWEL?EN8`7>D%!YUyWX-L(D-8hv~>o*uB~T}oNnI!%!G#E zIk3F3hbx9(HCd#!rN(sv)5A^7_(#~enV@I)i7${RWgOp%&KrN@J6wKD-D71G3STti zpV|%HoJd8YwvIMra1Xd%Vh<#&yR zPbtHB$kiqoT16C=e_j)N*Uz%jNNfRFZVwyG?CLzz%nlyQTf1exI9@4}m)c>7>Ap#e zDMWl6E$K}`UXX?^z^wtp;A3sxdY^2X^m|XcpPoh4ko3~4)kz*&t-{2hFw)!Tl5 zmEW`+)($K<;hFB;P2qXJ1D>zs(GK4p#NBQmWUpdl>(d^l7xB)Dc{~dsS>uqn zO=yihh2pVK&R;TDPo__9k2Z`~5<2yWmxAUxS5-O&=|{aKEQXRR{uSKI`SK^rNY?hF z^IrL&dv{-!s$usi^ZG~0)3-GG=Bw~Mx!O@DtMI^r=RN3cLd5J_*(|u+__Qp(s_cl0 z!#tk5+j&Fw9s(C@W7aZYsuBS=5@O=*&j!v)0)Jq~fAj}hb1!ENTb$%8iZE3yIi)t24`ckrHD}=bmy{2xzH|2?na54T;yQCQq-;I*U(s*i@)5Y`(~Ipx*%zqOO~GR0U)98& z{@vq#*hdNxXKZ zO1~0LnEH>zWAA}s%3v2zk6d3|Mvy_GAp0~OxvvlYWN~e_{*!6(-+xLI8?~1|!9yIk zXWj2Ljv61=kBI3Fjq?mMXrYzefb8m>~Aos z^fk4ut*B6qwk>dYFPG0_xISFL1s`|br{O`bIVEo^Jsl7H^7BG#R$LB;pg$p1G z{?9pi=Rf8o_SBP~Q1E{Rrt@L{4VX3uL_aHew{{+%r=d!BKotC6bMnpF|CW=jlwIq8 zL_tBR{-1zJ^*;iWBE_>=Tc>{>+In!$?|~@zzvkqxX5m+}@T*z))ht{o`@Vty)hq}a z_+QO}pn?C@EPxICuV&%bWZ~Cj;n!qAblI=T0yv!b)hr0cR6k7nel-jKk2ec1QYaOS zxQcrZSCpq_=$}?xdJ@WKmK1bOZ9HzMf7uTc%**81K>3@M5{-kt zr?u1<=igVv2ln69j{t|X4?E!Msr7JUy|qgp*M=MI=!$;YLm8(&&LxiZi@1JI6D))5 zmGA;-MsyqVaaa0f9AG34kquT06bt@xp&dEVw~lqJq1kbUZCx;AKWnPz*TAY<3x&^G zfAZce>;48Q>k{%%KMcI_jxXc`pFiWNT2^NW3hrh0bJv%xL`>i$`>q7uBV0GG+7c2Z zn518#j{!DqywKOEKj6K+UX9YdhfozfoTN-s=%C+&r}9iJ?ZH>s+mxZ!fwEA}hu$mK zxKFr!=h?Uy&k6b*aK6tTF!9)0@xxeoD4vTr;|J6nU6`Z8Ct~GpS&%2#o-zd+t%BHt0~AHC@jLr{KzBfC)yPT8nIYcR^&ynu@v~VBXD75%}FsZC;8?tk0xetvtx6 zP!86%O`(*bRVdsc6Nm zJ~QH#dG4u=^?FzyJvTA_Wy}D6+(^cZ%f0X#^{>DvnI4JkJ>E~aNBYY}Lp|AmL@uq} z?lFx1CvZA9c-_DX=zJ!hr6F!2GlCV_B~@OVpPB zT{pJG@K<{)1U+!oIl-`%5)wPuwL4tsQx+KwZL7~nH_>1o+%lHaRdMLpwUWnvZy|_@ z1El=xY}|PMvxJvKMAcs8<^H)?z-YN3Qbu8;kWt7Hs$|Y>6=R(fG#m8`ayDtYUPbM! zn^3|u1S0F8ZO^~!auDA8LC0MKDN~^M9*mcOXvf!h`>OA0ihFYBJ9Zjkf=rc~l7zIT z{#KAohvvIb>%}gjm7%7uYZmAh_?`suqGyrEn}^t>3@t|qFFBc^$<;y+Btl0o29lWU z87Doh)6nWlUF(NR5({j|o=EYIsDiYRk9h%4x-8R6$?^W%ucN}j#V+uW3V~Vsff5+? zwz5eRK$|e`&{Y+ZHp_9ke69tW9nhfZbBM7SaWiJ!BFnRphHI_ZYwou5^Sz=e&KeSM zXAMG^81QzU5RPfZbt$@7QJRN_89J6BCCnhIvwR#;UdR?dgnFMO{SLTo?NTx`5mE&L z(j}I9ra4B&TDN0GvB(AdL%Rif7>v!V6X`E&NVFkdo`h0Sevw}!Fq<=?;bC-B8U9=x z-kWG+P3PLnUpQwK&YH36{QP*Pt3Dw0?0Vg)?(DNpT(J!V0Vy&g86i&YDaU$lvFE?Z z&t(5Q`jpZgb>osA2o)_O*v&%Tah|Jcb9EjF&iU}@;inyod>4FAo;Wx2$WFQedH^@X~-=qzg zU^YQ-rH4=5T|(=BAYXfTQTMVj!|ugOW(MCo;$0rJlrc{4w(&_%xV6M?f}E5zm_~ls zk}I3$9-7eh8ZVZEmuJY1Iwx$ayTnh(MlF#M@IC3`9od&JnbS-{Y{AFcYIf|~J)}Bb z8ghfRqw4i83aMv1F_*I^C<)bj$ltSxwa~-9^jMbj8_cJ^o$zh*Haf=m+E1Kk!Gd7`r*9~b1+U$;k?cLJ^xByvfsOZ^5^T9sA4Cm=x* zgH^mMJiy`uF%vLa4UxTC_H|RAIBfPVwAX!DCrAF@FwZGY36MqXtP3H8_E|Y%DsCL& zy-gDA~f&-CA#aIg37 zys%ETx6A-~xp1B2ixl)FzhxduODtb+Sw7T>@)?A(;v!e8()H8rhxM+#-6GUf9FRNT zy~_|Y?ksz!>Z8n*zZ>RQ*_NHXS1JxFz{X(iU+(l)Q~^&MZPAEqqsm>o{3|D=LO%aK z$?=x9!b`$HSOr0K6jI$Tc^KEDlAD5VXqei*ui(bgddi?qZJ$9dv3}DWCmgQPw>8z&%E>i*Y=WmxS(Ai()R{Y_z0gw%$WIT{wln=;)(508JbJUz+wmhc8 zV=AvzdGGgLJ8Z>&p7s-Uy`PidxVmHPmRIu&eYFEY?*fBo-*_n~5+r0tm1FkW^W7(z zlH{Qb{RE$+p2x447PP7`(A^7ag9S@dKwK;A9RJArZPoUb-=q^GCv{HX<0R?|I&T+8 zP;Z^NcKutG3%D+zKBmU(wKLLv2S;B=0j{yx*h6+^``m(i4=LL5rHaI4JwtC*RQ260 z-3HQzWSauSFg8iuzWX8-ZPyzGHYMe}dGNT&t+)Z$*MOb_oLe1wBL~KQ?8~CT8=X67 zOFXIM^>e4xkq|T!wCQiy6Eds!S;c}}XSNQi=P(oGRTV5%0LVNuK@vYhS zA_c{rX1X50--*%Q+ViAB$xR zN2n{3`gbTyo;|auA%MYD8N{o6X;p^Hy0>GlM<22!$PgsJuL$E@So^&U7rF+ghqb(hYZb30pmQ(h^?LJC9}vC*`O2=;tMYNGRcG}M#cejZ zzkC6g^K+n|&;&3g9DQkdS8Ben@9+4rg{}p+2MLD3C*D~!j@0kX$dZR4Ob{|VMWOH% ziOrDyzIAOiqwl&9V32#eZn|lk8=0@`(u6j?*NLT~r6E^qQ}3#O!q(<)zJ|22`qzaXX~-+_o=$2? z4cyr3Gi2$_VEg`2Zp;!aw1YI(H%ZvME`$N_wt{=7fh0|GV@w~@1T19VDa}+3^(9yztF3C! zSxQs-f6F2}U<7IzLd4(+5X%U^Atvm%xN8L0>bry6CN!#vXyruo46O7$C$YG7vgg|& z?T~xkVink6490PKqPy?q`5Ud@{=F9M)q0@Cvn)2_g0QQ=uZVY<39{|3ibV-w{+=9s zB+|3!z=N|+D-au&VvdT-4i9{ON$0n)EVYK|rTRiI^Gw-*$I~CNz8+!0Znw{8xRD4% zc45bevXO;>sTEYp_#2o*8+w&kHJ^qVRj9U9i9*;>Rif+`h4N4eL2^$0C|Ga*Nh)R5 zocvCnx8MsMHYR7Bj>k*QbN}wWnz~XMt(rjn9T$hYPMdbd4*2){KH|}P$SZNoQV@L~ zcA}96 z`B><`TVeZ}<*?a#A)H_rKeQ2x9#~1;vfRVrtuXmO5m+vJZVt_{ziGYHLLKo@U<-C!G_&6>WrRsf{S*io_S;%g zn>ImhUi;HqrYLv*(%0UsD#9!iFt@$KXhhs z=W^3WK{wi!^vv40(|4`%s&CG%9jy}-PKyk+ww;|5?|t*);rEuj3CiP~+>Fg_RhR4A ztW)0p<5dMoC1;Ue_gpJ+MOafW`$#Kv?hl?2X!9m+3>yTsfdHmU!RP`c*DYo`-@4+& zbII$0+E&jiZA>al;jRP%uA)|{2--L_aD2!sQ?uV;4CY%p`TD6%`b%P3G!#Hef&Jhz zi0pk4&{xZ5KQz4|6vGiJb>N_*0ZS`xHfw1!*6Pn8MWmtiCXCDl4!0k>qcA_4Y5UrZUMf?K;FEw8 zqs^oNR_LNu?g6MBOS`0wG@nmx{0sVmg{S+ zg)Eo75B3lO@Y~oTKoumL#E;5*!Ja&i`3Ra#>54dXK08O1)gvYKr#z1q@NR6hgSRVllv-+wsW?+ZwB+C;*7$mTt@EKuRKxfkOZF59TgNP2=*Ry& z^J+RYYF?>1ab9-C-{3GQp;23MuTvB6!8s*_Sy$DajnJ+IVd6d6TE~QfG#7eW)DIs9 z?jtpc;DrT|@{b8FU=TyY;X-U&P-?w796uZ1WWips)ZtQYW3{Qo6cn# zqi$T^oRu(yH*~hR9l1|zj=)ud#yoe+BYp4%w?}N|@YvwV;Ny-LT7uQS%+&?T&h~oQ zgGB=@8y~QC&jvWhGG3x>JO24v@F?bf!iJteS!WCX`>;*mV2@C@3tzFV?@d~_3)jf1 z$9ubttN0{2w$T5YM3itKdBjtJeIJB1^$JF}XbiCOhwYRAqcBf)e*xaU^Nq+>%dtXA zm#G>q>Oo~_xS7r5!v$K2A5W!yHrivXmyv}{=zg>Y0nZ_|1P8nyS{~{R))WWNxfieE z0;cbR2$p8;YCAH(%jser2Db$W8S?mBtmE9i(OaVrC!CI+lj)OIQTs>EGQZk!C;hA` zgSb{W+?tnPlM0yZV5b6;5d0?5R&6DC9b(`v?2@eet!xGMyU(J~fP=v+bv`9~fMAG^ z%;kSRr@A!fC>HmEXq)b&QzyB9Is5~B+bb+U?oPUnR|ZK+dQ;5z(r4eqb3nZE{d@G)|51gEB4if6Vmy&|31xyYLER^7-&4jH zY_Vl5!MsKn!b&|!l{l@L*say7UOoD-H`>I#tnc6$?7ovK-6O?bc3O6@a z>hNL078-1X{;&;a?J9Zhkgf64Y?_=)t-h6E z5TlX`D8_m!gJ%~Ns_u5ykSrH4_~ebO(vva@vl1r1($l-F2*qIC7^>oxW6Q9;$HyR7 zIawgo)=gU3)YZJWrH&`3lBWh6SvN%7^uwy~gB}5{0VR58N~LORZjis`7Z>W;%BR_` z=xvQU%{eU}(-u8Meg;V3g01sa7B2N@2FAH1PSuNhydV@f*rkY{Ht z&yfsb8_aLqKofO8`D;A@>Ev5f7w(RPnMlU(zu4$}%N{GG3>89CrfmM#+29$uZFlFpK{I#ou~?wO@924c^OtGe%1C6JrT>R~NWWO8w+qtR} zm2(G#9b*1rsI}Kz-*lIEpm?&>(Aow$GdtdcZ{)FFRCKp1#CF3G1@q2tqw_Li2MBc0(MZ=SU4G9eE>p@vMW z%gV}f{DZV;5Z+1}+pFm@lkmv4`0z7{sXORsDE-jql?(O^w%47w@1pO<>af5X9=ydS z5p-tXx2UQU_bk~s0&wKp1 zl)epxYEN@5ol=gzD!-d-ks&W<5;E0$zA!Vb6xgPXbmL9s*>c@)zpwGIS7|)Av_&)S zD}%YZ4BPUa7rzK9TE%PBOiqF&kf)M55?~lE}dbFFz&6sl<^Ro^7wp6H;nUiP=?>&ox@5R z&<+`~irz4J8f3?+=$|dHZytZFxn2+u7T3rDWV-)Fu?~qWo+v28Mf8{Xm3&DmNhCH+ zx9o-ze5PADgGG6$8aLMnGZMpxn3i;n;dV{JqDEzwF+1#sLbG6mhi1enZgHekTum?e zE9%y@72AUs8VQNPGJ9PBJJ{BW1GBK+^itJNkN^m;D z>bu4)-j3bF!{sY;kh4v;S{Qa4B9~OGv^#ITvbqpzBp7Addd$ftTec$Vu|%LQ9^sQ? zQ@Vxjm4$VpGJDr%q|@derBbW|x=Sz98d*dARS+{dQ{+smBnmklt(%1J=6+DjBZ@5! zv#^Y1{ku<=PTfGomCWrXz(YDZIqK=BkY4_r39D16ppLUc>@|eUlp4u2+SJXVc!YHb z-#dp_=<9&hYvqRW=fZ}AdZv4ZBT+pt)YNtT1Cg_HgpNVd7 z!KCqcHIB&^G#cNOWgSAfh`3~qA8YgDRQe>#@AsjWUj)A9SIdt$oZ?9G-pVymNQ|ha zvyz`44>^#Q3!m6;SxNuSHdExZ3wq~reYG1kciHrSj6y~>Hx>UXr3Sg~(B%9%;<1Sl z@Eq}U!i=dKV>T@)sg1)yo1sg%QPka|9#{7AE7HPBFWF+l03)bi9ObDjU^YSvkhcpi z@xP1^{;X298$f6jo#S%xU@J~h0eqQLgl&OeX4*mq%F9_tsVGF=*&Lc#4jY2z-5I`9 z%4!^zW+L_(W)cO*ZUnlFxs~zfZ3(RQR6I(Z{Yn`{W7m{871{5uOQzCdxu-Y*bCWK- z2e9^%i5*ud1t}SAf|Hjdh;6n_nr)!_9t=kJBewr*(0E|)^t2a)|0!epwsH{>(Q?7x z0+?>V_h@q4bjRa)R5?FWNkSMBmo2>U$r!myheq1!D@G_qlNwDD3BFv;Sc+UMvFhdeF^;7+%oQel|rX{L$cS1;m@th2Dh|t3+bH2%; z*`&4Eb~kioxaR8ggb6+0a1=c7fBDd;AF-)3sPj%2fBZA`IE4s2I}>ssJdl&LF?#9G z1|$KE#+@;Fv}Uu8(xeTubCh2BljzF}M~TW9j$$5%h=NkJ`3aVKasN!@v*9oLHH1to z^CI9X^NCWccG{fb^#(hHve7^5!+JG;nsc;{mD&j~mD5k9Zl*TYrp1jVG|3Yy04^nb zp?R9c7%1GN&~neD(f&-cm^#L43*array))success + failure:(void(^)(NSError *error))failure; + +- (void)loadNameplate:(NSInteger)page + success:(void(^)(NSArray *array))success + failure:(void(^)(NSError *error))failure; + +- (void)loadRoomCard:(NSInteger)page + success:(void(^)(NSArray *array))success + failure:(void(^)(NSError *error))failure; + +- (void)loadBubble:(NSInteger)page + success:(void(^)(NSArray *array))success + failure:(void(^)(NSError *error))failure; + +- (void)loadMyDressingItems:(NSInteger)type + page:(NSInteger)page + success:(void(^)(NSArray *array, NSInteger type))success + failure:(void(^)(NSError *error))failure; + +- (void)useDressBy:(NSInteger)type + dressID:(NSInteger)dressID + success:(void(^)(UserInfoModel *userInfo))success + failure:(void(^)(NSError *error))failure; + +//- (void)reloadUserInfo:(void(^)(UserInfoModel *userInfo))success +// failure:(void(^)(NSError *error))failure; + +@end + +NS_ASSUME_NONNULL_END diff --git a/YuMi/Modules/ShoppingMall/MyDressingDataPresent.m b/YuMi/Modules/ShoppingMall/MyDressingDataPresent.m new file mode 100644 index 00000000..350a5c17 --- /dev/null +++ b/YuMi/Modules/ShoppingMall/MyDressingDataPresent.m @@ -0,0 +1,152 @@ +// +// MyDressingDataPresent.m +// YuMi +// +// Created by P on 2024/11/19. +// + +#import "MyDressingDataPresent.h" +#import "Api+DressUp.h" + +@implementation MyDressingDataPresent + +- (void)loadHeadWears:(void(^)(NSArray *array))success + failure:(void(^)(NSError *error))failure { + NSString * uid = [AccountInfoStorage instance].getUid; + NSString * ticket = [AccountInfoStorage instance].getTicket; + [Api headwearList:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + NSArray * array = [HeadwearModel modelsWithArray:data.data]; + if (success) { + success(array); + } + } fail:^(NSInteger code, NSString * _Nullable msg) { + if (failure) { + failure([NSError errorWithDomain:msg code:code userInfo:nil]); + } + } showLoading:YES errorToast:YES] ticket:ticket uid:uid]; +} + +- (void)loadVehicles:(void(^)(NSArray *array))success + failure:(void(^)(NSError *error))failure { + NSString * uid = [AccountInfoStorage instance].getUid; + NSString * ticket = [AccountInfoStorage instance].getTicket; + [Api carList:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + NSArray * array = [CarModel modelsWithArray:data.data]; + if (success) { + success(array); + } + } fail:^(NSInteger code, NSString * _Nullable msg) { + if (failure) { + failure([NSError errorWithDomain:msg code:code userInfo:nil]); + } + } showLoading:YES errorToast:YES] ticket:ticket uid:uid]; +} + +- (void)loadNameplate:(NSInteger)page + success:(void(^)(NSArray *array))success + failure:(void(^)(NSError *error))failure { + NSString * uid = [AccountInfoStorage instance].getUid; + NSString * ticket = [AccountInfoStorage instance].getTicket; + [Api nameplateList:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + if (success) { + NSArray *array = [NameplateModel modelsWithArray:data.data[@"nameplateList"]]; + success(array); + } + } fail:^(NSInteger code, NSString * _Nullable msg) { + + } showLoading:YES errorToast:YES] + ticket:ticket + uid:uid + page:@(page).stringValue + pageSize:@"2"]; +} + +- (void)loadRoomCard:(NSInteger)page + success:(void(^)(NSArray *array))success + failure:(void(^)(NSError *error))failure { + NSString * uid = [AccountInfoStorage instance].getUid; + NSString * ticket = [AccountInfoStorage instance].getTicket; + [Api nobleCardList:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + if (success) { + NSArray * array = [NobleCardModel modelsWithArray:data.data]; + success(array); + } + } fail:^(NSInteger code, NSString * _Nullable msg) { + + } showLoading:YES errorToast:YES] + ticket:ticket + uid:uid + page:@(page).stringValue + pageSize:@"2"]; +} + +- (void)loadBubble:(NSInteger)page + success:(void(^)(NSArray *array))success + failure:(void(^)(NSError *error))failure { + [Api chatBubbleList:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + + if (success) { + NSArray *array = [ChatBubbleModel modelsWithArray:data.data]; + success(array); + } + } fail:^(NSInteger code, NSString * _Nullable msg) { + + } showLoading:YES errorToast:YES] page:@(page).stringValue pageSize:@"2"]; +} + +- (void)loadMyDressingItems:(NSInteger)type + page:(NSInteger)page + success:(void(^)(NSArray *array, NSInteger type))success + failure:(void(^)(NSError *error))failure { + [Api mineDressItems:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + if (success) { + NSArray *models = [MyDressingDataModel modelsWithArray:data.data]; + success(models, type); + } + } fail:^(NSInteger code, NSString * _Nullable msg) { + + } showLoading:YES errorToast:YES] + page:@(page).stringValue + pageSize:@"2" + dressType:@(type).stringValue]; +} + +- (void)useDressBy:(NSInteger)type + dressID:(NSInteger)dressID + success:(void(^)(UserInfoModel *userInfo))success + failure:(void(^)(NSError *error))failure { + @kWeakify(self); + [Api optDress:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + @kStrongify(self); + @kWeakify(self); + [self reloadUserInfo:^(UserInfoModel *userInfo) { + if (success) { + success(userInfo); + } + } failure:nil]; + } fail:^(NSInteger code, NSString * _Nullable msg) { + if (failure) { + failure([NSError errorWithDomain:msg code:code userInfo:nil]); + } + } showLoading:YES errorToast:YES] + dressType:@(type).stringValue + dressId:@(dressID).stringValue]; +} + +- (void)reloadUserInfo:(void(^)(UserInfoModel *userInfo))success + failure:(void(^)(NSError *error))failure { + NSString * uid = [[AccountInfoStorage instance] getUid]; + if (uid.length == 0) { + return; + } + @kWeakify(self); + [Api getUserInfo:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + @kStrongify(self); + if (success) { + UserInfoModel * infoModel = [UserInfoModel modelWithDictionary:data.data]; + success(infoModel); + } + } fail:nil showLoading:YES errorToast:YES] uid:uid]; +} + +@end diff --git a/YuMi/Modules/ShoppingMall/MyDressingViewController.m b/YuMi/Modules/ShoppingMall/MyDressingViewController.m index ee9d4e4d..10859aa6 100644 --- a/YuMi/Modules/ShoppingMall/MyDressingViewController.m +++ b/YuMi/Modules/ShoppingMall/MyDressingViewController.m @@ -6,26 +6,448 @@ // #import "MyDressingViewController.h" +#import +#import +#import "UserInfoModel.h" +#import "MyDressingDataPresent.h" +#import "ShoppingMallItemPreview.h" +#import "XPSkillCardPlayerManager.h" +#import "ShoppingMallCategoryListView.h" + +static NSArray *> *MyDressUpOptions(void) { + return @[ + @{YMLocalizedString(@"XPMineDressUpViewController2") : @1}, // 座驾 + @{YMLocalizedString(@"XPMineDressUpViewController1") : @0}, // 头饰 + @{YMLocalizedString(@"XPMineDressUpViewController3") : @2}, // 铭牌 + @{YMLocalizedString(@"XPMineDressUpViewController4") : @3}, // 资料卡 + @{YMLocalizedString(@"XPMineDressUpViewController5") : @4}, // 聊天气泡 + @{YMLocalizedString(@"1.0.30_text_9") : @5} // 個人頁裝飾 + ]; +} @interface MyDressingViewController () +@property(nonatomic, strong) UserInfoModel *userInfo; + +@property(nonatomic, strong) NetImageView *avatarView; +@property(nonatomic, strong) NetImageView *avatarHeadViewImageView; +@property(nonatomic, strong) SVGAImageView *avatarHeadWearSVGA; + +@property (nonatomic, strong) UIView *playEffectMask; +@property (nonatomic, strong) VAPView *mp4Effect; +@property (nonatomic, strong) SVGAImageView *svgaEffect; +@property(nonatomic, strong) UIActivityIndicatorView *loading; + +@property(nonatomic, strong) MyDressingDataModel *selectedModel; + @end @implementation MyDressingViewController +- (MyDressingDataPresent *)createPresenter { + return [[MyDressingDataPresent alloc] init]; +} + +- (BOOL)isHiddenNavBar { + return YES; +} + - (void)viewDidLoad { [super viewDidLoad]; - // Do any additional setup after loading the view. + + self.userInfo = [XPSkillCardPlayerManager shareInstance].userInfoModel; + + [self setupUI]; + [self updateAvatar]; } -/* -#pragma mark - Navigation - -// In a storyboard-based application, you will often want to do a little preparation before navigation -- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { - // Get the new view controller using [segue destinationViewController]. - // Pass the selected object to the new view controller. +- (void)setupUI { + [self setupBackground]; + [self setupNavigationBar]; + [self setupAvatar]; + [self setupContentList]; +} + +- (void)setupBackground { + self.view.backgroundColor = UIColorFromRGB(0x02061D); + UIImageView *topBG = [[UIImageView alloc] initWithImage:kImage(@"mall_top_bg")]; + [self.view addSubview:topBG]; + [topBG mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.leading.trailing.mas_equalTo(self.view); + make.height.mas_equalTo(kGetScaleWidth(200)); + }]; +} + +- (void)setupNavigationBar { + UILabel *titleLabel = [self titleLabel]; + UIButton *backButton = [self backButton]; + [self.view addSubview:titleLabel]; + [self.view addSubview:backButton]; + + [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.mas_equalTo(self.view); + make.top.mas_equalTo(self.view).offset(44 + 11); + make.height.mas_equalTo(22); + }]; + + [backButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.leading.mas_equalTo(self.view).offset(16); + make.centerY.mas_equalTo(titleLabel); + make.size.mas_equalTo(CGSizeMake(22, 22)); + }]; +} + +- (void)setupAvatar { + [self.view addSubview:self.avatarView]; + [self.avatarView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(100); + make.centerX.mas_equalTo(self.view); + make.size.mas_equalTo(CGSizeMake(73, 73)); + }]; + + [self.view addSubview:self.avatarHeadViewImageView]; + [self.avatarHeadViewImageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.center.mas_equalTo(self.avatarView); + make.size.mas_equalTo(self.avatarView).multipliedBy(1.3); + }]; + + [self.view addSubview:self.avatarHeadWearSVGA]; + [self.avatarHeadWearSVGA mas_makeConstraints:^(MASConstraintMaker *make) { + make.center.mas_equalTo(self.avatarView); + make.size.mas_equalTo(self.avatarView).multipliedBy(1.3); + }]; +} + +- (void)setupContentList { + NSInteger bottom = kSafeAreaBottomHeight; + // 内部列表使用 frame 布局,这里要计算 height + ShoppingMallCategoryListView *listView = [[ShoppingMallCategoryListView alloc] initWithFrame:CGRectMake(0, 0, KScreenWidth, KScreenHeight - 100 - 73)]; + listView.isForMyDressingPage = YES; + listView.bottomSpace = bottom; + [self.view addSubview:listView]; + [listView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.avatarView.mas_bottom).offset(14); + make.leading.trailing.bottom.mas_equalTo(self.view); + }]; + @kWeakify(self); + listView.fetchDataForMyDressingPage = ^(NSInteger pageIndex, NSInteger pageNum, FetchDataForMyDressingCompletion _Nonnull completion) { + @kStrongify(self); + [self fetchDataForPage:pageIndex + pageNum:pageNum + completion:completion]; + }; + listView.didTapItemPlay = ^(NSString * _Nonnull resourcePath) { + @kStrongify(self); + [self playItemEffect:resourcePath]; + }; + listView.returnMyDressingSelectedModel = ^(MyDressingDataModel * _Nonnull model, NSInteger type) { + @kStrongify(self); + [self handTapItem:model type:type]; + }; + + listView.items = MyDressUpOptions(); +} + +- (void)updateAvatar { + self.avatarView.imageUrl = self.userInfo.avatar; + if ([NSString isEmpty:self.userInfo.headwearEffect]) { + [self.avatarHeadWearSVGA stopAnimation]; + self.avatarHeadWearSVGA.hidden = YES; + } else { + if ([self.userInfo.headwearEffect.lowercaseString hasSuffix:@"svga"]) { + self.avatarHeadWearSVGA.hidden = NO; + self.avatarHeadViewImageView.hidden = YES; + SVGAParser *p = [[SVGAParser alloc] init]; + [p parseWithURL:[NSURL URLWithString:self.userInfo.headwearEffect] + completionBlock:^(SVGAVideoEntity * _Nullable videoItem) { + self.avatarHeadWearSVGA.videoItem = videoItem; + [self.avatarHeadWearSVGA startAnimation]; + self.avatarHeadWearSVGA.hidden = NO; + } failureBlock:^(NSError * _Nullable error) {}]; + } else { + self.avatarHeadWearSVGA.hidden = YES; + self.avatarHeadViewImageView.hidden = NO; + self.avatarHeadViewImageView.imageUrl = self.userInfo.headwearEffect; + } + } +} + +#pragma mark - +- (void)fetchDataForPage:(NSInteger)pageIndex + pageNum:(NSInteger)pageNum + completion:(FetchDataForMyDressingCompletion)completion { + switch (pageIndex) { + case 0: { + [self.presenter loadMyDressingItems:0 + page:pageNum + success:^(NSArray * _Nonnull array, NSInteger type) { + if (completion) { + completion(array); + } + } failure:^(NSError * _Nonnull error) { + + }]; + } + break; + case 1: { + [self.presenter loadVehicles:^(NSArray * _Nonnull array) { + if (completion) { + NSMutableArray *transArray = @[].mutableCopy; + for (CarModel *car in array) { + MyDressingDataModel *model = [MyDressingDataModel modelFromVehicle:car]; + [transArray addObject:model]; + } + completion(transArray); + } + } failure:^(NSError * _Nonnull error) { }]; + } + break; + case 2: { + [self.presenter loadNameplate:pageNum + success:^(NSArray * _Nonnull array) { + if (completion) { + NSMutableArray *transArray = @[].mutableCopy; + for (NameplateModel *nameplate in array) { + MyDressingDataModel *model = [MyDressingDataModel modelFromNameplate:nameplate]; + [transArray addObject:model]; + } + completion(transArray); + } + } failure:^(NSError * _Nonnull error) { + + }]; + } + break; + case 3: { + [self.presenter loadRoomCard:pageNum + success:^(NSArray * _Nonnull array) { + if (completion) { + NSMutableArray *transArray = @[].mutableCopy; + for (NobleCardModel *nobleCard in array) { + MyDressingDataModel *model = [MyDressingDataModel modelFromNobelCard:nobleCard]; + [transArray addObject:model]; + } + completion(transArray); + } + } failure:^(NSError * _Nonnull error) { + + }]; + } + break; + case 4: { + [self.presenter loadBubble:pageNum + success:^(NSArray * _Nonnull array) { + if (completion) { + NSMutableArray *transArray = @[].mutableCopy; + for (ChatBubbleModel *bubble in array) { + MyDressingDataModel *model = [MyDressingDataModel modelFromChatBubble:bubble]; + [transArray addObject:model]; + } + completion(transArray); + } + } failure:^(NSError * _Nonnull error) { + + }]; + } + break; + case 5: { + [self.presenter loadMyDressingItems:5 + page:pageNum + success:^(NSArray * _Nonnull array, NSInteger type) { + if (completion) { + completion(array); + } + } failure:^(NSError * _Nonnull error) { + + }]; + } + break; + default: + break; + } +} + +- (void)handTapItem:(MyDressingDataModel *)model type:(NSInteger)type { + + if (model.hasExpired) { + // 弹窗购买 + ShoppingMallItemPreview *preview = [[ShoppingMallItemPreview alloc] initWithMyDress:model + isVaild:model.obtainWay<=1]; + preview.frame = CGRectMake(0, 0, 284, 353); + [TTPopup popupView:preview style:TTPopupStyleAlert]; +// @kWeakify(self); +// [preview setBuyWith:^(DressUpShopModel * _Nonnull model) { +// @kStrongify(self); +// @kWeakify(self); +// [self.presenter buyItem:model.dressUpId +// success:^(id _Nonnull obj) { +// @kStrongify(self); +// [TTPopup dismiss]; +// [self showSuccessToast:YMLocalizedString(@"XPDressUpShopListViewController0")]; +// } failure:^(NSError * _Nonnull error) { +// // @kStrongify(self); +// // [self showErrorToast:error.localizedDescription]; +// }]; +// }]; + } else { + // 直接使用 + NSInteger dressID = 0; + if (model) { + dressID = model.dressId; + } + @kWeakify(self); + [self.presenter useDressBy:type + dressID:dressID + success:^(UserInfoModel *userInfo){ + @kStrongify(self); + self.userInfo = userInfo; + [XPSkillCardPlayerManager shareInstance].userInfoModel = userInfo; + [self updateAvatar]; + // TODO: 更新列表的选中状态 + } failure:^(NSError * _Nonnull error) { + + }]; + } +} + +#pragma mark - +- (void)didTapBack { + [self.navigationController popViewControllerAnimated:YES]; +} + +#pragma mark - +- (void)playItemEffect:(NSString *)path { + if (!_playEffectMask) { + [self.view addSubview:self.playEffectMask]; + [self.playEffectMask mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.mas_equalTo(self.view); + }]; + + [self.playEffectMask addSubview:self.svgaEffect]; + [self.svgaEffect mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.mas_equalTo(self.playEffectMask).insets(UIEdgeInsetsMake(0, 0, 0, 0)); + }]; + } + self.playEffectMask.hidden = NO; + [self.loading startAnimating]; + + if ([path.lowercaseString hasSuffix:@"svga"]) { + @kWeakify(self); + SVGAParser *p = [[SVGAParser alloc] init]; + [p parseWithURL:[NSURL URLWithString:path] + completionBlock:^(SVGAVideoEntity * _Nullable videoItem) { + @kStrongify(self); + if (!self.playEffectMask.hidden) { + [self.loading stopAnimating]; + self.svgaEffect.hidden = NO; + self.svgaEffect.videoItem = videoItem; + [self.svgaEffect startAnimation]; + } + } failureBlock:^(NSError * _Nullable error) { + @kStrongify(self); + [self stopItemSVGAEffect]; + }]; + } else if ([path.lowercaseString hasSuffix:@"mp4"]) { + + } +} + +- (void)stopItemSVGAEffect { + [self.loading stopAnimating]; + [self.svgaEffect stopAnimation]; + self.svgaEffect.hidden = YES; + self.playEffectMask.hidden = YES; +} + +#pragma mark - +- (UIButton *)backButton { + UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom]; + [b setImage:[kImage(@"vip_center_back_button") ms_SetImageForRTL] + forState:UIControlStateNormal]; + [b addTarget:self + action:@selector(didTapBack) + forControlEvents:UIControlEventTouchUpInside]; + return b; +} + +- (UILabel *)titleLabel { + UILabel *label = [[UILabel alloc] init]; + label.textAlignment = NSTextAlignmentCenter; + label.font = kFontMedium(17); + label.text = YMLocalizedString(@"1.0.30_text_8"); + label.textColor = UIColorFromRGB(0xD9E7F7); + return label; +} + +- (UIView *)playEffectMask { + if (!_playEffectMask) { + _playEffectMask = [[UIView alloc] initWithFrame:self.view.bounds]; + _playEffectMask.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5f]; + [_playEffectMask addSubview:self.loading]; + [self.loading mas_makeConstraints:^(MASConstraintMaker *make) { + make.center.mas_equalTo(_playEffectMask); + }]; + + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stopItemSVGAEffect)]; + [_playEffectMask addGestureRecognizer:tap]; + } + return _playEffectMask; +} + +- (SVGAImageView *)svgaEffect { + if (!_svgaEffect) { + _svgaEffect = [[SVGAImageView alloc] init]; + _svgaEffect.contentMode = UIViewContentModeScaleAspectFit; + _svgaEffect.autoPlay = YES; + _svgaEffect.loops = 0; + _svgaEffect.clearsAfterStop = YES; + _svgaEffect.hidden = YES; + _svgaEffect.userInteractionEnabled = YES; + } + return _svgaEffect; +} + +- (UIActivityIndicatorView *)loading { + if (!_loading) { + UIActivityIndicatorView *indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhite]; + indicator.translatesAutoresizingMaskIntoConstraints = NO; + indicator.hidesWhenStopped = YES; + _loading = indicator; + } + return _loading; +} + +- (NetImageView *)avatarHeadViewImageView { + if (!_avatarHeadViewImageView) { + _avatarHeadViewImageView = [[NetImageView alloc] init]; + _avatarHeadViewImageView.contentMode = UIViewContentModeScaleAspectFit; + } + return _avatarHeadViewImageView; +} + +- (NetImageView *)avatarView { + if (!_avatarView) { + NetImageConfig * config = [[NetImageConfig alloc]init]; + config.imageType = ImageTypeUserIcon; + config.placeHolder = [UIImageConstant defaultAvatarPlaceholder]; + _avatarView = [[NetImageView alloc] initWithConfig:config]; + [_avatarView setCornerRadius:73/2 + corners:kCALayerMinXMinYCorner | kCALayerMaxXMinYCorner | kCALayerMinXMaxYCorner | kCALayerMaxXMaxYCorner + borderWidth:2 + borderColor:UIColorFromRGB(0xFBD99A)]; + } + return _avatarView; +} + +- (SVGAImageView *)avatarHeadWearSVGA { + if (!_avatarHeadWearSVGA) { + _avatarHeadWearSVGA = [[SVGAImageView alloc] init]; + _avatarHeadWearSVGA.contentMode = UIViewContentModeScaleAspectFit; + _avatarHeadWearSVGA.autoPlay = YES; + _avatarHeadWearSVGA.loops = 0; + _avatarHeadWearSVGA.clearsAfterStop = YES; + } + return _avatarHeadWearSVGA; } -*/ @end diff --git a/YuMi/Modules/ShoppingMall/ShoppingMallCategoryListView.h b/YuMi/Modules/ShoppingMall/ShoppingMallCategoryListView.h index 78ba1092..5f6e4bf2 100644 --- a/YuMi/Modules/ShoppingMall/ShoppingMallCategoryListView.h +++ b/YuMi/Modules/ShoppingMall/ShoppingMallCategoryListView.h @@ -8,21 +8,29 @@ #import #import "DressUpShopModel.h" +#import "MyDressingDataModel.h" NS_ASSUME_NONNULL_BEGIN typedef void (^DidTapPlay)(NSString *resourcePath); + typedef void (^FetchDataCompletion)(NSArray * modelList); typedef void (^FetchDataForPage)(NSInteger pageIndex, FetchDataCompletion completion); - +typedef void (^FetchDataForMyDressingCompletion)(NSArray * modelList); +typedef void (^FetchDataForMyDressingPage)(NSInteger pageIndex, NSInteger pageNum, FetchDataForMyDressingCompletion completion); @interface ShoppingMallCategoryListView : UIView -// 名称:类型 +@property(nonatomic, assign) CGFloat bottomSpace; +@property(nonatomic, assign) CGFloat isForMyDressingPage; + +/// 每个 category 的定义,{名称:类型} @property (nonatomic, copy) NSArray *> *items; @property (nonatomic, copy) FetchDataForPage fetchDataForPage; +@property(nonatomic, copy) FetchDataForMyDressingPage fetchDataForMyDressingPage; @property (nonatomic, copy) DidTapPlay didTapItemPlay; @property(nonatomic, copy) void(^returnSelectedModel)(DressUpShopModel * _Nullable model); +@property(nonatomic, copy) void(^returnMyDressingSelectedModel)(MyDressingDataModel * _Nullable model, NSInteger type); @end diff --git a/YuMi/Modules/ShoppingMall/ShoppingMallCategoryListView.m b/YuMi/Modules/ShoppingMall/ShoppingMallCategoryListView.m index ada8fbba..86ec79a5 100644 --- a/YuMi/Modules/ShoppingMall/ShoppingMallCategoryListView.m +++ b/YuMi/Modules/ShoppingMall/ShoppingMallCategoryListView.m @@ -7,7 +7,10 @@ #import "ShoppingMallCategoryListView.h" +#import +#import "MyDressingDataModel.h" #import "ShoppingMallTagView.h" +#import "XPRoomGiftAnimationParser.h" @interface ShoppingMallEmptyCard : UICollectionViewCell @@ -55,13 +58,273 @@ @end +@interface ShoppingMallRoomCardView : UIView + +@property(nonatomic, copy) NSString *imagePath; +@property(nonatomic, strong) NetImageView *imageView; + +@end + +@implementation ShoppingMallRoomCardView + +- (instancetype)init { + if (self = [super init]) { + self.backgroundColor = [UIColor whiteColor]; + + self.imageView = [[NetImageView alloc] init]; + self.imageView.contentMode = UIViewContentModeScaleAspectFill; + [self addSubview:self.imageView]; + [self.imageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(-6); + make.leading.trailing.mas_equalTo(self); + make.height.mas_equalTo(30); + }]; + + [self setCornerRadius:8]; + } + return self; +} + +- (void)setImagePath:(NSString *)imagePath { + _imagePath = imagePath; + self.imageView.imageUrl = imagePath; +} + +@end + +@interface MyDressingItemCard : UICollectionViewCell + +@property(nonatomic, strong) MyDressingDataModel *model; + +@property(nonatomic, strong) VAPView *mp4View; +@property(nonatomic, strong) XPRoomGiftAnimationParser *vapParser; +@property (nonatomic, strong) UIImageView *bgImageView; +@property (nonatomic, strong) UIView *itemBGView; +@property (nonatomic, strong) NetImageView *itemImageView; +@property(nonatomic, strong) ShoppingMallRoomCardView *roomCardView; +@property (nonatomic, strong) UILabel *titleLabel; +@property (nonatomic, strong) UIButton *playButton; +@property (nonatomic, strong) ShoppingMallTagView *statusLabel; +@property (nonatomic, copy) DidTapPlay didTapPlay; +@end + +@implementation MyDressingItemCard + +- (void)dealloc +{ + [self.mp4View stopHWDMP4]; + [self.mp4View removeFromSuperview]; +} + +- (void)prepareForReuse { + [super prepareForReuse]; + [self.mp4View stopHWDMP4]; +} + +- (void)setSelected:(BOOL)selected { + self.bgImageView.image = selected ? kImage(@"mu_dressing_card_selected_bg") : kImage(@"mu_dressing_card_normal_bg") ; +} + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + self.backgroundColor = [UIColor clearColor]; + + [self.contentView addSubview:self.bgImageView]; + [self.bgImageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.mas_equalTo(self.contentView); + }]; + + [self.contentView addSubview:self.itemBGView]; + [self.itemBGView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.leading.trailing.mas_equalTo(self.contentView).inset(9); + make.height.mas_equalTo(self.itemBGView.mas_width); + }]; + + [self.contentView addSubview:self.itemImageView]; + [self.itemImageView mas_remakeConstraints:^(MASConstraintMaker *make) { + make.center.mas_equalTo(self.itemBGView); + make.width.height.mas_equalTo(self.itemBGView.mas_width).multipliedBy(0.8f); + }]; + + [self.contentView addSubview:self.mp4View]; + [self.mp4View mas_makeConstraints:^(MASConstraintMaker *make) { + make.center.mas_equalTo(self.itemBGView); + make.width.height.mas_equalTo(self.itemBGView.mas_width).multipliedBy(0.8f); + }]; + + [self.contentView addSubview:self.roomCardView]; + [self.roomCardView mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.centerX.mas_equalTo(self.itemBGView); + make.width.height.mas_equalTo(self.itemBGView.mas_width).multipliedBy(0.8f); + }]; + + [self.contentView addSubview:self.playButton]; + [self.playButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.bottom.trailing.mas_equalTo(self.itemBGView).offset(-4); + make.size.mas_equalTo(CGSizeMake(26, 22)); + }]; + + [self.contentView addSubview:self.titleLabel]; + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.leading.trailing.mas_equalTo(self.itemBGView); + make.top.mas_equalTo(self.itemBGView.mas_bottom).offset(5); + make.height.mas_equalTo(20); + }]; + + [self.contentView addSubview:self.statusLabel]; + [self.statusLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.leading.mas_equalTo(self.bgImageView).offset(1); + make.width.mas_equalTo(50); + make.height.mas_equalTo(22); + }]; + } + return self; +} + +- (void)setModel:(MyDressingDataModel *)model { + _model = model; + if (model) { + self.titleLabel.text = model.name; + + if (model.dressType == 3) { + [self play:model.effect]; + self.playButton.hidden = YES; + self.itemImageView.hidden = YES; + } else { + self.playButton.hidden = !([model.effect.lowercaseString hasSuffix:@"svga"] || [model.effect.lowercaseString hasSuffix:@"mp4"]); + self.itemImageView.imageUrl = model.pic; + } + + if (model.hasExpired) { + [self.statusLabel updateBackgroundColors:@[UIColorFromRGB(0xC1CDDB)] textColor:UIColorFromRGB(0x2F3450)]; + } else { + [self.statusLabel updateBackgroundColors:@[UIColorFromRGB(0xA1CAFD)] textColor:UIColorFromRGB(0x172055)]; + } + self.statusLabel.text = [model expiredContent]; + [self.statusLabel mas_updateConstraints:^(MASConstraintMaker *make) { + make.width.mas_equalTo([self.statusLabel textWidth]); + }]; + [self layoutIfNeeded]; + } else { + self.playButton.hidden = YES; + self.itemImageView.image = kImage(@"mall_item_not_used"); + self.titleLabel.text = YMLocalizedString(@"1.0.30_text_12"); + } +} + +- (void)play:(NSString *)effect { + NSString *encodingUrl = [effect stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"`#%^{}\"[]|\\<> "].invertedSet]; + @kWeakify(self); + [self.vapParser parseWithURL:[encodingUrl stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]] completionBlock:^(NSString * _Nullable videoUrl) { + @kStrongify(self); + if (videoUrl.length) { + [self.mp4View setMute:YES]; + [self.mp4View playHWDMP4:videoUrl repeatCount:-1 delegate:self]; + } + } failureBlock:^(NSError * _Nullable error) { + }]; +} + +- (void)handleTapPlay:(id)sender { + if (_didTapPlay) { + self.didTapPlay(self.model.effect); + } +} + +#pragma mark - HWDMP4PlayDelegate +//即将开始播放时询问,true马上开始播放,false放弃播放 +- (BOOL)shouldStartPlayMP4:(VAPView *)container config:(QGVAPConfigModel *)config { + return YES; +} + +#pragma mark - +- (UIImageView *)bgImageView { + if (!_bgImageView) { + _bgImageView = [[UIImageView alloc] initWithImage:kImage(@"mu_dressing_card_normal_bg")]; + } + return _bgImageView; +} + +- (UIView *)itemBGView { + if (!_itemBGView) { + _itemBGView = [[UIView alloc] init]; + _itemBGView.backgroundColor = UIColorFromRGB(0x04184A); + [_itemBGView setCornerRadius:6]; + } + + return _itemBGView; +} + +- (NetImageView *)itemImageView { + if (!_itemImageView) { + _itemImageView = [[NetImageView alloc] init]; + _itemImageView.contentMode = UIViewContentModeScaleAspectFit; + } + return _itemImageView; +} + +- (ShoppingMallRoomCardView *)roomCardView { + if (!_roomCardView) { + _roomCardView = [[ShoppingMallRoomCardView alloc] init]; + _roomCardView.hidden = YES; + } + return _roomCardView; +} + +- (UILabel *)titleLabel { + if (!_titleLabel) { + _titleLabel = [UILabel labelInitWithText:@"" + font:kFontMedium(14) + textColor:UIColorFromRGB(0xD9E7F7)]; + _titleLabel.textAlignment = NSTextAlignmentCenter; + } + return _titleLabel; +} + +- (UIButton *)playButton { + if (!_playButton) { + _playButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_playButton setImage:kImage(@"mall_play_icon") forState:UIControlStateNormal]; + [_playButton addTarget:self + action:@selector(handleTapPlay:) + forControlEvents:UIControlEventTouchUpInside]; + } + return _playButton; +} + +- (ShoppingMallTagView *)statusLabel { + if (!_statusLabel) { + _statusLabel = [[ShoppingMallTagView alloc] init]; + } + return _statusLabel; +} + +- (XPRoomGiftAnimationParser *)vapParser { + if (!_vapParser) { + _vapParser = [[XPRoomGiftAnimationParser alloc] init]; + } + return _vapParser; +} + +- (VAPView *)mp4View { + if (!_mp4View) { + _mp4View = [[VAPView alloc] init]; + _mp4View.contentMode = UIViewContentModeScaleAspectFill; + } + return _mp4View; +} + +@end + @interface ShoppingMallItemCard : UICollectionViewCell +@property (nonatomic, strong) DressUpShopModel *cellModel; + @property (nonatomic, strong) UIImageView *bgImageView; @property (nonatomic, strong) UIView *itemBGView; @property (nonatomic, strong) NetImageView *itemImageView; +@property(nonatomic, strong) ShoppingMallRoomCardView *roomCardView; @property (nonatomic, strong) UILabel *titleLabel; -@property (nonatomic, strong) DressUpShopModel *cellModel; @property (nonatomic, strong) UIButton *playButton; @property (nonatomic, strong) UILabel *pricePerDayLabel; @property (nonatomic, strong) UILabel *description_1_label; @@ -70,8 +333,6 @@ @property (nonatomic, copy) DidTapPlay didTapPlay; -@property (nonatomic, copy) DidTapPlay didTapPlay; - @end @implementation ShoppingMallItemCard @@ -87,14 +348,20 @@ [self.contentView addSubview:self.itemBGView]; [self.itemBGView mas_makeConstraints:^(MASConstraintMaker *make) { - make.top.leading.trailing.mas_equalTo(self.contentView).inset(15); + make.top.leading.trailing.mas_equalTo(self.contentView).inset(9); make.height.mas_equalTo(self.itemBGView.mas_width); }]; [self.contentView addSubview:self.itemImageView]; - [self.itemImageView mas_makeConstraints:^(MASConstraintMaker *make) { + [self.itemImageView mas_remakeConstraints:^(MASConstraintMaker *make) { make.center.mas_equalTo(self.itemBGView); - make.width.height.mas_equalTo(self.itemBGView.mas_width).multipliedBy(0.7f); + make.width.height.mas_equalTo(self.itemBGView.mas_width).multipliedBy(0.8f); + }]; + + [self.contentView addSubview:self.roomCardView]; + [self.roomCardView mas_remakeConstraints:^(MASConstraintMaker *make) { + make.top.centerX.mas_equalTo(self.itemBGView); + make.width.height.mas_equalTo(self.itemBGView.mas_width).multipliedBy(0.8f); }]; [self.contentView addSubview:self.playButton]; @@ -143,35 +410,53 @@ - (void)setCellModel:(DressUpShopModel *)cellModel { _cellModel = cellModel; - self.titleLabel.text = cellModel.name; - self.itemImageView.imageUrl = cellModel.pic; - if (cellModel.vipLevel > 0) { - self.pricePerDayLabel.attributedText = [cellModel pricePerDayForVIP]; + + if (cellModel.dressType == 3) { + self.itemImageView.hidden = YES; + self.roomCardView.hidden = NO; + self.roomCardView.imagePath = cellModel.pic; } else { - self.pricePerDayLabel.attributedText = [cellModel pricePerDay]; + self.itemImageView.hidden = NO; + self.roomCardView.hidden = YES; + self.itemImageView.imageUrl = cellModel.pic; } - if (cellModel.obtainWay == 1) { - self.pricePerDayLabel.hidden = NO; - self.description_1_label.hidden = NO; - self.description_2_label.hidden = YES; - self.description_1_label.text = YMLocalizedString(@"1.0.30_text_1"); - } else { + self.titleLabel.text = cellModel.name; + self.pricePerDayLabel.attributedText = [cellModel generateAttributedStringWithIncludeOriginalPrice:YES]; + + if (cellModel.obtainWay > 1) { self.pricePerDayLabel.hidden = YES; self.description_1_label.hidden = YES; self.description_2_label.hidden = NO; self.description_2_label.text = YMLocalizedString(@"1.0.30_text_2"); + } else { + self.pricePerDayLabel.hidden = NO; + self.description_1_label.hidden = NO; + self.description_2_label.hidden = YES; + if (cellModel.vipLevel == 0) { + self.description_1_label.text = YMLocalizedString(@"1.0.30_text_1"); + } else { + self.description_1_label.text = [NSString stringWithFormat:YMLocalizedString(@"1.0.30_text_10"), @(cellModel.vipLevel), @(cellModel.discount)]; + } } - self.statusLabel.text = YMLocalizedString(@"1.0.30_text_4"); - self.statusLabel.hidden = (cellModel.dressLimitStatus == 0); - - [self.statusLabel mas_updateConstraints:^(MASConstraintMaker *make) { - make.width.mas_equalTo([self.statusLabel textWidth]); - }]; - [self.statusLabel layoutIfNeeded]; - self.playButton.hidden = !([cellModel.effect.lowercaseString hasSuffix:@"svga"] || [cellModel.effect.lowercaseString hasSuffix:@"mp4"]); + + [self.titleLabel mas_updateConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.itemBGView.mas_bottom).offset(5 + (cellModel.obtainWay > 1 ? 5 : 0)); + }]; + + if (cellModel.dressLimitStatus == 0) { + self.statusLabel.text = YMLocalizedString(@"1.0.30_text_4"); + self.statusLabel.hidden = NO; + [self.statusLabel mas_updateConstraints:^(MASConstraintMaker *make) { + make.width.mas_equalTo([self.statusLabel textWidth]); + }]; + } else { + self.statusLabel.hidden = YES; + } + + [self layoutIfNeeded]; } - (void)handleTapPlay:(id)sender { @@ -210,6 +495,13 @@ return _itemImageView; } +- (ShoppingMallRoomCardView *)roomCardView { + if (!_roomCardView) { + _roomCardView = [[ShoppingMallRoomCardView alloc] init]; + } + return _roomCardView; +} + - (UILabel *)titleLabel { if (!_titleLabel) { _titleLabel = [UILabel labelInitWithText:@"" @@ -261,22 +553,21 @@ _description_2_label.numberOfLines = 0; [_description_2_label setCornerRadius:11 cornerMask:kCALayerMinXMaxYCorner | kCALayerMaxXMaxYCorner]; - -#if DEBUG - _description_2_label.alpha = 0.3; -#endif } return _description_2_label; } - (ShoppingMallTagView *)statusLabel { if (!_statusLabel) { - // TODO: 需要单独处理 - _statusLabel = [[ShoppingMallTagView alloc] init]; + _statusLabel = [[ShoppingMallTagView alloc] initWithBackgroundColors:@[UIColorFromRGB(0xE29030), UIColorFromRGB(0xFCC074)] + font:kFontMedium(12) + textColor:UIColorFromRGB(0x51281B)]; } return _statusLabel; } + + @end @interface ShoppingMallCategoryCard : UIView @@ -367,14 +658,15 @@ @end @interface ShoppingMallCategoryListView () -@property (nonatomic, strong) UIScrollView *container1ScrollView; -@property (nonatomic, strong) UIScrollView *container2ScrollView; -@property (nonatomic, strong) NSMutableArray *itemViews1; -@property (nonatomic, strong) NSMutableArray *itemViews2; -@property (nonatomic, strong) NSMutableDictionary*> *dataCache; // 缓存每页数据 -@property (nonatomic, strong) NSMutableSet *requestedPages; // 记录已经请求的数据页 +@property (nonatomic, strong) UIScrollView *shoppoingMallCategoryScrollView; +@property (nonatomic, strong) UIScrollView *mallItemsScrollView; +@property (nonatomic, strong) NSMutableArray *categoryCardArray; +@property (nonatomic, strong) NSMutableArray *itemCollectionViewArray; +@property (nonatomic, strong) NSMutableDictionary*> *dataCache; // 缓存每页数据 - 商城页 +@property (nonatomic, strong) NSMutableDictionary*> *myDressingDataCache; // 缓存每页数据 - 我的装扮页 +@property (nonatomic, strong) NSMutableSet *requestedPages; // 记录已经请求的数据页, 通过下标判断 @property (nonatomic, strong) NSIndexPath *selectedIndexPath; - +@property(nonatomic, strong) NSMutableArray *myDressingDataPagesNumbers; //缓存我的装扮页每页的当前 API 分页 @end @implementation ShoppingMallCategoryListView @@ -382,8 +674,6 @@ - (instancetype)initWithFrame:(CGRect)frame { if (self = [super initWithFrame:frame]) { [self setupUI]; - -// [self loadDataForPageAtIndex:0]; } return self; } @@ -398,28 +688,30 @@ - (void)setupUI { self.backgroundColor = [UIColor clearColor]; self.dataCache = @{}.mutableCopy; + self.myDressingDataCache = @{}.mutableCopy; + self.myDressingDataPagesNumbers = @[].mutableCopy; self.requestedPages = [NSMutableSet set]; // 初始化请求记录 - self.container2ScrollView.delegate = self; // 设置代理 + self.mallItemsScrollView.delegate = self; // 设置代理 CGFloat screenWidth = KScreenWidth; CGFloat screenHeight = self.bounds.size.height; // 容器-1 (顶部) - self.container1ScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(15, 0, screenWidth-15, 90)]; - self.container1ScrollView.contentInset = UIEdgeInsetsMake(0, 15, 0, 15); - self.container1ScrollView.showsHorizontalScrollIndicator = NO; - self.container1ScrollView.clipsToBounds = NO; - [self addSubview:self.container1ScrollView]; + self.shoppoingMallCategoryScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(15, 0, screenWidth-15, 90)]; + self.shoppoingMallCategoryScrollView.contentInset = UIEdgeInsetsMake(0, 15, 0, 15); + self.shoppoingMallCategoryScrollView.showsHorizontalScrollIndicator = NO; + self.shoppoingMallCategoryScrollView.clipsToBounds = NO; + [self addSubview:self.shoppoingMallCategoryScrollView]; // 容器-2 (底部) - self.container2ScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 100, screenWidth, screenHeight - 100)]; - self.container2ScrollView.pagingEnabled = YES; - self.container2ScrollView.delegate = self; - self.container2ScrollView.showsHorizontalScrollIndicator = NO; - [self addSubview:self.container2ScrollView]; + self.mallItemsScrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 100, screenWidth, screenHeight - 100)]; + self.mallItemsScrollView.pagingEnabled = YES; + self.mallItemsScrollView.delegate = self; + self.mallItemsScrollView.showsHorizontalScrollIndicator = NO; + [self addSubview:self.mallItemsScrollView]; - self.itemViews1 = [NSMutableArray array]; - self.itemViews2 = [NSMutableArray array]; + self.categoryCardArray = [NSMutableArray array]; + self.itemCollectionViewArray = [NSMutableArray array]; } - (void)setItems:(NSArray *)items { @@ -431,8 +723,8 @@ CGFloat container1ItemSpacing = 12; // 设置 container1 和 container2 的内容大小 - self.container1ScrollView.contentSize = CGSizeMake(items.count * (container1ItemWidth + container1ItemSpacing), 90); - self.container2ScrollView.contentSize = CGSizeMake(items.count * screenWidth, self.container2ScrollView.frame.size.height); + self.shoppoingMallCategoryScrollView.contentSize = CGSizeMake(items.count * (container1ItemWidth + container1ItemSpacing), 90); + self.mallItemsScrollView.contentSize = CGSizeMake(items.count * screenWidth, self.mallItemsScrollView.frame.size.height); // 添加 item view-1 到 container1ScrollView for (int i = 0; i < items.count; i++) { @@ -445,8 +737,8 @@ UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleItemTap:)]; [itemView1 addGestureRecognizer:tapGesture]; - [self.container1ScrollView addSubview:itemView1]; - [self.itemViews1 addObject:itemView1]; + [self.shoppoingMallCategoryScrollView addSubview:itemView1]; + [self.categoryCardArray addObject:itemView1]; } // 添加 item view-2 到 container2ScrollView @@ -455,9 +747,9 @@ layout.itemSize = CGSizeZero;// CGSizeMake((screenWidth-30) / 2 - 10, kGetScaleWidth(244)); layout.minimumInteritemSpacing = 10; layout.minimumLineSpacing = 10; - layout.sectionInset = UIEdgeInsetsMake(0, 15, 10, 15); + layout.sectionInset = UIEdgeInsetsMake(0, 15, self.bottomSpace + 10, 15); - UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(i * screenWidth, 0, screenWidth, self.container2ScrollView.frame.size.height) + UICollectionView *collectionView = [[UICollectionView alloc] initWithFrame:CGRectMake(i * screenWidth, 0, screenWidth, self.mallItemsScrollView.frame.size.height) collectionViewLayout:layout]; collectionView.delegate = self; collectionView.dataSource = self; @@ -467,8 +759,16 @@ [collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:@"cell"]; [collectionView registerClass:[ShoppingMallItemCard class] forCellWithReuseIdentifier:@"ShoppingMallItemCard"]; [collectionView registerClass:[ShoppingMallEmptyCard class] forCellWithReuseIdentifier:@"ShoppingMallEmptyCard"]; - [self.container2ScrollView addSubview:collectionView]; - [self.itemViews2 addObject:collectionView]; + [collectionView registerClass:[MyDressingItemCard class] forCellWithReuseIdentifier:@"MyDressingItemCard"]; + [collectionView registerClass:[MyDressingItemCard class] forCellWithReuseIdentifier:@"MyDressingItemCard_NotUsed"]; + + [self.mallItemsScrollView addSubview:collectionView]; + [self.itemCollectionViewArray addObject:collectionView]; + + if (self.isForMyDressingPage) { + [self addRefreshControl:collectionView]; + [self addLoadMoreForCollectionView:collectionView]; + } } [self updateSelectionForItemAtIndex:0]; @@ -479,7 +779,7 @@ - (void)handleItemTap:(UITapGestureRecognizer *)gesture { NSInteger index = gesture.view.tag; CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width; - [self.container2ScrollView setContentOffset:CGPointMake(index * screenWidth, 0) animated:YES]; + [self.mallItemsScrollView setContentOffset:CGPointMake(index * screenWidth, 0) animated:YES]; [self updateSelectionForItemAtIndex:index]; [self loadDataForPageAtIndex:index]; } @@ -490,7 +790,7 @@ if (![self.requestedPages containsObject:@(pageIndex)]) { [self.requestedPages addObject:@(pageIndex)]; // 标记该页已请求 - // 使用外部提供的回调或代理来请求数据 + // 使用外部提供的回调或代理来请求数据, 我的装扮和商城使用不同的数据 if (self.fetchDataForPage) { NSDictionary *itemDic = [self.items xpSafeObjectAtIndex:pageIndex]; NSNumber *type = [itemDic allValues].firstObject; @@ -498,41 +798,87 @@ self.fetchDataForPage(type.integerValue, ^(NSArray *data) { @kStrongify(self); if (self) { - // 更新数据到对应的 itemView-2 - [self updateContainer2WithData:data atIndex:pageIndex]; + [self updateItemCollectionViewWithShoppingMallData:data atIndex:pageIndex]; } }); + } else if (self.fetchDataForMyDressingPage) { + [self.myDressingDataPagesNumbers addObject:@(1)]; + [self loadMyDressingItems:pageIndex pageNum:1]; } } } +- (void)loadMyDressingItems:(NSInteger)pageIndex + pageNum:(NSInteger)pageNum{ + NSDictionary *itemDic = [self.items xpSafeObjectAtIndex:pageIndex]; + NSNumber *type = [itemDic allValues].firstObject; + @kWeakify(self); + self.fetchDataForMyDressingPage(type.integerValue, pageNum, ^(NSArray * _Nonnull modelList) { + @kStrongify(self); + if (self) { + [self updateItemCollectionViewWithMyDressingData:modelList pageNum:pageNum atIndex:pageIndex]; + } + }); +} + // 更新选中的 item view-1 - (void)updateSelectionForItemAtIndex:(NSInteger)index { - for (ShoppingMallCategoryCard *view in self.itemViews1) { + for (ShoppingMallCategoryCard *view in self.categoryCardArray) { view.isSelected = (view.tag == index); } self.selectedIndexPath = nil; if (self.returnSelectedModel) { self.returnSelectedModel(nil); } - for (UICollectionView *itemView in self.itemViews2) { + for (UICollectionView *itemView in self.itemCollectionViewArray) { [itemView reloadData]; } } -- (void)updateContainer2WithData:(NSArray *)data - atIndex:(NSInteger)index { +- (void)updateItemCollectionViewWithShoppingMallData:(NSArray *)data + atIndex:(NSInteger)index { self.dataCache[@(index)] = data; - if (index < self.itemViews2.count) { - UICollectionView *itemView = self.itemViews2[index]; + if (index < self.itemCollectionViewArray.count) { + UICollectionView *itemView = self.itemCollectionViewArray[index]; [itemView reloadData]; } } +- (void)updateItemCollectionViewWithMyDressingData:(NSArray *)data + pageNum:(NSInteger)pageNum + atIndex:(NSInteger)index { + if (pageNum == 1) { + self.myDressingDataCache[@(index)] = data; + } else { + NSMutableArray *array = self.myDressingDataCache[@(index)].mutableCopy; + [array addObjectsFromArray:data]; + self.myDressingDataCache[@(index)] = array.copy; + } + + if (index < self.itemCollectionViewArray.count) { + UICollectionView *itemView = self.itemCollectionViewArray[index]; + [itemView.refreshControl endRefreshing]; + [itemView.mj_header endRefreshing]; + [itemView.mj_footer endRefreshing]; + [itemView reloadData]; + + if (data.count == 0 || data.count < 2) { + [itemView.mj_footer endRefreshingWithNoMoreData]; + NSNumber *currentPageNum = [self.myDressingDataPagesNumbers xpSafeObjectAtIndex:itemView.tag]; + + if (currentPageNum.integerValue > 1) { + currentPageNum = @(currentPageNum.integerValue - 1); + [self.myDressingDataPagesNumbers replaceObjectAtIndex:itemView.tag + withObject:currentPageNum]; + } + } + } +} + #pragma mark - UIScrollView Delegate // 根据 container2ScrollView 的滑动位置更新 item view-1 的选中状态 - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { - if (scrollView == self.container2ScrollView) { + if (scrollView == self.mallItemsScrollView) { NSInteger pageIndex = round(scrollView.contentOffset.x / scrollView.frame.size.width); [self updateSelectionForItemAtIndex:pageIndex]; [self loadDataForPageAtIndex:pageIndex]; @@ -541,43 +887,79 @@ #pragma mark - UICollectionView Delegate & DataSource - (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section { - NSArray *data = self.dataCache[@(collectionView.tag)]; - return data.count == 0 ? 1 : data.count; + if (self.isForMyDressingPage) { + NSArray *data = self.myDressingDataCache[@(collectionView.tag)]; + return data.count + 1; + } else { + NSArray *data = self.dataCache[@(collectionView.tag)]; + return data.count == 0 ? 1 : data.count; + } } -- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath { - NSArray *data = self.dataCache[@(collectionView.tag)]; - if (data.count == 0 ) { - return CGSizeMake(KScreenWidth, 200); +- (CGSize)collectionView:(UICollectionView *)collectionView + layout:(UICollectionViewLayout *)collectionViewLayout + sizeForItemAtIndexPath:(NSIndexPath *)indexPath { + if (self.isForMyDressingPage) { + return CGSizeMake((KScreenWidth-30) / 2 - 10, kGetScaleWidth(190)); } else { - return CGSizeMake((KScreenWidth-30) / 2 - 10, kGetScaleWidth(244)); + NSArray *data = self.dataCache[@(collectionView.tag)]; + if (data.count == 0 ) { + return CGSizeMake(KScreenWidth, 200); + } else { + return CGSizeMake((KScreenWidth-30) / 2 - 10, kGetScaleWidth(244)); + } } } - (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath { - - NSArray *data = self.dataCache[@(collectionView.tag)]; - if (data.count == 0) { - ShoppingMallEmptyCard *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ShoppingMallEmptyCard" forIndexPath:indexPath]; + if (self.isForMyDressingPage) { + MyDressingItemCard *cell = nil; + + if (indexPath.row == 0) { + cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyDressingItemCard_NotUsed" + forIndexPath:indexPath]; + cell.model = nil; + } else { + cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"MyDressingItemCard" + forIndexPath:indexPath]; + NSArray *data = self.myDressingDataCache[@(collectionView.tag)]; + cell.model = [data xpSafeObjectAtIndex:indexPath.row-1]; + cell.selected = cell.model.used; + + if (self.didTapItemPlay) { + @kWeakify(self); + cell.didTapPlay = ^(NSString * _Nonnull resourcePath) { + @kStrongify(self); + self.didTapItemPlay(resourcePath); + }; + } + } + return cell; } else { - ShoppingMallItemCard *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ShoppingMallItemCard" - forIndexPath:indexPath]; - cell.cellModel = [data xpSafeObjectAtIndex:indexPath.row]; - // 设置选中状态 - if ([indexPath isEqual:self.selectedIndexPath]) { - cell.selected = YES; + NSArray *data = self.dataCache[@(collectionView.tag)]; + if (data.count == 0) { + ShoppingMallEmptyCard *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ShoppingMallEmptyCard" forIndexPath:indexPath]; + return cell; } else { - cell.selected = NO; + ShoppingMallItemCard *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"ShoppingMallItemCard" + forIndexPath:indexPath]; + cell.cellModel = [data xpSafeObjectAtIndex:indexPath.row]; + // 设置选中状态 + if ([indexPath isEqual:self.selectedIndexPath]) { + cell.selected = YES; + } else { + cell.selected = NO; + } + if (self.didTapItemPlay) { + @kWeakify(self); + cell.didTapPlay = ^(NSString * _Nonnull resourcePath) { + @kStrongify(self); + self.didTapItemPlay(resourcePath); + }; + } + return cell; } - if (self.didTapItemPlay) { - @kWeakify(self); - cell.didTapPlay = ^(NSString * _Nonnull resourcePath) { - @kStrongify(self); - self.didTapItemPlay(resourcePath); - }; - } - return cell; } } @@ -586,10 +968,30 @@ self.selectedIndexPath = indexPath; - if (self.returnSelectedModel) { + if (self.isForMyDressingPage) { + NSArray *data = self.myDressingDataCache[@(collectionView.tag)]; + if (self.returnMyDressingSelectedModel) { + NSInteger type = collectionView.tag; + if (type == 0) { + type = 1; + } else if (type == 1) { + type = 0; + } + if (indexPath.row > 0) { + MyDressingDataModel *model = [data xpSafeObjectAtIndex:indexPath.row - 1]; + if (model.used == 0) { + self.returnMyDressingSelectedModel(model, type); + } + } else { + self.returnMyDressingSelectedModel(nil, type); + } + } + } else { NSArray *data = self.dataCache[@(collectionView.tag)]; - DressUpShopModel *model = [data xpSafeObjectAtIndex:indexPath.row]; - self.returnSelectedModel(model); + if (self.returnSelectedModel) { + DressUpShopModel *model = [data xpSafeObjectAtIndex:indexPath.row]; + self.returnSelectedModel(model); + } } NSMutableArray *indexPathsToReload = [NSMutableArray array]; @@ -600,5 +1002,45 @@ [collectionView reloadItemsAtIndexPaths:indexPathsToReload]; } +#pragma mark - 上下拉刷新 +- (void)addRefreshControl:(UICollectionView *)collectionView { + +// MJRefreshNormalHeader *header = [MJRefreshNormalHeader headerWithRefreshingTarget:self refreshingAction:@selector(handlePullToRefresh:)]; +// header.stateLabel.font = [UIFont systemFontOfSize:10.0]; +// header.lastUpdatedTimeLabel.font = [UIFont systemFontOfSize:10.0]; +// header.stateLabel.textColor = [DJDKMIMOMColor secondTextColor]; +// header.lastUpdatedTimeLabel.textColor = [DJDKMIMOMColor secondTextColor]; +// collectionView.mj_header = header; + UIRefreshControl *refreshControl = [[UIRefreshControl alloc] init]; + refreshControl.tintColor = [UIColor lightGrayColor]; + [refreshControl addTarget:self action:@selector(handlePullToRefresh:) forControlEvents:UIControlEventValueChanged]; + collectionView.refreshControl = refreshControl; +} + +- (void)addLoadMoreForCollectionView:(UICollectionView *)collectionView { + if (collectionView.tag == 0) { + return; + } + @kWeakify(self); + collectionView.mj_footer = [MJRefreshAutoNormalFooter footerWithRefreshingBlock:^{ + @kStrongify(self); + NSNumber *currentPageNum = [self.myDressingDataPagesNumbers xpSafeObjectAtIndex:collectionView.tag]; + currentPageNum = @(currentPageNum.integerValue + 1); + [self.myDressingDataPagesNumbers replaceObjectAtIndex:collectionView.tag + withObject:currentPageNum]; + [self loadMyDressingItems:collectionView.tag pageNum:currentPageNum.integerValue]; + }]; +} + +- (void)handlePullToRefresh:(UIRefreshControl *)collectionViewRefreshControl { + UICollectionView *collectionView = (UICollectionView *)collectionViewRefreshControl.superview; + if (collectionView) { + [self.myDressingDataPagesNumbers replaceObjectAtIndex:collectionView.tag + withObject:@(1)]; + [self loadMyDressingItems:collectionView.tag pageNum:1]; + [collectionView.mj_footer resetNoMoreData]; + } + +} @end diff --git a/YuMi/Modules/ShoppingMall/ShoppingMallItemPreview.h b/YuMi/Modules/ShoppingMall/ShoppingMallItemPreview.h index 397904ac..6e0b6b5c 100644 --- a/YuMi/Modules/ShoppingMall/ShoppingMallItemPreview.h +++ b/YuMi/Modules/ShoppingMall/ShoppingMallItemPreview.h @@ -7,15 +7,17 @@ #import -@class DressUpShopModel; +@class DressUpShopModel, MyDressingDataModel; NS_ASSUME_NONNULL_BEGIN @interface ShoppingMallItemPreview : UIView @property(nonatomic, copy) void(^buyWith)(DressUpShopModel *model); +@property(nonatomic, copy) void(^giveWith)(DressUpShopModel *model); -- (instancetype)initWith:(DressUpShopModel *)model; +- (instancetype)initWith:(DressUpShopModel *)model isForGive:(BOOL)isForGive; +- (instancetype)initWithMyDress:(MyDressingDataModel *)model isVaild:(BOOL)isVaild; @end diff --git a/YuMi/Modules/ShoppingMall/ShoppingMallItemPreview.m b/YuMi/Modules/ShoppingMall/ShoppingMallItemPreview.m index ea59dcef..3926791a 100644 --- a/YuMi/Modules/ShoppingMall/ShoppingMallItemPreview.m +++ b/YuMi/Modules/ShoppingMall/ShoppingMallItemPreview.m @@ -7,14 +7,22 @@ #import "ShoppingMallItemPreview.h" #import "DressUpShopModel.h" +#import "MyDressingDataModel.h" @interface ShoppingMallItemPreview () +@property(nonatomic, assign) BOOL isForGive; +@property(nonatomic, assign) BOOL isVaild; @property(nonatomic, strong) DressUpShopModel *model; +@property(nonatomic, strong) MyDressingDataModel *myDressingModel; @property(nonatomic, strong) NetImageView *itemImageView; @property(nonatomic, strong) UILabel *nameLabel; @property(nonatomic, strong) UILabel *pricePerDayLabel; +@property(nonatomic, strong) UIButton *cancelButton; +@property(nonatomic, strong) UIButton *buyButton; +@property(nonatomic, strong) UIButton *giveButton; + @end @implementation ShoppingMallItemPreview @@ -26,20 +34,50 @@ return self; } -- (instancetype)initWith:(DressUpShopModel *)model { +- (instancetype)initWith:(DressUpShopModel *)model isForGive:(BOOL)isForGive { if (self = [super init]) { [self setupUI]; + self.isForGive = isForGive; self.model = model; } return self; } +- (instancetype)initWithMyDress:(MyDressingDataModel *)model isVaild:(BOOL)isVaild { + if (self = [super init]) { + [self setupUI]; + self.isVaild = isVaild; + self.myDressingModel = model; + } + return self; +} + - (void)setModel:(DressUpShopModel *)model { _model = model; self.itemImageView.imageUrl = model.pic; self.nameLabel.text = model.name; - self.pricePerDayLabel.attributedText = model.pricePerDay; + self.pricePerDayLabel.attributedText = [model generateAttributedStringWithIncludeOriginalPrice:YES]; +} + +- (void)setMyDressingModel:(MyDressingDataModel *)myDressingModel { + _myDressingModel = myDressingModel; + self.itemImageView.imageUrl = myDressingModel.pic; + self.nameLabel.text = myDressingModel.name; + if (self.isVaild) { + self.buyButton.hidden = NO; + } else { + self.buyButton.hidden = YES; + self.giveButton.hidden = YES; + [self.cancelButton setTitle:YMLocalizedString(@"TTAlertConfig0") forState:UIControlStateNormal]; + [self.cancelButton mas_remakeConstraints:^(MASConstraintMaker *make) { + make.centerX.mas_equalTo(self); + make.bottom.mas_equalTo(-38); + make.size.mas_equalTo(CGSizeMake(90, 32)); + }]; + self.pricePerDayLabel.text = YMLocalizedString(@"1.0.30_text_2"); + } +// self.pricePerDayLabel.attributedText = [model generateAttributedStringWithIncludeOriginalPrice:YES]; } - (void)setupUI { @@ -78,17 +116,22 @@ make.top.mas_equalTo(self.pricePerDayLabel.mas_bottom).offset(18); }]; - UIButton *cancel = [self cancelButton]; - [self addSubview:cancel]; - [cancel mas_makeConstraints:^(MASConstraintMaker *make) { + [self addSubview:self.cancelButton]; + [self.cancelButton mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(line.mas_bottom).offset(20); make.leading.mas_equalTo(46); make.size.mas_equalTo(CGSizeMake(90, 32)); }]; - UIButton *buy = [self buyButton]; - [self addSubview:buy]; - [buy mas_makeConstraints:^(MASConstraintMaker *make) { + [self addSubview:self.buyButton]; + [self.buyButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(line.mas_bottom).offset(20); + make.trailing.mas_equalTo(-46); + make.size.mas_equalTo(CGSizeMake(90, 32)); + }]; + + [self addSubview:self.giveButton]; + [self.giveButton mas_makeConstraints:^(MASConstraintMaker *make) { make.top.mas_equalTo(line.mas_bottom).offset(20); make.trailing.mas_equalTo(-46); make.size.mas_equalTo(CGSizeMake(90, 32)); @@ -96,6 +139,13 @@ } #pragma mark - + +- (void)setIsForGive:(BOOL)isForGive { + _isForGive = isForGive; + self.buyButton.hidden = isForGive; + self.giveButton.hidden = !isForGive; +} + - (void)didTapCancel { [TTPopup dismiss]; } @@ -106,6 +156,12 @@ } } +- (void)didTapGive { + if (self.giveWith) { + self.giveWith(self.model); + } +} + #pragma mark - - (UIImageView *)backgroundImageView { UIImageView *bgImageView = [[UIImageView alloc] initWithImage:kImage(@"mall_item_preview_bg")]; @@ -144,30 +200,59 @@ } - (UIButton *)cancelButton { - UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom]; - [b setBackgroundColor:[UIColor colorWithWhite:0 alpha:0.3]]; - [b setAttributedTitle:[[NSAttributedString alloc] initWithString:YMLocalizedString(@"XPShareView7") - attributes:@{NSFontAttributeName: kFontMedium(14), - NSForegroundColorAttributeName: [UIColor whiteColor]}] - forState:UIControlStateNormal]; - [b addTarget:self action:@selector(didTapCancel) forControlEvents:UIControlEventTouchUpInside]; - [b setCornerRadius:16]; - return b; + if (!_cancelButton) { + UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom]; + [b setBackgroundColor:[UIColor colorWithWhite:0 alpha:0.3]]; + [b setAttributedTitle:[[NSAttributedString alloc] initWithString:YMLocalizedString(@"XPShareView7") + attributes:@{NSFontAttributeName: kFontMedium(14), + NSForegroundColorAttributeName: [UIColor whiteColor]}] + forState:UIControlStateNormal]; + [b addTarget:self action:@selector(didTapCancel) forControlEvents:UIControlEventTouchUpInside]; + [b setCornerRadius:16]; + _cancelButton = b; + } + + return _cancelButton; } - (UIButton *)buyButton { - UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom]; - [b addGradientBackgroundWithColors:@[UIColorFromRGB(0xFFE3B2), UIColorFromRGB(0xE9A71D)] - startPoint:CGPointMake(0.0, 0.5) - endPoint:CGPointMake(1.0, 0.5) - cornerRadius:16]; - [b setAttributedTitle:[[NSAttributedString alloc] initWithString:YMLocalizedString(@"XPDressUpShopCardTableViewCell2") - attributes:@{NSFontAttributeName: kFontMedium(14), - NSForegroundColorAttributeName: UIColorFromRGB(0x51281b)}] - forState:UIControlStateNormal]; - [b addTarget:self action:@selector(didTapBuy) forControlEvents:UIControlEventTouchUpInside]; - [b setCornerRadius:16]; - return b; + if (!_buyButton) { + UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom]; + [b addGradientBackgroundWithColors:@[UIColorFromRGB(0xFFE3B2), UIColorFromRGB(0xE9A71D)] + startPoint:CGPointMake(0.0, 0.5) + endPoint:CGPointMake(1.0, 0.5) + cornerRadius:16]; + [b setAttributedTitle:[[NSAttributedString alloc] initWithString:YMLocalizedString(@"XPDressUpShopCardTableViewCell2") + attributes:@{NSFontAttributeName: kFontMedium(14), + NSForegroundColorAttributeName: UIColorFromRGB(0x51281b)}] + forState:UIControlStateNormal]; + [b addTarget:self action:@selector(didTapBuy) forControlEvents:UIControlEventTouchUpInside]; + [b setCornerRadius:16]; + b.hidden = YES; + _buyButton = b; + } + + return _buyButton; +} + +- (UIButton *)giveButton { + if (!_giveButton) { + UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom]; + [b addGradientBackgroundWithColors:@[UIColorFromRGB(0xB2FCFF), UIColorFromRGB(0x4DA2EA)] + startPoint:CGPointMake(0.0, 0.5) + endPoint:CGPointMake(1.0, 0.5) + cornerRadius:16]; + [b setAttributedTitle:[[NSAttributedString alloc] initWithString:YMLocalizedString(@"1.0.30_text_6") + attributes:@{NSFontAttributeName: kFontMedium(14), + NSForegroundColorAttributeName: UIColorFromRGB(0x172055)}] + forState:UIControlStateNormal]; + [b addTarget:self action:@selector(didTapGive) forControlEvents:UIControlEventTouchUpInside]; + [b setCornerRadius:16]; + b.hidden = YES; + _giveButton = b; + } + + return _giveButton; } @end diff --git a/YuMi/Modules/ShoppingMall/ShoppingMallTagView.h b/YuMi/Modules/ShoppingMall/ShoppingMallTagView.h index 789c7e34..d22fe544 100644 --- a/YuMi/Modules/ShoppingMall/ShoppingMallTagView.h +++ b/YuMi/Modules/ShoppingMall/ShoppingMallTagView.h @@ -13,6 +13,13 @@ NS_ASSUME_NONNULL_BEGIN @property(nonatomic, copy) NSString *text; +- (instancetype)initWithBackgroundColors:(NSArray *)colors + font:(UIFont *)font + textColor:(UIColor *)textColor; + +- (void)updateBackgroundColors:(NSArray *)colors + textColor:(UIColor *)textColor; + - (CGFloat)textWidth; @end diff --git a/YuMi/Modules/ShoppingMall/ShoppingMallTagView.m b/YuMi/Modules/ShoppingMall/ShoppingMallTagView.m index 50194e8a..30350180 100644 --- a/YuMi/Modules/ShoppingMall/ShoppingMallTagView.m +++ b/YuMi/Modules/ShoppingMall/ShoppingMallTagView.m @@ -16,16 +16,51 @@ @implementation ShoppingMallTagView +- (instancetype)initWithBackgroundColors:(NSArray *)colors + font:(UIFont *)font + textColor:(UIColor *)textColor { + if (self = [super init]) { + [self setCornerRadius:10 + cornerMask:kCALayerMinXMinYCorner | kCALayerMaxXMaxYCorner]; + self.clipsToBounds = YES; + if (colors.count>1) { + [self addGradientBackgroundWithColors:colors //@[UIColorFromRGB(0xE29030), UIColorFromRGB(0xFCC074)] + startPoint:CGPointMake(0.0, 0.0) + endPoint:CGPointMake(0.0, 1.0) + cornerRadius:0]; + } else if (colors.count == 1){ + self.backgroundColor = colors.firstObject; + } + + [self addSubview:self.label]; + [self.label mas_makeConstraints:^(MASConstraintMaker *make) { + make.edges.mas_equalTo(self); + }]; + + self.label.font = font; + self.label.textColor = textColor; + } + return self; +} + +- (void)updateBackgroundColors:(NSArray *)colors + textColor:(UIColor *)textColor { + if (colors.count>1) { + [self addGradientBackgroundWithColors:colors //@[UIColorFromRGB(0xE29030), UIColorFromRGB(0xFCC074)] + startPoint:CGPointMake(0.0, 0.0) + endPoint:CGPointMake(0.0, 1.0) + cornerRadius:0]; + } else if (colors.count == 1){ + self.backgroundColor = colors.firstObject; + } + self.label.textColor = textColor; +} + - (instancetype)init { if (self = [super init]) { [self setCornerRadius:10 cornerMask:kCALayerMinXMinYCorner | kCALayerMaxXMaxYCorner]; self.clipsToBounds = YES; - - [self addGradientBackgroundWithColors:@[UIColorFromRGB(0xE29030), UIColorFromRGB(0xFCC074)] - startPoint:CGPointMake(0.0, 0.0) - endPoint:CGPointMake(0.0, 1.0) - cornerRadius:0]; [self addSubview:self.label]; [self.label mas_makeConstraints:^(MASConstraintMaker *make) { @@ -41,7 +76,7 @@ } - (CGFloat)textWidth { - return [self.text boundingRectWithSize:CGSizeMake(KScreenWidth/3, kFontMedium(12).lineHeight) + return [self.text boundingRectWithSize:CGSizeMake(KScreenWidth/3, self.label.font.lineHeight) options:NSStringDrawingUsesLineFragmentOrigin | NSStringDrawingUsesFontLeading attributes:@{NSFontAttributeName: kFontMedium(12)} context:nil].size.width + 20; diff --git a/YuMi/Modules/ShoppingMall/ShoppingMallViewController.m b/YuMi/Modules/ShoppingMall/ShoppingMallViewController.m index 5e6f25b4..546efee7 100644 --- a/YuMi/Modules/ShoppingMall/ShoppingMallViewController.m +++ b/YuMi/Modules/ShoppingMall/ShoppingMallViewController.m @@ -11,6 +11,7 @@ #import #import "MyDressingViewController.h" +#import "XPDressSearchViewController.h" #import "ShoppingMallItemPreview.h" #import "ShoppingMallDataPresent.h" @@ -29,6 +30,8 @@ static NSArray *> *DressUpOptions(void) { @interface ShoppingMallViewController () +@property(nonatomic, strong) ShoppingMallCategoryListView *listView; + @property (nonatomic, strong) UIView *playEffectMask; @property (nonatomic, strong) VAPView *mp4Effect; @property (nonatomic, strong) SVGAImageView *svgaEffect; @@ -63,6 +66,8 @@ static NSArray *> *DressUpOptions(void) { [self setupNavigationBar]; [self setupBottomControlBar]; [self setupContentList]; + + [self hideBottomControlArea]; } - (void)setupBackground { @@ -105,7 +110,8 @@ static NSArray *> *DressUpOptions(void) { - (void)setupBottomControlBar { [self.view addSubview:self.bottomControlArea]; [self.bottomControlArea mas_makeConstraints:^(MASConstraintMaker *make) { - make.bottom.leading.trailing.mas_equalTo(self.view); + make.bottom.mas_equalTo(0); + make.leading.trailing.mas_equalTo(self.view); make.height.mas_equalTo(80 + kSafeAreaBottomHeight); }]; } @@ -113,7 +119,10 @@ static NSArray *> *DressUpOptions(void) { - (void)setupContentList { NSInteger top = kNavigationHeight; NSInteger bottom = 80 + kSafeAreaBottomHeight; - ShoppingMallCategoryListView *listView = [[ShoppingMallCategoryListView alloc] initWithFrame:CGRectMake(0, top, KScreenWidth, KScreenHeight - top - bottom)]; + ShoppingMallCategoryListView *listView = [[ShoppingMallCategoryListView alloc] initWithFrame:CGRectMake(0, top, KScreenWidth, KScreenHeight - top)]; + listView.bottomSpace = bottom; + [self.view insertSubview:listView belowSubview:self.bottomControlArea]; + self.listView = listView; @kWeakify(self); listView.fetchDataForPage = ^(NSInteger pageIndex, FetchDataCompletion completion) { @kStrongify(self); @@ -129,11 +138,6 @@ static NSArray *> *DressUpOptions(void) { self.selectedModel = model; [self updateBottomControlArea]; }; - listView.didTapItemPlay = ^(NSString * _Nonnull resourcePath) { - @kStrongify(self); - [self playItemEffect:resourcePath]; - }; - [self.view addSubview:listView]; listView.items = DressUpOptions(); } @@ -152,17 +156,47 @@ static NSArray *> *DressUpOptions(void) { - (void)updateBottomControlArea { if (self.selectedModel) { - self.bottomControlPriceLabel.attributedText = self.selectedModel.pricePerDay; + if (self.selectedModel.obtainWay > 1) { + self.bottomControlPriceLabel.text = @""; + } else { + self.bottomControlPriceLabel.attributedText = [self.selectedModel generateAttributedStringWithIncludeOriginalPrice:YES]; + } } else { self.bottomControlPriceLabel.text = @""; } + + if ([NSString isEmpty:self.bottomControlPriceLabel.text]) { + [self hideBottomControlArea]; + } else { + [self showBottomControlArea]; + } +} + +- (void)showBottomControlArea { + [UIView animateWithDuration:0.25 animations:^{ + [self.bottomControlArea mas_updateConstraints:^(MASConstraintMaker *make) { + make.bottom.mas_equalTo(0); + }]; + } completion:^(BOOL finished) { + [self.view layoutIfNeeded]; + }]; +} + +- (void)hideBottomControlArea { + [UIView animateWithDuration:0.25 animations:^{ + [self.bottomControlArea mas_updateConstraints:^(MASConstraintMaker *make) { + make.bottom.mas_equalTo(80 + kSafeAreaBottomHeight); + }]; + } completion:^(BOOL finished) { + [self.view layoutIfNeeded]; + }]; } - (void)didTapBuy:(id)sender { if (!self.selectedModel) { return; } - ShoppingMallItemPreview *preview = [[ShoppingMallItemPreview alloc] initWith:self.selectedModel]; + ShoppingMallItemPreview *preview = [[ShoppingMallItemPreview alloc] initWith:self.selectedModel isForGive:NO]; preview.frame = CGRectMake(0, 0, 284, 353); [TTPopup popupView:preview style:TTPopupStyleAlert]; @kWeakify(self); @@ -185,6 +219,9 @@ static NSArray *> *DressUpOptions(void) { if (!self.selectedModel) { return; } + XPDressSearchViewController *vc = [[XPDressSearchViewController alloc] init]; + vc.selectedModel = self.selectedModel; + [self.navigationController pushViewController:vc animated:YES]; } - (void)didTapBack { @@ -218,10 +255,12 @@ static NSArray *> *DressUpOptions(void) { [p parseWithURL:[NSURL URLWithString:path] completionBlock:^(SVGAVideoEntity * _Nullable videoItem) { @kStrongify(self); - [self.loading stopAnimating]; - self.svgaEffect.hidden = NO; - self.svgaEffect.videoItem = videoItem; - [self.svgaEffect startAnimation]; + if (!self.playEffectMask.isHidden) { + [self.loading stopAnimating]; + self.svgaEffect.hidden = NO; + self.svgaEffect.videoItem = videoItem; + [self.svgaEffect startAnimation]; + } } failureBlock:^(NSError * _Nullable error) { @kStrongify(self); [self stopItemSVGAEffect]; @@ -247,6 +286,9 @@ static NSArray *> *DressUpOptions(void) { [self.loading mas_makeConstraints:^(MASConstraintMaker *make) { make.center.mas_equalTo(_playEffectMask); }]; + + UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stopItemSVGAEffect)]; + [_playEffectMask addGestureRecognizer:tap]; } return _playEffectMask; } @@ -259,9 +301,8 @@ static NSArray *> *DressUpOptions(void) { _svgaEffect.loops = 0; _svgaEffect.clearsAfterStop = YES; _svgaEffect.hidden = YES; - - UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(stopItemSVGAEffect)]; - [_svgaEffect addGestureRecognizer:tap]; + _svgaEffect.userInteractionEnabled = YES; + } return _svgaEffect; } diff --git a/YuMi/Modules/YMMine/View/DressUp/Api/Api+DressUp.h b/YuMi/Modules/YMMine/View/DressUp/Api/Api+DressUp.h index 39c1722a..bd626524 100644 --- a/YuMi/Modules/YMMine/View/DressUp/Api/Api+DressUp.h +++ b/YuMi/Modules/YMMine/View/DressUp/Api/Api+DressUp.h @@ -96,6 +96,15 @@ NS_ASSUME_NONNULL_BEGIN /// @param targetUid 目标用户UID + (void)requestSendDress:(HttpRequestHelperCompletion)completion id:(NSString *)dressId targetUid:(NSString *)targetUid; +/// 获取我的装扮内容 +/// @param completion 完成回掉 +/// @param page 分页 +/// @param pageSize 分页大小 +/// @param dressType 装扮类型,目前只支持 0 & 5 ++ (void)mineDressItems:(HttpRequestHelperCompletion)completion page:(NSString *)page pageSize:(NSString *)pageSize dressType:(NSString *)dressType; + ++ (void)optDress:(HttpRequestHelperCompletion)completion dressType:(NSString *)dressType dressId:(NSString *)dressId; + @end NS_ASSUME_NONNULL_END diff --git a/YuMi/Modules/YMMine/View/DressUp/Api/Api+DressUp.m b/YuMi/Modules/YMMine/View/DressUp/Api/Api+DressUp.m index 709421eb..c2733234 100644 --- a/YuMi/Modules/YMMine/View/DressUp/Api/Api+DressUp.m +++ b/YuMi/Modules/YMMine/View/DressUp/Api/Api+DressUp.m @@ -143,4 +143,12 @@ [self makeRequest:fang method:HttpRequestHelperMethodPOST completion:completion, __FUNCTION__, dressId, targetUid, nil]; } ++ (void)mineDressItems:(HttpRequestHelperCompletion)completion page:(NSString *)page pageSize:(NSString *)pageSize dressType:(NSString *)dressType { + [self makeRequest:@"dress/shop/mine" method:HttpRequestHelperMethodGET completion:completion, __FUNCTION__, page, pageSize, dressType, nil]; +} + ++ (void)optDress:(HttpRequestHelperCompletion)completion dressType:(NSString *)dressType dressId:(NSString *)dressId { + [self makeRequest:@"dress/shop/optDress" method:HttpRequestHelperMethodGET completion:completion, __FUNCTION__, dressType, dressId, nil]; +} + @end diff --git a/YuMi/Modules/YMMine/View/DressUp/Model/DressUpShopModel.h b/YuMi/Modules/YMMine/View/DressUp/Model/DressUpShopModel.h index 49fb548a..76b7b9d1 100644 --- a/YuMi/Modules/YMMine/View/DressUp/Model/DressUpShopModel.h +++ b/YuMi/Modules/YMMine/View/DressUp/Model/DressUpShopModel.h @@ -46,9 +46,14 @@ NS_ASSUME_NONNULL_BEGIN // "获得方式,1-普通,2-活动" @property (nonatomic, assign) NSInteger obtainWay; +// 个人页背景 +@property(nonatomic, copy) NSString *usingPersonalBackground; + - (NSMutableAttributedString *)pricePerDay; - (NSMutableAttributedString *)pricePerDayForVIP; +- (NSMutableAttributedString *)generateAttributedStringWithIncludeOriginalPrice:(BOOL)includeOriginalPrice; + @end NS_ASSUME_NONNULL_END diff --git a/YuMi/Modules/YMMine/View/DressUp/Model/DressUpShopModel.m b/YuMi/Modules/YMMine/View/DressUp/Model/DressUpShopModel.m index 5816d675..67a7aa16 100644 --- a/YuMi/Modules/YMMine/View/DressUp/Model/DressUpShopModel.m +++ b/YuMi/Modules/YMMine/View/DressUp/Model/DressUpShopModel.m @@ -37,7 +37,7 @@ //TODO: 修改为支持 0-1-2 位小数 - if (fmod(self.discountPrice, 1.0) == 0.0) { + if (fmod(self.dressPrice, 1.0) == 0.0) { formattedPriceString = [NSString stringWithFormat:@"%ld", (long)self.dressPrice]; } else { formattedPriceString = [NSString stringWithFormat:@"%.2ld", (long)self.dressPrice]; @@ -118,4 +118,67 @@ return attributedString; } +- (NSMutableAttributedString *)generateAttributedStringWithIncludeOriginalPrice:(BOOL)includeOriginalPrice { + // 创建一个 NSMutableAttributedString 来存放最终的结果 + NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] init]; + + // 1. 添加金币图片 + NSTextAttachment *coinAttachment = [[NSTextAttachment alloc] init]; + coinAttachment.image = kImage(@"moli_money_icon"); // 替换为你的金币图片名称 + coinAttachment.bounds = CGRectMake(0, -2, 18, 18); // 设置图片大小和位置,偏移量适当调整 + NSAttributedString *coinString = [NSAttributedString attributedStringWithAttachment:coinAttachment]; + [attributedString appendAttributedString:coinString]; + + // 2. 添加空格 + NSAttributedString *spaceString = [[NSAttributedString alloc] initWithString:@" "]; + [attributedString appendAttributedString:spaceString]; + + // 3. 添加 price 文本,字体加粗,颜色 #F8CE1F,字体大小 25 + NSDictionary *priceAttributes = @{ + NSFontAttributeName: kFontSemibold(15), + NSForegroundColorAttributeName: UIColorFromRGB(0xF8CE1F) + }; + NSString *formattedPriceString = [self formatPrice: self.vipLevel>0 ? self.discountPrice : self.dressPrice]; + NSAttributedString *priceString = [[NSAttributedString alloc] initWithString:formattedPriceString attributes:priceAttributes]; + [attributedString appendAttributedString:priceString]; + + // 4. 添加 day 文本,字体常规,颜色 #F8CE1F,字体大小 12 + NSDictionary *dayAttributes = @{ + NSFontAttributeName: kFontRegular(12), + NSForegroundColorAttributeName: UIColorFromRGB(0xF8CE1F) + }; + NSString *formattedDayString = [NSString stringWithFormat:@"/%ldD ", (long)self.dressDay]; + NSAttributedString *dayString = [[NSAttributedString alloc] initWithString:formattedDayString attributes:dayAttributes]; + [attributedString appendAttributedString:dayString]; + + // 如果需要添加原始价格 + if (includeOriginalPrice && self.vipLevel > 0) { + NSDictionary *originalPriceAttributes = @{ + NSFontAttributeName: kFontRegular(12), + NSForegroundColorAttributeName: UIColorRGBAlpha(0xD9E7F7, 0.5f), + NSStrikethroughStyleAttributeName: @(NSUnderlineStyleSingle), // 单条删除线 + NSStrikethroughColorAttributeName: UIColorRGBAlpha(0xD9E7F7, 0.5f) // 删除线颜色 + }; + NSString *originalPriceString = [NSString stringWithFormat:@"%@", [self formatPrice:self.dressPrice]]; + NSAttributedString *originalPriceAttributedString = [[NSAttributedString alloc] initWithString:originalPriceString attributes:originalPriceAttributes]; + [attributedString appendAttributedString:originalPriceAttributedString]; + } + + return attributedString; +} + +// 辅助方法:格式化价格,支持 0-2 位小数 +- (NSString *)formatPrice:(CGFloat)price { + if (fmod(price, 1.0) == 0.0) { + // 整数,移除小数部分 + return [NSString stringWithFormat:@"%.0f", price]; + } else if (fmod(price * 10, 1.0) == 0.0) { + // 保留 1 位小数 + return [NSString stringWithFormat:@"%.1f", price]; + } else { + // 保留 2 位小数 + return [NSString stringWithFormat:@"%.2f", price]; + } +} + @end diff --git a/YuMi/Modules/YMMine/View/DressUp/Model/NameplateModel.h b/YuMi/Modules/YMMine/View/DressUp/Model/NameplateModel.h index c2d05641..872a00cf 100644 --- a/YuMi/Modules/YMMine/View/DressUp/Model/NameplateModel.h +++ b/YuMi/Modules/YMMine/View/DressUp/Model/NameplateModel.h @@ -31,7 +31,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, assign) NSTimeInterval createTime; @property (nonatomic, assign) BOOL isCustomWord; @property (nonatomic, copy) NSString *remark; -@property (nonatomic, copy) NSString *iconPic; +//@property (nonatomic, copy) NSString *iconPic; @property (nonatomic, assign) NSInteger expireDays; @property (nonatomic, assign) NSTimeInterval updateTime; @property (nonatomic, copy) NSString *nameplateName; diff --git a/YuMi/Modules/YMMine/View/DressUp/View/XPDressSearchViewController.h b/YuMi/Modules/YMMine/View/DressUp/View/XPDressSearchViewController.h index 7b977cd9..b3607a06 100644 --- a/YuMi/Modules/YMMine/View/DressUp/View/XPDressSearchViewController.h +++ b/YuMi/Modules/YMMine/View/DressUp/View/XPDressSearchViewController.h @@ -7,12 +7,12 @@ #import "MvpViewController.h" +@class DressUpShopModel; NS_ASSUME_NONNULL_BEGIN @interface XPDressSearchViewController : MvpViewController -@property (nonatomic, assign) NSInteger dressType; -@property (nonatomic, copy) NSString *dressId; +@property(nonatomic, strong) DressUpShopModel *selectedModel; @end diff --git a/YuMi/Modules/YMMine/View/DressUp/View/XPDressSearchViewController.m b/YuMi/Modules/YMMine/View/DressUp/View/XPDressSearchViewController.m index 06fef0b0..9a61f1cf 100644 --- a/YuMi/Modules/YMMine/View/DressUp/View/XPDressSearchViewController.m +++ b/YuMi/Modules/YMMine/View/DressUp/View/XPDressSearchViewController.m @@ -14,6 +14,7 @@ #import "Api+DressUp.h" #import "NSArray+Safe.h" ///Model +#import "DressUpShopModel.h" #import "HomeSearchResultModel.h" ///View #import "XPDressShopSearchTableViewCell.h" @@ -24,7 +25,10 @@ ///VC #import "XPRoomViewController.h" #import "XPMineUserInfoViewController.h" -@interface XPDressSearchViewController () + +#import "ShoppingMallItemPreview.h" + +@interface XPDressSearchViewController () ///搜索 @property (nonatomic,strong) XPHomeSearchNavView *searchView; @@ -47,14 +51,55 @@ - (void)viewDidLoad { [super viewDidLoad]; + + self.view.backgroundColor = UIColorFromRGB(0x02061D); + + [self setupNavigationBar]; [self initSubViews]; [self initSubViewConstraints]; + + @kWeakify(self); + [self.presenter getFriends:^(NSArray * _Nonnull users) { + @kStrongify(self); + NSMutableArray *tempArray = @[].mutableCopy; + for (UserInfoModel *model in users) { + HomeSearchResultModel *resultModel = [[HomeSearchResultModel alloc] initFromUserInfo:model]; + [tempArray addObject:resultModel]; + } + self.datasource = tempArray.copy; + [self.tableView reloadData]; + } failure:^(NSError * _Nonnull error) { + + }]; +} + +- (void)setupNavigationBar { + UILabel *titleLabel = [self titleLabel]; + UIButton *backButton = [self backButton]; + [self.view addSubview:titleLabel]; + [self.view addSubview:backButton]; + + [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.centerX.mas_equalTo(self.view); + make.top.mas_equalTo(self.view).offset(44 + 11); + make.height.mas_equalTo(22); + }]; + + [backButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.leading.mas_equalTo(self.view).offset(16); + make.centerY.mas_equalTo(titleLabel); + make.size.mas_equalTo(CGSizeMake(22, 22)); + }]; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.view endEditing:YES]; } +- (void)didTapBack { + [self.navigationController popViewControllerAnimated:YES]; +} + #pragma mark - Public Method - (void)searchText:(NSString *)text { NSString * type = @"2"; @@ -69,7 +114,8 @@ - (void)initSubViewConstraints { [self.searchView mas_makeConstraints:^(MASConstraintMaker *make) { - make.leading.trailing.top.mas_equalTo(self.view); + make.top.mas_equalTo(self.view).offset(kNavigationHeight); + make.leading.trailing.mas_equalTo(self.view); make.height.mas_equalTo(kNavigationHeight); }]; [self.tableView mas_makeConstraints:^(MASConstraintMaker *make) { @@ -105,17 +151,29 @@ #pragma mark - XPDressShopSearchTableViewCellDelegate - (void)xPDressShopSearchTableViewCellSendButtonClick:(XPDressShopSearchTableViewCell *)cell { - NSIndexPath *indexPath = [self.tableView indexPathForCell:cell]; - HomeSearchResultModel * resultModel = [self.datasource xpSafeObjectAtIndex:indexPath.row]; - [self showLoading]; - [Api requestSendDress:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { - [self hideHUD]; - if (code == 200) { - [self showSuccessToast:YMLocalizedString(@"XPDressSearchViewController0")]; - } else { - [self showErrorToast:msg]; - } - } id:self.dressId targetUid:resultModel.uid]; + + ShoppingMallItemPreview *preview = [[ShoppingMallItemPreview alloc] initWith:self.selectedModel + isForGive:YES]; + preview.frame = CGRectMake(0, 0, 284, 353); + [TTPopup popupView:preview style:TTPopupStyleAlert]; + @kWeakify(self); + [preview setGiveWith:^(DressUpShopModel * _Nonnull model) { + @kStrongify(self); + @kWeakify(self); + NSIndexPath *indexPath = [self.tableView indexPathForCell:cell]; + HomeSearchResultModel * resultModel = [self.datasource xpSafeObjectAtIndex:indexPath.row]; + [self showLoading]; + [Api requestSendDress:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { + @kStrongify(self); + [self hideHUD]; + if (code == 200) { + [TTPopup dismiss]; + [self showSuccessToast:YMLocalizedString(@"XPDressSearchViewController0")]; + } else { + [self showErrorToast:msg]; + } + } id:self.selectedModel.dressUpId targetUid:resultModel.uid]; + }]; } #pragma mark - XPHomeSearchProtocol @@ -181,4 +239,25 @@ } return _searchView; } + +- (UIButton *)backButton { + UIButton *b = [UIButton buttonWithType:UIButtonTypeCustom]; + [b setImage:[kImage(@"vip_center_back_button") ms_SetImageForRTL] + forState:UIControlStateNormal]; + [b addTarget:self + action:@selector(didTapBack) + forControlEvents:UIControlEventTouchUpInside]; + return b; +} + +- (UILabel *)titleLabel { + UILabel *label = [[UILabel alloc] init]; + label.textAlignment = NSTextAlignmentCenter; + label.font = kFontMedium(17); + label.text = YMLocalizedString(@"1.0.30_text_11"); + label.textColor = UIColorFromRGB(0xD9E7F7); + return label; +} + + @end diff --git a/YuMi/Modules/YMMine/View/DressUp/View/XPDressUpShopCardViewController.m b/YuMi/Modules/YMMine/View/DressUp/View/XPDressUpShopCardViewController.m index a5d6a3fe..82fc57c2 100644 --- a/YuMi/Modules/YMMine/View/DressUp/View/XPDressUpShopCardViewController.m +++ b/YuMi/Modules/YMMine/View/DressUp/View/XPDressUpShopCardViewController.m @@ -55,8 +55,8 @@ ///赠送装扮 - (void)xPDressUpShopCardTableViewCellSendDress:(XPDressUpShopCardTableViewCell *)view dressId:(NSString *)dressId { XPDressSearchViewController *vc = [[XPDressSearchViewController alloc] init]; - vc.dressType = self.type; - vc.dressId = dressId; +// vc.dressType = self.type; +// vc.dressId = dressId; [self.navigationController pushViewController:vc animated:YES]; } diff --git a/YuMi/Modules/YMMine/View/DressUp/View/XPDressUpShopListViewController.m b/YuMi/Modules/YMMine/View/DressUp/View/XPDressUpShopListViewController.m index 8dd58113..0fa6f1ba 100644 --- a/YuMi/Modules/YMMine/View/DressUp/View/XPDressUpShopListViewController.m +++ b/YuMi/Modules/YMMine/View/DressUp/View/XPDressUpShopListViewController.m @@ -53,10 +53,11 @@ } #pragma mark - XPDressUpShopCollectionViewCellDelegate -- (void)xPDressUpShopCollectionViewCellSendDress:(XPDressUpShopCollectionViewCell *)view dressId:(NSString *)dressId { +- (void)xPDressUpShopCollectionViewCellSendDress:(XPDressUpShopCollectionViewCell *)view + dressId:(NSString *)dressId { XPDressSearchViewController *vc = [[XPDressSearchViewController alloc] init]; - vc.dressType = self.type; - vc.dressId = dressId; +// vc.dressType = self.type; +// vc.dressId = dressId; [self.navigationController pushViewController:vc animated:YES]; } diff --git a/YuMi/Modules/YMMine/View/DressUp/View/XPMineDressUpListViewController.m b/YuMi/Modules/YMMine/View/DressUp/View/XPMineDressUpListViewController.m index cd891173..9a89c57e 100644 --- a/YuMi/Modules/YMMine/View/DressUp/View/XPMineDressUpListViewController.m +++ b/YuMi/Modules/YMMine/View/DressUp/View/XPMineDressUpListViewController.m @@ -94,7 +94,6 @@ - (void)useHeadwearSuccess:(NSString *)headWearId { [self.presenter getUserHeadwearList]; [self showSuccessToast:headWearId.integerValue == 0 ? YMLocalizedString(@"XPMineDressUpViewController6") : YMLocalizedString(@"XPMineDressUpListViewController1")]; - } - (void)useCarSuccess:(NSString *)carId { diff --git a/YuMi/Modules/YMMine/View/XPMineViewController.m b/YuMi/Modules/YMMine/View/XPMineViewController.m index b295fc6e..edfe7324 100644 --- a/YuMi/Modules/YMMine/View/XPMineViewController.m +++ b/YuMi/Modules/YMMine/View/XPMineViewController.m @@ -47,8 +47,8 @@ #import "XPWebViewController.h" #import "XPRoomViewController.h" -#import "XPMineDressUpViewController.h" -#import "XPDressUpShopViewController.h" +#import "MyDressingViewController.h" +#import "ShoppingMallViewController.h" #import "XPMineFansViewController.h" #import "XPMineAttentionViewController.h" #import "XPMineVisitorViewController.h" @@ -70,6 +70,8 @@ #import "VIPCenterViewController.h" #import "VipSettingViewController.h" +#import "XPSkillCardPlayerManager.h" + UIKIT_EXTERN NSString *kRequestTicket; @@ -276,13 +278,13 @@ UIKIT_EXTERN NSString *kRequestTicket; break; case XPMineItemType_My_DressUp: { - XPMineDressUpViewController * dressUpVC = [[XPMineDressUpViewController alloc] init]; + MyDressingViewController * dressUpVC = [[MyDressingViewController alloc] init]; [self.navigationController pushViewController:dressUpVC animated:YES]; } break; case XPMineItemType_DressUp_Market: { - XPDressUpShopViewController *dressUpShopVc = [[XPDressUpShopViewController alloc] init]; + ShoppingMallViewController *dressUpShopVc = [[ShoppingMallViewController alloc] init]; [self.navigationController pushViewController:dressUpShopVc animated:YES]; } break; @@ -346,7 +348,7 @@ UIKIT_EXTERN NSString *kRequestTicket; } - (void)toMineItems { - XPMineDressUpViewController *vc = [[XPMineDressUpViewController alloc] init]; + MyDressingViewController *vc = [[MyDressingViewController alloc] init]; [self.navigationController pushViewController:vc animated:YES]; } @@ -641,6 +643,9 @@ UIKIT_EXTERN NSString *kRequestTicket; } - (void)onGetUserInfoSuccess:(UserInfoModel *)userInfo { + + [XPSkillCardPlayerManager shareInstance].userInfoModel = userInfo; + ///防止重新注册时,获取到用户信息不全,调用接口时401 [self hideHUD]; if (!userInfo.isBindPhone && [ClientConfig shareConfig].iOSPhoneBind) { diff --git a/YuMi/Modules/YMNewHome/Presenter/XPHomeContainerPresenter.m b/YuMi/Modules/YMNewHome/Presenter/XPHomeContainerPresenter.m index 1c0fa092..ffde9283 100644 --- a/YuMi/Modules/YMNewHome/Presenter/XPHomeContainerPresenter.m +++ b/YuMi/Modules/YMNewHome/Presenter/XPHomeContainerPresenter.m @@ -133,7 +133,6 @@ @kWeakify(self); [Api getHomeTabsCompletion:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) { @kStrongify(self); - // TODO: 不能在 API call 的地方返回 UI 元素 NSArray * array = [PIHomeCategoryTitleModel modelsWithArray:data.data]; for (PIHomeCategoryTitleModel *model in array) { model.checkedWidth = [UILabel getWidthWithText:model.name height:kGetScaleWidth(44) font:kFontSemibold(16)]; diff --git a/YuMi/Modules/YMNewHome/View/Search/Model/HomeSearchResultModel.h b/YuMi/Modules/YMNewHome/View/Search/Model/HomeSearchResultModel.h index 6e3d06f9..a04b30dd 100644 --- a/YuMi/Modules/YMNewHome/View/Search/Model/HomeSearchResultModel.h +++ b/YuMi/Modules/YMNewHome/View/Search/Model/HomeSearchResultModel.h @@ -8,6 +8,7 @@ #import #import "YUMINNNN.h" #import "UserLevelVo.h" +#import "UserInfoModel.h" NS_ASSUME_NONNULL_BEGIN @@ -33,6 +34,9 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic,assign) GenderType gender; ///搜索用户的时候 如果用户在房间中的话 就有roomUid @property (nonatomic,copy) NSString *roomUid; + +- (instancetype)initFromUserInfo:(UserInfoModel *)model; + @end NS_ASSUME_NONNULL_END diff --git a/YuMi/Modules/YMNewHome/View/Search/Model/HomeSearchResultModel.m b/YuMi/Modules/YMNewHome/View/Search/Model/HomeSearchResultModel.m index 3ccfe9e4..bc28832f 100644 --- a/YuMi/Modules/YMNewHome/View/Search/Model/HomeSearchResultModel.m +++ b/YuMi/Modules/YMNewHome/View/Search/Model/HomeSearchResultModel.m @@ -9,4 +9,15 @@ @implementation HomeSearchResultModel +- (instancetype)initFromUserInfo:(UserInfoModel *)model { + if (self = [super init]) { + self.userLevelVo = model.userLevelVo; + self.avatar = model.avatar; + self.uid = @(model.uid).stringValue; + self.nick = model.nick; + self.erbanNo = @(model.erbanNo).stringValue; + } + return self; +} + @end diff --git a/YuMi/Modules/YMNewHome/View/Search/Presenter/XPHomeSearchPresenter.h b/YuMi/Modules/YMNewHome/View/Search/Presenter/XPHomeSearchPresenter.h index 75d4d620..d3d17260 100644 --- a/YuMi/Modules/YMNewHome/View/Search/Presenter/XPHomeSearchPresenter.h +++ b/YuMi/Modules/YMNewHome/View/Search/Presenter/XPHomeSearchPresenter.h @@ -7,6 +7,8 @@ #import "BaseMvpPresenter.h" +@class UserInfoModel; + NS_ASSUME_NONNULL_BEGIN @interface XPHomeSearchPresenter : BaseMvpPresenter @@ -19,9 +21,8 @@ NS_ASSUME_NONNULL_BEGIN /// 获取首页推荐列表 - (void)getHomeRecommendRoomList; -/// 获取大家都在搜的房间列表 -/// @param sid keyID -- (void)getEveryOneSearchRoomList:(NSString *)sid; +- (void)getFriends:(void(^)(NSArray *users))success + failure:(void(^)(NSError *error))failure; @end diff --git a/YuMi/Modules/YMNewHome/View/Search/Presenter/XPHomeSearchPresenter.m b/YuMi/Modules/YMNewHome/View/Search/Presenter/XPHomeSearchPresenter.m index eef5c128..989deb22 100644 --- a/YuMi/Modules/YMNewHome/View/Search/Presenter/XPHomeSearchPresenter.m +++ b/YuMi/Modules/YMNewHome/View/Search/Presenter/XPHomeSearchPresenter.m @@ -7,6 +7,9 @@ #import "XPHomeSearchPresenter.h" #import "Api+Home.h" +#import "Api+Mine.h" +#import "UserInfoModel.h" +#import #import "XPHomeSearchProtocol.h" #import "AccountInfoStorage.h" #import "HomeSearchResultModel.h" @@ -36,13 +39,31 @@ }] uid:uid]; } -/// 获取大家都在搜的房间列表 -/// @param sid keyID -- (void)getEveryOneSearchRoomList:(NSString *)sid { -// [Api requestEveryOneSearchRoomList:[self createHttpCompletion:^(BaseModel * _Nonnull data) { -// NSArray * array = [HomeRecommendRoomModel modelsWithArray:data.data]; -// [[self getView] everyoneSearchListSuccess:array]; -// }] wordId:sid]; +- (void)getFriends:(void(^)(NSArray *users))success + failure:(void(^)(NSError *error))failure { + NSArray * array = [[NIMSDK sharedSDK].userManager myFriends]; + if (array.count > 0) { + NSMutableArray * uidsArray = [NSMutableArray array]; + for (int i = 0; i< array.count; i++) { + NIMUser * user = [array xpSafeObjectAtIndex:i]; + [uidsArray addObject:user.userId]; + } + NSString *uids = [uidsArray componentsJoinedByString:@","]; + [Api getUsersListInfo:[self createHttpCompletion:^(BaseModel * _Nonnull data) { + NSArray *users= [UserInfoModel modelsWithArray:data.data]; + if (success) { + success(users); + } + } fail:^(NSInteger code, NSString * _Nullable msg) { + if (failure) { + failure([NSError errorWithDomain:msg code:code userInfo:nil]); + } + } showLoading:YES errorToast:YES] uids:uids]; + } else { + if (success) { + success(@[]); + } + } } diff --git a/YuMi/Modules/YMNewHome/View/Search/Protocol/XPHomeSearchProtocol.h b/YuMi/Modules/YMNewHome/View/Search/Protocol/XPHomeSearchProtocol.h index 013c283b..837f0c35 100644 --- a/YuMi/Modules/YMNewHome/View/Search/Protocol/XPHomeSearchProtocol.h +++ b/YuMi/Modules/YMNewHome/View/Search/Protocol/XPHomeSearchProtocol.h @@ -10,14 +10,19 @@ NS_ASSUME_NONNULL_BEGIN @protocol XPHomeSearchProtocol + ///搜索成功 - (void)searchRoomSuccess:(NSArray *)data type:(NSString *)type; +@optional ///搜索页获取推荐房间成功 - (void)searchRoomGetRecommendSuccess:(NSArray *)data; + +@optional ///搜索页获取推荐房间失败 - (void)searchRoomGetRecommendFail; +@optional /// 大家都在搜房间列表成功 - (void)everyoneSearchListSuccess:(NSArray *)list; diff --git a/YuMi/Modules/YMNewHome/View/Search/View/SubView/XPHomeSearchNavView.h b/YuMi/Modules/YMNewHome/View/Search/View/SubView/XPHomeSearchNavView.h index ca8aeb96..fc5bd451 100644 --- a/YuMi/Modules/YMNewHome/View/Search/View/SubView/XPHomeSearchNavView.h +++ b/YuMi/Modules/YMNewHome/View/Search/View/SubView/XPHomeSearchNavView.h @@ -10,6 +10,8 @@ NS_ASSUME_NONNULL_BEGIN @class XPHomeSearchNavView; @protocol XPHomeSearchNavViewDelegate + +@optional //点击了返回 - (void)xPHomeSearchNavView:(XPHomeSearchNavView *)view didClickBack:(UIButton *)sender; ///点击了搜索 diff --git a/YuMi/Modules/YMNewHome/View/XPHomePagingViewController.m b/YuMi/Modules/YMNewHome/View/XPHomePagingViewController.m index adaab3a4..f1e561ea 100644 --- a/YuMi/Modules/YMNewHome/View/XPHomePagingViewController.m +++ b/YuMi/Modules/YMNewHome/View/XPHomePagingViewController.m @@ -14,8 +14,6 @@ #import "Api+Gift.h" #import "XPGiftStorage.h" -#import "ShoppingMallViewController.h" - @interface XPHomePagingViewController () @property (nonatomic, strong) UIView *topControlView; @@ -166,12 +164,6 @@ #pragma mark - - (void)didTapMineButton { -#if DEBUG - ShoppingMallViewController *vc = [[ShoppingMallViewController alloc] init]; - [self.navigationController pushViewController:vc animated:YES]; - return; -#endif - [self.pageContainer setViewControllers:@[self.viewControllers[1]] direction:UIPageViewControllerNavigationDirectionReverse animated:NO diff --git a/YuMi/Tools/CardManager/XPSkillCardPlayerManager.h b/YuMi/Tools/CardManager/XPSkillCardPlayerManager.h index 3679de07..f47c2247 100644 --- a/YuMi/Tools/CardManager/XPSkillCardPlayerManager.h +++ b/YuMi/Tools/CardManager/XPSkillCardPlayerManager.h @@ -7,6 +7,7 @@ #import #import +#import "UserInfoModel.h" typedef enum : NSUInteger { MICState_None = 0, @@ -38,8 +39,10 @@ typedef enum : NSUInteger { // 用户在 mic 的状态 @property (nonatomic, assign) MICState micState; +// 作为全局获取当前用户数据的 model,需要在相关 user/get 调用处更新,目前只在 mine tab 更新 +@property(nonatomic, strong) UserInfoModel *userInfoModel; -+ (instancetype)shareInstance ; ++ (instancetype)shareInstance; /** 播放一次音频 diff --git a/YuMi/ar.lproj/Localizable.strings b/YuMi/ar.lproj/Localizable.strings index 3493882d..ad1d2b0a 100644 --- a/YuMi/ar.lproj/Localizable.strings +++ b/YuMi/ar.lproj/Localizable.strings @@ -4009,3 +4009,7 @@ ineHeadView12" = "الحمل"; "1.0.30_text_7" = "Mall"; "1.0.30_text_8" = "My Dress"; "1.0.30_text_9" = "Homepage"; +"1.0.30_text_10" = "VIP%@-%@%"; +"1.0.30_text_11" = "Select Friend"; +"1.0.30_text_12" = "Not used"; +"1.0.30_text_13" = "<1Day"; diff --git a/YuMi/en.lproj/Localizable.strings b/YuMi/en.lproj/Localizable.strings index a00cb5cc..eb295ce2 100644 --- a/YuMi/en.lproj/Localizable.strings +++ b/YuMi/en.lproj/Localizable.strings @@ -3802,3 +3802,7 @@ "1.0.30_text_7" = "Mall"; "1.0.30_text_8" = "My Dress"; "1.0.30_text_9" = "Homepage"; +"1.0.30_text_10" = "VIP%@-%@%"; +"1.0.30_text_11" = "Select Friend"; +"1.0.30_text_12" = "Not used"; +"1.0.30_text_13" = "<1Day"; diff --git a/YuMi/tr.lproj/Localizable.strings b/YuMi/tr.lproj/Localizable.strings index 3295c489..13237696 100644 --- a/YuMi/tr.lproj/Localizable.strings +++ b/YuMi/tr.lproj/Localizable.strings @@ -3588,3 +3588,8 @@ "1.0.30_text_7" = "Mall"; "1.0.30_text_8" = "My Dress"; "1.0.30_text_9" = "Homepage"; +"1.0.30_text_10" = "VIP%@-%@%"; +"1.0.30_text_11" = "Select Friend"; +"1.0.30_text_12" = "Not used"; +"1.0.30_text_13" = "<1Day"; +"1.0.30_text_14" = "%@Day"; diff --git a/YuMi/zh-Hant.lproj/Localizable.strings b/YuMi/zh-Hant.lproj/Localizable.strings index a27fc807..44577eb9 100644 --- a/YuMi/zh-Hant.lproj/Localizable.strings +++ b/YuMi/zh-Hant.lproj/Localizable.strings @@ -3460,3 +3460,7 @@ "1.0.30_text_7" = "Mall"; "1.0.30_text_8" = "My Dress"; "1.0.30_text_9" = "Homepage"; +"1.0.30_text_10" = "VIP%@-%@%%"; +"1.0.30_text_11" = "Select Friend"; +"1.0.30_text_12" = "Not used"; +"1.0.30_text_13" = "<1Day";