From 68d35a7ed8830c4b352389dd59c4aa60d335ac34 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 13 May 2024 23:37:22 +0800 Subject: [PATCH 01/34] =?UTF-8?q?feat:=20=E6=90=9C=E7=B4=A2=E5=88=86?= =?UTF-8?q?=E5=8C=BAUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 +- lib/http/search.dart | 2 + lib/pages/search/widgets/search_text.dart | 7 +- lib/pages/search_panel/controller.dart | 14 +- .../search_panel/widgets/video_panel.dart | 125 +++++++++++++++++- 5 files changed, 142 insertions(+), 8 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 42fc03b8..77c168d0 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -189,7 +189,7 @@ class Api { 'https://s.search.bilibili.com/main/suggest'; // 分类搜索 - static const String searchByType = '/x/web-interface/search/type'; + static const String searchByType = '/x/web-interface/wbi/search/type'; // 记录视频播放进度 // https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/report.md diff --git a/lib/http/search.dart b/lib/http/search.dart index 9b493a84..70980547 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -75,6 +75,7 @@ class SearchHttp { required page, String? order, int? duration, + int? tid, }) async { var reqData = { 'search_type': searchType.type, @@ -84,6 +85,7 @@ class SearchHttp { 'page': page, if (order != null) 'order': order, if (duration != null) 'duration': duration, + if (tid != null) 'tid': tid, }; var res = await Request().get(Api.searchByType, data: reqData); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 039a851b..5e91a4cd 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -5,12 +5,14 @@ class SearchText extends StatelessWidget { final Function? onSelect; final int? searchTextIdx; final Function? onLongSelect; + final bool isSelect; const SearchText({ super.key, this.searchText, this.onSelect, this.searchTextIdx, this.onLongSelect, + this.isSelect = false, }); @override @@ -34,7 +36,10 @@ class SearchText extends StatelessWidget { child: Text( searchText!, style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant), + color: isSelect + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSurfaceVariant, + ), ), ), ), diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 85952e90..56d83601 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -16,14 +16,18 @@ class SearchPanelController extends GetxController { RxString order = ''.obs; // 视频时长筛选 仅用于搜索视频 RxInt duration = 0.obs; + // 视频分区筛选 仅用于搜索视频 + RxInt tid = (-1).obs; Future onSearch({type = 'init'}) async { var result = await SearchHttp.searchByType( - searchType: searchType!, - keyword: keyword!, - page: page.value, - order: searchType!.type != 'video' ? null : order.value, - duration: searchType!.type != 'video' ? null : duration.value); + searchType: searchType!, + keyword: keyword!, + page: page.value, + order: searchType!.type != 'video' ? null : order.value, + duration: searchType!.type != 'video' ? null : duration.value, + tid: searchType!.type != 'video' ? null : tid.value, + ); if (result['status']) { if (type == 'onRefresh') { resultList.value = result['data'].list; diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index c24a007c..06ee5d4b 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -3,6 +3,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/pages/search/widgets/search_text.dart'; import 'package:pilipala/pages/search_panel/index.dart'; class SearchVideoPanel extends StatelessWidget { @@ -94,7 +95,7 @@ class SearchVideoPanel extends StatelessWidget { style: ButtonStyle( padding: MaterialStateProperty.all(EdgeInsets.zero), ), - onPressed: () => controller.onShowFilterDialog(ctr), + onPressed: () => controller.onShowFilterSheet(ctr), icon: Icon( Icons.filter_list_outlined, size: 18, @@ -165,7 +166,33 @@ class VideoPanelController extends GetxController { {'label': '30-60分钟', 'value': 3}, {'label': '60分钟+', 'value': 4}, ]; + List> partFiltersList = [ + {'label': '全部', 'value': -1}, + {'label': '动画', 'value': 1}, + {'label': '番剧', 'value': 13}, + {'label': '国创', 'value': 167}, + {'label': '音乐', 'value': 3}, + {'label': '舞蹈', 'value': 129}, + {'label': '游戏', 'value': 4}, + {'label': '知识', 'value': 36}, + {'label': '科技', 'value': 188}, + {'label': '运动', 'value': 234}, + {'label': '汽车', 'value': 223}, + {'label': '生活', 'value': 160}, + {'label': '美食', 'value': 211}, + {'label': '动物', 'value': 217}, + {'label': '鬼畜', 'value': 119}, + {'label': '时尚', 'value': 155}, + {'label': '资讯', 'value': 202}, + {'label': '娱乐', 'value': 5}, + {'label': '影视', 'value': 181}, + {'label': '记录', 'value': 177}, + {'label': '电影', 'value': 23}, + {'label': '电视', 'value': 11}, + ]; + RxInt currentTimeFilterval = 0.obs; + RxInt currentPartFilterval = (-1).obs; @override void onInit() { @@ -219,4 +246,100 @@ class VideoPanelController extends GetxController { }, ); } + + onShowFilterSheet(searchPanelCtr) { + showModalBottomSheet( + context: Get.context!, + builder: (context) { + return StatefulBuilder( + builder: (context, StateSetter setState) { + return Container( + color: Theme.of(Get.context!).colorScheme.surface, + padding: const EdgeInsets.only(top: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ListTile( + title: Text('内容时长'), + ), + Padding( + padding: const EdgeInsets.only( + left: 14, + right: 14, + bottom: 14, + ), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in timeFiltersList) + Obx( + () => SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: + currentTimeFilterval.value == i['value'], + onSelect: (value) async { + currentTimeFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast("「${i['label']}」的筛选结果"); + SearchPanelController ctr = + Get.find( + tag: 'video${searchPanelCtr.keyword!}'); + ctr.duration.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ), + ) + ], + ), + ), + const ListTile( + title: Text('内容分区'), + ), + Padding( + padding: const EdgeInsets.only(left: 14, right: 14), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in partFiltersList) + SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: currentPartFilterval.value == i['value'], + onSelect: (value) async { + currentPartFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast("「${i['label']}」的筛选结果"); + SearchPanelController ctr = + Get.find( + tag: 'video${searchPanelCtr.keyword!}'); + ctr.tid.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ) + ], + ), + ) + ], + ), + ); + }, + ); + }, + ); + } } From cb7cd0a2f0658ca84d48b213760aee80decff555 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 19 May 2024 19:57:13 +0800 Subject: [PATCH 02/34] feat: videoDetail menu edit --- assets/images/coin.png | Bin 0 -> 7159 bytes lib/models/common/action_type.dart | 94 ++++++++++++++++ lib/pages/setting/pages/action_menu_set.dart | 100 ++++++++++++++++++ lib/pages/setting/style_setting.dart | 5 + lib/pages/video/detail/introduction/view.dart | 28 ++--- .../introduction/widgets/action_item.dart | 54 ++++++---- lib/router/app_pages.dart | 4 + lib/utils/storage.dart | 3 +- 8 files changed, 249 insertions(+), 39 deletions(-) create mode 100644 assets/images/coin.png create mode 100644 lib/models/common/action_type.dart create mode 100644 lib/pages/setting/pages/action_menu_set.dart diff --git a/assets/images/coin.png b/assets/images/coin.png new file mode 100644 index 0000000000000000000000000000000000000000..bc2952a7a5cf519a331b83c0f268eef88769d471 GIT binary patch literal 7159 zcmai3XEa<vI?2oo<*AX$uJ|9 zbE)c;5wTPqLBNLDXyI`F_$sdFW^)=nesPzJh7-d7Tk-$I#4N)}`MRL7DXk^Ce63JJ z$@|x~Dt)%t|F(sSB|<)Jwbk{iJW~8(_(!4PBksrHCfo7KN9jac37>Zv1N(QM&AyBK zr6Q{qIyv=nqi|5paxTn8tu9Ps`T@6=^q%B)@V@A&uz#M=&%bz?S5^*F<32*K3>Lq3 zVXCYAgH*bFr5u6k<-WPOe*5v6)S&0*WP^jaVU`j396F&bw*nD7yp@`KZ zf_Bfei3C%9{_H8dcljj8=IFOlR=nC(1>f%J2`k5Joj9#YKV0weokY&;Ba0H6&ZavJ z-`kJUtqiVj-R64NcxCU+tIcgOuD!4d!s@}{U5PNqxU%EJs~0&lomZ&`ck18=J=A); zb?~oe*?p-K*30XWEGdh}g@sRLjkpq!)vYGro@xZx9t*R_g`PET$k5@)GQTZ9J^Ip3 zhi=jp_$_R~_P|jrsiCnAy?(ms>v?kAYr%H%_Zc&~C#(w6&{!%z0lH5u2k{X5QrdV? z>xSUJRZ<+M1Tb_)0bVO6@f26cL}69`JOaG|*3rf&If+G(*Y-T_=f3;wHgy@fWS)E& zHluGHXednEwVc-1VNG-0^Lk9BHQQqGkTFM?w(8#C9bi$K>jVvL?rb^II?Ez=s94`P z|DpD2@$y-3KQj|!lYV#<|FExhgWWbZY_vAw`%md~ed)g9;CYj2=Oa~3!(Ri{A-X8v zpx$XkRN%?|H7(bPeTDT$U2Rn5Pt08M4%Dr=hUddQ3bO}vgt~_!e--cCO+GX`r6@mF z4)W?sy!%7r`bKCnc77>HZbRWXU4Qf!i{94oamm_O@#VKk*x?6i_IbSE{(qqL@pX>6g6tMuMySM63bXjKKQW`zugF43{dpMj=NYW(uw%8RJ+=#W zI?~pKSmdm9yVR-Bt?)~To7;!c|EzB$#BF(@wND79z8upagtno+y&?wL1Om$oC)Mkz?W>meIH|OtG$Eu09UW5x;^+{3Pb=)_j+O07=RCt<= zvDJ3?+-3010YS=Mw`{sCT}H(Vj8IsLVh$DT(sSUnyxOSFSSV_e;wcryI^ye~*LfKWB*0r%2$vcM19va@SGLQVv5rKjb7b-dKIVwZtba{LV?Nauw z4~NDSM}5AC{0d;OV4IsBZW5tZ;O)rno#Li7l~M2ob00O-f|9b_@h@czYKS^W*^iiF z8Esz>@pwWp6&sLaJH&b$=P@Q_@`O+0$9gxKdvw2I16+{7=P&FjN*)+bsfk0h^`^>Y$s z1Jl{Rze6gqdDR;=?7fNt1SYa`L%)B^8jU_a>8lF|`!Pxs@J@1uy!=`;zD(PFYkhQm zhqhbq(~@455`m}W;==ln7oTFf1hALu97-*24?!-b8%C-e2;|NQ_$GFB(7W9kU59@N z)6IT0UUMUwXIY{111C|1&)!n|fcE;n)ld*K>*r#LNCb zNDed2a>}ny*$^chgGqo-gJcIKPu7@TPaB?3sSNzuW`>pPiJa;N)ml43RU@x~Hp$PD<*cS3_SGnAH+P{(iOgzWiuelm zy;)VB^g6EwzBe=8r-$}bJ#h>aWW=$aC%E=(R+|mAJJx?}+2nS&yHE|}r}<_O$y7J4+{c!?hJv;GL$Qx-qLBx5v2?txK2h=+ z#IJ9zS)3%_ZJg0of3vwC)xJ?KRVM)R#<{Bf{4r!98A36E&b~=zN8rh9apQ~mfI4Q& zl9EWHY{TE+aQnRZ(ZYtnlilpbM`D~A4WP5JcJ8<{ngMj24hFJ2N!k0C=cAl0K_eTZ zt8|s*nLV>nHyCjHRFUN@+2|^Xsh8`ja9RhsWa#$(OkM0^OMr3Y;K>6dp&Ht75<@Zu z!i%bN`?*JQ>S830vo~#l&QomI(bRIh+cRbQtH7p$vyzgKBnH%0<3Uf&ad9=e!JCdq z3GoodNF`M%Vg$Llzj0}f4?8ZoS{E893BBNrRYj*bm2z1af$&Yz8ZC2_;QFcc`ApgfE@m_6@j3NSowGS>vcBjyf(feK zguOKg%X=Epv;?eJ+yLx6jm>F-Z|6@5{qjzVj^P>QWv%w?sg#7s6!o6mOL9(b0!`1eQFzt7 z{wCnv&gTR~k6phrGQZ$|4P~nvHPiM!DZ~n_A z4>-!;5yStQTKT3*2Kz~0@txrO*UwltY>P7MX&8gB&2Mv=Hi}hpSgS2nMz!YE`f5Ef z{To&zQPW?zohc;Mu)k6;!g$Kb=dAW1yDb~x4;J!$>T4B-g1L<;8z3Gtz~?ZVwo}`` z=ZR*?Qcv>#U{5ZNoB%Q-6uz2$(=haqpperZP%$u8q0qYzlW+dzeMb5Y7yMbAqHDPV z(B&ZfhXSTXKyp*q95nLy;jE|on>S!zy+fOTNawa$7Y>dMfN84C=(Sq_a%JpNs?7K` zirCMB2yi#NhlHr9!=s0!G-)VgFvhdPgvzLtSD?9(#7 znZYH~qFt1M0SiOU-+rLWm4ie#d<9N|E^=bIWoiab4f_7G_PYrz?5(ra+nNWJai!Kc z3aAA~oIkx_?jG~w{0cA(e4zp1eZV@bu|{J=jG4VOfemZCpr|~y(d)%EkFFp$ZlJ*z zoLsvDr!_gZTSohkQ^p57p>ok9&S$YJ&5y|noJ*tq%AeBFvt6sx#r{cN`tjxMZ*B}V z{_5IGl{Fr$+g(bV$EEZyw^fc$uNf-4KfC9~T(opPy!jgNsMDM6eRCdt$MBo+Lwm(6 ziUmXZY%ch<kP$X5Z#1YG)4EL>;v@HohmWj$TvEO`G}z} zYVkwBeo&MYQ2CQ&A=sgbW-$xE#U&$-Z@>PagwI9ia<@<66W-Ac(9Vwddl_R5h!CBc zG3K{kL(FVG?M8#RxCzhOZWWJJ04VgU`hks8Ysy(&VTKk<4&!z~2Y_BQSX~)E1!xDw zjZhS5iqhhNJa9`oI76`NxlhZBOVLv^26!eH;Xb15DkKN2{*^+x6nt@)5w3)i&RHwt zQboiRTkG>>88hLm&*R^zDW)<%LjjT^#IZmUj4A#$wo7v>s@Dt)x+uDf{hLTspnLvV6%+(ehY}w; zmDXjAH9@v_To}8w#qmpTPoIYf&1}4u>wMgSLK%m!^H6Hvqp>?#&;ipCmB69~I= zP@-4DjsyVY4qWP|N}u$C6Pa zyr3P!P383j*$5`MBdZ8j*h!B!{YKGMTkJI?Nr;+j0dNJVw(v2(QWOtGChlmb^a6lg zC*Vy`187H|qcuIw&&>zKib3?2@B4d=I3f|tE=TS#Lb6{FoXnnV8(!p+{|LB5$;Az{ z2A+ikYYI^lb@B4v0??(6Z)6L!Nz#a(@Wm~_d`BeR`P(5|EYG6-v%-`dpfFoZx|R)= zhuTCF&VuQe4f!WSUSJRO1L?LT@x~wn`^b_8B1pf-g^J8Y=6W3qP?Ix+MF#y5GYXVs zzKgYC?AM?$J5&V5U}?^{KJMlrn%Mf;XlVM-kfb*+_j1mf#LdW(1kI9glLva(V}Ras zF{)ERm`uhjH$m8G=je1&Wr!^4y>g`>nKHEF2GZ3)d-^%Q6Cjra<`%`BR@x6yDmBy{ zHxkQrZYkWy{uxyWN-Ck&iKNU~( zWp)o*2#?mOWTYj1%yvQZY>#qXKaek|QheO(^CysR0;xjTK}l3c){!iQu@FM}trG|C zqY5YrCR2^jqeeTmTMjzX|*d>wip!(kJ z!p}ZGWEgbn^dT6EXIQ{dF&V4f$)GFc1>o6F*#N?qI-mq9NVB>2W%IJvD((_Ov>zC; zu{ehOwMC3t{exWrr>uB6A3XckT-Ko<(By@ar-Nz~l#rN2z7VMueFfhr{wL{>qc8VT za4)%7;@3;z(18rs?VL0QEcpD3zelnAO%41*Jxptp02w{oQt!yIhddsb>ZGuGBcG;& zCe_KaJv4fb1KcqwV3SW|970g^gl~qV3WF7Ipei19>cs%90ZzXU1|?=Vpp*nBPwXgP8yQTOq*RrTP3P>%daQQbea)PLeks}Xrr+6LA*Ka(u5c}R5qmD52lqO8pTp!r6U29u7)!;n}G=UUpKi0-g@d0#GkcZcR&@3mbP6tml39i)NU^5%gvTlEWZ)^j?U!1zv=LD z!cwU^j(5y)=1!4??g7o-RxJ@awi=BZ4I`dZMR1?u?4Yfu((sKL{p)@z`uM8nN^5c^K%u4`D8*BuHq@?_UtvzAyTb`C1qB-=ha`Nx9t3_oxpo6^h1 zLM2H4wb#om6&YhU*B0U$ZCn{SJPNu9LdmLBv_B-54aox;Io4ZZr9HA#Llq594)q}s z+cQB$`Ze5Z{@TNP4~)m;&mmcs2b5Y(vE&1i%W@XGUhgHH_fpNnD!&_Ng!Rvttj~Ri zNGcPl#OH#pRBRk(9sH(aeY|t8Fn{1(j_%`RG1ESs!f+A&cf!ONSwps)>!a zVZ*@Vw=oUde@rIMA9m=!>^7h6(R}C;9$^*&KBeOG^$(0(%Gjin-dU9vwE021SmV3A z3pQF;IZ#Oc!*hWuKZ1EpRWFD{=m*1t#}vz7#veSv>qt)Ccay~l3?j}M_W^Sb> zanl})m_^+3yBa@tnNLoh!B$qRt9@J-uDM^O)Yw_e5fK!-#Hhf<4~$C1tYDZ+VR*j2 z{AXn_JknafKU@GSfFq;bO$J00#NCOj=YfX{vw@|#%eclfp=l9mVYiTjtw?C3fz@>` z64MaIIvWz}K}O^ZXYc3Z0QPjEN28^=OFvS~3bHRx_2$Opi5)U+-2V1_n7e#)<_Bm& zPx&(X!U8W>knt#XH`GULXw_N{>k=#HTX{r7$qnCfh zx#>2(>CSNv;(8i+@0HuB6L)}((vj_K;ACWkoW_Gkca-~j4<3I0x4&E;JFSuX-cPN1 zPTh(Ela&}+8UF9VvIY8T5A@WKUU-)00i*sUX0gY_>-?{$-IJLMSGW$I2ivT{Oio1U z9K+mo%PCIl%UPt$X72CNc{zD~9%J>anzm~xLt4ovwS|KGIL&hCdKI7CUGl6pHbbD{R_;~c|h%se}} zrD8#cvGbkY-XL9k?27}j;imls*~h=d@@E$;iY=ulsFvNxE(CR+#E=Z4$~mA#@or`07naUm?|czbR&_QxkoW|i^+tLG?WF2;EYGN+Xu zq{gQryB(ta#Js$)LG(M@QD^CaMY$*DJ?*4ZBwR+z1Jj!7e9TGb-_M0z8I+;Gv^lR? z)8teeQF2KBc46zCd*)gEhIaC%ql<(yDSIs+^?_Cn;Kf;qd{u&}$2A5?Bs==_6Q>lD z{wGM17^~LvGEx5({$?LdGm?b%>LS}?NmC1#2Asc6Xn(J@63(7#_mS#17$zM&d#6xK zRZYf(wPmrB);c{Isu~g(iD{%>353^R*Ho(LUkQ;?-yx zwd~$Igjqu3uA7dwQG^J?w=jk!&+A2roChWuZy(m~Q(KKkC8DPWUb}`eKP5Kkiu(Ac ztUt)act5wB=+d~|3OuN7HtL$%QB0$rT6|^H@07(_lJNDBf7^$r+dNc$%~nDcwuQAx zsqVpgo+C)J1?jgwX)jscx&ElNw3f?%S@~`1<8Xo56>eiau0M1B+ZQO?|Gpn1&hT(C l{=<}~mvg`V-P`>?i}iJ^lzFK-(e&S({%sST25l$Qe*pGzPR;-T literal 0 HcmV?d00001 diff --git a/lib/models/common/action_type.dart b/lib/models/common/action_type.dart new file mode 100644 index 00000000..2284df6c --- /dev/null +++ b/lib/models/common/action_type.dart @@ -0,0 +1,94 @@ +// 操作类型的枚举值:点赞 不喜欢 收藏 投币 稍后再看 下载封面 后台播放 听视频 分享 下载视频 +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +enum ActionType { + like, + coin, + collect, + watchLater, + share, + dislike, + downloadCover, + copyLink, + threeAction, + // backgroundPlay, + // listenVideo, + // downloadVideo, +} + +extension ActionTypeExtension on ActionType { + String get value => [ + 'like', + 'coin', + 'collect', + 'watchLater', + 'share', + 'dislike', + 'downloadCover', + 'copyLink', + // 'backgroundPlay', + // 'listenVideo', + // 'downloadVideo', + ][index]; + String get label => [ + '点赞视频', + '投币', + '收藏视频', + '稍后再看', + '视频分享', + '不喜欢', + '下载封面', + '复制链接', + // '后台播放', + // '听视频', + // '下载视频', + ][index]; +} + +List actionMenuConfig = [ + { + 'icon': const Icon(Icons.thumb_up_alt_outlined), + 'label': '点赞视频', + 'value': ActionType.like, + }, + { + 'icon': Image.asset( + 'assets/images/coin.png', + width: 26, + color: IconTheme.of(Get.context!).color!.withOpacity(0.65), + ), + 'label': '投币', + 'value': ActionType.coin, + }, + { + 'icon': const Icon(Icons.star_border), + 'label': '收藏视频', + 'value': ActionType.collect, + }, + { + 'icon': const Icon(Icons.watch_later_outlined), + 'label': '稍后再看', + 'value': ActionType.watchLater, + }, + { + 'icon': const Icon(Icons.share), + 'label': '视频分享', + 'value': ActionType.share, + }, + { + 'icon': const Icon(Icons.thumb_down_alt_outlined), + 'label': '不喜欢', + 'value': ActionType.dislike, + }, + { + 'icon': const Icon(Icons.image_outlined), + 'label': '下载封面', + 'value': ActionType.downloadCover, + }, + { + 'icon': const Icon(Icons.link_outlined), + 'label': '复制链接', + 'value': ActionType.copyLink, + }, +]; diff --git a/lib/pages/setting/pages/action_menu_set.dart b/lib/pages/setting/pages/action_menu_set.dart new file mode 100644 index 00000000..f7d92e17 --- /dev/null +++ b/lib/pages/setting/pages/action_menu_set.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/models/common/action_type.dart'; +import '../../../utils/storage.dart'; + +class ActionMenuSetPage extends StatefulWidget { + const ActionMenuSetPage({super.key}); + + @override + State createState() => _ActionMenuSetPageState(); +} + +class _ActionMenuSetPageState extends State { + Box settingStorage = GStrorage.setting; + late List actionTypeSort; + late List allLabels; + + @override + void initState() { + super.initState(); + actionTypeSort = settingStorage.get(SettingBoxKey.actionTypeSort, + defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); + allLabels = actionMenuConfig; + allLabels.sort((a, b) { + int indexA = actionTypeSort.indexOf((a['value'] as ActionType).value); + int indexB = actionTypeSort.indexOf((b['value'] as ActionType).value); + if (indexA == -1) indexA = actionTypeSort.length; + if (indexB == -1) indexB = actionTypeSort.length; + return indexA.compareTo(indexB); + }); + } + + void saveEdit() { + List sortedTabbar = allLabels + .where((i) => actionTypeSort.contains((i['value'] as ActionType).value)) + .map((i) => (i['value'] as ActionType).value) + .toList(); + settingStorage.put(SettingBoxKey.actionTypeSort, sortedTabbar); + SmartDialog.showToast('保存成功,下次启动时生效'); + } + + void onReorder(int oldIndex, int newIndex) { + setState(() { + if (newIndex > oldIndex) { + newIndex -= 1; + } + final tabsItem = allLabels.removeAt(oldIndex); + allLabels.insert(newIndex, tabsItem); + }); + } + + @override + Widget build(BuildContext context) { + final listTiles = [ + for (int i = 0; i < allLabels.length; i++) ...[ + CheckboxListTile( + key: Key((allLabels[i]['value'] as ActionType).value), + value: actionTypeSort + .contains((allLabels[i]['value'] as ActionType).value), + onChanged: (bool? newValue) { + String actionTypeId = (allLabels[i]['value'] as ActionType).value; + if (!newValue!) { + actionTypeSort.remove(actionTypeId); + } else { + actionTypeSort.add(actionTypeId); + } + setState(() {}); + }, + title: Row( + children: [ + allLabels[i]['icon'], + const SizedBox(width: 8), + Text(allLabels[i]['label']), + ], + ), + secondary: const Icon(Icons.drag_indicator_rounded), + ) + ] + ]; + + return Scaffold( + appBar: AppBar( + title: const Text('视频操作菜单'), + actions: [ + TextButton(onPressed: () => saveEdit(), child: const Text('保存')), + const SizedBox(width: 12) + ], + ), + body: ReorderableListView( + onReorder: onReorder, + physics: const NeverScrollableScrollPhysics(), + footer: SizedBox( + height: MediaQuery.of(context).padding.bottom + 30, + ), + children: listTiles, + ), + ); + } +} diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 364eabf0..20fdada0 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -289,6 +289,11 @@ class _StyleSettingState extends State { onTap: () => Get.toNamed('/navbarSetting'), title: Text('底部导航栏设置', style: titleStyle), ), + ListTile( + dense: false, + onTap: () => Get.toNamed('/actionMenuSet'), + title: Text('操作菜单设置', style: titleStyle), + ), if (Platform.isAndroid) ListTile( dense: false, diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9103c130..dab81f18 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,7 +1,6 @@ import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; @@ -539,26 +538,21 @@ class _VideoInfoState extends State with TickerProviderStateMixin { builder: (BuildContext context, BoxConstraints constraints) { return Container( margin: const EdgeInsets.only(top: 6, bottom: 4), - height: constraints.maxWidth / 5 * 0.8, - child: GridView.count( - physics: const NeverScrollableScrollPhysics(), - primary: false, - padding: EdgeInsets.zero, - crossAxisCount: 5, - childAspectRatio: 1.25, - children: [ + height: constraints.maxWidth / 5, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.thumbsUp), - selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), + icon: const Icon(Icons.thumb_up_alt_outlined), + selectIcon: const Icon(Icons.thumb_up), onTap: handleState(videoIntroController.actionLikeVideo), selectStatus: videoIntroController.hasLike.value, text: widget.videoDetail!.stat!.like!.toString()), ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.b), - selectIcon: const Icon(FontAwesomeIcons.b), + icon: Image.asset('assets/images/coin.png', width: 30), onTap: handleState(videoIntroController.actionCoinVideo), selectStatus: videoIntroController.hasCoin.value, text: widget.videoDetail!.stat!.coin!.toString(), @@ -566,8 +560,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.star), - selectIcon: const Icon(FontAwesomeIcons.solidStar), + icon: const Icon(Icons.star_border), + selectIcon: const Icon(Icons.star), onTap: () => showFavBottomSheet(), onLongPress: () => showFavBottomSheet(type: 'longPress'), selectStatus: videoIntroController.hasFav.value, @@ -575,7 +569,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ActionItem( - icon: const Icon(FontAwesomeIcons.clock), + icon: const Icon(Icons.watch_later_outlined), onTap: () async { final res = await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); @@ -585,7 +579,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { text: '稍后看', ), ActionItem( - icon: const Icon(FontAwesomeIcons.shareFromSquare), + icon: const Icon(Icons.share), onTap: () => videoIntroController.actionShareVideo(), selectStatus: false, text: '分享', diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 022d9223..2a89afe7 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/utils/feed_back.dart'; class ActionItem extends StatelessWidget { - final Icon? icon; + final dynamic icon; final Icon? selectIcon; final Function? onTap; final Function? onLongPress; @@ -31,26 +32,37 @@ class ActionItem extends StatelessWidget { if (onLongPress != null) {onLongPress!()} }, borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 4), - selectStatus - ? Icon(selectIcon!.icon!, - size: 18, color: Theme.of(context).colorScheme.primary) - : Icon(icon!.icon!, - size: 18, color: Theme.of(context).colorScheme.outline), - const SizedBox(height: 6), - Text( - text ?? '', - style: TextStyle( - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, - ), - ) - ], + child: SizedBox( + width: (Get.size.width - 24) / 5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + icon is Icon + ? Icon( + selectStatus ? selectIcon!.icon ?? icon!.icon : icon!.icon, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ) + : Image.asset( + 'assets/images/coin.png', + width: 25, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + const SizedBox(height: 6), + Text( + text ?? '', + style: TextStyle( + color: + selectStatus ? Theme.of(context).colorScheme.primary : null, + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + ), + ) + ], + ), ), ); } diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 7fda1bd8..2ca333f8 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -35,6 +35,7 @@ import '../pages/search/index.dart'; import '../pages/search_result/index.dart'; import '../pages/setting/extra_setting.dart'; import '../pages/setting/index.dart'; +import '../pages/setting/pages/action_menu_set.dart'; import '../pages/setting/pages/color_select.dart'; import '../pages/setting/pages/display_mode.dart'; import '../pages/setting/pages/font_size_select.dart'; @@ -174,6 +175,9 @@ class Routes { // navigation bar CustomGetPage( name: '/navbarSetting', page: () => const NavigationBarSetPage()), + // 操作菜单 + CustomGetPage( + name: '/actionMenuSet', page: () => const ActionMenuSetPage()), ]; } diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 29cf1846..4a163446 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -149,7 +149,8 @@ class SettingBoxKey { tabbarSort = 'tabbarSort', // 首页tabbar dynamicBadgeMode = 'dynamicBadgeMode', enableGradientBg = 'enableGradientBg', - navBarSort = 'navBarSort'; + navBarSort = 'navBarSort', + actionTypeSort = 'actionTypeSort'; } class LocalCacheKey { From eef9e3ae9eb3713a14b55fd04f0880132d623beb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 30 May 2024 23:58:42 +0800 Subject: [PATCH 03/34] opt: loading Ani --- assets/loading.json | 1 + assets/trail_loading.json | 1 + lib/pages/video/detail/introduction/view.dart | 12 ++++++------ lib/plugin/pl_player/view.dart | 7 ++++--- pubspec.lock | 8 ++++++++ pubspec.yaml | 8 +++++--- 6 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 assets/loading.json create mode 100644 assets/trail_loading.json diff --git a/assets/loading.json b/assets/loading.json new file mode 100644 index 00000000..38bccbed --- /dev/null +++ b/assets/loading.json @@ -0,0 +1 @@ +{"v":"5.7.11","fr":60,"ip":0,"op":81,"w":1920,"h":1080,"nm":"Loading Dots","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Dot4","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":25,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":39,"s":[100]},{"t":55,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[1142,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":39,"s":[1142,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":55,"s":[1142,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":25,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":39,"s":[75,75,100]},{"t":55,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Dot3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":17,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":31,"s":[100]},{"t":47,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":17,"s":[1022,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":31,"s":[1022,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":47,"s":[1022,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":17,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":31,"s":[75,75,100]},{"t":47,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Dot2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":23,"s":[100]},{"t":39,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":9,"s":[902,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":23,"s":[902,500,0],"to":[0,0,0],"ti":[0,0,0]},{"t":39,"s":[902,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":9,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":23,"s":[75,75,100]},{"t":39,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Dot1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[100]},{"t":30,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[782,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":14,"s":[782,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":30,"s":[782,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":14,"s":[75,75,100]},{"t":30,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/trail_loading.json b/assets/trail_loading.json new file mode 100644 index 00000000..9fb39ea6 --- /dev/null +++ b/assets/trail_loading.json @@ -0,0 +1 @@ +{"v":"4.6.8","fr":60,"ip":0,"op":106,"w":500,"h":500,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 5","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":20,"s":[0],"e":[360]},{"t":110}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[10,10]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":20,"op":620,"st":20,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 4","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":15,"s":[0],"e":[360]},{"t":105}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":15,"op":615,"st":15,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":10,"s":[0],"e":[360]},{"t":100}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[30,30]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":10,"op":610,"st":10,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":5,"s":[0],"e":[360]},{"t":95}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[40,40]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":5,"op":605,"st":5,"bm":0,"sr":1},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[0],"e":[360]},{"t":90}]},"p":{"a":0,"k":[250,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0"],"t":0,"s":[50,50],"e":[40,40]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0"],"t":84,"s":[40,40],"e":[50,50]},{"t":100}]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":600,"st":0,"bm":0,"sr":1}]} \ No newline at end of file diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9103c130..06a026aa 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -5,6 +5,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; +import 'package:lottie/lottie.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/video/detail/index.dart'; @@ -97,11 +98,14 @@ class _VideoIntroPanelState extends State ); } } else { - return const SliverToBoxAdapter( + return SliverToBoxAdapter( child: SizedBox( height: 100, child: Center( - child: CircularProgressIndicator(), + child: Lottie.asset( + 'assets/loading.json', + width: 200, + ), ), ), ); @@ -595,8 +599,4 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); }); } - - // Widget StaffPanel(BuildContext context, videoIntroController) { - // return - // } } diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index df68da2f..6508b624 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -7,6 +7,7 @@ import 'package:flutter_volume_controller/flutter_volume_controller.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:lottie/lottie.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:pilipala/models/common/gesture_mode.dart'; @@ -909,9 +910,9 @@ class _PLVideoPlayerState extends State colors: [Colors.black26, Colors.transparent], ), ), - child: Image.asset( - 'assets/images/loading.gif', - height: 25, + child: Lottie.asset( + 'assets/loading.json', + width: 200, ), ), ); diff --git a/pubspec.lock b/pubspec.lock index 8f8a5cae..7ff7ef0d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -846,6 +846,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" + lottie: + dependency: "direct main" + description: + name: lottie + sha256: "6a24ade5d3d918c306bb1c21a6b9a04aab0489d51a2582522eea820b4093b62b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.2" mailer: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a269cd6f..10a0f065 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -146,6 +146,7 @@ dependencies: expandable: ^5.0.1 # 投屏 dlna_dart: ^0.0.8 + lottie: ^3.1.2 dev_dependencies: flutter_test: @@ -166,15 +167,15 @@ dev_dependencies: build_runner: ^2.4.8 dependency_overrides: - media_kit: + media_kit: git: url: https://github.com/media-kit/media-kit path: media_kit - media_kit_video: + media_kit_video: git: url: https://github.com/media-kit/media-kit path: media_kit_video - media_kit_libs_video: + media_kit_libs_video: git: url: https://github.com/media-kit/media-kit path: libs/universal/media_kit_libs_video @@ -205,6 +206,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: + - assets/ - assets/images/ - assets/images/lv/ - assets/images/logo/ From 3b1ad133db0a9e8bdec5d47ec44e030dc4ee8367 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 2 Jun 2024 22:32:00 +0800 Subject: [PATCH 04/34] mod: logs replace# --- lib/pages/setting/pages/logs.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pages/setting/pages/logs.dart b/lib/pages/setting/pages/logs.dart index 0958edb8..f497aee5 100644 --- a/lib/pages/setting/pages/logs.dart +++ b/lib/pages/setting/pages/logs.dart @@ -41,7 +41,8 @@ class _LogsPageState extends State { .replaceAll('DEVICE INFO', '设备信息') .replaceAll('APP INFO', '应用信息') .replaceAll('ERROR', '错误信息') - .replaceAll('STACK TRACE', '错误堆栈'); + .replaceAll('STACK TRACE', '错误堆栈') + .replaceAll('#', 'Line'); }).toList(); List> result = []; for (String i in contentList) { @@ -50,7 +51,7 @@ class _LogsPageState extends State { .split("\n") .map((l) { if (l.startsWith("Crash occurred on")) { - date = DateTime.parse( + date = DateTime.tryParse( l.split("Crash occurred on")[1].trim().split('.')[0], ); return ""; From e0e1c6b08af706f79856074ba4b3ea5a53806fc4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 4 Jun 2024 23:59:23 +0800 Subject: [PATCH 05/34] upgrade: color --- lib/common/pages_bottom_sheet.dart | 2 +- lib/common/skeleton/skeleton.dart | 4 ++-- lib/common/widgets/stat/danmu.dart | 2 +- lib/common/widgets/stat/view.dart | 2 +- .../introduction/widgets/intro_detail.dart | 4 ++-- lib/pages/dynamics/view.dart | 5 ++--- lib/pages/dynamics/widgets/additional_panel.dart | 2 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- lib/pages/dynamics/widgets/up_panel.dart | 4 ++-- lib/pages/search/widgets/search_text.dart | 5 ++++- lib/pages/setting/pages/font_size_select.dart | 2 +- .../detail/introduction/widgets/fav_panel.dart | 2 +- .../detail/introduction/widgets/group_panel.dart | 2 +- .../detail/introduction/widgets/menu_row.dart | 4 ++-- lib/pages/video/detail/reply_new/view.dart | 2 +- lib/pages/video/detail/reply_reply/view.dart | 2 +- lib/pages/video/detail/widgets/ai_detail.dart | 2 +- lib/pages/video/detail/widgets/app_bar.dart | 2 +- .../video/detail/widgets/header_control.dart | 16 ++++++++-------- lib/utils/image_save.dart | 2 +- 20 files changed, 35 insertions(+), 33 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index 2a56eb41..49e9b4d8 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -124,7 +124,7 @@ class EpisodeBottomSheet { }); return Container( height: sheetHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ buildTitle(), diff --git a/lib/common/skeleton/skeleton.dart b/lib/common/skeleton/skeleton.dart index 34e87f55..b17a55fc 100644 --- a/lib/common/skeleton/skeleton.dart +++ b/lib/common/skeleton/skeleton.dart @@ -13,8 +13,8 @@ class Skeleton extends StatelessWidget { var shimmerGradient = LinearGradient( colors: [ Colors.transparent, - Theme.of(context).colorScheme.background.withAlpha(10), - Theme.of(context).colorScheme.background.withAlpha(10), + Theme.of(context).colorScheme.surface.withAlpha(10), + Theme.of(context).colorScheme.surface.withAlpha(10), Colors.transparent, ], stops: const [ diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart index c1c439db..511839a0 100644 --- a/lib/common/widgets/stat/danmu.dart +++ b/lib/common/widgets/stat/danmu.dart @@ -14,7 +14,7 @@ class StatDanMu extends StatelessWidget { Map colorObject = { 'white': Colors.white, 'gray': Theme.of(context).colorScheme.outline, - 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), + 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), }; Color color = colorObject[theme]!; return Row( diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart index 2665e2d4..5359c979 100644 --- a/lib/common/widgets/stat/view.dart +++ b/lib/common/widgets/stat/view.dart @@ -14,7 +14,7 @@ class StatView extends StatelessWidget { Map colorObject = { 'white': Colors.white, 'gray': Theme.of(context).colorScheme.outline, - 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), + 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), }; Color color = colorObject[theme]!; return Row( diff --git a/lib/pages/bangumi/introduction/widgets/intro_detail.dart b/lib/pages/bangumi/introduction/widgets/intro_detail.dart index c5bbd566..07684a86 100644 --- a/lib/pages/bangumi/introduction/widgets/intro_detail.dart +++ b/lib/pages/bangumi/introduction/widgets/intro_detail.dart @@ -20,10 +20,10 @@ class IntroDetail extends StatelessWidget { sheetHeight = localCache.get('sheetHeight'); TextStyle smallTitle = TextStyle( fontSize: 12, - color: Theme.of(context).colorScheme.onBackground, + color: Theme.of(context).colorScheme.onSurface, ); return Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(left: 14, right: 14), height: sheetHeight, child: Column( diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index da15239d..258ad531 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -162,13 +162,12 @@ class _DynamicsPageState extends State decoration: BoxDecoration( color: Theme.of(context) .colorScheme - .surfaceVariant + .surfaceContainerHighest .withOpacity(0.7), borderRadius: BorderRadius.circular(20), ), thumbDecoration: BoxDecoration( - color: - Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(20), ), duration: const Duration(milliseconds: 300), diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart index fa11f217..50e1b6d3 100644 --- a/lib/pages/dynamics/widgets/additional_panel.dart +++ b/lib/pages/dynamics/widgets/additional_panel.dart @@ -19,7 +19,7 @@ Widget addWidget(item, context, type, {floor = 1}) { }; Color bgColor = floor == 1 ? Theme.of(context).dividerColor.withOpacity(0.08) - : Theme.of(context).colorScheme.background; + : Theme.of(context).colorScheme.surface; switch (type) { case 'ADDITIONAL_TYPE_UGC': // 转发的投稿 diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 0d3baecd..8acdc26a 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -52,7 +52,7 @@ class AuthorPanel extends StatelessWidget { color: item.modules.moduleAuthor!.vip != null && item.modules.moduleAuthor!.vip['status'] > 0 ? const Color.fromARGB(255, 251, 100, 163) - : Theme.of(context).colorScheme.onBackground, + : Theme.of(context).colorScheme.onSurface, fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, ), ), diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index ee522cbb..f8c973a0 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -69,7 +69,7 @@ class _UpPanelState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(left: 16, right: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -95,7 +95,7 @@ class _UpPanelState extends State { ), Container( height: 90, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Row( children: [ Flexible( diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 039a851b..1b96d412 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -16,7 +16,10 @@ class SearchText extends StatelessWidget { @override Widget build(BuildContext context) { return Material( - color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), + color: Theme.of(context) + .colorScheme + .surfaceContainerHighest + .withOpacity(0.5), borderRadius: BorderRadius.circular(6), child: Padding( padding: EdgeInsets.zero, diff --git a/lib/pages/setting/pages/font_size_select.dart b/lib/pages/setting/pages/font_size_select.dart index 4985c83f..f5ca6be3 100644 --- a/lib/pages/setting/pages/font_size_select.dart +++ b/lib/pages/setting/pages/font_size_select.dart @@ -66,7 +66,7 @@ class _FontSizeSelectPageState extends State { .colorScheme .primary .withOpacity(0.3))), - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), child: Row( children: [ diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index 517caeaa..5ef78967 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -29,7 +29,7 @@ class _FavPanelState extends State { Widget build(BuildContext context) { return Container( height: sheetHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ AppBar( diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/pages/video/detail/introduction/widgets/group_panel.dart index 64ff913d..dcdaf9c5 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/group_panel.dart @@ -57,7 +57,7 @@ class _GroupPanelState extends State { Widget build(BuildContext context) { return Container( height: sheetHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ AppBar( diff --git a/lib/pages/video/detail/introduction/widgets/menu_row.dart b/lib/pages/video/detail/introduction/widgets/menu_row.dart index c175aff1..a26c86f0 100644 --- a/lib/pages/video/detail/introduction/widgets/menu_row.dart +++ b/lib/pages/video/detail/introduction/widgets/menu_row.dart @@ -12,7 +12,7 @@ class MenuRow extends StatelessWidget { Widget build(BuildContext context) { return Container( width: double.infinity, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(top: 9, bottom: 9, left: 12), child: SingleChildScrollView( scrollDirection: Axis.horizontal, @@ -84,7 +84,7 @@ class MenuRow extends StatelessWidget { style: TextStyle( fontSize: 13, color: selectStatus - ? Theme.of(context).colorScheme.onBackground + ? Theme.of(context).colorScheme.onSurface : Theme.of(context).colorScheme.outline), ), ), diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 3da15f64..d7355d81 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -170,7 +170,7 @@ class _VideoReplyNewDialogState extends State topLeft: Radius.circular(12), topRight: Radius.circular(12), ), - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 3fe84c71..6dda9512 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -78,7 +78,7 @@ class _VideoReplyReplyPanelState extends State { Widget build(BuildContext context) { return Container( height: widget.source == 'videoDetail' ? widget.sheetHeight : null, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ if (widget.source == 'videoDetail') diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index 882a9a8b..197d6124 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -23,7 +23,7 @@ class AiDetail extends StatelessWidget { Widget build(BuildContext context) { sheetHeight = localCache.get('sheetHeight'); return Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(left: 14, right: 14), height: sheetHeight, child: Column( diff --git a/lib/pages/video/detail/widgets/app_bar.dart b/lib/pages/video/detail/widgets/app_bar.dart index efc0b593..b16623ad 100644 --- a/lib/pages/video/detail/widgets/app_bar.dart +++ b/lib/pages/video/detail/widgets/app_bar.dart @@ -29,7 +29,7 @@ class ScrollAppBar extends StatelessWidget { opacity: scrollDistance / (videoHeight - kToolbarHeight), child: Container( height: statusBarHeight + kToolbarHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: EdgeInsets.only(top: statusBarHeight), child: AppBar( primary: false, diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 544d93e0..3ff74b44 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -93,7 +93,7 @@ class _HeaderControlState extends State { height: 460, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -317,7 +317,7 @@ class _HeaderControlState extends State { height: 500, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -377,7 +377,7 @@ class _HeaderControlState extends State { inactiveThumbColor: Theme.of(context).colorScheme.primaryContainer, inactiveTrackColor: - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, splashRadius: 10.0, // boolean variable value value: shutdownTimerService.waitForPlayingCompleted, @@ -570,7 +570,7 @@ class _HeaderControlState extends State { height: 310, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -660,7 +660,7 @@ class _HeaderControlState extends State { height: 250, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -734,7 +734,7 @@ class _HeaderControlState extends State { height: 250, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -828,7 +828,7 @@ class _HeaderControlState extends State { height: 580, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -1084,7 +1084,7 @@ class _HeaderControlState extends State { height: 250, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0cd6915c..0b77b7cc 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -12,7 +12,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { builder: (context) => Container( margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(10.0), ), child: Column( From 7fc4f3fd4145acacdf116a82ff04ce8aa68002f3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 6 Jun 2024 00:28:46 +0800 Subject: [PATCH 06/34] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5app=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E5=85=B3=E6=B3=A8=E7=8A=B6=E6=80=81=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/home/rcmd/result.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 78747d1a..0098fe95 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -69,9 +69,10 @@ class RecVideoItemAppModel { : null; // 由于app端api并不会直接返回与owner的关注状态 // 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效 + RegExp regex = RegExp(r'已关注|新关注'); isFollowed = rcmdReason != null && rcmdReason!.content != null && - rcmdReason!.content!.contains('关注') + regex.hasMatch(rcmdReason!.content!) ? 1 : 0; // 如果是,就无需再显示推荐原因,交由view统一处理即可 From f8897f74bfae9b2d86d8d30625f3816ab9797462 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 7 Jun 2024 23:48:48 +0800 Subject: [PATCH 07/34] =?UTF-8?q?feat:=20web=E7=AB=AF=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 +- lib/http/login.dart | 99 ++++++++++++++++++++++--------- lib/pages/login/controller.dart | 80 ++++++++++++++++++++++--- lib/pages/login/view.dart | 37 +++++++----- lib/pages/mine/controller.dart | 5 +- lib/pages/webview/controller.dart | 52 +--------------- lib/pages/webview/view.dart | 4 +- lib/utils/login.dart | 58 ++++++++++++++++++ 8 files changed, 231 insertions(+), 108 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 42fc03b8..8a72479b 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -400,10 +400,12 @@ class Api { '${HttpString.passBaseUrl}/x/passport-login/captcha?source=main_web'; // web端短信验证码 - static const String smsCode = + static const String webSmsCode = '${HttpString.passBaseUrl}/x/passport-login/web/sms/send'; // web端验证码登录 + static const String webSmsLogin = + "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; // web端密码登录 diff --git a/lib/http/login.dart b/lib/http/login.dart index ff3fee23..13236623 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:crypto/crypto.dart'; import 'package:dio/dio.dart'; import 'package:encrypt/encrypt.dart'; +import 'package:pilipala/http/constants.dart'; import 'package:uuid/uuid.dart'; import '../models/login/index.dart'; import '../utils/login.dart'; @@ -21,32 +22,32 @@ class LoginHttp { } } - static Future sendSmsCode({ - int? cid, - required int tel, - required String token, - required String challenge, - required String validate, - required String seccode, - }) async { - var res = await Request().post( - Api.appSmsCode, - data: { - 'cid': cid, - 'tel': tel, - "source": "main_web", - 'token': token, - 'challenge': challenge, - 'validate': validate, - 'seccode': seccode, - }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - // headers: {'user-agent': ApiConstants.userAgent} - ), - ); - print(res); - } + // static Future sendSmsCode({ + // int? cid, + // required int tel, + // required String token, + // required String challenge, + // required String validate, + // required String seccode, + // }) async { + // var res = await Request().post( + // Api.appSmsCode, + // data: { + // 'cid': cid, + // 'tel': tel, + // "source": "main_web", + // 'token': token, + // 'challenge': challenge, + // 'validate': validate, + // 'seccode': seccode, + // }, + // options: Options( + // contentType: Headers.formUrlEncodedContentType, + // // headers: {'user-agent': ApiConstants.userAgent} + // ), + // ); + // print(res); + // } // web端验证码 static Future sendWebSmsCode({ @@ -60,6 +61,7 @@ class LoginHttp { Map data = { 'cid': cid, 'tel': tel, + "source": "main_web", 'token': token, 'challenge': challenge, 'validate': validate, @@ -67,17 +69,56 @@ class LoginHttp { }; FormData formData = FormData.fromMap({...data}); var res = await Request().post( - Api.smsCode, + Api.webSmsCode, data: formData, options: Options( contentType: Headers.formUrlEncodedContentType, ), ); - print(res); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } } // web端验证码登录 - static Future loginInByWebSmsCode() async {} + static Future loginInByWebSmsCode({ + int? cid, + required int tel, + required int code, + required String captchaKey, + }) async { + // webSmsLogin + Map data = { + "cid": cid, + "tel": tel, + "code": code, + "source": "main_mini", + "keep": 0, + "captcha_key": captchaKey, + "go_url": HttpString.baseUrl + }; + FormData formData = FormData.fromMap({...data}); + var res = await Request().post( + Api.webSmsLogin, + data: formData, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } // web端密码登录 static Future liginInByWebPwd() async {} diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index c002fdf9..e47653e2 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -1,11 +1,16 @@ +import 'dart:async'; +import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:get/get_rx/get_rx.dart'; import 'package:pilipala/http/login.dart'; import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart'; import 'package:pilipala/models/login/index.dart'; +import 'package:pilipala/pages/webview/index.dart'; +import 'package:pilipala/utils/login.dart'; class LoginPageController extends GetxController { final GlobalKey mobFormKey = GlobalKey(); @@ -26,9 +31,19 @@ class LoginPageController extends GetxController { final Gt3FlutterPlugin captcha = Gt3FlutterPlugin(); + // 倒计时60s + RxInt seconds = 60.obs; + late Timer timer; + RxBool smsCodeSendStatus = false.obs; + // 默认密码登录 RxInt loginType = 0.obs; + late String captchaLey; + + late int tel; + late int webSmsCode; + // 监听pageView切换 void onPageChange(int index) { currentIndex.value = index; @@ -43,6 +58,7 @@ class LoginPageController extends GetxController { curve: Curves.easeInOut, ); passwordTextFieldNode.requestFocus(); + (mobFormKey.currentState as FormState).save(); } } @@ -86,18 +102,32 @@ class LoginPageController extends GetxController { } } - // 验证码登录 - void loginInByCode() { - if ((msgCodeFormKey.currentState as FormState).validate()) {} + // web端验证码登录 + void loginInByCode() async { + if ((msgCodeFormKey.currentState as FormState).validate()) { + (msgCodeFormKey.currentState as FormState).save(); + var res = await LoginHttp.loginInByWebSmsCode( + cid: 86, + tel: tel, + code: webSmsCode, + captchaKey: captchaLey, + ); + if (res['status']) { + log(res.toString()); + LoginUtils.confirmLogin('', null); + } else { + SmartDialog.showToast(res['msg']); + } + } } - // app端验证码 - void getMsgCode() async { + // 获取app端验证码 + void getAppMsgCode() async { getCaptcha((data) async { CaptchaDataModel captchaData = data; var res = await LoginHttp.sendAppSmsCode( cid: 86, - tel: 13734077064, + tel: tel, token: captchaData.token!, challenge: captchaData.geetest!.challenge!, validate: captchaData.validate!, @@ -121,7 +151,7 @@ class LoginPageController extends GetxController { captcha.addEventHandler(onShow: (Map message) async { SmartDialog.dismiss(); }, onClose: (Map message) async { - SmartDialog.showToast('关闭验证'); + SmartDialog.showToast('取消验证'); }, onResult: (Map message) async { debugPrint("Captcha result: $message"); String code = message["code"]; @@ -201,4 +231,40 @@ class LoginPageController extends GetxController { captcha.startCaptcha(registerData); } else {} } + + // 获取web端验证码 + void getWebMsgCode() async { + getCaptcha((data) async { + CaptchaDataModel captchaData = data; + var res = await LoginHttp.sendWebSmsCode( + cid: 86, + tel: tel, + token: captchaData.token!, + challenge: captchaData.geetest!.challenge!, + validate: captchaData.validate!, + seccode: captchaData.seccode!, + ); + if (res['status']) { + captchaLey = res['data']['captcha_key']; + SmartDialog.showToast('验证码已发送'); + // 倒计时60s + smsCodeSendStatus.value = true; + startTimer(); + } else { + SmartDialog.showToast(res['msg']); + } + }); + } + + void startTimer() { + timer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (seconds.value > 0) { + seconds.value--; + } else { + seconds.value = 60; + smsCodeSendStatus.value = false; + timer.cancel(); + } + }); + } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 6521e9d9..ae0a60b9 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -93,9 +93,7 @@ class _LoginPageState extends State { validator: (v) { return v!.trim().isNotEmpty ? null : "手机号码不能为空"; }, - onSaved: (val) { - print(val); - }, + onSaved: (val) => _loginPageCtr.tel = int.parse(val!), onEditingComplete: () { _loginPageCtr.nextStep(); }, @@ -308,21 +306,28 @@ class _LoginPageState extends State { ? null : "验证码不能为空"; }, - onSaved: (val) { - print(val); - }, + onSaved: (val) => _loginPageCtr.webSmsCode = + int.parse(val!), ), - Positioned( - right: 8, - top: 4, - child: Center( - child: TextButton( - onPressed: () => - _loginPageCtr.getMsgCode(), - child: const Text('获取验证码'), + Obx(() { + return Positioned( + right: 8, + top: 0, + child: Center( + child: TextButton( + onPressed: _loginPageCtr + .smsCodeSendStatus.value + ? null + : () => + _loginPageCtr.getWebMsgCode(), + child: _loginPageCtr + .smsCodeSendStatus.value + ? Text( + '重新获取(${_loginPageCtr.seconds.value}s)') + : const Text('获取验证码')), ), - ), - ), + ); + }) ], ), ), diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index a61bb820..153a7162 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -6,7 +6,6 @@ import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/theme_type.dart'; import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/models/user/stat.dart'; -import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; class MineController extends GetxController { @@ -34,8 +33,8 @@ class MineController extends GetxController { onLogin() async { if (!userLogin.value) { - RoutePush.loginPush(); - // Get.toNamed('/loginPage'); + // RoutePush.loginPush(); + Get.toNamed('/loginPage'); } else { int mid = userInfo.value.mid!; String face = userInfo.value.face!; diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index bdacc652..e0ff113c 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -76,7 +76,7 @@ class WebviewController extends GetxController { (url.startsWith( 'https://passport.bilibili.com/web/sso/exchange_cookie') || url.startsWith('https://m.bilibili.com/'))) { - confirmLogin(url); + LoginUtils.confirmLogin(url, controller); } }, onWebResourceError: (WebResourceError error) {}, @@ -97,54 +97,4 @@ class WebviewController extends GetxController { ) ..loadRequest(Uri.parse(url)); } - - confirmLogin(url) async { - var content = ''; - if (url != null) { - content = '${content + url}; \n'; - } - try { - await SetCookie.onSet(); - final result = await UserHttp.userInfo(); - if (result['status'] && result['data'].isLogin) { - SmartDialog.showToast('登录成功'); - try { - Box userInfoCache = GStrorage.userInfo; - if (!userInfoCache.isOpen) { - userInfoCache = await Hive.openBox('userInfo'); - } - await userInfoCache.put('userInfoCache', result['data']); - - final HomeController homeCtr = Get.find(); - homeCtr.updateLoginStatus(true); - homeCtr.userFace.value = result['data'].face; - final MediaController mediaCtr = Get.find(); - mediaCtr.mid = result['data'].mid; - await LoginUtils.refreshLoginStatus(true); - } catch (err) { - SmartDialog.show(builder: (BuildContext context) { - return AlertDialog( - title: const Text('登录遇到问题'), - content: Text(err.toString()), - actions: [ - TextButton( - onPressed: () => controller.reload(), - child: const Text('确认'), - ) - ], - ); - }); - } - Get.back(); - } else { - // 获取用户信息失败 - SmartDialog.showToast(result['msg']); - Clipboard.setData(ClipboardData(text: result['msg'])); - } - } catch (e) { - SmartDialog.showNotify(msg: e.toString(), notifyType: NotifyType.warning); - content = content + e.toString(); - Clipboard.setData(ClipboardData(text: content)); - } - } } diff --git a/lib/pages/webview/view.dart b/lib/pages/webview/view.dart index 8edd2189..cba40ad1 100644 --- a/lib/pages/webview/view.dart +++ b/lib/pages/webview/view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/login.dart'; import 'package:url_launcher/url_launcher.dart'; import 'controller.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -43,7 +44,8 @@ class _WebviewPageState extends State { Obx( () => _webviewController.type.value == 'login' ? TextButton( - onPressed: () => _webviewController.confirmLogin(null), + onPressed: () => + LoginUtils.confirmLogin(null, _webviewController), child: const Text('刷新登录状态'), ) : const SizedBox(), diff --git a/lib/utils/login.dart b/lib/utils/login.dart index 59c53027..2687a8c2 100644 --- a/lib/utils/login.dart +++ b/lib/utils/login.dart @@ -2,12 +2,18 @@ import 'dart:convert'; import 'dart:math'; import 'package:crypto/crypto.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/http/user.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/mine/index.dart'; +import 'package:pilipala/utils/cookie.dart'; +import 'package:pilipala/utils/storage.dart'; import 'package:uuid/uuid.dart'; class LoginUtils { @@ -57,4 +63,56 @@ class LoginUtils { String uuid = getUUID() + getUUID(); return 'XY${uuid.substring(0, 35).toUpperCase()}'; } + + static confirmLogin(url, controller) async { + var content = ''; + if (url != null) { + content = '${content + url}; \n'; + } + try { + await SetCookie.onSet(); + final result = await UserHttp.userInfo(); + if (result['status'] && result['data'].isLogin) { + SmartDialog.showToast('登录成功'); + try { + Box userInfoCache = GStrorage.userInfo; + if (!userInfoCache.isOpen) { + userInfoCache = await Hive.openBox('userInfo'); + } + await userInfoCache.put('userInfoCache', result['data']); + + final HomeController homeCtr = Get.find(); + homeCtr.updateLoginStatus(true); + homeCtr.userFace.value = result['data'].face; + final MediaController mediaCtr = Get.find(); + mediaCtr.mid = result['data'].mid; + await LoginUtils.refreshLoginStatus(true); + } catch (err) { + SmartDialog.show(builder: (BuildContext context) { + return AlertDialog( + title: const Text('登录遇到问题'), + content: Text(err.toString()), + actions: [ + TextButton( + onPressed: controller != null + ? () => controller.reload() + : SmartDialog.dismiss, + child: const Text('确认'), + ) + ], + ); + }); + } + Get.back(); + } else { + // 获取用户信息失败 + SmartDialog.showToast(result['msg']); + Clipboard.setData(ClipboardData(text: result['msg'])); + } + } catch (e) { + SmartDialog.showNotify(msg: e.toString(), notifyType: NotifyType.warning); + content = content + e.toString(); + Clipboard.setData(ClipboardData(text: content)); + } + } } From 9888aba62e93b87c75832c543697302930c588e5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 00:26:08 +0800 Subject: [PATCH 08/34] =?UTF-8?q?feat:=20web=E7=AB=AF=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 ++ lib/http/login.dart | 38 ++++++++++++++++++++++++++ lib/pages/login/controller.dart | 47 +++++++++++++++++++++++++++------ lib/pages/login/view.dart | 2 +- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 8a72479b..e0b92c02 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -408,6 +408,8 @@ class Api { "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; // web端密码登录 + static const String loginInByWebPwd = + "${HttpString.passBaseUrl}/x/passport-login/web/login"; // app端短信验证码 static const String appSmsCode = diff --git a/lib/http/login.dart b/lib/http/login.dart index 13236623..a97360c7 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -214,4 +214,42 @@ class LoginHttp { ); print(res); } + + // web端密码登录 + static Future loginInByWebPwd({ + required int username, + required String password, + required String token, + required String challenge, + required String validate, + required String seccode, + }) async { + Map data = { + 'username': username, + 'password': password, + 'keep': 0, + 'token': token, + 'challenge': challenge, + 'validate': validate, + 'seccode': seccode, + 'source': 'main-fe-header', + "go_url": HttpString.baseUrl + }; + FormData formData = FormData.fromMap({...data}); + var res = await Request().post( + Api.loginInByWebPwd, + data: formData, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index e47653e2..354076f9 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -1,15 +1,13 @@ import 'dart:async'; -import 'dart:developer'; import 'dart:io'; +import 'package:encrypt/encrypt.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:get/get_rx/get_rx.dart'; import 'package:pilipala/http/login.dart'; import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart'; import 'package:pilipala/models/login/index.dart'; -import 'package:pilipala/pages/webview/index.dart'; import 'package:pilipala/utils/login.dart'; class LoginPageController extends GetxController { @@ -39,7 +37,7 @@ class LoginPageController extends GetxController { // 默认密码登录 RxInt loginType = 0.obs; - late String captchaLey; + late String captchaKey; late int tel; late int webSmsCode; @@ -102,6 +100,39 @@ class LoginPageController extends GetxController { } } + // web端密码登录 + void loginInByWebPassword() async { + if ((passwordFormKey.currentState as FormState).validate()) { + getCaptcha((data) async { + CaptchaDataModel captchaData = data; + var webKeyRes = await LoginHttp.getWebKey(); + if (webKeyRes['status']) { + String rhash = webKeyRes['data']['hash']; + String key = webKeyRes['data']['key']; + dynamic publicKey = RSAKeyParser().parse(key); + String passwordEncryptyed = Encrypter(RSA(publicKey: publicKey)) + .encrypt(rhash + passwordTextController.text) + .base64; + var res = await LoginHttp.loginInByWebPwd( + username: tel, + password: passwordEncryptyed, + token: captchaData.token!, + challenge: captchaData.geetest!.challenge!, + validate: captchaData.validate!, + seccode: captchaData.seccode!, + ); + if (res['status']) { + await LoginUtils.confirmLogin('', null); + } else { + SmartDialog.showToast(res['msg']); + } + } else { + SmartDialog.showToast(webKeyRes['msg']); + } + }); + } + } + // web端验证码登录 void loginInByCode() async { if ((msgCodeFormKey.currentState as FormState).validate()) { @@ -110,11 +141,10 @@ class LoginPageController extends GetxController { cid: 86, tel: tel, code: webSmsCode, - captchaKey: captchaLey, + captchaKey: captchaKey, ); if (res['status']) { - log(res.toString()); - LoginUtils.confirmLogin('', null); + await LoginUtils.confirmLogin('', null); } else { SmartDialog.showToast(res['msg']); } @@ -245,7 +275,7 @@ class LoginPageController extends GetxController { seccode: captchaData.seccode!, ); if (res['status']) { - captchaLey = res['data']['captcha_key']; + captchaKey = res['data']['captcha_key']; SmartDialog.showToast('验证码已发送'); // 倒计时60s smsCodeSendStatus.value = true; @@ -256,6 +286,7 @@ class LoginPageController extends GetxController { }); } + // 验证码倒计时 void startTimer() { timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (seconds.value > 0) { diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index ae0a60b9..2f406706 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -234,7 +234,7 @@ class _LoginPageState extends State { .primary, // 设置按钮背景色 ), onPressed: () => - _loginPageCtr.loginInByAppPassword(), + _loginPageCtr.loginInByWebPassword(), child: const Text('确认登录'), ) ], From 4fe8366c2fe6013f8f9f941ebc6b33437709975e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 00:42:45 +0800 Subject: [PATCH 09/34] upgrade: gt3 plugin --- pubspec.lock | 20 ++++++++++---------- pubspec.yaml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index fdcd517c..406fbca4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -674,10 +674,10 @@ packages: dependency: "direct main" description: name: gt3_flutter_plugin - sha256: f12bff2bfbcf27467833f8d564dcc24ee2f1b3254a7c7cf5eb2c4590baf11cc1 + sha256: "08f35692e937770ad6b3e2017eb8ef81839a82b8a63f5acf3abab14b688fc36c" url: "https://pub.flutter-io.cn" source: hosted - version: "0.0.8" + version: "0.1.0" hive: dependency: "direct main" description: @@ -981,10 +981,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "1.0.5" nm: dependency: transitive description: @@ -1054,10 +1054,10 @@ packages: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.3" path_provider_android: dependency: transitive description: @@ -1070,10 +1070,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -1238,10 +1238,10 @@ packages: dependency: "direct main" description: name: saver_gallery - sha256: "2657953427ebe5a3b2d08157d41587c01923ccce3f1a616d55082be7470f8530" + sha256: "0f740608072053a0da3b19cc5812a87e36f5c3c0b959d2475c4eb3d697f4a782" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.1" + version: "3.0.3" screen_brightness: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index f758d308..f02c2ead 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -132,7 +132,7 @@ dependencies: # html渲染 flutter_html: ^3.0.0-beta.2 # 极验 - gt3_flutter_plugin: ^0.0.8 + gt3_flutter_plugin: ^0.1.0 uuid: ^3.0.7 scrollable_positioned_list: ^0.3.8 catcher_2: ^1.2.6 From 3a94281310ecf86ccdd0a5d21853b9ed38dceff6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 03:14:37 +0800 Subject: [PATCH 10/34] =?UTF-8?q?feat:=20web=E7=AB=AF=E6=89=AB=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 12 +++- lib/http/login.dart | 27 +++++++ lib/pages/login/controller.dart | 35 +++++++++ lib/pages/login/view.dart | 121 ++++++++++++++++++++++++++------ pubspec.lock | 16 +++++ pubspec.yaml | 2 + 6 files changed, 190 insertions(+), 23 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index e0b92c02..b0fc0556 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -405,11 +405,19 @@ class Api { // web端验证码登录 static const String webSmsLogin = - "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; + '${HttpString.passBaseUrl}/x/passport-login/web/login/sms'; // web端密码登录 static const String loginInByWebPwd = - "${HttpString.passBaseUrl}/x/passport-login/web/login"; + '${HttpString.passBaseUrl}/x/passport-login/web/login'; + + // web端二维码 + static const String qrCodeApi = + '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/generate'; + + // 扫码登录 + static const String loginInByQrcode = + '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/poll'; // app端短信验证码 static const String appSmsCode = diff --git a/lib/http/login.dart b/lib/http/login.dart index a97360c7..2437b72a 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -252,4 +252,31 @@ class LoginHttp { return {'status': false, 'data': [], 'msg': res.data['message']}; } } + + // web端登录二维码 + static Future getWebQrcode() async { + var res = await Request().get(Api.qrCodeApi); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } + + // web端二维码轮询登录状态 + static Future queryWebQrcodeStatus(String qrcodeKey) async { + var res = await Request() + .get(Api.loginInByQrcode, data: {'qrcode_key': qrcodeKey}); + if (res.data['data']['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index 354076f9..b5ddba16 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -42,6 +42,10 @@ class LoginPageController extends GetxController { late int tel; late int webSmsCode; + RxInt validSeconds = 180.obs; + late Timer validTimer; + late String qrcodeKey; + // 监听pageView切换 void onPageChange(int index) { currentIndex.value = index; @@ -298,4 +302,35 @@ class LoginPageController extends GetxController { } }); } + + // 获取登录二维码 + Future getWebQrcode() async { + var res = await LoginHttp.getWebQrcode(); + validSeconds.value = 180; + if (res['status']) { + qrcodeKey = res['data']['qrcode_key']; + validTimer = Timer.periodic(const Duration(seconds: 1), (validTimer) { + if (validSeconds.value > 0) { + validSeconds.value--; + queryWebQrcodeStatus(); + } else { + getWebQrcode(); + validTimer.cancel(); + } + }); + return res; + } else { + SmartDialog.showToast(res['msg']); + } + } + + // 轮询二维码登录状态 + Future queryWebQrcodeStatus() async { + var res = await LoginHttp.queryWebQrcodeStatus(qrcodeKey); + if (res['status']) { + await LoginUtils.confirmLogin('', null); + validTimer.cancel(); + Get.back(); + } + } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 2f406706..cd91ea26 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:qr_flutter/qr_flutter.dart'; import 'controller.dart'; @@ -37,6 +38,105 @@ class _LoginPageState extends State { icon: const Icon(Icons.arrow_back), ), ), + actions: [ + IconButton( + tooltip: '浏览器打开', + onPressed: () { + Get.offNamed( + '/webview', + parameters: { + 'url': 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + }, + icon: const Icon(Icons.language), + ), + IconButton( + tooltip: '二维码登录', + onPressed: () { + showDialog( + context: context, + builder: (context) { + return StatefulBuilder( + builder: (context, StateSetter setState) { + return AlertDialog( + title: Row( + children: [ + const Text('扫码登录'), + IconButton( + onPressed: () { + setState(() {}); + }, + icon: const Icon(Icons.refresh), + ), + ], + ), + contentPadding: const EdgeInsets.fromLTRB(0, 0, 0, 4), + content: AspectRatio( + aspectRatio: 1, + child: Container( + width: 200, + padding: const EdgeInsets.all(12), + child: FutureBuilder( + future: _loginPageCtr.getWebQrcode(), + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + Map data = snapshot.data as Map; + return QrImageView( + data: data['data']['url'], + backgroundColor: Colors.transparent, + ); + } else { + return const Center( + child: SizedBox( + width: 40, + height: 40, + child: CircularProgressIndicator(), + ), + ); + } + }, + ), + ), + ), + actions: [ + TextButton( + onPressed: () {}, + child: Obx(() { + return Text( + '有效期: ${_loginPageCtr.validSeconds.value}s', + style: Theme.of(context).textTheme.titleMedium, + ); + }), + ), + TextButton( + onPressed: () {}, + child: Text( + '检查登录状态', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .titleMedium! + .fontSize, + ), + ), + ) + ], + ); + }); + }, + ); + }, + icon: const Icon(Icons.qr_code), + ), + const SizedBox(width: 22), + ], ), body: PageView( physics: const NeverScrollableScrollPhysics(), @@ -99,27 +199,6 @@ class _LoginPageState extends State { }, ), ), - GestureDetector( - onTap: () { - Get.offNamed( - '/webview', - parameters: { - 'url': - 'https://passport.bilibili.com/h5-app/passport/login', - 'type': 'login', - 'pageTitle': '登录bilibili', - }, - ); - }, - child: Padding( - padding: const EdgeInsets.only(left: 2), - child: Text( - '使用网页端登录', - style: TextStyle( - color: Theme.of(context).colorScheme.primary), - ), - ), - ), const Spacer(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/pubspec.lock b/pubspec.lock index 406fbca4..5c6f2b43 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1218,6 +1218,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.1.0" + qr: + dependency: transitive + description: + name: qr + sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.0" rxdart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f02c2ead..66187abf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -145,6 +145,8 @@ dependencies: # 投屏 dlna_dart: ^0.0.8 lottie: ^3.1.2 + # 二维码 + qr_flutter: ^4.1.0 dev_dependencies: flutter_test: From aee52b02470f91db723b24a9ce2b9a6dbc573c10 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 04:09:00 +0800 Subject: [PATCH 11/34] =?UTF-8?q?opt:=20=E6=B6=88=E6=81=AF=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=A2=9E=E5=8A=A0=E9=AA=A8=E6=9E=B6=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 270 +++++++++++++++++++----------------- 1 file changed, 142 insertions(+), 128 deletions(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index fa7ad60b..fa95463b 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/skeleton.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; @@ -102,134 +103,83 @@ class _WhisperPageState extends State { }, child: SingleChildScrollView( controller: _scrollController, - child: Column( - children: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - RxList sessionList = _whisperController.sessionList; - return Obx( - () => sessionList.isEmpty - ? const SizedBox() - : ListView.separated( - itemCount: sessionList.length, - shrinkWrap: true, - physics: - const NeverScrollableScrollPhysics(), - itemBuilder: (_, int i) { - return ListTile( - onTap: () { - sessionList[i].unreadCount = 0; - sessionList.refresh(); - Get.toNamed( - '/whisperDetail', - parameters: { - 'talkerId': sessionList[i] - .talkerId - .toString(), - 'name': sessionList[i] - .accountInfo - .name, - 'face': sessionList[i] - .accountInfo - .face, - 'mid': sessionList[i] - .accountInfo - .mid - .toString(), - }, - ); - }, - leading: Badge( - isLabelVisible: - sessionList[i].unreadCount > 0, - label: Text(sessionList[i] - .unreadCount - .toString()), - alignment: Alignment.topRight, - child: NetworkImgLayer( - width: 45, - height: 45, - type: 'avatar', - src: sessionList[i] - .accountInfo - .face, - ), - ), - title: Text( - sessionList[i].accountInfo.name), - subtitle: Text( - sessionList[i].lastMsg.content != - null && - sessionList[i] - .lastMsg - .content != - '' - ? (sessionList[i] - .lastMsg - .content['text'] ?? - sessionList[i] - .lastMsg - .content['content'] ?? - sessionList[i] - .lastMsg - .content['title'] ?? - sessionList[i] - .lastMsg - .content[ - 'reply_content'] ?? - '不支持的消息类型') - : '不支持的消息类型', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .labelMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline)), - trailing: Text( - Utils.dateFormat(sessionList[i] - .lastMsg - .timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline), - ), - ); - }, - separatorBuilder: - (BuildContext context, int index) { - return Divider( - indent: 72, - endIndent: 20, - height: 6, - color: Colors.grey.withOpacity(0.1), - ); - }, - ), - ); - } else { - // 请求错误 - return Center( - child: Text(data?['msg'] ?? '请求异常'), - ); - } - } else { - // 骨架屏 - return const SizedBox(); - } - }, - ) - ], + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + RxList sessionList = _whisperController.sessionList; + return Obx( + () => sessionList.isEmpty + ? const SizedBox() + : ListView.separated( + itemCount: sessionList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return SessionItem( + sessionItem: sessionList[i], + changeFucCall: () => + sessionList.refresh(), + ); + }, + separatorBuilder: + (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return Center( + child: Text(data?['msg'] ?? '请求异常'), + ); + } + } else { + // 骨架屏 + return ListView.builder( + itemCount: 15, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, int i) { + return Skeleton( + child: ListTile( + leading: Container( + width: 45, + height: 45, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .onInverseSurface, + borderRadius: BorderRadius.circular(25), + ), + ), + title: Container( + width: 100, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + subtitle: Container( + width: 80, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + ), + ); + }, + ); + } + }, ), ), ), @@ -239,3 +189,67 @@ class _WhisperPageState extends State { ); } } + +class SessionItem extends StatelessWidget { + final dynamic sessionItem; + final Function changeFucCall; + + const SessionItem({ + super.key, + required this.sessionItem, + required this.changeFucCall, + }); + + @override + Widget build(BuildContext context) { + final content = sessionItem.lastMsg.content; + return ListTile( + onTap: () { + sessionItem.unreadCount = 0; + changeFucCall.call(); + Get.toNamed( + '/whisperDetail', + parameters: { + 'talkerId': sessionItem.talkerId.toString(), + 'name': sessionItem.accountInfo.name, + 'face': sessionItem.accountInfo.face, + 'mid': sessionItem.accountInfo.mid.toString(), + }, + ); + }, + leading: Badge( + isLabelVisible: sessionItem.unreadCount > 0, + label: Text(sessionItem.unreadCount.toString()), + alignment: Alignment.topRight, + child: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: sessionItem.accountInfo.face, + ), + ), + title: Text(sessionItem.accountInfo.name), + subtitle: Text( + content != null && content != '' + ? (content['text'] ?? + content['content'] ?? + content['title'] ?? + content['reply_content'] ?? + '不支持的消息类型') + : '不支持的消息类型', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline)), + trailing: Text( + Utils.dateFormat(sessionItem.lastMsg.timestamp), + style: Theme.of(context) + .textTheme + .labelSmall! + .copyWith(color: Theme.of(context).colorScheme.outline), + ), + ); + } +} From f4b7f77e440cbd8d51558f74312a4e4439fc1f8c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 13:29:36 +0800 Subject: [PATCH 12/34] upgrade: new version syntax --- lib/common/widgets/http_error.dart | 2 +- lib/common/widgets/video_card_h.dart | 2 +- lib/pages/bangumi/introduction/view.dart | 8 ++++---- lib/pages/bangumi/widgets/bangumi_panel.dart | 2 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- lib/pages/fav_detail/widget/fav_video_card.dart | 2 +- lib/pages/history/widgets/item.dart | 5 ++--- lib/pages/home/view.dart | 8 ++++---- lib/pages/live_room/widgets/bottom_control.dart | 2 +- lib/pages/login/view.dart | 6 ++---- lib/pages/media/view.dart | 4 ++-- lib/pages/member/view.dart | 5 ++--- lib/pages/search_panel/widgets/video_panel.dart | 2 +- lib/pages/setting/extra_setting.dart | 6 +++--- lib/pages/setting/style_setting.dart | 6 +++--- lib/pages/setting/widgets/switch_item.dart | 6 +++--- .../video/detail/introduction/widgets/page_panel.dart | 2 +- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- lib/pages/video/detail/reply_new/toolbar_icon_button.dart | 4 ++-- lib/pages/video/detail/reply_new/view.dart | 2 +- lib/pages/video/detail/view.dart | 2 +- lib/pages/video/detail/widgets/ai_detail.dart | 2 +- lib/pages/video/detail/widgets/header_control.dart | 8 ++++---- lib/pages/whisper_detail/view.dart | 6 +++--- lib/plugin/pl_player/view.dart | 6 +++--- lib/plugin/pl_player/widgets/common_btn.dart | 2 +- lib/plugin/pl_player/widgets/play_pause_btn.dart | 2 +- lib/utils/image_save.dart | 2 +- 28 files changed, 52 insertions(+), 56 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index cbc6659b..b130faae 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -34,7 +34,7 @@ class HttpError extends StatelessWidget { fn!(); }, style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith((states) { + backgroundColor: WidgetStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.primary.withAlpha(20); }), ), diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 1265477f..c674b223 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -305,7 +305,7 @@ class VideoContent extends StatelessWidget { if (source == 'later') ...[ IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => onPressedFn?.call(), icon: Icon( diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 95d4d898..bb85be1a 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -232,11 +232,11 @@ class _BangumiInfoState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.zero), + padding: + WidgetStateProperty.all(EdgeInsets.zero), backgroundColor: - MaterialStateProperty.resolveWith( - (Set states) { + WidgetStateProperty.resolveWith( + (Set states) { return t.colorScheme.primaryContainer .withOpacity(0.7); }), diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 3df7ce25..345a47b6 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -139,7 +139,7 @@ class _BangumiPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.bangumiIntroController?.bottomSheetController = diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 8acdc26a..e66e2a91 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -82,7 +82,7 @@ class AuthorPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { showModalBottomSheet( diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 79e5c073..72d7b4a0 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -217,7 +217,7 @@ class VideoContent extends StatelessWidget { bottom: -4, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { showDialog( diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index baebfedb..8e71df6f 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -217,11 +217,10 @@ class HistoryItem extends StatelessWidget { curve: Curves.easeInOut, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( EdgeInsets.zero), backgroundColor: - MaterialStateProperty - .resolveWith( + WidgetStateProperty.resolveWith( (states) { return Colors.white .withOpacity(0.8); diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index a25389bd..0c45a262 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -278,8 +278,8 @@ class DefaultUser extends StatelessWidget { height: 38, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith((states) { + padding: WidgetStateProperty.all(EdgeInsets.zero), + backgroundColor: WidgetStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.onInverseSurface; }), ), @@ -371,8 +371,8 @@ class CustomChip extends StatelessWidget { ), backgroundColor: secondaryContainer, selectedColor: secondaryContainer, - color: MaterialStateProperty.resolveWith( - (Set states) => secondaryContainer.withAlpha(200)), + color: WidgetStateProperty.resolveWith( + (Set states) => secondaryContainer.withAlpha(200)), padding: const EdgeInsets.fromLTRB(7, 1, 7, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 4dd7c538..6abb1260 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -124,7 +124,7 @@ class _BottomControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index cd91ea26..ecc53af2 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -253,8 +253,7 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - MaterialStateProperty.resolveWith( - (states) { + WidgetStateProperty.resolveWith((states) { return Theme.of(context) .colorScheme .primary @@ -344,8 +343,7 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - MaterialStateProperty.resolveWith( - (states) { + WidgetStateProperty.resolveWith((states) { return Theme.of(context) .colorScheme .primary diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index cc413e59..f6a033e5 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -178,10 +178,10 @@ class _MediaPageState extends State child: Center( child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( EdgeInsets.zero), backgroundColor: - MaterialStateProperty.resolveWith( + WidgetStateProperty.resolveWith( (states) { return Theme.of(context) .colorScheme diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index b6648647..bb0d92be 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -90,9 +90,8 @@ class _MemberPageState extends State () => Text( _memberController.memberInfo.value.name ?? '', style: TextStyle( - color: Theme.of(context) - .colorScheme - .onBackground, + color: + Theme.of(context).colorScheme.onSurface, fontSize: 14), ), ), diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index c24a007c..3207a8ea 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -92,7 +92,7 @@ class SearchVideoPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => controller.onShowFilterDialog(ctr), icon: Icon( diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index aaaa8b84..885a831c 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -240,10 +240,10 @@ class _ExtraSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: MaterialStateProperty.resolveWith( - (Set states) { + thumbIcon: WidgetStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == MaterialState.selected) { + states.first == WidgetState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 364eabf0..e07bef66 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -69,10 +69,10 @@ class _StyleSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: MaterialStateProperty.resolveWith( - (Set states) { + thumbIcon: WidgetStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == MaterialState.selected) { + states.first == WidgetState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart index d0c2bbf2..36c4433e 100644 --- a/lib/pages/setting/widgets/switch_item.dart +++ b/lib/pages/setting/widgets/switch_item.dart @@ -70,9 +70,9 @@ class _SetSwitchItemState extends State { alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大 scale: 0.8, child: Switch( - thumbIcon: MaterialStateProperty.resolveWith( - (Set states) { - if (states.isNotEmpty && states.first == MaterialState.selected) { + thumbIcon: + WidgetStateProperty.resolveWith((Set states) { + if (states.isNotEmpty && states.first == WidgetState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index 266f5566..c8111847 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -106,7 +106,7 @@ class _PagesPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.videoIntroCtr.bottomSheetController = diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 55c16f7d..08e4d405 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -97,7 +97,7 @@ class ReplyItem extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), - color: colorScheme.background, + color: colorScheme.surface, ), child: Icon( Icons.offline_bolt, diff --git a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart index c4390796..bf810ce2 100644 --- a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart +++ b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart @@ -27,8 +27,8 @@ class ToolbarIconButton extends StatelessWidget { ? Theme.of(context).colorScheme.onSecondaryContainer : Theme.of(context).colorScheme.outline, style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith((states) { + padding: WidgetStateProperty.all(EdgeInsets.zero), + backgroundColor: WidgetStateProperty.resolveWith((states) { return selected ? Theme.of(context).colorScheme.secondaryContainer : null; diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index d7355d81..a136f623 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -259,7 +259,7 @@ class _VideoReplyNewDialogState extends State size: 22), label: const Text('转发到动态'), style: ButtonStyle( - foregroundColor: MaterialStateProperty.all( + foregroundColor: WidgetStateProperty.all( isForward.value ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index fa531684..a0f5d6bc 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -412,7 +412,7 @@ class _VideoDetailPageState extends State height: 32, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => vdCtr.showShootDanmakuSheet(), child: diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index 197d6124..37d51106 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -108,7 +108,7 @@ class AiDetail extends StatelessWidget { fontSize: 13, color: Theme.of(context) .colorScheme - .onBackground, + .onSurface, height: 1.5, ), children: [ diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 3ff74b44..d839a1eb 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1248,7 +1248,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => showShootDanmakuSheet(), child: const Text( @@ -1263,7 +1263,7 @@ class _HeaderControlState extends State { child: Obx( () => IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { _.isOpenDanmu.value = !_.isOpenDanmu.value; @@ -1286,7 +1286,7 @@ class _HeaderControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; @@ -1330,7 +1330,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => showSetSpeedSheet(), child: Text( diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 1701be33..042afca1 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -95,9 +95,9 @@ class _WhisperDetailPageState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) { + padding: WidgetStateProperty.all(EdgeInsets.zero), + backgroundColor: WidgetStateProperty.resolveWith( + (Set states) { return Theme.of(context) .colorScheme .primaryContainer diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 34140be8..0e405884 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -279,7 +279,7 @@ class _PLVideoPlayerState extends State widget.showEposideCb?.call(); }, style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), child: const Text( '选集', @@ -294,7 +294,7 @@ class _PLVideoPlayerState extends State child: TextButton( onPressed: () => _.toggleVideoFit(), style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), child: Obx( () => Text( @@ -311,7 +311,7 @@ class _PLVideoPlayerState extends State height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () {}, child: Obx( diff --git a/lib/plugin/pl_player/widgets/common_btn.dart b/lib/plugin/pl_player/widgets/common_btn.dart index 5f33311c..bf9467a8 100644 --- a/lib/plugin/pl_player/widgets/common_btn.dart +++ b/lib/plugin/pl_player/widgets/common_btn.dart @@ -17,7 +17,7 @@ class ComBtn extends StatelessWidget { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { fuc!(); diff --git a/lib/plugin/pl_player/widgets/play_pause_btn.dart b/lib/plugin/pl_player/widgets/play_pause_btn.dart index 6cbe31f0..7547a1cb 100644 --- a/lib/plugin/pl_player/widgets/play_pause_btn.dart +++ b/lib/plugin/pl_player/widgets/play_pause_btn.dart @@ -68,7 +68,7 @@ class PlayOrPauseButtonState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: player.playOrPause, color: Colors.white, diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0b77b7cc..0727ad68 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -39,7 +39,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { const BorderRadius.all(Radius.circular(20))), child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => closeFn!(), icon: const Icon( From 897551bf237c65ec364507aa1c3d1cff865c6851 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 15:35:45 +0800 Subject: [PATCH 13/34] =?UTF-8?q?fix:=20=E7=A7=81=E4=BF=A1=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E7=B1=BB=E5=9E=8B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whisper_detail/widget/chat_item.dart | 203 +++++++++--------- 1 file changed, 101 insertions(+), 102 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index ad11e4c3..0d37e8b3 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -259,115 +259,114 @@ class ChatItem extends StatelessWidget { ); case MsgType.auto_reply_push: return Container( - constraints: const BoxConstraints( - maxWidth: 300.0, // 设置最大宽度为200.0 + constraints: const BoxConstraints( + maxWidth: 300.0, // 设置最大宽度为200.0 + ), + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .secondaryContainer + .withOpacity(0.4), + borderRadius: const BorderRadius.all( + Radius.circular(16), ), - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .secondaryContainer - .withOpacity(0.4), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - bottomLeft: Radius.circular(6), - bottomRight: Radius.circular(16), - ), - ), - margin: const EdgeInsets.all(12), - padding: const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - content['main_title'], - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), - fontWeight: FontWeight.bold, - ), + ), + margin: const EdgeInsets.all(12), + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + content['main_title'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, ), - for (var i in content['sub_cards']) ...[ - const SizedBox(height: 6), - GestureDetector( - onTap: () async { - RegExp bvRegex = RegExp(r'BV[0-9A-Za-z]{10}', - caseSensitive: false); - Iterable matches = - bvRegex.allMatches(i['jump_url']); - if (matches.isNotEmpty) { - Match match = matches.first; - String bvid = match.group(0)!; - try { - SmartDialog.showLoading(); - final int cid = await SearchHttp.ab2c(bvid: bvid); - final String heroTag = Utils.makeHeroTag(bvid); - SmartDialog.dismiss().then( - (e) => Get.toNamed( - '/video?bvid=$bvid&cid=$cid', - arguments: { - 'pic': i['cover_url'], - 'heroTag': heroTag, - }), - ); - } catch (err) { - SmartDialog.dismiss(); - SmartDialog.showToast(err.toString()); - } - } else { - SmartDialog.showToast('未匹配到 BV 号'); - Get.toNamed('/webview', - arguments: {'url': i['jump_url']}); - } - }, - child: Row( + ), + for (var i in content['sub_cards']) ...[ + const SizedBox(height: 6), + GestureDetector( + onTap: () async { + RegExp bvRegex = + RegExp(r'BV[0-9A-Za-z]{10}', caseSensitive: false); + Iterable matches = + bvRegex.allMatches(i['jump_url']); + if (matches.isNotEmpty) { + Match match = matches.first; + String bvid = match.group(0)!; + try { + SmartDialog.showLoading(); + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + SmartDialog.dismiss().then( + (e) => Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': i['cover_url'], + 'heroTag': heroTag, + }), + ); + } catch (err) { + SmartDialog.dismiss(); + SmartDialog.showToast(err.toString()); + } + } else { + SmartDialog.showToast('未匹配到 BV 号'); + Get.toNamed('/webview', + arguments: {'url': i['jump_url']}); + } + }, + child: Row( + children: [ + NetworkImgLayer( + width: 130, + height: 130 * 9 / 16, + src: i['cover_url'], + ), + const SizedBox(width: 6), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - NetworkImgLayer( - width: 130, - height: 130 * 9 / 16, - src: i['cover_url'], + Text( + i['field1'], + maxLines: 2, + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, + ), + ), + Text( + i['field2'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context).withOpacity(0.6), + fontSize: 12, + ), + ), + Text( + i['field3'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context).withOpacity(0.6), + fontSize: 12, + ), ), - const SizedBox(width: 6), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - i['field1'], - maxLines: 2, - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), - fontWeight: FontWeight.bold, - ), - ), - Text( - i['field2'], - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context).withOpacity(0.6), - fontSize: 12, - ), - ), - Text( - Utils.timeFormat(int.parse(i['field3'])), - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context).withOpacity(0.6), - fontSize: 12, - ), - ), - ], - )), ], )), - ], + ], + ), + ), ], - )); + ], + ), + ); default: return Text( content != null && content != '' From a03d159a86b338c6ca6e7cc145cb83b86e603870 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 17:36:55 +0800 Subject: [PATCH 14/34] =?UTF-8?q?mod:=20=E5=86=85=E5=AE=B9=E5=88=86?= =?UTF-8?q?=E5=8C=BA=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/search.dart | 4 ++-- lib/pages/search/widgets/search_text.dart | 10 ++++++---- lib/pages/search_panel/controller.dart | 6 +++--- lib/pages/search_panel/widgets/video_panel.dart | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/http/search.dart b/lib/http/search.dart index 70980547..075defc7 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -75,7 +75,7 @@ class SearchHttp { required page, String? order, int? duration, - int? tid, + int? tids, }) async { var reqData = { 'search_type': searchType.type, @@ -85,7 +85,7 @@ class SearchHttp { 'page': page, if (order != null) 'order': order, if (duration != null) 'duration': duration, - if (tid != null) 'tid': tid, + if (tids != null && tids != -1) 'tids': tids, }; var res = await Request().get(Api.searchByType, data: reqData); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index fbd1cfc6..d3ffafea 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -18,10 +18,12 @@ class SearchText extends StatelessWidget { @override Widget build(BuildContext context) { return Material( - color: Theme.of(context) - .colorScheme - .surfaceContainerHighest - .withOpacity(0.5), + color: isSelect + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context) + .colorScheme + .surfaceContainerHighest + .withOpacity(0.5), borderRadius: BorderRadius.circular(6), child: Padding( padding: EdgeInsets.zero, diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 56d83601..35113198 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -16,8 +16,8 @@ class SearchPanelController extends GetxController { RxString order = ''.obs; // 视频时长筛选 仅用于搜索视频 RxInt duration = 0.obs; - // 视频分区筛选 仅用于搜索视频 - RxInt tid = (-1).obs; + // 视频分区筛选 仅用于搜索视频 -1时不传 + RxInt tids = (-1).obs; Future onSearch({type = 'init'}) async { var result = await SearchHttp.searchByType( @@ -26,7 +26,7 @@ class SearchPanelController extends GetxController { page: page.value, order: searchType!.type != 'video' ? null : order.value, duration: searchType!.type != 'video' ? null : duration.value, - tid: searchType!.type != 'video' ? null : tid.value, + tids: searchType!.type != 'video' ? null : tids.value, ); if (result['status']) { if (type == 'onRefresh') { diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index 06ee5d4b..15745bde 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -323,7 +323,7 @@ class VideoPanelController extends GetxController { SearchPanelController ctr = Get.find( tag: 'video${searchPanelCtr.keyword!}'); - ctr.tid.value = i['value']; + ctr.tids.value = i['value']; Get.back(); SmartDialog.showLoading(msg: '获取中'); await ctr.onRefresh(); From b37232931bc0c838b37af1cd2d6620afb0ca293b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 17:45:27 +0800 Subject: [PATCH 15/34] =?UTF-8?q?mod:=20=E9=BB=98=E8=AE=A4=E4=B8=8D?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=94=B5=E6=B1=A0=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 1a2ce989..05c0476c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,7 +17,6 @@ import 'package:pilipala/pages/search/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/router/app_pages.dart'; import 'package:pilipala/pages/main/view.dart'; -import 'package:pilipala/services/disable_battery_opt.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; @@ -66,7 +65,6 @@ void main() async { } PiliSchame.init(); - DisableBatteryOpt(); } class MyApp extends StatelessWidget { From be3bffa33cee1d142d78bb7917c288ef9502ed37 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 17:54:40 +0800 Subject: [PATCH 16/34] =?UTF-8?q?opt:=20=E6=88=91=E7=9A=84=E8=AE=A2?= =?UTF-8?q?=E9=98=85=E9=A1=B5=E9=9D=A2=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/subscription/view.dart | 31 +++++++++++++++--------- lib/pages/subscription/widgets/item.dart | 19 +++++++-------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index bcc03cc3..e1d1820d 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -53,18 +53,25 @@ class _SubPageState extends State { if (snapshot.connectionState == ConnectionState.done) { Map? data = snapshot.data; if (data != null && data['status']) { - return Obx( - () => ListView.builder( - controller: scrollController, - itemCount: _subController.subFolderData.value.list!.length, - itemBuilder: (context, index) { - return SubItem( - subFolderItem: - _subController.subFolderData.value.list![index], - cancelSub: _subController.cancelSub); - }, - ), - ); + if (_subController.subFolderData.value.list!.isNotEmpty) { + return Obx( + () => ListView.builder( + controller: scrollController, + itemCount: _subController.subFolderData.value.list!.length, + itemBuilder: (context, index) { + return SubItem( + subFolderItem: + _subController.subFolderData.value.list![index], + cancelSub: _subController.cancelSub); + }, + ), + ); + } else { + return const CustomScrollView( + physics: NeverScrollableScrollPhysics(), + slivers: [HttpError(errMsg: '', btnText: '没有数据', fn: null)], + ); + } } else { return CustomScrollView( physics: const NeverScrollableScrollPhysics(), diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index 0d424611..6c1cc2b9 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -114,16 +114,15 @@ class VideoContent extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - SizedBox( - height: 35, - width: 35, - child: IconButton( - onPressed: () => cancelSub?.call(subFolderItem), - style: TextButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.outline, - padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - ), - icon: const Icon(Icons.delete_outline, size: 18), + IconButton( + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => cancelSub?.call(subFolderItem), + icon: Icon( + Icons.clear_outlined, + color: Theme.of(context).colorScheme.outline, + size: 18, ), ) ], From 006ba8bc59f1898f77d7c95c4a8013a861455514 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 9 Jun 2024 14:51:59 +0800 Subject: [PATCH 17/34] =?UTF-8?q?feat:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 6 +- lib/models/common/action_type.dart | 1 - lib/pages/setting/pages/action_menu_set.dart | 10 +- lib/pages/setting/style_setting.dart | 10 +- .../video/detail/introduction/controller.dart | 55 ++-- lib/pages/video/detail/introduction/view.dart | 234 ++++++++++++++---- .../introduction/widgets/action_item.dart | 39 +-- lib/utils/global_data.dart | 3 +- 8 files changed, 246 insertions(+), 112 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 04fe6670..a400600f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -23,7 +23,7 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - gt3_flutter_plugin (0.0.8): + - gt3_flutter_plugin (0.0.9): - Flutter - GT3Captcha-iOS - GT3Captcha-iOS (0.15.8.3) @@ -171,13 +171,13 @@ SPEC CHECKSUMS: flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529 fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - gt3_flutter_plugin: bfa1f26e9a09dc00401514be5ed437f964cabf23 + gt3_flutter_plugin: 5bd2c08d3c19cbb6ee3b08f4358439e54c8ab2ee GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 diff --git a/lib/models/common/action_type.dart b/lib/models/common/action_type.dart index 2284df6c..b888be17 100644 --- a/lib/models/common/action_type.dart +++ b/lib/models/common/action_type.dart @@ -11,7 +11,6 @@ enum ActionType { dislike, downloadCover, copyLink, - threeAction, // backgroundPlay, // listenVideo, // downloadVideo, diff --git a/lib/pages/setting/pages/action_menu_set.dart b/lib/pages/setting/pages/action_menu_set.dart index f7d92e17..7a4fd9ba 100644 --- a/lib/pages/setting/pages/action_menu_set.dart +++ b/lib/pages/setting/pages/action_menu_set.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/action_type.dart'; +import 'package:pilipala/utils/global_data.dart'; import '../../../utils/storage.dart'; class ActionMenuSetPage extends StatefulWidget { @@ -12,14 +13,14 @@ class ActionMenuSetPage extends StatefulWidget { } class _ActionMenuSetPageState extends State { - Box settingStorage = GStrorage.setting; + Box setting = GStrorage.setting; late List actionTypeSort; late List allLabels; @override void initState() { super.initState(); - actionTypeSort = settingStorage.get(SettingBoxKey.actionTypeSort, + actionTypeSort = setting.get(SettingBoxKey.actionTypeSort, defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); allLabels = actionMenuConfig; allLabels.sort((a, b) { @@ -36,8 +37,9 @@ class _ActionMenuSetPageState extends State { .where((i) => actionTypeSort.contains((i['value'] as ActionType).value)) .map((i) => (i['value'] as ActionType).value) .toList(); - settingStorage.put(SettingBoxKey.actionTypeSort, sortedTabbar); - SmartDialog.showToast('保存成功,下次启动时生效'); + setting.put(SettingBoxKey.actionTypeSort, sortedTabbar); + GlobalData().actionTypeSort = sortedTabbar; + SmartDialog.showToast('操作成功'); } void onReorder(int oldIndex, int newIndex) { diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 20fdada0..5fca0c86 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -289,11 +289,11 @@ class _StyleSettingState extends State { onTap: () => Get.toNamed('/navbarSetting'), title: Text('底部导航栏设置', style: titleStyle), ), - ListTile( - dense: false, - onTap: () => Get.toNamed('/actionMenuSet'), - title: Text('操作菜单设置', style: titleStyle), - ), + // ListTile( + // dense: false, + // onTap: () => Get.toNamed('/actionMenuSet'), + // title: Text('操作菜单设置', style: titleStyle), + // ), if (Platform.isAndroid) ListTile( dense: false, diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 9ed04870..9c542f21 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -38,6 +38,8 @@ class VideoIntroController extends GetxController { RxBool hasCoin = false.obs; // 是否收藏 RxBool hasFav = false.obs; + // 是否不喜欢 + RxBool hasDisLike = false.obs; Box userInfoCache = GStrorage.userInfo; bool userLogin = false; Rx favFolderData = FavFolderData().obs; @@ -153,36 +155,16 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('🙏 UP已经收到了~'); return false; } - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('一键三连 给UP送温暖'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: const Text('点错了')), - TextButton( - onPressed: () async { - var result = await VideoHttp.oneThree(bvid: bvid); - if (result['status']) { - hasLike.value = result["data"]["like"]; - hasCoin.value = result["data"]["coin"]; - hasFav.value = result["data"]["fav"]; - SmartDialog.showToast('三连成功 🎉'); - } else { - SmartDialog.showToast(result['msg']); - } - SmartDialog.dismiss(); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); + var result = await VideoHttp.oneThree(bvid: bvid); + print('🤣🦴:${result["data"]}'); + if (result['status']) { + hasLike.value = result["data"]["like"]; + hasCoin.value = result["data"]["coin"]; + hasFav.value = result["data"]["fav"]; + SmartDialog.showToast('三连成功'); + } else { + SmartDialog.showToast(result['msg']); + } } // (取消)点赞 @@ -193,9 +175,8 @@ class VideoIntroController extends GetxController { } var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value); if (result['status']) { - // hasLike.value = result["data"] == 1 ? true : false; if (!hasLike.value) { - SmartDialog.showToast('点赞成功 👍'); + SmartDialog.showToast('点赞成功'); hasLike.value = true; videoDetail.value.stat!.like = videoDetail.value.stat!.like! + 1; } else if (hasLike.value) { @@ -215,6 +196,10 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('账号未登录'); return; } + if (hasCoin.value) { + SmartDialog.showToast('已投过币了'); + return; + } showDialog( context: Get.context!, builder: (context) { @@ -236,7 +221,7 @@ class VideoIntroController extends GetxController { var res = await VideoHttp.coinVideo( bvid: bvid, multiply: _tempThemeValue); if (res['status']) { - SmartDialog.showToast('投币成功 👏'); + SmartDialog.showToast('投币成功'); hasCoin.value = true; videoDetail.value.stat!.coin = videoDetail.value.stat!.coin! + _tempThemeValue; @@ -269,7 +254,7 @@ class VideoIntroController extends GetxController { if (result['status']) { // 重新获取收藏状态 await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); + SmartDialog.showToast('操作成功'); } else { SmartDialog.showToast(result['msg']); } @@ -299,7 +284,7 @@ class VideoIntroController extends GetxController { Get.back(); // 重新获取收藏状态 await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); + SmartDialog.showToast('操作成功'); } else { SmartDialog.showToast(result['msg']); } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index ceec8fa4..91287c59 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,6 @@ +import 'dart:ffi'; + +import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -15,6 +18,7 @@ import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/introduction/controller.dart'; import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; import '../../../../http/user.dart'; @@ -146,13 +150,18 @@ class _VideoInfoState extends State with TickerProviderStateMixin { RxBool isExpand = false.obs; late ExpandableController _expandableCtr; - void Function()? handleState(Future Function() action) { + // 一键三连动画 + late AnimationController _controller; + late Animation _scaleTransition; + final RxDouble _progress = 0.0.obs; + + void Function()? handleState(Future Function() action) { return isProcessing ? null : () async { - setState(() => isProcessing = true); - await action(); - setState(() => isProcessing = false); + isProcessing = true; + await action.call(); + isProcessing = false; }; } @@ -169,6 +178,25 @@ class _VideoInfoState extends State with TickerProviderStateMixin { follower = Utils.numFormat(videoIntroController.userStat['follower']); enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); _expandableCtr = ExpandableController(initialExpanded: false); + + /// 一键三连动画 + _controller = AnimationController( + duration: const Duration(milliseconds: 1500), + reverseDuration: const Duration(milliseconds: 300), + vsync: this, + ); + _scaleTransition = Tween(begin: 0.5, end: 1.5).animate(_controller) + ..addListener(() async { + _progress.value = _scaleTransition.value - 0.5; + if (_progress.value == 1) { + if (_controller.status == AnimationStatus.completed) { + await videoIntroController.actionOneThree(); + } + _progress.value = 0; + _scaleTransition.removeListener(() {}); + _controller.stop(); + } + }); } // 收藏 @@ -249,6 +277,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override void dispose() { _expandableCtr.dispose(); + _controller.dispose(); + _scaleTransition.removeListener(() {}); super.dispose(); } @@ -538,6 +568,157 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } Widget actionGrid(BuildContext context, videoIntroController) { + final actionTypeSort = GlobalData().actionTypeSort; + + Widget progressWidget(progress) { + return SizedBox( + width: 68, + height: 68, + child: CircularProgressIndicator( + value: progress.value, + strokeWidth: 4, + ), + ); + } + + Map menuListWidgets = { + 'like': Obx( + () { + bool likeStatus = videoIntroController.hasLike.value; + ColorScheme colorScheme = Theme.of(context).colorScheme; + return Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + InkWell( + onTapDown: (details) { + feedBack(); + _controller.forward(); + }, + onTapUp: (TapUpDetails details) { + if (_progress.value == 0) { + feedBack(); + EasyThrottle.throttle( + 'my-throttler', const Duration(milliseconds: 200), () { + videoIntroController.actionLikeVideo(); + }); + } + _controller.reverse(); + }, + borderRadius: StyleString.mdRadius, + child: SizedBox( + width: (Get.size.width - 24) / 5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Icon( + key: ValueKey(likeStatus), + likeStatus + ? Icons.thumb_up + : Icons.thumb_up_alt_outlined, + color: likeStatus + ? colorScheme.primary + : colorScheme.outline, + ), + ), + const SizedBox(height: 6), + Text( + widget.videoDetail!.stat!.like!.toString(), + style: TextStyle( + color: likeStatus ? colorScheme.primary : null, + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize, + ), + ) + ], + ), + ), + ), + ], + ); + }, + ), + 'coin': Obx( + () => Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + ActionItem( + icon: Image.asset('assets/images/coin.png', width: 30), + onTap: handleState(videoIntroController.actionCoinVideo), + selectStatus: videoIntroController.hasCoin.value, + text: widget.videoDetail!.stat!.coin!.toString(), + ), + ], + ), + ), + 'collect': Obx( + () => Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + ActionItem( + icon: const Icon(Icons.star_border), + selectIcon: const Icon(Icons.star), + onTap: () => showFavBottomSheet(), + onLongPress: () => showFavBottomSheet(type: 'longPress'), + selectStatus: videoIntroController.hasFav.value, + text: widget.videoDetail!.stat!.favorite!.toString(), + ), + ], + ), + ), + 'watchLater': ActionItem( + icon: const Icon(Icons.watch_later_outlined), + onTap: () async { + final res = + await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); + SmartDialog.showToast(res['msg']); + }, + selectStatus: false, + text: '稍后看', + ), + 'share': ActionItem( + icon: const Icon(Icons.share), + onTap: () => videoIntroController.actionShareVideo(), + selectStatus: false, + text: '分享', + ), + 'dislike': Obx( + () => ActionItem( + icon: const Icon(Icons.thumb_down_alt_outlined), + selectIcon: const Icon(Icons.thumb_down), + onTap: () {}, + selectStatus: videoIntroController.hasDisLike.value, + text: '不喜欢', + ), + ), + 'downloadCover': ActionItem( + icon: const Icon(Icons.image_outlined), + onTap: () {}, + selectStatus: false, + text: '下载封面', + ), + 'copyLink': ActionItem( + icon: const Icon(Icons.link_outlined), + onTap: () {}, + selectStatus: false, + text: '复制链接', + ), + }; + final List list = []; + for (var i = 0; i < actionTypeSort.length; i++) { + list.add(menuListWidgets[actionTypeSort[i]]!); + } + return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Container( @@ -545,50 +726,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { height: constraints.maxWidth / 5, child: ListView( scrollDirection: Axis.horizontal, - children: [ - Obx( - () => ActionItem( - icon: const Icon(Icons.thumb_up_alt_outlined), - selectIcon: const Icon(Icons.thumb_up), - onTap: handleState(videoIntroController.actionLikeVideo), - selectStatus: videoIntroController.hasLike.value, - text: widget.videoDetail!.stat!.like!.toString()), - ), - Obx( - () => ActionItem( - icon: Image.asset('assets/images/coin.png', width: 30), - onTap: handleState(videoIntroController.actionCoinVideo), - selectStatus: videoIntroController.hasCoin.value, - text: widget.videoDetail!.stat!.coin!.toString(), - ), - ), - Obx( - () => ActionItem( - icon: const Icon(Icons.star_border), - selectIcon: const Icon(Icons.star), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), - selectStatus: videoIntroController.hasFav.value, - text: widget.videoDetail!.stat!.favorite!.toString(), - ), - ), - ActionItem( - icon: const Icon(Icons.watch_later_outlined), - onTap: () async { - final res = - await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); - SmartDialog.showToast(res['msg']); - }, - selectStatus: false, - text: '稍后看', - ), - ActionItem( - icon: const Icon(Icons.share), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - text: '分享', - ), - ], + children: list, ), ); }); diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 2a89afe7..af409dfc 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -38,20 +38,29 @@ class ActionItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 4), - icon is Icon - ? Icon( - selectStatus ? selectIcon!.icon ?? icon!.icon : icon!.icon, - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ) - : Image.asset( - 'assets/images/coin.png', - width: 25, - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: icon is Icon + ? Icon( + selectStatus + ? selectIcon!.icon ?? icon!.icon + : icon!.icon, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ) + : Image.asset( + key: ValueKey(selectStatus), + 'assets/images/coin.png', + width: 25, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + ), const SizedBox(height: 6), Text( text ?? '', @@ -60,7 +69,7 @@ class ActionItem extends StatelessWidget { selectStatus ? Theme.of(context).colorScheme.primary : null, fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, ), - ) + ), ], ), ), diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart index 29791210..97bff5a5 100644 --- a/lib/utils/global_data.dart +++ b/lib/utils/global_data.dart @@ -11,7 +11,8 @@ class GlobalData { bool enablePlayerControlAnimation = true; final bool enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); - + List actionTypeSort = setting.get(SettingBoxKey.actionTypeSort, + defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); // 私有构造函数 GlobalData._(); From d94ddca9056f1f00f8b2a7412a3cdcf86e07edfa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 00:58:04 +0800 Subject: [PATCH 18/34] =?UTF-8?q?opt:=20=E4=B8=89=E8=BF=9E=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=9D=A1=E6=A0=B7=E5=BC=8F&=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 91287c59..22f2dfe2 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,5 +1,3 @@ -import 'dart:ffi'; - import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; @@ -576,7 +574,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { height: 68, child: CircularProgressIndicator( value: progress.value, - strokeWidth: 4, + strokeWidth: 2, ), ); } @@ -593,6 +591,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { InkWell( onTapDown: (details) { feedBack(); + if (videoIntroController.userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } _controller.forward(); }, onTapUp: (TapUpDetails details) { From 61c29d30829a59d189225abfc1eae830fc0d1ff4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 01:09:28 +0800 Subject: [PATCH 19/34] =?UTF-8?q?fix:=20=E8=B7=B3=E8=BD=AC=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/login/controller.dart | 6 +++--- lib/pages/login/view.dart | 30 +++++++++++++----------------- lib/pages/mine/controller.dart | 3 +-- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index b5ddba16..fbb06e2f 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -31,7 +31,7 @@ class LoginPageController extends GetxController { // 倒计时60s RxInt seconds = 60.obs; - late Timer timer; + Timer? timer; RxBool smsCodeSendStatus = false.obs; // 默认密码登录 @@ -43,7 +43,7 @@ class LoginPageController extends GetxController { late int webSmsCode; RxInt validSeconds = 180.obs; - late Timer validTimer; + Timer? validTimer; late String qrcodeKey; // 监听pageView切换 @@ -329,7 +329,7 @@ class LoginPageController extends GetxController { var res = await LoginHttp.queryWebQrcodeStatus(qrcodeKey); if (res['status']) { await LoginUtils.confirmLogin('', null); - validTimer.cancel(); + validTimer?.cancel(); Get.back(); } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index cd91ea26..85a8adf0 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -15,8 +15,10 @@ class _LoginPageState extends State { final LoginPageController _loginPageCtr = Get.put(LoginPageController()); @override - void initState() { - super.initState(); + void dispose() { + _loginPageCtr.validTimer?.cancel(); + _loginPageCtr.timer?.cancel(); + super.dispose(); } @override @@ -51,7 +53,7 @@ class _LoginPageState extends State { }, ); }, - icon: const Icon(Icons.language), + icon: const Icon(Icons.language, size: 20), ), IconButton( tooltip: '二维码登录', @@ -90,7 +92,7 @@ class _LoginPageState extends State { Map data = snapshot.data as Map; return QrImageView( data: data['data']['url'], - backgroundColor: Colors.transparent, + backgroundColor: Colors.white, ); } else { return const Center( @@ -131,9 +133,11 @@ class _LoginPageState extends State { ); }); }, - ); + ).then((value) { + _loginPageCtr.validTimer!.cancel(); + }); }, - icon: const Icon(Icons.qr_code), + icon: const Icon(Icons.qr_code, size: 20), ), const SizedBox(width: 22), ], @@ -164,17 +168,9 @@ class _LoginPageState extends State { fontSize: 34, fontWeight: FontWeight.w500), ), - Row( - children: [ - Text( - '请使用您的 BiliBili 账号登录。', - style: Theme.of(context).textTheme.titleSmall!, - ), - GestureDetector( - onTap: () {}, - child: const Icon(Icons.info_outline, size: 16), - ) - ], + Text( + '请使用您的 BiliBili 账号登录。', + style: Theme.of(context).textTheme.titleSmall!, ), Container( margin: const EdgeInsets.only(top: 38, bottom: 15), diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 153a7162..75c50d82 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -33,8 +33,7 @@ class MineController extends GetxController { onLogin() async { if (!userLogin.value) { - // RoutePush.loginPush(); - Get.toNamed('/loginPage'); + Get.toNamed('/loginPage', preventDuplicates: false); } else { int mid = userInfo.value.mid!; String face = userInfo.value.face!; From 51be62cd26f06364d9a8ef923ed13b874ce6c88d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 12:50:15 +0800 Subject: [PATCH 20/34] =?UTF-8?q?opt:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 22f2dfe2..3b89d1de 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -185,7 +185,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); _scaleTransition = Tween(begin: 0.5, end: 1.5).animate(_controller) ..addListener(() async { - _progress.value = _scaleTransition.value - 0.5; + _progress.value = + double.parse((_scaleTransition.value - 0.5).toStringAsFixed(3)); if (_progress.value == 1) { if (_controller.status == AnimationStatus.completed) { await videoIntroController.actionOneThree(); @@ -570,8 +571,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget progressWidget(progress) { return SizedBox( - width: 68, - height: 68, + width: 33, + height: 33, child: CircularProgressIndicator( value: progress.value, strokeWidth: 2, @@ -585,16 +586,15 @@ class _VideoInfoState extends State with TickerProviderStateMixin { bool likeStatus = videoIntroController.hasLike.value; ColorScheme colorScheme = Theme.of(context).colorScheme; return Stack( - alignment: Alignment.center, children: [ - progressWidget(_progress), + Positioned(child: progressWidget(_progress), top: 15, left: 24), InkWell( onTapDown: (details) { feedBack(); - if (videoIntroController.userInfo == null) { - SmartDialog.showToast('账号未登录'); - return; - } + // if (videoIntroController.userInfo == null) { + // SmartDialog.showToast('账号未登录'); + // return; + // } _controller.forward(); }, onTapUp: (TapUpDetails details) { @@ -650,9 +650,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), 'coin': Obx( () => Stack( - alignment: Alignment.center, children: [ - progressWidget(_progress), + Positioned(child: progressWidget(_progress), top: 15, left: 24), ActionItem( icon: Image.asset('assets/images/coin.png', width: 30), onTap: handleState(videoIntroController.actionCoinVideo), @@ -664,9 +663,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), 'collect': Obx( () => Stack( - alignment: Alignment.center, children: [ - progressWidget(_progress), + Positioned(child: progressWidget(_progress), top: 15, left: 24), ActionItem( icon: const Icon(Icons.star_border), selectIcon: const Icon(Icons.star), From 128c9201bda0efae89cc627bdd841a5313e921dd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 13:36:23 +0800 Subject: [PATCH 21/34] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E6=A1=86?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E8=B7=9D=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_new/view.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index a136f623..d55e2c1c 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -214,7 +214,15 @@ class _VideoReplyNewDialogState extends State ), Container( height: 52, - padding: const EdgeInsets.only(left: 12, right: 12), + padding: const EdgeInsets.only( + left: 12, + right: 12, + ), + margin: EdgeInsets.only( + bottom: toolbarType == 'input' && keyboardHeight == 0.0 + ? MediaQuery.of(context).padding.bottom + : 0, + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ From e47816d0c8f748411779bde717de2e2d72c51250 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 14:18:59 +0800 Subject: [PATCH 22/34] =?UTF-8?q?opt:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 33 ++++++++++++++----- .../introduction/widgets/action_item.dart | 2 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 3b89d1de..93cc26c9 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -571,8 +571,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget progressWidget(progress) { return SizedBox( - width: 33, - height: 33, + width: const IconThemeData.fallback().size! + 5, + height: const IconThemeData.fallback().size! + 5, child: CircularProgressIndicator( value: progress.value, strokeWidth: 2, @@ -587,14 +587,19 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ColorScheme colorScheme = Theme.of(context).colorScheme; return Stack( children: [ - Positioned(child: progressWidget(_progress), top: 15, left: 24), + Positioned( + top: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size!), + left: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size! + 5) / 2, + child: progressWidget(_progress)), InkWell( onTapDown: (details) { feedBack(); - // if (videoIntroController.userInfo == null) { - // SmartDialog.showToast('账号未登录'); - // return; - // } + if (videoIntroController.userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } _controller.forward(); }, onTapUp: (TapUpDetails details) { @@ -651,7 +656,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { 'coin': Obx( () => Stack( children: [ - Positioned(child: progressWidget(_progress), top: 15, left: 24), + Positioned( + top: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size!), + left: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size! + 5) / 2, + child: progressWidget(_progress)), ActionItem( icon: Image.asset('assets/images/coin.png', width: 30), onTap: handleState(videoIntroController.actionCoinVideo), @@ -664,7 +674,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { 'collect': Obx( () => Stack( children: [ - Positioned(child: progressWidget(_progress), top: 15, left: 24), + Positioned( + top: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size!), + left: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size! + 5) / 2, + child: progressWidget(_progress)), ActionItem( icon: const Icon(Icons.star_border), selectIcon: const Icon(Icons.star), diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index af409dfc..3288b2fa 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -55,7 +55,7 @@ class ActionItem extends StatelessWidget { : Image.asset( key: ValueKey(selectStatus), 'assets/images/coin.png', - width: 25, + width: const IconThemeData.fallback().size, color: selectStatus ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, From beb5945135209ce70a20f48b37f18745b1200298 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 11 Jun 2024 00:06:49 +0800 Subject: [PATCH 23/34] =?UTF-8?q?fix:=20=E7=BD=91=E7=BB=9C=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E6=97=B6=E7=99=BB=E5=BD=95=E7=8A=B6=E6=80=81=E9=87=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/mine/controller.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 75c50d82..e682a7e5 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -57,8 +57,6 @@ class MineController extends GetxController { } else { resetUserInfo(); } - } else { - resetUserInfo(); } await queryUserStatOwner(); return res; From 7af1ebd3d5fd63d4dab2b5ec8dc0356dd02046d7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 11 Jun 2024 22:59:07 +0800 Subject: [PATCH 24/34] =?UTF-8?q?fix:=20=E5=BC=B9=E5=B9=95=E6=98=BE?= =?UTF-8?q?=E7=A4=BA&=E5=BC=B9=E5=B9=95=E8=AE=BE=E7=BD=AE=E7=BC=93?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 41 +++++++++++-------- .../video/detail/widgets/header_control.dart | 4 +- lib/plugin/pl_player/controller.dart | 17 +++++--- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a0f5d6bc..a99ee936 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -591,27 +591,32 @@ class _VideoDetailPageState extends State verticalScreen(); } }, - child: Hero( - tag: heroTag, - child: Stack( - children: [ - if (isShowing) videoPlayerPanel, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints constraints) { + return Hero( + tag: heroTag, + child: Stack( + children: [ + if (isShowing) videoPlayerPanel, - /// 关闭自动播放时 手动播放 - Obx( - () => Visibility( - visible: !vdCtr.autoPlay.value && - vdCtr.isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: handlePlayPanel(), + /// 关闭自动播放时 手动播放 + Obx( + () => Visibility( + visible: !vdCtr.autoPlay.value && + vdCtr.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: handlePlayPanel(), + ), + ), ), - ), + ], ), - ], - ), + ); + }, ), ), ), diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index d839a1eb..b597a060 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1069,7 +1069,9 @@ class _HeaderControlState extends State { ); }); }, - ); + ).then((value) { + widget.controller!.cacheDanmakuOption(); + }); } /// 播放顺序 diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 03c2efbe..2865a117 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -1089,6 +1089,16 @@ class PlPlayerController { videoStorage.put(VideoBoxKey.playRepeat, type.value); } + /// 缓存本次弹幕选项 + cacheDanmakuOption() { + localCache.put(LocalCacheKey.danmakuBlockType, blockTypes); + localCache.put(LocalCacheKey.danmakuShowArea, showArea); + localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); + localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); + localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal); + localCache.put(LocalCacheKey.strokeWidth, strokeWidth); + } + Future dispose({String type = 'single'}) async { // 每次减1,最后销毁 if (type == 'single' && playerCount.value > 1) { @@ -1118,12 +1128,7 @@ class PlPlayerController { // dataStatus.status.close(); /// 缓存本次弹幕选项 - localCache.put(LocalCacheKey.danmakuBlockType, blockTypes); - localCache.put(LocalCacheKey.danmakuShowArea, showArea); - localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); - localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); - localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal); - localCache.put(LocalCacheKey.strokeWidth, strokeWidth); + cacheDanmakuOption(); if (_videoPlayerController != null) { var pp = _videoPlayerController!.platform as NativePlayer; await pp.setProperty('audio-files', ''); From 5c3d3cc957e037a699a7f8a19a32c375227d35f5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 12 Jun 2024 23:37:29 +0800 Subject: [PATCH 25/34] undo: flutter version 3.22.1 -> 3.19.1 --- lib/common/widgets/http_error.dart | 2 +- lib/common/widgets/video_card_h.dart | 2 +- lib/pages/bangumi/introduction/view.dart | 8 ++--- lib/pages/bangumi/widgets/bangumi_panel.dart | 2 +- lib/pages/dynamics/view.dart | 2 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- .../fav_detail/widget/fav_video_card.dart | 2 +- lib/pages/history/widgets/item.dart | 5 +-- lib/pages/home/view.dart | 8 ++--- .../live_room/widgets/bottom_control.dart | 2 +- lib/pages/login/view.dart | 6 ++-- lib/pages/media/view.dart | 4 +-- lib/pages/search/widgets/search_text.dart | 5 +-- .../search_panel/widgets/video_panel.dart | 2 +- lib/pages/setting/extra_setting.dart | 6 ++-- lib/pages/setting/style_setting.dart | 6 ++-- lib/pages/setting/widgets/switch_item.dart | 6 ++-- lib/pages/subscription/widgets/item.dart | 2 +- .../introduction/widgets/page_panel.dart | 2 +- .../detail/reply_new/toolbar_icon_button.dart | 4 +-- lib/pages/video/detail/reply_new/view.dart | 2 +- lib/pages/video/detail/view.dart | 2 +- .../video/detail/widgets/header_control.dart | 8 ++--- lib/pages/whisper_detail/view.dart | 6 ++-- lib/plugin/pl_player/view.dart | 6 ++-- lib/plugin/pl_player/widgets/common_btn.dart | 2 +- .../pl_player/widgets/play_pause_btn.dart | 2 +- lib/utils/image_save.dart | 2 +- pubspec.lock | 36 +++++++++---------- 29 files changed, 72 insertions(+), 72 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index b130faae..cbc6659b 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -34,7 +34,7 @@ class HttpError extends StatelessWidget { fn!(); }, style: ButtonStyle( - backgroundColor: WidgetStateProperty.resolveWith((states) { + backgroundColor: MaterialStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.primary.withAlpha(20); }), ), diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index c674b223..1265477f 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -305,7 +305,7 @@ class VideoContent extends StatelessWidget { if (source == 'later') ...[ IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => onPressedFn?.call(), icon: Icon( diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index bb85be1a..95d4d898 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -232,11 +232,11 @@ class _BangumiInfoState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: - WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all( + EdgeInsets.zero), backgroundColor: - WidgetStateProperty.resolveWith( - (Set states) { + MaterialStateProperty.resolveWith( + (Set states) { return t.colorScheme.primaryContainer .withOpacity(0.7); }), diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 345a47b6..3df7ce25 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -139,7 +139,7 @@ class _BangumiPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.bangumiIntroController?.bottomSheetController = diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 258ad531..1775798e 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -162,7 +162,7 @@ class _DynamicsPageState extends State decoration: BoxDecoration( color: Theme.of(context) .colorScheme - .surfaceContainerHighest + .surfaceVariant .withOpacity(0.7), borderRadius: BorderRadius.circular(20), ), diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index e66e2a91..8acdc26a 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -82,7 +82,7 @@ class AuthorPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { showModalBottomSheet( diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 72d7b4a0..79e5c073 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -217,7 +217,7 @@ class VideoContent extends StatelessWidget { bottom: -4, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { showDialog( diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 8e71df6f..baebfedb 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -217,10 +217,11 @@ class HistoryItem extends StatelessWidget { curve: Curves.easeInOut, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all( + padding: MaterialStateProperty.all( EdgeInsets.zero), backgroundColor: - WidgetStateProperty.resolveWith( + MaterialStateProperty + .resolveWith( (states) { return Colors.white .withOpacity(0.8); diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 0c45a262..a25389bd 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -278,8 +278,8 @@ class DefaultUser extends StatelessWidget { height: 38, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - backgroundColor: WidgetStateProperty.resolveWith((states) { + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.onInverseSurface; }), ), @@ -371,8 +371,8 @@ class CustomChip extends StatelessWidget { ), backgroundColor: secondaryContainer, selectedColor: secondaryContainer, - color: WidgetStateProperty.resolveWith( - (Set states) => secondaryContainer.withAlpha(200)), + color: MaterialStateProperty.resolveWith( + (Set states) => secondaryContainer.withAlpha(200)), padding: const EdgeInsets.fromLTRB(7, 1, 7, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 6abb1260..4dd7c538 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -124,7 +124,7 @@ class _BottomControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 04d5a1a8..85a8adf0 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -249,7 +249,8 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - WidgetStateProperty.resolveWith((states) { + MaterialStateProperty.resolveWith( + (states) { return Theme.of(context) .colorScheme .primary @@ -339,7 +340,8 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - WidgetStateProperty.resolveWith((states) { + MaterialStateProperty.resolveWith( + (states) { return Theme.of(context) .colorScheme .primary diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index f6a033e5..cc413e59 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -178,10 +178,10 @@ class _MediaPageState extends State child: Center( child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all( + padding: MaterialStateProperty.all( EdgeInsets.zero), backgroundColor: - WidgetStateProperty.resolveWith( + MaterialStateProperty.resolveWith( (states) { return Theme.of(context) .colorScheme diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index d3ffafea..bcc8c17e 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -20,10 +20,7 @@ class SearchText extends StatelessWidget { return Material( color: isSelect ? Theme.of(context).colorScheme.primaryContainer - : Theme.of(context) - .colorScheme - .surfaceContainerHighest - .withOpacity(0.5), + : Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), borderRadius: BorderRadius.circular(6), child: Padding( padding: EdgeInsets.zero, diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index bc9b48c9..15745bde 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -93,7 +93,7 @@ class SearchVideoPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => controller.onShowFilterSheet(ctr), icon: Icon( diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index 885a831c..aaaa8b84 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -240,10 +240,10 @@ class _ExtraSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: WidgetStateProperty.resolveWith( - (Set states) { + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == WidgetState.selected) { + states.first == MaterialState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index b6ee5041..5fca0c86 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -69,10 +69,10 @@ class _StyleSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: WidgetStateProperty.resolveWith( - (Set states) { + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == WidgetState.selected) { + states.first == MaterialState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart index 36c4433e..d0c2bbf2 100644 --- a/lib/pages/setting/widgets/switch_item.dart +++ b/lib/pages/setting/widgets/switch_item.dart @@ -70,9 +70,9 @@ class _SetSwitchItemState extends State { alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大 scale: 0.8, child: Switch( - thumbIcon: - WidgetStateProperty.resolveWith((Set states) { - if (states.isNotEmpty && states.first == WidgetState.selected) { + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { + if (states.isNotEmpty && states.first == MaterialState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index 6c1cc2b9..b244d3c7 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -116,7 +116,7 @@ class VideoContent extends StatelessWidget { children: [ IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => cancelSub?.call(subFolderItem), icon: Icon( diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index c8111847..266f5566 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -106,7 +106,7 @@ class _PagesPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.videoIntroCtr.bottomSheetController = diff --git a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart index bf810ce2..c4390796 100644 --- a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart +++ b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart @@ -27,8 +27,8 @@ class ToolbarIconButton extends StatelessWidget { ? Theme.of(context).colorScheme.onSecondaryContainer : Theme.of(context).colorScheme.outline, style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - backgroundColor: WidgetStateProperty.resolveWith((states) { + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith((states) { return selected ? Theme.of(context).colorScheme.secondaryContainer : null; diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index d55e2c1c..40267559 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -267,7 +267,7 @@ class _VideoReplyNewDialogState extends State size: 22), label: const Text('转发到动态'), style: ButtonStyle( - foregroundColor: WidgetStateProperty.all( + foregroundColor: MaterialStateProperty.all( isForward.value ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a99ee936..22271c2b 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -412,7 +412,7 @@ class _VideoDetailPageState extends State height: 32, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => vdCtr.showShootDanmakuSheet(), child: diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index b597a060..f51edff1 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1250,7 +1250,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => showShootDanmakuSheet(), child: const Text( @@ -1265,7 +1265,7 @@ class _HeaderControlState extends State { child: Obx( () => IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { _.isOpenDanmu.value = !_.isOpenDanmu.value; @@ -1288,7 +1288,7 @@ class _HeaderControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; @@ -1332,7 +1332,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => showSetSpeedSheet(), child: Text( diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 042afca1..1701be33 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -95,9 +95,9 @@ class _WhisperDetailPageState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - backgroundColor: WidgetStateProperty.resolveWith( - (Set states) { + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { return Theme.of(context) .colorScheme .primaryContainer diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 0e405884..34140be8 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -279,7 +279,7 @@ class _PLVideoPlayerState extends State widget.showEposideCb?.call(); }, style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), child: const Text( '选集', @@ -294,7 +294,7 @@ class _PLVideoPlayerState extends State child: TextButton( onPressed: () => _.toggleVideoFit(), style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), child: Obx( () => Text( @@ -311,7 +311,7 @@ class _PLVideoPlayerState extends State height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () {}, child: Obx( diff --git a/lib/plugin/pl_player/widgets/common_btn.dart b/lib/plugin/pl_player/widgets/common_btn.dart index bf9467a8..5f33311c 100644 --- a/lib/plugin/pl_player/widgets/common_btn.dart +++ b/lib/plugin/pl_player/widgets/common_btn.dart @@ -17,7 +17,7 @@ class ComBtn extends StatelessWidget { height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { fuc!(); diff --git a/lib/plugin/pl_player/widgets/play_pause_btn.dart b/lib/plugin/pl_player/widgets/play_pause_btn.dart index 7547a1cb..6cbe31f0 100644 --- a/lib/plugin/pl_player/widgets/play_pause_btn.dart +++ b/lib/plugin/pl_player/widgets/play_pause_btn.dart @@ -68,7 +68,7 @@ class PlayOrPauseButtonState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: player.playOrPause, color: Colors.white, diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0727ad68..0b77b7cc 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -39,7 +39,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { const BorderRadius.all(Radius.circular(20))), child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => closeFn!(), icon: const Icon( diff --git a/pubspec.lock b/pubspec.lock index 5c6f2b43..c73ce395 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -762,10 +762,10 @@ packages: dependency: transitive description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.flutter-io.cn" source: hosted - version: "0.19.0" + version: "0.18.1" io: dependency: transitive description: @@ -794,26 +794,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" url: "https://pub.flutter-io.cn" source: hosted - version: "10.0.4" + version: "10.0.0" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.3" + version: "2.0.1" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.1" + version: "2.0.1" lints: dependency: transitive description: @@ -973,10 +973,10 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.flutter-io.cn" source: hosted - version: "1.12.0" + version: "1.11.0" mime: dependency: transitive description: @@ -1467,10 +1467,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.0" + version: "0.6.1" timing: dependency: transitive description: @@ -1627,10 +1627,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.flutter-io.cn" source: hosted - version: "14.2.1" + version: "13.0.0" volume_controller: dependency: transitive description: @@ -1707,10 +1707,10 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: f42447ca49523f11d8f70abea55ea211b3cafe172dd7a0e7ac007bb35dd356dc + sha256: "0d21cfc3bfdd2e30ab2ebeced66512b91134b39e72e97b43db2d47dda1c4e53a" url: "https://pub.flutter-io.cn" source: hosted - version: "3.16.4" + version: "3.16.3" webview_flutter_platform_interface: dependency: transitive description: @@ -1768,5 +1768,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.22.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" From 5afb50cac990d41ea5f69001fa637e3a59dee38f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 13 Jun 2024 22:55:54 +0800 Subject: [PATCH 26/34] =?UTF-8?q?fix:=20=E5=BF=AB=E8=BF=9B=E5=BF=AB?= =?UTF-8?q?=E9=80=80=E8=A7=A6=E5=8F=91=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 4 ++-- lib/plugin/pl_player/widgets/backward_seek.dart | 4 ++-- lib/plugin/pl_player/widgets/forward_seek.dart | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 34140be8..fa0dc444 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -940,7 +940,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekBackwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 200), + duration: const Duration(milliseconds: 150), builder: (BuildContext context, double value, Widget? child) => Opacity( @@ -983,7 +983,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekForwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 200), + duration: const Duration(milliseconds: 150), builder: (BuildContext context, double value, Widget? child) => Opacity( diff --git a/lib/plugin/pl_player/widgets/backward_seek.dart b/lib/plugin/pl_player/widgets/backward_seek.dart index 8fddf80a..8289d77c 100644 --- a/lib/plugin/pl_player/widgets/backward_seek.dart +++ b/lib/plugin/pl_player/widgets/backward_seek.dart @@ -30,14 +30,14 @@ class BackwardSeekIndicatorState extends State { @override void initState() { super.initState(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); } void increment() { timer?.cancel(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); widget.onChanged.call(value); diff --git a/lib/plugin/pl_player/widgets/forward_seek.dart b/lib/plugin/pl_player/widgets/forward_seek.dart index 7e3886ce..3f68fe0d 100644 --- a/lib/plugin/pl_player/widgets/forward_seek.dart +++ b/lib/plugin/pl_player/widgets/forward_seek.dart @@ -30,14 +30,14 @@ class ForwardSeekIndicatorState extends State { @override void initState() { super.initState(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); } void increment() { timer?.cancel(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); widget.onChanged.call(value); From 28737543174976d65a0a91e560c21bbc870367f7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 13 Jun 2024 23:13:47 +0800 Subject: [PATCH 27/34] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E5=85=A8=E5=B1=8F=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 7 +++++++ lib/plugin/pl_player/view.dart | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 22271c2b..ef067d1b 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -514,6 +514,13 @@ class _VideoDetailPageState extends State showEposideCb: () => vdCtr.videoType == SearchType.video ? videoIntroController.showEposideHandler() : bangumiIntroController.showEposideHandler(), + fullScreenCb: (bool status) { + if (status) { + videoHeight.value = Get.size.height; + } else { + videoHeight.value = defaultVideoHeight; + } + }, ), ); }, diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index fa0dc444..f3e0946b 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -39,6 +39,7 @@ class PLVideoPlayer extends StatefulWidget { this.customWidget, this.customWidgets, this.showEposideCb, + this.fullScreenCb, super.key, }); @@ -52,6 +53,7 @@ class PLVideoPlayer extends StatefulWidget { final Widget? customWidget; final List? customWidgets; final Function? showEposideCb; + final Function? fullScreenCb; @override State createState() => _PLVideoPlayerState(); @@ -335,7 +337,10 @@ class _PLVideoPlayerState extends State color: Colors.white, ), ), - fuc: () => _.triggerFullScreen(status: !_.isFullScreen.value), + fuc: () { + _.triggerFullScreen(status: !_.isFullScreen.value); + widget.fullScreenCb?.call(!_.isFullScreen.value); + }, ), }; final List list = []; From 5e63f0da7b93eb3e2d2b27ed0ba8b52d24518f50 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 15 Jun 2024 16:03:45 +0800 Subject: [PATCH 28/34] =?UTF-8?q?feat:=20=E7=A7=81=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/msg.dart | 66 ++-- lib/pages/member/widgets/profile.dart | 12 +- lib/pages/whisper/controller.dart | 9 + lib/pages/whisper/view.dart | 23 +- lib/pages/whisper_detail/controller.dart | 37 +- lib/pages/whisper_detail/view.dart | 342 ++++++++++-------- .../whisper_detail/widget/chat_item.dart | 162 +++++---- 7 files changed, 373 insertions(+), 278 deletions(-) diff --git a/lib/http/msg.dart b/lib/http/msg.dart index d1d31958..7ac30bc1 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,4 +1,6 @@ +import 'dart:convert'; import 'dart:math'; +import 'package:dio/dio.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; import '../utils/wbi_sign.dart'; @@ -134,56 +136,42 @@ class MsgHttp { // 发送私信 static Future sendMsg({ - int? senderUid, - int? receiverId, + required int senderUid, + required int receiverId, int? receiverType, int? msgType, dynamic content, }) async { String csrf = await Request.getCsrf(); - Map params = await WbiSign().makSign({ - 'msg[sender_uid]': senderUid, - 'msg[receiver_id]': receiverId, - 'msg[receiver_type]': receiverType ?? 1, - 'msg[msg_type]': msgType ?? 1, - 'msg[msg_status]': 0, - 'msg[dev_id]': getDevId(), - 'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000, - 'msg[new_face_version]': 0, - 'msg[content]': content, - 'from_firework': 0, - 'build': 0, - 'mobi_app': 'web', - 'csrf_token': csrf, - 'csrf': csrf, - }); - var res = - await Request().post(Api.sendMsg, queryParameters: { - ...params, - 'csrf_token': csrf, - 'csrf': csrf, - }, data: { - 'w_sender_uid': params['msg[sender_uid]'], - 'w_receiver_id': params['msg[receiver_id]'], - 'w_dev_id': params['msg[dev_id]'], - 'w_rid': params['w_rid'], - 'wts': params['wts'], - 'csrf_token': csrf, - 'csrf': csrf, - }); + var res = await Request().post( + Api.sendMsg, + data: { + 'msg[sender_uid]': senderUid, + 'msg[receiver_id]': receiverId, + 'msg[receiver_type]': 1, + 'msg[msg_type]': 1, + 'msg[msg_status]': 0, + 'msg[content]': jsonEncode(content), + 'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000, + 'msg[new_face_version]': 0, + 'msg[dev_id]': getDevId(), + 'from_firework': 0, + 'build': 0, + 'mobi_app': 'web', + 'csrf_token': csrf, + 'csrf': csrf, + }, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); if (res.data['code'] == 0) { return { 'status': true, 'data': res.data['data'], }; } else { - return { - 'status': false, - 'date': [], - 'msg': "message: ${res.data['message']}," - " msg: ${res.data['msg']}," - " code: ${res.data['code']}", - }; + return {'status': false, 'date': [], 'msg': res.data['message']}; } } diff --git a/lib/pages/member/widgets/profile.dart b/lib/pages/member/widgets/profile.dart index a708a35e..8c6385db 100644 --- a/lib/pages/member/widgets/profile.dart +++ b/lib/pages/member/widgets/profile.dart @@ -208,7 +208,17 @@ class ProfilePanel extends StatelessWidget { const SizedBox(width: 8), Expanded( child: TextButton( - onPressed: () {}, + onPressed: () { + Get.toNamed( + '/whisperDetail', + parameters: { + 'name': memberInfo.name!, + 'face': memberInfo.face!, + 'mid': memberInfo.mid.toString(), + 'heroTag': ctr.heroTag!, + }, + ); + }, style: TextButton.styleFrom( backgroundColor: Theme.of(context) .colorScheme diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index c82cab5b..b7c52d2e 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -62,4 +62,13 @@ class WhisperController extends GetxController { Future onRefresh() async { querySessionList('onRefresh'); } + + void refreshLastMsg(int talkerId, String content) { + final SessionList currentItem = + sessionList.where((p0) => p0.talkerId == talkerId).first; + currentItem.lastMsg!.content['content'] = content; + sessionList.removeWhere((p0) => p0.talkerId == talkerId); + sessionList.insert(0, currentItem); + sessionList.refresh(); + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index fa95463b..6d2f281b 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -202,6 +202,7 @@ class SessionItem extends StatelessWidget { @override Widget build(BuildContext context) { + final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo.mid); final content = sessionItem.lastMsg.content; return ListTile( onTap: () { @@ -214,6 +215,7 @@ class SessionItem extends StatelessWidget { 'name': sessionItem.accountInfo.name, 'face': sessionItem.accountInfo.face, 'mid': sessionItem.accountInfo.mid.toString(), + 'heroTag': heroTag, }, ); }, @@ -221,11 +223,14 @@ class SessionItem extends StatelessWidget { isLabelVisible: sessionItem.unreadCount > 0, label: Text(sessionItem.unreadCount.toString()), alignment: Alignment.topRight, - child: NetworkImgLayer( - width: 45, - height: 45, - type: 'avatar', - src: sessionItem.accountInfo.face, + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: sessionItem.accountInfo.face, + ), ), ), title: Text(sessionItem.accountInfo.name), @@ -245,10 +250,10 @@ class SessionItem extends StatelessWidget { .copyWith(color: Theme.of(context).colorScheme.outline)), trailing: Text( Utils.dateFormat(sessionItem.lastMsg.timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith(color: Theme.of(context).colorScheme.outline), + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), ), ); } diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 6e950f81..3c7e0837 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -1,30 +1,39 @@ +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/session.dart'; +import 'package:pilipala/pages/whisper/index.dart'; import '../../utils/feed_back.dart'; import '../../utils/storage.dart'; class WhisperDetailController extends GetxController { - late int talkerId; + int? talkerId; late String name; late String face; late String mid; + late String heroTag; RxList messageList = [].obs; //表情转换图片规则 - List? eInfos; + RxList eInfos = [].obs; final TextEditingController replyContentController = TextEditingController(); Box userInfoCache = GStrorage.userInfo; + List emoteList = []; @override void onInit() { super.onInit(); - talkerId = int.parse(Get.parameters['talkerId']!); + if (Get.parameters.containsKey('talkerId')) { + talkerId = int.parse(Get.parameters['talkerId']!); + } else { + talkerId = int.parse(Get.parameters['mid']!); + } name = Get.parameters['name']!; face = Get.parameters['face']!; mid = Get.parameters['mid']!; + heroTag = Get.parameters['heroTag']!; } Future querySessionMsg() async { @@ -34,7 +43,7 @@ class WhisperDetailController extends GetxController { if (messageList.isNotEmpty) { ackSessionMsg(); if (res['data'].eInfos != null) { - eInfos = res['data'].eInfos; + eInfos.value = res['data'].eInfos; } } } else { @@ -73,7 +82,25 @@ class WhisperDetailController extends GetxController { msgType: 1, ); if (result['status']) { - SmartDialog.showToast('发送成功'); + String content = jsonDecode(result['data']['msg_content'])['content']; + messageList.insert( + 0, + MessageItem( + msgSeqno: result['data']['msg_key'], + senderUid: userInfo.mid, + receiverId: int.parse(mid), + content: {'content': content}, + msgType: 1, + timestamp: DateTime.now().millisecondsSinceEpoch, + ), + ); + eInfos.addAll(emoteList); + replyContentController.clear(); + try { + late final WhisperController whisperController = + Get.find(); + whisperController.refreshLastMsg(talkerId!, message); + } catch (_) {} } else { SmartDialog.showToast(result['msg']); } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 1701be33..7c5762d9 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -1,9 +1,12 @@ import 'dart:async'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/video/reply/emote.dart'; import 'package:pilipala/pages/emote/index.dart'; +import 'package:pilipala/pages/video/detail/reply_new/toolbar_icon_button.dart'; import 'package:pilipala/pages/whisper_detail/controller.dart'; import 'package:pilipala/utils/feed_back.dart'; import '../../utils/storage.dart'; @@ -24,9 +27,9 @@ class _WhisperDetailPageState extends State late TextEditingController _replyContentController; final FocusNode replyContentFocusNode = FocusNode(); final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间 - late double emoteHeight = 0.0; + late double emoteHeight = 230.0; double keyboardHeight = 0.0; // 键盘高度 - String toolbarType = 'input'; + RxString toolbarType = ''.obs; Box userInfoCache = GStrorage.userInfo; @override @@ -41,9 +44,7 @@ class _WhisperDetailPageState extends State _focuslistener() { replyContentFocusNode.addListener(() { if (replyContentFocusNode.hasFocus) { - setState(() { - toolbarType = 'input'; - }); + toolbarType.value = 'input'; } }); } @@ -52,7 +53,7 @@ class _WhisperDetailPageState extends State void didChangeMetrics() { super.didChangeMetrics(); final String routePath = Get.currentRoute; - if (mounted && routePath.startsWith('/whisper_detail')) { + if (mounted && routePath.startsWith('/whisperDetail')) { WidgetsBinding.instance.addPostFrameCallback((_) { // 键盘高度 final viewInsets = EdgeInsets.fromViewPadding( @@ -61,8 +62,11 @@ class _WhisperDetailPageState extends State if (mounted) { if (keyboardHeight == 0) { setState(() { - emoteHeight = keyboardHeight = + keyboardHeight = keyboardHeight == 0.0 ? viewInsets.bottom : keyboardHeight; + if (keyboardHeight != 0) { + emoteHeight = keyboardHeight; + } }); } } @@ -79,6 +83,23 @@ class _WhisperDetailPageState extends State super.dispose(); } + void onChooseEmote(PackageItem package, Emote emote) { + _whisperDetailController.emoteList.add( + {'text': emote.text, 'url': emote.url}, + ); + final int cursorPosition = + max(_replyContentController.selection.baseOffset, 0); + final String currentText = _replyContentController.text; + final String newText = currentText.substring(0, cursorPosition) + + emote.text! + + currentText.substring(cursorPosition); + _replyContentController.value = TextEditingValue( + text: newText, + selection: + TextSelection.collapsed(offset: cursorPosition + emote.text!.length), + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -88,30 +109,20 @@ class _WhisperDetailPageState extends State width: double.infinity, height: 50, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox( width: 34, height: 34, child: IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) { - return Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.6); - }), - ), onPressed: () => Get.back(), icon: Icon( - Icons.arrow_back_outlined, + Icons.arrow_back_ios, size: 18, color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), ), + const SizedBox(width: 10), GestureDetector( onTap: () { feedBack(); @@ -125,11 +136,14 @@ class _WhisperDetailPageState extends State }, child: Row( children: [ - NetworkImgLayer( - width: 34, - height: 34, - type: 'avatar', - src: _whisperDetailController.face, + Hero( + tag: _whisperDetailController.heroTag, + child: NetworkImgLayer( + width: 34, + height: 34, + type: 'avatar', + src: _whisperDetailController.face, + ), ), const SizedBox(width: 6), Text( @@ -143,155 +157,169 @@ class _WhisperDetailPageState extends State ], ), ), - ), - body: GestureDetector( - onTap: () { - FocusScope.of(context).unfocus(); - setState(() { - keyboardHeight = 0; - }); - }, - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - final Map data = snapshot.data as Map; - if (data['status']) { - List messageList = _whisperDetailController.messageList; - return Obx( - () => messageList.isEmpty - ? const SizedBox() - : ListView.builder( - itemCount: messageList.length, - shrinkWrap: true, - reverse: true, - itemBuilder: (_, int i) { - if (i == 0) { - return Column( - children: [ - ChatItem( - item: messageList[i], - e_infos: _whisperDetailController.eInfos), - const SizedBox(height: 12), - ], - ); - } else { - return ChatItem( - item: messageList[i], - e_infos: _whisperDetailController.eInfos); - } - }, - ), - ); - } else { - // 请求错误 - return const SizedBox(); - } - } else { - // 骨架屏 - return const SizedBox(); - } - }, - ), - ), - // resizeToAvoidBottomInset: true, - bottomNavigationBar: Container( - width: double.infinity, - height: MediaQuery.of(context).padding.bottom + 70 + keyboardHeight, - padding: EdgeInsets.only( - left: 8, - right: 12, - top: 12, - bottom: MediaQuery.of(context).padding.bottom, - ), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - width: 4, - color: Theme.of(context).colorScheme.primary.withOpacity(0.1), + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert_outlined, + size: 20, ), ), - ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // IconButton( - // onPressed: () {}, - // icon: Icon( - // Icons.add_circle_outline, - // color: Theme.of(context).colorScheme.outline, - // ), - // ), - IconButton( - onPressed: () { - // if (toolbarType == 'input') { - // setState(() { - // toolbarType = 'emote'; - // }); - // } - // FocusScope.of(context).unfocus(); - }, - icon: Icon( - Icons.emoji_emotions_outlined, - color: Theme.of(context).colorScheme.outline, + const SizedBox(width: 14) + ], + ), + body: Column( + children: [ + Expanded( + child: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + toolbarType.value = ''; + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + final Map data = snapshot.data as Map; + if (data['status']) { + List messageList = _whisperDetailController.messageList; + return Obx( + () => messageList.isEmpty + ? const SizedBox() + : Align( + alignment: Alignment.topCenter, + child: ListView.builder( + itemCount: messageList.length, + shrinkWrap: true, + reverse: true, + itemBuilder: (_, int i) { + if (i == 0) { + return Column( + children: [ + ChatItem( + item: messageList[i], + e_infos: _whisperDetailController + .eInfos), + const SizedBox(height: 20), + ], + ); + } else { + return ChatItem( + item: messageList[i], + e_infos: + _whisperDetailController.eInfos); + } + }, + ), + ), + ); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + // 骨架屏 + return const SizedBox(); + } + }, + ), + ), + ), + Obx( + () => Container( + padding: EdgeInsets.fromLTRB( + 8, + 12, + 12, + toolbarType.value == '' + ? MediaQuery.of(context).padding.bottom + 6 + : 6, + ), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + width: 1, + color: Colors.grey.withOpacity(0.15), ), ), - Expanded( - child: Container( - height: 45, - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .primary - .withOpacity(0.08), - borderRadius: BorderRadius.circular(40.0), - ), - child: TextField( - readOnly: true, - style: Theme.of(context).textTheme.titleMedium, - controller: _replyContentController, - autofocus: false, - focusNode: replyContentFocusNode, - decoration: const InputDecoration( - border: InputBorder.none, // 移除默认边框 - hintText: '开发中 ...', // 提示文本 - contentPadding: EdgeInsets.symmetric( - horizontal: 16.0, vertical: 12.0), // 内边距 + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ToolbarIconButton( + onPressed: () { + if (toolbarType.value == '') { + toolbarType.value = 'emote'; + } else if (toolbarType.value == 'input') { + FocusScope.of(context).unfocus(); + toolbarType.value = 'emote'; + } else if (toolbarType.value == 'emote') { + FocusScope.of(context).requestFocus(); + } + }, + icon: const Icon(Icons.emoji_emotions_outlined, size: 22), + toolbarType: toolbarType.value, + selected: false, + ), + const SizedBox(width: 4), + Expanded( + child: Container( + height: 45, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .outline + .withOpacity(0.05), + borderRadius: BorderRadius.circular(40.0), + ), + child: TextField( + style: Theme.of(context).textTheme.titleMedium, + controller: _replyContentController, + autofocus: false, + focusNode: replyContentFocusNode, + decoration: const InputDecoration( + border: InputBorder.none, // 移除默认边框 + hintText: '文明发言 ~', // 提示文本 + contentPadding: EdgeInsets.symmetric( + horizontal: 16.0, vertical: 12.0), // 内边距 + ), ), ), ), - ), - IconButton( - // onPressed: _whisperDetailController.sendMsg, - onPressed: null, - icon: Icon( - Icons.send, - color: Theme.of(context).colorScheme.outline, + IconButton( + onPressed: _whisperDetailController.sendMsg, + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.outline, + ), ), - ), - // const SizedBox(width: 16), - ], + ], + ), ), - AnimatedSize( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 300), + ), + Obx( + () => AnimatedSize( + curve: Curves.linear, + duration: const Duration(milliseconds: 200), child: SizedBox( width: double.infinity, - height: toolbarType == 'input' ? keyboardHeight : emoteHeight, + height: toolbarType.value == 'input' + ? keyboardHeight + : toolbarType.value == 'emote' + ? emoteHeight + : 0, child: EmotePanel( - onChoose: (package, emote) => {}, + onChoose: (package, emote) => onChooseEmote(package, emote), ), ), ), - ], - ), + ), + ], ), + resizeToAvoidBottomInset: false, ); } } diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 0d37e8b3..c0d87221 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -1,7 +1,6 @@ // ignore_for_file: must_be_immutable import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -69,9 +68,13 @@ class ChatItem extends StatelessWidget { Color textColor(BuildContext context) { return isOwner ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.onSecondaryContainer; + : Theme.of(context).colorScheme.onBackground; } + const double safeDistanceval = 6; + const double borderRadiusVal = 12; + const double paddingVal = 10; + Widget richTextMessage(BuildContext context) { var text = content['content']; if (e_infos != null) { @@ -386,73 +389,98 @@ class ChatItem extends StatelessWidget { ? messageContent(context) : isRevoke ? const SizedBox() - : Row( - children: [ - if (!isOwner) const SizedBox(width: 12), - if (isOwner) const Spacer(), - Container( - constraints: const BoxConstraints( - maxWidth: 300.0, // 设置最大宽度为200.0 - ), - decoration: BoxDecoration( - color: isOwner - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.secondaryContainer, - borderRadius: BorderRadius.only( - topLeft: const Radius.circular(16), - topRight: const Radius.circular(16), - bottomLeft: Radius.circular(isOwner ? 16 : 6), - bottomRight: Radius.circular(isOwner ? 6 : 16), + : Padding( + padding: const EdgeInsets.only(top: 12), + child: Row( + mainAxisAlignment: !isOwner + ? MainAxisAlignment.start + : MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(width: safeDistanceval), + if (isOwner) + Text( + Utils.dateFormat(item.timestamp), + style: Theme.of(context).textTheme.labelSmall!.copyWith( + color: Theme.of(context).colorScheme.outline), ), + Container( + constraints: const BoxConstraints( + maxWidth: 300.0, // 设置最大宽度为200.0 + ), + decoration: BoxDecoration( + color: isOwner + ? Theme.of(context) + .colorScheme + .primary + .withAlpha(180) + : Theme.of(context) + .colorScheme + .outlineVariant + .withOpacity(0.6) + .withAlpha(125), + borderRadius: BorderRadius.only( + topLeft: const Radius.circular(borderRadiusVal), + topRight: const Radius.circular(borderRadiusVal), + bottomLeft: + Radius.circular(isOwner ? borderRadiusVal : 2), + bottomRight: + Radius.circular(isOwner ? 2 : borderRadiusVal), + ), + ), + margin: const EdgeInsets.only( + left: 8, + right: 8, + ), + padding: const EdgeInsets.all(paddingVal), + child: messageContent(context), + // child: Column( + // crossAxisAlignment: isOwner + // ? CrossAxisAlignment.end + // : CrossAxisAlignment.start, + // children: [ + // messageContent(context), + // SizedBox(height: isPic ? 7 : 2), + // Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text( + // Utils.dateFormat(item.timestamp), + // style: Theme.of(context) + // .textTheme + // .labelSmall! + // .copyWith( + // color: isOwner + // ? Theme.of(context) + // .colorScheme + // .onPrimary + // .withOpacity(0.8) + // : Theme.of(context) + // .colorScheme + // .onSecondaryContainer + // .withOpacity(0.8)), + // ), + // item.msgStatus == 1 + // ? Text( + // ' 已撤回', + // style: + // Theme.of(context).textTheme.labelSmall!, + // ) + // : const SizedBox() + // ], + // ) + // ], + // ), ), - margin: const EdgeInsets.only(top: 12), - padding: EdgeInsets.only( - top: 8, - bottom: 6, - left: isPic ? 8 : 12, - right: isPic ? 8 : 12, - ), - child: Column( - crossAxisAlignment: isOwner - ? CrossAxisAlignment.end - : CrossAxisAlignment.start, - children: [ - messageContent(context), - SizedBox(height: isPic ? 7 : 2), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - Utils.dateFormat(item.timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith( - color: isOwner - ? Theme.of(context) - .colorScheme - .onPrimary - .withOpacity(0.8) - : Theme.of(context) - .colorScheme - .onSecondaryContainer - .withOpacity(0.8)), - ), - item.msgStatus == 1 - ? Text( - ' 已撤回', - style: - Theme.of(context).textTheme.labelSmall!, - ) - : const SizedBox() - ], - ) - ], - ), - ), - if (!isOwner) const Spacer(), - if (isOwner) const SizedBox(width: 12), - ], + if (!isOwner) + Text( + Utils.dateFormat(item.timestamp), + style: Theme.of(context).textTheme.labelSmall!.copyWith( + color: Theme.of(context).colorScheme.outline), + ), + const SizedBox(width: safeDistanceval), + ], + ), ); } } From 46cef5e55b41adc0c9448255afc79051c3c44622 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 15 Jun 2024 17:03:52 +0800 Subject: [PATCH 29/34] =?UTF-8?q?feat:=20=E4=BC=9A=E8=AF=9D=E7=A7=BB?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 ++ lib/http/msg.dart | 31 +++++++++++---- lib/pages/whisper/controller.dart | 6 +++ lib/pages/whisper/view.dart | 18 +++++---- lib/pages/whisper_detail/controller.dart | 38 +++++++++++++++++++ lib/pages/whisper_detail/view.dart | 16 ++++---- .../whisper_detail/widget/chat_item.dart | 15 +++++++- 7 files changed, 105 insertions(+), 23 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index e519d91c..aaa804fa 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -535,4 +535,8 @@ class Api { /// 搜索结果计数 static const String searchCount = '/x/web-interface/wbi/search/all/v2'; + + /// 关闭会话 + static const String removeSession = + '${HttpString.tUrl}/session_svr/v1/session_svr/remove_session'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 7ac30bc1..9cdc9160 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -124,13 +124,7 @@ class MsgHttp { 'data': res.data['data'], }; } else { - return { - 'status': false, - 'date': [], - 'msg': "message: ${res.data['message']}," - " msg: ${res.data['msg']}," - " code: ${res.data['code']}", - }; + return {'status': false, 'date': [], 'msg': res.data['message']}; } } @@ -208,4 +202,27 @@ class MsgHttp { } return s.join(); } + + static Future removeSession({ + int? talkerId, + }) async { + String csrf = await Request.getCsrf(); + Map params = await WbiSign().makSign({ + 'talker_id': talkerId, + 'session_type': 1, + 'build': 0, + 'mobi_app': 'web', + 'csrf_token': csrf, + 'csrf': csrf + }); + var res = await Request().get(Api.removeSession, data: params); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index b7c52d2e..195b238b 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -71,4 +71,10 @@ class WhisperController extends GetxController { sessionList.insert(0, currentItem); sessionList.refresh(); } + + // 移除会话 + void removeSessionMsg(int talkerId) { + sessionList.removeWhere((p0) => p0.talkerId == talkerId); + sessionList.refresh(); + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 6d2f281b..bbe17048 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -204,6 +204,8 @@ class SessionItem extends StatelessWidget { Widget build(BuildContext context) { final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo.mid); final content = sessionItem.lastMsg.content; + final msgStatus = sessionItem.lastMsg.msgStatus; + return ListTile( onTap: () { sessionItem.unreadCount = 0; @@ -235,13 +237,15 @@ class SessionItem extends StatelessWidget { ), title: Text(sessionItem.accountInfo.name), subtitle: Text( - content != null && content != '' - ? (content['text'] ?? - content['content'] ?? - content['title'] ?? - content['reply_content'] ?? - '不支持的消息类型') - : '不支持的消息类型', + msgStatus == 1 + ? '你撤回了一条消息' + : content != null && content != '' + ? (content['text'] ?? + content['content'] ?? + content['title'] ?? + content['reply_content'] ?? + '不支持的消息类型') + : '不支持的消息类型', maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context) diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 3c7e0837..32e0ceb0 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -105,4 +106,41 @@ class WhisperDetailController extends GetxController { SmartDialog.showToast(result['msg']); } } + + void removeSession(context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + clipBehavior: Clip.hardEdge, + title: const Text('提示'), + content: const Text('确认清空会话内容并移除会话?'), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await MsgHttp.removeSession(talkerId: talkerId); + if (res['status']) { + SmartDialog.showToast('操作成功'); + try { + late final WhisperController whisperController = + Get.find(); + whisperController.removeSessionMsg(talkerId!); + Get.back(); + } catch (_) {} + } + }, + child: const Text('确认'), + ), + ], + ); + }, + ); + } } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 7c5762d9..912b5dc5 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -145,7 +145,7 @@ class _WhisperDetailPageState extends State src: _whisperDetailController.face, ), ), - const SizedBox(width: 6), + const SizedBox(width: 10), Text( _whisperDetailController.name, style: Theme.of(context).textTheme.titleMedium, @@ -158,12 +158,14 @@ class _WhisperDetailPageState extends State ), ), actions: [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert_outlined, - size: 20, - ), + PopupMenuButton( + icon: const Icon(Icons.more_vert_outlined, size: 20), + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + onTap: () => _whisperDetailController.removeSession(context), + child: const Text('关闭会话'), + ) + ], ), const SizedBox(width: 14) ], diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index c0d87221..f64cf223 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -389,8 +389,19 @@ class ChatItem extends StatelessWidget { ? messageContent(context) : isRevoke ? const SizedBox() - : Padding( - padding: const EdgeInsets.only(top: 12), + : Container( + padding: const EdgeInsets.only(top: 6, bottom: 6), + decoration: BoxDecoration( + border: Border( + left: item.msgStatus == 1 && !isOwner + ? BorderSide( + width: 4, color: Theme.of(context).dividerColor) + : BorderSide.none, + right: item.msgStatus == 1 && isOwner + ? BorderSide( + width: 4, color: Theme.of(context).primaryColor) + : BorderSide.none, + )), child: Row( mainAxisAlignment: !isOwner ? MainAxisAlignment.start From bfdd996b0833f13ad7b4de6b1ee11850ba8e4fbd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 15 Jun 2024 20:34:28 +0800 Subject: [PATCH 30/34] =?UTF-8?q?feat:=20=E6=9C=AA=E8=AF=BB=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E8=AE=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 12 ++ lib/pages/whisper/controller.dart | 45 +++++ lib/pages/whisper/view.dart | 269 +++++++++++++++--------------- 4 files changed, 194 insertions(+), 135 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index aaa804fa..198d6174 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -539,4 +539,7 @@ class Api { /// 关闭会话 static const String removeSession = '${HttpString.tUrl}/session_svr/v1/session_svr/remove_session'; + + /// 消息未读数 + static const String unread = '${HttpString.tUrl}/x/im/web/msgfeed/unread'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 9cdc9160..9a6e878b 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -225,4 +225,16 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + static Future unread() async { + var res = await Request().get(Api.unread); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index 195b238b..e00c990e 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/account.dart'; @@ -7,6 +8,38 @@ class WhisperController extends GetxController { RxList sessionList = [].obs; RxList accountList = [].obs; bool isLoading = false; + RxList noticesList = [ + { + 'icon': Icons.message_outlined, + 'title': '回复我的', + 'path': '', + 'count': 0, + }, + { + 'icon': Icons.alternate_email, + 'title': '@ 我的', + 'path': '', + 'count': 0, + }, + { + 'icon': Icons.thumb_up_outlined, + 'title': '收到的赞', + 'path': '', + 'count': 0, + }, + { + 'icon': Icons.notifications_none_outlined, + 'title': '系统通知', + 'path': '', + 'count': 0, + } + ].obs; + + @override + void onInit() { + unread(); + super.onInit(); + } Future querySessionList(String? type) async { if (isLoading) return; @@ -77,4 +110,16 @@ class WhisperController extends GetxController { sessionList.removeWhere((p0) => p0.talkerId == talkerId); sessionList.refresh(); } + + // 消息未读数 + void unread() async { + var res = await MsgHttp.unread(); + if (res['status']) { + noticesList[0]['count'] = res['data']['reply']; + noticesList[1]['count'] = res['data']['at']; + noticesList[2]['count'] = res['data']['like']; + noticesList[3]['count'] = res['data']['sys_msg']; + noticesList.refresh(); + } + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index bbe17048..8070498e 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/skeleton.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; @@ -44,147 +45,145 @@ class _WhisperPageState extends State { appBar: AppBar( title: const Text('消息'), ), - body: Column( - children: [ - // LayoutBuilder( - // builder: (BuildContext context, BoxConstraints constraints) { - // // 在这里根据父级容器的约束条件构建小部件树 - // return Padding( - // padding: const EdgeInsets.only(left: 20, right: 20), - // child: SizedBox( - // height: constraints.maxWidth / 5, - // child: GridView.count( - // primary: false, - // crossAxisCount: 4, - // padding: const EdgeInsets.all(0), - // childAspectRatio: 1.25, - // children: [ - // Column( - // crossAxisAlignment: CrossAxisAlignment.center, - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // SizedBox( - // width: 36, - // height: 36, - // child: IconButton( - // style: ButtonStyle( - // padding: - // MaterialStateProperty.all(EdgeInsets.zero), - // backgroundColor: - // MaterialStateProperty.resolveWith((states) { - // return Theme.of(context) - // .colorScheme - // .primary - // .withOpacity(0.1); - // }), - // ), - // onPressed: () {}, - // icon: Icon( - // Icons.message_outlined, - // size: 18, - // color: Theme.of(context).colorScheme.primary, - // ), - // ), - // ), - // const SizedBox(height: 6), - // const Text('回复我的', style: TextStyle(fontSize: 13)) - // ], - // ), - // ], - // ), - // ), - // ); - // }, - // ), - Expanded( - child: RefreshIndicator( - onRefresh: () async { - await _whisperController.onRefresh(); - }, - child: SingleChildScrollView( - controller: _scrollController, - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - RxList sessionList = _whisperController.sessionList; - return Obx( - () => sessionList.isEmpty - ? const SizedBox() - : ListView.separated( - itemCount: sessionList.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (_, int i) { - return SessionItem( - sessionItem: sessionList[i], - changeFucCall: () => - sessionList.refresh(), - ); - }, - separatorBuilder: - (BuildContext context, int index) { - return Divider( - indent: 72, - endIndent: 20, - height: 6, - color: Colors.grey.withOpacity(0.1), - ); - }, + body: RefreshIndicator( + onRefresh: () async { + _whisperController.unread(); + await _whisperController.onRefresh(); + }, + child: SingleChildScrollView( + controller: _scrollController, + child: Column( + children: [ + LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + // 在这里根据父级容器的约束条件构建小部件树 + return Padding( + padding: const EdgeInsets.only(left: 20, right: 20), + child: SizedBox( + height: constraints.maxWidth / 4, + child: Obx( + () => GridView.count( + primary: false, + crossAxisCount: 4, + padding: const EdgeInsets.all(0), + children: [ + ..._whisperController.noticesList.map((element) { + return InkWell( + onTap: () => {}, + onLongPress: () {}, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Badge( + isLabelVisible: element['count'] > 0, + label: Text(element['count'] > 99 + ? '99+' + : element['count'].toString()), + child: Padding( + padding: const EdgeInsets.all(10), + child: Icon( + element['icon'], + size: 21, + color: Theme.of(context) + .colorScheme + .primary, + ), + ), + ), + const SizedBox(height: 4), + Text(element['title']) + ], ), - ); - } else { - // 请求错误 - return Center( - child: Text(data?['msg'] ?? '请求异常'), - ); - } + ); + }).toList(), + ], + ), + ), + ), + ); + }, + ), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + RxList sessionList = _whisperController.sessionList; + return Obx( + () => sessionList.isEmpty + ? const SizedBox() + : ListView.separated( + itemCount: sessionList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return SessionItem( + sessionItem: sessionList[i], + changeFucCall: () => sessionList.refresh(), + ); + }, + separatorBuilder: + (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); } else { - // 骨架屏 - return ListView.builder( - itemCount: 15, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, int i) { - return Skeleton( - child: ListTile( - leading: Container( - width: 45, - height: 45, - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .onInverseSurface, - borderRadius: BorderRadius.circular(25), - ), - ), - title: Container( - width: 100, - height: 14, - color: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - subtitle: Container( - width: 80, - height: 14, - color: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - ), - ); - }, + // 请求错误 + return Center( + child: Text(data?['msg'] ?? '请求异常'), ); } - }, - ), + } else { + // 骨架屏 + return ListView.builder( + itemCount: 15, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, int i) { + return Skeleton( + child: ListTile( + leading: Container( + width: 45, + height: 45, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .onInverseSurface, + borderRadius: BorderRadius.circular(25), + ), + ), + title: Container( + width: 100, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + subtitle: Container( + width: 80, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + ), + ); + }, + ); + } + }, ), - ), + ], ), - ], + ), ), ); } From 569f7c23fd2e64072fe905dbd0d098b1ebc66ec9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 13:58:43 +0800 Subject: [PATCH 31/34] =?UTF-8?q?feat:=20=E5=9B=9E=E5=A4=8D=E6=88=91?= =?UTF-8?q?=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 26 +++ lib/models/msg/reply.dart | 168 ++++++++++++++ lib/pages/message/reply/controller.dart | 25 +++ lib/pages/message/reply/index.dart | 4 + lib/pages/message/reply/view.dart | 285 ++++++++++++++++++++++++ lib/pages/whisper/controller.dart | 2 +- lib/pages/whisper/view.dart | 2 +- lib/router/app_pages.dart | 3 + 9 files changed, 516 insertions(+), 2 deletions(-) create mode 100644 lib/models/msg/reply.dart create mode 100644 lib/pages/message/reply/controller.dart create mode 100644 lib/pages/message/reply/index.dart create mode 100644 lib/pages/message/reply/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 198d6174..4db5994d 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -542,4 +542,7 @@ class Api { /// 消息未读数 static const String unread = '${HttpString.tUrl}/x/im/web/msgfeed/unread'; + + /// 回复我的 + static const String messageReplyAPi = '/x/msgfeed/reply'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 9a6e878b..20905386 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:math'; import 'package:dio/dio.dart'; +import 'package:pilipala/models/msg/reply.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; import '../utils/wbi_sign.dart'; @@ -237,4 +238,29 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + // 回复我的 + static Future messageReply({ + int? id, + int? replyTime, + }) async { + var params = { + if (id != null) 'id': id, + if (replyTime != null) 'reply_time': replyTime, + }; + var res = await Request().get(Api.messageReplyAPi, data: params); + if (res.data['code'] == 0) { + try { + return { + 'status': true, + 'data': MessageReplyModel.fromJson(res.data['data']), + }; + } catch (err) { + print(err); + return {'status': false, 'date': [], 'msg': err.toString()}; + } + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/models/msg/reply.dart b/lib/models/msg/reply.dart new file mode 100644 index 00000000..be617088 --- /dev/null +++ b/lib/models/msg/reply.dart @@ -0,0 +1,168 @@ +class MessageReplyModel { + MessageReplyModel({ + this.cursor, + this.items, + }); + + Cursor? cursor; + List? items; + + MessageReplyModel.fromJson(Map json) { + cursor = Cursor.fromJson(json['cursor']); + items = json["items"] != null + ? json["items"].map((e) { + return MessageReplyItem.fromJson(e); + }).toList() + : []; + } +} + +class Cursor { + Cursor({ + this.id, + this.isEnd, + this.time, + }); + + int? id; + bool? isEnd; + int? time; + + Cursor.fromJson(Map json) { + id = json['id']; + isEnd = json['is_end']; + time = json['time']; + } +} + +class MessageReplyItem { + MessageReplyItem({ + this.count, + this.id, + this.isMulti, + this.item, + this.replyTime, + this.user, + }); + + int? count; + int? id; + int? isMulti; + ReplyContentItem? item; + int? replyTime; + ReplyUser? user; + + MessageReplyItem.fromJson(Map json) { + count = json['count']; + id = json['id']; + isMulti = json['is_multi']; + item = ReplyContentItem.fromJson(json["item"]); + replyTime = json['reply_time']; + user = ReplyUser.fromJson(json['user']); + } +} + +class ReplyContentItem { + ReplyContentItem({ + this.subjectId, + this.rootId, + this.sourceId, + this.targetId, + this.type, + this.businessId, + this.business, + this.title, + this.desc, + this.image, + this.uri, + this.nativeUri, + this.detailTitle, + this.rootReplyContent, + this.sourceContent, + this.targetReplyContent, + this.atDetails, + this.topicDetails, + this.hideReplyButton, + this.hideLikeButton, + this.likeState, + this.danmu, + this.message, + }); + + int? subjectId; + int? rootId; + int? sourceId; + int? targetId; + String? type; + int? businessId; + String? business; + String? title; + String? desc; + String? image; + String? uri; + String? nativeUri; + String? detailTitle; + String? rootReplyContent; + String? sourceContent; + String? targetReplyContent; + List? atDetails; + List? topicDetails; + bool? hideReplyButton; + bool? hideLikeButton; + int? likeState; + String? danmu; + String? message; + + ReplyContentItem.fromJson(Map json) { + subjectId = json['subject_id']; + rootId = json['root_id']; + sourceId = json['source_id']; + targetId = json['target_id']; + type = json['type']; + businessId = json['business_id']; + business = json['business']; + title = json['title']; + desc = json['desc']; + image = json['image']; + uri = json['uri']; + nativeUri = json['native_uri']; + detailTitle = json['detail_title']; + rootReplyContent = json['root_reply_content']; + sourceContent = json['source_content']; + targetReplyContent = json['target_reply_content']; + atDetails = json['at_details']; + topicDetails = json['topic_details']; + hideReplyButton = json['hide_reply_button']; + hideLikeButton = json['hide_like_button']; + likeState = json['like_state']; + danmu = json['danmu']; + message = json['message']; + } +} + +class ReplyUser { + ReplyUser({ + this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow, + }); + + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + ReplyUser.fromJson(Map json) { + mid = json['mid']; + fans = json['fans']; + nickname = json['nickname']; + avatar = json['avatar']; + midLink = json['mid_link']; + follow = json['follow']; + } +} diff --git a/lib/pages/message/reply/controller.dart b/lib/pages/message/reply/controller.dart new file mode 100644 index 00000000..d62662df --- /dev/null +++ b/lib/pages/message/reply/controller.dart @@ -0,0 +1,25 @@ +import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import 'package:pilipala/models/msg/reply.dart'; + +class MessageReplyController extends GetxController { + Cursor? cursor; + RxList replyItems = [].obs; + + Future queryMessageReply({String type = 'init'}) async { + if (cursor != null && cursor!.isEnd == true) { + return {}; + } + var params = { + if (type == 'onLoad') 'id': cursor!.id, + if (type == 'onLoad') 'replyTime': cursor!.time, + }; + var res = await MsgHttp.messageReply( + id: params['id'], replyTime: params['replyTime']); + if (res['status']) { + cursor = res['data'].cursor; + replyItems.addAll(res['data'].items); + } + return res; + } +} diff --git a/lib/pages/message/reply/index.dart b/lib/pages/message/reply/index.dart new file mode 100644 index 00000000..969d03dd --- /dev/null +++ b/lib/pages/message/reply/index.dart @@ -0,0 +1,4 @@ +library message_reply; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/reply/view.dart b/lib/pages/message/reply/view.dart new file mode 100644 index 00000000..c4c02f6c --- /dev/null +++ b/lib/pages/message/reply/view.dart @@ -0,0 +1,285 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/msg/reply.dart'; +import 'package:pilipala/utils/utils.dart'; + +import 'controller.dart'; + +class MessageReplyPage extends StatefulWidget { + const MessageReplyPage({super.key}); + + @override + State createState() => _MessageReplyPageState(); +} + +class _MessageReplyPageState extends State { + final MessageReplyController _messageReplyCtr = + Get.put(MessageReplyController()); + late Future _futureBuilderFuture; + final ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _messageReplyCtr.queryMessageReply(); + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle('follow', const Duration(seconds: 1), () { + _messageReplyCtr.queryMessageReply(type: 'onLoad'); + }); + } + }, + ); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('回复我的'), + ), + body: RefreshIndicator( + onRefresh: () async { + await _messageReplyCtr.queryMessageReply(type: 'init'); + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + final replyItems = _messageReplyCtr.replyItems; + return Obx( + () => ListView.separated( + controller: scrollController, + itemBuilder: (context, index) => + ReplyItem(item: replyItems[index]), + itemCount: replyItems.length, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 66, + endIndent: 14, + height: 1, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return CustomScrollView( + slivers: [ + HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageReplyCtr.queryMessageReply(); + }); + }, + ) + ], + ); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ); + } +} + +class ReplyItem extends StatelessWidget { + final MessageReplyItem item; + + const ReplyItem({super.key, required this.item}); + + @override + Widget build(BuildContext context) { + final String heroTag = Utils.makeHeroTag(item.user!.mid); + final String bvid = item.item!.uri!.split('/').last; + // 页码 + final String page = + item.item!.nativeUri!.split('page=').last.split('&').first; + // 根评论id + final String commentRootId = + item.item!.nativeUri!.split('comment_root_id=').last.split('&').first; + // 二级评论id + final String commentSecondaryId = + item.item!.nativeUri!.split('comment_secondary_id=').last; + + return InkWell( + onTap: () async { + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': '', + 'heroTag': heroTag, + }, + ); + }, + child: Padding( + padding: const EdgeInsets.all(14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + Get.toNamed('/member?mid=${item.user!.mid}', + arguments: {'face': item.user!.avatar, 'heroTag': heroTag}); + }, + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + width: 42, + height: 42, + type: 'avatar', + src: item.user!.avatar, + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich(TextSpan(children: [ + TextSpan(text: item.user!.nickname!), + const TextSpan(text: ' '), + if (item.item!.type! == 'video') + TextSpan( + text: '对我的视频发表了评论', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + if (item.item!.type! == 'reply') + TextSpan( + text: '回复了我的评论', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ])), + const SizedBox(height: 6), + Text.rich( + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle(letterSpacing: 0.3), + buildContent(context, item.item)), + if (item.item!.targetReplyContent != '') ...[ + const SizedBox(height: 2), + Text( + item.item!.targetReplyContent!, + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ], + const SizedBox(height: 4), + Row( + children: [ + Text( + Utils.dateFormat(item.replyTime!, formatType: 'detail'), + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + const SizedBox(width: 16), + Text( + '回复', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ], + ) + ], + ), + ), + // Spacer(), + const SizedBox(width: 25), + if (item.item!.type! == 'reply') + Container( + width: 60, + height: 80, + padding: const EdgeInsets.all(4), + child: Text( + item.item!.rootReplyContent!, + maxLines: 4, + style: const TextStyle( + fontSize: 12, + letterSpacing: 0.3, + ), + overflow: TextOverflow.ellipsis, + ), + ), + if (item.item!.type! == 'video') + NetworkImgLayer( + width: 60, + height: 60, + src: item.item!.image, + ), + ], + ), + ), + ); + } +} + +InlineSpan buildContent(BuildContext context, item) { + List? atDetails = item!.atDetails; + final List spanChilds = []; + if (atDetails!.isNotEmpty) { + final String patternStr = + atDetails.map((e) => '@${e['nickname']}').toList().join('|'); + final RegExp regExp = RegExp(patternStr); + item.sourceContent!.splitMapJoin( + regExp, + onMatch: (Match match) { + spanChilds.add( + TextSpan( + text: match.group(0), + recognizer: TapGestureRecognizer() + ..onTap = () { + var currentUser = atDetails + .where((e) => e['nickname'] == match.group(0)!.substring(1)) + .first; + Get.toNamed('/member?mid=${currentUser['mid']}', arguments: { + 'face': currentUser['avatar'], + }); + }, + style: TextStyle(color: Theme.of(context).colorScheme.primary), + ), + ); + return ''; + }, + onNonMatch: (String nonMatch) { + spanChilds.add( + TextSpan(text: nonMatch), + ); + return ''; + }, + ); + } else { + spanChilds.add( + TextSpan(text: item.sourceContent), + ); + } + + return TextSpan(children: spanChilds); +} diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index e00c990e..f3cc47d6 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -12,7 +12,7 @@ class WhisperController extends GetxController { { 'icon': Icons.message_outlined, 'title': '回复我的', - 'path': '', + 'path': '/messageReply', 'count': 0, }, { diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 8070498e..fccdd844 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -69,7 +69,7 @@ class _WhisperPageState extends State { children: [ ..._whisperController.noticesList.map((element) { return InkWell( - onTap: () => {}, + onTap: () => Get.toNamed(element['path']), onLongPress: () {}, borderRadius: StyleString.mdRadius, child: Column( diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 2ca333f8..bb036c6e 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/pages/follow_search/view.dart'; +import 'package:pilipala/pages/message/reply/index.dart'; import 'package:pilipala/pages/setting/pages/logs.dart'; import '../pages/about/index.dart'; @@ -178,6 +179,8 @@ class Routes { // 操作菜单 CustomGetPage( name: '/actionMenuSet', page: () => const ActionMenuSetPage()), + // 回复我的 + CustomGetPage(name: '/messageReply', page: () => const MessageReplyPage()), ]; } From 779f7e76662fbadc562bff5f13c3b5c0f4858386 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 17:53:46 +0800 Subject: [PATCH 32/34] =?UTF-8?q?feat:=20=E6=94=B6=E5=88=B0=E7=9A=84?= =?UTF-8?q?=E8=B5=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 26 +- lib/models/msg/like.dart | 183 +++++++++++++ lib/pages/message/at/controller.dart | 3 + lib/pages/message/at/index.dart | 4 + lib/pages/message/at/view.dart | 19 ++ lib/pages/message/like/controller.dart | 30 +++ lib/pages/message/like/index.dart | 4 + lib/pages/message/like/view.dart | 319 +++++++++++++++++++++++ lib/pages/message/reply/view.dart | 27 +- lib/pages/message/system/controller.dart | 3 + lib/pages/message/system/index.dart | 4 + lib/pages/message/system/view.dart | 19 ++ lib/pages/whisper/controller.dart | 6 +- lib/router/app_pages.dart | 10 + 15 files changed, 636 insertions(+), 24 deletions(-) create mode 100644 lib/models/msg/like.dart create mode 100644 lib/pages/message/at/controller.dart create mode 100644 lib/pages/message/at/index.dart create mode 100644 lib/pages/message/at/view.dart create mode 100644 lib/pages/message/like/controller.dart create mode 100644 lib/pages/message/like/index.dart create mode 100644 lib/pages/message/like/view.dart create mode 100644 lib/pages/message/system/controller.dart create mode 100644 lib/pages/message/system/index.dart create mode 100644 lib/pages/message/system/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 4db5994d..f20b8bcf 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -545,4 +545,7 @@ class Api { /// 回复我的 static const String messageReplyAPi = '/x/msgfeed/reply'; + + /// 收到的赞 + static const String messageLikeAPi = '/x/msgfeed/like'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 20905386..7c168230 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:math'; import 'package:dio/dio.dart'; +import 'package:pilipala/models/msg/like.dart'; import 'package:pilipala/models/msg/reply.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; @@ -256,7 +257,30 @@ class MsgHttp { 'data': MessageReplyModel.fromJson(res.data['data']), }; } catch (err) { - print(err); + return {'status': false, 'date': [], 'msg': err.toString()}; + } + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } + + // 收到的赞 + static Future messageLike({ + int? id, + int? likeTime, + }) async { + var params = { + if (id != null) 'id': id, + if (likeTime != null) 'like_time': likeTime, + }; + var res = await Request().get(Api.messageLikeAPi, data: params); + if (res.data['code'] == 0) { + try { + return { + 'status': true, + 'data': MessageLikeModel.fromJson(res.data['data']), + }; + } catch (err) { return {'status': false, 'date': [], 'msg': err.toString()}; } } else { diff --git a/lib/models/msg/like.dart b/lib/models/msg/like.dart new file mode 100644 index 00000000..b279131b --- /dev/null +++ b/lib/models/msg/like.dart @@ -0,0 +1,183 @@ +class MessageLikeModel { + MessageLikeModel({ + this.latest, + this.total, + }); + + Latest? latest; + Total? total; + + factory MessageLikeModel.fromJson(Map json) => + MessageLikeModel( + latest: json["latest"] == null ? null : Latest.fromJson(json["latest"]), + total: json["total"] == null ? null : Total.fromJson(json["total"]), + ); +} + +class Latest { + Latest({ + this.items, + this.lastViewAt, + }); + + List? items; + int? lastViewAt; + + factory Latest.fromJson(Map json) => Latest( + items: json["items"], + lastViewAt: json["last_view_at"], + ); +} + +class Total { + Total({ + this.cursor, + this.items, + }); + + Cursor? cursor; + List? items; + + factory Total.fromJson(Map json) => Total( + cursor: Cursor.fromJson(json['cursor']), + items: json["items"] == null + ? [] + : json["items"].map((e) { + return MessageLikeItem.fromJson(e); + }).toList(), + ); +} + +class Cursor { + Cursor({ + this.id, + this.isEnd, + this.time, + }); + + int? id; + bool? isEnd; + int? time; + + factory Cursor.fromJson(Map json) => Cursor( + id: json['id'], + isEnd: json['is_end'], + time: json['time'], + ); +} + +class MessageLikeItem { + MessageLikeItem({ + this.id, + this.users, + this.item, + this.counts, + this.likeTime, + this.noticeState, + this.isExpand = false, + }); + + int? id; + List? users; + MessageLikeItemItem? item; + int? counts; + int? likeTime; + int? noticeState; + bool isExpand; + + factory MessageLikeItem.fromJson(Map json) => + MessageLikeItem( + id: json["id"], + users: json["users"] == null + ? [] + : json["users"].map((e) { + return MessageLikeUser.fromJson(e); + }).toList(), + item: json["item"] == null + ? null + : MessageLikeItemItem.fromJson(json["item"]), + counts: json["counts"], + likeTime: json["like_time"], + noticeState: json["notice_state"], + ); +} + +class MessageLikeUser { + MessageLikeUser({ + this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow, + }); + + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + factory MessageLikeUser.fromJson(Map json) => + MessageLikeUser( + mid: json["mid"], + fans: json["fans"], + nickname: json["nickname"], + avatar: json["avatar"], + midLink: json["mid_link"], + follow: json["follow"], + ); +} + +class MessageLikeItemItem { + MessageLikeItemItem({ + this.itemId, + this.pid, + this.type, + this.business, + this.businessId, + this.replyBusinessId, + this.likeBusinessId, + this.title, + this.desc, + this.image, + this.uri, + this.detailName, + this.nativeUri, + this.ctime, + }); + + int? itemId; + int? pid; + String? type; + String? business; + int? businessId; + int? replyBusinessId; + int? likeBusinessId; + String? title; + String? desc; + String? image; + String? uri; + String? detailName; + String? nativeUri; + int? ctime; + + factory MessageLikeItemItem.fromJson(Map json) => + MessageLikeItemItem( + itemId: json["item_id"], + pid: json["pid"], + type: json["type"], + business: json["business"], + businessId: json["business_id"], + replyBusinessId: json["reply_business_id"], + likeBusinessId: json["like_business_id"], + title: json["title"], + desc: json["desc"], + image: json["image"], + uri: json["uri"], + detailName: json["detail_name"], + nativeUri: json["native_uri"], + ctime: json["ctime"], + ); +} diff --git a/lib/pages/message/at/controller.dart b/lib/pages/message/at/controller.dart new file mode 100644 index 00000000..af08987f --- /dev/null +++ b/lib/pages/message/at/controller.dart @@ -0,0 +1,3 @@ +import 'package:get/get.dart'; + +class MessageAtController extends GetxController {} diff --git a/lib/pages/message/at/index.dart b/lib/pages/message/at/index.dart new file mode 100644 index 00000000..b2c573e6 --- /dev/null +++ b/lib/pages/message/at/index.dart @@ -0,0 +1,4 @@ +library message_at; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/at/view.dart b/lib/pages/message/at/view.dart new file mode 100644 index 00000000..9c48ec99 --- /dev/null +++ b/lib/pages/message/at/view.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class MessageAtPage extends StatefulWidget { + const MessageAtPage({super.key}); + + @override + State createState() => _MessageAtPageState(); +} + +class _MessageAtPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('@我的'), + ), + ); + } +} diff --git a/lib/pages/message/like/controller.dart b/lib/pages/message/like/controller.dart new file mode 100644 index 00000000..9b09f89a --- /dev/null +++ b/lib/pages/message/like/controller.dart @@ -0,0 +1,30 @@ +import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import 'package:pilipala/models/msg/like.dart'; + +class MessageLikeController extends GetxController { + Cursor? cursor; + RxList likeItems = [].obs; + + Future queryMessageLike({String type = 'init'}) async { + if (cursor != null && cursor!.isEnd == true) { + return {}; + } + var params = { + if (type == 'onLoad') 'id': cursor!.id, + if (type == 'onLoad') 'likeTime': cursor!.time, + }; + var res = await MsgHttp.messageLike( + id: params['id'], likeTime: params['likeTime']); + if (res['status']) { + cursor = res['data'].total.cursor; + likeItems.addAll(res['data'].total.items); + } + return res; + } + + Future expandedUsersAvatar(i) async { + likeItems[i].isExpand = !likeItems[i].isExpand; + likeItems.refresh(); + } +} diff --git a/lib/pages/message/like/index.dart b/lib/pages/message/like/index.dart new file mode 100644 index 00000000..4f9c143a --- /dev/null +++ b/lib/pages/message/like/index.dart @@ -0,0 +1,4 @@ +library message_like; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart new file mode 100644 index 00000000..e677fb25 --- /dev/null +++ b/lib/pages/message/like/view.dart @@ -0,0 +1,319 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/msg/like.dart'; +import 'package:pilipala/utils/utils.dart'; + +import 'controller.dart'; + +class MessageLikePage extends StatefulWidget { + const MessageLikePage({super.key}); + + @override + State createState() => _MessageLikePageState(); +} + +class _MessageLikePageState extends State { + final MessageLikeController _messageLikeCtr = + Get.put(MessageLikeController()); + late Future _futureBuilderFuture; + final ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _messageLikeCtr.queryMessageLike(); + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle('follow', const Duration(seconds: 1), () { + _messageLikeCtr.queryMessageLike(type: 'onLoad'); + }); + } + }, + ); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('收到的赞'), + ), + body: RefreshIndicator( + onRefresh: () async { + await _messageLikeCtr.queryMessageLike(type: 'init'); + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + final likeItems = _messageLikeCtr.likeItems; + return Obx( + () => ListView.separated( + controller: scrollController, + itemBuilder: (context, index) => LikeItem( + item: likeItems[index], + index: index, + messageLikeCtr: _messageLikeCtr, + ), + itemCount: likeItems.length, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 66, + endIndent: 14, + height: 1, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return CustomScrollView( + slivers: [ + HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageLikeCtr.queryMessageLike(); + }); + }, + ) + ], + ); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ); + } +} + +class LikeItem extends StatelessWidget { + final MessageLikeItem item; + final int index; + final MessageLikeController messageLikeCtr; + + const LikeItem( + {super.key, + required this.item, + required this.index, + required this.messageLikeCtr}); + + @override + Widget build(BuildContext context) { + Color outline = Theme.of(context).colorScheme.outline; + final nickNameList = item.users!.map((e) => e.nickname).take(2).toList(); + int usersLen = item.users!.length > 3 ? 3 : item.users!.length; + final String bvid = item.item!.uri!.split('/').last; + // 页码 + final String page = + item.item!.nativeUri!.split('page=').last.split('&').first; + // 根评论id + final String commentRootId = + item.item!.nativeUri!.split('comment_root_id=').last.split('&').first; + // 二级评论id + final String commentSecondaryId = + item.item!.nativeUri!.split('comment_secondary_id=').last; + + return InkWell( + onTap: () async { + try { + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': '', + 'heroTag': heroTag, + }, + ); + } catch (_) { + SmartDialog.showToast('视频可能失效了'); + } + }, + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + if (usersLen == 1) { + final String heroTag = + Utils.makeHeroTag(item.users!.first.mid); + Get.toNamed('/member?mid=${item.users!.first.mid}', + arguments: { + 'face': item.users!.first.avatar, + 'heroTag': heroTag + }); + } else { + messageLikeCtr.expandedUsersAvatar(index); + } + }, + // 多个头像层叠 + child: SizedBox( + width: 50, + height: 50, + child: Stack( + children: [ + for (var i = 0; i < usersLen; i++) + Positioned( + top: i % 2 * (50 / (usersLen >= 2 ? 2 : 1)), + left: i / 2 * (50 / (usersLen >= 2 ? 2 : 1)), + child: NetworkImgLayer( + width: 50 / (usersLen >= 2 ? 2 : 1), + height: 50 / (usersLen >= 2 ? 2 : 1), + type: 'avatar', + src: item.users![i].avatar, + ), + ), + ], + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich(TextSpan(children: [ + TextSpan(text: nickNameList.join('、')), + const TextSpan(text: ' '), + if (item.users!.length > 1) + TextSpan( + text: '等总计${item.users!.length}人', + style: TextStyle(color: outline), + ), + TextSpan( + text: '赞了我的评论', + style: TextStyle(color: outline), + ), + ])), + const SizedBox(height: 4), + Text( + Utils.dateFormat(item.likeTime!, formatType: 'detail'), + style: TextStyle(color: outline), + ), + ], + ), + ), + const SizedBox(width: 25), + if (item.item!.type! == 'reply') + Container( + width: 60, + height: 60, + padding: const EdgeInsets.all(4), + child: Text( + item.item!.title!, + maxLines: 4, + style: const TextStyle(fontSize: 12, letterSpacing: 0.3), + overflow: TextOverflow.ellipsis, + ), + ), + if (item.item!.type! == 'video') + NetworkImgLayer( + width: 60, + height: 60, + src: item.item!.image, + ), + ], + ), + ), + Positioned( + top: 0, + right: 0, + bottom: 0, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + width: item.isExpand ? Get.size.width - 74 : 0, + color: Theme.of(context).colorScheme.secondaryContainer, + child: ListView.builder( + itemCount: item.users!.length, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int i) { + return Padding( + padding: EdgeInsets.fromLTRB(i == 0 ? 12 : 4, 8, 4, 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + final String heroTag = + Utils.makeHeroTag(item.users![i].mid); + Get.toNamed( + '/member?mid=${item.users![i].mid}', + arguments: { + 'face': item.users![i].avatar, + 'heroTag': heroTag + }, + ); + }, + child: NetworkImgLayer( + width: 42, + height: 42, + type: 'avatar', + src: item.users![i].avatar, + ), + ), + const SizedBox(height: 6), + SizedBox( + width: 68, + child: Text( + textAlign: TextAlign.center, + item.users![i].nickname!, + maxLines: 1, + overflow: TextOverflow.clip, + style: TextStyle(color: outline), + ), + ), + ], + ), + ); + }, + ), + ), + ), + Positioned( + top: 0, + left: 0, + bottom: 0, + child: GestureDetector( + onTap: () { + messageLikeCtr.expandedUsersAvatar(index); + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + width: item.isExpand ? 74 : 0, + color: Colors.black.withOpacity(0.3), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/message/reply/view.dart b/lib/pages/message/reply/view.dart index c4c02f6c..881f8650 100644 --- a/lib/pages/message/reply/view.dart +++ b/lib/pages/message/reply/view.dart @@ -113,6 +113,7 @@ class ReplyItem extends StatelessWidget { @override Widget build(BuildContext context) { + Color outline = Theme.of(context).colorScheme.outline; final String heroTag = Utils.makeHeroTag(item.user!.mid); final String bvid = item.item!.uri!.split('/').last; // 页码 @@ -167,15 +168,11 @@ class ReplyItem extends StatelessWidget { const TextSpan(text: ' '), if (item.item!.type! == 'video') TextSpan( - text: '对我的视频发表了评论', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), + text: '对我的视频发表了评论', style: TextStyle(color: outline)), if (item.item!.type! == 'reply') TextSpan( text: '回复了我的评论', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: outline), ), ])), const SizedBox(height: 6), @@ -188,8 +185,7 @@ class ReplyItem extends StatelessWidget { const SizedBox(height: 2), Text( item.item!.targetReplyContent!, - style: TextStyle( - color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: outline), ), ], const SizedBox(height: 4), @@ -197,21 +193,15 @@ class ReplyItem extends StatelessWidget { children: [ Text( Utils.dateFormat(item.replyTime!, formatType: 'detail'), - style: TextStyle( - color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: outline), ), const SizedBox(width: 16), - Text( - '回复', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), + Text('回复', style: TextStyle(color: outline)), ], ) ], ), ), - // Spacer(), const SizedBox(width: 25), if (item.item!.type! == 'reply') Container( @@ -221,10 +211,7 @@ class ReplyItem extends StatelessWidget { child: Text( item.item!.rootReplyContent!, maxLines: 4, - style: const TextStyle( - fontSize: 12, - letterSpacing: 0.3, - ), + style: const TextStyle(fontSize: 12, letterSpacing: 0.3), overflow: TextOverflow.ellipsis, ), ), diff --git a/lib/pages/message/system/controller.dart b/lib/pages/message/system/controller.dart new file mode 100644 index 00000000..ad28af56 --- /dev/null +++ b/lib/pages/message/system/controller.dart @@ -0,0 +1,3 @@ +import 'package:get/get.dart'; + +class MessageSystemController extends GetxController {} diff --git a/lib/pages/message/system/index.dart b/lib/pages/message/system/index.dart new file mode 100644 index 00000000..70b9fc6d --- /dev/null +++ b/lib/pages/message/system/index.dart @@ -0,0 +1,4 @@ +library message_system; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/system/view.dart b/lib/pages/message/system/view.dart new file mode 100644 index 00000000..da0f1219 --- /dev/null +++ b/lib/pages/message/system/view.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class MessageSystemPage extends StatefulWidget { + const MessageSystemPage({super.key}); + + @override + State createState() => _MessageSystemPageState(); +} + +class _MessageSystemPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('系统通知'), + ), + ); + } +} diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index f3cc47d6..2614bf5a 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -18,19 +18,19 @@ class WhisperController extends GetxController { { 'icon': Icons.alternate_email, 'title': '@ 我的', - 'path': '', + 'path': '/messageAt', 'count': 0, }, { 'icon': Icons.thumb_up_outlined, 'title': '收到的赞', - 'path': '', + 'path': '/messageLike', 'count': 0, }, { 'icon': Icons.notifications_none_outlined, 'title': '系统通知', - 'path': '', + 'path': '/messageSystem', 'count': 0, } ].obs; diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index bb036c6e..a6b48f0d 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -4,7 +4,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/pages/follow_search/view.dart'; +import 'package:pilipala/pages/message/at/index.dart'; +import 'package:pilipala/pages/message/like/index.dart'; import 'package:pilipala/pages/message/reply/index.dart'; +import 'package:pilipala/pages/message/system/index.dart'; import 'package:pilipala/pages/setting/pages/logs.dart'; import '../pages/about/index.dart'; @@ -181,6 +184,13 @@ class Routes { name: '/actionMenuSet', page: () => const ActionMenuSetPage()), // 回复我的 CustomGetPage(name: '/messageReply', page: () => const MessageReplyPage()), + // @我的 + CustomGetPage(name: '/messageAt', page: () => const MessageAtPage()), + // 收到的赞 + CustomGetPage(name: '/messageLike', page: () => const MessageLikePage()), + // 系统通知 + CustomGetPage( + name: '/messageSystem', page: () => const MessageSystemPage()), ]; } From a2210f17f64b65008481d05503f8c8219d4c2dfa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 18:24:31 +0800 Subject: [PATCH 33/34] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E9=87=8D=E5=A4=8D=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index a83b7809..023e52be 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -196,9 +196,7 @@ class PiliSchame { parameters: {'url': redirectUrl, 'type': 'url', 'pageTitle': ''}, ); } - } - - if (path != null) { + } else if (path != null) { final String area = path.split('/').last; switch (area) { case 'bangumi': From 4591593bb56a36539700383404823355410244c5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 18:34:32 +0800 Subject: [PATCH 34/34] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E8=A1=A8?= =?UTF-8?q?=E6=83=85=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_new/view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 40267559..0f04455f 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -117,6 +117,7 @@ class _VideoReplyNewDialogState extends State final String newText = currentText.substring(0, cursorPosition) + emote.text! + currentText.substring(cursorPosition); + message.value = newText; _replyContentController.value = TextEditingValue( text: newText, selection: