From 41ddeab41aeecca0b1bb966c0e734110db487566 Mon Sep 17 00:00:00 2001 From: orz12 Date: Sat, 20 Jan 2024 15:14:52 +0800 Subject: [PATCH 001/102] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=A8=A1=E6=8B=9F?= =?UTF-8?q?=E6=9C=AA=E7=99=BB=E5=BD=95=E6=8E=A8=E8=8D=90=EF=BC=8C=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E6=8E=A8=E8=8D=90=E8=AE=BE=E7=BD=AE=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9Eaccesskey=E9=A3=8E=E6=8E=A7=E8=AD=A6=E5=91=8A=EF=BC=8C?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E6=8E=A8=E8=8D=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 18 ++- lib/models/common/rcmd_type.dart | 6 +- lib/pages/rcmd/controller.dart | 91 ++++++--------- lib/pages/rcmd/view.dart | 60 ++-------- lib/pages/setting/extra_setting.dart | 62 ---------- lib/pages/setting/recommend_setting.dart | 137 +++++++++++++++++++++++ lib/pages/setting/view.dart | 5 + lib/router/app_pages.dart | 4 +- lib/utils/storage.dart | 8 +- 9 files changed, 210 insertions(+), 181 deletions(-) create mode 100644 lib/pages/setting/recommend_setting.dart diff --git a/lib/http/video.dart b/lib/http/video.dart index 923e93a2..39c961cd 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -46,7 +46,9 @@ class VideoHttp { setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); for (var i in res.data['data']['item']) { //过滤掉live与ad,以及拉黑用户 - if (i['goto'] == 'av' && !blackMidsList.contains(i['owner']['mid'])) { + if (i['goto'] == 'av' && + (i['owner'] != null && + !blackMidsList.contains(i['owner']['mid']))) { list.add(RecVideoItemModel.fromJson(i)); } } @@ -59,7 +61,9 @@ class VideoHttp { } } - static Future rcmdVideoListApp({int? ps, required int freshIdx}) async { + // 添加额外的loginState变量模拟未登录状态 + static Future rcmdVideoListApp( + {bool loginStatus = true, required int freshIdx}) async { try { var res = await Request().get( Api.recommendListApp, @@ -72,9 +76,11 @@ class VideoHttp { 'device_name': 'vivo', 'pull': freshIdx == 0 ? 'true' : 'false', 'appkey': Constants.appKey, - 'access_key': localCache - .get(LocalCacheKey.accessKey, defaultValue: {})['value'] ?? - '' + 'access_key': loginStatus + ? (localCache.get(LocalCacheKey.accessKey, + defaultValue: {})['value'] ?? + '') + : '' }, ); if (res.data['code'] == 0) { @@ -92,7 +98,7 @@ class VideoHttp { } return {'status': true, 'data': list}; } else { - return {'status': false, 'data': [], 'msg': ''}; + return {'status': false, 'data': [], 'msg': res.data['message']}; } } catch (err) { return {'status': false, 'data': [], 'msg': err.toString()}; diff --git a/lib/models/common/rcmd_type.dart b/lib/models/common/rcmd_type.dart index dbb64b15..5af18d77 100644 --- a/lib/models/common/rcmd_type.dart +++ b/lib/models/common/rcmd_type.dart @@ -1,7 +1,7 @@ // 首页推荐类型 -enum RcmdType { web, app } +enum RcmdType { web, app, notLogin } extension RcmdTypeExtension on RcmdType { - String get values => ['web', 'app'][index]; - String get labels => ['web端', 'app端'][index]; + String get values => ['web', 'app', 'notLogin'][index]; + String get labels => ['web端', 'app端', '模拟未登录'][index]; } diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index 4ed1ade1..ed98606a 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -9,8 +9,8 @@ import 'package:pilipala/utils/storage.dart'; class RcmdController extends GetxController { final ScrollController scrollController = ScrollController(); int _currentPage = 0; - RxList appVideoList = [].obs; - RxList webVideoList = [].obs; + // RxList appVideoList = [].obs; + // RxList webVideoList = [].obs; bool isLoadingMore = true; OverlayEntry? popupDialog; Box recVideo = GStrorage.recVideo; @@ -18,6 +18,7 @@ class RcmdController extends GetxController { RxInt crossAxisCount = 2.obs; late bool enableSaveLastData; late String defaultRcmdType = 'web'; + late RxList videoList; @override void onInit() { @@ -37,85 +38,59 @@ class RcmdController extends GetxController { setting.get(SettingBoxKey.enableSaveLastData, defaultValue: false); defaultRcmdType = setting.get(SettingBoxKey.defaultRcmdType, defaultValue: 'web'); + if (defaultRcmdType == 'web'){ + videoList = [].obs; + } else { + videoList = [].obs; + } } // 获取推荐 Future queryRcmdFeed(type) async { - print(defaultRcmdType); - if (defaultRcmdType == 'app') { - return await queryRcmdFeedApp(type); - } - if (defaultRcmdType == 'web') { - return await queryRcmdFeedWeb(type); - } - } - - // 获取app端推荐 - Future queryRcmdFeedApp(type) async { if (isLoadingMore == false) { return; } if (type == 'onRefresh') { _currentPage = 0; } - var res = await VideoHttp.rcmdVideoListApp( - freshIdx: _currentPage, - ); + late final Map res; + switch (defaultRcmdType) { + case 'app': case 'notLogin': + res = await VideoHttp.rcmdVideoListApp( + loginStatus: defaultRcmdType != 'notLogin', + freshIdx: _currentPage, + ); + break; + default: //'web' + res = await VideoHttp.rcmdVideoList( + freshIdx: _currentPage, + ps: 20, + ); + } if (res['status']) { if (type == 'init') { - if (appVideoList.isNotEmpty) { - appVideoList.addAll(res['data']); + if (videoList.isNotEmpty) { + videoList.addAll(res['data']); } else { - appVideoList.value = res['data']; + videoList.value = res['data']; } } else if (type == 'onRefresh') { if (enableSaveLastData) { - appVideoList.insertAll(0, res['data']); + videoList.insertAll(0, res['data']); } else { - appVideoList.value = res['data']; + videoList.value = res['data']; } } else if (type == 'onLoad') { - appVideoList.addAll(res['data']); + videoList.addAll(res['data']); } - recVideo.put('cacheList', res['data']); - _currentPage += 1; - } - isLoadingMore = false; - return res; - } - - // 获取web端推荐 - Future queryRcmdFeedWeb(type) async { - if (isLoadingMore == false) { - return; - } - if (type == 'onRefresh') { - _currentPage = 0; - } - var res = await VideoHttp.rcmdVideoList( - ps: 20, - freshIdx: _currentPage, - ); - if (res['status']) { - if (type == 'init') { - if (webVideoList.isNotEmpty) { - webVideoList.addAll(res['data']); - } else { - webVideoList.value = res['data']; - } - } else if (type == 'onRefresh') { - if (enableSaveLastData) { - webVideoList.insertAll(0, res['data']); - } else { - webVideoList.value = res['data']; - } - } else if (type == 'onLoad') { - webVideoList.addAll(res['data']); + if (defaultRcmdType != 'web') { + recVideo.put('cacheList', res['data']); } _currentPage += 1; + } else { + Get.snackbar('提示', res['msg']); } isLoadingMore = false; - return res; } // 下拉刷新 @@ -129,7 +104,7 @@ class RcmdController extends GetxController { queryRcmdFeed('onLoad'); } - // 返回顶部并刷新 + // 返回顶部 void animateToTop() async { if (scrollController.offset >= MediaQuery.of(Get.context!).size.height * 5) { diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index bc3c4a00..0a345511 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -91,54 +91,18 @@ class _RcmdPageState extends State SliverPadding( padding: const EdgeInsets.fromLTRB(0, StyleString.safeSpace, 0, 0), - sliver: FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { - return Platform.isAndroid || Platform.isIOS - ? Obx( - () => contentGrid( - _rcmdController, - _rcmdController.defaultRcmdType == 'web' - ? _rcmdController.webVideoList - : _rcmdController.appVideoList), - ) - : SliverLayoutBuilder( - builder: (context, boxConstraints) { - return Obx( - () => contentGrid( - _rcmdController, - _rcmdController.defaultRcmdType == 'web' - ? _rcmdController.webVideoList - : _rcmdController.appVideoList), - ); - }); - } else { - return HttpError( - errMsg: data['msg'], - fn: () { - setState(() { - _futureBuilderFuture = - _rcmdController.queryRcmdFeed('init'); - }); - }, - ); - } - } else { - // 缓存数据 - // if (_rcmdController.videoList.isNotEmpty) { - // return contentGrid( - // _rcmdController, _rcmdController.videoList); - // } - // // 骨架屏 - // else { - return contentGrid(_rcmdController, []); - // } - } - }, - ), + sliver: Obx(() { + // 使用Obx来监听数据的变化 + if (_rcmdController.isLoadingMore) { + // 如果正在加载,则显示骨架屏 + return contentGrid(_rcmdController, []); + } else { + // 显示视频列表 + return contentGrid( + _rcmdController, + _rcmdController.videoList); + } + }), ), LoadingMore(ctr: _rcmdController) ], diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index b4275815..b32a06f5 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; -import 'package:pilipala/http/member.dart'; import 'package:pilipala/models/common/dynamics_type.dart'; -import 'package:pilipala/models/common/rcmd_type.dart'; import 'package:pilipala/models/common/reply_sort_type.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; import 'package:pilipala/utils/storage.dart'; @@ -20,23 +18,16 @@ class ExtraSetting extends StatefulWidget { class _ExtraSettingState extends State { Box setting = GStrorage.setting; static Box localCache = GStrorage.localCache; - late dynamic defaultRcmdType; late dynamic defaultReplySort; late dynamic defaultDynamicType; late dynamic enableSystemProxy; late String defaultSystemProxyHost; late String defaultSystemProxyPort; - Box userInfoCache = GStrorage.userInfo; - var userInfo; bool userLogin = false; - var accessKeyInfo; @override void initState() { super.initState(); - // 首页默认推荐类型 - defaultRcmdType = - setting.get(SettingBoxKey.defaultRcmdType, defaultValue: 'web'); // 默认优先显示最新评论 defaultReplySort = setting.get(SettingBoxKey.replySortType, defaultValue: 0); @@ -49,9 +40,6 @@ class _ExtraSettingState extends State { localCache.get(LocalCacheKey.systemProxyHost, defaultValue: ''); defaultSystemProxyPort = localCache.get(LocalCacheKey.systemProxyPort, defaultValue: ''); - userInfo = userInfoCache.get('userInfoCache'); - userLogin = userInfo != null; - accessKeyInfo = localCache.get(LocalCacheKey.accessKey, defaultValue: null); } // 设置代理 @@ -159,12 +147,6 @@ class _ExtraSettingState extends State { setKey: SettingBoxKey.enableSearchWord, defaultVal: true, ), - const SetSwitchItem( - title: '推荐动态', - subTitle: '是否在推荐内容中展示动态', - setKey: SettingBoxKey.enableRcmdDynamic, - defaultVal: true, - ), const SetSwitchItem( title: '快速收藏', subTitle: '点按收藏至默认,长按选择文件夹', @@ -177,50 +159,6 @@ class _ExtraSettingState extends State { setKey: SettingBoxKey.enableWordRe, defaultVal: false, ), - const SetSwitchItem( - title: '首页推荐刷新', - subTitle: '下拉刷新时保留上次内容', - setKey: SettingBoxKey.enableSaveLastData, - defaultVal: false, - ), - ListTile( - dense: false, - title: Text('首页推荐类型', style: titleStyle), - subtitle: Text( - '当前使用「$defaultRcmdType端」推荐', - style: subTitleStyle, - ), - onTap: () async { - String? result = await showDialog( - context: context, - builder: (context) { - return SelectDialog( - title: '推荐类型', - value: defaultRcmdType, - values: RcmdType.values.map((e) { - return {'title': e.labels, 'value': e.values}; - }).toList(), - ); - }, - ); - if (result != null) { - if (result == 'app') { - // app端推荐需要access_key - if (accessKeyInfo == null) { - if (!userLogin) { - SmartDialog.showToast('请先登录'); - return; - } - await MemberHttp.cookieToKey(); - } - } - defaultRcmdType = result; - setting.put(SettingBoxKey.defaultRcmdType, result); - SmartDialog.showToast('下次启动时生效'); - setState(() {}); - } - }, - ), const SetSwitchItem( title: '启用ai总结', subTitle: '视频详情页开启ai总结', diff --git a/lib/pages/setting/recommend_setting.dart b/lib/pages/setting/recommend_setting.dart new file mode 100644 index 00000000..8e049bc4 --- /dev/null +++ b/lib/pages/setting/recommend_setting.dart @@ -0,0 +1,137 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/http/member.dart'; +import 'package:pilipala/models/common/rcmd_type.dart'; +import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; +import 'package:pilipala/utils/storage.dart'; + +import 'widgets/switch_item.dart'; + +class RecommendSetting extends StatefulWidget { + const RecommendSetting({super.key}); + + @override + State createState() => _RecommendSettingState(); +} + +class _RecommendSettingState extends State { + Box setting = GStrorage.setting; + static Box localCache = GStrorage.localCache; + late dynamic defaultRcmdType; + Box userInfoCache = GStrorage.userInfo; + late dynamic userInfo; + bool userLogin = false; + late dynamic accessKeyInfo; + + @override + void initState() { + super.initState(); + // 首页默认推荐类型 + defaultRcmdType = + setting.get(SettingBoxKey.defaultRcmdType, defaultValue: 'web'); + userInfo = userInfoCache.get('userInfoCache'); + userLogin = userInfo != null; + accessKeyInfo = localCache.get(LocalCacheKey.accessKey, defaultValue: null); + } + + @override + Widget build(BuildContext context) { + TextStyle titleStyle = Theme.of(context).textTheme.titleMedium!; + TextStyle subTitleStyle = Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline); + return Scaffold( + appBar: AppBar( + centerTitle: false, + titleSpacing: 0, + title: Text( + '推荐设置', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + body: ListView( + children: [ + const SetSwitchItem( + title: '推荐动态', + subTitle: '是否在推荐内容中展示动态', + setKey: SettingBoxKey.enableRcmdDynamic, + defaultVal: true, + ), + const SetSwitchItem( + title: '首页推荐刷新', + subTitle: '下拉刷新时保留上次内容', + setKey: SettingBoxKey.enableSaveLastData, + defaultVal: false, + ), + ListTile( + dense: false, + title: Text('首页推荐类型', style: titleStyle), + subtitle: Text( + '当前使用「$defaultRcmdType端」推荐', + style: subTitleStyle, + ), + onTap: () async { + String? result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '推荐类型', + value: defaultRcmdType, + values: RcmdType.values.map((e) { + return {'title': e.labels, 'value': e.values}; + }).toList(), + ); + }, + ); + if (result != null) { + if (result == 'app') { + // app端推荐需要access_key + if (accessKeyInfo == null) { + if (!userLogin) { + SmartDialog.showToast('请先登录'); + return; + } + // 显示一个确认框,告知用户可能会导致账号被风控 + SmartDialog.show( + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (context) { + return AlertDialog( + title: const Text('提示'), + content: const Text( + '使用app端推荐需获取access_key,有小概率触发风控导致账号退出(在官方app重新登录即可解除),是否继续?'), + actions: [ + TextButton( + onPressed: () { + result = null; + SmartDialog.dismiss(); + }, + child: const Text('取消'), + ), + TextButton( + onPressed: () async { + SmartDialog.dismiss(); + await MemberHttp.cookieToKey(); + }, + child: const Text('确定'), + ), + ], + ); + }); + } + } + if (result != null) { + defaultRcmdType = result; + setting.put(SettingBoxKey.defaultRcmdType, result); + SmartDialog.showToast('下次启动时生效'); + setState(() {}); + } + } + }, + ), + ], + ), + ); + } +} diff --git a/lib/pages/setting/view.dart b/lib/pages/setting/view.dart index 677a4546..19cdedaf 100644 --- a/lib/pages/setting/view.dart +++ b/lib/pages/setting/view.dart @@ -24,6 +24,11 @@ class SettingPage extends StatelessWidget { dense: false, title: const Text('隐私设置'), ), + ListTile( + onTap: () => Get.toNamed('/recommendSetting'), + dense: false, + title: const Text('推荐设置'), + ), ListTile( onTap: () => Get.toNamed('/playSetting'), dense: false, diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 996869be..d6b2f9a9 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -37,6 +37,7 @@ import '../pages/setting/pages/color_select.dart'; import '../pages/setting/pages/display_mode.dart'; import '../pages/setting/pages/font_size_select.dart'; import '../pages/setting/pages/play_speed_set.dart'; +import '../pages/setting/recommend_setting.dart'; import '../pages/setting/play_setting.dart'; import '../pages/setting/privacy_setting.dart'; import '../pages/setting/style_setting.dart'; @@ -100,7 +101,8 @@ class Routes { // 二级回复 CustomGetPage( name: '/replyReply', page: () => const VideoReplyReplyPanel()), - + // 推荐设置 + CustomGetPage(name: '/recommendSetting', page: () => const RecommendSetting()), // 播放设置 CustomGetPage(name: '/playSetting', page: () => const PlaySetting()), // 外观设置 diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index f59a7bc1..c412606f 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -120,17 +120,19 @@ class SettingBoxKey { /// 隐私 blackMidsList = 'blackMidsList', + /// 推荐 + enableRcmdDynamic = 'enableRcmdDynamic', + defaultRcmdType = 'defaultRcmdType', + enableSaveLastData = 'enableSaveLastData', + /// 其他 autoUpdate = 'autoUpdate', - defaultRcmdType = 'defaultRcmdType', replySortType = 'replySortType', defaultDynamicType = 'defaultDynamicType', enableHotKey = 'enableHotKey', enableQuickFav = 'enableQuickFav', enableWordRe = 'enableWordRe', enableSearchWord = 'enableSearchWord', - enableRcmdDynamic = 'enableRcmdDynamic', - enableSaveLastData = 'enableSaveLastData', enableSystemProxy = 'enableSystemProxy', enableAi = 'enableAi'; From 9122dd7f3a16c3259b262ee52558fb9b0ca426df Mon Sep 17 00:00:00 2001 From: orz12 Date: Sat, 20 Jan 2024 17:07:10 +0800 Subject: [PATCH 002/102] =?UTF-8?q?mod:=20=E6=96=B0=E5=A2=9E=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E8=BF=87=E6=BB=A4=E5=99=A8=EF=BC=8C=E5=9B=9E=E9=80=80?= =?UTF-8?q?model=E8=BD=AC=E6=8D=A2=E4=BF=AE=E6=94=B9=EF=BC=8C=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=E4=B8=8D=E5=BF=85=E8=A6=81=E7=9A=84futureBuilder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_h.dart | 5 +- lib/common/widgets/video_card_v.dart | 12 +- lib/http/video.dart | 16 ++- lib/main.dart | 2 + lib/models/home/rcmd/result.dart | 20 ++- lib/models/home/rcmd/result.g.dart | 2 +- lib/models/model_rec_video_item.dart | 11 +- lib/models/model_rec_video_item.g.dart | 4 +- lib/pages/rcmd/controller.dart | 19 ++- lib/pages/rcmd/view.dart | 24 ++-- lib/pages/setting/recommend_setting.dart | 151 ++++++++++++++++++++--- lib/utils/recommend_filter.dart | 52 ++++++++ lib/utils/storage.dart | 5 + lib/utils/utils.dart | 11 +- 14 files changed, 274 insertions(+), 60 deletions(-) create mode 100644 lib/utils/recommend_filter.dart diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index b00f1759..5d39bc65 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -324,8 +324,9 @@ class VideoContent extends StatelessWidget { reSrc: 11, ); SmartDialog.dismiss(); - SmartDialog.showToast( - res['msg'] ?? '成功'); + SmartDialog.showToast(res['code'] == 0 + ? '成功' + : res['msg']); }, child: const Text('确认'), ) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index c5577af3..dfc7eb94 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -158,12 +158,12 @@ class VideoCardV extends StatelessWidget { height: maxHeight, ), ), - if (videoItem.duration != null) + if (videoItem.duration > 0) if (crossAxisCount == 1) ...[ PBadge( bottom: 10, right: 10, - text: videoItem.duration, + text: Utils.timeFormat(videoItem.duration), ) ] else ...[ PBadge( @@ -171,7 +171,7 @@ class VideoCardV extends StatelessWidget { right: 7, size: 'small', type: 'gray', - text: videoItem.duration, + text: Utils.timeFormat(videoItem.duration), ) ], ], @@ -330,10 +330,8 @@ class VideoStat extends StatelessWidget { color: Theme.of(context).colorScheme.outline, ), children: [ - if (videoItem.stat.view != '-') - TextSpan(text: '${videoItem.stat.view}观看'), - if (videoItem.stat.danmu != '-') - TextSpan(text: ' • ${videoItem.stat.danmu}弹幕'), + TextSpan(text: '${Utils.numFormat(videoItem.stat.view)}观看'), + TextSpan(text: ' • ${Utils.numFormat(videoItem.stat.danmu)}弹幕'), ], ), ); diff --git a/lib/http/video.dart b/lib/http/video.dart index 39c961cd..a48dd11b 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -9,6 +9,7 @@ import '../models/user/fav_folder.dart'; import '../models/video/ai.dart'; import '../models/video/play/url.dart'; import '../models/video_detail_res.dart'; +import '../utils/recommend_filter.dart'; import '../utils/storage.dart'; import '../utils/wbi_sign.dart'; import 'api.dart'; @@ -49,7 +50,10 @@ class VideoHttp { if (i['goto'] == 'av' && (i['owner'] != null && !blackMidsList.contains(i['owner']['mid']))) { - list.add(RecVideoItemModel.fromJson(i)); + RecVideoItemModel videoItem = RecVideoItemModel.fromJson(i); + if (!RecommendFilter.filter(videoItem)){ + list.add(videoItem); + } } } return {'status': true, 'data': list}; @@ -93,7 +97,10 @@ class VideoHttp { (!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) && (i['args'] != null && !blackMidsList.contains(i['args']['up_mid']))) { - list.add(RecVideoItemAppModel.fromJson(i)); + RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i); + if (!RecommendFilter.filter(videoItem)){ + list.add(videoItem); + } } } return {'status': true, 'data': list}; @@ -209,7 +216,10 @@ class VideoHttp { if (res.data['code'] == 0) { List list = []; for (var i in res.data['data']) { - list.add(HotVideoItemModel.fromJson(i)); + HotVideoItemModel videoItem = HotVideoItemModel.fromJson(i); + if (!RecommendFilter.filter(videoItem, relatedVideos: true)){ + list.add(videoItem); + } } return {'status': true, 'data': list}; } else { diff --git a/lib/main.dart b/lib/main.dart index 4e1482d7..29b8d118 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -21,6 +21,7 @@ import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. +import 'package:pilipala/utils/recommend_filter.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); @@ -32,6 +33,7 @@ void main() async { await setupServiceLocator(); Request(); await Request.setCookie(); + RecommendFilter(); runApp(const MyApp()); // 小白条、导航栏沉浸 SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index a2a8006d..4e810491 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -40,7 +40,7 @@ class RecVideoItemAppModel { @HiveField(5) RcmdStat? stat; @HiveField(6) - String? duration; + int? duration; @HiveField(7) String? title; @HiveField(8) @@ -79,13 +79,27 @@ class RecVideoItemAppModel { cid = json['player_args'] != null ? json['player_args']['cid'] : -1; pic = json['cover']; stat = RcmdStat.fromJson(json); - duration = json['cover_right_text']; + // 改用player_args中的duration作为原始数据(秒数) + duration = json['player_args'] != null + ? json['player_args']['duration'] + : -1; + //duration = json['cover_right_text']; title = json['title']; - isFollowed = 0; owner = RcmdOwner.fromJson(json); rcmdReason = json['rcmd_reason_style'] != null ? RcmdReason.fromJson(json['rcmd_reason_style']) : null; + // 由于app端api并不会直接返回与owner的关注状态 + // 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效 + isFollowed = rcmdReason != null && + rcmdReason!.content != null && + rcmdReason!.content!.contains('关注') + ? 1 + : 0; + // 如果是,就无需再显示推荐原因,交由view统一处理即可 + if (isFollowed == 1) { + rcmdReason = null; + } goto = json['goto']; param = int.parse(json['param']); uri = json['uri']; diff --git a/lib/models/home/rcmd/result.g.dart b/lib/models/home/rcmd/result.g.dart index 43bf4bcf..f46886e8 100644 --- a/lib/models/home/rcmd/result.g.dart +++ b/lib/models/home/rcmd/result.g.dart @@ -23,7 +23,7 @@ class RecVideoItemAppModelAdapter extends TypeAdapter { cid: fields[3] as int?, pic: fields[4] as String?, stat: fields[5] as RcmdStat?, - duration: fields[6] as String?, + duration: fields[6] as int?, title: fields[7] as String?, isFollowed: fields[8] as int?, owner: fields[9] as RcmdOwner?, diff --git a/lib/models/model_rec_video_item.dart b/lib/models/model_rec_video_item.dart index bd42fd82..1503f192 100644 --- a/lib/models/model_rec_video_item.dart +++ b/lib/models/model_rec_video_item.dart @@ -1,5 +1,3 @@ -import 'package:pilipala/utils/utils.dart'; - import './model_owner.dart'; import 'package:hive/hive.dart'; @@ -38,7 +36,7 @@ class RecVideoItemModel { @HiveField(6) String? title = ''; @HiveField(7) - String? duration = ''; + int? duration = -1; @HiveField(8) int? pubdate = -1; @HiveField(9) @@ -58,7 +56,7 @@ class RecVideoItemModel { uri = json["uri"]; pic = json["pic"]; title = json["title"]; - duration = Utils.tampToSeektime(json["duration"]); + duration = json["duration"]; pubdate = json["pubdate"]; owner = Owner.fromJson(json["owner"]); stat = Stat.fromJson(json["stat"]); @@ -77,14 +75,15 @@ class Stat { this.danmu, }); @HiveField(0) - String? view; + int? view; @HiveField(1) int? like; @HiveField(2) int? danmu; Stat.fromJson(Map json) { - view = Utils.numFormat(json["view"]); + // 无需在model中转换以保留原始数据,在view层处理即可 + view = json["view"]; like = json["like"]; danmu = json['danmaku']; } diff --git a/lib/models/model_rec_video_item.g.dart b/lib/models/model_rec_video_item.g.dart index 1de6ab03..dc614354 100644 --- a/lib/models/model_rec_video_item.g.dart +++ b/lib/models/model_rec_video_item.g.dart @@ -24,7 +24,7 @@ class RecVideoItemModelAdapter extends TypeAdapter { uri: fields[4] as String?, pic: fields[5] as String?, title: fields[6] as String?, - duration: fields[7] as String?, + duration: fields[7] as int?, pubdate: fields[8] as int?, owner: fields[9] as Owner?, stat: fields[10] as Stat?, @@ -87,7 +87,7 @@ class StatAdapter extends TypeAdapter { for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), }; return Stat( - view: fields[0] as String?, + view: fields[0] as int?, like: fields[1] as int?, danmu: fields[2] as int?, ); diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index ed98606a..54cb8c14 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -55,12 +55,13 @@ class RcmdController extends GetxController { } late final Map res; switch (defaultRcmdType) { - case 'app': case 'notLogin': - res = await VideoHttp.rcmdVideoListApp( - loginStatus: defaultRcmdType != 'notLogin', - freshIdx: _currentPage, - ); - break; + case 'app': + case 'notLogin': + res = await VideoHttp.rcmdVideoListApp( + loginStatus: defaultRcmdType != 'notLogin', + freshIdx: _currentPage, + ); + break; default: //'web' res = await VideoHttp.rcmdVideoList( freshIdx: _currentPage, @@ -83,10 +84,16 @@ class RcmdController extends GetxController { } else if (type == 'onLoad') { videoList.addAll(res['data']); } + // 目前仅支持app端系列保存缓存 if (defaultRcmdType != 'web') { recVideo.put('cacheList', res['data']); } _currentPage += 1; + // 若videoList数量太小,可能会影响翻页,此时再次请求 + // 为避免请求到的数据太少时还在反复请求,要求本次返回数据大于1条才触发 + if (res['data'].length > 1 && videoList.length < 10){ + queryRcmdFeed('onLoad'); + } } else { Get.snackbar('提示', res['msg']); } diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index 0a345511..687eacd1 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -1,5 +1,4 @@ import 'dart:async'; -import 'dart:io'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; @@ -8,7 +7,7 @@ import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart'; -import 'package:pilipala/common/widgets/http_error.dart'; +// import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_v.dart'; import 'package:pilipala/pages/home/index.dart'; @@ -26,7 +25,6 @@ class RcmdPage extends StatefulWidget { class _RcmdPageState extends State with AutomaticKeepAliveClientMixin { final RcmdController _rcmdController = Get.put(RcmdController()); - late Future _futureBuilderFuture; @override bool get wantKeepAlive => true; @@ -34,7 +32,7 @@ class _RcmdPageState extends State @override void initState() { super.initState(); - _futureBuilderFuture = _rcmdController.queryRcmdFeed('init'); + _rcmdController.queryRcmdFeed('init'); ScrollController scrollController = _rcmdController.scrollController; StreamController mainStream = Get.find().bottomBarStream; @@ -90,21 +88,21 @@ class _RcmdPageState extends State slivers: [ SliverPadding( padding: - const EdgeInsets.fromLTRB(0, StyleString.safeSpace, 0, 0), - sliver: Obx(() { - // 使用Obx来监听数据的变化 - if (_rcmdController.isLoadingMore) { - // 如果正在加载,则显示骨架屏 + const EdgeInsets.fromLTRB(0, StyleString.safeSpace, 0, 0), + sliver: Obx(() { // 使用Obx来监听数据的变化 + if (_rcmdController.isLoadingMore && _rcmdController.videoList.isEmpty) { return contentGrid(_rcmdController, []); + // 如果正在加载并且列表为空,则显示加载指示器 + // return const SliverToBoxAdapter( + // child: Center(child: CircularProgressIndicator()), + // ); } else { // 显示视频列表 - return contentGrid( - _rcmdController, - _rcmdController.videoList); + return contentGrid(_rcmdController, _rcmdController.videoList); } }), ), - LoadingMore(ctr: _rcmdController) + LoadingMore(ctr: _rcmdController), ], ), ), diff --git a/lib/pages/setting/recommend_setting.dart b/lib/pages/setting/recommend_setting.dart index 8e049bc4..ab8ec063 100644 --- a/lib/pages/setting/recommend_setting.dart +++ b/lib/pages/setting/recommend_setting.dart @@ -4,6 +4,7 @@ import 'package:hive/hive.dart'; import 'package:pilipala/http/member.dart'; import 'package:pilipala/models/common/rcmd_type.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; +import 'package:pilipala/utils/recommend_filter.dart'; import 'package:pilipala/utils/storage.dart'; import 'widgets/switch_item.dart'; @@ -23,6 +24,9 @@ class _RecommendSettingState extends State { late dynamic userInfo; bool userLogin = false; late dynamic accessKeyInfo; + // late int filterUnfollowedRatio; + late int minDurationForRcmd; + late int minLikeRatioForRecommend; @override void initState() { @@ -33,6 +37,12 @@ class _RecommendSettingState extends State { userInfo = userInfoCache.get('userInfoCache'); userLogin = userInfo != null; accessKeyInfo = localCache.get(LocalCacheKey.accessKey, defaultValue: null); + // filterUnfollowedRatio = setting + // .get(SettingBoxKey.filterUnfollowedRatio, defaultValue: 0); + minDurationForRcmd = + setting.get(SettingBoxKey.minDurationForRcmd, defaultValue: 0); + minLikeRatioForRecommend = + setting.get(SettingBoxKey.minLikeRatioForRecommend, defaultValue: 0); } @override @@ -53,23 +63,11 @@ class _RecommendSettingState extends State { ), body: ListView( children: [ - const SetSwitchItem( - title: '推荐动态', - subTitle: '是否在推荐内容中展示动态', - setKey: SettingBoxKey.enableRcmdDynamic, - defaultVal: true, - ), - const SetSwitchItem( - title: '首页推荐刷新', - subTitle: '下拉刷新时保留上次内容', - setKey: SettingBoxKey.enableSaveLastData, - defaultVal: false, - ), ListTile( dense: false, title: Text('首页推荐类型', style: titleStyle), subtitle: Text( - '当前使用「$defaultRcmdType端」推荐', + '当前使用「$defaultRcmdType端」推荐¹', style: subTitleStyle, ), onTap: () async { @@ -100,7 +98,7 @@ class _RecommendSettingState extends State { return AlertDialog( title: const Text('提示'), content: const Text( - '使用app端推荐需获取access_key,有小概率触发风控导致账号退出(在官方app重新登录即可解除),是否继续?'), + '使用app端推荐需获取access_key,有小概率触发风控导致账号退出(在官方版本app重新登录即可解除),是否继续?'), actions: [ TextButton( onPressed: () { @@ -130,6 +128,131 @@ class _RecommendSettingState extends State { } }, ), + const SetSwitchItem( + title: '推荐动态', + subTitle: '是否在推荐内容中展示动态(仅app端)', + setKey: SettingBoxKey.enableRcmdDynamic, + defaultVal: true, + ), + const SetSwitchItem( + title: '首页推荐刷新', + subTitle: '下拉刷新时保留上次内容', + setKey: SettingBoxKey.enableSaveLastData, + defaultVal: false, + ), + // 分割线 + const Divider(height: 1), + ListTile( + dense: false, + title: Text('点赞率过滤', style: titleStyle), + subtitle: Text( + '过滤掉点赞数/播放量「小于$minLikeRatioForRecommend%」的推荐视频(仅web端)', + style: subTitleStyle, + ), + onTap: () async { + int? result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '选择点赞率(0即不过滤)', + value: minLikeRatioForRecommend, + values: [0, 1, 2, 3, 4].map((e) { + return {'title': '$e %', 'value': e}; + }).toList()); + }, + ); + if (result != null) { + minLikeRatioForRecommend = result; + setting.put(SettingBoxKey.minLikeRatioForRecommend, result); + RecommendFilter.update(); + setState(() {}); + } + }, + ), + ListTile( + dense: false, + title: Text('视频时长过滤', style: titleStyle), + subtitle: Text( + '过滤掉时长「小于$minDurationForRcmd秒」的推荐视频', + style: subTitleStyle, + ), + onTap: () async { + int? result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '选择时长(0即不过滤)', + value: minDurationForRcmd, + values: [0, 30, 60, 90, 120].map((e) { + return {'title': '$e 秒', 'value': e}; + }).toList()); + }, + ); + if (result != null) { + minDurationForRcmd = result; + setting.put(SettingBoxKey.minDurationForRcmd, result); + RecommendFilter.update(); + setState(() {}); + } + }, + ), + SetSwitchItem( + title: '已关注Up豁免推荐过滤', + subTitle: '推荐中已关注用户发布的内容不会被过滤', + setKey: SettingBoxKey.exemptFilterForFollowed, + defaultVal: true, + callFn: (_) => {RecommendFilter.update}, + ), + // ListTile( + // dense: false, + // title: Text('按比例过滤未关注Up', style: titleStyle), + // subtitle: Text( + // '滤除推荐中占比「$filterUnfollowedRatio%」的未关注用户发布的内容', + // style: subTitleStyle, + // ), + // onTap: () async { + // int? result = await showDialog( + // context: context, + // builder: (context) { + // return SelectDialog( + // title: '选择滤除比例(0即不过滤)', + // value: filterUnfollowedRatio, + // values: [0, 16, 32, 48, 64].map((e) { + // return {'title': '$e %', 'value': e}; + // }).toList()); + // }, + // ); + // if (result != null) { + // filterUnfollowedRatio = result; + // setting.put( + // SettingBoxKey.filterUnfollowedRatio, result); + // RecommendFilter.update(); + // setState(() {}); + // } + // }, + // ), + SetSwitchItem( + title: '过滤器也应用于相关视频', + subTitle: '视频详情页的相关视频也进行过滤²', + setKey: SettingBoxKey.applyFilterToRelatedVideos, + defaultVal: true, + callFn: (_) => {RecommendFilter.update}, + ), + ListTile( + dense: true, + subtitle: Text( + '¹ 若默认web端推荐不太符合预期,可尝试切换至app端。\n' + '¹ 选择“模拟未登录(notLogin)”,将以空的key请求推荐接口,但播放页仍会携带用户信息,保证账号能正常记录进度、点赞投币等。\n\n' + '² 由于接口未提供关注信息,无法豁免相关视频中的已关注Up。\n\n' + '* 其它(如热门视频、手动搜索、链接跳转等)均不受过滤器影响。\n' + '* 设定较严苛的条件可导致推荐项数锐减或多次请求,请酌情选择。\n' + '* 后续可能会增加更多过滤条件,敬请期待。', + style: Theme.of(context) + .textTheme + .labelSmall! + .copyWith(color: Theme.of(context).colorScheme.outline.withOpacity(0.7)), + ), + ) ], ), ); diff --git a/lib/utils/recommend_filter.dart b/lib/utils/recommend_filter.dart new file mode 100644 index 00000000..113e2261 --- /dev/null +++ b/lib/utils/recommend_filter.dart @@ -0,0 +1,52 @@ +import 'dart:math'; + +import 'storage.dart'; + +class RecommendFilter { + // static late int filterUnfollowedRatio; + static late int minDurationForRcmd; + static late int minLikeRatioForRecommend; + static late bool exemptFilterForFollowed; + static late bool applyFilterToRelatedVideos; + RecommendFilter() { + update(); + } + + static void update() { + var setting = GStrorage.setting; + // filterUnfollowedRatio = + // setting.get(SettingBoxKey.filterUnfollowedRatio, defaultValue: 0); + minDurationForRcmd = + setting.get(SettingBoxKey.minDurationForRcmd, defaultValue: 0); + minLikeRatioForRecommend = + setting.get(SettingBoxKey.minLikeRatioForRecommend, defaultValue: 0); + exemptFilterForFollowed = + setting.get(SettingBoxKey.exemptFilterForFollowed, defaultValue: true); + applyFilterToRelatedVideos = setting + .get(SettingBoxKey.applyFilterToRelatedVideos, defaultValue: true); + } + + static bool filter(dynamic videoItem, {bool relatedVideos = false}) { + if (relatedVideos && !applyFilterToRelatedVideos) { + return false; + } + //由于相关视频中没有已关注标签,只能视为非关注视频 + if (!relatedVideos && + videoItem.isFollowed == 1 && + exemptFilterForFollowed) { + return false; + } + if (videoItem.duration > 0 && videoItem.duration < minDurationForRcmd) { + return true; + } + if (videoItem.stat.view is int && + videoItem.stat.view > -1 && + videoItem.stat.like is int && + videoItem.stat.like > -1 && + videoItem.stat.like * 100 < + minLikeRatioForRecommend * videoItem.stat.view) { + return true; + } + return false; + } +} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index c412606f..fdb9ee02 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -124,6 +124,11 @@ class SettingBoxKey { enableRcmdDynamic = 'enableRcmdDynamic', defaultRcmdType = 'defaultRcmdType', enableSaveLastData = 'enableSaveLastData', + minDurationForRcmd = 'minDurationForRcmd', + minLikeRatioForRecommend = 'minLikeRatioForRecommend', + exemptFilterForFollowed = 'exemptFilterForFollowed', + //filterUnfollowedRatio = 'filterUnfollowedRatio', + applyFilterToRelatedVideos = 'applyFilterToRelatedVideos', /// 其他 autoUpdate = 'autoUpdate', diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 08693d24..f0b56fc4 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -9,7 +9,6 @@ import 'package:crypto/crypto.dart'; import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get_utils/get_utils.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:path_provider/path_provider.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -28,10 +27,16 @@ class Utils { return tempPath; } - static String numFormat(int number) { + static String numFormat(dynamic number) { + if (number == null){ + return '0'; + } + if (number is String) { + return number; + } final String res = (number / 10000).toString(); if (int.parse(res.split('.')[0]) >= 1) { - return '${(number / 10000).toPrecision(1)}万'; + return '${(number / 10000).toStringAsFixed(1)}万'; } else { return number.toString(); } From 569184a507c9233fb0bb5c599e61d6dd76ab9f8d Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 22 Jan 2024 12:03:17 +0800 Subject: [PATCH 003/102] =?UTF-8?q?opt:=20=E5=88=87=E6=8D=A2=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E6=97=B6=E9=94=80=E6=AF=81=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E7=BB=84=E4=BB=B6=E6=8F=90=E5=8D=87=E6=80=A7=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 78 ++++++++++++++++---------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 59fa39b2..3d3c1c30 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -61,6 +61,7 @@ class _VideoDetailPageState extends State final Floating floating = Floating(); // 生命周期监听 late final AppLifecycleListener _lifecycleListener; + bool isShowing = true; @override void initState() { @@ -216,15 +217,15 @@ class _VideoDetailPageState extends State videoIntroController.isPaused = true; plPlayerController!.removeStatusLister(playerListener); plPlayerController!.pause(); - plPlayerController!.danmakuController?.pause(); - plPlayerController!.danmakuController?.clear(); } + setState(() => isShowing = false); super.didPushNext(); } @override // 返回当前页面时 void didPopNext() async { + setState(() => isShowing = true); videoDetailController.isFirstTime = false; final bool autoplay = autoPlayEnable; videoDetailController.playerInit(autoplay: autoplay); @@ -354,44 +355,45 @@ class _VideoDetailPageState extends State boxConstraints.maxHeight; return Stack( children: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (snapshot.hasData && - snapshot.data['status']) { - return Obx( - () => !videoDetailController - .autoPlay.value - ? const SizedBox() - : PLVideoPlayer( - controller: - plPlayerController!, - headerControl: - videoDetailController - .headerControl, - danmuWidget: Obx( - () => PlDanmaku( - key: Key( - videoDetailController - .danmakuCid - .value - .toString()), - cid: - videoDetailController - .danmakuCid - .value, - playerController: - plPlayerController!, + if (isShowing) + FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData && + snapshot.data['status']) { + return Obx( + () => !videoDetailController + .autoPlay.value + ? nil + : PLVideoPlayer( + controller: + plPlayerController!, + headerControl: + videoDetailController + .headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key( + videoDetailController + .danmakuCid + .value + .toString()), + cid: + videoDetailController + .danmakuCid + .value, + playerController: + plPlayerController!, + ), ), ), - ), - ); - } else { - return const SizedBox(); - } - }, - ), + ); + } else { + return const SizedBox(); + } + }, + ), /// 关闭自动播放时 手动播放 if (!videoDetailController From 103423abf7503e376a79039a74c56b82e5b5633d Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 22 Jan 2024 12:04:00 +0800 Subject: [PATCH 004/102] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E9=83=A8?= =?UTF-8?q?=E5=88=86=E6=89=8B=E6=9C=BA=E6=A8=AA=E5=B1=8F=E4=B8=A4=E4=BE=A7?= =?UTF-8?q?=E4=B8=8D=E7=AD=89=E5=AE=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 3d3c1c30..278e2c64 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -292,8 +292,8 @@ class _VideoDetailPageState extends State plPlayerController?.isFullScreen.value == true, bottom: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, - left: plPlayerController?.isFullScreen.value != true, - right: plPlayerController?.isFullScreen.value != true, + left: false,//plPlayerController?.isFullScreen.value != true, + right: false, //plPlayerController?.isFullScreen.value != true, child: Stack( children: [ Scaffold( From 1dd70f482fb2162992aeeb27f37a99e24698a7ab Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 22 Jan 2024 12:54:32 +0800 Subject: [PATCH 005/102] =?UTF-8?q?fix:=20=E6=97=8B=E8=BD=AC=E6=A8=AA?= =?UTF-8?q?=E5=B1=8F=E4=BB=8D=E6=9C=89=E7=8A=B6=E6=80=81=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 362 ++++++++++++++++--------------- 1 file changed, 184 insertions(+), 178 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 278e2c64..507ea66e 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -281,18 +281,12 @@ class _VideoDetailPageState extends State final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; final double pinnedHeaderHeight = statusBarHeight + kToolbarHeight + videoHeight; - if (MediaQuery.of(context).orientation == Orientation.landscape || - plPlayerController?.isFullScreen.value == true) { - enterFullScreen(); - } else { - exitFullScreen(); - } Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, bottom: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, - left: false,//plPlayerController?.isFullScreen.value != true, + left: false, //plPlayerController?.isFullScreen.value != true, right: false, //plPlayerController?.isFullScreen.value != true, child: Stack( children: [ @@ -313,185 +307,197 @@ class _VideoDetailPageState extends State (BuildContext context, bool innerBoxIsScrolled) { return [ Obx( - () => SliverAppBar( - automaticallyImplyLeading: false, - // 假装使用一个非空变量,避免Obx检测不到而罢工 - pinned: videoDetailController.autoPlay.value ^ - false ^ - videoDetailController.autoPlay.value, - elevation: 0, - scrolledUnderElevation: 0, - forceElevated: innerBoxIsScrolled, - expandedHeight: MediaQuery.of(context).orientation == - Orientation.landscape || - plPlayerController?.isFullScreen.value == true - ? MediaQuery.sizeOf(context).height - - (MediaQuery.of(context).orientation == - Orientation.landscape - ? 0 - : MediaQuery.of(context).padding.top) - : videoHeight, - backgroundColor: Colors.black, - flexibleSpace: FlexibleSpaceBar( - background: PopScope( - canPop: - plPlayerController?.isFullScreen.value != true, - onPopInvoked: (bool didPop) { - if (plPlayerController?.isFullScreen.value == - true) { - plPlayerController! - .triggerFullScreen(status: false); - } - if (MediaQuery.of(context).orientation == - Orientation.landscape) { - verticalScreen(); - } - }, - child: LayoutBuilder( - builder: (BuildContext context, - BoxConstraints boxConstraints) { - final double maxWidth = boxConstraints.maxWidth; - final double maxHeight = - boxConstraints.maxHeight; - return Stack( - children: [ - if (isShowing) - FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (snapshot.hasData && - snapshot.data['status']) { - return Obx( - () => !videoDetailController - .autoPlay.value - ? nil - : PLVideoPlayer( - controller: - plPlayerController!, - headerControl: - videoDetailController - .headerControl, - danmuWidget: Obx( - () => PlDanmaku( - key: Key( - videoDetailController - .danmakuCid - .value - .toString()), - cid: - videoDetailController - .danmakuCid - .value, - playerController: - plPlayerController!, - ), - ), - ), - ); - } else { - return const SizedBox(); - } - }, - ), + () { + if (MediaQuery.of(context).orientation == + Orientation.landscape || + plPlayerController?.isFullScreen.value == true) { + enterFullScreen(); + } else { + exitFullScreen(); + } + return SliverAppBar( + automaticallyImplyLeading: false, + // 假装使用一个非空变量,避免Obx检测不到而罢工 + pinned: videoDetailController.autoPlay.value ^ + false ^ + videoDetailController.autoPlay.value, + elevation: 0, + scrolledUnderElevation: 0, + forceElevated: innerBoxIsScrolled, + expandedHeight: MediaQuery.of(context).orientation == + Orientation.landscape || + plPlayerController?.isFullScreen.value == true + ? MediaQuery.sizeOf(context).height - + (MediaQuery.of(context).orientation == + Orientation.landscape + ? 0 + : MediaQuery.of(context).padding.top) + : videoHeight, + backgroundColor: Colors.black, + flexibleSpace: FlexibleSpaceBar( + background: PopScope( + canPop: plPlayerController?.isFullScreen.value != + true, + onPopInvoked: (bool didPop) { + if (plPlayerController?.isFullScreen.value == + true) { + plPlayerController! + .triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape) { + verticalScreen(); + } + }, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints boxConstraints) { + final double maxWidth = + boxConstraints.maxWidth; + final double maxHeight = + boxConstraints.maxHeight; + return Stack( + children: [ + if (isShowing) + FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, + AsyncSnapshot snapshot) { + if (snapshot.hasData && + snapshot.data['status']) { + return Obx( + () => + !videoDetailController + .autoPlay.value + ? nil + : PLVideoPlayer( + controller: + plPlayerController!, + headerControl: + videoDetailController + .headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key(videoDetailController + .danmakuCid + .value + .toString()), + cid: videoDetailController + .danmakuCid + .value, + playerController: + plPlayerController!, + ), + ), + ), + ); + } else { + return const SizedBox(); + } + }, + ), - /// 关闭自动播放时 手动播放 - if (!videoDetailController - .autoPlay.value) ...[ - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: GestureDetector( - onTap: () { - handlePlay(); - }, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: maxWidth, - height: maxHeight, + /// 关闭自动播放时 手动播放 + if (!videoDetailController + .autoPlay.value) ...[ + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: GestureDetector( + onTap: () { + handlePlay(); + }, + child: NetworkImgLayer( + type: 'emote', + src: videoDetailController + .videoItem['pic'], + width: maxWidth, + height: maxHeight, + ), ), ), ), ), - ), - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value && - videoDetailController - .isEffective.value, - child: Stack( - children: [ - Positioned( - top: 0, - left: 0, - right: 0, - child: AppBar( - primary: false, - foregroundColor: - Colors.white, - elevation: 0, - scrolledUnderElevation: 0, - backgroundColor: - Colors.transparent, - actions: [ - IconButton( - tooltip: '稍后再看', - onPressed: () async { - var res = await UserHttp - .toViewLater( - bvid: - videoDetailController - .bvid); - SmartDialog.showToast( - res['msg']); - }, - icon: const Icon(Icons - .history_outlined), - ), - const SizedBox(width: 14) - ], - ), - ), - Positioned( - right: 12, - bottom: 10, - child: TextButton.icon( - style: ButtonStyle( + Obx( + () => Visibility( + visible: videoDetailController + .isShowCover.value && + videoDetailController + .isEffective.value, + child: Stack( + children: [ + Positioned( + top: 0, + left: 0, + right: 0, + child: AppBar( + primary: false, + foregroundColor: + Colors.white, + elevation: 0, + scrolledUnderElevation: 0, backgroundColor: - MaterialStateProperty - .resolveWith( - (states) { - return Colors.white - .withOpacity(0.8); - }), + Colors.transparent, + actions: [ + IconButton( + tooltip: '稍后再看', + onPressed: () async { + var res = await UserHttp + .toViewLater( + bvid: videoDetailController + .bvid); + SmartDialog + .showToast( + res['msg']); + }, + icon: const Icon(Icons + .history_outlined), + ), + const SizedBox( + width: 14) + ], ), - onPressed: () => - handlePlay(), - icon: const Icon( - Icons.play_circle_outline, - size: 20, - ), - label: const Text('轻触封面播放'), ), - ), - ], - )), - ), - ] - ], - ); - }, - )), - ), - ), + Positioned( + right: 12, + bottom: 10, + child: TextButton.icon( + style: ButtonStyle( + backgroundColor: + MaterialStateProperty + .resolveWith( + (states) { + return Colors.white + .withOpacity(0.8); + }), + ), + onPressed: () => + handlePlay(), + icon: const Icon( + Icons + .play_circle_outline, + size: 20, + ), + label: + const Text('轻触封面播放'), + ), + ), + ], + )), + ), + ] + ], + ); + }, + )), + ), + ); + }, ), ]; }, From a68c04001b883636cce6c432bc86f6f8c427aa3a Mon Sep 17 00:00:00 2001 From: orz12 Date: Mon, 22 Jan 2024 13:19:29 +0800 Subject: [PATCH 006/102] =?UTF-8?q?fix:=20=E7=AB=96=E5=B1=8F=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 507ea66e..dec474ba 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -304,7 +304,7 @@ class _VideoDetailPageState extends State body: ExtendedNestedScrollView( controller: _extendNestCtr, headerSliverBuilder: - (BuildContext context, bool innerBoxIsScrolled) { + (BuildContext context2, bool innerBoxIsScrolled) { return [ Obx( () { From 16895b5c32a2e26522323f4d40cc62c7f7a7a17f Mon Sep 17 00:00:00 2001 From: orz12 Date: Tue, 23 Jan 2024 15:12:52 +0800 Subject: [PATCH 007/102] =?UTF-8?q?fix:=20=E7=82=B9=E5=87=BB=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E8=AF=84=E8=AE=BA=E5=8C=BA=E7=94=A8=E6=88=B7=E5=A4=B4?= =?UTF-8?q?=E5=83=8F=E5=90=8E=E8=BF=94=E5=9B=9E=E8=AF=A6=E6=83=85=E9=A1=B5?= =?UTF-8?q?=E7=81=B0=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/related/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/related/controller.dart b/lib/pages/video/detail/related/controller.dart index eb1a14d5..f7042871 100644 --- a/lib/pages/video/detail/related/controller.dart +++ b/lib/pages/video/detail/related/controller.dart @@ -4,7 +4,7 @@ import 'package:pilipala/http/video.dart'; class ReleatedController extends GetxController { // 视频aid - String bvid = Get.parameters['bvid']!; + String bvid = Get.parameters['bvid'] ?? ""; // 推荐视频列表 List relatedVideoList = []; From aaeecc9e531d3340bce5a02074286e79d4015d6a Mon Sep 17 00:00:00 2001 From: orz12 Date: Wed, 24 Jan 2024 11:01:54 +0800 Subject: [PATCH 008/102] =?UTF-8?q?fix:=20=E9=87=8D=E5=8A=9B=E6=97=8B?= =?UTF-8?q?=E8=BD=AC=E5=90=8E=E5=88=92=E5=87=BA=E4=B8=8B=E6=96=B9=E7=9A=84?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index dec474ba..1ce78b3c 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -508,7 +508,9 @@ class _VideoDetailPageState extends State // }, /// 不收回 pinnedHeaderSliverHeightBuilder: () { - return plPlayerController?.isFullScreen.value == true + return MediaQuery.of(context).orientation == + Orientation.landscape || + plPlayerController?.isFullScreen.value == true ? MediaQuery.sizeOf(context).height : pinnedHeaderHeight; }, From 545def36e6a6b8ec64fcbc038712f86099041308 Mon Sep 17 00:00:00 2001 From: orz12 Date: Thu, 25 Jan 2024 12:59:21 +0800 Subject: [PATCH 009/102] =?UTF-8?q?mod:=20=E8=87=AA=E5=8A=A8=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E6=8C=89=E9=92=AE=E6=94=B9=E4=B8=BA=E5=AE=98=E6=96=B9?= =?UTF-8?q?=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 29 +++++++++-------------------- 1 file changed, 9 insertions(+), 20 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 1ce78b3c..3644fa41 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -466,26 +466,15 @@ class _VideoDetailPageState extends State Positioned( right: 12, bottom: 10, - child: TextButton.icon( - style: ButtonStyle( - backgroundColor: - MaterialStateProperty - .resolveWith( - (states) { - return Colors.white - .withOpacity(0.8); - }), - ), - onPressed: () => - handlePlay(), - icon: const Icon( - Icons - .play_circle_outline, - size: 20, - ), - label: - const Text('轻触封面播放'), - ), + child: IconButton( + tooltip: '播放', + onPressed: () => + handlePlay(), + icon: Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + )), ), ], )), From bf8ae0f3172c77e234e1512eb32715a3e8e27bf3 Mon Sep 17 00:00:00 2001 From: orz12 Date: Thu, 25 Jan 2024 11:36:20 +0800 Subject: [PATCH 010/102] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E6=8F=8F=E8=BE=B9=E7=B2=97=E7=BB=86=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E9=BB=98=E8=AE=A4=E5=80=BC=E9=99=8D=E4=BD=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/view.dart | 3 ++ .../video/detail/widgets/header_control.dart | 41 +++++++++++++++++++ lib/plugin/pl_player/controller.dart | 5 +++ lib/utils/storage.dart | 3 +- 4 files changed, 51 insertions(+), 1 deletion(-) diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 83d6020e..109f0206 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -35,6 +35,7 @@ class _PlDanmakuState extends State { late double opacityVal; late double fontSizeVal; late double danmakuDurationVal; + late double strokeWidth; int latestAddedPosition = -1; @override @@ -65,6 +66,7 @@ class _PlDanmakuState extends State { showArea = playerController.showArea; opacityVal = playerController.opacityVal; fontSizeVal = playerController.fontSizeVal; + strokeWidth = playerController.strokeWidth; danmakuDurationVal = playerController.danmakuDurationVal; } @@ -136,6 +138,7 @@ class _PlDanmakuState extends State { hideBottom: blockTypes.contains(4), duration: danmakuDurationVal / playerController.playbackSpeed, + strokeWidth: strokeWidth, // initDuration / // (danmakuSpeedVal * widget.playerController.playbackSpeed), ), diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index f2e38870..3af35efd 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1,4 +1,5 @@ import 'dart:io'; +import 'dart:math'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; @@ -724,6 +725,8 @@ class _HeaderControlState extends State { double fontSizeVal = widget.controller!.fontSizeVal; // 弹幕速度 double danmakuDurationVal = widget.controller!.danmakuDurationVal; + // 弹幕描边 + double strokeWidth = widget.controller!.strokeWidth; final DanmakuController danmakuController = widget.controller!.danmakuController!; @@ -857,6 +860,44 @@ class _HeaderControlState extends State { ), ), ), + Text('描边粗细 $strokeWidth'), + Padding( + padding: const EdgeInsets.only( + top: 0, + bottom: 6, + left: 10, + right: 10, + ), + child: SliderTheme( + data: SliderThemeData( + trackShape: MSliderTrackShape(), + thumbColor: Theme.of(context).colorScheme.primary, + activeTrackColor: Theme.of(context).colorScheme.primary, + trackHeight: 10, + thumbShape: const RoundSliderThumbShape( + enabledThumbRadius: 6.0), + ), + child: Slider( + min: 0, + max: 3, + value: strokeWidth, + divisions: 6, + label: '$strokeWidth', + onChanged: (double val) { + strokeWidth = val; + widget.controller!.strokeWidth = val; + setState(() {}); + try { + final DanmakuOption currentOption = + danmakuController.option; + final DanmakuOption updatedOption = + currentOption.copyWith(strokeWidth: val); + danmakuController.updateOption(updatedOption); + } catch (_) {} + }, + ), + ), + ), Text('字体大小 ${(fontSizeVal * 100).toStringAsFixed(1)}%'), Padding( padding: const EdgeInsets.only( diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index d35987f8..dfa580ab 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -221,6 +221,7 @@ class PlPlayerController { late double showArea; late double opacityVal; late double fontSizeVal; + late double strokeWidth; late double danmakuDurationVal; late List speedsList; // 缓存 @@ -275,6 +276,9 @@ class PlPlayerController { // 弹幕时间 danmakuDurationVal = localCache.get(LocalCacheKey.danmakuDuration, defaultValue: 4.0); + // 描边粗细 + strokeWidth = + localCache.get(LocalCacheKey.strokeWidth, defaultValue: 1.5); playRepeat = PlayRepeat.values.toList().firstWhere( (e) => e.value == @@ -1086,6 +1090,7 @@ class PlPlayerController { localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal); + localCache.put(LocalCacheKey.strokeWidth, strokeWidth); if (_videoPlayerController != null) { var pp = _videoPlayerController!.platform as NativePlayer; await pp.setProperty('audio-files', ''); diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 99037611..e4bc8048 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -158,12 +158,13 @@ class LocalCacheKey { wbiKeys = 'wbiKeys', timeStamp = 'timeStamp', - // 弹幕相关设置 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 + // 弹幕相关设置 屏蔽类型 显示区域 透明度 字体大小 弹幕时间 描边粗细 danmakuBlockType = 'danmakuBlockType', danmakuShowArea = 'danmakuShowArea', danmakuOpacity = 'danmakuOpacity', danmakuFontScale = 'danmakuFontScale', danmakuDuration = 'danmakuDuration', + strokeWidth = 'strokeWidth', // 代理host port systemProxyHost = 'systemProxyHost', From 1b03f3f31ffd594eb3bb50411219c91e5a575de7 Mon Sep 17 00:00:00 2001 From: orz12 Date: Wed, 24 Jan 2024 21:43:02 +0800 Subject: [PATCH 011/102] =?UTF-8?q?mod:=20AI=E8=A7=86=E9=A2=91=E6=80=BB?= =?UTF-8?q?=E7=BB=93=E6=9C=AA=E6=94=AF=E6=8C=81=E6=8F=90=E9=86=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 7 ++++++- lib/pages/video/detail/introduction/controller.dart | 4 +++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index 923e93a2..e7b09801 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -447,11 +447,16 @@ class VideoHttp { 'up_mid': upMid, }); var res = await Request().get(Api.aiConclusion, data: params); - if (res.data['code'] == 0) { + if (res.data['code'] == 0 && res.data['data']['code'] == 0) { return { 'status': true, 'data': AiConclusionModel.fromJson(res.data['data']), }; + } else { + return { + 'status': false, + 'data': [] + }; } } } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 1230a94b..1deadb07 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -570,10 +570,12 @@ class VideoIntroController extends GetxController { cid: lastPlayCid.value, upMid: videoDetail.value.owner!.mid!, ); + SmartDialog.dismiss(); if (res['status']) { modelResult = res['data'].modelResult; + } else { + SmartDialog.showToast("当前视频可能暂不支持AI视频总结"); } - SmartDialog.dismiss(); return res; } } From 9663278916014376f4142d557c0d328bf5ee3825 Mon Sep 17 00:00:00 2001 From: orz12 Date: Thu, 25 Jan 2024 20:24:13 +0800 Subject: [PATCH 012/102] =?UTF-8?q?feat:=20=E7=A7=81=E4=BF=A1=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=88=86=E4=BA=AB=E8=A7=86=E9=A2=91=E5=86=85=E5=AE=B9?= =?UTF-8?q?=E3=80=81=E5=AF=8C=E6=96=87=E6=9C=AC=E8=A1=A8=E6=83=85=EF=BC=8C?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E4=BF=A1=E6=81=AF=E7=B1=BB=E5=9E=8B=E6=9E=9A?= =?UTF-8?q?=E4=B8=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/msg/session.dart | 2 +- lib/pages/whisper_detail/controller.dart | 6 + lib/pages/whisper_detail/view.dart | 8 +- .../whisper_detail/widget/chat_item.dart | 206 +++++++++++++++--- 4 files changed, 187 insertions(+), 35 deletions(-) diff --git a/lib/models/msg/session.dart b/lib/models/msg/session.dart index 1fa05cb0..ea241249 100644 --- a/lib/models/msg/session.dart +++ b/lib/models/msg/session.dart @@ -166,7 +166,7 @@ class SessionMsgDataModel { int? hasMore; int? minSeqno; int? maxSeqno; - List? eInfos; + List? eInfos; SessionMsgDataModel.fromJson(Map json) { messages = json['messages'] diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 52a70c49..71dd4c03 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/session.dart'; @@ -8,6 +9,8 @@ class WhisperDetailController extends GetxController { late String face; late String mid; RxList messageList = [].obs; + //表情转换图片规则 + List? eInfos; @override void onInit() { @@ -22,6 +25,9 @@ class WhisperDetailController extends GetxController { var res = await MsgHttp.sessionMsg(talkerId: talkerId); if (res['status']) { messageList.value = res['data'].messages; + if (messageList.isNotEmpty && res['data'].eInfos != null) { + eInfos = res['data'].eInfos; + } } return res; } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 5d108037..8d2297c4 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -110,12 +110,16 @@ class _WhisperDetailPageState extends State { if (i == 0) { return Column( children: [ - ChatItem(item: messageList[i]), + ChatItem( + item: messageList[i], + e_infos: _whisperDetailController.eInfos), const SizedBox(height: 12), ], ); } else { - return ChatItem(item: messageList[i]); + return ChatItem( + item: messageList[i], + e_infos: _whisperDetailController.eInfos); } }, ), diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 9ea6bf2a..9f0e191b 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -1,38 +1,200 @@ // 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'; +import 'package:get/get_core/src/get_main.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/storage.dart'; +import '../../../http/search.dart'; + +enum MsgType { + invalid(value: 0, label: "空空的~"), + text(value: 1, label: "文本消息"), + pic(value: 2, label: "图片消息"), + audio(value: 3, label: "语音消息"), + share(value: 4, label: "分享消息"), + revoke(value: 5, label: "撤回消息"), + custom_face(value: 6, label: "自定义表情"), + share_v2(value: 7, label: "分享v2消息"), + sys_cancel(value: 8, label: "系统撤销"), + mini_program(value: 9, label: "小程序"), + notify_msg(value: 10, label: "业务通知"), + archive_card(value: 11, label: "投稿卡片"), + article_card(value: 12, label: "专栏卡片"), + pic_card(value: 13, label: "图片卡片"), + common_share(value: 14, label: "异形卡片"), + notify_text(value: 18, label: "文本提示"); + + final int value; + final String label; + const MsgType({required this.value, required this.label}); + static MsgType parse(int value) { + return MsgType.values + .firstWhere((e) => e.value == value, orElse: () => MsgType.invalid); + } +} + class ChatItem extends StatelessWidget { dynamic item; + List? e_infos; ChatItem({ super.key, this.item, + this.e_infos, }); @override Widget build(BuildContext context) { bool isOwner = item.senderUid == GStrorage.userInfo.get('userInfoCache').mid; - bool isPic = item.msgType == 2; // 图片 - bool isText = item.msgType == 1; // 文本 - // bool isAchive = item.msgType == 11; // 投稿 + bool isPic = item.msgType == MsgType.pic; // 图片 + bool isText = item.msgType == MsgType.text; // 文本 + // bool isArchive = item.msgType == 11; // 投稿 // bool isArticle = item.msgType == 12; // 专栏 - bool isRevoke = item.msgType == 5; // 撤回消息 - + bool isRevoke = item.msgType == MsgType.revoke; // 撤回消息 + bool isShareV2 = item.msgType == MsgType.share_v2; bool isSystem = item.msgType == 18 || item.msgType == 10 || item.msgType == 13; - int msgType = item.msgType; dynamic content = item.content ?? ''; + Color textColor(BuildContext context) { + return isOwner + ? Theme.of(context).colorScheme.onPrimary + : Theme.of(context).colorScheme.onSecondaryContainer; + } + + Widget richTextMessage(BuildContext context) { + var text = content['content']; + if (e_infos != null) { + final List children = []; + Map emojiMap = {}; + for (var e in e_infos!) { + emojiMap[e['text']] = e['url']; + } + text.splitMapJoin( + RegExp(r"\[.+?\]"), + onMatch: (Match match) { + final String emojiKey = match[0]!; + if (emojiMap.containsKey(emojiKey)) { + children.add(WidgetSpan( + child: NetworkImgLayer( + width: 18, height: 18, + src: emojiMap[emojiKey]!,), + )); + } + return ''; + }, + onNonMatch: (String text) { + children.add(TextSpan(text: text, style: TextStyle( + color: textColor(context), + letterSpacing: 0.6, + height: 1.5, + ))); + return ''; + }, + ); + return RichText( + text: TextSpan( + children: children, + ), + ); + } else { + return Text( + text, + style: TextStyle( + letterSpacing: 0.6, + color: textColor(context), + height: 1.5, + ), + ); + } + } + + Widget messageContent(BuildContext context) { + switch (MsgType.parse(item.msgType)) { + case MsgType.notify_msg: + return SystemNotice(item: item); + case MsgType.pic_card: + return SystemNotice2(item: item); + case MsgType.notify_text: + return Text( + jsonDecode(content['content']).map((m) => m['text'] as String).join("\n"), + style: TextStyle( + letterSpacing: 0.6, + height: 5, + color: Theme.of(context).colorScheme.outline.withOpacity(0.8) + ), + ); + case MsgType.text: + return richTextMessage(context); + case MsgType.pic: + return NetworkImgLayer( + width: 220, + height: 220 * content['height'] / content['width'], + src: content['url'], + ); + case MsgType.share_v2: + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () async { + SmartDialog.showLoading(); + var bvid = content["bvid"]; + 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': content['thumb'], + 'heroTag': heroTag, + }), + ); + }, + child: NetworkImgLayer( + width: 220, + height: 220 * 9 / 16, + src: content['thumb'], + ), + ), + const SizedBox(height: 6), + Text( + content['title'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), fontWeight: FontWeight.bold), + ), + const SizedBox(height: 1), + Text( + content['author'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context).withOpacity(0.6), + fontSize: 12, + ), + ), + ], + ); + default: + return Text( + content['content'] ?? content.toString(), + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), fontWeight: FontWeight.bold), + ); + } + } + return isSystem - ? (msgType == 10 - ? SystemNotice(item: item) - : msgType == 13 - ? SystemNotice2(item: item) - : const SizedBox()) + ? messageContent(context) : isRevoke ? const SizedBox() : Row( @@ -66,27 +228,7 @@ class ChatItem extends StatelessWidget { ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ - isText - ? Text( - content['content'], - style: TextStyle( - color: isOwner - ? Theme.of(context) - .colorScheme - .onPrimary - : Theme.of(context) - .colorScheme - .onSecondaryContainer), - ) - : isPic - ? NetworkImgLayer( - width: 220, - height: 220 * - content['height'] / - content['width'], - src: content['url'], - ) - : const SizedBox(), + messageContent(context), SizedBox(height: isPic ? 7 : 2), Row( mainAxisSize: MainAxisSize.min, From 932be48125f6c2340a0c78eac6eccec2e8e3e122 Mon Sep 17 00:00:00 2001 From: orz12 Date: Sat, 20 Jan 2024 21:05:01 +0800 Subject: [PATCH 013/102] =?UTF-8?q?mod:=20=E6=8E=A8=E8=8D=90=E3=80=81?= =?UTF-8?q?=E6=90=9C=E7=B4=A2=E9=A1=B5=E6=B7=BB=E5=8A=A0=E6=97=B6=E9=97=B4?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E8=A7=86=E9=A2=91=E6=90=9C=E7=B4=A2?= =?UTF-8?q?=E9=A1=B5=E6=97=A0=E6=B3=95=E7=AD=9B=E9=80=89=E5=92=8C=E5=9B=9E?= =?UTF-8?q?=E9=A1=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_v.dart | 39 ++++++++++++------- lib/pages/search_panel/view.dart | 2 +- .../search_panel/widgets/video_panel.dart | 6 +-- lib/utils/utils.dart | 28 ++++++++++++- 4 files changed, 56 insertions(+), 19 deletions(-) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 0bbd5377..c40d439c 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'stat/danmu.dart'; +import 'stat/view.dart'; import '../../http/dynamics.dart'; import '../../http/search.dart'; import '../../http/user.dart'; @@ -322,21 +324,30 @@ class VideoStat extends StatelessWidget { @override Widget build(BuildContext context) { - return RichText( - maxLines: 1, - text: TextSpan( - style: TextStyle( - fontSize: MediaQuery.textScalerOf(context) - .scale(Theme.of(context).textTheme.labelSmall!.fontSize!), - color: Theme.of(context).colorScheme.outline, + return Row( + children: [ + StatView( + theme: 'gray', + view: videoItem.stat.view as int, ), - children: [ - if (videoItem.stat.view != '-') - TextSpan(text: '${videoItem.stat.view}观看'), - if (videoItem.stat.danmu != '-') - TextSpan(text: ' • ${videoItem.stat.danmu}弹幕'), - ], - ), + const SizedBox(width: 8), + StatDanMu( + theme: 'gray', + danmu: videoItem.stat.danmu as int, + ), + const Spacer(), + RichText( + maxLines: 1, + text: TextSpan( + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + text: Utils.formatTimestampToRelativeTime(videoItem.pubdate) + ), + ), + const SizedBox(width: 4), + ], ); } } diff --git a/lib/pages/search_panel/view.dart b/lib/pages/search_panel/view.dart index f7392c88..182fbea0 100644 --- a/lib/pages/search_panel/view.dart +++ b/lib/pages/search_panel/view.dart @@ -43,7 +43,7 @@ class _SearchPanelState extends State keyword: widget.keyword, searchType: widget.searchType, ), - tag: widget.searchType!.type + widget.keyword!, + tag: widget.searchType!.type, ); scrollController = _searchPanelController.scrollController; scrollController.addListener(() async { diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index 0b5d5eb8..3debcfb9 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -35,7 +35,7 @@ class SearchVideoPanel extends StatelessWidget { padding: index == 0 ? const EdgeInsets.only(top: 2) : EdgeInsets.zero, - child: VideoCardH(videoItem: i), + child: VideoCardH(videoItem: i, showPubdate: true), ); }, ), @@ -70,7 +70,7 @@ class SearchVideoPanel extends StatelessWidget { controller.selectedType.value = i['type']; ctr!.order.value = i['type'].toString().split('.').last; - SmartDialog.showLoading(msg: 'loooad'); + SmartDialog.showLoading(msg: 'loading'); await ctr!.onRefresh(); SmartDialog.dismiss(); }, @@ -201,7 +201,7 @@ class VideoPanelController extends GetxController { SearchPanelController ctr = Get.find(tag: 'video'); ctr.duration.value = i['value']; - SmartDialog.showLoading(msg: 'loooad'); + SmartDialog.showLoading(msg: 'loading'); await ctr.onRefresh(); SmartDialog.dismiss(); }, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 08693d24..7ef0e53f 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -28,7 +28,13 @@ class Utils { return tempPath; } - static String numFormat(int number) { + static String numFormat(dynamic number) { + if (number == null) { + return '0'; + } + if (number is String) { + return number; + } final String res = (number / 10000).toString(); if (int.parse(res.split('.')[0]) >= 1) { return '${(number / 10000).toPrecision(1)}万'; @@ -58,6 +64,26 @@ class Utils { } } + // 完全相对时间显示 + static String formatTimestampToRelativeTime(timeStamp) { + var difference = DateTime.now() + .difference(DateTime.fromMillisecondsSinceEpoch(timeStamp * 1000)); + + if (difference.inDays > 365) { + return '${difference.inDays ~/ 365}年前'; + } else if (difference.inDays > 30) { + return '${difference.inDays ~/ 30}个月前'; + } else if (difference.inDays > 0) { + return '${difference.inDays}天前'; + } else if (difference.inHours > 0) { + return '${difference.inHours}小时前'; + } else if (difference.inMinutes > 0) { + return '${difference.inMinutes}分钟前'; + } else { + return '刚刚'; + } + } + // 时间显示,刚刚,x分钟前 static String dateFormat(timeStamp, {formatType = 'list'}) { // 当前时间 From 23c8b3418971e8d0c36217e6cc1508125027ef5b Mon Sep 17 00:00:00 2001 From: orz12 Date: Sat, 20 Jan 2024 21:43:01 +0800 Subject: [PATCH 014/102] =?UTF-8?q?fix:=20app=E7=AB=AF=E6=8E=A8=E8=8D=90?= =?UTF-8?q?=E5=B1=8F=E8=94=BD=E6=97=B6=E9=97=B4=E6=98=BE=E7=A4=BA=EF=BC=8C?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E9=87=8F=E4=B8=8E=E5=BC=B9=E5=B9=95=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E6=94=B9=E4=B8=BA=E5=8A=A8=E6=80=81=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/stat/danmu.dart | 2 +- lib/common/widgets/stat/view.dart | 2 +- lib/common/widgets/video_card_v.dart | 28 +++++++++++++++------------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart index 44f662a9..c1c439db 100644 --- a/lib/common/widgets/stat/danmu.dart +++ b/lib/common/widgets/stat/danmu.dart @@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart'; class StatDanMu extends StatelessWidget { final String? theme; - final int? danmu; + final dynamic danmu; final String? size; const StatDanMu({Key? key, this.theme, this.danmu, this.size}) diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart index 8b97b605..2665e2d4 100644 --- a/lib/common/widgets/stat/view.dart +++ b/lib/common/widgets/stat/view.dart @@ -3,7 +3,7 @@ import 'package:pilipala/utils/utils.dart'; class StatView extends StatelessWidget { final String? theme; - final int? view; + final dynamic view; final String? size; const StatView({Key? key, this.theme, this.view, this.size}) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index c40d439c..86ea2a5a 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import '../../models/model_rec_video_item.dart'; import 'stat/danmu.dart'; import 'stat/view.dart'; import '../../http/dynamics.dart'; @@ -328,25 +329,26 @@ class VideoStat extends StatelessWidget { children: [ StatView( theme: 'gray', - view: videoItem.stat.view as int, + view: videoItem.stat.view, ), const SizedBox(width: 8), StatDanMu( theme: 'gray', - danmu: videoItem.stat.danmu as int, + danmu: videoItem.stat.danmu, ), - const Spacer(), - RichText( - maxLines: 1, - text: TextSpan( - style: TextStyle( - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - text: Utils.formatTimestampToRelativeTime(videoItem.pubdate) + if (videoItem is RecVideoItemModel) ...[ + const Spacer(), + RichText( + maxLines: 1, + text: TextSpan( + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + text: Utils.formatTimestampToRelativeTime(videoItem.pubdate)), ), - ), - const SizedBox(width: 4), + const SizedBox(width: 4), + ] ], ); } From 349de75dfd9569e03b3438146fb17ee2191c4711 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jan 2024 10:12:21 +0800 Subject: [PATCH 015/102] =?UTF-8?q?mod:=20=E7=A7=BB=E9=99=A4=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E6=8E=A8=E8=8D=90=E8=A7=86=E9=A2=91=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/home/rcmd/result.dart | 34 ----- lib/models/home/rcmd/result.g.dart | 209 ----------------------------- lib/pages/rcmd/controller.dart | 11 -- lib/utils/storage.dart | 15 --- 4 files changed, 269 deletions(-) delete mode 100644 lib/models/home/rcmd/result.g.dart diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index a2a8006d..d5bc7d89 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -1,8 +1,3 @@ -import 'package:hive/hive.dart'; - -part 'result.g.dart'; - -@HiveType(typeId: 0) class RecVideoItemAppModel { RecVideoItemAppModel({ this.id, @@ -27,47 +22,27 @@ class RecVideoItemAppModel { this.adInfo, }); - @HiveField(0) int? id; - @HiveField(1) int? aid; - @HiveField(2) String? bvid; - @HiveField(3) int? cid; - @HiveField(4) String? pic; - @HiveField(5) RcmdStat? stat; - @HiveField(6) String? duration; - @HiveField(7) String? title; - @HiveField(8) int? isFollowed; - @HiveField(9) RcmdOwner? owner; - @HiveField(10) RcmdReason? rcmdReason; - @HiveField(11) String? goto; - @HiveField(12) int? param; - @HiveField(13) String? uri; - @HiveField(14) String? talkBack; // 番剧 - @HiveField(15) String? bangumiView; - @HiveField(16) String? bangumiFollow; - @HiveField(17) String? bangumiBadge; - @HiveField(18) String? cardType; - @HiveField(19) Map? adInfo; RecVideoItemAppModel.fromJson(Map json) { @@ -102,18 +77,14 @@ class RecVideoItemAppModel { } } -@HiveType(typeId: 1) class RcmdStat { RcmdStat({ this.view, this.like, this.danmu, }); - @HiveField(0) String? view; - @HiveField(1) String? like; - @HiveField(2) String? danmu; RcmdStat.fromJson(Map json) { @@ -122,13 +93,10 @@ class RcmdStat { } } -@HiveType(typeId: 2) class RcmdOwner { RcmdOwner({this.name, this.mid}); - @HiveField(0) String? name; - @HiveField(1) int? mid; RcmdOwner.fromJson(Map json) { @@ -141,13 +109,11 @@ class RcmdOwner { } } -@HiveType(typeId: 8) class RcmdReason { RcmdReason({ this.content, }); - @HiveField(0) String? content; RcmdReason.fromJson(Map json) { diff --git a/lib/models/home/rcmd/result.g.dart b/lib/models/home/rcmd/result.g.dart deleted file mode 100644 index 43bf4bcf..00000000 --- a/lib/models/home/rcmd/result.g.dart +++ /dev/null @@ -1,209 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'result.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class RecVideoItemAppModelAdapter extends TypeAdapter { - @override - final int typeId = 0; - - @override - RecVideoItemAppModel read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return RecVideoItemAppModel( - id: fields[0] as int?, - aid: fields[1] as int?, - bvid: fields[2] as String?, - cid: fields[3] as int?, - pic: fields[4] as String?, - stat: fields[5] as RcmdStat?, - duration: fields[6] as String?, - title: fields[7] as String?, - isFollowed: fields[8] as int?, - owner: fields[9] as RcmdOwner?, - rcmdReason: fields[10] as RcmdReason?, - goto: fields[11] as String?, - param: fields[12] as int?, - uri: fields[13] as String?, - talkBack: fields[14] as String?, - bangumiView: fields[15] as String?, - bangumiFollow: fields[16] as String?, - bangumiBadge: fields[17] as String?, - cardType: fields[18] as String?, - adInfo: (fields[19] as Map?)?.cast(), - ); - } - - @override - void write(BinaryWriter writer, RecVideoItemAppModel obj) { - writer - ..writeByte(20) - ..writeByte(0) - ..write(obj.id) - ..writeByte(1) - ..write(obj.aid) - ..writeByte(2) - ..write(obj.bvid) - ..writeByte(3) - ..write(obj.cid) - ..writeByte(4) - ..write(obj.pic) - ..writeByte(5) - ..write(obj.stat) - ..writeByte(6) - ..write(obj.duration) - ..writeByte(7) - ..write(obj.title) - ..writeByte(8) - ..write(obj.isFollowed) - ..writeByte(9) - ..write(obj.owner) - ..writeByte(10) - ..write(obj.rcmdReason) - ..writeByte(11) - ..write(obj.goto) - ..writeByte(12) - ..write(obj.param) - ..writeByte(13) - ..write(obj.uri) - ..writeByte(14) - ..write(obj.talkBack) - ..writeByte(15) - ..write(obj.bangumiView) - ..writeByte(16) - ..write(obj.bangumiFollow) - ..writeByte(17) - ..write(obj.bangumiBadge) - ..writeByte(18) - ..write(obj.cardType) - ..writeByte(19) - ..write(obj.adInfo); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RecVideoItemAppModelAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class RcmdStatAdapter extends TypeAdapter { - @override - final int typeId = 1; - - @override - RcmdStat read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return RcmdStat( - view: fields[0] as String?, - like: fields[1] as String?, - danmu: fields[2] as String?, - ); - } - - @override - void write(BinaryWriter writer, RcmdStat obj) { - writer - ..writeByte(3) - ..writeByte(0) - ..write(obj.view) - ..writeByte(1) - ..write(obj.like) - ..writeByte(2) - ..write(obj.danmu); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RcmdStatAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class RcmdOwnerAdapter extends TypeAdapter { - @override - final int typeId = 2; - - @override - RcmdOwner read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return RcmdOwner( - name: fields[0] as String?, - mid: fields[1] as int?, - ); - } - - @override - void write(BinaryWriter writer, RcmdOwner obj) { - writer - ..writeByte(2) - ..writeByte(0) - ..write(obj.name) - ..writeByte(1) - ..write(obj.mid); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RcmdOwnerAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class RcmdReasonAdapter extends TypeAdapter { - @override - final int typeId = 8; - - @override - RcmdReason read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return RcmdReason( - content: fields[0] as String?, - ); - } - - @override - void write(BinaryWriter writer, RcmdReason obj) { - writer - ..writeByte(1) - ..writeByte(0) - ..write(obj.content); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RcmdReasonAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index 4ed1ade1..d6dab1b1 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -13,7 +13,6 @@ class RcmdController extends GetxController { RxList webVideoList = [].obs; bool isLoadingMore = true; OverlayEntry? popupDialog; - Box recVideo = GStrorage.recVideo; Box setting = GStrorage.setting; RxInt crossAxisCount = 2.obs; late bool enableSaveLastData; @@ -24,15 +23,6 @@ class RcmdController extends GetxController { super.onInit(); crossAxisCount.value = setting.get(SettingBoxKey.customRows, defaultValue: 2); - // 读取app端缓存内容 - // if (recVideo.get('cacheList') != null && - // recVideo.get('cacheList').isNotEmpty) { - // List list = []; - // for (var i in recVideo.get('cacheList')) { - // list.add(i); - // } - // videoList.value = list; - // } enableSaveLastData = setting.get(SettingBoxKey.enableSaveLastData, defaultValue: false); defaultRcmdType = @@ -77,7 +67,6 @@ class RcmdController extends GetxController { } else if (type == 'onLoad') { appVideoList.addAll(res['data']); } - recVideo.put('cacheList', res['data']); _currentPage += 1; } isLoadingMore = false; diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 99037611..ff8e7d63 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -3,13 +3,11 @@ import 'dart:io'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:path_provider/path_provider.dart'; -import 'package:pilipala/models/home/rcmd/result.dart'; import 'package:pilipala/models/model_owner.dart'; import 'package:pilipala/models/search/hot.dart'; import 'package:pilipala/models/user/info.dart'; class GStrorage { - static late final Box recVideo; static late final Box userInfo; static late final Box historyword; static late final Box localCache; @@ -21,13 +19,6 @@ class GStrorage { final String path = dir.path; await Hive.initFlutter('$path/hive'); regAdapter(); - // 首页推荐视频 - recVideo = await Hive.openBox( - 'recVideo', - compactionStrategy: (int entries, int deletedEntries) { - return deletedEntries > 12; - }, - ); // 登录用户信息 userInfo = await Hive.openBox( 'userInfo', @@ -54,10 +45,6 @@ class GStrorage { } static void regAdapter() { - Hive.registerAdapter(RecVideoItemAppModelAdapter()); - Hive.registerAdapter(RcmdReasonAdapter()); - Hive.registerAdapter(RcmdStatAdapter()); - Hive.registerAdapter(RcmdOwnerAdapter()); Hive.registerAdapter(OwnerAdapter()); Hive.registerAdapter(UserInfoDataAdapter()); Hive.registerAdapter(LevelInfoAdapter()); @@ -73,8 +60,6 @@ class GStrorage { static Future close() async { // user.compact(); // user.close(); - recVideo.compact(); - recVideo.close(); userInfo.compact(); userInfo.close(); historyword.compact(); From 10d2995429bb20a9cc51e6b46e56d6bcf69665c8 Mon Sep 17 00:00:00 2001 From: orz12 Date: Sat, 27 Jan 2024 12:05:04 +0800 Subject: [PATCH 016/102] =?UTF-8?q?mod:=20=E5=AF=B9=E9=BD=90=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E6=A0=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/search_panel/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/search_panel/view.dart b/lib/pages/search_panel/view.dart index 182fbea0..f7392c88 100644 --- a/lib/pages/search_panel/view.dart +++ b/lib/pages/search_panel/view.dart @@ -43,7 +43,7 @@ class _SearchPanelState extends State keyword: widget.keyword, searchType: widget.searchType, ), - tag: widget.searchType!.type, + tag: widget.searchType!.type + widget.keyword!, ); scrollController = _searchPanelController.scrollController; scrollController.addListener(() async { From c0162892ef310a4d34daaf9c370bc2ba6017f84f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jan 2024 13:05:59 +0800 Subject: [PATCH 017/102] =?UTF-8?q?mod:=20=E9=95=BF=E5=9B=BE=E6=A0=87?= =?UTF-8?q?=E7=AD=BE=E6=98=BE=E7=A4=BA=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/content_panel.dart | 3 ++- lib/pages/dynamics/widgets/pic_panel.dart | 3 ++- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index eee4255c..d10804d7 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -1,5 +1,6 @@ // 内容 import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/dynamics/result.dart'; @@ -80,7 +81,7 @@ class _ContentState extends State { height: height, ), ), - height > maxHeight + height > Get.size.height * 0.9 ? const PBadge( text: '长图', right: 8, diff --git a/lib/pages/dynamics/widgets/pic_panel.dart b/lib/pages/dynamics/widgets/pic_panel.dart index 25b22c21..4e94e6fd 100644 --- a/lib/pages/dynamics/widgets/pic_panel.dart +++ b/lib/pages/dynamics/widgets/pic_panel.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -87,7 +88,7 @@ Widget picWidget(item, context) { childAspectRatio: aspectRatio, children: list, ), - if (len == 1 && origAspectRatio < 0.4) + if (len == 1 && height > Get.size.height * 0.9) const PBadge( text: '长图', top: null, diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 475c0939..48518bee 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -880,7 +880,7 @@ InlineSpan buildContent( height: height, ), ), - height > maxHeight + height > Get.size.height * 0.9 ? const PBadge( text: '长图', right: 8, From 0c4bad406ea2b8620b131a45a059dec57e84969b Mon Sep 17 00:00:00 2001 From: orz12 Date: Sat, 27 Jan 2024 14:28:15 +0800 Subject: [PATCH 018/102] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E8=AF=86=E5=88=AB=E9=80=BB=E8=BE=91=E9=87=8D=E6=9E=84=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=90=AB=E6=9C=89=E5=85=B3=E9=94=AE=E8=AF=8D?= =?UTF-8?q?=E7=9A=84=E8=AF=84=E8=AE=BA=E9=87=8D=E5=A4=8D=E5=87=BA=E7=8E=B0?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 363 +++++++----------- 1 file changed, 131 insertions(+), 232 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 48518bee..6ecf3983 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,5 +1,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.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:hive/hive.dart'; @@ -585,255 +586,153 @@ InlineSpan buildContent( if (content.message.contains('&')) { content.message = content.message.replaceAll('&', '&'); } - // 匹配表情 + print("content.jumpUrl.keys:" + content.jumpUrl.keys.toString()); + // 构建正则表达式 + final List specialTokens = [ + ...content.emote.keys, + ...content.atNameToMid.keys.map((e) => '@$e'), + ...content.jumpUrl.keys.map((e) => + e.replaceAll('?', '\\?').replaceAll('+', '\\+').replaceAll('*', '\\*')), + r'\b\d{2}:\d{2}\b' + ]; + + final String patternStr = specialTokens.map(RegExp.escape).join('|'); + final RegExp pattern = RegExp(patternStr); + List matchedStrs = []; + // 分割文本并处理每个部分 content.message.splitMapJoin( - RegExp(r"\[.*?\]"), + pattern, onMatch: (Match match) { - final String matchStr = match[0]!; - if (content.emote.isNotEmpty && - matchStr.indexOf('[') == matchStr.lastIndexOf('[') && - matchStr.indexOf(']') == matchStr.lastIndexOf(']')) { + String matchStr = match[0]!; + if (content.emote.containsKey(matchStr)) { + // 处理表情 final int size = content.emote[matchStr]['meta']['size']; - if (content.emote.keys.contains(matchStr)) { + spanChilds.add(WidgetSpan( + child: NetworkImgLayer( + src: content.emote[matchStr]['url'], + type: 'emote', + width: size * 20, + height: size * 20, + ), + )); + } else if (matchStr.startsWith("@") && + content.atNameToMid.containsKey(matchStr.substring(1))) { + // 处理@用户 + final String userName = matchStr.substring(1); + final int userId = content.atNameToMid[userName]; + spanChilds.add( + TextSpan( + text: matchStr, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + final String heroTag = Utils.makeHeroTag(userId); + Get.toNamed( + '/member?mid=$userId', + arguments: {'face': '', 'heroTag': heroTag}, + ); + }, + ), + ); + } else if (matchStr.contains(':')) { + spanChilds.add( + TextSpan( + text: ' $matchStr ', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 跳转到指定位置 + try { + Get.find(tag: Get.arguments['heroTag']) + .plPlayerController + .seekTo( + Duration(seconds: Utils.duration(matchStr)), + ); + } catch (_) {} + }, + ), + ); + } else { + print("matchStr=$matchStr"); + String appUrlSchema = ''; + final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, + defaultValue: false) as bool; + if (enableWordRe && content.jumpUrl[matchStr] != null && !matchedStrs.contains(matchStr)) { + appUrlSchema = content.jumpUrl[matchStr]['app_url_schema']; spanChilds.add( - WidgetSpan( - child: NetworkImgLayer( - src: content.emote[matchStr]['url'], - type: 'emote', - width: size * 20, - height: size * 20, + TextSpan( + text: content.jumpUrl[matchStr]['title'], + style: TextStyle( + color: Theme.of(context).colorScheme.primary, ), + recognizer: TapGestureRecognizer() + ..onTap = () { + if (appUrlSchema == '') { + final String str = Uri.parse(matchStr).pathSegments[0]; + final Map matchRes = IdUtils.matchAvorBv(input: str); + final List matchKeys = matchRes.keys.toList(); + if (matchKeys.isNotEmpty) { + if (matchKeys.first == 'BV') { + Get.toNamed( + '/searchResult', + parameters: {'keyword': matchRes['BV']}, + ); + } + } else { + Get.toNamed( + '/webview', + parameters: { + 'url': matchStr, + 'type': 'url', + 'pageTitle': '' + }, + ); + } + } else { + if (appUrlSchema.startsWith('bilibili://search')) { + Get.toNamed('/searchResult', parameters: { + 'keyword': content.jumpUrl[matchStr]['title'] + }); + } + } + }, ), ); + if (appUrlSchema.startsWith('bilibili://search') && enableWordRe) { + spanChilds.add( + WidgetSpan( + child: Icon( + FontAwesomeIcons.magnifyingGlass, + size: 9, + color: Theme.of(context).colorScheme.primary, + ), + alignment: PlaceholderAlignment.top, + ), + ); + } + // 只显示一次 + matchedStrs.add(matchStr); } else { spanChilds.add(TextSpan( text: matchStr, recognizer: TapGestureRecognizer() ..onTap = () => replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); - return matchStr; } - } else { - spanChilds.add(TextSpan( - text: matchStr, - recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); - return matchStr; } return ''; }, - onNonMatch: (String str) { - // 匹配@用户 - String matchMember = str; - if (content.atNameToMid.isNotEmpty) { - final List atNameToMidKeys = content.atNameToMid.keys.toList(); - RegExp reg = RegExp(atNameToMidKeys.map((key) => key).join('|')); - // if (!content.message.contains(':')) { - // reg = RegExp(r"@.*( |:)"); - // } - - // 只@用户没有内容 - if (!content.message.contains(':') || - (content.atNameToMid.length == 1 && - content.message == '@${content.members.first.uname}')) { - reg = RegExp(r"@.*( |:|$)"); - } - matchMember = str.splitMapJoin( - reg, - onMatch: (Match match) { - if (match[0] != null) { - hasMatchMember = true; - content.atNameToMid.forEach((key, value) { - if (str.contains('回复')) { - spanChilds.add( - TextSpan( - text: '回复 ', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.titleSmall!.fontSize, - ), - ), - ); - } - spanChilds.add( - TextSpan( - text: '@$key', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.titleSmall!.fontSize, - color: Theme.of(context).colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - final String heroTag = Utils.makeHeroTag(value); - Get.toNamed( - '/member?mid=$value', - arguments: {'face': '', 'heroTag': heroTag}, - ); - }, - ), - ); - }); - } - return ''; - }, - onNonMatch: (String str) { - if (!str.contains('@')) { - spanChilds.add(TextSpan(text: str)); - } - print(str); - return str; - }, - ); - } else { - matchMember = str; - } - - // 匹配 jumpUrl - String matchUrl = matchMember; - if (content.jumpUrl.isNotEmpty) { - final List urlKeys = content.jumpUrl.keys.toList().reversed.toList(); - for (int index = 0; index < urlKeys.length; index++) { - var i = urlKeys[index]; - if (i.contains('?')) { - urlKeys[index] = i.replaceAll('?', '\\?'); - } - if (i.contains('+')) { - urlKeys[index] = i.replaceAll('+', '\\+'); - } - if (i.contains('*')) { - urlKeys[index] = i.replaceAll('*', '\\*'); - } - } - if (hasMatchMember) { - matchMember = matchMember.split('回复 @ :').length > 1 - ? matchMember.split('回复 @ :')[1] - : matchMember; - } - matchUrl = matchMember.splitMapJoin( - /// RegExp.escape() 转义特殊字符 - RegExp(urlKeys.map((key) => key).join("|")), - // RegExp('What does the fox say\\?'), - onMatch: (Match match) { - final String matchStr = match[0]!; - String appUrlSchema = ''; - if (content.jumpUrl[matchStr] != null) { - appUrlSchema = content.jumpUrl[matchStr]['app_url_schema']; - } - // 默认不显示关键词 - final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, - defaultValue: false) as bool; - if (content.jumpUrl[matchStr] != null) { - spanChilds.add( - TextSpan( - text: content.jumpUrl[matchStr]['title'], - style: TextStyle( - color: enableWordRe - ? Theme.of(context).colorScheme.primary - : null, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - if (appUrlSchema == '') { - final String str = Uri.parse(matchStr).pathSegments[0]; - final Map matchRes = IdUtils.matchAvorBv(input: str); - final List matchKeys = matchRes.keys.toList(); - if (matchKeys.isNotEmpty) { - if (matchKeys.first == 'BV') { - Get.toNamed( - '/searchResult', - parameters: {'keyword': matchRes['BV']}, - ); - } - } else { - Get.toNamed( - '/webview', - parameters: { - 'url': matchStr, - 'type': 'url', - 'pageTitle': '' - }, - ); - } - } else { - if (appUrlSchema.startsWith('bilibili://search') && - enableWordRe) { - Get.toNamed('/searchResult', parameters: { - 'keyword': content.jumpUrl[matchStr]['title'] - }); - } - } - }, - ), - ); - } - - if (appUrlSchema.startsWith('bilibili://search') && enableWordRe) { - spanChilds.add( - WidgetSpan( - child: Icon( - FontAwesomeIcons.magnifyingGlass, - size: 9, - color: Theme.of(context).colorScheme.primary, - ), - alignment: PlaceholderAlignment.top, - ), - ); - } - return ''; - }, - onNonMatch: (String str) { - spanChilds.add(TextSpan( - text: str, - recognizer: TapGestureRecognizer() - ..onTap = () => replyReply( - replyItem.root == 0 ? replyItem : fReplyItem))); - return str; - }, - ); - } - str = matchUrl.splitMapJoin( - RegExp(r'\b\d{2}:\d{2}\b'), - onMatch: (Match match) { - String matchStr = match[0]!; - spanChilds.add( - TextSpan( - text: ' $matchStr ', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 跳转到指定位置 - try { - Get.find( - tag: Get.arguments['heroTag']) - .plPlayerController - .seekTo( - Duration(seconds: Utils.duration(matchStr)), - ); - } catch (_) {} - }, - ), - ); - return ''; - }, - onNonMatch: (str) { - return str; - }, - ); - - if (content.atNameToMid.isEmpty && content.jumpUrl.isEmpty) { - if (str != '') { - spanChilds.add(TextSpan( - text: str, - recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); - } - } - return str; + onNonMatch: (String nonMatchStr) { + spanChilds.add(TextSpan( + text: nonMatchStr, + recognizer: TapGestureRecognizer() + ..onTap = () => + replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); + return nonMatchStr; }, ); From e603942b5fae5813ace24c4df50ca728f7c6c46c Mon Sep 17 00:00:00 2001 From: orz12 Date: Sat, 27 Jan 2024 15:49:34 +0800 Subject: [PATCH 019/102] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=AD=A3=E5=88=99=E6=8B=BC=E6=8E=A5=E9=A1=BA?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/reply/widgets/reply_item.dart | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 6ecf3983..98d8ba6a 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -593,10 +593,10 @@ InlineSpan buildContent( ...content.atNameToMid.keys.map((e) => '@$e'), ...content.jumpUrl.keys.map((e) => e.replaceAll('?', '\\?').replaceAll('+', '\\+').replaceAll('*', '\\*')), - r'\b\d{2}:\d{2}\b' ]; - final String patternStr = specialTokens.map(RegExp.escape).join('|'); + final String patternStr = + specialTokens.map(RegExp.escape).join('|') + r'|\b[0-9]{1,2}:[0-9]{2}\b'; final RegExp pattern = RegExp(patternStr); List matchedStrs = []; // 分割文本并处理每个部分 @@ -647,12 +647,15 @@ InlineSpan buildContent( ..onTap = () { // 跳转到指定位置 try { + SmartDialog.showToast('跳转至:$matchStr'); Get.find(tag: Get.arguments['heroTag']) .plPlayerController .seekTo( Duration(seconds: Utils.duration(matchStr)), ); - } catch (_) {} + } catch (e) { + SmartDialog.showToast('跳转失败: $e'); + } }, ), ); @@ -661,7 +664,9 @@ InlineSpan buildContent( String appUrlSchema = ''; final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, defaultValue: false) as bool; - if (enableWordRe && content.jumpUrl[matchStr] != null && !matchedStrs.contains(matchStr)) { + if (enableWordRe && + content.jumpUrl[matchStr] != null && + !matchedStrs.contains(matchStr)) { appUrlSchema = content.jumpUrl[matchStr]['app_url_schema']; spanChilds.add( TextSpan( From 127c6734f89a12332d6c8406cb4ae4d2c1d9fe8f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Jan 2024 23:15:34 +0800 Subject: [PATCH 020/102] =?UTF-8?q?feat:=20=E8=87=AA=E5=8A=A8=E6=89=93?= =?UTF-8?q?=E5=8C=85ipa=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 201 +++++++++++++++++++++++++------------ 1 file changed, 137 insertions(+), 64 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 90fa6311..e7f09f96 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,84 +1,157 @@ -name: build_apk +name: Pilipala Release # action事件触发 on: push: # push tag时触发 tags: - - 'v*.*.*' + - "v*.*.*" # 可以有多个jobs jobs: - build_apk: - # 运行环境 ubuntu-latest window-latest mac-latest - runs-on: ubuntu-latest + android: + # 运行环境 ubuntu-latest window-latest mac-latest + runs-on: ubuntu-latest - # 每个jobs中可以有多个steps - steps: - - name: 代码迁出 - uses: actions/checkout@v3 + # 每个jobs中可以有多个steps + steps: + - name: 代码迁出 + uses: actions/checkout@v3 - - name: 构建Java环境 - uses: actions/setup-java@v3 - with: - distribution: "zulu" - java-version: "17" - token: ${{secrets.GIT_TOKEN}} + - name: 构建Java环境 + uses: actions/setup-java@v3 + with: + distribution: "zulu" + java-version: "17" + token: ${{secrets.GIT_TOKEN}} - - name: 检查缓存 - uses: actions/cache@v2 - id: cache-flutter - with: - path: /root/flutter-sdk # Flutter SDK 的路径 - key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.lock') }} + - name: 检查缓存 + uses: actions/cache@v2 + id: cache-flutter + with: + path: /root/flutter-sdk # Flutter SDK 的路径 + key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.lock') }} - - name: 安装Flutter - if: steps.cache-flutter.outputs.cache-hit != 'true' - uses: subosito/flutter-action@v2 - with: - flutter-version: 3.16.5 - channel: any + - name: 安装Flutter + if: steps.cache-flutter.outputs.cache-hit != 'true' + uses: subosito/flutter-action@v2 + with: + flutter-version: 3.16.5 + channel: any - - name: 下载项目依赖 - run: flutter pub get + - name: 下载项目依赖 + run: flutter pub get - - name: 解码生成 jks - run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks - env: - KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} + - name: 解码生成 jks + run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks + env: + KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} - - name: flutter build apk - # 对应 android/app/build.gradle signingConfigs中的配置项 - run: flutter build apk --release --split-per-abi - env: - KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} - KEY_ALIAS: ${{ secrets.KEY_ALIAS }} - KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}} + - name: flutter build apk + run: flutter build apk --release --split-per-abi + env: + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}} - - name: 获取版本号 - id: version - run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT + - name: flutter build apk + run: flutter build apk --release + env: + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}} - # - name: 获取当前日期 - # id: date - # run: echo "date=$(date +'%m%d')" >>$GITHUB_OUTPUT + - name: 获取版本号 + id: version + run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT - - name: 重命名应用 Pili-arm64-v8a-*.*.*.0101.apk - run: | - # DATE=${{ steps.date.outputs.date }} - for file in build/app/outputs/flutter-apk/app-*-release.apk; do - if [[ $file =~ app-(.*)-release.apk ]]; then - new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}-${{ steps.version.outputs.version }}.apk" - mv "$file" "$new_file_name" - fi - done + # - name: 获取当前日期 + # id: date + # run: echo "date=$(date +'%m%d')" >>$GITHUB_OUTPUT - - name: 构建和发布release - uses: ncipollo/release-action@v1 - with: - # release title - name: v${{ steps.version.outputs.version }} - artifacts: "build/app/outputs/flutter-apk/Pili-*.apk" - bodyFile: "change_log/${{steps.version.outputs.version}}.md" - token: ${{ secrets.GIT_TOKEN }} - allowUpdates: true + - name: 重命名应用 + run: | + # DATE=${{ steps.date.outputs.date }} + for file in build/app/outputs/flutter-apk/app-*.apk; do + if [[ $file =~ app-(.?*)release.apk ]]; then + new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}${{ steps.version.outputs.version }}.apk" + mv "$file" "$new_file_name" + fi + done + + - name: 上传 + uses: actions/upload-artifact@v3 + with: + name: Pilipala-Release + path: | + build/app/outputs/flutter-apk/Pili-*.apk + + iOS: + runs-on: macos-latest + + steps: + - name: 代码迁出 + uses: actions/checkout@v4 + + - name: 安装Flutter + if: steps.cache-flutter.outputs.cache-hit != 'true' + uses: subosito/flutter-action@v2.10.0 + with: + cache: true + flutter-version: 3.10.6 + + - name: flutter build ipa + run: | + flutter build ios --release --no-codesign + ln -sf ./build/ios/iphoneos Payload + zip -r9 app.ipa Payload/runner.app + + - name: 获取版本号 + id: version + run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT + + - name: 重命名应用 + run: | + DATE=${{ steps.date.outputs.date }} + for file in app.ipa; do + new_file_name="build/Pili-${{ steps.version.outputs.version }}.ipa" + mv "$file" "$new_file_name" + done + + - name: 上传 + uses: actions/upload-artifact@v3 + with: + if-no-files-found: error + name: Pilipala-Release + path: | + build/Pili-*.ipa + + upload: + runs-on: ubuntu-latest + + needs: + - android + - iOS + steps: + - uses: actions/download-artifact@v3 + with: + name: Pilipala-Release + path: ./Pilipala-Release + + - name: Install dependencies + run: sudo apt-get install tree -y + + - name: Get version + id: version + run: echo "version=${GITHUB_REF#refs/tags/v}" >>$GITHUB_OUTPUT + + - name: Upload Release + uses: ncipollo/release-action@v1 + with: + name: v${{ steps.version.outputs.version }} + token: ${{ secrets.GIT_TOKEN }} + omitBodyDuringUpdate: true + omitNameDuringUpdate: true + omitPrereleaseDuringUpdate: true + allowUpdates: true + artifacts: Pilipala-Release/* From 8169f5739cdc68765c8fc8b00b95d1bf9694afa8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Jan 2024 23:44:41 +0800 Subject: [PATCH 021/102] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5tabbar?= =?UTF-8?q?=E6=8E=92=E5=BA=8F=E6=97=A0=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/controller.dart | 11 +++++++---- lib/pages/setting/pages/home_tabbar_set.dart | 11 +++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index e6fafba6..685ff5c3 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -63,13 +63,16 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { } void setTabConfig() async { - defaultTabs = tabsConfig; + defaultTabs = [...tabsConfig]; tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort, defaultValue: ['live', 'rcmd', 'hot', 'bangumi']); + defaultTabs.retainWhere( + (item) => tabbarSort.contains((item['type'] as TabType).id)); + defaultTabs.sort((a, b) => tabbarSort + .indexOf((a['type'] as TabType).id) + .compareTo(tabbarSort.indexOf((b['type'] as TabType).id))); - tabs.value = defaultTabs - .where((i) => tabbarSort.contains((i['type'] as TabType).id)) - .toList(); + tabs.value = defaultTabs; if (tabbarSort.contains(TabType.rcmd.id)) { initialIndex.value = tabbarSort.indexOf(TabType.rcmd.id); diff --git a/lib/pages/setting/pages/home_tabbar_set.dart b/lib/pages/setting/pages/home_tabbar_set.dart index 445ca4f5..4cb3944c 100644 --- a/lib/pages/setting/pages/home_tabbar_set.dart +++ b/lib/pages/setting/pages/home_tabbar_set.dart @@ -22,6 +22,17 @@ class _TabbarSetPageState extends State { defaultTabs = tabsConfig; tabbarSort = settingStorage.get(SettingBoxKey.tabbarSort, defaultValue: ['live', 'rcmd', 'hot', 'bangumi']); + // 对 tabData 进行排序 + defaultTabs.sort((a, b) { + int indexA = tabbarSort.indexOf((a['type'] as TabType).id); + int indexB = tabbarSort.indexOf((b['type'] as TabType).id); + + // 如果类型在 sortOrder 中不存在,则放在末尾 + if (indexA == -1) indexA = tabbarSort.length; + if (indexB == -1) indexB = tabbarSort.length; + + return indexA.compareTo(indexB); + }); } void saveEdit() { From 699361e04c6e6df69a8090f2db31353d1d784b5f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Jan 2024 23:11:54 +0800 Subject: [PATCH 022/102] fix: aid --- lib/http/reply.dart | 4 ++-- lib/http/video.dart | 2 +- lib/pages/bangumi/introduction/controller.dart | 2 +- lib/pages/dynamics/detail/view.dart | 4 ++-- lib/pages/html/view.dart | 3 ++- lib/pages/video/detail/introduction/controller.dart | 2 +- lib/pages/video/detail/reply/controller.dart | 6 +++--- lib/pages/video/detail/reply/view.dart | 7 ++++--- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- lib/pages/video/detail/reply_new/view.dart | 2 +- 10 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/http/reply.dart b/lib/http/reply.dart index fab433fc..4c165b3b 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -4,7 +4,7 @@ import 'init.dart'; class ReplyHttp { static Future replyList({ - required int oid, + required dynamic oid, required int pageNum, required int type, int? ps, @@ -76,7 +76,7 @@ class ReplyHttp { // 评论点赞 static Future likeReply({ required int type, - required int oid, + required dynamic oid, required int rpid, required int action, }) async { diff --git a/lib/http/video.dart b/lib/http/video.dart index 923e93a2..7929be63 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -331,7 +331,7 @@ class VideoHttp { // plat num 发送平台标识 非必要 1:web端 2:安卓客户端 3:ios客户端 4:wp客户端 static Future replyAdd({ required ReplyType type, - required int oid, + required dynamic oid, required String message, int? root, int? parent, diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 13dd50c0..695bca3a 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -266,7 +266,7 @@ class BangumiIntroController extends GetxController { /// 未渲染回复组件时可能异常 VideoReplyController videoReplyCtr = Get.find(tag: Get.arguments['heroTag']); - videoReplyCtr.aid = aid; + videoReplyCtr.oid = bvid; videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} } diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 840cd33f..9489346f 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -385,8 +385,8 @@ class _DynamicDetailPageState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: _dynamicDetailController.oid ?? - IdUtils.bv2av(Get.parameters['bvid']!), + oid: _dynamicDetailController.oid?.toString() ?? + Get.parameters['bvid'], root: 0, parent: 0, replyType: ReplyType.values[replyType], diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart index 9f0c865c..7625306f 100644 --- a/lib/pages/html/view.dart +++ b/lib/pages/html/view.dart @@ -13,6 +13,7 @@ import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/pages/video/detail/reply_reply/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/id_utils.dart'; import 'controller.dart'; @@ -427,7 +428,7 @@ class _HtmlRenderPageState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: _htmlRenderCtr.oid.value, + oid: IdUtils.av2bv(_htmlRenderCtr.oid.value), root: 0, parent: 0, replyType: ReplyType.values[type], diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 1230a94b..73165117 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -478,7 +478,7 @@ class VideoIntroController extends GetxController { /// 未渲染回复组件时可能异常 final VideoReplyController videoReplyCtr = Get.find(tag: heroTag); - videoReplyCtr.aid = aid; + videoReplyCtr.oid = bvid; videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} this.bvid = bvid; diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 9e05542b..03c5fb0e 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -11,13 +11,13 @@ import 'package:pilipala/utils/storage.dart'; class VideoReplyController extends GetxController { VideoReplyController( - this.aid, + this.oid, this.rpid, this.replyLevel, ); final ScrollController scrollController = ScrollController(); // 视频aid 请求时使用的oid - int? aid; + String? oid; // 层级 2为楼中楼 String? replyLevel; // rpid 请求楼中楼回复 @@ -57,7 +57,7 @@ class VideoReplyController extends GetxController { return; } final res = await ReplyHttp.replyList( - oid: aid!, + oid: oid!, pageNum: currentPage + 1, ps: ps, type: ReplyType.video.index, diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index b2c67b1e..474615fc 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -40,6 +40,7 @@ class _VideoReplyPanelState extends State bool _isFabVisible = true; String replyLevel = '1'; late String heroTag; + late String oid; // 添加页面缓存 @override @@ -48,7 +49,7 @@ class _VideoReplyPanelState extends State @override void initState() { super.initState(); - int oid = widget.bvid != null ? IdUtils.bv2av(widget.bvid!) : 0; + oid = widget.bvid != null ? widget.bvid! : '0'; heroTag = Get.arguments['heroTag']; replyLevel = widget.replyLevel ?? '1'; if (replyLevel == '2') { @@ -297,8 +298,8 @@ class _VideoReplyPanelState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: _videoReplyController.aid ?? - IdUtils.bv2av(Get.parameters['bvid']!), + oid: + _videoReplyController.oid ?? Get.parameters['bvid'], root: 0, parent: 0, replyType: ReplyType.video, diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 48518bee..3352cdc3 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -353,7 +353,7 @@ class ReplyItem extends StatelessWidget { isScrollControlled: true, builder: (builder) { return VideoReplyNewDialog( - oid: replyItem!.oid, + oid: IdUtils.av2bv(replyItem!.oid!), root: replyItem!.rpid, parent: replyItem!.rpid, replyType: replyType, diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 01c95adc..61a78869 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -8,7 +8,7 @@ import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/utils/feed_back.dart'; class VideoReplyNewDialog extends StatefulWidget { - final int? oid; + final String? oid; final int? root; final int? parent; final ReplyType? replyType; From 163bb3c8da88ab843c8afef5526bad2d105a74b6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Jan 2024 23:17:00 +0800 Subject: [PATCH 023/102] =?UTF-8?q?v1.0.18=20=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- change_log/1.0.18.0130.md | 16 ++++++++++++++++ pubspec.yaml | 18 ++++++++---------- 2 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 change_log/1.0.18.0130.md diff --git a/change_log/1.0.18.0130.md b/change_log/1.0.18.0130.md new file mode 100644 index 00000000..2f0b80ca --- /dev/null +++ b/change_log/1.0.18.0130.md @@ -0,0 +1,16 @@ +## 1.0.18 + + +### 功能 + + +### 修复 + + +### 优化 + + + + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index d81781c8..81e6ca9f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.17+1017 +version: 1.0.18+1018 environment: sdk: ">=2.19.6 <3.0.0" @@ -51,7 +51,7 @@ dependencies: cached_network_image: ^3.3.0 extended_image: ^8.2.0 saver_gallery: ^3.0.1 - + # 存储 path_provider: ^2.1.1 hive: ^2.2.3 @@ -85,14 +85,14 @@ dependencies: encrypt: ^5.0.3 # 视频播放器 - media_kit: ^1.1.10 # Primary package. - media_kit_video: ^1.2.4 # For video rendering. + media_kit: ^1.1.10 # Primary package. + media_kit_video: ^1.2.4 # For video rendering. media_kit_libs_video: ^1.0.4 # 媒体通知 audio_service: ^0.18.12 audio_session: ^0.1.16 - + # 音量、亮度、屏幕控制 flutter_volume_controller: ^1.3.1 screen_brightness: ^0.2.2+1 @@ -103,7 +103,7 @@ dependencies: auto_orientation: ^2.3.1 protobuf: ^3.0.0 animations: ^2.0.8 - + # 获取appx信息 package_info_plus: ^4.1.0 url_launcher: ^6.1.14 @@ -116,7 +116,7 @@ dependencies: appscheme: ^1.0.8 # 弹幕 ns_danmaku: - git: + git: url: https://github.com/guozhigq/flutter_ns_danmaku.git ref: master # 状态栏图标控制 @@ -138,7 +138,6 @@ dependencies: logger: ^2.0.2+1 path: 1.8.3 - dev_dependencies: flutter_test: sdk: flutter @@ -207,7 +206,6 @@ flutter: # - family: HarmonyOS # fonts: # - asset: assets/fonts/HarmonyOS_Sans_SC_Regular.ttf - - + # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages From 7157f892457ae35f46058b2d849a4f800f1506e7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Jan 2024 23:23:12 +0800 Subject: [PATCH 024/102] Update main.yml --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e7f09f96..78230645 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -98,7 +98,7 @@ jobs: uses: subosito/flutter-action@v2.10.0 with: cache: true - flutter-version: 3.10.6 + flutter-version: 3.16.5 - name: flutter build ipa run: | From 9e93b50860ac997d789fe963dc4113fec5168a71 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 22:33:04 +0800 Subject: [PATCH 025/102] =?UTF-8?q?mod:=20=E8=BF=98=E5=8E=9Faid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/reply.dart | 4 ++-- lib/http/video.dart | 2 +- lib/pages/bangumi/introduction/controller.dart | 2 +- lib/pages/dynamics/detail/view.dart | 4 ++-- lib/pages/html/view.dart | 3 +-- lib/pages/video/detail/introduction/controller.dart | 2 +- lib/pages/video/detail/reply/controller.dart | 6 +++--- lib/pages/video/detail/reply/view.dart | 7 +++---- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- lib/pages/video/detail/reply_new/view.dart | 2 +- 10 files changed, 16 insertions(+), 18 deletions(-) diff --git a/lib/http/reply.dart b/lib/http/reply.dart index 4c165b3b..fab433fc 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -4,7 +4,7 @@ import 'init.dart'; class ReplyHttp { static Future replyList({ - required dynamic oid, + required int oid, required int pageNum, required int type, int? ps, @@ -76,7 +76,7 @@ class ReplyHttp { // 评论点赞 static Future likeReply({ required int type, - required dynamic oid, + required int oid, required int rpid, required int action, }) async { diff --git a/lib/http/video.dart b/lib/http/video.dart index 7929be63..923e93a2 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -331,7 +331,7 @@ class VideoHttp { // plat num 发送平台标识 非必要 1:web端 2:安卓客户端 3:ios客户端 4:wp客户端 static Future replyAdd({ required ReplyType type, - required dynamic oid, + required int oid, required String message, int? root, int? parent, diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 695bca3a..13dd50c0 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -266,7 +266,7 @@ class BangumiIntroController extends GetxController { /// 未渲染回复组件时可能异常 VideoReplyController videoReplyCtr = Get.find(tag: Get.arguments['heroTag']); - videoReplyCtr.oid = bvid; + videoReplyCtr.aid = aid; videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} } diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 9489346f..840cd33f 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -385,8 +385,8 @@ class _DynamicDetailPageState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: _dynamicDetailController.oid?.toString() ?? - Get.parameters['bvid'], + oid: _dynamicDetailController.oid ?? + IdUtils.bv2av(Get.parameters['bvid']!), root: 0, parent: 0, replyType: ReplyType.values[replyType], diff --git a/lib/pages/html/view.dart b/lib/pages/html/view.dart index 7625306f..9f0c865c 100644 --- a/lib/pages/html/view.dart +++ b/lib/pages/html/view.dart @@ -13,7 +13,6 @@ import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/pages/video/detail/reply_reply/index.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/id_utils.dart'; import 'controller.dart'; @@ -428,7 +427,7 @@ class _HtmlRenderPageState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: IdUtils.av2bv(_htmlRenderCtr.oid.value), + oid: _htmlRenderCtr.oid.value, root: 0, parent: 0, replyType: ReplyType.values[type], diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 73165117..1230a94b 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -478,7 +478,7 @@ class VideoIntroController extends GetxController { /// 未渲染回复组件时可能异常 final VideoReplyController videoReplyCtr = Get.find(tag: heroTag); - videoReplyCtr.oid = bvid; + videoReplyCtr.aid = aid; videoReplyCtr.queryReplyList(type: 'init'); } catch (_) {} this.bvid = bvid; diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 03c5fb0e..9e05542b 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -11,13 +11,13 @@ import 'package:pilipala/utils/storage.dart'; class VideoReplyController extends GetxController { VideoReplyController( - this.oid, + this.aid, this.rpid, this.replyLevel, ); final ScrollController scrollController = ScrollController(); // 视频aid 请求时使用的oid - String? oid; + int? aid; // 层级 2为楼中楼 String? replyLevel; // rpid 请求楼中楼回复 @@ -57,7 +57,7 @@ class VideoReplyController extends GetxController { return; } final res = await ReplyHttp.replyList( - oid: oid!, + oid: aid!, pageNum: currentPage + 1, ps: ps, type: ReplyType.video.index, diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 474615fc..b2c67b1e 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -40,7 +40,6 @@ class _VideoReplyPanelState extends State bool _isFabVisible = true; String replyLevel = '1'; late String heroTag; - late String oid; // 添加页面缓存 @override @@ -49,7 +48,7 @@ class _VideoReplyPanelState extends State @override void initState() { super.initState(); - oid = widget.bvid != null ? widget.bvid! : '0'; + int oid = widget.bvid != null ? IdUtils.bv2av(widget.bvid!) : 0; heroTag = Get.arguments['heroTag']; replyLevel = widget.replyLevel ?? '1'; if (replyLevel == '2') { @@ -298,8 +297,8 @@ class _VideoReplyPanelState extends State isScrollControlled: true, builder: (BuildContext context) { return VideoReplyNewDialog( - oid: - _videoReplyController.oid ?? Get.parameters['bvid'], + oid: _videoReplyController.aid ?? + IdUtils.bv2av(Get.parameters['bvid']!), root: 0, parent: 0, replyType: ReplyType.video, diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 3352cdc3..48518bee 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -353,7 +353,7 @@ class ReplyItem extends StatelessWidget { isScrollControlled: true, builder: (builder) { return VideoReplyNewDialog( - oid: IdUtils.av2bv(replyItem!.oid!), + oid: replyItem!.oid, root: replyItem!.rpid, parent: replyItem!.rpid, replyType: replyType, diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 61a78869..01c95adc 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -8,7 +8,7 @@ import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/utils/feed_back.dart'; class VideoReplyNewDialog extends StatefulWidget { - final String? oid; + final int? oid; final int? root; final int? parent; final ReplyType? replyType; From d703e38c3fd9599f08cd37f31a22f0e8fcd5afb3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 22:43:40 +0800 Subject: [PATCH 026/102] =?UTF-8?q?fix:=20avbv=E8=BD=AC=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/id_utils.dart | 79 +++++++++++++++++++++-------------------- 1 file changed, 40 insertions(+), 39 deletions(-) diff --git a/lib/utils/id_utils.dart b/lib/utils/id_utils.dart index a68bfb40..d0451b17 100644 --- a/lib/utils/id_utils.dart +++ b/lib/utils/id_utils.dart @@ -1,51 +1,52 @@ -// ignore_for_file: constant_identifier_names +// ignore_for_file: constant_identifier_names, non_constant_identifier_names import 'dart:convert'; -import 'dart:math'; - -import 'package:flutter/material.dart'; class IdUtils { - static const String TABLE = - 'fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF'; - static const List S = [11, 10, 3, 8, 4, 6]; // 位置编码表 - static const int XOR = 177451812; // 固定异或值 - static const int ADD = 8728348608; // 固定加法值 - static const List r = [ - 'B', - 'V', - '1', - '', - '', - '4', - '', - '1', - '', - '7', - '', - '' - ]; + static final XOR_CODE = BigInt.parse('23442827791579'); + static final MASK_CODE = BigInt.parse('2251799813685247'); + static final MAX_AID = BigInt.one << (BigInt.from(51)).toInt(); + static final BASE = BigInt.from(58); + + static const data = + 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf'; /// av转bv - static String av2bv(int av) { - int x_ = (av ^ XOR) + ADD; - List newR = []; - newR.addAll(r); - for (int i = 0; i < S.length; i++) { - newR[S[i]] = - TABLE.characters.elementAt((x_ / pow(58, i).toInt() % 58).toInt()); + static String av2bv(int aid) { + List bytes = List.filled(12, '0', growable: false); + int bvIndex = bytes.length - 1; + BigInt tmp = (MAX_AID | BigInt.from(aid)) ^ XOR_CODE; + while (tmp > BigInt.zero) { + bytes[bvIndex] = data[(tmp % BASE).toInt()]; + tmp = tmp ~/ BASE; + bvIndex -= 1; } - return newR.join(); + final tmpValue = bytes[3]; + bytes[3] = bytes[9]; + bytes[9] = tmpValue; + + final tmpValue2 = bytes[4]; + bytes[4] = bytes[7]; + bytes[7] = tmpValue2; + + return bytes.join(); } - /// bv转bv - static int bv2av(String bv) { - int r = 0; - for (int i = 0; i < S.length; i++) { - r += (TABLE.indexOf(bv.characters.elementAt(S[i])).toInt()) * - pow(58, i).toInt(); - } - return (r - ADD) ^ XOR; + /// bv转av + static int bv2av(String bvid) { + List bvidArr = bvid.split(''); + final tmpValue = bvidArr[3]; + bvidArr[3] = bvidArr[9]; + bvidArr[9] = tmpValue; + + final tmpValue2 = bvidArr[4]; + bvidArr[4] = bvidArr[7]; + bvidArr[7] = tmpValue2; + + bvidArr.removeRange(0, 3); + BigInt tmp = bvidArr.fold(BigInt.zero, + (pre, bvidChar) => pre * BASE + BigInt.from(data.indexOf(bvidChar))); + return ((tmp & MASK_CODE) ^ XOR_CODE).toInt(); } // 匹配 From 8a2c0233430a2c5e31498b4fa14943131df85fd4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 23:03:45 +0800 Subject: [PATCH 027/102] fix: magType value --- .../whisper_detail/widget/chat_item.dart | 56 +++++++++++-------- 1 file changed, 33 insertions(+), 23 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 9f0e191b..41df23ab 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -5,7 +5,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:get/get_core/src/get_main.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/storage.dart'; @@ -53,12 +52,13 @@ class ChatItem extends StatelessWidget { Widget build(BuildContext context) { bool isOwner = item.senderUid == GStrorage.userInfo.get('userInfoCache').mid; - bool isPic = item.msgType == MsgType.pic; // 图片 - bool isText = item.msgType == MsgType.text; // 文本 + + bool isPic = item.msgType == MsgType.pic.value; // 图片 + bool isText = item.msgType == MsgType.text.value; // 文本 // bool isArchive = item.msgType == 11; // 投稿 // bool isArticle = item.msgType == 12; // 专栏 - bool isRevoke = item.msgType == MsgType.revoke; // 撤回消息 - bool isShareV2 = item.msgType == MsgType.share_v2; + bool isRevoke = item.msgType == MsgType.revoke.value; // 撤回消息 + bool isShareV2 = item.msgType == MsgType.share_v2.value; bool isSystem = item.msgType == 18 || item.msgType == 10 || item.msgType == 13; dynamic content = item.content ?? ''; @@ -72,7 +72,7 @@ class ChatItem extends StatelessWidget { var text = content['content']; if (e_infos != null) { final List children = []; - Map emojiMap = {}; + Map emojiMap = {}; for (var e in e_infos!) { emojiMap[e['text']] = e['url']; } @@ -83,18 +83,22 @@ class ChatItem extends StatelessWidget { if (emojiMap.containsKey(emojiKey)) { children.add(WidgetSpan( child: NetworkImgLayer( - width: 18, height: 18, - src: emojiMap[emojiKey]!,), + width: 18, + height: 18, + src: emojiMap[emojiKey]!, + ), )); } return ''; }, onNonMatch: (String text) { - children.add(TextSpan(text: text, style: TextStyle( - color: textColor(context), - letterSpacing: 0.6, - height: 1.5, - ))); + children.add(TextSpan( + text: text, + style: TextStyle( + color: textColor(context), + letterSpacing: 0.6, + height: 1.5, + ))); return ''; }, ); @@ -123,11 +127,13 @@ class ChatItem extends StatelessWidget { return SystemNotice2(item: item); case MsgType.notify_text: return Text( - jsonDecode(content['content']).map((m) => m['text'] as String).join("\n"), + jsonDecode(content['content']) + .map((m) => m['text'] as String) + .join("\n"), style: TextStyle( - letterSpacing: 0.6, - height: 5, - color: Theme.of(context).colorScheme.outline.withOpacity(0.8) + letterSpacing: 0.6, + height: 5, + color: Theme.of(context).colorScheme.outline.withOpacity(0.8), ), ); case MsgType.text: @@ -166,9 +172,11 @@ class ChatItem extends StatelessWidget { Text( content['title'], style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), fontWeight: FontWeight.bold), + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, + ), ), const SizedBox(height: 1), Text( @@ -186,9 +194,11 @@ class ChatItem extends StatelessWidget { return Text( content['content'] ?? content.toString(), style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), fontWeight: FontWeight.bold), + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, + ), ); } } From ea8af288289a3d45203e3eec4e32a1eec8f86600 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 23:11:03 +0800 Subject: [PATCH 028/102] =?UTF-8?q?fix:=20=E4=B8=93=E6=A0=8F=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E5=9B=BE=E5=B0=BA=E5=AF=B8=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/search_panel/widgets/article_panel.dart | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pages/search_panel/widgets/article_panel.dart b/lib/pages/search_panel/widgets/article_panel.dart index 35e39640..c7074229 100644 --- a/lib/pages/search_panel/widgets/article_panel.dart +++ b/lib/pages/search_panel/widgets/article_panel.dart @@ -25,16 +25,17 @@ Widget searchArticlePanel(BuildContext context, ctr, list) { padding: const EdgeInsets.fromLTRB( StyleString.safeSpace, 5, StyleString.safeSpace, 5), child: LayoutBuilder(builder: (context, boxConstraints) { - double width = (boxConstraints.maxWidth - - StyleString.cardSpace * - 6 / - MediaQuery.textScalerOf(context).scale(2.0)); + final double width = (boxConstraints.maxWidth - + StyleString.cardSpace * + 6 / + MediaQuery.textScalerOf(context).scale(1.0)) / + 2; return Container( constraints: const BoxConstraints(minHeight: 88), height: width / StyleString.aspectRatio, child: Row( crossAxisAlignment: CrossAxisAlignment.start, - children: [ + children: [ if (list[index].imageUrls != null && list[index].imageUrls.isNotEmpty) AspectRatio( From 89a43b1285b66a7caacdc46bcfc2217f820cf8ce Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 31 Jan 2024 23:28:51 +0800 Subject: [PATCH 029/102] =?UTF-8?q?v1.0.19=20=E6=9B=B4=E6=96=B0=E6=97=A5?= =?UTF-8?q?=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- change_log/1.0.19.0131.md | 15 +++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.19.0131.md diff --git a/change_log/1.0.19.0131.md b/change_log/1.0.19.0131.md new file mode 100644 index 00000000..1fd3071b --- /dev/null +++ b/change_log/1.0.19.0131.md @@ -0,0 +1,15 @@ +## 1.0.19 + + +### 修复 ++ 视频404、评论加载错误 ++ bvav转换 + +### 优化 ++ 视频详情页内存占用 + + + + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index 81e6ca9f..f4b8fe61 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 1.0.18+1018 +version: 1.0.19+1019 environment: sdk: ">=2.19.6 <3.0.0" From e24ccc16fae4cb252c65c8188a4f096a94cc44d2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 1 Feb 2024 00:32:52 +0800 Subject: [PATCH 030/102] =?UTF-8?q?mod:=20av2bv=E6=96=B9=E6=B3=95=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/id_utils.dart | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/utils/id_utils.dart b/lib/utils/id_utils.dart index d0451b17..906f6348 100644 --- a/lib/utils/id_utils.dart +++ b/lib/utils/id_utils.dart @@ -13,7 +13,20 @@ class IdUtils { /// av转bv static String av2bv(int aid) { - List bytes = List.filled(12, '0', growable: false); + List bytes = [ + 'B', + 'V', + '1', + '0', + '0', + '0', + '0', + '0', + '0', + '0', + '0', + '0' + ]; int bvIndex = bytes.length - 1; BigInt tmp = (MAX_AID | BigInt.from(aid)) ^ XOR_CODE; while (tmp > BigInt.zero) { @@ -21,13 +34,13 @@ class IdUtils { tmp = tmp ~/ BASE; bvIndex -= 1; } - final tmpValue = bytes[3]; + String tmpSwap = bytes[3]; bytes[3] = bytes[9]; - bytes[9] = tmpValue; + bytes[9] = tmpSwap; - final tmpValue2 = bytes[4]; + tmpSwap = bytes[4]; bytes[4] = bytes[7]; - bytes[7] = tmpValue2; + bytes[7] = tmpSwap; return bytes.join(); } From 85a59e11b961de26d781308619b3386619439507 Mon Sep 17 00:00:00 2001 From: orz12 Date: Thu, 1 Feb 2024 22:56:29 +0800 Subject: [PATCH 031/102] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E5=85=B3=E9=94=AE=E8=AF=8D=E6=97=B6=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E5=8C=B9=E9=85=8D=E6=97=B6=E9=97=B4=E3=80=81=E4=BF=AE=E5=A4=8D?= =?UTF-8?q?=E4=B8=8D=E6=98=BE=E7=A4=BA=E5=85=B3=E9=94=AE=E8=AF=8D=E6=97=B6?= =?UTF-8?q?=E4=B8=8D=E6=9B=BF=E6=8D=A2=E8=B6=85=E9=93=BE=E6=8E=A5=E3=80=81?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E6=B7=BB=E5=8A=A0=E4=B8=AD=E6=96=87=E5=86=92?= =?UTF-8?q?=E5=8F=B7=E5=8C=B9=E9=85=8D=E5=B9=B6=E6=8F=90=E5=8D=87=E5=88=86?= =?UTF-8?q?=E6=94=AF=E5=88=A4=E5=AE=9A=E4=B8=A5=E6=A0=BC=E7=A8=8B=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 98d8ba6a..cab9d420 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -595,10 +595,21 @@ InlineSpan buildContent( e.replaceAll('?', '\\?').replaceAll('+', '\\+').replaceAll('*', '\\*')), ]; - final String patternStr = - specialTokens.map(RegExp.escape).join('|') + r'|\b[0-9]{1,2}:[0-9]{2}\b'; + String patternStr = + specialTokens.map(RegExp.escape).join('|'); + if (patternStr.isNotEmpty) { + patternStr += "|"; + } + patternStr += r'(\b\d{1,2}[::]\d{2}\b)'; final RegExp pattern = RegExp(patternStr); List matchedStrs = []; + void addPlainTextSpan(str){ + spanChilds.add(TextSpan( + text: str, + recognizer: TapGestureRecognizer() + ..onTap = () => + replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); + } // 分割文本并处理每个部分 content.message.splitMapJoin( pattern, @@ -636,7 +647,7 @@ InlineSpan buildContent( }, ), ); - } else if (matchStr.contains(':')) { + } else if (RegExp(r'^\b[0-9]{1,2}[::][0-9]{2}\b$').hasMatch(matchStr)) { spanChilds.add( TextSpan( text: ' $matchStr ', @@ -647,6 +658,7 @@ InlineSpan buildContent( ..onTap = () { // 跳转到指定位置 try { + matchStr = matchStr.replaceAll(':', ':'); SmartDialog.showToast('跳转至:$matchStr'); Get.find(tag: Get.arguments['heroTag']) .plPlayerController @@ -664,10 +676,13 @@ InlineSpan buildContent( String appUrlSchema = ''; final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, defaultValue: false) as bool; - if (enableWordRe && - content.jumpUrl[matchStr] != null && + if (content.jumpUrl[matchStr] != null && !matchedStrs.contains(matchStr)) { appUrlSchema = content.jumpUrl[matchStr]['app_url_schema']; + if (appUrlSchema.startsWith('bilibili://search') && !enableWordRe) { + addPlainTextSpan(matchStr); + return ""; + } spanChilds.add( TextSpan( text: content.jumpUrl[matchStr]['title'], @@ -707,7 +722,7 @@ InlineSpan buildContent( }, ), ); - if (appUrlSchema.startsWith('bilibili://search') && enableWordRe) { + if (appUrlSchema.startsWith('bilibili://search')) { spanChilds.add( WidgetSpan( child: Icon( @@ -722,21 +737,13 @@ InlineSpan buildContent( // 只显示一次 matchedStrs.add(matchStr); } else { - spanChilds.add(TextSpan( - text: matchStr, - recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); + addPlainTextSpan(matchStr); } } return ''; }, onNonMatch: (String nonMatchStr) { - spanChilds.add(TextSpan( - text: nonMatchStr, - recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); + addPlainTextSpan(nonMatchStr); return nonMatchStr; }, ); From 81371c5a31538425af93c020943cc0782b92da4d Mon Sep 17 00:00:00 2001 From: orz12 Date: Fri, 2 Feb 2024 00:06:46 +0800 Subject: [PATCH 032/102] =?UTF-8?q?fix:=20=E5=8F=AA=E6=9C=89=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E7=9A=84=E8=AF=84=E8=AE=BA=E5=8C=BA=E4=B8=8D=E9=AB=98?= =?UTF-8?q?=E4=BA=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index cab9d420..48503d30 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -544,7 +544,9 @@ InlineSpan buildContent( content.atNameToMid.isEmpty && content.jumpUrl.isEmpty && content.vote.isEmpty && - content.pictures.isEmpty) { + content.pictures.isEmpty && + content.message.isNotEmpty && + !RegExp(r'\b[0-9]{1,2}[::][0-9]{2}\b').hasMatch(content.message)) { return TextSpan( text: content.message, recognizer: TapGestureRecognizer() From 0b349e102ebf77467205d4f298b9cef6b81f250e Mon Sep 17 00:00:00 2001 From: orz12 Date: Fri, 2 Feb 2024 00:34:52 +0800 Subject: [PATCH 033/102] =?UTF-8?q?fix=EF=BC=9A=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?HTML=E5=AE=9E=E4=BD=93=E8=BD=AC=E4=B9=89=EF=BC=9B=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E9=94=99=E8=AF=AF=E7=9F=AD=E8=B7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 27 ++++++------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 48503d30..2867474e 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -540,20 +540,6 @@ InlineSpan buildContent( // replyReply 查看二楼回复(回复详情)回调 // fReplyItem 父级回复内容,用作二楼回复(回复详情)展示 final content = replyItem.content; - if (content.emote.isEmpty && - content.atNameToMid.isEmpty && - content.jumpUrl.isEmpty && - content.vote.isEmpty && - content.pictures.isEmpty && - content.message.isNotEmpty && - !RegExp(r'\b[0-9]{1,2}[::][0-9]{2}\b').hasMatch(content.message)) { - return TextSpan( - text: content.message, - recognizer: TapGestureRecognizer() - ..onTap = - () => replyReply(replyItem.root == 0 ? replyItem : fReplyItem), - ); - } final List spanChilds = []; bool hasMatchMember = false; @@ -585,10 +571,13 @@ InlineSpan buildContent( }); } // content.message = content.message.replaceAll(RegExp(r"\{vote:.*?\}"), ' '); - if (content.message.contains('&')) { - content.message = content.message.replaceAll('&', '&'); - } - print("content.jumpUrl.keys:" + content.jumpUrl.keys.toString()); + content.message = content.message.replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll(''', "'") + .replaceAll(' ', ' '); + // print("content.jumpUrl.keys:" + content.jumpUrl.keys.toString()); // 构建正则表达式 final List specialTokens = [ ...content.emote.keys, @@ -674,7 +663,7 @@ InlineSpan buildContent( ), ); } else { - print("matchStr=$matchStr"); + // print("matchStr=$matchStr"); String appUrlSchema = ''; final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, defaultValue: false) as bool; From 33f61ac0faca92f4932c9dda30a62660c5d96f8f Mon Sep 17 00:00:00 2001 From: orz12 Date: Fri, 2 Feb 2024 01:16:02 +0800 Subject: [PATCH 034/102] =?UTF-8?q?fix:=20=E6=9F=A5=E8=AF=A2=E5=9C=A8?= =?UTF-8?q?=E7=BA=BF=E4=BA=BA=E6=95=B0=E9=94=99=E8=AF=AF=E6=97=B6=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E8=BF=94=E5=9B=9Estatus?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/http/video.dart b/lib/http/video.dart index e7b09801..0e869512 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -433,6 +433,8 @@ class VideoHttp { }); if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; + } else { + return {'status': false, 'data': null, 'msg': res.data['message']}; } } From 5b1022628cd0d2c208da1c1db10e2bdfe9c6a053 Mon Sep 17 00:00:00 2001 From: orz12 Date: Fri, 2 Feb 2024 02:34:14 +0800 Subject: [PATCH 035/102] =?UTF-8?q?fix:=20=E4=B9=9D=E5=9B=BE=E9=83=A8?= =?UTF-8?q?=E5=88=86=E4=BD=8D=E7=BD=AE=E6=97=A0=E6=B3=95=E7=82=B9=E5=87=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 2867474e..5e5a5828 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -743,10 +743,10 @@ InlineSpan buildContent( if (content.pictures.isNotEmpty) { final List picList = []; final int len = content.pictures.length; + spanChilds.add(const TextSpan(text: '\n')); if (len == 1) { Map pictureItem = content.pictures.first; picList.add(pictureItem['img_src']); - spanChilds.add(const TextSpan(text: '\n')); spanChilds.add( WidgetSpan( child: LayoutBuilder( From eb1e2ca5f4920c025f9b39976659a9c327a52f27 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 2 Feb 2024 23:24:36 +0800 Subject: [PATCH 036/102] =?UTF-8?q?fix:=20=E4=B8=AA=E4=BA=BA=E9=9D=A2?= =?UTF-8?q?=E6=9D=BF=E6=97=A0=E6=B3=95=E8=B7=B3=E8=BD=AC=E8=AE=BE=E7=BD=AE?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/mine/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/mine/view.dart b/lib/pages/mine/view.dart index 06c375da..091b2149 100644 --- a/lib/pages/mine/view.dart +++ b/lib/pages/mine/view.dart @@ -64,7 +64,7 @@ class _MinePageState extends State { ), ), IconButton( - onPressed: () => Get.toNamed('/setting'), + onPressed: () => Get.toNamed('/setting', preventDuplicates: false), icon: const Icon( CupertinoIcons.slider_horizontal_3, ), From 1da84508d8f6b6fb7e5060e9972e97c6915f0464 Mon Sep 17 00:00:00 2001 From: orz12 Date: Sat, 3 Feb 2024 00:22:58 +0800 Subject: [PATCH 037/102] =?UTF-8?q?feat:=20=E8=87=AA=E5=8A=A8=E6=8E=A8?= =?UTF-8?q?=E9=80=81=E5=9B=9E=E5=A4=8D=E7=A7=81=E4=BF=A1=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whisper_detail/widget/chat_item.dart | 164 +++++++++++++++++- 1 file changed, 162 insertions(+), 2 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 41df23ab..0925d569 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -27,6 +27,7 @@ enum MsgType { article_card(value: 12, label: "专栏卡片"), pic_card(value: 13, label: "图片卡片"), common_share(value: 14, label: "异形卡片"), + auto_reply_push(value: 16, label: "自动回复推送"), notify_text(value: 18, label: "文本提示"); final int value; @@ -59,8 +60,10 @@ class ChatItem extends StatelessWidget { // bool isArticle = item.msgType == 12; // 专栏 bool isRevoke = item.msgType == MsgType.revoke.value; // 撤回消息 bool isShareV2 = item.msgType == MsgType.share_v2.value; - bool isSystem = - item.msgType == 18 || item.msgType == 10 || item.msgType == 13; + bool isSystem = item.msgType == MsgType.notify_text.value || + item.msgType == MsgType.notify_msg.value || + item.msgType == MsgType.pic_card.value || + item.msgType == MsgType.auto_reply_push.value; dynamic content = item.content ?? ''; Color textColor(BuildContext context) { return isOwner @@ -190,6 +193,163 @@ class ChatItem extends StatelessWidget { ), ], ); + case MsgType.archive_card: + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () async { + SmartDialog.showLoading(); + var bvid = content["bvid"]; + 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': content['thumb'], + 'heroTag': heroTag, + }), + ); + }, + child: NetworkImgLayer( + width: 220, + height: 220 * 9 / 16, + src: content['cover'], + ), + ), + const SizedBox(height: 6), + Text( + content['title'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, + ), + ), + const SizedBox(height: 1), + Text( + Utils.timeFormat(content['times']), + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context).withOpacity(0.6), + fontSize: 12, + ), + ), + ], + ); + case MsgType.auto_reply_push: + return Container( + constraints: const BoxConstraints( + maxWidth: 300.0, // 设置最大宽度为200.0 + ), + 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, + ), + ), + 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: [ + 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['content'] ?? content.toString(), From b5315998938faf3047849c44dde3b9e8cb97e79a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 3 Feb 2024 00:29:47 +0800 Subject: [PATCH 038/102] =?UTF-8?q?mod:=20floating=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 9 +++++---- pubspec.yaml | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index d9b6a01d..f5d63ca9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -500,10 +500,11 @@ packages: floating: dependency: "direct main" description: - name: floating - sha256: d9d563089e34fbd714ffdcdd2df447ec41b40c9226dacae6b4f78847aef8b991 - url: "https://pub.flutter-io.cn" - source: hosted + path: "." + ref: main + resolved-ref: d2d8421c4d80f6113f832404109853684721e11a + url: "https://github.com/guozhigq/floating.git" + source: git version: "2.0.1" flutter: dependency: "direct main" diff --git a/pubspec.yaml b/pubspec.yaml index f4b8fe61..16df4ad7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -124,7 +124,10 @@ dependencies: # 代理 system_proxy: ^0.1.0 # pip - floating: ^2.0.1 + floating: + git: + url: https://github.com/guozhigq/floating.git + ref: main # html解析 html: ^0.15.4 # html渲染 From 045186b3c8370e1f4da0280959834b44023762e8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 3 Feb 2024 00:33:29 +0800 Subject: [PATCH 039/102] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E5=93=8D=E5=BA=94status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index 0e869512..6ffbde21 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -361,7 +361,7 @@ class VideoHttp { if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; } else { - return {'status': true, 'data': []}; + return {'status': false, 'data': []}; } } @@ -377,7 +377,7 @@ class VideoHttp { if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; } else { - return {'status': true, 'data': []}; + return {'status': false, 'data': []}; } } @@ -455,10 +455,7 @@ class VideoHttp { 'data': AiConclusionModel.fromJson(res.data['data']), }; } else { - return { - 'status': false, - 'data': [] - }; + return {'status': false, 'data': []}; } } } From 18bb58d293ed48bf84f5590d540ec92328b35142 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 3 Feb 2024 00:43:38 +0800 Subject: [PATCH 040/102] =?UTF-8?q?mod:=20=E6=8A=95=E5=B8=81=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=93=8D=E5=BA=94status?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 3 ++- lib/pages/video/detail/introduction/controller.dart | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index 6ffbde21..eaa131f3 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -224,10 +224,11 @@ class VideoHttp { // 获取投币状态 static Future hasCoinVideo({required String bvid}) async { var res = await Request().get(Api.hasCoinVideo, data: {'bvid': bvid}); + print('res: $res'); if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; } else { - return {'status': true, 'data': []}; + return {'status': false, 'data': []}; } } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 1deadb07..b1b02420 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -148,7 +148,9 @@ class VideoIntroController extends GetxController { // 获取投币状态 Future queryHasCoinVideo() async { var result = await VideoHttp.hasCoinVideo(bvid: bvid); - hasCoin.value = result["data"]['multiply'] == 0 ? false : true; + if (result['status']) { + hasCoin.value = result["data"]['multiply'] == 0 ? false : true; + } } // 获取收藏状态 From 6a844da259cb707696282fe398f80289891da4fc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 3 Feb 2024 00:53:18 +0800 Subject: [PATCH 041/102] =?UTF-8?q?mod:=20=E7=82=B9=E8=B5=9E=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E7=99=BB=E5=BD=95=E6=8B=A6=E6=88=AA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/controller.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index b1b02420..d1298fcc 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -210,6 +210,10 @@ class VideoIntroController extends GetxController { // (取消)点赞 Future actionLikeVideo() async { + if (userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value); if (result['status']) { // hasLike.value = result["data"] == 1 ? true : false; From 47bdfec8c2c3446f178861002bb10e2da6493067 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 3 Feb 2024 01:07:12 +0800 Subject: [PATCH 042/102] fix: github assets null error --- lib/models/github/latest.dart | 5 +++-- lib/utils/utils.dart | 18 ++++++++++-------- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/models/github/latest.dart b/lib/models/github/latest.dart index 8730a4ba..c4b88b63 100644 --- a/lib/models/github/latest.dart +++ b/lib/models/github/latest.dart @@ -17,8 +17,9 @@ class LatestDataModel { url = json['url']; tagName = json['tag_name']; createdAt = json['created_at']; - assets = - json['assets'].map((e) => AssetItem.fromJson(e)).toList(); + assets = json['assets'] != null + ? json['assets'].map((e) => AssetItem.fromJson(e)).toList() + : []; body = json['body']; } } diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 08693d24..e17ed5ef 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -276,16 +276,18 @@ class Utils { // [arm64-v8a] String abi = androidInfo.supportedAbis.first; late String downloadUrl; - for (var i in data.assets) { - if (i.downloadUrl.contains(abi)) { - downloadUrl = i.downloadUrl; + if (data.assets.isNotEmpty) { + for (var i in data.assets) { + if (i.downloadUrl.contains(abi)) { + downloadUrl = i.downloadUrl; + } } + // 应用外下载 + launchUrl( + Uri.parse(downloadUrl), + mode: LaunchMode.externalApplication, + ); } - // 应用外下载 - launchUrl( - Uri.parse(downloadUrl), - mode: LaunchMode.externalApplication, - ); } } From 230dd813427e692151cdf9bb8dc8b719dcc6d9d8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 3 Feb 2024 01:13:36 +0800 Subject: [PATCH 043/102] =?UTF-8?q?fix:=20List=20=E8=B6=8A=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/services/audio_handler.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index 61b32b96..ad510e7d 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -147,8 +147,8 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { processingState: AudioProcessingState.idle, playing: false, )); - _item.removeLast(); if (_item.isNotEmpty) { + _item.removeLast(); setMediaItem(_item.last); } if (_item.isEmpty) { From 387c799de1d86a580d750bf67beb256e87c72134 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 3 Feb 2024 16:59:54 +0800 Subject: [PATCH 044/102] =?UTF-8?q?feat:=20=E5=8A=A8=E6=80=81=E6=9C=AA?= =?UTF-8?q?=E8=AF=BB=E6=A0=87=E8=AE=B0=20issues=20#459?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/dynamic_badge_mode.dart | 9 +++++ lib/pages/main/controller.dart | 9 ++++- lib/pages/main/view.dart | 41 +++++++++++++++++------ lib/pages/setting/controller.dart | 34 +++++++++++++++++++ lib/pages/setting/style_setting.dart | 9 +++++ lib/utils/storage.dart | 3 +- 6 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 lib/models/common/dynamic_badge_mode.dart diff --git a/lib/models/common/dynamic_badge_mode.dart b/lib/models/common/dynamic_badge_mode.dart new file mode 100644 index 00000000..2609c5e2 --- /dev/null +++ b/lib/models/common/dynamic_badge_mode.dart @@ -0,0 +1,9 @@ +enum DynamicBadgeMode { hidden, point, number } + +extension DynamicBadgeModeDesc on DynamicBadgeMode { + String get description => ['隐藏', '红点', '数字'][index]; +} + +extension DynamicBadgeModeCode on DynamicBadgeMode { + int get code => [0, 1, 2][index]; +} diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index a55c143e..6f33d9cd 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -11,6 +11,7 @@ import 'package:pilipala/pages/home/view.dart'; import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; +import '../../models/common/dynamic_badge_mode.dart'; class MainController extends GetxController { List pages = [ @@ -65,6 +66,7 @@ class MainController extends GetxController { int selectedIndex = 0; Box userInfoCache = GStrorage.userInfo; RxBool userLogin = false.obs; + late Rx dynamicBadgeType = DynamicBadgeMode.number.obs; @override void onInit() { @@ -75,7 +77,12 @@ class MainController extends GetxController { hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true); var userInfo = userInfoCache.get('userInfoCache'); userLogin.value = userInfo != null; - getUnreadDynamic(); + dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( + SettingBoxKey.dynamicBadgeMode, + defaultValue: DynamicBadgeMode.number.code)]; + if (dynamicBadgeType.value != DynamicBadgeMode.hidden) { + getUnreadDynamic(); + } } void onBackPressed(BuildContext context) { diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 5a570a8f..04e0f087 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/models/common/dynamic_badge_mode.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/media/index.dart'; @@ -127,11 +128,21 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { destinations: [ ..._mainController.navigationBars.map((e) { return NavigationDestination( - icon: Badge( - label: Text(e['count'].toString()), - padding: const EdgeInsets.fromLTRB(6, 0, 6, 0), - isLabelVisible: e['count'] > 0, - child: e['icon'], + icon: Obx( + () => Badge( + label: + _mainController.dynamicBadgeType.value == + DynamicBadgeMode.number + ? Text(e['count'].toString()) + : null, + padding: + const EdgeInsets.fromLTRB(6, 0, 6, 0), + isLabelVisible: + _mainController.dynamicBadgeType.value != + DynamicBadgeMode.hidden && + e['count'] > 0, + child: e['icon'], + ), ), selectedIcon: e['selectIcon'], label: e['label'], @@ -148,11 +159,21 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { items: [ ..._mainController.navigationBars.map((e) { return BottomNavigationBarItem( - icon: Badge( - label: Text(e['count'].toString()), - padding: const EdgeInsets.fromLTRB(6, 0, 6, 0), - isLabelVisible: e['count'] > 0, - child: e['icon'], + icon: Obx( + () => Badge( + label: + _mainController.dynamicBadgeType.value == + DynamicBadgeMode.number + ? Text(e['count'].toString()) + : null, + padding: + const EdgeInsets.fromLTRB(6, 0, 6, 0), + isLabelVisible: + _mainController.dynamicBadgeType.value != + DynamicBadgeMode.hidden && + e['count'] > 0, + child: e['icon'], + ), ), activeIcon: e['selectIcon'], label: e['label'], diff --git a/lib/pages/setting/controller.dart b/lib/pages/setting/controller.dart index afd14156..2e6680e5 100644 --- a/lib/pages/setting/controller.dart +++ b/lib/pages/setting/controller.dart @@ -7,6 +7,9 @@ import 'package:pilipala/models/common/theme_type.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/login.dart'; import 'package:pilipala/utils/storage.dart'; +import '../../models/common/dynamic_badge_mode.dart'; +import '../main/index.dart'; +import 'widgets/select_dialog.dart'; class SettingController extends GetxController { Box userInfoCache = GStrorage.userInfo; @@ -19,6 +22,7 @@ class SettingController extends GetxController { RxInt picQuality = 10.obs; Rx themeType = ThemeType.system.obs; var userInfo; + Rx dynamicBadgeType = DynamicBadgeMode.number.obs; @override void onInit() { @@ -33,6 +37,9 @@ class SettingController extends GetxController { setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); themeType.value = ThemeType.values[setting.get(SettingBoxKey.themeMode, defaultValue: ThemeType.system.code)]; + dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( + SettingBoxKey.dynamicBadgeMode, + defaultValue: DynamicBadgeMode.number.code)]; } loginOut() async { @@ -76,4 +83,31 @@ class SettingController extends GetxController { feedBackEnable.value = !feedBackEnable.value; setting.put(SettingBoxKey.feedBackEnable, feedBackEnable.value); } + + // 设置动态未读标记 + setDynamicBadgeMode(BuildContext context) async { + DynamicBadgeMode? result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '动态未读标记', + value: dynamicBadgeType.value, + values: DynamicBadgeMode.values.map((e) { + return {'title': e.description, 'value': e}; + }).toList(), + ); + }, + ); + if (result != null) { + dynamicBadgeType.value = result; + setting.put(SettingBoxKey.dynamicBadgeMode, result.code); + MainController mainController = Get.put(MainController()); + mainController.dynamicBadgeType.value = + DynamicBadgeMode.values[result.code]; + if (mainController.dynamicBadgeType.value != DynamicBadgeMode.hidden) { + mainController.getUnreadDynamic(); + } + SmartDialog.showToast('设置成功'); + } + } } diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 57ec110a..fb3c780a 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -10,6 +10,7 @@ import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; import 'package:pilipala/pages/setting/widgets/slide_dialog.dart'; import 'package:pilipala/utils/storage.dart'; +import '../../models/common/dynamic_badge_mode.dart'; import 'controller.dart'; import 'widgets/switch_item.dart'; @@ -241,6 +242,14 @@ class _StyleSettingState extends State { '当前模式:${settingController.themeType.value.description}', style: subTitleStyle)), ), + ListTile( + dense: false, + onTap: () => settingController.setDynamicBadgeMode(context), + title: Text('动态未读标记', style: titleStyle), + subtitle: Obx(() => Text( + '当前标记样式:${settingController.dynamicBadgeType.value.description}', + style: subTitleStyle)), + ), ListTile( dense: false, onTap: () => Get.toNamed('/colorSetting'), diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index d408cc1c..89dae3e7 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -130,7 +130,8 @@ class SettingBoxKey { enableMYBar = 'enableMYBar', hideSearchBar = 'hideSearchBar', // 收起顶栏 hideTabBar = 'hideTabBar', // 收起底栏 - tabbarSort = 'tabbarSort'; // 首页tabbar + tabbarSort = 'tabbarSort', // 首页tabbar + dynamicBadgeMode = 'dynamicBadgeMode'; } class LocalCacheKey { From 5f92a0c293b51a0d6255aaaf38e01742d0d802da Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 4 Feb 2024 00:32:01 +0800 Subject: [PATCH 045/102] =?UTF-8?q?mod:=20=E7=94=A8=E6=88=B7=E6=8A=95?= =?UTF-8?q?=E7=A8=BF=E6=98=BE=E7=A4=BA=E5=BC=B9=E5=B9=95=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/member/archive.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/models/member/archive.dart b/lib/models/member/archive.dart index 5d2ea77e..d735ab7c 100644 --- a/lib/models/member/archive.dart +++ b/lib/models/member/archive.dart @@ -142,7 +142,7 @@ class Stat { Stat.fromJson(Map json) { view = json["play"]; - danmaku = json['comment']; + danmaku = json['video_review']; } } From fb32388536146f57c0b6e8236ad4f2d014c9ec56 Mon Sep 17 00:00:00 2001 From: orz12 Date: Sun, 4 Feb 2024 16:41:44 +0800 Subject: [PATCH 046/102] =?UTF-8?q?fix:=20=E5=B0=9D=E8=AF=95=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E7=84=A6=E7=82=B9=E6=81=A2=E5=A4=8D=E6=97=B6=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=92=AD=E6=94=BE=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/services/audio_session.dart | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/services/audio_session.dart b/lib/services/audio_session.dart index 98707652..57c42e05 100644 --- a/lib/services/audio_session.dart +++ b/lib/services/audio_session.dart @@ -20,11 +20,15 @@ class AudioSessionHandler { session.interruptionEventStream.listen((event) { final player = PlPlayerController.getInstance(); if (event.begin) { + if (player.playerStatus != PlayerStatus.playing) return; switch (event.type) { case AudioInterruptionType.duck: player.setVolume(player.volume.value * 0.5); break; case AudioInterruptionType.pause: + player.pause(isInterrupt: true); + _playInterrupted = true; + break; case AudioInterruptionType.unknown: player.pause(isInterrupt: true); _playInterrupted = true; @@ -36,7 +40,7 @@ class AudioSessionHandler { player.setVolume(player.volume.value * 2); break; case AudioInterruptionType.pause: - if (_playInterrupted) PlPlayerController.getInstance().play(); + if (_playInterrupted) player.play(); break; case AudioInterruptionType.unknown: break; @@ -47,7 +51,10 @@ class AudioSessionHandler { // 耳机拔出暂停 session.becomingNoisyEventStream.listen((_) { - PlPlayerController.getInstance().pause(); + final player = PlPlayerController.getInstance(); + if (player.playerStatus == PlayerStatus.playing) { + player.pause(); + } }); } } From 80b39daaff4fbaa122b71f387ab0c1cfaadca0d9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 4 Feb 2024 22:06:45 +0800 Subject: [PATCH 047/102] =?UTF-8?q?mod:=20jumpUrl=E5=A2=9E=E5=8A=A0icon?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=20issues=20#471?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 5e5a5828..7991a366 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -571,7 +571,8 @@ InlineSpan buildContent( }); } // content.message = content.message.replaceAll(RegExp(r"\{vote:.*?\}"), ' '); - content.message = content.message.replaceAll('&', '&') + content.message = content.message + .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"') @@ -586,21 +587,21 @@ InlineSpan buildContent( e.replaceAll('?', '\\?').replaceAll('+', '\\+').replaceAll('*', '\\*')), ]; - String patternStr = - specialTokens.map(RegExp.escape).join('|'); + String patternStr = specialTokens.map(RegExp.escape).join('|'); if (patternStr.isNotEmpty) { patternStr += "|"; } patternStr += r'(\b\d{1,2}[::]\d{2}\b)'; final RegExp pattern = RegExp(patternStr); List matchedStrs = []; - void addPlainTextSpan(str){ + void addPlainTextSpan(str) { spanChilds.add(TextSpan( text: str, recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); + ..onTap = + () => replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); } + // 分割文本并处理每个部分 content.message.splitMapJoin( pattern, @@ -674,57 +675,56 @@ InlineSpan buildContent( addPlainTextSpan(matchStr); return ""; } - spanChilds.add( - TextSpan( - text: content.jumpUrl[matchStr]['title'], - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - if (appUrlSchema == '') { - final String str = Uri.parse(matchStr).pathSegments[0]; - final Map matchRes = IdUtils.matchAvorBv(input: str); - final List matchKeys = matchRes.keys.toList(); - if (matchKeys.isNotEmpty) { - if (matchKeys.first == 'BV') { + spanChilds.addAll( + [ + if (content.jumpUrl[matchStr]?['prefix_icon'] != null) ...[ + WidgetSpan( + child: Image.network( + content.jumpUrl[matchStr]['prefix_icon'], + height: 19, + color: Theme.of(context).colorScheme.primary, + ), + ) + ], + TextSpan( + text: content.jumpUrl[matchStr]['title'], + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + if (appUrlSchema == '') { + final String str = Uri.parse(matchStr).pathSegments[0]; + final Map matchRes = IdUtils.matchAvorBv(input: str); + final List matchKeys = matchRes.keys.toList(); + if (matchKeys.isNotEmpty) { + if (matchKeys.first == 'BV') { + Get.toNamed( + '/searchResult', + parameters: {'keyword': matchRes['BV']}, + ); + } + } else { Get.toNamed( - '/searchResult', - parameters: {'keyword': matchRes['BV']}, + '/webview', + parameters: { + 'url': matchStr, + 'type': 'url', + 'pageTitle': '' + }, ); } } else { - Get.toNamed( - '/webview', - parameters: { - 'url': matchStr, - 'type': 'url', - 'pageTitle': '' - }, - ); + if (appUrlSchema.startsWith('bilibili://search')) { + Get.toNamed('/searchResult', parameters: { + 'keyword': content.jumpUrl[matchStr]['title'] + }); + } } - } else { - if (appUrlSchema.startsWith('bilibili://search')) { - Get.toNamed('/searchResult', parameters: { - 'keyword': content.jumpUrl[matchStr]['title'] - }); - } - } - }, - ), + }, + ) + ], ); - if (appUrlSchema.startsWith('bilibili://search')) { - spanChilds.add( - WidgetSpan( - child: Icon( - FontAwesomeIcons.magnifyingGlass, - size: 9, - color: Theme.of(context).colorScheme.primary, - ), - alignment: PlaceholderAlignment.top, - ), - ); - } // 只显示一次 matchedStrs.add(matchStr); } else { From a560d66567891afc376a03acf95013fcc8fa8d40 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 4 Feb 2024 23:03:24 +0800 Subject: [PATCH 048/102] mod: rcmd FutureBuilder --- lib/models/common/rcmd_type.dart | 2 +- lib/pages/rcmd/controller.dart | 7 +++-- lib/pages/rcmd/view.dart | 53 +++++++++++++++++++++++--------- 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/lib/models/common/rcmd_type.dart b/lib/models/common/rcmd_type.dart index 5af18d77..2dfdad1c 100644 --- a/lib/models/common/rcmd_type.dart +++ b/lib/models/common/rcmd_type.dart @@ -3,5 +3,5 @@ enum RcmdType { web, app, notLogin } extension RcmdTypeExtension on RcmdType { String get values => ['web', 'app', 'notLogin'][index]; - String get labels => ['web端', 'app端', '模拟未登录'][index]; + String get labels => ['web端', 'app端', '游客模式'][index]; } diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index 6ecc9d63..b7e58f44 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -28,7 +28,7 @@ class RcmdController extends GetxController { setting.get(SettingBoxKey.enableSaveLastData, defaultValue: false); defaultRcmdType = setting.get(SettingBoxKey.defaultRcmdType, defaultValue: 'web'); - if (defaultRcmdType == 'web'){ + if (defaultRcmdType == 'web') { videoList = [].obs; } else { videoList = [].obs; @@ -43,7 +43,7 @@ class RcmdController extends GetxController { if (type == 'onRefresh') { _currentPage = 0; } - late final Map res; + late final Map res; switch (defaultRcmdType) { case 'app': case 'notLogin': @@ -77,13 +77,14 @@ class RcmdController extends GetxController { _currentPage += 1; // 若videoList数量太小,可能会影响翻页,此时再次请求 // 为避免请求到的数据太少时还在反复请求,要求本次返回数据大于1条才触发 - if (res['data'].length > 1 && videoList.length < 10){ + if (res['data'].length > 1 && videoList.length < 10) { queryRcmdFeed('onLoad'); } } else { Get.snackbar('提示', res['msg']); } isLoadingMore = false; + return res; } // 下拉刷新 diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index 687eacd1..42b66364 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -7,7 +7,7 @@ import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart'; -// import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_v.dart'; import 'package:pilipala/pages/home/index.dart'; @@ -25,6 +25,7 @@ class RcmdPage extends StatefulWidget { class _RcmdPageState extends State with AutomaticKeepAliveClientMixin { final RcmdController _rcmdController = Get.put(RcmdController()); + late Future _futureBuilderFuture; @override bool get wantKeepAlive => true; @@ -32,7 +33,7 @@ class _RcmdPageState extends State @override void initState() { super.initState(); - _rcmdController.queryRcmdFeed('init'); + _futureBuilderFuture = _rcmdController.queryRcmdFeed('init'); ScrollController scrollController = _rcmdController.scrollController; StreamController mainStream = Get.find().bottomBarStream; @@ -88,19 +89,41 @@ class _RcmdPageState extends State slivers: [ SliverPadding( padding: - const EdgeInsets.fromLTRB(0, StyleString.safeSpace, 0, 0), - sliver: Obx(() { // 使用Obx来监听数据的变化 - if (_rcmdController.isLoadingMore && _rcmdController.videoList.isEmpty) { - return contentGrid(_rcmdController, []); - // 如果正在加载并且列表为空,则显示加载指示器 - // return const SliverToBoxAdapter( - // child: Center(child: CircularProgressIndicator()), - // ); - } else { - // 显示视频列表 - return contentGrid(_rcmdController, _rcmdController.videoList); - } - }), + const EdgeInsets.fromLTRB(0, StyleString.safeSpace, 0, 0), + sliver: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (data['status']) { + return Obx( + () { + if (_rcmdController.isLoadingMore && + _rcmdController.videoList.isEmpty) { + return contentGrid(_rcmdController, []); + } else { + // 显示视频列表 + return contentGrid( + _rcmdController, _rcmdController.videoList); + } + }, + ); + } else { + return HttpError( + errMsg: data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _rcmdController.queryRcmdFeed('init'); + }); + }, + ); + } + } else { + return contentGrid(_rcmdController, []); + } + }, + ), ), LoadingMore(ctr: _rcmdController), ], From 9e471b83d992755d23764c7e24228949c9b75cd4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 5 Feb 2024 00:19:03 +0800 Subject: [PATCH 049/102] mod: cancel Get.snackbar --- lib/pages/rcmd/controller.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index b7e58f44..28ff055b 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -80,8 +80,6 @@ class RcmdController extends GetxController { if (res['data'].length > 1 && videoList.length < 10) { queryRcmdFeed('onLoad'); } - } else { - Get.snackbar('提示', res['msg']); } isLoadingMore = false; return res; From 78ade4a1936c47327e6b7d01648edf66b81e3f55 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 5 Feb 2024 23:41:40 +0800 Subject: [PATCH 050/102] =?UTF-8?q?mod:=20=E7=A7=BB=E9=99=A4=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E6=8C=89=E3=80=90=E6=9C=80=E5=A4=9A=E5=9B=9E=E5=A4=8D?= =?UTF-8?q?=E3=80=91=E6=8E=92=E5=BA=8F=20issues=20#298?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/reply_sort_type.dart | 6 +++--- lib/pages/dynamics/detail/controller.dart | 7 ++++--- lib/pages/html/controller.dart | 3 --- lib/pages/setting/extra_setting.dart | 4 ++++ lib/pages/video/detail/reply/controller.dart | 9 +++++---- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/models/common/reply_sort_type.dart b/lib/models/common/reply_sort_type.dart index 7c203c13..89da82b3 100644 --- a/lib/models/common/reply_sort_type.dart +++ b/lib/models/common/reply_sort_type.dart @@ -1,6 +1,6 @@ -enum ReplySortType { time, like, reply } +enum ReplySortType { time, like } extension ReplySortTypeExtension on ReplySortType { - String get titles => ['最新评论', '最热评论', '回复最多'][index]; - String get labels => ['最新', '最热', '最多回复'][index]; + String get titles => ['最新评论', '最热评论'][index]; + String get labels => ['最新', '最热'][index]; } diff --git a/lib/pages/dynamics/detail/controller.dart b/lib/pages/dynamics/detail/controller.dart index 1520377e..4c5f35d6 100644 --- a/lib/pages/dynamics/detail/controller.dart +++ b/lib/pages/dynamics/detail/controller.dart @@ -37,6 +37,10 @@ class DynamicDetailController extends GetxController { } int deaultReplySortIndex = setting.get(SettingBoxKey.replySortType, defaultValue: 0); + if (deaultReplySortIndex == 2) { + setting.put(SettingBoxKey.replySortType, 0); + deaultReplySortIndex = 0; + } _sortType = ReplySortType.values[deaultReplySortIndex]; sortTypeTitle.value = _sortType.titles; sortTypeLabel.value = _sortType.labels; @@ -92,9 +96,6 @@ class DynamicDetailController extends GetxController { _sortType = ReplySortType.like; break; case ReplySortType.like: - _sortType = ReplySortType.reply; - break; - case ReplySortType.reply: _sortType = ReplySortType.time; break; default: diff --git a/lib/pages/html/controller.dart b/lib/pages/html/controller.dart index f3187828..1175ce29 100644 --- a/lib/pages/html/controller.dart +++ b/lib/pages/html/controller.dart @@ -96,9 +96,6 @@ class HtmlRenderController extends GetxController { _sortType = ReplySortType.like; break; case ReplySortType.like: - _sortType = ReplySortType.reply; - break; - case ReplySortType.reply: _sortType = ReplySortType.time; break; default: diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index b32a06f5..1580ad8b 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -31,6 +31,10 @@ class _ExtraSettingState extends State { // 默认优先显示最新评论 defaultReplySort = setting.get(SettingBoxKey.replySortType, defaultValue: 0); + if (defaultReplySort == 2) { + setting.put(SettingBoxKey.replySortType, 0); + defaultReplySort = 0; + } // 优先展示全部动态 all defaultDynamicType = setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0); diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 9e05542b..5781cbba 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -41,8 +41,12 @@ class VideoReplyController extends GetxController { @override void onInit() { super.onInit(); - final int deaultReplySortIndex = + int deaultReplySortIndex = setting.get(SettingBoxKey.replySortType, defaultValue: 0) as int; + if (deaultReplySortIndex == 2) { + setting.put(SettingBoxKey.replySortType, 0); + deaultReplySortIndex = 0; + } _sortType = ReplySortType.values[deaultReplySortIndex]; sortTypeTitle.value = _sortType.titles; sortTypeLabel.value = _sortType.labels; @@ -115,9 +119,6 @@ class VideoReplyController extends GetxController { _sortType = ReplySortType.like; break; case ReplySortType.like: - _sortType = ReplySortType.reply; - break; - case ReplySortType.reply: _sortType = ReplySortType.time; break; default: From 4a5f4ca2caaa34c7157db4241c410c98038bf3fc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 6 Feb 2024 00:14:46 +0800 Subject: [PATCH 051/102] =?UTF-8?q?fix:=20=E9=99=90=E6=97=B6=E5=85=8D?= =?UTF-8?q?=E8=B4=B9=E6=97=A0=E6=B3=95=E6=92=AD=E6=94=BE=20issues=20#457?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/widgets/bangumi_panel.dart | 25 +++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index b5ec4efe..5996d6c8 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -151,7 +151,7 @@ class _BangumiPanelState extends State { } void changeFucCall(item, i) async { - if (item.badge != null && vipStatus != 1) { + if (item.badge != null && item.badge == '会员' && vipStatus != 1) { SmartDialog.showToast('需要大会员'); return; } @@ -255,11 +255,24 @@ class _BangumiPanelState extends State { ), const SizedBox(width: 2), if (widget.pages[i].badge != null) ...[ - Image.asset( - 'assets/images/big-vip.png', - height: 16, - ), - ], + if (widget.pages[i].badge == '会员') ...[ + Image.asset( + 'assets/images/big-vip.png', + height: 16, + ), + ], + if (widget.pages[i].badge != '会员') ...[ + const Spacer(), + Text( + widget.pages[i].badge!, + style: TextStyle( + fontSize: 11, + color: + Theme.of(context).colorScheme.primary, + ), + ), + ], + ] ], ), const SizedBox(height: 3), From 71ccb9c0e51dce5437a14e7311f36b13f0f2ac8a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 6 Feb 2024 11:01:36 +0800 Subject: [PATCH 052/102] =?UTF-8?q?fix:=20=E6=94=B6=E8=97=8F=E5=9B=BD?= =?UTF-8?q?=E5=88=9B=E8=B7=B3=E8=BD=AC=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav_detail/widget/fav_video_card.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 9394fbe3..d988a0e1 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -27,7 +27,9 @@ class FavVideoCardH extends StatelessWidget { onTap: () async { // int? seasonId; String? epId; - if (videoItem.ogv != null && videoItem.ogv['type_name'] == '番剧') { + if (videoItem.ogv != null && + (videoItem.ogv['type_name'] == '番剧' || + videoItem.ogv['type_name'] == '国创')) { videoItem.cid = await SearchHttp.ab2c(bvid: bvid); // seasonId = videoItem.ogv['season_id']; epId = videoItem.epId; From 083739e562a5ec3d40c4597d79103611f264796a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 6 Feb 2024 11:13:33 +0800 Subject: [PATCH 053/102] =?UTF-8?q?mod:=20=E6=94=B6=E8=97=8F=E5=8D=A1?= =?UTF-8?q?=E7=89=87=E5=86=85=E5=AE=B9=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fav_detail/widget/fav_video_card.dart | 55 +++++++++++-------- 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index d988a0e1..a4f22f6b 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -9,6 +9,7 @@ import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import '../../../common/widgets/badge.dart'; // 收藏视频卡片 - 水平布局 class FavVideoCardH extends StatelessWidget { @@ -86,22 +87,21 @@ class FavVideoCardH extends StatelessWidget { height: maxHeight, ), ), - Positioned( - right: 4, - bottom: 4, - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 1, horizontal: 6), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: Colors.black54.withOpacity(0.4)), - child: Text( - Utils.timeFormat(videoItem.duration!), - style: const TextStyle( - fontSize: 11, color: Colors.white), - ), + PBadge( + text: Utils.timeFormat(videoItem.duration!), + right: 6.0, + bottom: 6.0, + type: 'gray', + ), + if (videoItem.ogv != null) ...[ + PBadge( + text: videoItem.ogv['type_name'], + top: 6.0, + right: 6.0, + bottom: null, + left: null, ), - ) + ], ], ); }, @@ -143,19 +143,30 @@ class VideoContent extends StatelessWidget { maxLines: 2, overflow: TextOverflow.ellipsis, ), + if (videoItem.ogv != null) ...[ + Text( + videoItem.intro, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + ], const Spacer(), Text( - Utils.dateFormat(videoItem.ctime!), + Utils.dateFormat(videoItem.favTime), style: TextStyle( fontSize: 11, color: Theme.of(context).colorScheme.outline), ), - Text( - videoItem.owner.name, - style: TextStyle( - fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, + if (videoItem.owner.name != '') ...[ + Text( + videoItem.owner.name, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), ), - ), + ], Row( children: [ StatView( From ebdeec67303b6e60268a6ae80903c8f4be3a8803 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 6 Feb 2024 12:23:07 +0800 Subject: [PATCH 054/102] =?UTF-8?q?fix:=20up=E4=B8=BB=E9=A1=B5=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E6=90=9C=E7=B4=A2mid=E5=8F=96=E5=80=BC=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 1d3cd5c4..0663e94e 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -105,7 +105,7 @@ class _MemberPageState extends State actions: [ IconButton( onPressed: () => Get.toNamed( - '/memberSearch?mid=${Get.parameters['mid']}&uname=${_memberController.memberInfo.value.name!}'), + '/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'), icon: const Icon(Icons.search_outlined), ), PopupMenuButton( From 63d600070b628424039807837f2b9a4679641ac1 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 6 Feb 2024 15:27:39 +0800 Subject: [PATCH 055/102] =?UTF-8?q?fix:=20=E6=94=B6=E8=97=8F=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E8=B7=B3=E8=BD=AC=E6=90=9C=E7=B4=A2mediaId?= =?UTF-8?q?=E5=8F=96=E5=80=BC=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav_detail/view.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index f5897550..27d7182b 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -24,10 +24,12 @@ class _FavDetailPageState extends State { Get.put(FavDetailController()); late StreamController titleStreamC; // a Future? _futureBuilderFuture; + late String mediaId; @override void initState() { super.initState(); + mediaId = Get.parameters['mediaId']!; _futureBuilderFuture = _favDetailController.queryUserFavFolderDetail(); titleStreamC = StreamController(); _controller.addListener( @@ -94,8 +96,8 @@ class _FavDetailPageState extends State { ), actions: [ IconButton( - onPressed: () => Get.toNamed( - '/favSearch?searchType=0&mediaId=${Get.parameters['mediaId']!}'), + onPressed: () => + Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'), icon: const Icon(Icons.search_outlined), ), // IconButton( From 40c666e3d1112269f01a9b877bdabfe7d69883ab Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 7 Feb 2024 00:52:25 +0800 Subject: [PATCH 056/102] =?UTF-8?q?mod:=20=E7=BD=91=E7=BB=9C=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E7=BB=84=E4=BB=B6=E6=A0=B7=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/http_error.dart | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index b02182c6..cbc6659b 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -22,20 +22,27 @@ class HttpError extends StatelessWidget { "assets/images/error.svg", height: 200, ), - const SizedBox(height: 20), + const SizedBox(height: 30), Text( errMsg ?? '请求异常', textAlign: TextAlign.center, style: Theme.of(context).textTheme.titleSmall, ), - const SizedBox(height: 30), - OutlinedButton.icon( + const SizedBox(height: 20), + FilledButton.tonal( onPressed: () { fn!(); }, - icon: const Icon(Icons.arrow_forward_outlined, size: 20), - label: Text(btnText ?? '点击重试'), - ) + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith((states) { + return Theme.of(context).colorScheme.primary.withAlpha(20); + }), + ), + child: Text( + btnText ?? '点击重试', + style: TextStyle(color: Theme.of(context).colorScheme.primary), + ), + ), ], ), ), From 191472d0c42c3cd09f9701ccd828e7fb13e21aa8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 7 Feb 2024 01:17:35 +0800 Subject: [PATCH 057/102] =?UTF-8?q?mod:=20=E7=BD=91=E7=BB=9C=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=BC=82=E5=B8=B8=E6=A0=B7=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 8 +++---- lib/pages/dynamics/view.dart | 28 ++---------------------- lib/pages/dynamics/widgets/up_panel.dart | 7 ++++++ lib/pages/search/view.dart | 10 ++++++--- lib/pages/search_panel/view.dart | 12 ++++++++-- 5 files changed, 30 insertions(+), 35 deletions(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index d0faabaa..b311bfa1 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -51,7 +51,7 @@ class VideoHttp { (i['owner'] != null && !blackMidsList.contains(i['owner']['mid']))) { RecVideoItemModel videoItem = RecVideoItemModel.fromJson(i); - if (!RecommendFilter.filter(videoItem)){ + if (!RecommendFilter.filter(videoItem)) { list.add(videoItem); } } @@ -98,7 +98,7 @@ class VideoHttp { (i['args'] != null && !blackMidsList.contains(i['args']['up_mid']))) { RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i); - if (!RecommendFilter.filter(videoItem)){ + if (!RecommendFilter.filter(videoItem)) { list.add(videoItem); } } @@ -130,7 +130,7 @@ class VideoHttp { } return {'status': true, 'data': list}; } else { - return {'status': false, 'data': []}; + return {'status': false, 'data': [], 'msg': res.data['message']}; } } catch (err) { return {'status': false, 'data': [], 'msg': err}; @@ -217,7 +217,7 @@ class VideoHttp { List list = []; for (var i in res.data['data']) { HotVideoItemModel videoItem = HotVideoItemModel.fromJson(i); - if (!RecommendFilter.filter(videoItem, relatedVideos: true)){ + if (!RecommendFilter.filter(videoItem, relatedVideos: true)) { list.add(videoItem); } } diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 575c8767..4a92cdfb 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -192,22 +192,6 @@ class _DynamicsPageState extends State ) ], ), - // Obx( - // () => Visibility( - // visible: _dynamicsController.userLogin.value, - // child: Positioned( - // right: 4, - // top: 0, - // bottom: 0, - // child: IconButton( - // padding: EdgeInsets.zero, - // onPressed: () => - // {feedBack(), _dynamicsController.resetSearch()}, - // icon: const Icon(Icons.history, size: 21), - // ), - // ), - // ), - // ), ], ), ), @@ -229,7 +213,8 @@ class _DynamicsPageState extends State return Obx(() => UpPanel(_dynamicsController.upData.value)); } else { return const SliverToBoxAdapter( - child: SizedBox(height: 80)); + child: SizedBox(height: 80), + ); } } else { return const SliverToBoxAdapter( @@ -240,15 +225,6 @@ class _DynamicsPageState extends State } }, ), - SliverToBoxAdapter( - child: Container( - height: 6, - color: Theme.of(context) - .colorScheme - .onInverseSurface - .withOpacity(0.5), - ), - ), FutureBuilder( future: _futureBuilderFuture, builder: (context, snapshot) { diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index ba4a562d..f1043fd8 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -121,6 +121,13 @@ class _UpPanelState extends State { ], ), ), + Container( + height: 6, + color: Theme.of(context) + .colorScheme + .onInverseSurface + .withOpacity(0.5), + ), ], )), ); diff --git a/lib/pages/search/view.dart b/lib/pages/search/view.dart index c262df38..95d3134e 100644 --- a/lib/pages/search/view.dart +++ b/lib/pages/search/view.dart @@ -187,9 +187,13 @@ class _SearchPageState extends State with RouteAware { ), ); } else { - return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), + return CustomScrollView( + slivers: [ + HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ) + ], ); } } else { diff --git a/lib/pages/search_panel/view.dart b/lib/pages/search_panel/view.dart index f7392c88..c5824d70 100644 --- a/lib/pages/search_panel/view.dart +++ b/lib/pages/search_panel/view.dart @@ -105,7 +105,11 @@ class _SearchPanelState extends State slivers: [ HttpError( errMsg: data['msg'], - fn: () => setState(() {}), + fn: () { + setState(() { + _searchPanelController.onSearch(); + }); + }, ), ], ); @@ -116,7 +120,11 @@ class _SearchPanelState extends State slivers: [ HttpError( errMsg: '没有相关数据', - fn: () => setState(() {}), + fn: () { + setState(() { + _searchPanelController.onSearch(); + }); + }, ), ], ); From b0c56feef5c6169eb088aa257005f2863009e3b8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 7 Feb 2024 02:47:11 +0800 Subject: [PATCH 058/102] =?UTF-8?q?mod:=20=E9=A6=96=E9=A1=B5=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E5=BC=82=E5=B8=B8=E8=AF=B7=E6=B1=82=E9=87=8D=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 16 +++++++--------- lib/pages/bangumi/view.dart | 7 ++++--- lib/pages/hot/view.dart | 10 +++++++--- lib/pages/live/controller.dart | 4 +--- lib/pages/live/view.dart | 23 +++++++++-------------- lib/pages/rcmd/view.dart | 34 ++-------------------------------- 6 files changed, 30 insertions(+), 64 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 5c467722..64559ffd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -25,7 +25,6 @@ import 'package:pilipala/utils/recommend_filter.dart'; import 'package:catcher_2/catcher_2.dart'; import './services/loggeer.dart'; - void main() async { WidgetsFlutterBinding.ensureInitialized(); MediaKit.ensureInitialized(); @@ -55,14 +54,13 @@ void main() async { [FileHandler(await getLogsPath())], ); - Catcher2( - debugConfig: debugConfig, - releaseConfig: releaseConfig, - runAppFunction: () { - runApp(const MyApp()); - }, - ); - + // Catcher2( + // debugConfig: debugConfig, + // releaseConfig: releaseConfig, + // runAppFunction: () { + runApp(const MyApp()); + // }, + // ); // 小白条、导航栏沉浸 SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart index 560f09a5..f59f94a2 100644 --- a/lib/pages/bangumi/view.dart +++ b/lib/pages/bangumi/view.dart @@ -9,7 +9,6 @@ import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/main/index.dart'; -import 'package:pilipala/pages/rcmd/view.dart'; import 'controller.dart'; import 'widgets/bangumu_card_v.dart'; @@ -199,7 +198,10 @@ class _BangumiPageState extends State } else { return HttpError( errMsg: data['msg'], - fn: () => {}, + fn: () { + _futureBuilderFuture = + _bangumidController.queryBangumiListFeed(); + }, ); } } else { @@ -208,7 +210,6 @@ class _BangumiPageState extends State }, ), ), - const LoadingMore() ], ), ); diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index 2673e84c..7a0a57ea 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -89,8 +89,7 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { if (data['status']) { return Obx( () => SliverList( - delegate: - SliverChildBuilderDelegate((context, index) { + delegate: SliverChildBuilderDelegate((context, index) { return VideoCardH( videoItem: _hotController.videoList[index], showPubdate: true, @@ -110,7 +109,12 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { } else { return HttpError( errMsg: data['msg'], - fn: () => setState(() {}), + fn: () { + setState(() { + _futureBuilderFuture = + _hotController.queryHotFeed('init'); + }); + }, ); } } else { diff --git a/lib/pages/live/controller.dart b/lib/pages/live/controller.dart index 6a26f0d2..74fb6e9a 100644 --- a/lib/pages/live/controller.dart +++ b/lib/pages/live/controller.dart @@ -10,8 +10,7 @@ class LiveController extends GetxController { int count = 12; int _currentPage = 1; RxInt crossAxisCount = 2.obs; - RxList liveList = [LiveItemModel()].obs; - bool isLoadingMore = false; + RxList liveList = [].obs; bool flag = false; OverlayEntry? popupDialog; Box setting = GStrorage.setting; @@ -39,7 +38,6 @@ class LiveController extends GetxController { } _currentPage += 1; } - isLoadingMore = false; return res; } diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index f693acf1..f3f91c9e 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -11,7 +11,6 @@ import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/main/index.dart'; -import 'package:pilipala/pages/rcmd/index.dart'; import 'controller.dart'; import 'widgets/live_item.dart'; @@ -45,8 +44,8 @@ class _LivePageState extends State () { if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 200) { - EasyThrottle.throttle('liveList', const Duration(seconds: 1), () { - _liveController.isLoadingMore = true; + EasyThrottle.throttle('liveList', const Duration(milliseconds: 200), + () { _liveController.onLoad(); }); } @@ -108,24 +107,20 @@ class _LivePageState extends State } else { return HttpError( errMsg: data['msg'], - fn: () => {}, + fn: () { + setState(() { + _futureBuilderFuture = + _liveController.queryLiveList('init'); + }); + }, ); } } else { - // 缓存数据 - if (_liveController.liveList.length > 1) { - return contentGrid( - _liveController, _liveController.liveList); - } - // 骨架屏 - else { - return contentGrid(_liveController, []); - } + return contentGrid(_liveController, []); } }, ), ), - LoadingMore(ctr: _liveController) ], ), ), diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index 42b66364..d732f370 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -44,7 +44,7 @@ class _RcmdPageState extends State if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 200) { EasyThrottle.throttle( - 'my-throttler', const Duration(milliseconds: 500), () { + 'my-throttler', const Duration(milliseconds: 200), () { _rcmdController.isLoadingMore = true; _rcmdController.onLoad(); }); @@ -113,6 +113,7 @@ class _RcmdPageState extends State errMsg: data['msg'], fn: () { setState(() { + _rcmdController.isLoadingMore = true; _futureBuilderFuture = _rcmdController.queryRcmdFeed('init'); }); @@ -125,7 +126,6 @@ class _RcmdPageState extends State }, ), ), - LoadingMore(ctr: _rcmdController), ], ), ), @@ -188,33 +188,3 @@ class _RcmdPageState extends State ); } } - -class LoadingMore extends StatelessWidget { - final dynamic ctr; - const LoadingMore({super.key, this.ctr}); - - @override - Widget build(BuildContext context) { - return SliverToBoxAdapter( - child: Container( - height: MediaQuery.of(context).padding.bottom + 80, - padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), - child: GestureDetector( - onTap: () { - if (ctr != null) { - ctr!.isLoadingMore = true; - ctr!.onLoad(); - } - }, - child: Center( - child: Text( - '点击加载更多 👇', - style: TextStyle( - color: Theme.of(context).colorScheme.outline, fontSize: 13), - ), - ), - ), - ), - ); - } -} From d258474a5a69b75542ae040830c3cd9b7343b873 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 7 Feb 2024 22:23:29 +0800 Subject: [PATCH 059/102] =?UTF-8?q?mod:=20=E7=9B=B4=E6=92=AD=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=86=85=E5=AE=B9=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/images/live/default_bg.webp | Bin 0 -> 16312 bytes lib/http/api.dart | 4 + lib/http/live.dart | 19 +++ lib/main.dart | 2 - lib/models/live/room_info_h5.dart | 130 +++++++++++++++++++ lib/pages/live_room/controller.dart | 19 ++- lib/pages/live_room/view.dart | 193 +++++++++++++++------------- lib/plugin/pl_player/view.dart | 3 +- pubspec.yaml | 1 + 9 files changed, 279 insertions(+), 92 deletions(-) create mode 100644 assets/images/live/default_bg.webp create mode 100644 lib/models/live/room_info_h5.dart diff --git a/assets/images/live/default_bg.webp b/assets/images/live/default_bg.webp new file mode 100644 index 0000000000000000000000000000000000000000..a58259dea48887982df1f1be955cf73c489bf232 GIT binary patch literal 16312 zcmY+qb3mov7d3vfZJU#uCfjbZZM(_lWZQO4HYeM*t*MFcZ9d=khrgccp68r>Hr8Hi z-xMXq#A^8g05wq|1yuzO;zHohwQ*osAhagnK`?xA5;-zOh4fXq^X8njs4y0`ud?xB zsQ_{49xgtO=6v6+0`~k%u)JR?e3#-=Li`V<5W>|+Jo4(ua3?vT&lqhf=_am=$)Tek zJ0D3=uB^USA0TJo(8$CB{-EH{xP;Cpb0uvPe`jzShf- zbS{3M*o$_CH)^?P<<=73f{BI!DLOLbYXx|ezzI-!$6Oi z7~cknab|lydSH6%WOKOS_E4wyOPkwIcc#49>}_xBnIJ)bu*X+CY&@yCYL=#}UD+vH z1)@qH<-V7S%hb{}XR?Ujci(TF%dvF?nVorZYUK7S^5>f9u~p~68|4Pw~q zN@I&{a&zA2O$d&0OLX1{o(8Zjl+%JxBsbpZ$1)-k2%JY2dYrhi8wZ348q^1g&8A$z ziIF7Rl_KA)x+QUv?%6r&o4sL@+&UvSh~yb~%rGc;xt7b?Cvg1r^cT(ZfNg&FYjS*N zxm?>5RF~-Rxv~NIzk3r-1Ef%@&Cy4aB0WqZXGld{t8t{_#@5Snzt)Kok9=Av!2Z^y zMjnAzs12@^O)jr!Swja-Qk$OdUae$?zssjlbeV#VoT5`1&*h3_f~ZevH|<-cv1nI2 z_uWP@u(lTVD(efPaCuhBvKWsfIF(w8gcC%U|6!kZMvkL#0g3cS?T>P84P+;` zDYX02pQ7$zKjVeh@Mq-71`LJcJRFtccl^();ob9Bb$effv}g#2K@cA);Rl&)cbcjK z((UT+bIx9{A&B#z!EPrqXtBG?4uDm}={sKVvQ0murXl&Dlxbv9Mn~&JtDMTzrR5$m z9!<@JdlDjB&x5EYH0#bY=p!qADNI|*j*j#iR`e`9W%eY-i08xe^sZ?P!M9*pv zp=9Eq04g<_=Cn%~&=$lOJ-`YTaOdhC?c^#F(V2B+g;wH%x#V#%o~|x~xqsp-N@@MI zdnw%>9gRUTin0!a5C@pfxe6li(ywa@azl;pE_o-xO(kZ)n`$3v1p4b!&fS_xqiIBd z!?>7$>ttaZJq*cD3%n#BG`qYx9}c3`6yOegmPGpqC8!j%L|iZKp+2gF{sO6}9-giX zSUu~JPlp(LV02?v0&C+mJrlpVbsh)m!ptmjwr=*q< zL?Y1+aKn<~FJFxK6m-gDF4Fvoi1{b`Z-zm2U-{^ezlxNWO2~MEC=H7nm7#KwM}TSf z$)9GE_FI;WU{sE*w&|oBD^t+ZN;q)cjGm&P*in?fP*j#Wmm#Ic)OcZk@ zFKL)th5S}s#YY){y~ovsd`cxskf*9?Us8`G`?cfTE+GMz({_W~6dPKBk*#@4@WI5G zc2g{y@~v2I`@4+=`r~>Ui!T$E(aRU_FvFK+%(Gt_-XooGUS|?gkIv2+9rBY6@_4sc zTaIRgAT_XTo$hcAUAW-2c|yUuzXJv%u_MTEYu?{1a%&)$rZLmRQ33mxW0y`*ye{?j z#}Gzk0UE&lf$2=CkYxEs-gh%$b+tBYMF1%@1CCOwL;dF*%v_mA_Y|s~;co*>*bHB7 zXWhqZasn2Mx}eSm&G{X|ec-$9>Znh|f|hLXEG%|Wrp6YSg`a4JuH!rAVTHZ6DVVv6 z4^=`BTqu(!M1a1F`VEb%;Pn(XK>31@=f_6laYGvb}s|(VgIVGOV{^k#`CIhp*Ra%rjihAKLi5*RO|7 zC?Axh{{vnZ`14P%#Kd<{E9416T9?Aj~1wlt>Q65OQ`y#Yjo@1;e6K zD-^OTisgyN0DXNk(QdC^tc0xh<=+RuBtdN45&HaD5yiMQ!OpWaPS7ZE(&`gd9isp9 zE8^7uETyAbx!D^_;wqMcm19z`GaUR0Jp1#}{w>kon@u8)R3wb@ZfMSD4!_13+??k) zV@THg<{HWMKZmgv%N{KIi=zb$#_QJ^KnDb4Q4{C_t;-W$5)IUEGyOai=sht`^k^!% zc3?=4g(J6QrBs0K!iZkOBKWedIaLw4J!GYF;$6$2KzsMHxEn1}y$=6{h^m-}3tp#w<~1-yV9Mj6Q|<2YMnL*&lfss~L;kA)PnN*c zKp~j#hL_X&11_nxdj4`6v$d9{7uV`_X`kkBIiEqyWGCBxF7KS4jy!xk9Hqfojm6rk zk1vxd{sa!0G*$w>wluD1c$9Q63XYqdt!8k3w`vZkwCb4+JUDG${7ulutF3OI_nb$H zODSYxYAmSQ{l_L@#M9IdXY@g4eu2b&e)pDEkdCuW;Pr^+m#G|nI8 zB9h${B-k@WP?` z^E9GRalVFA9f_Q@wEbYxhw4@1g!K(3fmrZYr0kmo?oJa9MCai~MRM_FS#hYaoVAUs zt)Z_)(1aKqTz_ik450vsAb3w56&?awZ}?Q%`H-JosiA@Z6CRF_U{(*-EG<|!S7}$4 zy|gUGxc)bMP#KOC?0Sc!gz-~?4-(Px)w(Nz`@t43J69U8WVk|k7-Jkn=I*PkD9I88+gelVjv_!w4whhM?kE(v%25Cf>afJ;zIt!+T*naI;j64ExU!9E%?$*7Y$(% z5DO4|Hm|&MVTr-4Eoxf+essQk7~c@FYC8kLLV#gDTy3SlkHMbq`{Ks}V#{!+z2O}j zu)Qo0LSkU5q-@ElAYm93yX`HH-tTDg@u;^Ry>Qa{dW_zgmN%aHOH zuF;W6pD=vw2fxylww+J%ad4I8Pw)?mQ)WLi`#An0g<|k+`vb-C2Xc%5i)DIgxEb%< zDph+Y5(Sb0gFm?qVMv$+L(!E&*Ln5v1oj{mXxfClFjc{1Cq7DNlu@yUCsDO}d+6?a$C zNBMng5@+A)$Mj({y8`{Ep zNIK^_3Vs8eF#;7KdlGKtG>2S3yMw=TvO_r>CY{FNRE*-{kNA2f``7YFhXdD04wpJE zJIt)p)8=i^g4PE_Ns9K(M54A`P}zCLVy>gzza5L%#7R9?47f0-NPGNbigeDUJJG#L zK&VbAbvjZVn~o*f;QES(lQTw#2CGxvWhU%3dKN(K0wQ({O|Z0iakHh3DCcQ}9=hVP z3)0mQaZMS`;93LZkV^z;CFT&TiGyeYl{a_6Fq4^;dQGr5iaKLae@eq80F-{HW+q+H zHh8xbiD+|J_=36R9u~d-y{E8Zsx@yB1)j_9IplO&-&c*=9E^xApCckyOST5;Pbe5S z5Wb5l+3qWcA#c|Wn*`m-RFQ%7k3Q7biGn8EswCWwJh0ls7kz&o+lM5HJk6)S9Zjqw zdN+vd)^&VX2Dq-j8*@Hrtuq#Vb4dPV3{&5NFpCN=+=A;xl{iQPP$`zAtvYof^v}eG z?^E1&gdF;pHf&t)zu9<4HUnD(SExweF;+bDwZr|kb z<8vM%)oTD~EHBJ_=6E-yWDw|x!5%WwKVG;%5028%(eag3up!Qb31X|ln1Tm-3nMep zPb)ywn1&OcdqgDy!Vv4yz^yo2!fOgjd^Z|9^^0>M?=E~%s@BqRF2qb0Q45&5t^!<# zVrv9Yqzh>*Mc!%!s`IHSN5Hjup~FdDgk`42ZLN25ha@F1iyW+yQwhPBicTATyOoYL z_A@Kkx*bqT6P#rDNtZo^u!5f#H4hNHA2XQ@l4Ga}`SxH8K5}Q+5Kw1k6nd zCwEx_xgBU(T?s;bEuraH1aL!^9eqt|XYAwrNC=NTi0_G0I^#9-auswBA$}m@Y(WI% z7Ge09XPV}bg2CSF8OLi;T-I37oi?xS#r@=?ylp_5vM=12&D75ikSmas>{NoOXe1W; zO>KeFHAXz^N?H)*O*G7dTvDJx)PPx$Gr-?>Ws8VS@(ZFplIarCuEsfni{44nQMISC zfD}OYWYT3n@EG0MA6AA|X`GV;92$muL3 z9edPmk;c2(62osIH6i8WzefHJ*R8IlbsCndnuB7z*8nnX>wUhL43R>0vtwL!QlPtqWX_lv5HM{jYJQ9D$ zx*&b*tkV}iFlJJprfGmeD2nPZNWwUQg?9Lq=(}Tipb+EQC-2CO$3j7ruvjdg)yY}8 z8JEZMz=S378&wH_9N@z|n{0#UC(n@NPVz&T@@v%y!$j}>xt<8USr!Y{{;TH{?p|H>8Co*iU;`=Ca#QaMl-hF0a^^N!v-YcI}4rBjIogfh7-cI*&^c{P?L-}oNw#Ce_{9#{EtL}-jVoQ^pW z3P2&rocTGbfEeJbLtq#noy5j_6)>TD?h8Hx%9-FoINJU{L5k{YkGFTvdqA!+uiQmP zdslkMc84o>BsEpdMWgJi%}ZvyHzGgU=_www>iVc|`IP{0O7jd)simR8;RJ>R+I4MTHjI8sY5@0L; zJUf;FC<@E`ZlFfOs-wJ9gSRTe+^&4ydc|gp&@NTljsLTb`rm3vfy`UxgY>;$shMd* z)jgJJ*WPz(fODc2j88#`sY=Nwb)6?%WwM#G%t*-DbW^Kg^`&;C>&>cVg6vq)t2MuyOch~R}$Ak>2;#^OBGolzn7QU{v2q2&NY2oK?EN#*nfGiB*;8} zda8Bnv++?%&x)f_MtL^h-rc0Cc@ZHG3b@|0JM8N))NxT|RAoVx^-eKu?stU1{By1n z+NI3GrB-ru02(B!^L97*3QybYL`fKwd}#XbTveej)=xL*tC;dr{1E2+vdBdViS#(>=vHQ@El$ZYO=S+P`yW!>j|U=_r*Q2P#| zl_dx}4akcbC&|6fzSo6y961UJ&d#98c(!~oaPeRV^0j|E4^~`eEBOHkp6xWt4~dxm7N?!iVyj`j~@$dP&vfGH|dTg$`L+S z3b*qa3fiZCvub2w5fuGyt`x76QIRkYs4HU%a}qF6;3VR+`(Eh)+2oq2B9N5-R~wIY z1}<+SSX|V!*GhK11LXmE8$U<<#!nhie;CPsW`j(cu=uC%1{ofP;eqK2BkyfM#-}=c1C!+7GAB;tCmK z|3#_Qe6`iJsv0ic7kq0N)@|aDM)8+TViAN>o*j#YRvca5q$f-zy>Ziwdia$9XGtC6 z2T+&?GzwO&gSp|qCe6HzH2{U>AvymF`sI_43zzNoP4;uR0F32q;_p3I!#RL@tr#A-lme8VOOr+k2nEdiF?}QU zM_TP8e`Jo%)cOpN_mP)0YB-7{)Yd(zH)i)>03Wq?v@@^8Wk-7Vj+){4+`S^`L)kf zSWNBDf#E|dkYU$vSd1eg5V1pP17Q?I6pPHe6_{W!dxp62U}z{tS!UmI+J$wW5q}zk zZfth-`T&clv|9JMgN_{YsJ@k9+|2`B!r18J@9g#!Qn;%t;QF*cpJA{BV#=e$Lv+~2 zs1{{IB!bPJ*E{a}urylxQu-ZL`G_|jl72c6K~8bnMxT4FN*%jvRff@YJuaC&Amrqg zVSRL!A;C?E_l70q1hXtU*g7%xbjL!=g9s&AFO*c}?x`T+jw8Pd?}GI*x-8GW|0l}g z^X+xTvK@(JLi+&Y*8+m`p|sUB;i6k~%7Xm&d6+Vg^?|Av$@Ciyw4m2`!_r6|Ohn_K zRaW%?wwMVrtZs_kL z2{@HR4X4kSH|{F#Q$OM8AH?zbPuPFy147Bc$$jyNr}jHd5Jdq*k<`j_p3Y_o_g$BG z_TTL#cA=q{{j|nF1!SvkfVfCQ%4fmX#$}YqrIRiHhA$yF8Z8ZPC0-=mOihV-`iZ_zb12AlLFhL-Y_{9jMxS_+0GXeyD* z$n&w0E2C~*_5*M!od25&8`iNEO!icH=c#r({ z74f9(f0|MxoSLK(9#hzSUsS944@mRZSe?qi?7o2sU9;<1Lm%tp3^?h21Btt0pYWrJ zRHw&}qsyvUiO(eeSsnlm0XkJ8m#-Onoq8;@Qqf+BLuu%5jxwoE6~0=l+i52-)O<&T z4qfqgBk~zB%-Ihe?r^6!(pTSl?{juF82LHMYa2M3>)OQ->5=OCk~+M0Ngq-H_C0g= z2?eAw?I38kd4QCLrAz~C8I;%Il>_kpqVRET-SF+GQ-J;l3n!i!2`#$a{SJLrBK+?W ztY=zPJ){#aymb|&IJrK9P{DO^1tW1%&8Bw9y2>V@a&Q|=@3<)29jY8jJd-)cSf>gI)y6SVPsi&k%}v!{zTEdl^}tM4kR)J zUNh3i+Iu9z*X7W7|Cio6*+LeYgg9IDL$*Q#?_-Ky8I$4HEEs4v!`QbE{!V<`7*Rlc z>}A%9&Ezxv(p~5Bw|;DIdF~M;{T0>>pW3Ns-jiaH0( zy85DD@*C;yT#HQ@$+W(xuL8d}tAkX^goXg59XJ^NZ3U<`vC7xcE}`$WcsPg^WS#hS zI=;|oZ#?RDXnJLq>mB8N$ie=;Dq8}etz~f?_e=2?=dF%k7IHZhp#I}geQ)IX@vn7Xlm?}+kGfd@e9>{29q5pj3VClBz+{X@c;+VsZ zb$OQaM@3s?cx>5JF%XQBdt*T2{-z9>l`z}*9k+T3Miy#_Fe#x_*jJ7W4wwu4c zg{s!%FR!-S^s;l$sSrGr6q%5Y^NLjf7aos?fFKn*Wt#p$-<@2Y0x{t+h?6fy;N|`f zc-!-so4|TUyhnWZD~elgG)=@pX-&fLMF#SIh&1U=^D#L1F5%B@m#cHXokI4!K;aA3d8IBj)>20~2jZtHshb!P~sSpcy7^ind&pJ0l^7Ev~4O2D~4 z`>TEMPe&n1(}Ae8WP(A$_bj*X)Zdx2H_e+9hNTXOW2h}a>b z_xCd@U-TG68==Ku0uyyO!uHo(jKwXW$TtIBZxlh6zK?c8{^IVwC$(_xOLhB3I)z>r zL9i<;O)jHEi)v=5mHg#6@l1`(;QqE@p2~h;OKQGIAXPL|bEoU{am&b#G(SX^KyVOAU33F1 zy7_`I>OVoze=!43_6b{4GQ8b;E|Gl)YWjuLm3oveN3;?QQV;2quuo?Z4A$}oYEW?) z8bKrFF-@V4Qs%+*i$#r4i@gKg_@7UizQgK$!+)~}Tj`ar(YoNlLWN5_$R6S$p)BA0 z3)kb=kaN@5PG%LfM#x5cRW~V<|1oeULNo_Lvfp}gr2_uuxSI|$gW32C{@7d+>*c`Q zdK*A=o^-H7J#Ng~dcKwXha7x|KS?bBmn=M3RNoKPIHOIgf#D!j_9~l8S~dzM*EAOqh0dU6*$c%aRl?@G22JF7?01N1e`PbHjBi7I^U%Jl ze2H_4CCTZBsOaqq&=l=nun@a)I4$!Di!)G($G98 zVyL-U&_e)}qeIa#Gk@;_R^-SbgbO2&YOWg`rvv~%$OP60a1=nJ{Hg#zoCrhrgDIC;vMp(lC%M23P;Q7jH^rK-qQ>D^C&x;YJULo6A z))^R5DgcVZJ}lVF>PKZ~hEP!P1(T$H*j8o7+$>9w%xlOwV3%TCsZpcwIbdtyn@!sg z(_CRhoGi`#l=Z><7J;)L06^E+@qoGB;Zl3T(pf7jL+@8O_4?%@7hn|9hv-ky3mg7H zX8Y!dtrN{N2|YZg3;;k(hxMhB4g~I+TcH_;TuW1dEp)nD_hf?V8%aMj37f&B_yG`q z1MoqxpZeT2V(3DHH;mZo%o(;e^l?}39TjApDjkZp~x+Bc-v)_ddvwLsDaLw zj##OzG!p(I`y)6QtYD|YzyVti6ek{k+k6h9kb;H_3tkv)mE+23+43ueMU`zJ6cg1Y zG2(A|TTaE8?3j-G73BLnw{`vs9*%ukBp>~?6yXu5QIQ-QN4dGIlxD9w07RRL#kPQH z8T`f*R+8Q#e?DRy0F~a!Q>hh2IS6fWbmQl4 z!ot>uAYVA-cZX*l(3tQCbp-C3Z?Of}L4b2uma}w4pB%0S69C5xF3{g{{g;u5;JXwN zhLT+jV{=RD(!G;Mvn}XPlV3J)-syuq!U#unA75_2pEyMu(*XdI_$-J?7qmH=0z$sG zho{E4!EI?etL9K_-8-`+*+~(o?znY}N5S@ne3r@Tu>h*UVlwibInrgWJgB40M~sqt z>qO4KGncVY>ft>Jyr{exy^>ur#`zD5l++=>{EI}jcKCV8E>e2dp$W(u7{t%dGK+)_ zOvOV|+S^ZP;I}_%Hj17hSeSk|K!XMVxGmIHmF^gwm$Gj%Nmw_9?-l?$sRs>I!59dg zjjVvomM&RuR&x3a7m-uXc_mZXGb@-4eB$H)492eY)$b=Qte44zh4|VS^mI@X-LvV@ zdQsg__l3WHUu{4!l`+Wer%GC03=PHHao38X`W zIPvfBTR-I`Kf_U$4McpWW{7a5FaJ_(_Klg`z+=~S-^e}ENH3SH%sYj)6@QZ7Tgw$ zo3Xfj4?qkOd+B*Bph9h<0U!nb9X(X>56PVJQ4mGiJMVFrq^_GEh=Pz1)+8(zxT5=( z;%{{jc1hAro(cfyp zk3|E{$+Y6hz6$6rfacO`ra{VpJt!qUDQUZ%;??P}2`i0oBQ85c+`T$8Gsz#T`K+WB zJSi@4Kuq9FH(RnK2DW`E?EEh7iD5ue)d2mby^;6c1R1E5Og6a6ooeOc`uHGF*~TQj zoc^dL#g{FVfi>dXaxJFxhYGOXknvc9Zb`18);ctz z9FFPucJ!{C3;rAV_;7$=3`lbo1!?B|kZwA02@d>u(t|&2itBTwu+$04km_rh)vC}n zuscFramnm2q@@l;auw*mFST-FAJ{wNjSSu1I>w6f=kWtH$ddypOJUY|C6Xtb7%xg$ zZY|3qn@hxL1XweTd2NOHj zf%m;Im&$VZT1f#y!a_Vg?q?KpH5Hd3rV~>1(%~Sr97&eVjn30jM88~`72k$WRNbFe zdg{+v98l-pN?HY&zLv}sQ^ErO2}tjdhZO(Hv+ugVhYyTnE50k!#UR#0bKaq~kbwyFLGok#7k z+Us4T8*jP0pcbu~HvW6eDACcdE@}^YaKH)UmP0UBNqLoEPjV58A#I;GppWo$(&sY7 zKlSB9Px{6JB9_$<27sz8kbEzIbUh>LVF$H4=yMv0$fLS&N})n;7uG7cV1zNZ5|`+k;l)i`V`k`v2Xpz_f*eE{JR zdGY~*7gyk;hQ3Z&NZwmfBr%S?0A1_z*$}Ee=!dz3+LWiA%7r5Aq~yoJ;WR@Nx$o^# zNK$J6fa`3FW3t0Iatju@inRr_?MTq5otgkCS?$k8?@Z#lCxrMY_{$W1qT&W=R^N&R z`}Z`OFS)H#Az#&JNec1cUYRXTkMA&R>1k#%JxiXl$iuMhnj*!djR)^Fh)ZsjeZ(Gv zMrkQ3T(pV7q*gu}+aDpg(lH%-6+>r*)p4q_Et!vg#1YTwqg9%r3><-Yjd}=UKRHY2 zgU!}%^Q+dMb(qxKE*#ml` zYJFh;v3E7F(y6BMvRnqQcXpDm43BeTo zcK0BVLTeQzUf!Eeh06_~3(gzfQU)R*j)4M#uFy^LBp{Rssc`$V0f&y%MnAq&?O`o$ z$ON3nO=xB`FB$jvH&_cG@Av2O^LU9tT?j7@VB^hMTKbkk+-bvn4=U=x*bLS2d1%$A z%*Y^|GR0bWEwB*mQ&CnJHDEr+1NjRR61L97cY;~;RMhq%)m%SQ%*!H{dlE!w?!NOU^9bpnhu#9b zO}N6T{FI@h1`LU9(QBX8;4hy)wszuISXKt9j_y0`UWin;`Jk^5H6wYIq$J#MSV2f zs3~gPR12Hw-0raVW#L;JzVE0VyA1XL z7cxu*wWq?kP>=VwjJR2aEJXm9Uzmp^Zmx3V`=d3(()wlC)8T&ldn_kBJdAGwZ z(7V%-az&d1bagHpIYY{QXvL>y2k+?pAZ=Wvr6bq;b0)!HxAK~TGm!O14K+^>XRCfv zY(KqE`0#4qUyfZT6J4kv*4VZr!17e>N;h8lIm0p;^;zm^q#d0(0kG4(xE1_|U|{FE zo-}+PRoT8kCl((~Jt`fJ>crqm;<9_u9Mi*w^^7*j{4s$zT5VA(fa2FOoZv0^N;`um zgtg0k9 zz9D5d69_iPZ3Vp&_t)QNc)nGoR$G6`6DU|jigjvWp6Ae}cx~9QmM&3grY)piGPVNV zV&fkIuUrCWjr7wJ1N$lkH`qg}gSDwKeQdAYA00-`c)J?5VtDJI04Ntjlosj^-(MOF z*lN<4zwU}_0&ox{c(3hG)>ubUp|)~IR~_q+9dh=KXxVIQ>SxdVy&=`25E(sGy=^YB z?*)iMCMEeMp6i+C$`drLoiCvGTL_j<-%tjXICH;%lSTGvRl>Cv55CCnUZu^f3XfB6 z_4TJJy%g5%ChGg<{ld5}fqV!tv0eM^x>Ea8;@y*<8>9vumP(xq<9#@%5)okY#a6@l z17`qcxrkNvU}+=&1-s?O7*Q{a+`p;cgHX;@R(=k1R#@Z;l zO*9h}w7Yo-Y5R|XZGQp*b_F9^(=%^nW*Mp>b9*ljJ>0s}6C%U{{Su}fIfoTjw}m9} zwrhEZqY}&Y05|Lp>Ls?Y)VhOKmbpcR(JyO)1*S<^x8lzqvh)d%etB3{XUDXuRnyjRf5H;y$O#GTujj7gEa&6)BN%LB&M?lWD73TYxNG(_w?R;79STWbeI>Q1E7 zn=zEcfc-ZEWU4qJKVGd6#|O@~CvM?Q^D_KfMq^t?6A5320$)>il@kU~5`Gz&#=5(6 zR4aCv7LdjA>WPp^aW^a)GVe}0MY?NHI9>L+(P^o}?n~F7GPNn9;vhesNplTZ%WFun z%w$UehG-XBW&Q1$rk!>je|6e2k;w3`PQBl+O_P==(WIbsA8D|8gy@x=R=+IY{kLVjzSgahpsm{4}X1%3Bur@~=XwF*fo#o<;flHL3!)IIi zh-c5i23uxJ@-}HaCSj9z0}zAALme4gwnWPhM%ZX!;Xjyc(2RJYUBs5~)HxaQEOIz;ter z7Kk#wy#(9cKvk3hDOn;|fY@me#A{fdI*EG^^#<;w*@1fq-LNPC;fRm;*n$Z)#wyd; z>^|kxyy%@Erx9w0ZFbOmT+`+_Gj-k0iKLI6MFb}*4u!81Ra$UDlQ8( zt2&51aU?u<%?rF^8)}QAitxou@7^hu$jnkbBzi5tNJA(GWYcrI&ArY%QzgtD9b!Gs zsVvN^Hz;eiY7Jmo3!l_gB}*3R2CLAW&D2&?(d7mnWhCfA3hxIxh_$1f5;hU@%m09Y zpEuc!rYtr+^A9P(wVfv4Q6hpz$s=Z2KRZlx0HZ=`XZGfNy5Dq!ds(p^R}3iDkfo0( z_HTfI{zXXYIVg9o!XNDw07DH}xPxh{CjU4<>Hr&F?59{;gxF6n=`yBF#mYKmmizl^ zdt@Ini$YPXQr0<9l8pSb5=>M}xo znHfOIgZUt8@Yv%Q~ZfCr2yjYVsQ395u2{ylY~6K}0~nCe)e& zINMYRO4U2^mDW%8ZJWE}OsLCaEea3c3A<49WcHDeQ&1wu=1o!r0D#>dzg1c?s$?0l za{ooGb^y4?FTAFQd4{HD@OsXLY;{Ns6A|D{3@jmzdtqpx;CFk>SXr9U@AADK zu|bM+qWB^`o^wmEC{qCdq)aJRyXXEW+W7R}lICw!j4=M^zst9})%a$W!;B3QwhA1K zUlFKufA|4V-C266_Ug#k%Vjf+5AJ62>@Suj^vDi@hZ#VVWTkJPuv?-BxY5jvzBGJH z0LOrI5?m-kyZ8n!b8EhwT5WA9-oNB0zE-G3P>+3|VX=!^450;>K`Pbadzf9&;7%rKvu&v^c~(@+!}szWW)q< z^2{?sP63oTS?tYai(*;;vxypAld))#jcR!FOZ->DbCZu>D>u5h0x@~Kydogg&ok2$ zr1Bb!=CP3V!rW#8kon2`1a%SeGgB)L$mHZf;dJLmVpQYBa?^_2uhmF^&AnRr%Y7fU zvMEP3KN8iLf&-6UkphfGb^P-4J9vm6e^)YCkV?cxU|sbOzDVQBq?K*#l^B$9O6$Di zKl31lmmb(5qk%$RswaR*f>3qz$%7CMIzn?d?daHQSSByOaZ^# z?Ha%fXsK&J9(Tq*9L-idE*do#b8wW{Wn_CO5b=+;CC+8_pNTBuR>h~$nA3-(FXt4}LAUe2JHoHqS_}0TWuqGJn>QrMtnr@eT2(sKP%)D3bnu^KxNyA# z-4J+$l3bMBK~rtQ;%}NpKXx+#ypdR@(8BgvQ+a==OpGjdOBqSP%^p{>@#Kl)i|aMy z2eefWM)?F-^h2J4St^uw<$RXCbiqsd;Pi*&soO;<>~ku+LvFF`DBLQjxv?0=*#=Ns zISBm#Am0`O8L?}WU!io&o0>)Z`;0PmTdyJ{o-N;3#{G_mh?8k#3)ol%QhZd+Bj>L? z2}QRfjV8}xV>LBd%HpxaLXUBY%qo@Fs$?juIZbA+g))M%Yt*oGMVq#aDiCtfHK!pG z{g4=B`QxaRrCD&IJ!2~$6gkXHWNG_1t~kdvk0(QE#yCTF#T3g95gV5>E}su4jwW$^ zY{als#utqk*n59_rXyq|&P;kAO8~1@bI|815`ad`K5C=cr)AZZP^8ApEVKn-bPbDbC(?1Z1OVN+ zpwO){%BKB;;cxcFZo1n6qMJG%uveF&g;8E9BrYIbjwciIuybm}*c`b|nr0Hzrq)Nh zWEUz^8c{4G;T!3+C)80dEKMT+qge8sopBKal&E({~LpdA4Ysv`p%^HWN8@p{g_0a+a_ozRYcbS z^R@`s)nTUHm^wp8a&ron7>Ay4wb;sN4kdX json) { + roomInfo = RoomInfo.fromJson(json['room_info']); + anchorInfo = AnchorInfo.fromJson(json['anchor_info']); + isRoomFeed = json['is_room_feed']; + watchedShow = json['watched_show']; + likeInfoV3 = LikeInfoV3.fromJson(json['like_info_v3']); + blockInfo = json['block_info']; + } +} + +class RoomInfo { + RoomInfo({ + this.uid, + this.roomId, + this.title, + this.cover, + this.description, + this.liveStatus, + this.liveStartTime, + this.areaId, + this.areaName, + this.parentAreaId, + this.parentAreaName, + this.online, + this.background, + this.appBackground, + this.liveId, + }); + + int? uid; + int? roomId; + String? title; + String? cover; + String? description; + int? liveStatus; + int? liveStartTime; + int? areaId; + String? areaName; + int? parentAreaId; + String? parentAreaName; + int? online; + String? background; + String? appBackground; + String? liveId; + + RoomInfo.fromJson(Map json) { + uid = json['uid']; + roomId = json['room_id']; + title = json['title']; + cover = json['cover']; + description = json['description']; + liveStatus = json['liveS_satus']; + liveStartTime = json['live_start_time']; + areaId = json['area_id']; + areaName = json['area_name']; + parentAreaId = json['parent_area_id']; + parentAreaName = json['parent_area_name']; + online = json['online']; + background = json['background']; + appBackground = json['app_background']; + liveId = json['live_id']; + } +} + +class AnchorInfo { + AnchorInfo({ + this.baseInfo, + this.relationInfo, + }); + + BaseInfo? baseInfo; + RelationInfo? relationInfo; + + AnchorInfo.fromJson(Map json) { + baseInfo = BaseInfo.fromJson(json['base_info']); + relationInfo = RelationInfo.fromJson(json['relation_info']); + } +} + +class BaseInfo { + BaseInfo({ + this.uname, + this.face, + }); + + String? uname; + String? face; + + BaseInfo.fromJson(Map json) { + uname = json['uname']; + face = json['face']; + } +} + +class RelationInfo { + RelationInfo({this.attention}); + + int? attention; + + RelationInfo.fromJson(Map json) { + attention = json['attention']; + } +} + +class LikeInfoV3 { + LikeInfoV3({this.totalLikes}); + + int? totalLikes; + + LikeInfoV3.fromJson(Map json) { + totalLikes = json['total_likes']; + } +} diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 2f489fec..56da0a78 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -4,6 +4,8 @@ import 'package:pilipala/http/live.dart'; import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; +import '../../models/live/room_info_h5.dart'; + class LiveRoomController extends GetxController { String cover = ''; late int roomId; @@ -21,6 +23,7 @@ class LiveRoomController extends GetxController { // controlsStyle: ControlsStyle.live, // enabledButtons: const EnabledButtons(pip: true), // ); + Rx roomInfoH5 = RoomInfoH5Model().obs; @override void onInit() { @@ -37,10 +40,11 @@ class LiveRoomController extends GetxController { } } queryLiveInfo(); + queryLiveInfoH5(); } - playerInit(source) { - plPlayerController.setDataSource( + playerInit(source) async { + await plPlayerController.setDataSource( DataSource( videoSource: source, audioSource: null, @@ -66,7 +70,8 @@ class LiveRoomController extends GetxController { String videoUrl = (item.urlInfo?.first.host)! + item.baseUrl! + item.urlInfo!.first.extra!; - playerInit(videoUrl); + await playerInit(videoUrl); + return res; } } @@ -80,4 +85,12 @@ class LiveRoomController extends GetxController { volumeOff.value = true; } } + + Future queryLiveInfoH5() async { + var res = await LiveHttp.liveRoomInfoH5(roomId: roomId); + if (res['status']) { + roomInfoH5.value = res['data']; + } + return res; + } } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 5ac382e6..f727bfd0 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -19,6 +19,8 @@ class LiveRoomPage extends StatefulWidget { class _LiveRoomPageState extends State { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); PlPlayerController? plPlayerController; + late Future? _futureBuilder; + late Future? _futureBuilderFuture; bool isShowCover = true; bool isPlay = true; @@ -39,6 +41,8 @@ class _LiveRoomPageState extends State { if (Platform.isAndroid) { floating = Floating(); } + _futureBuilder = _liveRoomController.queryLiveInfoH5(); + _futureBuilderFuture = _liveRoomController.queryLiveInfo(); } @override @@ -52,57 +56,110 @@ class _LiveRoomPageState extends State { @override Widget build(BuildContext context) { + Widget videoPlayerPanel = FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData && snapshot.data['status']) { + return PLVideoPlayer( + controller: plPlayerController!, + bottomControl: BottomControl( + controller: plPlayerController, + liveRoomCtr: _liveRoomController, + floating: floating, + ), + ); + } else { + return const SizedBox(); + } + }, + ); + Widget childWhenDisabled = Scaffold( primary: true, - appBar: PreferredSize( - preferredSize: Size.fromHeight( - MediaQuery.of(context).orientation == Orientation.portrait ? 56 : 0, - ), - child: AppBar( - centerTitle: false, - titleSpacing: 0, - title: _liveRoomController.liveItem != null - ? Row( - children: [ - NetworkImgLayer( - width: 34, - height: 34, - type: 'avatar', - src: _liveRoomController.liveItem.face, - ), - const SizedBox(width: 10), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - _liveRoomController.liveItem.uname, - style: const TextStyle(fontSize: 14), - ), - const SizedBox(height: 1), - if (_liveRoomController.liveItem.watchedShow != null) - Text( - _liveRoomController - .liveItem.watchedShow['text_large'] ?? - '', - style: const TextStyle(fontSize: 12)), - ], - ), - ], - ) - : const SizedBox(), - // actions: [ - // SizedBox( - // height: 34, - // child: ElevatedButton(onPressed: () {}, child: const Text('关注')), - // ), - // const SizedBox(width: 12), - // ], - ), - ), - body: Column( + backgroundColor: Colors.black, + body: Stack( children: [ - Stack( + Obx( + () => + _liveRoomController.roomInfoH5.value.roomInfo?.appBackground != + '' + ? Positioned.fill( + child: Opacity( + opacity: 0.8, + child: NetworkImgLayer( + width: Get.width, + height: Get.height, + src: _liveRoomController + .roomInfoH5.value.roomInfo?.appBackground ?? + '', + ), + ), + ) + : Image.asset( + 'assets/images/live/default_bg.webp', + width: Get.width, + height: Get.height, + ), + ), + Column( children: [ + AppBar( + centerTitle: false, + titleSpacing: 0, + backgroundColor: Colors.transparent, + foregroundColor: Colors.white, + toolbarHeight: + MediaQuery.of(context).orientation == Orientation.portrait + ? 56 + : 0, + title: FutureBuilder( + future: _futureBuilder, + builder: (context, snapshot) { + if (snapshot.data == null) { + return const SizedBox(); + } + Map data = snapshot.data as Map; + if (data['status']) { + return Obx( + () => Row( + children: [ + NetworkImgLayer( + width: 34, + height: 34, + type: 'avatar', + src: _liveRoomController + .roomInfoH5.value.anchorInfo!.baseInfo!.face, + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _liveRoomController.roomInfoH5.value + .anchorInfo!.baseInfo!.uname!, + style: const TextStyle(fontSize: 14), + ), + const SizedBox(height: 1), + if (_liveRoomController + .roomInfoH5.value.watchedShow != + null) + Text( + _liveRoomController.roomInfoH5.value + .watchedShow!['text_large'] ?? + '', + style: const TextStyle(fontSize: 12), + ), + ], + ), + ], + ), + ); + } else { + return const SizedBox(); + } + }, + ), + ), PopScope( canPop: plPlayerController?.isFullScreen.value != true, onPopInvoked: (bool didPop) { @@ -120,55 +177,19 @@ class _LiveRoomPageState extends State { Orientation.landscape ? Get.size.height : Get.size.width * 9 / 16, - child: plPlayerController!.videoPlayerController != null - ? PLVideoPlayer( - controller: plPlayerController!, - bottomControl: BottomControl( - controller: plPlayerController, - liveRoomCtr: _liveRoomController, - floating: floating, - ), - ) - : const SizedBox(), + child: videoPlayerPanel, ), ), - // if (_liveRoomController.liveItem != null && - // _liveRoomController.liveItem.cover != null) - // Visibility( - // visible: isShowCover, - // child: Positioned( - // top: 0, - // left: 0, - // right: 0, - // child: NetworkImgLayer( - // type: 'emote', - // src: _liveRoomController.liveItem.cover, - // width: Get.size.width, - // height: videoHeight, - // ), - // ), - // ), ], ), ], ), ); - Widget childWhenEnabled = AspectRatio( - aspectRatio: 16 / 9, - child: plPlayerController!.videoPlayerController != null - ? PLVideoPlayer( - controller: plPlayerController!, - bottomControl: BottomControl( - controller: plPlayerController, - liveRoomCtr: _liveRoomController, - ), - ) - : const SizedBox(), - ); if (Platform.isAndroid) { return PiPSwitcher( childWhenDisabled: childWhenDisabled, - childWhenEnabled: childWhenEnabled, + childWhenEnabled: videoPlayerPanel, + floating: floating, ); } else { return childWhenDisabled; diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 3980453b..d073945b 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -586,6 +586,7 @@ class _PLVideoPlayerState extends State ), /// 进度条 live模式下禁用 + Obx( () { final int value = _.sliderPositionSeconds.value; @@ -609,7 +610,7 @@ class _PLVideoPlayerState extends State } if (_.videoType.value == 'live') { - return nil; + return const SizedBox(); } if (value > max || max <= 0) { return nil; diff --git a/pubspec.yaml b/pubspec.yaml index 16df4ad7..9597eab7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -188,6 +188,7 @@ flutter: - assets/images/ - assets/images/lv/ - assets/images/logo/ + - assets/images/live/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware From c39e91073b616626d4b8109443b9d877bc1ba782 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 7 Feb 2024 22:57:30 +0800 Subject: [PATCH 060/102] =?UTF-8?q?feat:=20=E5=BA=94=E7=94=A8=E5=86=85?= =?UTF-8?q?=E7=BC=93=E5=AD=98=E6=B8=85=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/about/index.dart | 25 ++++++ lib/utils/cache_manage.dart | 154 ++++++++++++++++++++++++++++++++++++ 2 files changed, 179 insertions(+) create mode 100644 lib/utils/cache_manage.dart diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 5df05d31..0f159ce7 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -7,6 +7,7 @@ import 'package:pilipala/http/index.dart'; import 'package:pilipala/models/github/latest.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:url_launcher/url_launcher.dart'; +import '../../utils/cache_manage.dart'; class AboutPage extends StatefulWidget { const AboutPage({super.key}); @@ -17,6 +18,19 @@ class AboutPage extends StatefulWidget { class _AboutPageState extends State { final AboutController _aboutController = Get.put(AboutController()); + String cacheSize = ''; + + @override + void initState() { + super.initState(); + // 读取缓存占用 + getCacheSize(); + } + + Future getCacheSize() async { + final res = await CacheManage().loadApplicationCache(); + setState(() => cacheSize = res); + } @override Widget build(BuildContext context) { @@ -138,6 +152,17 @@ class _AboutPageState extends State { title: const Text('错误日志'), trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), ), + ListTile( + onTap: () async { + var cleanStatus = await CacheManage().clearCacheAll(); + if (cleanStatus) { + getCacheSize(); + } + }, + title: const Text('清除缓存'), + subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle), + trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), + ), ], ), ), diff --git a/lib/utils/cache_manage.dart b/lib/utils/cache_manage.dart new file mode 100644 index 00000000..e250ab70 --- /dev/null +++ b/lib/utils/cache_manage.dart @@ -0,0 +1,154 @@ +import 'dart:async'; +import 'dart:io'; +import 'package:flutter/material.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; + +class CacheManage { + CacheManage._internal(); + + static final CacheManage cacheManage = CacheManage._internal(); + + factory CacheManage() => cacheManage; + + // 获取缓存目录 + Future loadApplicationCache() async { + /// clear all of image in memory + // clearMemoryImageCache(); + /// get ImageCache + // var res = getMemoryImageCache(); + + // 缓存大小 + double cacheSize = 0; + // cached_network_image directory + Directory tempDirectory = await getTemporaryDirectory(); + // get_storage directory + Directory docDirectory = await getApplicationDocumentsDirectory(); + + // 获取缓存大小 + if (tempDirectory.existsSync()) { + double value = await getTotalSizeOfFilesInDir(tempDirectory); + cacheSize += value; + } + + /// 获取缓存大小 dioCache + if (docDirectory.existsSync()) { + double value = 0; + String dioCacheFileName = + '${docDirectory.path}${Platform.pathSeparator}DioCache.db'; + var dioCacheFile = File(dioCacheFileName); + if (dioCacheFile.existsSync()) { + value = await getTotalSizeOfFilesInDir(dioCacheFile); + } + cacheSize += value; + } + + return formatSize(cacheSize); + } + + // 循环计算文件的大小(递归) + Future getTotalSizeOfFilesInDir(final FileSystemEntity file) async { + if (file is File) { + int length = await file.length(); + return double.parse(length.toString()); + } + if (file is Directory) { + final List children = file.listSync(); + double total = 0; + for (final FileSystemEntity child in children) { + total += await getTotalSizeOfFilesInDir(child); + } + return total; + } + return 0; + } + + // 缓存大小格式转换 + String formatSize(double value) { + List unitArr = ['B', 'K', 'M', 'G']; + int index = 0; + while (value > 1024) { + index++; + value = value / 1024; + } + String size = value.toStringAsFixed(2); + return size + unitArr[index]; + } + + // 清除缓存 + Future clearCacheAll() async { + bool cleanStatus = await SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('该操作将清除图片及网络请求缓存数据,确认清除?'), + actions: [ + TextButton( + onPressed: (() => {SmartDialog.dismiss()}), + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + SmartDialog.dismiss(); + SmartDialog.showLoading(msg: '正在清除...'); + try { + // 清除缓存 图片缓存 + await clearLibraryCache(); + Timer(const Duration(milliseconds: 500), () { + SmartDialog.dismiss().then((res) { + SmartDialog.showToast('清除完成'); + }); + }); + } catch (err) { + SmartDialog.dismiss(); + SmartDialog.showToast(err.toString()); + } + }, + child: const Text('确认'), + ) + ], + ); + }, + ).then((res) { + return true; + }); + return cleanStatus; + } + + /// 清除 Documents 目录下的 DioCache.db + Future clearApplicationCache() async { + Directory directory = await getApplicationDocumentsDirectory(); + if (directory.existsSync()) { + String dioCacheFileName = + '${directory.path}${Platform.pathSeparator}DioCache.db'; + var dioCacheFile = File(dioCacheFileName); + if (dioCacheFile.existsSync()) { + dioCacheFile.delete(); + } + } + } + + // 清除 Library/Caches 目录及文件缓存 + Future clearLibraryCache() async { + var appDocDir = await getTemporaryDirectory(); + if (appDocDir.existsSync()) { + await appDocDir.delete(recursive: true); + } + } + + /// 递归方式删除目录及文件 + Future deleteDirectory(FileSystemEntity file) async { + if (file is Directory) { + final List children = file.listSync(); + for (final FileSystemEntity child in children) { + await deleteDirectory(child); + } + } + await file.delete(); + } +} From 0d5d33a365a4b103fe886865bc890ce0c9976976 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 8 Feb 2024 10:29:26 +0800 Subject: [PATCH 061/102] =?UTF-8?q?feat:=20up=E6=8A=95=E7=A8=BF=E6=8E=92?= =?UTF-8?q?=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_archive/controller.dart | 14 +++++--- lib/pages/member_archive/view.dart | 44 ++++++------------------ 2 files changed, 21 insertions(+), 37 deletions(-) diff --git a/lib/pages/member_archive/controller.dart b/lib/pages/member_archive/controller.dart index 785cd764..4c41de4c 100644 --- a/lib/pages/member_archive/controller.dart +++ b/lib/pages/member_archive/controller.dart @@ -25,7 +25,7 @@ class MemberArchiveController extends GetxController { // 获取用户投稿 Future getMemberArchive(type) async { - if (type == 'onRefresh') { + if (type == 'init') { pn = 1; } var res = await MemberHttp.memberArchive( @@ -34,7 +34,12 @@ class MemberArchiveController extends GetxController { order: currentOrder['type']!, ); if (res['status']) { - archivesList.addAll(res['data'].list.vlist); + if (type == 'init') { + archivesList.value = res['data'].list.vlist; + } + if (type == 'onLoad') { + archivesList.addAll(res['data'].list.vlist); + } count = res['data'].page['count']; pn += 1; } @@ -42,13 +47,14 @@ class MemberArchiveController extends GetxController { } toggleSort() async { - pn = 1; - int index = orderList.indexOf(currentOrder); + List typeList = orderList.map((e) => e['type']!).toList(); + int index = typeList.indexOf(currentOrder['type']!); if (index == orderList.length - 1) { currentOrder.value = orderList.first; } else { currentOrder.value = orderList[index + 1]; } + getMemberArchive('init'); } // 上拉加载 diff --git a/lib/pages/member_archive/view.dart b/lib/pages/member_archive/view.dart index 5091026e..0b5417b4 100644 --- a/lib/pages/member_archive/view.dart +++ b/lib/pages/member_archive/view.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/utils/utils.dart'; +import '../../common/constants.dart'; import 'controller.dart'; class MemberArchivePage extends StatefulWidget { @@ -48,39 +49,16 @@ class _MemberArchivePageState extends State { titleSpacing: 0, centerTitle: false, title: Text('他的投稿', style: Theme.of(context).textTheme.titleMedium), - // actions: [ - // Obx( - // () => PopupMenuButton( - // padding: EdgeInsets.zero, - // tooltip: '投稿排序', - // icon: Icon( - // Icons.more_vert_outlined, - // color: Theme.of(context).colorScheme.outline, - // ), - // position: PopupMenuPosition.under, - // onSelected: (String type) {}, - // itemBuilder: (BuildContext context) => >[ - // for (var i in _memberArchivesController.orderList) ...[ - // PopupMenuItem( - // onTap: () {}, - // value: _memberArchivesController.currentOrder['label'], - // child: Row( - // mainAxisSize: MainAxisSize.min, - // children: [ - // Text(i['label']!), - // if (_memberArchivesController.currentOrder['label'] == - // i['label']) ...[ - // const SizedBox(width: 10), - // const Icon(Icons.done, size: 20), - // ], - // ], - // ), - // ), - // ] - // ], - // ), - // ), - // ], + actions: [ + Obx( + () => TextButton.icon( + icon: const Icon(Icons.sort, size: 20), + onPressed: _memberArchivesController.toggleSort, + label: Text(_memberArchivesController.currentOrder['label']!), + ), + ), + const SizedBox(width: 6), + ], ), body: CustomScrollView( controller: _memberArchivesController.scrollController, From fb8b2de115a1ebb84a583500e5e7e0561f74922b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 8 Feb 2024 21:27:22 +0800 Subject: [PATCH 062/102] =?UTF-8?q?feat:=20up=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/member.dart | 37 +++++++ lib/pages/follow/view.dart | 42 ++++++++ lib/pages/follow/widgets/follow_item.dart | 2 +- lib/pages/follow_search/controller.dart | 73 +++++++++++++ lib/pages/follow_search/index.dart | 4 + lib/pages/follow_search/view.dart | 121 ++++++++++++++++++++++ lib/router/app_pages.dart | 6 +- 8 files changed, 286 insertions(+), 2 deletions(-) create mode 100644 lib/pages/follow_search/controller.dart create mode 100644 lib/pages/follow_search/index.dart create mode 100644 lib/pages/follow_search/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 532ca341..77694de7 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -214,6 +214,9 @@ class Api { // https://api.bilibili.com/x/relation/tags static const String followingsClass = '/x/relation/tags'; + // 搜索follow + static const followSearch = '/x/relation/followings/search'; + // 粉丝 // vmid 用户id pn 页码 ps 每页个数,最大50 order: desc // order_type 排序规则 最近访问传空,最常访问传 attention diff --git a/lib/http/member.dart b/lib/http/member.dart index bf84b6eb..6b6df7fe 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -461,4 +461,41 @@ class MemberHttp { }; } } + + // 搜索follow + static Future getfollowSearch({ + required int mid, + required int ps, + required int pn, + required String name, + }) async { + Map data = { + 'vmid': mid, + 'pn': pn, + 'ps': ps, + 'order': 'desc', + 'order_type': 'attention', + 'gaia_source': 'main_web', + 'name': name, + 'web_location': 333.999, + }; + Map params = await WbiSign().makSign(data); + var res = await Request().get(Api.followSearch, data: { + ...data, + 'w_rid': params['w_rid'], + 'wts': params['wts'], + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': FollowDataModel.fromJson(res.data['data']) + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/follow/view.dart b/lib/pages/follow/view.dart index a9fcab4e..9633e7f0 100644 --- a/lib/pages/follow/view.dart +++ b/lib/pages/follow/view.dart @@ -37,6 +37,29 @@ class _FollowPageState extends State { : '${_followController.name}的关注', style: Theme.of(context).textTheme.titleMedium, ), + actions: [ + IconButton( + onPressed: () => Get.toNamed('/followSearch?mid=$mid'), + icon: const Icon(Icons.search_outlined), + ), + PopupMenuButton( + icon: const Icon(Icons.more_vert), + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + onTap: () => Get.toNamed('/blackListPage'), + child: const Row( + mainAxisSize: MainAxisSize.min, + children: [ + Icon(Icons.block, size: 19), + SizedBox(width: 10), + Text('黑名单管理'), + ], + ), + ) + ], + ), + const SizedBox(width: 6), + ], ), body: Obx( () => !_followController.isOwner.value @@ -87,3 +110,22 @@ class _FollowPageState extends State { ); } } + +class _FakeAPI { + static const List _kOptions = [ + 'aardvark', + 'bobcat', + 'chameleon', + ]; + // Searches the options, but injects a fake "network" delay. + static Future> search(String query) async { + await Future.delayed( + const Duration(seconds: 1)); // Fake 1 second delay. + if (query == '') { + return const Iterable.empty(); + } + return _kOptions.where((String option) { + return option.contains(query.toLowerCase()); + }); + } +} diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart index ac9cc01b..d21a89bc 100644 --- a/lib/pages/follow/widgets/follow_item.dart +++ b/lib/pages/follow/widgets/follow_item.dart @@ -42,7 +42,7 @@ class FollowItem extends StatelessWidget { overflow: TextOverflow.ellipsis, ), dense: true, - trailing: ctr!.isOwner.value + trailing: ctr != null && ctr!.isOwner.value ? SizedBox( height: 34, child: TextButton( diff --git a/lib/pages/follow_search/controller.dart b/lib/pages/follow_search/controller.dart new file mode 100644 index 00000000..9fd1590d --- /dev/null +++ b/lib/pages/follow_search/controller.dart @@ -0,0 +1,73 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/member.dart'; + +import '../../models/follow/result.dart'; + +class FollowSearchController extends GetxController { + Rx controller = TextEditingController().obs; + final FocusNode searchFocusNode = FocusNode(); + RxString searchKeyWord = ''.obs; + String hintText = '搜索'; + RxString loadingStatus = 'init'.obs; + late int mid = 1; + RxString uname = ''.obs; + int ps = 20; + int pn = 1; + RxList followList = [].obs; + RxInt total = 0.obs; + + @override + void onInit() { + super.onInit(); + mid = int.parse(Get.parameters['mid']!); + } + + // 清空搜索 + void onClear() { + if (searchKeyWord.value.isNotEmpty && controller.value.text != '') { + controller.value.clear(); + searchKeyWord.value = ''; + } else { + Get.back(); + } + } + + void onChange(value) { + searchKeyWord.value = value; + } + + // 提交搜索内容 + void submit() { + loadingStatus.value = 'loading'; + searchFollow(); + } + + Future searchFollow({type = 'init'}) async { + if (controller.value.text == '') { + return {'status': true, 'data': [].obs}; + } + if (type == 'init') { + ps = 1; + } + var res = await MemberHttp.getfollowSearch( + mid: mid, + ps: ps, + pn: pn, + name: controller.value.text, + ); + if (res['status']) { + if (type == 'init') { + followList.value = res['data'].list; + } else { + followList.addAll(res['data'].list); + } + total.value = res['data'].total; + } + return res; + } + + void onLoad() { + searchFollow(type: 'onLoad'); + } +} diff --git a/lib/pages/follow_search/index.dart b/lib/pages/follow_search/index.dart new file mode 100644 index 00000000..805d8c47 --- /dev/null +++ b/lib/pages/follow_search/index.dart @@ -0,0 +1,4 @@ +library follow_search; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/follow_search/view.dart b/lib/pages/follow_search/view.dart new file mode 100644 index 00000000..6be42676 --- /dev/null +++ b/lib/pages/follow_search/view.dart @@ -0,0 +1,121 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/pages/follow_search/index.dart'; + +import '../follow/widgets/follow_item.dart'; + +class FollowSearchPage extends StatefulWidget { + const FollowSearchPage({super.key}); + + @override + State createState() => _FollowSearchPageState(); +} + +class _FollowSearchPageState extends State { + final FollowSearchController _followSearchController = + Get.put(FollowSearchController()); + late Future? _futureBuilder; + final ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _futureBuilder = _followSearchController.searchFollow(); + scrollController.addListener( + () { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle( + 'my-throttler', const Duration(milliseconds: 500), () { + _followSearchController.onLoad(); + }); + } + }, + ); + } + + void reRequest() { + setState(() { + _futureBuilder = _followSearchController.searchFollow(); + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + titleSpacing: 0, + actions: [ + IconButton( + onPressed: reRequest, + icon: const Icon(CupertinoIcons.search, size: 22), + ), + const SizedBox(width: 6), + ], + title: TextField( + autofocus: true, + focusNode: _followSearchController.searchFocusNode, + controller: _followSearchController.controller.value, + textInputAction: TextInputAction.search, + onChanged: (value) => _followSearchController.onChange(value), + decoration: InputDecoration( + hintText: _followSearchController.hintText, + border: InputBorder.none, + suffixIcon: IconButton( + icon: Icon( + Icons.clear, + size: 22, + color: Theme.of(context).colorScheme.outline, + ), + onPressed: () => _followSearchController.onClear(), + ), + ), + onSubmitted: (String value) => reRequest(), + ), + ), + body: FutureBuilder( + future: _futureBuilder, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + var data = snapshot.data; + if (data == null) { + return CustomScrollView( + slivers: [ + HttpError(errMsg: snapshot.data['msg'], fn: reRequest) + ], + ); + } + if (data['status']) { + RxList followList = _followSearchController.followList; + return Obx( + () => followList.isNotEmpty + ? ListView.builder( + controller: scrollController, + itemCount: followList.length, + itemBuilder: ((context, index) { + return FollowItem( + item: followList[index], + ); + }), + ) + : CustomScrollView( + slivers: [HttpError(errMsg: '未搜索到结果', fn: reRequest)], + ), + ); + } else { + return CustomScrollView( + slivers: [ + HttpError(errMsg: snapshot.data['msg'], fn: reRequest) + ], + ); + } + } else { + return const SizedBox(); + } + }), + ); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index ff506630..45d7cad1 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -3,6 +3,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/setting/pages/logs.dart'; import '../pages/about/index.dart'; @@ -104,7 +105,8 @@ class Routes { CustomGetPage( name: '/replyReply', page: () => const VideoReplyReplyPanel()), // 推荐设置 - CustomGetPage(name: '/recommendSetting', page: () => const RecommendSetting()), + CustomGetPage( + name: '/recommendSetting', page: () => const RecommendSetting()), // 播放设置 CustomGetPage(name: '/playSetting', page: () => const PlaySetting()), // 外观设置 @@ -156,6 +158,8 @@ class Routes { name: '/memberSeasons', page: () => const MemberSeasonsPage()), // 日志 CustomGetPage(name: '/logs', page: () => const LogsPage()), + // 搜索关注 + CustomGetPage(name: '/followSearch', page: () => const FollowSearchPage()), ]; } From 94f3b7c1e4797fbf04f3ee7124af5e5a50ea2481 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 8 Feb 2024 21:33:02 +0800 Subject: [PATCH 063/102] =?UTF-8?q?fix:=20minePage=20=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/mine/controller.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index d1e17a83..5ad9e852 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -119,7 +119,7 @@ class MineController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - Get.toNamed('/follow?mid=${userInfo.value.mid}'); + Get.toNamed('/follow?mid=${userInfo.value.mid}', preventDuplicates: false); } pushFans() { @@ -127,7 +127,7 @@ class MineController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - Get.toNamed('/fan?mid=${userInfo.value.mid}'); + Get.toNamed('/fan?mid=${userInfo.value.mid}', preventDuplicates: false); } pushDynamic() { @@ -135,6 +135,7 @@ class MineController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - Get.toNamed('/memberDynamics?mid=${userInfo.value.mid}'); + Get.toNamed('/memberDynamics?mid=${userInfo.value.mid}', + preventDuplicates: false); } } From 10b928474bfc96ccc32fd33d0a6b3b1ad5c1eea6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 8 Feb 2024 22:46:39 +0800 Subject: [PATCH 064/102] mod --- lib/pages/live/widgets/live_item.dart | 36 ++++++++++++------ lib/pages/live_room/view.dart | 55 +++++++++++++++++---------- 2 files changed, 59 insertions(+), 32 deletions(-) diff --git a/lib/pages/live/widgets/live_item.dart b/lib/pages/live/widgets/live_item.dart index 8fa797fb..9218d4fb 100644 --- a/lib/pages/live/widgets/live_item.dart +++ b/lib/pages/live/widgets/live_item.dart @@ -184,18 +184,32 @@ class VideoStat extends StatelessWidget { tileMode: TileMode.mirror, ), ), - child: RichText( - maxLines: 1, - textAlign: TextAlign.justify, - softWrap: false, - text: TextSpan( - style: const TextStyle(fontSize: 11, color: Colors.white), - children: [ - TextSpan(text: liveItem!.areaName!), - TextSpan(text: liveItem!.watchedShow!['text_small']), - ], - ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + liveItem!.areaName!, + style: const TextStyle(fontSize: 11, color: Colors.white), + ), + Text( + liveItem!.watchedShow!['text_small'], + style: const TextStyle(fontSize: 11, color: Colors.white), + ), + ], ), + + // child: RichText( + // maxLines: 1, + // textAlign: TextAlign.justify, + // softWrap: false, + // text: TextSpan( + // style: const TextStyle(fontSize: 11, color: Colors.white), + // children: [ + // TextSpan(text: liveItem!.areaName!), + // TextSpan(text: liveItem!.watchedShow!['text_small']), + // ], + // ), + // ), ); } } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index f727bfd0..20dfe403 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -79,27 +79,40 @@ class _LiveRoomPageState extends State { backgroundColor: Colors.black, body: Stack( children: [ - Obx( - () => - _liveRoomController.roomInfoH5.value.roomInfo?.appBackground != - '' - ? Positioned.fill( - child: Opacity( - opacity: 0.8, - child: NetworkImgLayer( - width: Get.width, - height: Get.height, - src: _liveRoomController - .roomInfoH5.value.roomInfo?.appBackground ?? - '', - ), - ), - ) - : Image.asset( - 'assets/images/live/default_bg.webp', - width: Get.width, - height: Get.height, - ), + // Obx( + // () => Positioned.fill( + // child: Opacity( + // opacity: 0.8, + // child: _liveRoomController + // .roomInfoH5.value.roomInfo?.appBackground != + // '' && + // _liveRoomController + // .roomInfoH5.value.roomInfo?.appBackground != + // null + // ? NetworkImgLayer( + // width: Get.width, + // height: Get.height, + // src: _liveRoomController + // .roomInfoH5.value.roomInfo?.appBackground ?? + // '', + // ) + // : Image.asset( + // 'assets/images/live/default_bg.webp', + // width: Get.width, + // height: Get.height, + // ), + // ), + // ), + // ), + Positioned.fill( + child: Opacity( + opacity: 0.8, + child: Image.asset( + 'assets/images/live/default_bg.webp', + width: Get.width, + height: Get.height, + ), + ), ), Column( children: [ From d0f036ec35a20f30b09b87d89cec0d813081c368 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 9 Feb 2024 09:32:28 +0800 Subject: [PATCH 065/102] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D=E5=A4=9A=E5=BC=A0=E5=9B=BE=E7=89=87=E6=8B=89=E4=BC=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 7991a366..d6778293 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -797,8 +797,7 @@ InlineSpan buildContent( ), ), ); - } - if (len > 1) { + } else if (len > 1) { List list = []; for (var i = 0; i < len; i++) { picList.add(content.pictures[i]['img_src']); @@ -816,10 +815,11 @@ InlineSpan buildContent( ); }, child: NetworkImgLayer( - src: content.pictures[i]['img_src'], - width: box.maxWidth, - height: box.maxWidth, - ), + src: content.pictures[i]['img_src'], + width: box.maxWidth, + height: box.maxWidth, + origAspectRatio: content.pictures[i]['img_width'] / + content.pictures[i]['img_height']), ); }, ), From 44a162762cc134ec2b752021d05c89096fd8f2e6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 9 Feb 2024 23:24:26 +0800 Subject: [PATCH 066/102] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E8=B7=AF=E7=94=B1=E8=B7=B3=E8=BD=AC=20issues=20#405?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 74 +++++++++++++------ lib/utils/url_utils.dart | 61 +++++++++++++++ 2 files changed, 113 insertions(+), 22 deletions(-) create mode 100644 lib/utils/url_utils.dart diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 7991a366..fb76f4d4 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,7 +1,6 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.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:hive/hive.dart'; import 'package:pilipala/common/widgets/badge.dart'; @@ -12,10 +11,9 @@ import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; +import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; - import 'zan.dart'; Box setting = GStrorage.setting; @@ -541,7 +539,6 @@ InlineSpan buildContent( // fReplyItem 父级回复内容,用作二楼回复(回复详情)展示 final content = replyItem.content; final List spanChilds = []; - bool hasMatchMember = false; // 投票 if (content.vote.isNotEmpty) { @@ -591,7 +588,7 @@ InlineSpan buildContent( if (patternStr.isNotEmpty) { patternStr += "|"; } - patternStr += r'(\b\d{1,2}[::]\d{2}\b)'; + patternStr += r'(\b\d{1,2}[::]\d{2}(?:[::]\d{2})?\b)'; final RegExp pattern = RegExp(patternStr); List matchedStrs = []; void addPlainTextSpan(str) { @@ -639,7 +636,9 @@ InlineSpan buildContent( }, ), ); - } else if (RegExp(r'^\b[0-9]{1,2}[::][0-9]{2}\b$').hasMatch(matchStr)) { + } else if (RegExp(r'^\b\d{1,2}[::]\d{2}(?:[::]\d{2})?\b$') + .hasMatch(matchStr)) { + matchStr = matchStr.replaceAll(':', ':'); spanChilds.add( TextSpan( text: ' $matchStr ', @@ -650,7 +649,6 @@ InlineSpan buildContent( ..onTap = () { // 跳转到指定位置 try { - matchStr = matchStr.replaceAll(':', ':'); SmartDialog.showToast('跳转至:$matchStr'); Get.find(tag: Get.arguments['heroTag']) .plPlayerController @@ -692,16 +690,54 @@ InlineSpan buildContent( color: Theme.of(context).colorScheme.primary, ), recognizer: TapGestureRecognizer() - ..onTap = () { + ..onTap = () async { + final String title = content.jumpUrl[matchStr]['title']; if (appUrlSchema == '') { - final String str = Uri.parse(matchStr).pathSegments[0]; - final Map matchRes = IdUtils.matchAvorBv(input: str); - final List matchKeys = matchRes.keys.toList(); - if (matchKeys.isNotEmpty) { - if (matchKeys.first == 'BV') { + final String redirectUrl = + await UrlUtils.parseRedirectUrl(matchStr); + final String pathSegment = Uri.parse(redirectUrl).path; + final String lastPathSegment = + pathSegment.split('/').last; + if (lastPathSegment.startsWith('BV')) { + UrlUtils.matchUrlPush( + lastPathSegment, + title, + redirectUrl, + ); + } else { + Get.toNamed( + '/webview', + parameters: { + 'url': redirectUrl, + 'type': 'url', + 'pageTitle': title + }, + ); + } + } else { + if (appUrlSchema.startsWith('bilibili://search')) { + Get.toNamed('/searchResult', + parameters: {'keyword': title}); + } else if (matchStr.startsWith('https://b23.tv')) { + final String redirectUrl = + await UrlUtils.parseRedirectUrl(matchStr); + final String pathSegment = Uri.parse(redirectUrl).path; + final String lastPathSegment = + pathSegment.split('/').last; + if (lastPathSegment.startsWith('BV')) { + UrlUtils.matchUrlPush( + lastPathSegment, + title, + redirectUrl, + ); + } else { Get.toNamed( - '/searchResult', - parameters: {'keyword': matchRes['BV']}, + '/webview', + parameters: { + 'url': redirectUrl, + 'type': 'url', + 'pageTitle': title + }, ); } } else { @@ -710,16 +746,10 @@ InlineSpan buildContent( parameters: { 'url': matchStr, 'type': 'url', - 'pageTitle': '' + 'pageTitle': title }, ); } - } else { - if (appUrlSchema.startsWith('bilibili://search')) { - Get.toNamed('/searchResult', parameters: { - 'keyword': content.jumpUrl[matchStr]['title'] - }); - } } }, ) diff --git a/lib/utils/url_utils.dart b/lib/utils/url_utils.dart new file mode 100644 index 00000000..bac6cdfa --- /dev/null +++ b/lib/utils/url_utils.dart @@ -0,0 +1,61 @@ +import 'package:dio/dio.dart'; +import 'package:get/get.dart'; + +import '../http/search.dart'; +import 'id_utils.dart'; +import 'utils.dart'; + +class UrlUtils { + // 302重定向路由截取 + static Future parseRedirectUrl(String url) async { + late String redirectUrl; + final dio = Dio(); + dio.options.followRedirects = false; + dio.options.validateStatus = (status) { + return status == 200 || status == 301 || status == 302; + }; + final response = await dio.get(url); + if (response.statusCode == 302) { + redirectUrl = response.headers['location']?.first as String; + if (redirectUrl.endsWith('/')) { + redirectUrl = redirectUrl.substring(0, redirectUrl.length - 1); + } + } else { + if (url.endsWith('/')) { + url = url.substring(0, url.length - 1); + } + return url; + } + return redirectUrl; + } + + // 匹配url路由跳转 + static matchUrlPush( + String pathSegment, + String title, + String redirectUrl, + ) async { + final Map matchRes = IdUtils.matchAvorBv(input: pathSegment); + if (matchRes.containsKey('BV')) { + final String bv = matchRes['BV']; + final int cid = await SearchHttp.ab2c(bvid: bv); + final String heroTag = Utils.makeHeroTag(bv); + await Get.toNamed( + '/video?bvid=$bv&cid=$cid', + arguments: { + 'pic': '', + 'heroTag': heroTag, + }, + ); + } else { + await Get.toNamed( + '/webview', + parameters: { + 'url': redirectUrl, + 'type': 'url', + 'pageTitle': title, + }, + ); + } + } +} From 3d5ebe7e99a1340e14618ca934d5600ee591a3b8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 10 Feb 2024 19:57:10 +0800 Subject: [PATCH 067/102] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E8=AF=84=E8=AE=BA=E9=87=8D=E5=A4=8D=E8=AF=B7?= =?UTF-8?q?=E6=B1=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/controller.dart | 3 +++ lib/pages/video/detail/reply/view.dart | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 5781cbba..bfcd0e80 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -53,6 +53,9 @@ class VideoReplyController extends GetxController { } Future queryReplyList({type = 'init'}) async { + if (isLoadingMore) { + return; + } isLoadingMore = true; if (type == 'init') { currentPage = 0; diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index b2c67b1e..9b7546af 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -75,7 +75,8 @@ class _VideoReplyPanelState extends State () { if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 300) { - EasyThrottle.throttle('replylist', const Duration(seconds: 2), () { + EasyThrottle.throttle('replylist', const Duration(milliseconds: 200), + () { _videoReplyController.onLoad(); }); } From 289cc99bc2a88cce0b0f6596a78ee0c40594bdb6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 11 Feb 2024 09:10:45 +0800 Subject: [PATCH 068/102] mod --- lib/pages/member_archive/view.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pages/member_archive/view.dart b/lib/pages/member_archive/view.dart index 0b5417b4..43867323 100644 --- a/lib/pages/member_archive/view.dart +++ b/lib/pages/member_archive/view.dart @@ -3,7 +3,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/utils/utils.dart'; -import '../../common/constants.dart'; import 'controller.dart'; class MemberArchivePage extends StatefulWidget { @@ -26,8 +25,7 @@ class _MemberArchivePageState extends State { final String heroTag = Utils.makeHeroTag(mid); _memberArchivesController = Get.put(MemberArchiveController(), tag: heroTag); - _futureBuilderFuture = - _memberArchivesController.getMemberArchive('onRefresh'); + _futureBuilderFuture = _memberArchivesController.getMemberArchive('init'); scrollController = _memberArchivesController.scrollController; scrollController.addListener( () { From e2fd01a6d5154d3f068a60b0bbecad3420447c04 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 11 Feb 2024 18:47:51 +0800 Subject: [PATCH 069/102] =?UTF-8?q?fix:=20video=20Storage=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 1 - lib/utils/storage.dart | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 5849f8a3..fc2149de 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -70,7 +70,6 @@ void main() async { statusBarColor: Colors.transparent, )); Data.init(); - GStrorage.lazyInit(); PiliSchame.init(); }); } diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 1b0b0c37..41ec0ce8 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -42,6 +42,8 @@ class GStrorage { return deletedEntries > 10; }, ); + // 视频设置 + video = await Hive.openBox('video'); } static void regAdapter() { @@ -52,11 +54,6 @@ class GStrorage { Hive.registerAdapter(HotSearchItemAdapter()); } - static Future lazyInit() async { - // 视频设置 - video = await Hive.openBox('video'); - } - static Future close() async { // user.compact(); // user.close(); From b264427be603759271d954535cfb12bfbd05eb16 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 11 Feb 2024 23:07:44 +0800 Subject: [PATCH 070/102] =?UTF-8?q?fix:=20=E5=88=87=E6=8D=A2=E5=90=88?= =?UTF-8?q?=E9=9B=86=E8=AF=84=E8=AE=BA=E4=B8=8D=E5=88=B7=E6=96=B0=20issues?= =?UTF-8?q?=20#326=20#525?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 6 ++++-- lib/pages/video/detail/introduction/controller.dart | 1 + lib/pages/video/detail/reply/controller.dart | 1 + lib/pages/video/detail/reply/view.dart | 11 +++++++---- lib/pages/video/detail/view.dart | 7 +++++-- 5 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index f36eb589..0162654f 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -19,6 +19,7 @@ import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/video_utils.dart'; import 'package:screen_brightness/screen_brightness.dart'; +import '../../../utils/id_utils.dart'; import 'widgets/header_control.dart'; class VideoDetailController extends GetxController @@ -61,7 +62,7 @@ class VideoDetailController extends GetxController Box localCache = GStrorage.localCache; Box setting = GStrorage.setting; - int oid = 0; + RxInt oid = 0.obs; // 评论id 请求楼中楼评论使用 int fRpid = 0; @@ -135,13 +136,14 @@ class VideoDetailController extends GetxController defaultValue: VideoDecodeFormats.values.last.code); cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.hiRes.code); + oid.value = IdUtils.bv2av(Get.parameters['bvid']!); } showReplyReplyPanel() { PersistentBottomSheetController? ctr = scaffoldKey.currentState?.showBottomSheet((BuildContext context) { return VideoReplyReplyPanel( - oid: oid, + oid: oid.value, rpid: fRpid, closePanel: () => { fRpid = 0, diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index d1298fcc..fc92fb56 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -476,6 +476,7 @@ class VideoIntroController extends GetxController { final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); videoDetailCtr.bvid = bvid; + videoDetailCtr.oid.value = aid; videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.queryVideoUrl(); diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index bfcd0e80..e564ef02 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -59,6 +59,7 @@ class VideoReplyController extends GetxController { isLoadingMore = true; if (type == 'init') { currentPage = 0; + noMore.value = ''; } if (noMore.value == '没有更多了') { return; diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 9b7546af..8489cbb1 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -16,11 +16,13 @@ import 'widgets/reply_item.dart'; class VideoReplyPanel extends StatefulWidget { final String? bvid; + final int? oid; final int rpid; final String? replyLevel; const VideoReplyPanel({ this.bvid, + this.oid, this.rpid = 0, this.replyLevel, super.key, @@ -48,16 +50,17 @@ class _VideoReplyPanelState extends State @override void initState() { super.initState(); - int oid = widget.bvid != null ? IdUtils.bv2av(widget.bvid!) : 0; + // int oid = widget.bvid != null ? IdUtils.bv2av(widget.bvid!) : 0; heroTag = Get.arguments['heroTag']; replyLevel = widget.replyLevel ?? '1'; if (replyLevel == '2') { _videoReplyController = Get.put( - VideoReplyController(oid, widget.rpid.toString(), replyLevel), + VideoReplyController(widget.oid, widget.rpid.toString(), replyLevel), tag: widget.rpid.toString()); } else { - _videoReplyController = - Get.put(VideoReplyController(oid, '', replyLevel), tag: heroTag); + _videoReplyController = Get.put( + VideoReplyController(widget.oid, '', replyLevel), + tag: heroTag); } fabAnimationCtr = AnimationController( diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 3644fa41..14311cc9 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -570,8 +570,11 @@ class _VideoDetailPageState extends State ); }, ), - VideoReplyPanel( - bvid: videoDetailController.bvid, + Obx( + () => VideoReplyPanel( + bvid: videoDetailController.bvid, + oid: videoDetailController.oid.value, + ), ) ], ), From 89026e671c01b4b6d8fc686c44fa01b47c6d87dd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 12 Feb 2024 10:07:29 +0800 Subject: [PATCH 071/102] =?UTF-8?q?mod:=20=E6=94=B6=E8=97=8F=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E7=9B=B8=E5=85=B3=20issues=20#51=20#534?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 8 +- .../fav_detail/widget/fav_video_card.dart | 174 +++++++++--------- .../video/detail/introduction/controller.dart | 5 +- 3 files changed, 100 insertions(+), 87 deletions(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index d0faabaa..0df814be 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -51,7 +51,7 @@ class VideoHttp { (i['owner'] != null && !blackMidsList.contains(i['owner']['mid']))) { RecVideoItemModel videoItem = RecVideoItemModel.fromJson(i); - if (!RecommendFilter.filter(videoItem)){ + if (!RecommendFilter.filter(videoItem)) { list.add(videoItem); } } @@ -98,7 +98,7 @@ class VideoHttp { (i['args'] != null && !blackMidsList.contains(i['args']['up_mid']))) { RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i); - if (!RecommendFilter.filter(videoItem)){ + if (!RecommendFilter.filter(videoItem)) { list.add(videoItem); } } @@ -217,7 +217,7 @@ class VideoHttp { List list = []; for (var i in res.data['data']) { HotVideoItemModel videoItem = HotVideoItemModel.fromJson(i); - if (!RecommendFilter.filter(videoItem, relatedVideos: true)){ + if (!RecommendFilter.filter(videoItem, relatedVideos: true)) { list.add(videoItem); } } @@ -322,7 +322,7 @@ class VideoHttp { if (res.data['code'] == 0) { return {'status': true, 'data': res.data['data']}; } else { - return {'status': false, 'data': []}; + return {'status': false, 'data': [], 'msg': res.data['message']}; } } diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index a4f22f6b..a3c1e8e5 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -130,97 +130,107 @@ class VideoContent extends StatelessWidget { return Expanded( child: Padding( padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Stack( children: [ - Text( - videoItem.title, - textAlign: TextAlign.start, - style: const TextStyle( - fontWeight: FontWeight.w500, - letterSpacing: 0.3, - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - if (videoItem.ogv != null) ...[ - Text( - videoItem.intro, - style: TextStyle( - fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - ], - const Spacer(), - Text( - Utils.dateFormat(videoItem.favTime), - style: TextStyle( - fontSize: 11, color: Theme.of(context).colorScheme.outline), - ), - if (videoItem.owner.name != '') ...[ - Text( - videoItem.owner.name, - style: TextStyle( - fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - ], - Row( + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - StatView( - theme: 'gray', - view: videoItem.cntInfo['play'], + Text( + videoItem.title, + textAlign: TextAlign.start, + style: const TextStyle( + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), - const SizedBox(width: 8), - StatDanMu(theme: 'gray', danmu: videoItem.cntInfo['danmaku']), - const Spacer(), - SizedBox( - width: 26, - height: 26, - child: IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('要取消收藏吗?'), - actions: [ - TextButton( - onPressed: () => Get.back(), - child: Text( - '取消', - style: TextStyle( - color: Theme.of(context) - .colorScheme - .outline), - )), - TextButton( - onPressed: () async { - await callFn!(); - Get.back(); - }, - child: const Text('确定取消'), - ) - ], - ); - }, - ); - }, - icon: Icon( - Icons.clear_outlined, + if (videoItem.ogv != null) ...[ + Text( + videoItem.intro, + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, color: Theme.of(context).colorScheme.outline, - size: 18, ), ), + ], + const Spacer(), + Text( + Utils.dateFormat(videoItem.favTime), + style: TextStyle( + fontSize: 11, + color: Theme.of(context).colorScheme.outline), + ), + if (videoItem.owner.name != '') ...[ + Text( + videoItem.owner.name, + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + ], + Padding( + padding: const EdgeInsets.only(top: 2), + child: Row( + children: [ + StatView( + theme: 'gray', + view: videoItem.cntInfo['play'], + ), + const SizedBox(width: 8), + StatDanMu( + theme: 'gray', danmu: videoItem.cntInfo['danmaku']), + const Spacer(), + ], + ), ), ], ), + Positioned( + right: 0, + bottom: -4, + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('要取消收藏吗?'), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text( + '取消', + style: TextStyle( + color: + Theme.of(context).colorScheme.outline), + )), + TextButton( + onPressed: () async { + await callFn!(); + Get.back(); + }, + child: const Text('确定取消'), + ) + ], + ); + }, + ); + }, + icon: Icon( + Icons.clear_outlined, + color: Theme.of(context).colorScheme.outline, + size: 18, + ), + ), + ), ], ), ), diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index fc92fb56..4672b4bd 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -298,7 +298,6 @@ class VideoIntroController extends GetxController { await queryVideoInFolder(); int defaultFolderId = favFolderData.value.list!.first.id!; int favStatus = favFolderData.value.list!.first.favState!; - print('favStatus: $favStatus'); var result = await VideoHttp.favVideo( aid: IdUtils.bv2av(bvid), addIds: favStatus == 0 ? '$defaultFolderId' : '', @@ -310,6 +309,8 @@ class VideoIntroController extends GetxController { await queryHasFavVideo(); SmartDialog.showToast('✅ 操作成功'); } + } else { + SmartDialog.showToast(result['msg']); } return; } @@ -340,6 +341,8 @@ class VideoIntroController extends GetxController { await queryHasFavVideo(); SmartDialog.showToast('✅ 操作成功'); } + } else { + SmartDialog.showToast(result['msg']); } } From 5e59db85be3ccfd7a3ee9ad6cc50c97021f507e5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 12 Feb 2024 16:51:05 +0800 Subject: [PATCH 072/102] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E7=AC=94?= =?UTF-8?q?=E8=AE=B0=E8=B7=B3=E8=BD=AC=20issues=20#472?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/view.dart | 2 +- .../detail/reply/widgets/reply_item.dart | 55 +++++++++++++++++-- 2 files changed, 51 insertions(+), 6 deletions(-) diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 8489cbb1..df8c75b1 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -114,7 +114,7 @@ class _VideoReplyPanelState extends State final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); if (replyItem != null) { - videoDetailCtr.oid = replyItem.oid; + videoDetailCtr.oid.value = replyItem.oid; videoDetailCtr.fRpid = replyItem.rpid!; videoDetailCtr.firstFloor = replyItem; videoDetailCtr.showReplyReplyPanel(); diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 7991a366..a9e1044e 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -739,6 +739,47 @@ InlineSpan buildContent( }, ); + if (content.jumpUrl.keys.isNotEmpty) { + List unmatchedItems = content.jumpUrl.keys + .toList() + .where((item) => !content.message.contains(item)) + .toList(); + if (unmatchedItems.isNotEmpty) { + for (int i = 0; i < unmatchedItems.length; i++) { + String patternStr = unmatchedItems[i]; + spanChilds.addAll( + [ + if (content.jumpUrl[patternStr]?['prefix_icon'] != null) ...[ + WidgetSpan( + child: Image.network( + content.jumpUrl[patternStr]['prefix_icon'], + height: 19, + color: Theme.of(context).colorScheme.primary, + ), + ) + ], + TextSpan( + text: content.jumpUrl[patternStr]['title'], + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + Get.toNamed( + '/webview', + parameters: { + 'url': patternStr, + 'type': 'url', + 'pageTitle': content.jumpUrl[patternStr]['title'] + }, + ); + }, + ) + ], + ); + } + } + } // 图片渲染 if (content.pictures.isNotEmpty) { final List picList = []; @@ -753,11 +794,15 @@ InlineSpan buildContent( builder: (BuildContext context, BoxConstraints box) { double maxHeight = box.maxWidth * 0.6; // 设置最大高度 // double width = (box.maxWidth / 2).truncateToDouble(); - double height = ((box.maxWidth / - 2 * - pictureItem['img_height'] / - pictureItem['img_width'])) - .truncateToDouble(); + double height = 100; + try { + height = ((box.maxWidth / + 2 * + pictureItem['img_height'] / + pictureItem['img_width'])) + .truncateToDouble(); + } catch (_) {} + return GestureDetector( onTap: () { showDialog( From b817a0c80775c9ab5d7ca1521d98afc7e988f4da Mon Sep 17 00:00:00 2001 From: KoolShow <51787949+KoolShow@users.noreply.github.com> Date: Mon, 12 Feb 2024 17:20:18 +0800 Subject: [PATCH 073/102] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=AD=A3=E5=88=99?= =?UTF-8?q?=E8=A1=A8=E8=BE=BE=E5=BC=8F=E4=BB=A5=E5=8C=B9=E9=85=8D=E5=90=AB?= =?UTF-8?q?=E5=B0=8F=E6=97=B6=E7=9A=84=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 929d9c03..773ad3b1 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -591,7 +591,7 @@ InlineSpan buildContent( if (patternStr.isNotEmpty) { patternStr += "|"; } - patternStr += r'(\b\d{1,2}[::]\d{2}\b)'; + patternStr += r'(\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b)'; final RegExp pattern = RegExp(patternStr); List matchedStrs = []; void addPlainTextSpan(str) { @@ -639,7 +639,7 @@ InlineSpan buildContent( }, ), ); - } else if (RegExp(r'^\b[0-9]{1,2}[::][0-9]{2}\b$').hasMatch(matchStr)) { + } else if (RegExp(r'^\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b$').hasMatch(matchStr)) { spanChilds.add( TextSpan( text: ' $matchStr ', From cdf800c49f41731240ae9657c8dcb7fd88104c92 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 13 Feb 2024 23:33:51 +0800 Subject: [PATCH 074/102] =?UTF-8?q?mod:=20=E8=AF=84=E8=AE=BA=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E9=80=BB=E8=BE=91=20issues=20#420=20#331=20#297=20=20?= =?UTF-8?q?#152?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 259 ++++++++++-------- 1 file changed, 142 insertions(+), 117 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 46efc4f9..d0bcbaec 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,5 +1,6 @@ import 'package:flutter/gestures.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'; @@ -46,6 +47,17 @@ class ReplyItem extends StatelessWidget { replyReply!(replyItem); } }, + onLongPress: () { + feedBack(); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return MorePanel(item: replyItem); + }, + ); + }, child: Column( children: [ Padding( @@ -121,98 +133,6 @@ class ReplyItem extends StatelessWidget { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // 头像、昵称 - // SizedBox( - // width: double.infinity, - // child: Stack( - // children: [ - // GestureDetector( - // behavior: HitTestBehavior.opaque, - // onTap: () { - // feedBack(); - // Get.toNamed('/member?mid=${replyItem!.mid}', arguments: { - // 'face': replyItem!.member!.avatar!, - // 'heroTag': heroTag - // }); - // }, - // child: Row( - // crossAxisAlignment: CrossAxisAlignment.center, - // mainAxisSize: MainAxisSize.min, - // children: [ - // lfAvtar(context, heroTag), - // const SizedBox(width: 12), - // Text( - // replyItem!.member!.uname!, - // style: TextStyle( - // color: replyItem!.member!.vip!['vipStatus'] > 0 - // ? const Color.fromARGB(255, 251, 100, 163) - // : Theme.of(context).colorScheme.outline, - // fontSize: 13, - // ), - // ), - // const SizedBox(width: 6), - // Image.asset( - // 'assets/images/lv/lv${replyItem!.member!.level}.png', - // height: 11, - // ), - // const SizedBox(width: 6), - // if (replyItem!.isUp!) - // const PBadge( - // text: 'UP', - // size: 'small', - // stack: 'normal', - // fs: 9, - // ), - // ], - // ), - // ), - // Positioned( - // top: 0, - // left: 0, - // right: 0, - // child: Container( - // width: double.infinity, - // height: 45, - // decoration: BoxDecoration( - // image: replyItem!.member!.userSailing!.cardbg != null - // ? DecorationImage( - // alignment: Alignment.centerRight, - // fit: BoxFit.fitHeight, - // image: NetworkImage( - // replyItem!.member!.userSailing!.cardbg!['image'], - // ), - // ) - // : null, - // ), - // ), - // ), - // if (replyItem!.member!.userSailing!.cardbg != null && - // replyItem!.member!.userSailing!.cardbg!['fan']['number'] > 0) - // Positioned( - // top: 10, - // left: Get.size.width / 7 * 5.8, - // child: DefaultTextStyle( - // style: TextStyle( - // fontFamily: 'fansCard', - // fontSize: 9, - // color: Theme.of(context).colorScheme.primary, - // ), - // child: Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // const Text('NO.'), - // Text( - // replyItem!.member!.userSailing!.cardbg!['fan'] - // ['num_desc'], - // ), - // ], - // ), - // ), - // ), - // ], - // ), - // ), /// fix Stack内GestureDetector onTap无效 GestureDetector( behavior: HitTestBehavior.opaque, @@ -289,30 +209,26 @@ class ReplyItem extends StatelessWidget { // title Container( margin: const EdgeInsets.only(top: 10, left: 45, right: 6, bottom: 4), - child: SelectableRegion( - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Text.rich( - style: const TextStyle(height: 1.75), - maxLines: - replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999, - overflow: TextOverflow.ellipsis, - TextSpan( - children: [ - if (replyItem!.isTop!) - const WidgetSpan( - alignment: PlaceholderAlignment.top, - child: PBadge( - text: 'TOP', - size: 'small', - stack: 'normal', - type: 'line', - fs: 9, - ), + child: Text.rich( + style: const TextStyle(height: 1.75), + maxLines: + replyItem!.content!.isText! && replyLevel == '1' ? 3 : 999, + overflow: TextOverflow.ellipsis, + TextSpan( + children: [ + if (replyItem!.isTop!) + const WidgetSpan( + alignment: PlaceholderAlignment.top, + child: PBadge( + text: 'TOP', + size: 'small', + stack: 'normal', + type: 'line', + fs: 9, ), - buildContent(context, replyItem!, replyReply, null), - ], - ), + ), + buildContent(context, replyItem!, replyReply, null), + ], ), ), ), @@ -445,6 +361,17 @@ class ReplyItemRow extends StatelessWidget { InkWell( // 一楼点击评论展开评论详情 onTap: () => replyReply!(replyItem), + onLongPress: () { + feedBack(); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return MorePanel(item: replies![i]); + }, + ); + }, child: Container( width: double.infinity, padding: EdgeInsets.fromLTRB( @@ -636,8 +563,8 @@ InlineSpan buildContent( }, ), ); - - } else if (RegExp(r'^\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b$').hasMatch(matchStr)) { + } else if (RegExp(r'^\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b$') + .hasMatch(matchStr)) { matchStr = matchStr.replaceAll(':', ':'); spanChilds.add( TextSpan( @@ -955,3 +882,101 @@ InlineSpan buildContent( // spanChilds.add(TextSpan(text: matchMember)); return TextSpan(children: spanChilds); } + +class MorePanel extends StatelessWidget { + final dynamic item; + const MorePanel({super.key, required this.item}); + + Future menuActionHandler(String type) async { + String message = item.content.message ?? item.content; + switch (type) { + case 'copyAll': + await Clipboard.setData(ClipboardData(text: message)); + SmartDialog.showToast('已复制'); + Get.back(); + break; + case 'copyFreedom': + Get.back(); + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('自由复制'), + content: SelectableText(message), + ); + }, + ); + break; + // case 'block': + // SmartDialog.showToast('加入黑名单'); + // break; + // case 'report': + // SmartDialog.showToast('举报'); + // break; + // case 'delete': + // SmartDialog.showToast('删除'); + // break; + default: + } + } + + @override + Widget build(BuildContext context) { + Color errorColor = Theme.of(context).colorScheme.error; + return Container( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + // clipBehavior: Clip.hardEdge, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () => Get.back(), + child: Container( + height: 48, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.outline, + borderRadius: const BorderRadius.all(Radius.circular(3))), + ), + ), + ), + ), + ListTile( + onTap: () async => await menuActionHandler('copyAll'), + minLeadingWidth: 0, + leading: const Icon(Icons.copy_all_outlined), + title: const Text('复制全部'), + ), + ListTile( + onTap: () async => await menuActionHandler('copyFreedom'), + minLeadingWidth: 0, + leading: const Icon(Icons.copy_outlined), + title: const Text('自由复制'), + ), + // ListTile( + // onTap: () async => await menuActionHandler('block'), + // minLeadingWidth: 0, + // leading: Icon(Icons.block_outlined, color: errorColor), + // title: Text('加入黑名单', style: TextStyle(color: errorColor)), + // ), + // ListTile( + // onTap: () async => await menuActionHandler('report'), + // minLeadingWidth: 0, + // leading: Icon(Icons.report_outlined, color: errorColor), + // title: Text('举报', style: TextStyle(color: errorColor)), + // ), + // ListTile( + // onTap: () async => await menuActionHandler('del'), + // minLeadingWidth: 0, + // leading: Icon(Icons.delete_outline, color: errorColor), + // title: Text('删除', style: TextStyle(color: errorColor)), + // ), + ], + ), + ); + } +} From 42ad9591558df4bfd0cbe34575f60ce2cee04b12 Mon Sep 17 00:00:00 2001 From: orz12 Date: Wed, 14 Feb 2024 08:38:01 +0800 Subject: [PATCH 075/102] =?UTF-8?q?fix:=20=E9=80=9F=E5=BA=A6=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E6=97=A0=E6=B3=95=E5=8F=96=E6=B6=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/widgets/header_control.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 3af35efd..8bc7d84e 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -438,7 +438,7 @@ class _HeaderControlState extends State { }), actions: [ TextButton( - onPressed: () => SmartDialog.dismiss(), + onPressed: () => Get.back(), child: Text( '取消', style: TextStyle(color: Theme.of(context).colorScheme.outline), From 3efad736aeefb10f3047b9e8e988be0be34b1138 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 14 Feb 2024 19:38:55 +0800 Subject: [PATCH 076/102] =?UTF-8?q?fix:=20=E7=9B=B4=E6=92=AD=E9=97=AA?= =?UTF-8?q?=E9=80=80=20issues=20#540?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/controller.dart | 10 ---------- lib/pages/live_room/view.dart | 16 ++++++---------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 56da0a78..ee7b8214 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -3,7 +3,6 @@ import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/live.dart'; import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; - import '../../models/live/room_info_h5.dart'; class LiveRoomController extends GetxController { @@ -16,13 +15,6 @@ class LiveRoomController extends GetxController { RxBool volumeOff = false.obs; PlPlayerController plPlayerController = PlPlayerController.getInstance(videoType: 'live'); - - // MeeduPlayerController meeduPlayerController = MeeduPlayerController( - // colorTheme: Theme.of(Get.context!).colorScheme.primary, - // pipEnabled: true, - // controlsStyle: ControlsStyle.live, - // enabledButtons: const EnabledButtons(pip: true), - // ); Rx roomInfoH5 = RoomInfoH5Model().obs; @override @@ -39,8 +31,6 @@ class LiveRoomController extends GetxController { cover = liveItem.cover; } } - queryLiveInfo(); - queryLiveInfoH5(); } playerInit(source) async { diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 20dfe403..39800b90 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -29,22 +29,18 @@ class _LiveRoomPageState extends State { @override void initState() { super.initState(); - plPlayerController = _liveRoomController.plPlayerController; - plPlayerController!.onPlayerStatusChanged.listen( - (PlayerStatus status) { - if (status == PlayerStatus.playing) { - isShowCover = false; - setState(() {}); - } - }, - ); if (Platform.isAndroid) { floating = Floating(); } - _futureBuilder = _liveRoomController.queryLiveInfoH5(); + videoSourceInit(); _futureBuilderFuture = _liveRoomController.queryLiveInfo(); } + Future videoSourceInit() async { + _futureBuilder = _liveRoomController.queryLiveInfoH5(); + plPlayerController = _liveRoomController.plPlayerController; + } + @override void dispose() { plPlayerController!.dispose(); From d95fe9fe14465da7cce7faa36de973ce09d5d21a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 15 Feb 2024 21:07:23 +0800 Subject: [PATCH 077/102] =?UTF-8?q?mod:=20MorePanel=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/reply/widgets/reply_item.dart | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index d0bcbaec..dc242216 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -925,14 +925,13 @@ class MorePanel extends StatelessWidget { Color errorColor = Theme.of(context).colorScheme.error; return Container( padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), - // clipBehavior: Clip.hardEdge, child: Column( mainAxisSize: MainAxisSize.min, children: [ InkWell( onTap: () => Get.back(), child: Container( - height: 48, + height: 35, padding: const EdgeInsets.only(bottom: 2), child: Center( child: Container( @@ -948,14 +947,14 @@ class MorePanel extends StatelessWidget { ListTile( onTap: () async => await menuActionHandler('copyAll'), minLeadingWidth: 0, - leading: const Icon(Icons.copy_all_outlined), - title: const Text('复制全部'), + leading: const Icon(Icons.copy_all_outlined, size: 19), + title: Text('复制全部', style: Theme.of(context).textTheme.titleSmall), ), ListTile( onTap: () async => await menuActionHandler('copyFreedom'), minLeadingWidth: 0, - leading: const Icon(Icons.copy_outlined), - title: const Text('自由复制'), + leading: const Icon(Icons.copy_outlined, size: 19), + title: Text('自由复制', style: Theme.of(context).textTheme.titleSmall), ), // ListTile( // onTap: () async => await menuActionHandler('block'), From d57f84a1d7ca10b50e733f18a9a6a2e70a90cf94 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 15 Feb 2024 21:59:28 +0800 Subject: [PATCH 078/102] =?UTF-8?q?fix:=20=E8=B7=AF=E7=94=B1=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E4=BC=A0=E5=8F=82=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav_detail/view.dart | 6 ++++-- lib/pages/member/view.dart | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index f5897550..7f3435ef 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -24,11 +24,13 @@ class _FavDetailPageState extends State { Get.put(FavDetailController()); late StreamController titleStreamC; // a Future? _futureBuilderFuture; + late String mediaId; @override void initState() { super.initState(); _futureBuilderFuture = _favDetailController.queryUserFavFolderDetail(); + mediaId = Get.parameters['mediaId']!; titleStreamC = StreamController(); _controller.addListener( () { @@ -94,8 +96,8 @@ class _FavDetailPageState extends State { ), actions: [ IconButton( - onPressed: () => Get.toNamed( - '/favSearch?searchType=0&mediaId=${Get.parameters['mediaId']!}'), + onPressed: () => + Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'), icon: const Icon(Icons.search_outlined), ), // IconButton( diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 1d3cd5c4..0663e94e 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -105,7 +105,7 @@ class _MemberPageState extends State actions: [ IconButton( onPressed: () => Get.toNamed( - '/memberSearch?mid=${Get.parameters['mid']}&uname=${_memberController.memberInfo.value.name!}'), + '/memberSearch?mid=$mid&uname=${_memberController.memberInfo.value.name!}'), icon: const Icon(Icons.search_outlined), ), PopupMenuButton( From 5bf7b69d790e8fd4f42114c15a0c4a93b14501a0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 16 Feb 2024 09:33:59 +0800 Subject: [PATCH 079/102] =?UTF-8?q?feat:=20=E6=94=B6=E8=97=8F=E6=90=9C?= =?UTF-8?q?=E7=B4=A2=E7=BB=93=E6=9E=9C=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fav_detail/widget/fav_video_card.dart | 110 ++++++++++-------- lib/pages/fav_search/controller.dart | 20 ++++ lib/pages/fav_search/view.dart | 13 ++- 3 files changed, 92 insertions(+), 51 deletions(-) diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index a3c1e8e5..1c4008ff 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -15,9 +15,14 @@ import '../../../common/widgets/badge.dart'; class FavVideoCardH extends StatelessWidget { final dynamic videoItem; final Function? callFn; + final int? searchType; - const FavVideoCardH({Key? key, required this.videoItem, this.callFn}) - : super(key: key); + const FavVideoCardH({ + Key? key, + required this.videoItem, + this.callFn, + this.searchType, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -107,7 +112,11 @@ class FavVideoCardH extends StatelessWidget { }, ), ), - VideoContent(videoItem: videoItem, callFn: callFn) + VideoContent( + videoItem: videoItem, + callFn: callFn, + searchType: searchType, + ) ], ), ); @@ -123,7 +132,13 @@ class FavVideoCardH extends StatelessWidget { class VideoContent extends StatelessWidget { final dynamic videoItem; final Function? callFn; - const VideoContent({super.key, required this.videoItem, this.callFn}); + final int? searchType; + const VideoContent({ + super.key, + required this.videoItem, + this.callFn, + this.searchType, + }); @override Widget build(BuildContext context) { @@ -189,48 +204,51 @@ class VideoContent extends StatelessWidget { ), ], ), - Positioned( - right: 0, - bottom: -4, - child: IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - showDialog( - context: Get.context!, - builder: (context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('要取消收藏吗?'), - actions: [ - TextButton( - onPressed: () => Get.back(), - child: Text( - '取消', - style: TextStyle( - color: - Theme.of(context).colorScheme.outline), - )), - TextButton( - onPressed: () async { - await callFn!(); - Get.back(); - }, - child: const Text('确定取消'), - ) - ], - ); - }, - ); - }, - icon: Icon( - Icons.clear_outlined, - color: Theme.of(context).colorScheme.outline, - size: 18, - ), - ), - ), + searchType != 1 + ? Positioned( + right: 0, + bottom: -4, + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + showDialog( + context: Get.context!, + builder: (context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('要取消收藏吗?'), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text( + '取消', + style: TextStyle( + color: Theme.of(context) + .colorScheme + .outline), + )), + TextButton( + onPressed: () async { + await callFn!(); + Get.back(); + }, + child: const Text('确定取消'), + ) + ], + ); + }, + ); + }, + icon: Icon( + Icons.clear_outlined, + color: Theme.of(context).colorScheme.outline, + size: 18, + ), + ), + ) + : const SizedBox(), ], ), ), diff --git a/lib/pages/fav_search/controller.dart b/lib/pages/fav_search/controller.dart index 642fea6b..abd25e0b 100644 --- a/lib/pages/fav_search/controller.dart +++ b/lib/pages/fav_search/controller.dart @@ -1,8 +1,11 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/user/fav_detail.dart'; +import '../../http/video.dart'; + class FavSearchController extends GetxController { final ScrollController scrollController = ScrollController(); Rx controller = TextEditingController().obs; @@ -72,4 +75,21 @@ class FavSearchController extends GetxController { if (!hasMore) return; searchFav(type: 'onLoad'); } + + onCancelFav(int id) async { + var result = await VideoHttp.favVideo( + aid: id, addIds: '', delIds: mediaId.toString()); + if (result['status']) { + if (result['data']['prompt']) { + List dataList = favList; + for (var i in dataList) { + if (i.id == id) { + dataList.remove(i); + break; + } + } + SmartDialog.showToast('取消收藏'); + } + } + } } diff --git a/lib/pages/fav_search/view.dart b/lib/pages/fav_search/view.dart index 37e3046f..9b2ab15d 100644 --- a/lib/pages/fav_search/view.dart +++ b/lib/pages/fav_search/view.dart @@ -8,9 +8,7 @@ import 'package:pilipala/pages/fav_detail/widget/fav_video_card.dart'; import 'controller.dart'; class FavSearchPage extends StatefulWidget { - final int? sourceType; - final int? mediaId; - const FavSearchPage({super.key, this.sourceType, this.mediaId}); + const FavSearchPage({super.key}); @override State createState() => _FavSearchPageState(); @@ -19,11 +17,12 @@ class FavSearchPage extends StatefulWidget { class _FavSearchPageState extends State { final FavSearchController _favSearchCtr = Get.put(FavSearchController()); late ScrollController scrollController; + late int searchType; @override void initState() { super.initState(); - + searchType = int.parse(Get.parameters['searchType']!); scrollController = _favSearchCtr.scrollController; scrollController.addListener( () { @@ -100,7 +99,11 @@ class _FavSearchPageState extends State { } else { return FavVideoCardH( videoItem: _favSearchCtr.favList[index], - callFn: () => null, + searchType: searchType, + callFn: () => searchType != 1 + ? _favSearchCtr + .onCancelFav(_favSearchCtr.favList[index].id!) + : {}, ); } }, From 3d5c578fefcf581b83ec0aa6377dcddc1dcfe90e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 16 Feb 2024 11:00:23 +0800 Subject: [PATCH 080/102] =?UTF-8?q?mod:=20=E5=8A=A8=E6=80=81=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2upPanel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/up_panel.dart | 35 ++++++++++++++---------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index f1043fd8..84753ff9 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -36,8 +36,7 @@ class _UpPanelState extends State { } upList.insert( 0, - UpItem( - face: 'https://files.catbox.moe/8uc48f.png', uname: '全部动态', mid: -1), + UpItem(face: '', uname: '全部动态', mid: -1), ); userInfo = userInfoCache.get('userInfoCache'); upList.insert( @@ -56,7 +55,7 @@ class _UpPanelState extends State { floating: true, pinned: false, delegate: _SliverHeaderDelegate( - height: 124, + height: 126, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -178,6 +177,9 @@ class _UpPanelState extends State { }, onLongPress: () { feedBack(); + if (data.mid == -1) { + return; + } String heroTag = Utils.makeHeroTag(data.mid); Get.toNamed('/member?mid=${data.mid}', arguments: {'face': data.face, 'heroTag': heroTag}); @@ -205,12 +207,19 @@ class _UpPanelState extends State { backgroundColor: data.type == 'live' ? Theme.of(context).colorScheme.secondaryContainer : Theme.of(context).colorScheme.primary, - child: NetworkImgLayer( - width: 49, - height: 49, - src: data.face, - type: 'avatar', - ), + child: data.face != '' + ? NetworkImgLayer( + width: 50, + height: 50, + src: data.face, + type: 'avatar', + ) + : const CircleAvatar( + radius: 25, + backgroundImage: AssetImage( + 'assets/images/noface.jpeg', + ), + ), ), Padding( padding: const EdgeInsets.only(top: 4), @@ -278,13 +287,11 @@ class UpPanelSkeleton extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ Container( - width: 49, - height: 49, + width: 50, + height: 50, decoration: BoxDecoration( color: Theme.of(context).colorScheme.onInverseSurface, - borderRadius: const BorderRadius.all( - Radius.circular(24), - ), + borderRadius: BorderRadius.circular(50), ), ), Container( From 7a78729a4436416ef32aab0049a6ff79f79a4ed5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 16 Feb 2024 18:23:34 +0800 Subject: [PATCH 081/102] =?UTF-8?q?fix:=20=E5=90=88=E9=9B=86=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E6=8E=A8=E8=8D=90=E8=A7=86=E9=A2=91=E6=9C=AA=E5=88=B7?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/controller.dart | 5 ++ .../video/detail/related/controller.dart | 12 ++- lib/pages/video/detail/related/view.dart | 87 ++++++++++++------- 3 files changed, 71 insertions(+), 33 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 4672b4bd..723e1355 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -18,6 +18,7 @@ import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:share_plus/share_plus.dart'; +import '../related/index.dart'; import 'widgets/group_panel.dart'; class VideoIntroController extends GetxController { @@ -478,11 +479,15 @@ class VideoIntroController extends GetxController { // 重新获取视频资源 final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); + final ReleatedController releatedCtr = + Get.find(tag: heroTag); videoDetailCtr.bvid = bvid; videoDetailCtr.oid.value = aid; videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.queryVideoUrl(); + releatedCtr.bvid = bvid; + releatedCtr.queryRelatedVideo(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 diff --git a/lib/pages/video/detail/related/controller.dart b/lib/pages/video/detail/related/controller.dart index f7042871..0578bba2 100644 --- a/lib/pages/video/detail/related/controller.dart +++ b/lib/pages/video/detail/related/controller.dart @@ -1,14 +1,22 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/video.dart'; +import '../../../../models/model_hot_video_item.dart'; class ReleatedController extends GetxController { // 视频aid String bvid = Get.parameters['bvid'] ?? ""; // 推荐视频列表 - List relatedVideoList = []; + RxList relatedVideoList = [].obs; OverlayEntry? popupDialog; - Future queryRelatedVideo() => VideoHttp.relatedVideoList(bvid: bvid); + Future queryRelatedVideo() async { + return VideoHttp.relatedVideoList(bvid: bvid).then((value) { + if (value['status']) { + relatedVideoList.value = value['data']; + } + return value; + }); + } } diff --git a/lib/pages/video/detail/related/view.dart b/lib/pages/video/detail/related/view.dart index 51c296f3..0912724e 100644 --- a/lib/pages/video/detail/related/view.dart +++ b/lib/pages/video/detail/related/view.dart @@ -7,48 +7,73 @@ import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import './controller.dart'; -class RelatedVideoPanel extends StatelessWidget { - final ReleatedController _releatedController = - Get.put(ReleatedController(), tag: Get.arguments?['heroTag']); - RelatedVideoPanel({super.key}); +class RelatedVideoPanel extends StatefulWidget { + const RelatedVideoPanel({super.key}); + + @override + State createState() => _RelatedVideoPanelState(); +} + +class _RelatedVideoPanelState extends State + with AutomaticKeepAliveClientMixin { + late ReleatedController _releatedController; + late Future _futureBuilder; + + @override + bool get wantKeepAlive => true; + + @override + void initState() { + super.initState(); + _releatedController = + Get.put(ReleatedController(), tag: Get.arguments?['heroTag']); + _futureBuilder = _releatedController.queryRelatedVideo(); + } @override Widget build(BuildContext context) { + super.build(context); return FutureBuilder( - future: _releatedController.queryRelatedVideo(), + future: _futureBuilder, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.data == null) { return const SliverToBoxAdapter(child: SizedBox()); } - if (snapshot.data!['status']) { + if (snapshot.data!['status'] && snapshot.data != null) { + RxList relatedVideoList = _releatedController.relatedVideoList; // 请求成功 - return SliverList( + return Obx( + () => SliverList( delegate: SliverChildBuilderDelegate((context, index) { - if (index == snapshot.data['data'].length) { - return SizedBox(height: MediaQuery.of(context).padding.bottom); - } else { - return Material( - child: VideoCardH( - videoItem: snapshot.data['data'][index], - showPubdate: true, - longPress: () { - try { - _releatedController.popupDialog = - _createPopupDialog(snapshot.data['data'][index]); - Overlay.of(context) - .insert(_releatedController.popupDialog!); - } catch (err) { - return {}; - } - }, - longPressEnd: () { - _releatedController.popupDialog?.remove(); - }, - ), - ); - } - }, childCount: snapshot.data['data'].length + 1)); + if (index == relatedVideoList.length) { + return SizedBox( + height: MediaQuery.of(context).padding.bottom); + } else { + return Material( + child: VideoCardH( + videoItem: relatedVideoList[index], + showPubdate: true, + longPress: () { + try { + _releatedController.popupDialog = + _createPopupDialog(_releatedController + .relatedVideoList[index]); + Overlay.of(context) + .insert(_releatedController.popupDialog!); + } catch (err) { + return {}; + } + }, + longPressEnd: () { + _releatedController.popupDialog?.remove(); + }, + ), + ); + } + }, childCount: relatedVideoList.length + 1), + ), + ); } else { // 请求错误 return HttpError(errMsg: '出错了', fn: () {}); From 8950658f0818344c7306ac073bce00a2e5c96416 Mon Sep 17 00:00:00 2001 From: orz12 Date: Fri, 16 Feb 2024 19:52:21 +0800 Subject: [PATCH 082/102] =?UTF-8?q?mod:=20=E5=9B=BE=E7=89=87=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E9=A1=B5=EF=BC=8C=E5=AE=89=E5=8D=93=E4=B9=9F=E9=9A=90?= =?UTF-8?q?=E8=97=8F=E7=8A=B6=E6=80=81=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/preview/view.dart | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/pages/preview/view.dart b/lib/pages/preview/view.dart index 1c37c833..dcffc973 100644 --- a/lib/pages/preview/view.dart +++ b/lib/pages/preview/view.dart @@ -102,15 +102,12 @@ class _ImagePreviewState extends State ); } - // 设置状态栏图标透明 + // 隐藏状态栏,避免遮挡图片内容 setStatusBar() async { - if (Platform.isIOS) { + if (Platform.isIOS || Platform.isAndroid) { await StatusBarControl.setHidden(true, animation: StatusBarAnimation.SLIDE); } - if (Platform.isAndroid) { - await StatusBarControl.setColor(Colors.transparent); - } } @override From 3d2c6a122ac652712a6ee82f7f16234ad790c5d7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 16 Feb 2024 21:30:29 +0800 Subject: [PATCH 083/102] =?UTF-8?q?feat:=20=E5=85=85=E7=94=B5=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E8=AF=95=E7=9C=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/play/url.dart | 31 +++++++++++++++++++++++++- lib/pages/video/detail/controller.dart | 23 ++++++++++++++++--- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/lib/models/video/play/url.dart b/lib/models/video/play/url.dart index 4c43cb00..792cd50d 100644 --- a/lib/models/video/play/url.dart +++ b/lib/models/video/play/url.dart @@ -34,6 +34,7 @@ class PlayUrlModel { String? seekParam; String? seekType; Dash? dash; + List? durl; List? supportFormats; // String? highFormat; int? lastPlayTime; @@ -52,7 +53,8 @@ class PlayUrlModel { videoCodecid = json['video_codecid']; seekParam = json['seek_param']; seekType = json['seek_type']; - dash = Dash.fromJson(json['dash']); + dash = json['dash'] != null ? Dash.fromJson(json['dash']) : null; + durl = json['durl']?.map((e) => Durl.fromJson(e)).toList(); supportFormats = json['support_formats'] != null ? json['support_formats'] .map((e) => FormatItem.fromJson(e)) @@ -250,3 +252,30 @@ class Flac { audio = json['audio'] != null ? AudioItem.fromJson(json['audio']) : null; } } + +class Durl { + Durl({ + this.order, + this.length, + this.size, + this.ahead, + this.vhead, + this.url, + }); + + int? order; + int? length; + int? size; + String? ahead; + String? vhead; + String? url; + + Durl.fromJson(Map json) { + order = json['order']; + length = json['length']; + size = json['size']; + ahead = json['ahead']; + vhead = json['vhead']; + url = json['url']; + } +} diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 0162654f..d0cf53a6 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -229,9 +229,11 @@ class VideoDetailController extends GetxController seekTo: seekToTime ?? defaultST, duration: duration ?? Duration(milliseconds: data.timeLength ?? 0), // 宽>高 水平 否则 垂直 - direction: (firstVideo.width! - firstVideo.height!) > 0 - ? 'horizontal' - : 'vertical', + direction: firstVideo.width != null && firstVideo.height != null + ? ((firstVideo.width! - firstVideo.height!) > 0 + ? 'horizontal' + : 'vertical') + : null, bvid: bvid, cid: cid.value, enableHeart: enableHeart, @@ -248,6 +250,21 @@ class VideoDetailController extends GetxController var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid); if (result['status']) { data = result['data']; + if (data.acceptDesc!.isNotEmpty && data.acceptDesc!.contains('试看')) { + SmartDialog.showToast( + '该视频为专属视频,仅提供试看', + displayTime: const Duration(seconds: 3), + ); + videoUrl = data.durl!.first.url!; + audioUrl = ''; + defaultST = Duration.zero; + firstVideo = VideoItem(); + if (autoPlay.value) { + await playerInit(); + isShowCover.value = false; + } + return result; + } final List allVideosList = data.dash!.video!; try { // 当前可播放的最高质量视频 From 3c0f54bfd74b50f25d46466fceab768f7f604d96 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 16 Feb 2024 21:46:48 +0800 Subject: [PATCH 084/102] =?UTF-8?q?fix:=20app=E7=AB=AFmodel=20bvid=20null?= =?UTF-8?q?=20issues=20#546?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/home/rcmd/result.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 9363beb3..78747d1a 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -1,3 +1,5 @@ +import 'package:pilipala/utils/id_utils.dart'; + class RecVideoItemAppModel { RecVideoItemAppModel({ this.id, @@ -50,14 +52,15 @@ class RecVideoItemAppModel { ? json['player_args']['aid'] : int.parse(json['param'] ?? '-1'); aid = json['player_args'] != null ? json['player_args']['aid'] : -1; - bvid = null; + bvid = json['player_args'] != null + ? IdUtils.av2bv(json['player_args']['aid']) + : ''; cid = json['player_args'] != null ? json['player_args']['cid'] : -1; pic = json['cover']; stat = RcmdStat.fromJson(json); // 改用player_args中的duration作为原始数据(秒数) - duration = json['player_args'] != null - ? json['player_args']['duration'] - : -1; + duration = + json['player_args'] != null ? json['player_args']['duration'] : -1; //duration = json['cover_right_text']; title = json['title']; owner = RcmdOwner.fromJson(json); From e8f63f611441f7f7df2bc1c35cf8cc69f197f8a4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 17 Feb 2024 17:32:13 +0800 Subject: [PATCH 085/102] =?UTF-8?q?fix:=20up=E6=8A=95=E7=A8=BF=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E9=A1=B5=E5=A2=9E=E5=8A=A0=E6=9C=AA=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E9=A3=8E=E6=8E=A7=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/member.dart | 10 ++++++++-- lib/pages/member_archive/view.dart | 11 +++++++++-- lib/pages/member_dynamics/view.dart | 11 +++++++++-- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/lib/http/member.dart b/lib/http/member.dart index 6b6df7fe..82d2992b 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -101,10 +101,13 @@ class MemberHttp { 'data': MemberArchiveDataModel.fromJson(res.data['data']) }; } else { + Map errMap = { + -352: '风控校验失败,请检查登录状态', + }; return { 'status': false, 'data': [], - 'msg': res.data['message'], + 'msg': errMap[res.data['code']] ?? res.data['message'], }; } } @@ -123,10 +126,13 @@ class MemberHttp { 'data': DynamicsDataModel.fromJson(res.data['data']), }; } else { + Map errMap = { + -352: '风控校验失败,请检查登录状态', + }; return { 'status': false, 'data': [], - 'msg': res.data['message'], + 'msg': errMap[res.data['code']] ?? res.data['message'], }; } } diff --git a/lib/pages/member_archive/view.dart b/lib/pages/member_archive/view.dart index 43867323..3103683a 100644 --- a/lib/pages/member_archive/view.dart +++ b/lib/pages/member_archive/view.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/utils/utils.dart'; +import '../../common/widgets/http_error.dart'; import 'controller.dart'; class MemberArchivePage extends StatefulWidget { @@ -86,10 +87,16 @@ class _MemberArchivePageState extends State { : const SliverToBoxAdapter(), ); } else { - return const SliverToBoxAdapter(); + return HttpError( + errMsg: snapshot.data['msg'], + fn: () {}, + ); } } else { - return const SliverToBoxAdapter(); + return HttpError( + errMsg: snapshot.data['msg'], + fn: () {}, + ); } } else { return const SliverToBoxAdapter(); diff --git a/lib/pages/member_dynamics/view.dart b/lib/pages/member_dynamics/view.dart index fe7573fc..68aa72d7 100644 --- a/lib/pages/member_dynamics/view.dart +++ b/lib/pages/member_dynamics/view.dart @@ -4,6 +4,7 @@ import 'package:get/get.dart'; import 'package:pilipala/pages/member_dynamics/index.dart'; import 'package:pilipala/utils/utils.dart'; +import '../../common/widgets/http_error.dart'; import '../dynamics/widgets/dynamic_panel.dart'; class MemberDynamicsPage extends StatefulWidget { @@ -80,10 +81,16 @@ class _MemberDynamicsPageState extends State { : const SliverToBoxAdapter(), ); } else { - return const SliverToBoxAdapter(); + return HttpError( + errMsg: snapshot.data['msg'], + fn: () {}, + ); } } else { - return const SliverToBoxAdapter(); + return HttpError( + errMsg: snapshot.data['msg'], + fn: () {}, + ); } } else { return const SliverToBoxAdapter(); From 4811dc5ba5da887c69ea8b7304d162694c3237cb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 18 Feb 2024 08:11:11 +0800 Subject: [PATCH 086/102] fix: changeSeasonOrbangu aid null --- lib/pages/video/detail/introduction/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 723e1355..2c5e2dc5 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -482,7 +482,7 @@ class VideoIntroController extends GetxController { final ReleatedController releatedCtr = Get.find(tag: heroTag); videoDetailCtr.bvid = bvid; - videoDetailCtr.oid.value = aid; + videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid); videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.queryVideoUrl(); From 841d0f25f537c19aab3034cc8fa7b4afdc43038f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 18 Feb 2024 08:20:48 +0800 Subject: [PATCH 087/102] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BAjumpU?= =?UTF-8?q?rl=20BV=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index dc242216..5840b2bf 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -12,6 +12,7 @@ import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; @@ -620,26 +621,34 @@ InlineSpan buildContent( ..onTap = () async { final String title = content.jumpUrl[matchStr]['title']; if (appUrlSchema == '') { - final String redirectUrl = - await UrlUtils.parseRedirectUrl(matchStr); - final String pathSegment = Uri.parse(redirectUrl).path; - final String lastPathSegment = - pathSegment.split('/').last; - if (lastPathSegment.startsWith('BV')) { + if (matchStr.startsWith('BV')) { UrlUtils.matchUrlPush( - lastPathSegment, + matchStr, title, - redirectUrl, + '', ); } else { - Get.toNamed( - '/webview', - parameters: { - 'url': redirectUrl, - 'type': 'url', - 'pageTitle': title - }, - ); + final String redirectUrl = + await UrlUtils.parseRedirectUrl(matchStr); + final String pathSegment = Uri.parse(redirectUrl).path; + final String lastPathSegment = + pathSegment.split('/').last; + if (lastPathSegment.startsWith('BV')) { + UrlUtils.matchUrlPush( + lastPathSegment, + title, + redirectUrl, + ); + } else { + Get.toNamed( + '/webview', + parameters: { + 'url': redirectUrl, + 'type': 'url', + 'pageTitle': title + }, + ); + } } } else { if (appUrlSchema.startsWith('bilibili://search')) { From bcf94e287ae325011677ce26b9eb9926246201e9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 18 Feb 2024 23:44:21 +0800 Subject: [PATCH 088/102] =?UTF-8?q?mod:=20=E4=BF=AE=E6=94=B9=E6=94=B6?= =?UTF-8?q?=E8=97=8F=E8=A7=86=E9=A2=91=E5=93=8D=E5=BA=94=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bangumi/introduction/controller.dart | 14 +++++------- lib/pages/fav_detail/controller.dart | 14 +++++------- lib/pages/fav_search/controller.dart | 14 +++++------- .../video/detail/introduction/controller.dart | 22 ++++++++----------- 4 files changed, 27 insertions(+), 37 deletions(-) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 13dd50c0..6b3123ea 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -218,14 +218,12 @@ class BangumiIntroController extends GetxController { addIds: addMediaIdsNew.join(','), delIds: delMediaIdsNew.join(',')); if (result['status']) { - if (result['data']['prompt']) { - addMediaIdsNew = []; - delMediaIdsNew = []; - Get.back(); - // 重新获取收藏状态 - queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); - } + addMediaIdsNew = []; + delMediaIdsNew = []; + // 重新获取收藏状态 + queryHasFavVideo(); + SmartDialog.showToast('✅ 操作成功'); + Get.back(); } } diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index c2c63dd5..95130be6 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -60,16 +60,14 @@ class FavDetailController extends GetxController { var result = await VideoHttp.favVideo( aid: id, addIds: '', delIds: mediaId.toString()); if (result['status']) { - if (result['data']['prompt']) { - List dataList = favList; - for (var i in dataList) { - if (i.id == id) { - dataList.remove(i); - break; - } + List dataList = favList; + for (var i in dataList) { + if (i.id == id) { + dataList.remove(i); + break; } - SmartDialog.showToast('取消收藏'); } + SmartDialog.showToast('取消收藏'); } } diff --git a/lib/pages/fav_search/controller.dart b/lib/pages/fav_search/controller.dart index abd25e0b..371a3a07 100644 --- a/lib/pages/fav_search/controller.dart +++ b/lib/pages/fav_search/controller.dart @@ -80,16 +80,14 @@ class FavSearchController extends GetxController { var result = await VideoHttp.favVideo( aid: id, addIds: '', delIds: mediaId.toString()); if (result['status']) { - if (result['data']['prompt']) { - List dataList = favList; - for (var i in dataList) { - if (i.id == id) { - dataList.remove(i); - break; - } + List dataList = favList; + for (var i in dataList) { + if (i.id == id) { + dataList.remove(i); + break; } - SmartDialog.showToast('取消收藏'); } + SmartDialog.showToast('取消收藏'); } } } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 2c5e2dc5..26cbd2e0 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -305,11 +305,9 @@ class VideoIntroController extends GetxController { delIds: favStatus == 1 ? '$defaultFolderId' : '', ); if (result['status']) { - if (result['data']['prompt']) { - // 重新获取收藏状态 - await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); - } + // 重新获取收藏状态 + await queryHasFavVideo(); + SmartDialog.showToast('✅ 操作成功'); } else { SmartDialog.showToast(result['msg']); } @@ -334,14 +332,12 @@ class VideoIntroController extends GetxController { delIds: delMediaIdsNew.join(',')); SmartDialog.dismiss(); if (result['status']) { - if (result['data']['prompt']) { - addMediaIdsNew = []; - delMediaIdsNew = []; - Get.back(); - // 重新获取收藏状态 - await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); - } + addMediaIdsNew = []; + delMediaIdsNew = []; + Get.back(); + // 重新获取收藏状态 + await queryHasFavVideo(); + SmartDialog.showToast('✅ 操作成功'); } else { SmartDialog.showToast(result['msg']); } From fce96d497689b18959995baddcbe1a45eccbfcf0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 20 Feb 2024 23:54:45 +0800 Subject: [PATCH 089/102] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E8=AF=9D?= =?UTF-8?q?=E9=A2=98=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/reply/content.dart | 3 +++ .../detail/reply/widgets/reply_item.dart | 20 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/lib/models/video/reply/content.dart b/lib/models/video/reply/content.dart index 9180ec97..d62a4bca 100644 --- a/lib/models/video/reply/content.dart +++ b/lib/models/video/reply/content.dart @@ -9,6 +9,7 @@ class ReplyContent { this.vote, this.richText, this.isText, + this.topicsMeta, }); String? message; @@ -20,6 +21,7 @@ class ReplyContent { Map? vote; Map? richText; bool? isText; + Map? topicsMeta; ReplyContent.fromJson(Map json) { message = json['message'] @@ -39,6 +41,7 @@ class ReplyContent { richText = json['rich_text'] ?? {}; // 不包含@ 笔记 图片的时候,文字可折叠 isText = atNameToMid!.isEmpty && vote!.isEmpty && pictures!.isEmpty; + topicsMeta = json['topics_meta'] ?? {}; } } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 7991a366..d0b2b0e5 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -582,6 +582,7 @@ InlineSpan buildContent( // 构建正则表达式 final List specialTokens = [ ...content.emote.keys, + ...content.topicsMeta?.keys?.map((e) => '#$e#') ?? [], ...content.atNameToMid.keys.map((e) => '@$e'), ...content.jumpUrl.keys.map((e) => e.replaceAll('?', '\\?').replaceAll('+', '\\+').replaceAll('*', '\\*')), @@ -664,7 +665,7 @@ InlineSpan buildContent( ), ); } else { - // print("matchStr=$matchStr"); + print("matchStr=$matchStr"); String appUrlSchema = ''; final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, defaultValue: false) as bool; @@ -727,6 +728,23 @@ InlineSpan buildContent( ); // 只显示一次 matchedStrs.add(matchStr); + } else if (content + .topicsMeta[matchStr.substring(1, matchStr.length - 1)] != + null) { + spanChilds.add( + TextSpan( + text: matchStr, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + final String topic = + matchStr.substring(1, matchStr.length - 1); + Get.toNamed('/searchResult', parameters: {'keyword': topic}); + }, + ), + ); } else { addPlainTextSpan(matchStr); } From a0f92df5b558cfb002c9c8142282bd4eae2835d6 Mon Sep 17 00:00:00 2001 From: My-Responsitories <107370289+My-Responsitories@users.noreply.github.com> Date: Wed, 21 Feb 2024 13:16:38 +0800 Subject: [PATCH 090/102] fix dynamic risk challenge --- lib/http/api.dart | 6 ++++++ lib/http/init.dart | 42 ++++++++++++++++++++++++++++++++++++------ lib/http/member.dart | 8 +++++++- lib/utils/utils.dart | 15 ++++++++++++++- 4 files changed, 63 insertions(+), 8 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 4b6bbeb8..2e758439 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -477,4 +477,10 @@ class Api { /// 获取未读动态数 static const getUnreadDynamic = '/x/web-interface/dynamic/entrance'; + + /// 用户动态主页 + static const dynamicSpmPrefix = 'https://space.bilibili.com/1/dynamic'; + + /// 激活buvid3 + static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi'; } diff --git a/lib/http/init.dart b/lib/http/init.dart index dcc9f402..a0b36369 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -1,7 +1,9 @@ // ignore_for_file: avoid_print import 'dart:async'; +import 'dart:convert'; import 'dart:developer'; import 'dart:io'; +import 'dart:math' show Random; import 'package:cookie_jar/cookie_jar.dart'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; @@ -11,6 +13,7 @@ import 'package:hive/hive.dart'; import 'package:pilipala/utils/id_utils.dart'; import '../utils/storage.dart'; import '../utils/utils.dart'; +import 'api.dart'; import 'constants.dart'; import 'interceptor.dart'; @@ -24,6 +27,7 @@ class Request { late bool enableSystemProxy; late String systemProxyHost; late String systemProxyPort; + static final RegExp spmPrefixExp = RegExp(r''); /// 设置cookie static setCookie() async { @@ -51,13 +55,12 @@ class Request { } setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null); - if (cookie.isEmpty) { - try { - await Request().get(HttpString.baseUrl); - } catch (e) { - log("setCookie, ${e.toString()}"); - } + try { + await buvidActivate(); + } catch (e) { + log("setCookie, ${e.toString()}"); } + final String cookieString = cookie .map((Cookie cookie) => '${cookie.name}=${cookie.value}') .join('; '); @@ -87,6 +90,33 @@ class Request { dio.options.headers['referer'] = 'https://www.bilibili.com/'; } + static Future buvidActivate() async { + var html = await Request().get(Api.dynamicSpmPrefix); + String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!; + Random rand = Random(); + String rand_png_end = base64.encode( + List.generate(32, (_) => rand.nextInt(256)) + + List.filled(4, 0) + + [73, 69, 78, 68] + + List.generate(4, (_) => rand.nextInt(256)) + ); + + String jsonData = json.encode({ + '3064': 1, + '39c8': '${spmPrefix}.fp.risk', + '3c43': { + 'adca': 'Linux', + 'bfe9': rand_png_end.substring(rand_png_end.length - 50), + }, + }); + + await Request().post( + Api.activateBuvidApi, + data: {'payload': jsonData}, + options: Options(contentType: 'application/json') + ); + } + /* * config it and create */ diff --git a/lib/http/member.dart b/lib/http/member.dart index 82d2992b..1af0f9a4 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -79,6 +79,8 @@ class MemberHttp { String order = 'pubdate', bool orderAvoided = true, }) async { + String dmImgStr = Utils.base64EncodeRandomString(16, 64); + String dmCoverImgStr = Utils.base64EncodeRandomString(32, 128); Map params = await WbiSign().makSign({ 'mid': mid, 'ps': ps, @@ -88,7 +90,11 @@ class MemberHttp { 'order': order, 'platform': 'web', 'web_location': 1550101, - 'order_avoided': orderAvoided + 'order_avoided': orderAvoided, + 'dm_img_list': '[]', + 'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2), + 'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2), + 'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}', }); var res = await Request().get( Api.memberArchive, diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index d68ac51b..09d8c97f 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -16,6 +16,8 @@ import '../http/index.dart'; import '../models/github/latest.dart'; class Utils { + static final Random random = Random(); + static Future getCookiePath() async { final Directory tempDir = await getApplicationSupportDirectory(); final String tempPath = "${tempDir.path}/.plpl/"; @@ -180,7 +182,7 @@ class Utils { } static String makeHeroTag(v) { - return v.toString() + Random().nextInt(9999).toString(); + return v.toString() + random.nextInt(9999).toString(); } static int duration(String duration) { @@ -340,4 +342,15 @@ class Utils { return md5String; } + + static List generateRandomBytes(int minLength, int maxLength) { + return List.generate( + random.nextInt(maxLength-minLength+1), (_) => random.nextInt(0x60) + 0x20 + ); + } + + static String base64EncodeRandomString(int minLength, int maxLength) { + List randomBytes = generateRandomBytes(minLength, maxLength); + return base64.encode(randomBytes); + } } From 740d5f1ddd2d52f48ae3b3dfd5ee60ad59951b7a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 21 Feb 2024 23:27:32 +0800 Subject: [PATCH 091/102] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E7=82=B9=E5=87=BB=E4=B8=BB=E9=A1=B5=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E5=8D=A1=E6=AD=BB=20issues=20#562?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 14311cc9..3d315804 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -225,7 +225,10 @@ class _VideoDetailPageState extends State @override // 返回当前页面时 void didPopNext() async { - setState(() => isShowing = true); + if (plPlayerController != null && + plPlayerController!.videoPlayerController != null) { + setState(() => isShowing = true); + } videoDetailController.isFirstTime = false; final bool autoplay = autoPlayEnable; videoDetailController.playerInit(autoplay: autoplay); From 5746b85b275945ef1c88725a2891b70f7e8aae38 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 22 Feb 2024 00:17:38 +0800 Subject: [PATCH 092/102] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E9=81=AE=E6=8C=A1=20issues=20#347?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 13 +++++++++++-- lib/pages/video/detail/view.dart | 9 +++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index d0cf53a6..7465c6f2 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -90,6 +90,8 @@ class VideoDetailController extends GetxController late String cacheDecode; late int cacheAudioQa; + PersistentBottomSheetController? replyReplyBottomSheetCtr; + @override void onInit() { super.onInit(); @@ -140,7 +142,7 @@ class VideoDetailController extends GetxController } showReplyReplyPanel() { - PersistentBottomSheetController? ctr = + replyReplyBottomSheetCtr = scaffoldKey.currentState?.showBottomSheet((BuildContext context) { return VideoReplyReplyPanel( oid: oid.value, @@ -153,7 +155,7 @@ class VideoDetailController extends GetxController source: 'videoDetail', ); }); - ctr?.closed.then((value) { + replyReplyBottomSheetCtr?.closed.then((value) { fRpid = 0; }); } @@ -372,4 +374,11 @@ class VideoDetailController extends GetxController } return result; } + + // mob端全屏状态关闭二级回复 + hiddenReplyReplyPanel() { + replyReplyBottomSheetCtr != null + ? replyReplyBottomSheetCtr!.close() + : print('replyReplyBottomSheetCtr is null'); + } } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 3d315804..febca105 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -92,6 +92,7 @@ class _VideoDetailPageState extends State videoSourceInit(); appbarStreamListen(); lifecycleListener(); + fullScreenStatusListener(); } // 获取视频资源,初始化播放器 @@ -188,6 +189,14 @@ class _VideoDetailPageState extends State ); } + void fullScreenStatusListener() { + plPlayerController?.isFullScreen.listen((bool isFullScreen) { + if (isFullScreen) { + videoDetailController.hiddenReplyReplyPanel(); + } + }); + } + @override void dispose() { shutdownTimerService.handleWaitingFinished(); From 35dc94014c8346fd28a8c03bd0a97bbdd58b35cc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 23 Feb 2024 00:30:26 +0800 Subject: [PATCH 093/102] =?UTF-8?q?mod:=20=E7=9B=B4=E6=92=ADmcdn=E9=93=BE?= =?UTF-8?q?=E6=8E=A5=E6=9B=BF=E6=8D=A2=20issues=20#568?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/controller.dart | 13 ++++++++++--- lib/utils/video_utils.dart | 13 +++++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index ee7b8214..2bb1cd0a 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -4,6 +4,8 @@ import 'package:pilipala/http/live.dart'; import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import '../../models/live/room_info_h5.dart'; +import '../../utils/storage.dart'; +import '../../utils/video_utils.dart'; class LiveRoomController extends GetxController { String cover = ''; @@ -16,6 +18,7 @@ class LiveRoomController extends GetxController { PlPlayerController plPlayerController = PlPlayerController.getInstance(videoType: 'live'); Rx roomInfoH5 = RoomInfoH5Model().obs; + late bool enableCDN; @override void onInit() { @@ -31,6 +34,8 @@ class LiveRoomController extends GetxController { cover = liveItem.cover; } } + // CDN优化 + enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); } playerInit(source) async { @@ -57,9 +62,11 @@ class LiveRoomController extends GetxController { List codec = res['data'].playurlInfo.playurl.stream.first.format.first.codec; CodecItem item = codec.first; - String videoUrl = (item.urlInfo?.first.host)! + - item.baseUrl! + - item.urlInfo!.first.extra!; + String videoUrl = enableCDN + ? VideoUtils.getCdnUrl(item) + : (item.urlInfo?.first.host)! + + item.baseUrl! + + item.urlInfo!.first.extra!; await playerInit(videoUrl); return res; } diff --git a/lib/utils/video_utils.dart b/lib/utils/video_utils.dart index 88faba3c..a4bdd027 100644 --- a/lib/utils/video_utils.dart +++ b/lib/utils/video_utils.dart @@ -1,5 +1,7 @@ import 'package:pilipala/models/video/play/url.dart'; +import '../models/live/room_info.dart'; + class VideoUtils { static String getCdnUrl(dynamic item) { var backupUrl = ""; @@ -12,13 +14,20 @@ class VideoUtils { } else if (item is AudioItem) { backupUrl = item.backupUrl ?? ""; videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); + } else if (item is CodecItem) { + backupUrl = (item.urlInfo?.first.host)! + + item.baseUrl! + + item.urlInfo!.first.extra!; + videoUrl = backupUrl.contains("http") ? backupUrl : (item.baseUrl ?? ""); } else { return ""; } /// issues #70 - if (videoUrl.contains(".mcdn.bilivideo") || - videoUrl.contains("/upgcxcode/")) { + if (videoUrl.contains(".mcdn.bilivideo")) { + videoUrl = + 'https://proxy-tf-all-ws.bilivideo.com/?url=${Uri.encodeComponent(videoUrl)}'; + } else if (videoUrl.contains("/upgcxcode/")) { //CDN列表 var cdnList = { 'ali': 'upos-sz-mirrorali.bilivideo.com', From 02cc1646359ad032c0088874c7779009aa41613d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 23 Feb 2024 22:44:10 +0800 Subject: [PATCH 094/102] =?UTF-8?q?feat:=20=E9=A6=96=E9=A1=B5tabbar?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F=E8=AE=BE=E7=BD=AE=20issues=20#564?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/controller.dart | 3 + lib/pages/home/view.dart | 88 ++++++++++++++++++++-------- lib/pages/setting/style_setting.dart | 6 ++ lib/utils/storage.dart | 3 +- 4 files changed, 75 insertions(+), 25 deletions(-) diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index 685ff5c3..9f6f8ac5 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -26,6 +26,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { late List defaultTabs; late List tabbarSort; RxString defaultSearch = ''.obs; + late bool enableGradientBg; @override void onInit() { @@ -40,6 +41,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) { searchDefault(); } + enableGradientBg = + setting.get(SettingBoxKey.enableGradientBg, defaultValue: true); } void onRefresh() { diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index ce7b46c6..91d0ea8c 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -48,38 +48,48 @@ class _HomePageState extends State super.build(context); Brightness currentBrightness = MediaQuery.of(context).platformBrightness; // 设置状态栏图标的亮度 - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - statusBarIconBrightness: currentBrightness == Brightness.light - ? Brightness.dark - : Brightness.light, - )); + if (_homeController.enableGradientBg) { + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + statusBarIconBrightness: currentBrightness == Brightness.light + ? Brightness.dark + : Brightness.light, + )); + } return Scaffold( extendBody: true, extendBodyBehindAppBar: true, body: Stack( children: [ // gradient background - Align( - alignment: Alignment.topLeft, - child: Opacity( - opacity: 0.6, - child: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Theme.of(context).colorScheme.primary.withOpacity(0.9), - Theme.of(context).colorScheme.primary.withOpacity(0.5), - Theme.of(context).colorScheme.surface - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - stops: const [0, 0.0034, 0.34]), + if (_homeController.enableGradientBg) ...[ + Align( + alignment: Alignment.topLeft, + child: Opacity( + opacity: 0.6, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Theme.of(context) + .colorScheme + .primary + .withOpacity(0.9), + Theme.of(context) + .colorScheme + .primary + .withOpacity(0.5), + Theme.of(context).colorScheme.surface + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + stops: const [0, 0.0034, 0.34]), + ), ), ), ), - ), + ], Column( children: [ CustomAppBar( @@ -90,7 +100,37 @@ class _HomePageState extends State callback: showUserBottomSheet, ), if (_homeController.tabs.length > 1) ...[ - const CustomTabs(), + if (_homeController.enableGradientBg) ...[ + const CustomTabs(), + ] else ...[ + const SizedBox(height: 4), + SizedBox( + width: double.infinity, + height: 42, + child: Align( + alignment: Alignment.center, + child: TabBar( + controller: _homeController.tabController, + tabs: [ + for (var i in _homeController.tabs) + Tab(text: i['label']) + ], + isScrollable: true, + dividerColor: Colors.transparent, + enableFeedback: true, + splashBorderRadius: BorderRadius.circular(10), + tabAlignment: TabAlignment.center, + onTap: (value) { + feedBack(); + if (_homeController.initialIndex.value == value) { + _homeController.tabsCtrList[value]().animateToTop(); + } + _homeController.initialIndex.value = value; + }, + ), + ), + ), + ], ] else ...[ const SizedBox(height: 6), ], diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index fb3c780a..c9bffa69 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -102,6 +102,12 @@ class _StyleSettingState extends State { defaultVal: true, needReboot: true, ), + const SetSwitchItem( + title: '首页底栏背景渐变', + setKey: SettingBoxKey.enableGradientBg, + defaultVal: true, + needReboot: true, + ), ListTile( onTap: () async { int? result = await showDialog( diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 41ec0ce8..16cef463 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -135,7 +135,8 @@ class SettingBoxKey { hideSearchBar = 'hideSearchBar', // 收起顶栏 hideTabBar = 'hideTabBar', // 收起底栏 tabbarSort = 'tabbarSort', // 首页tabbar - dynamicBadgeMode = 'dynamicBadgeMode'; + dynamicBadgeMode = 'dynamicBadgeMode', + enableGradientBg = 'enableGradientBg'; } class LocalCacheKey { From b1a05c5c27bf30c2a1b502ee28dcd4ad0063cf8c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Feb 2024 01:38:27 +0800 Subject: [PATCH 095/102] =?UTF-8?q?mod:=20=E4=BF=AE=E6=94=B9=E5=85=B3?= =?UTF-8?q?=E4=BA=8E=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/about/index.dart | 145 +++++++++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 39 deletions(-) diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 0f159ce7..41ee1516 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -53,29 +53,54 @@ class _AboutPageState extends State { style: Theme.of(context).textTheme.titleMedium, ), const SizedBox(height: 6), - Text( - '使用Flutter开发的哔哩哔哩第三方客户端', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - const SizedBox(height: 20), Obx( - () => ListTile( - title: const Text('当前版本'), - trailing: Text(_aboutController.currentVersion.value, - style: subTitleStyle), - ), - ), - Obx( - () => ListTile( - onTap: () => _aboutController.onUpdate(), - title: const Text('最新版本'), - trailing: Text( - _aboutController.isLoading.value - ? '正在获取' - : _aboutController.isUpdate.value - ? '有新版本 ❤️${_aboutController.remoteVersion.value}' - : '当前已是最新版', - style: subTitleStyle, + () => Badge( + isLabelVisible: _aboutController.isLoading.value + ? false + : _aboutController.isUpdate.value, + label: const Text('New'), + child: Padding( + padding: const EdgeInsets.fromLTRB(0, 0, 0, 30), + child: FilledButton.tonal( + onPressed: () { + showModalBottomSheet( + context: context, + builder: (context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + onTap: () => _aboutController.githubRelease(), + title: const Text('Github下载'), + ), + ListTile( + onTap: () => _aboutController.panDownload(), + title: const Text('网盘下载'), + ), + ListTile( + onTap: () => _aboutController.webSiteUrl(), + title: const Text('官网下载'), + ), + ListTile( + onTap: () => _aboutController.qimiao(), + title: const Text('奇妙应用'), + ), + SizedBox( + height: + MediaQuery.of(context).padding.bottom + + 20) + ], + ); + }, + ); + }, + child: Text( + 'V${_aboutController.currentVersion.value}', + style: subTitleStyle.copyWith( + color: Theme.of(context).primaryColor, + ), + ), + ), ), ), ), @@ -87,14 +112,9 @@ class _AboutPageState extends State { // size: 16, // ), // ), - Divider( - thickness: 1, - height: 30, - color: Theme.of(context).colorScheme.outlineVariant, - ), ListTile( onTap: () => _aboutController.githubUrl(), - title: const Text('Github'), + title: const Text('开源地址'), trailing: Text( 'github.com/guozhigq/pilipala', style: subTitleStyle, @@ -129,19 +149,43 @@ class _AboutPageState extends State { ), ), ListTile( - onTap: () => _aboutController.qqChanel(), - title: const Text('QQ群'), + onTap: () { + showModalBottomSheet( + context: context, + builder: (context) { + return Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListTile( + onTap: () => _aboutController.qqChanel(), + title: const Text('QQ群'), + trailing: Text( + '616150809', + style: subTitleStyle, + ), + ), + ListTile( + onTap: () => _aboutController.tgChanel(), + title: const Text('TG频道'), + trailing: Text( + 'https://t.me/+lm_oOVmF0RJiODk1', + style: subTitleStyle, + ), + ), + SizedBox( + height: MediaQuery.of(context).padding.bottom + 20) + ], + ); + }, + ); + }, + title: const Text('交流社区'), trailing: Icon( Icons.arrow_forward_ios, size: 16, color: outline, ), ), - ListTile( - onTap: () => _aboutController.tgChanel(), - title: const Text('TG频道'), - trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), - ), ListTile( onTap: () => _aboutController.aPay(), title: const Text('赞助'), @@ -163,6 +207,7 @@ class _AboutPageState extends State { subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle), trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), ), + SizedBox(height: MediaQuery.of(context).padding.bottom + 20) ], ), ), @@ -230,11 +275,26 @@ class AboutController extends GetxController { ); } + githubRelease() { + launchUrl( + Uri.parse('https://github.com/guozhigq/pilipala/release'), + mode: LaunchMode.externalApplication, + ); + } + // 从网盘下载 panDownload() { - launchUrl( - Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), - mode: LaunchMode.externalApplication, + Clipboard.setData( + const ClipboardData(text: 'pili'), + ); + SmartDialog.showToast( + '已复制提取码:pili', + displayTime: const Duration(milliseconds: 500), + ).then( + (value) => launchUrl( + Uri.parse('https://www.123pan.com/s/9sVqVv-flu0A.html'), + mode: LaunchMode.externalApplication, + ), ); } @@ -250,7 +310,7 @@ class AboutController extends GetxController { // qq频道 qqChanel() { Clipboard.setData( - const ClipboardData(text: '489981949'), + const ClipboardData(text: '616150809'), ); SmartDialog.showToast('已复制QQ群号'); } @@ -291,6 +351,13 @@ class AboutController extends GetxController { ); } + qimiao() { + launchUrl( + Uri.parse('https://www.magicalapk.com/home'), + mode: LaunchMode.externalApplication, + ); + } + // 日志 logs() { Get.toNamed('/logs'); From e2befb11ff5903524fa8e33ebe6fe0b8525a426d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Feb 2024 02:37:16 +0800 Subject: [PATCH 096/102] =?UTF-8?q?feat:=20=E6=A8=AA=E5=B1=8F=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E6=97=B6=E5=B1=95=E7=A4=BA=E8=A7=86=E9=A2=91=E6=A0=87?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/widgets/header_control.dart | 83 +++++++++++++++---- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 8bc7d84e..596c567b 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -19,6 +19,8 @@ import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/http/danmaku.dart'; import 'package:pilipala/services/shutdown_timer_service.dart'; +import '../../../../models/video_detail_res.dart'; +import '../introduction/index.dart'; class HeaderControl extends StatefulWidget implements PreferredSizeWidget { const HeaderControl({ @@ -48,11 +50,31 @@ class _HeaderControlState extends State { final Box videoStorage = GStrorage.video; late List speedsList; double buttonSpace = 8; + bool showTitle = false; + late String heroTag; + late VideoIntroController videoIntroController; + late VideoDetailData videoDetail; + @override void initState() { super.initState(); videoInfo = widget.videoDetailCtr!.data; speedsList = widget.controller!.speedsList; + fullScreenStatusListener(); + heroTag = Get.arguments['heroTag']; + videoIntroController = Get.put(VideoIntroController(), tag: heroTag); + } + + void fullScreenStatusListener() { + widget.videoDetailCtr!.plPlayerController.isFullScreen + .listen((bool isFullScreen) { + if (isFullScreen) { + showTitle = true; + } else { + showTitle = false; + } + setState(() {}); + }); } /// 设置面板 @@ -342,8 +364,7 @@ class _HeaderControlState extends State { }, dense: true, contentPadding: const EdgeInsets.only(), - title: - const Text("额外等待视频播放完毕", style: titleStyle), + title: const Text("额外等待视频播放完毕", style: titleStyle), trailing: Switch( // thumb color (round icon) activeColor: Theme.of(context).colorScheme.primary, @@ -891,7 +912,7 @@ class _HeaderControlState extends State { final DanmakuOption currentOption = danmakuController.option; final DanmakuOption updatedOption = - currentOption.copyWith(strokeWidth: val); + currentOption.copyWith(strokeWidth: val); danmakuController.updateOption(updatedOption); } catch (_) {} }, @@ -1047,6 +1068,8 @@ class _HeaderControlState extends State { color: Colors.white, fontSize: 12, ); + final bool isLandscape = + MediaQuery.of(context).orientation == Orientation.landscape; return AppBar( backgroundColor: Colors.transparent, foregroundColor: Colors.white, @@ -1081,21 +1104,47 @@ class _HeaderControlState extends State { }, ), SizedBox(width: buttonSpace), - ComBtn( - icon: const Icon( - FontAwesomeIcons.house, - size: 15, - color: Colors.white, + if (showTitle && isLandscape) ...[ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ConstrainedBox( + constraints: BoxConstraints(maxWidth: 200), + child: Text( + videoIntroController.videoDetail.value.title!, + style: const TextStyle( + color: Colors.white, + fontSize: 16, + ), + ), + ), + if (videoIntroController.isShowOnlineTotal) + Text( + '${videoIntroController.total.value}人正在看', + style: const TextStyle( + color: Colors.white, + fontSize: 12, + ), + ) + ], + ) + ] else ...[ + ComBtn( + icon: const Icon( + FontAwesomeIcons.house, + size: 15, + color: Colors.white, + ), + fuc: () async { + // 销毁播放器实例 + await widget.controller!.dispose(type: 'all'); + if (mounted) { + Navigator.popUntil( + context, (Route route) => route.isFirst); + } + }, ), - fuc: () async { - // 销毁播放器实例 - await widget.controller!.dispose(type: 'all'); - if (mounted) { - Navigator.popUntil( - context, (Route route) => route.isFirst); - } - }, - ), + ], const Spacer(), // ComBtn( // icon: const Icon( From 4da6667b81e088a670b9f6289d72fe938d58d688 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Feb 2024 17:37:14 +0800 Subject: [PATCH 097/102] =?UTF-8?q?mod:=20=E7=9B=B4=E6=92=AD=E9=97=B4?= =?UTF-8?q?=E8=83=8C=E6=99=AF=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/network_img_layer.dart | 24 ++++----- lib/pages/live_room/view.dart | 60 ++++++++++++----------- 2 files changed, 45 insertions(+), 39 deletions(-) diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 03adc166..52c56a8a 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -104,17 +104,19 @@ class NetworkImgLayer extends StatelessWidget { ? 0 : StyleString.imgRadius.x), ), - child: Center( - child: Image.asset( - type == 'avatar' - ? 'assets/images/noface.jpeg' - : 'assets/images/loading.png', - width: width, - height: height, - cacheWidth: width.cacheSize(context), - cacheHeight: height.cacheSize(context), - ), - ), + child: type == 'bg' + ? const SizedBox() + : Center( + child: Image.asset( + type == 'avatar' + ? 'assets/images/noface.jpeg' + : 'assets/images/loading.png', + width: width, + height: height, + cacheWidth: width.cacheSize(context), + cacheHeight: height.cacheSize(context), + ), + ), ); } } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 39800b90..1e5c29c5 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -75,41 +75,45 @@ class _LiveRoomPageState extends State { backgroundColor: Colors.black, body: Stack( children: [ - // Obx( - // () => Positioned.fill( - // child: Opacity( - // opacity: 0.8, - // child: _liveRoomController - // .roomInfoH5.value.roomInfo?.appBackground != - // '' && - // _liveRoomController - // .roomInfoH5.value.roomInfo?.appBackground != - // null - // ? NetworkImgLayer( - // width: Get.width, - // height: Get.height, - // src: _liveRoomController - // .roomInfoH5.value.roomInfo?.appBackground ?? - // '', - // ) - // : Image.asset( - // 'assets/images/live/default_bg.webp', - // width: Get.width, - // height: Get.height, - // ), - // ), - // ), - // ), - Positioned.fill( + Positioned( + left: 0, + right: 0, + bottom: 0, child: Opacity( opacity: 0.8, child: Image.asset( 'assets/images/live/default_bg.webp', - width: Get.width, - height: Get.height, + fit: BoxFit.cover, + // width: Get.width, + // height: Get.height, ), ), ), + Obx( + () => Positioned( + left: 0, + right: 0, + bottom: 0, + child: _liveRoomController + .roomInfoH5.value.roomInfo?.appBackground != + '' && + _liveRoomController + .roomInfoH5.value.roomInfo?.appBackground != + null + ? Opacity( + opacity: 0.8, + child: NetworkImgLayer( + width: Get.width, + height: Get.height, + type: 'bg', + src: _liveRoomController + .roomInfoH5.value.roomInfo?.appBackground ?? + '', + ), + ) + : const SizedBox(), + ), + ), Column( children: [ AppBar( From 078e4716b4de4acc93f4f963ebf24fc44e80147e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 25 Feb 2024 12:12:54 +0800 Subject: [PATCH 098/102] =?UTF-8?q?feat:=20=E6=88=91=E7=9A=84=E8=AE=A2?= =?UTF-8?q?=E9=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 6 + lib/http/user.dart | 44 +++ lib/models/user/sub_detail.dart | 123 +++++++++ lib/models/user/sub_folder.dart | 111 ++++++++ lib/pages/fav/controller.dart | 2 +- lib/pages/fav_detail/controller.dart | 2 +- lib/pages/fav_detail/view.dart | 1 - lib/pages/history/view.dart | 4 - lib/pages/media/controller.dart | 5 + lib/pages/subscription/controller.dart | 49 ++++ lib/pages/subscription/index.dart | 4 + lib/pages/subscription/view.dart | 85 ++++++ lib/pages/subscription/widgets/item.dart | 108 ++++++++ lib/pages/subscription_detail/controller.dart | 60 ++++ lib/pages/subscription_detail/index.dart | 4 + lib/pages/subscription_detail/view.dart | 257 ++++++++++++++++++ .../widget/sub_video_card.dart | 168 ++++++++++++ lib/router/app_pages.dart | 6 + 18 files changed, 1032 insertions(+), 7 deletions(-) create mode 100644 lib/models/user/sub_detail.dart create mode 100644 lib/models/user/sub_folder.dart create mode 100644 lib/pages/subscription/controller.dart create mode 100644 lib/pages/subscription/index.dart create mode 100644 lib/pages/subscription/view.dart create mode 100644 lib/pages/subscription/widgets/item.dart create mode 100644 lib/pages/subscription_detail/controller.dart create mode 100644 lib/pages/subscription_detail/index.dart create mode 100644 lib/pages/subscription_detail/view.dart create mode 100644 lib/pages/subscription_detail/widget/sub_video_card.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 2e758439..736030b2 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -483,4 +483,10 @@ class Api { /// 激活buvid3 static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi'; + + /// 我的订阅 + static const userSubFolder = '/x/v3/fav/folder/collected/list'; + + /// 我的订阅详情 + static const userSubFolderDetail = '/x/space/fav/season/list'; } diff --git a/lib/http/user.dart b/lib/http/user.dart index c1f86285..7d3def4e 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -6,6 +6,8 @@ import '../models/user/fav_folder.dart'; import '../models/user/history.dart'; import '../models/user/info.dart'; import '../models/user/stat.dart'; +import '../models/user/sub_detail.dart'; +import '../models/user/sub_folder.dart'; import 'api.dart'; import 'init.dart'; @@ -305,4 +307,46 @@ class UserHttp { return {'status': false, 'msg': res.data['message']}; } } + + // 我的订阅 + static Future userSubFolder({ + required int mid, + required int pn, + required int ps, + }) async { + var res = await Request().get(Api.userSubFolder, data: { + 'up_mid': mid, + 'ps': ps, + 'pn': pn, + 'platform': 'web', + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': SubFolderModelData.fromJson(res.data['data']) + }; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } + + static Future userSubFolderDetail({ + required int seasonId, + required int pn, + required int ps, + }) async { + var res = await Request().get(Api.userSubFolderDetail, data: { + 'season_id': seasonId, + 'ps': ps, + 'pn': pn, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': SubDetailModelData.fromJson(res.data['data']) + }; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/models/user/sub_detail.dart b/lib/models/user/sub_detail.dart new file mode 100644 index 00000000..a1e52e55 --- /dev/null +++ b/lib/models/user/sub_detail.dart @@ -0,0 +1,123 @@ +class SubDetailModelData { + DetailInfo? info; + List? medias; + + SubDetailModelData({this.info, this.medias}); + + SubDetailModelData.fromJson(Map json) { + info = DetailInfo.fromJson(json['info']); + if (json['medias'] != null) { + medias = []; + json['medias'].forEach((v) { + medias!.add(SubDetailMediaItem.fromJson(v)); + }); + } + } +} + +class SubDetailMediaItem { + int? id; + String? title; + String? cover; + String? pic; + int? duration; + int? pubtime; + String? bvid; + Map? upper; + Map? cntInfo; + int? enableVt; + String? vtDisplay; + + SubDetailMediaItem({ + this.id, + this.title, + this.cover, + this.pic, + this.duration, + this.pubtime, + this.bvid, + this.upper, + this.cntInfo, + this.enableVt, + this.vtDisplay, + }); + + SubDetailMediaItem.fromJson(Map json) { + id = json['id']; + title = json['title']; + cover = json['cover']; + pic = json['cover']; + duration = json['duration']; + pubtime = json['pubtime']; + bvid = json['bvid']; + upper = json['upper']; + cntInfo = json['cnt_info']; + enableVt = json['enable_vt']; + vtDisplay = json['vt_display']; + } + + Map toJson() { + final data = {}; + data['id'] = id; + data['title'] = title; + data['cover'] = cover; + data['duration'] = duration; + data['pubtime'] = pubtime; + data['bvid'] = bvid; + data['upper'] = upper; + data['cnt_info'] = cntInfo; + data['enable_vt'] = enableVt; + data['vt_display'] = vtDisplay; + return data; + } +} + +class DetailInfo { + int? id; + int? seasonType; + String? title; + String? cover; + Map? upper; + Map? cntInfo; + int? mediaCount; + String? intro; + int? enableVt; + + DetailInfo({ + this.id, + this.seasonType, + this.title, + this.cover, + this.upper, + this.cntInfo, + this.mediaCount, + this.intro, + this.enableVt, + }); + + DetailInfo.fromJson(Map json) { + id = json['id']; + seasonType = json['season_type']; + title = json['title']; + cover = json['cover']; + upper = json['upper']; + cntInfo = json['cnt_info']; + mediaCount = json['media_count']; + intro = json['intro']; + enableVt = json['enable_vt']; + } + + Map toJson() { + final data = {}; + data['id'] = id; + data['season_type'] = seasonType; + data['title'] = title; + data['cover'] = cover; + data['upper'] = upper; + data['cnt_info'] = cntInfo; + data['media_count'] = mediaCount; + data['intro'] = intro; + data['enable_vt'] = enableVt; + return data; + } +} diff --git a/lib/models/user/sub_folder.dart b/lib/models/user/sub_folder.dart new file mode 100644 index 00000000..d496a1cf --- /dev/null +++ b/lib/models/user/sub_folder.dart @@ -0,0 +1,111 @@ +class SubFolderModelData { + final int? count; + final List? list; + + SubFolderModelData({ + this.count, + this.list, + }); + + factory SubFolderModelData.fromJson(Map json) { + return SubFolderModelData( + count: json['count'], + list: json['list'] != null + ? (json['list'] as List) + .map((i) => SubFolderItemData.fromJson(i)) + .toList() + : null, + ); + } +} + +class SubFolderItemData { + final int? id; + final int? fid; + final int? mid; + final int? attr; + final String? title; + final String? cover; + final Upper? upper; + final int? coverType; + final String? intro; + final int? ctime; + final int? mtime; + final int? state; + final int? favState; + final int? mediaCount; + final int? viewCount; + final int? vt; + final int? playSwitch; + final int? type; + final String? link; + final String? bvid; + + SubFolderItemData({ + this.id, + this.fid, + this.mid, + this.attr, + this.title, + this.cover, + this.upper, + this.coverType, + this.intro, + this.ctime, + this.mtime, + this.state, + this.favState, + this.mediaCount, + this.viewCount, + this.vt, + this.playSwitch, + this.type, + this.link, + this.bvid, + }); + + factory SubFolderItemData.fromJson(Map json) { + return SubFolderItemData( + id: json['id'], + fid: json['fid'], + mid: json['mid'], + attr: json['attr'], + title: json['title'], + cover: json['cover'], + upper: json['upper'] != null ? Upper.fromJson(json['upper']) : null, + coverType: json['cover_type'], + intro: json['intro'], + ctime: json['ctime'], + mtime: json['mtime'], + state: json['state'], + favState: json['fav_state'], + mediaCount: json['media_count'], + viewCount: json['view_count'], + vt: json['vt'], + playSwitch: json['play_switch'], + type: json['type'], + link: json['link'], + bvid: json['bvid'], + ); + } +} + +class Upper { + final int? mid; + final String? name; + final String? face; + + Upper({ + this.mid, + this.name, + this.face, + }); + + factory Upper.fromJson(Map json) { + return Upper( + mid: json['mid'], + name: json['name'], + face: json['face'], + ); + } +} diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index c3f76186..a5f94525 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -24,7 +24,7 @@ class FavController extends GetxController { if (!hasMore.value) { return; } - var res = await await UserHttp.userfavFolder( + var res = await UserHttp.userfavFolder( pn: currentPage, ps: pageSize, mid: userInfo!.mid!, diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 95130be6..69cc939e 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -34,7 +34,7 @@ class FavDetailController extends GetxController { return; } isLoadingMore = true; - var res = await await UserHttp.userFavFolderDetail( + var res = await UserHttp.userFavFolderDetail( pn: currentPage, ps: 20, mediaId: mediaId!, diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 787fa96e..27d7182b 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -31,7 +31,6 @@ class _FavDetailPageState extends State { super.initState(); mediaId = Get.parameters['mediaId']!; _futureBuilderFuture = _favDetailController.queryUserFavFolderDetail(); - mediaId = Get.parameters['mediaId']!; titleStreamC = StreamController(); _controller.addListener( () { diff --git a/lib/pages/history/view.dart b/lib/pages/history/view.dart index d8fc60f0..92e1eee7 100644 --- a/lib/pages/history/view.dart +++ b/lib/pages/history/view.dart @@ -70,10 +70,6 @@ class _HistoryPageState extends State { child1: AppBar( titleSpacing: 0, centerTitle: false, - leading: IconButton( - onPressed: () => Get.back(), - icon: const Icon(Icons.arrow_back_outlined), - ), title: Text( '观看记录', style: Theme.of(context).textTheme.titleMedium, diff --git a/lib/pages/media/controller.dart b/lib/pages/media/controller.dart index 8b875511..757d5ac9 100644 --- a/lib/pages/media/controller.dart +++ b/lib/pages/media/controller.dart @@ -28,6 +28,11 @@ class MediaController extends GetxController { 'title': '我的收藏', 'onTap': () => Get.toNamed('/fav'), }, + { + 'icon': Icons.subscriptions_outlined, + 'title': '我的订阅', + 'onTap': () => Get.toNamed('/subscription'), + }, { 'icon': Icons.watch_later_outlined, 'title': '稍后再看', diff --git a/lib/pages/subscription/controller.dart b/lib/pages/subscription/controller.dart new file mode 100644 index 00000000..bf0c593c --- /dev/null +++ b/lib/pages/subscription/controller.dart @@ -0,0 +1,49 @@ +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/user.dart'; +import 'package:pilipala/models/user/info.dart'; +import 'package:pilipala/utils/storage.dart'; + +import '../../models/user/sub_folder.dart'; + +class SubController extends GetxController { + final ScrollController scrollController = ScrollController(); + Rx subFolderData = SubFolderModelData().obs; + Box userInfoCache = GStrorage.userInfo; + UserInfoData? userInfo; + int currentPage = 1; + int pageSize = 20; + RxBool hasMore = true.obs; + + Future querySubFolder({type = 'init'}) async { + userInfo = userInfoCache.get('userInfoCache'); + if (userInfo == null) { + return {'status': false, 'msg': '账号未登录'}; + } + var res = await UserHttp.userSubFolder( + pn: currentPage, + ps: pageSize, + mid: userInfo!.mid!, + ); + if (res['status']) { + if (type == 'init') { + subFolderData.value = res['data']; + } else { + if (res['data'].list.isNotEmpty) { + subFolderData.value.list!.addAll(res['data'].list); + subFolderData.update((val) {}); + } + } + currentPage++; + } else { + SmartDialog.showToast(res['msg']); + } + return res; + } + + Future onLoad() async { + querySubFolder(type: 'onload'); + } +} diff --git a/lib/pages/subscription/index.dart b/lib/pages/subscription/index.dart new file mode 100644 index 00000000..4d034396 --- /dev/null +++ b/lib/pages/subscription/index.dart @@ -0,0 +1,4 @@ +library sub; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart new file mode 100644 index 00000000..b2a4965b --- /dev/null +++ b/lib/pages/subscription/view.dart @@ -0,0 +1,85 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/pages/fav/widgets/item.dart'; +import 'controller.dart'; +import 'widgets/item.dart'; + +class SubPage extends StatefulWidget { + const SubPage({super.key}); + + @override + State createState() => _SubPageState(); +} + +class _SubPageState extends State { + final SubController _subController = Get.put(SubController()); + late Future _futureBuilderFuture; + late ScrollController scrollController; + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _subController.querySubFolder(); + scrollController = _subController.scrollController; + scrollController.addListener( + () { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 300) { + EasyThrottle.throttle('history', const Duration(seconds: 1), () { + _subController.onLoad(); + }); + } + }, + ); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + centerTitle: false, + titleSpacing: 0, + title: Text( + '我的订阅', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + body: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + 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]); + }, + ), + ); + } else { + return CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + slivers: [ + HttpError( + errMsg: data?['msg'], + fn: () => setState(() {}), + ), + ], + ); + } + } else { + // 骨架屏 + return const Text('请求中'); + } + }, + ), + ); + } +} diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart new file mode 100644 index 00000000..fd08ffa5 --- /dev/null +++ b/lib/pages/subscription/widgets/item.dart @@ -0,0 +1,108 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/utils/utils.dart'; + +import '../../../models/user/sub_folder.dart'; + +class SubItem extends StatelessWidget { + final SubFolderItemData subFolderItem; + const SubItem({super.key, required this.subFolderItem}); + + @override + Widget build(BuildContext context) { + String heroTag = Utils.makeHeroTag(subFolderItem.id); + return InkWell( + onTap: () => Get.toNamed( + '/subDetail', + arguments: subFolderItem, + parameters: { + 'heroTag': heroTag, + 'seasonId': subFolderItem.id.toString(), + }, + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(12, 7, 12, 7), + child: LayoutBuilder( + builder: (context, boxConstraints) { + double width = + (boxConstraints.maxWidth - StyleString.cardSpace * 6) / 2; + return SizedBox( + height: width / StyleString.aspectRatio, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder( + builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Hero( + tag: heroTag, + child: NetworkImgLayer( + src: subFolderItem.cover, + width: maxWidth, + height: maxHeight, + ), + ); + }, + ), + ), + VideoContent(subFolderItem: subFolderItem) + ], + ), + ); + }, + ), + ), + ); + } +} + +class VideoContent extends StatelessWidget { + final SubFolderItemData subFolderItem; + const VideoContent({super.key, required this.subFolderItem}); + + @override + Widget build(BuildContext context) { + return Expanded( + child: Padding( + padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + subFolderItem.title!, + textAlign: TextAlign.start, + style: const TextStyle( + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), + ), + const SizedBox(height: 2), + Text( + '合集 UP主:${subFolderItem.upper!.name!}', + textAlign: TextAlign.start, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + const SizedBox(height: 2), + Text( + '${subFolderItem.mediaCount}个视频', + textAlign: TextAlign.start, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/subscription_detail/controller.dart b/lib/pages/subscription_detail/controller.dart new file mode 100644 index 00000000..6ecb894e --- /dev/null +++ b/lib/pages/subscription_detail/controller.dart @@ -0,0 +1,60 @@ +import 'package:get/get.dart'; +import 'package:pilipala/http/user.dart'; + +import '../../models/user/sub_detail.dart'; +import '../../models/user/sub_folder.dart'; + +class SubDetailController extends GetxController { + late SubFolderItemData item; + + late int seasonId; + late String heroTag; + int currentPage = 1; + bool isLoadingMore = false; + Rx subInfo = DetailInfo().obs; + RxList subList = [].obs; + RxString loadingText = '加载中...'.obs; + int mediaCount = 0; + + @override + void onInit() { + item = Get.arguments; + if (Get.parameters.keys.isNotEmpty) { + seasonId = int.parse(Get.parameters['seasonId']!); + heroTag = Get.parameters['heroTag']!; + } + super.onInit(); + } + + Future queryUserSubFolderDetail({type = 'init'}) async { + if (type == 'onLoad' && subList.length >= mediaCount) { + loadingText.value = '没有更多了'; + return; + } + isLoadingMore = true; + var res = await UserHttp.userSubFolderDetail( + seasonId: seasonId, + ps: 20, + pn: currentPage, + ); + if (res['status']) { + subInfo.value = res['data'].info; + if (currentPage == 1 && type == 'init') { + subList.value = res['data'].medias; + mediaCount = res['data'].info.mediaCount; + } else if (type == 'onLoad') { + subList.addAll(res['data'].medias); + } + if (subList.length >= mediaCount) { + loadingText.value = '没有更多了'; + } + } + currentPage += 1; + isLoadingMore = false; + return res; + } + + onLoad() { + queryUserSubFolderDetail(type: 'onLoad'); + } +} diff --git a/lib/pages/subscription_detail/index.dart b/lib/pages/subscription_detail/index.dart new file mode 100644 index 00000000..71df4b24 --- /dev/null +++ b/lib/pages/subscription_detail/index.dart @@ -0,0 +1,4 @@ +library sub_detail; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart new file mode 100644 index 00000000..d56125cd --- /dev/null +++ b/lib/pages/subscription_detail/view.dart @@ -0,0 +1,257 @@ +import 'dart:async'; + +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/video_card_h.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/common/widgets/no_data.dart'; + +import '../../models/user/sub_folder.dart'; +import '../../utils/utils.dart'; +import 'controller.dart'; +import 'widget/sub_video_card.dart'; + +class SubDetailPage extends StatefulWidget { + const SubDetailPage({super.key}); + + @override + State createState() => _SubDetailPageState(); +} + +class _SubDetailPageState extends State { + late final ScrollController _controller = ScrollController(); + final SubDetailController _subDetailController = + Get.put(SubDetailController()); + late StreamController titleStreamC; // a + late Future _futureBuilderFuture; + late String seasonId; + + @override + void initState() { + super.initState(); + seasonId = Get.parameters['seasonId']!; + _futureBuilderFuture = _subDetailController.queryUserSubFolderDetail(); + titleStreamC = StreamController(); + _controller.addListener( + () { + if (_controller.offset > 160) { + titleStreamC.add(true); + } else if (_controller.offset <= 160) { + titleStreamC.add(false); + } + + if (_controller.position.pixels >= + _controller.position.maxScrollExtent - 200) { + EasyThrottle.throttle('subDetail', const Duration(seconds: 1), () { + _subDetailController.onLoad(); + }); + } + }, + ); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: CustomScrollView( + controller: _controller, + slivers: [ + SliverAppBar( + expandedHeight: 260 - MediaQuery.of(context).padding.top, + pinned: true, + titleSpacing: 0, + title: StreamBuilder( + stream: titleStreamC.stream, + initialData: false, + builder: (context, AsyncSnapshot snapshot) { + return AnimatedOpacity( + opacity: snapshot.data ? 1 : 0, + curve: Curves.easeOut, + duration: const Duration(milliseconds: 500), + child: Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + _subDetailController.item.title!, + style: Theme.of(context).textTheme.titleMedium, + ), + Text( + '共${_subDetailController.item.mediaCount!}条视频', + style: Theme.of(context).textTheme.labelMedium, + ) + ], + ) + ], + ), + ); + }, + ), + flexibleSpace: FlexibleSpaceBar( + background: Container( + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor.withOpacity(0.2), + ), + ), + ), + padding: EdgeInsets.only( + top: kTextTabBarHeight + + MediaQuery.of(context).padding.top + + 30, + left: 20, + right: 20), + child: SizedBox( + height: 200, + child: Row( + // mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Hero( + tag: _subDetailController.heroTag, + child: NetworkImgLayer( + width: 180, + height: 110, + src: _subDetailController.item.cover, + ), + ), + const SizedBox(width: 14), + Expanded( + child: Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 4), + Text( + _subDetailController.item.title!, + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .titleMedium! + .fontSize, + fontWeight: FontWeight.bold), + ), + const SizedBox(height: 4), + GestureDetector( + onTap: () { + SubFolderItemData item = + _subDetailController.item; + Get.toNamed( + '/member?mid=${item.upper!.mid}', + arguments: { + 'face': item.upper!.face, + }, + ); + }, + child: Text( + _subDetailController.item.upper!.name!, + style: TextStyle( + color: + Theme.of(context).colorScheme.primary), + ), + ), + const SizedBox(height: 4), + Text( + '${Utils.numFormat(_subDetailController.item.viewCount)}次播放', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize, + color: Theme.of(context).colorScheme.outline), + ), + ], + ), + ), + ], + ), + ), + ), + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.only(top: 15, bottom: 8, left: 14), + child: Obx( + () => Text( + '共${_subDetailController.subList.length}条视频', + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + letterSpacing: 1), + ), + ), + ), + ), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data; + if (data['status']) { + if (_subDetailController.item.mediaCount == 0) { + return const NoData(); + } else { + List subList = _subDetailController.subList; + return Obx( + () => subList.isEmpty + ? const SliverToBoxAdapter(child: SizedBox()) + : SliverList( + delegate: + SliverChildBuilderDelegate((context, index) { + return SubVideoCardH( + videoItem: subList[index], + ); + }, childCount: subList.length), + ), + ); + } + } else { + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ); + } + } else { + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoCardHSkeleton(); + }, childCount: 10), + ); + } + }, + ), + SliverToBoxAdapter( + child: Container( + height: MediaQuery.of(context).padding.bottom + 60, + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom), + child: Center( + child: Obx( + () => Text( + _subDetailController.loadingText.value, + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: 13), + ), + ), + ), + ), + ) + ], + ), + ); + } +} diff --git a/lib/pages/subscription_detail/widget/sub_video_card.dart b/lib/pages/subscription_detail/widget/sub_video_card.dart new file mode 100644 index 00000000..11aebc39 --- /dev/null +++ b/lib/pages/subscription_detail/widget/sub_video_card.dart @@ -0,0 +1,168 @@ +import 'package:get/get.dart'; +import 'package:flutter/material.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/stat/danmu.dart'; +import 'package:pilipala/common/widgets/stat/view.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/utils/utils.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import '../../../common/widgets/badge.dart'; +import '../../../models/user/sub_detail.dart'; + +// 收藏视频卡片 - 水平布局 +class SubVideoCardH extends StatelessWidget { + final SubDetailMediaItem videoItem; + final int? searchType; + + const SubVideoCardH({ + Key? key, + required this.videoItem, + this.searchType, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + int id = videoItem.id!; + String bvid = videoItem.bvid!; + String heroTag = Utils.makeHeroTag(id); + return InkWell( + onTap: () async { + int cid = await SearchHttp.ab2c(bvid: bvid); + Map parameters = { + 'bvid': bvid, + 'cid': cid.toString(), + }; + + Get.toNamed('/video', parameters: parameters, arguments: { + 'videoItem': videoItem, + 'heroTag': heroTag, + 'videoType': SearchType.video, + }); + }, + child: Column( + children: [ + Padding( + padding: const EdgeInsets.fromLTRB( + StyleString.safeSpace, 5, StyleString.safeSpace, 5), + child: LayoutBuilder( + builder: (context, boxConstraints) { + double width = + (boxConstraints.maxWidth - StyleString.cardSpace * 6) / 2; + return SizedBox( + height: width / StyleString.aspectRatio, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder( + builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: videoItem.cover, + width: maxWidth, + height: maxHeight, + ), + ), + PBadge( + text: Utils.timeFormat(videoItem.duration!), + right: 6.0, + bottom: 6.0, + type: 'gray', + ), + // if (videoItem.ogv != null) ...[ + // PBadge( + // text: videoItem.ogv['type_name'], + // top: 6.0, + // right: 6.0, + // bottom: null, + // left: null, + // ), + // ], + ], + ); + }, + ), + ), + VideoContent( + videoItem: videoItem, + searchType: searchType, + ) + ], + ), + ); + }, + ), + ), + ], + ), + ); + } +} + +class VideoContent extends StatelessWidget { + final dynamic videoItem; + final int? searchType; + const VideoContent({ + super.key, + required this.videoItem, + this.searchType, + }); + + @override + Widget build(BuildContext context) { + return Expanded( + child: Padding( + padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), + child: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoItem.title, + textAlign: TextAlign.start, + style: const TextStyle( + fontWeight: FontWeight.w500, + letterSpacing: 0.3, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const Spacer(), + Text( + Utils.dateFormat(videoItem.pubtime), + style: TextStyle( + fontSize: 11, + color: Theme.of(context).colorScheme.outline), + ), + Padding( + padding: const EdgeInsets.only(top: 2), + child: Row( + children: [ + StatView( + theme: 'gray', + view: videoItem.cntInfo['play'], + ), + const SizedBox(width: 8), + StatDanMu( + theme: 'gray', danmu: videoItem.cntInfo['danmaku']), + const Spacer(), + ], + ), + ), + ], + ), + ], + ), + ), + ); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 45d7cad1..6ebaa638 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -44,6 +44,8 @@ import '../pages/setting/recommend_setting.dart'; import '../pages/setting/play_setting.dart'; import '../pages/setting/privacy_setting.dart'; import '../pages/setting/style_setting.dart'; +import '../pages/subscription/index.dart'; +import '../pages/subscription_detail/index.dart'; import '../pages/video/detail/index.dart'; import '../pages/video/detail/reply_reply/index.dart'; import '../pages/webview/index.dart'; @@ -160,6 +162,10 @@ class Routes { CustomGetPage(name: '/logs', page: () => const LogsPage()), // 搜索关注 CustomGetPage(name: '/followSearch', page: () => const FollowSearchPage()), + // 订阅 + CustomGetPage(name: '/subscription', page: () => const SubPage()), + // 订阅详情 + CustomGetPage(name: '/subDetail', page: () => const SubDetailPage()), ]; } From f8a8c0967a95a9eb1b349b5cef0941f4c777d18e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 25 Feb 2024 19:09:12 +0800 Subject: [PATCH 099/102] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=A1=A8=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/reply.dart | 20 +++ lib/models/video/reply/emote.dart | 120 ++++++++++++++++ lib/pages/emote/controller.dart | 20 +++ lib/pages/emote/index.dart | 4 + lib/pages/emote/view.dart | 116 ++++++++++++++++ .../detail/reply_new/toolbar_icon_button.dart | 40 ++++++ lib/pages/video/detail/reply_new/view.dart | 128 ++++++++++++++---- 8 files changed, 426 insertions(+), 25 deletions(-) create mode 100644 lib/models/video/reply/emote.dart create mode 100644 lib/pages/emote/controller.dart create mode 100644 lib/pages/emote/index.dart create mode 100644 lib/pages/emote/view.dart create mode 100644 lib/pages/video/detail/reply_new/toolbar_icon_button.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 736030b2..d812d76c 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -489,4 +489,7 @@ class Api { /// 我的订阅详情 static const userSubFolderDetail = '/x/space/fav/season/list'; + + /// 表情 + static const emojiList = '/x/emote/user/panel/web'; } diff --git a/lib/http/reply.dart b/lib/http/reply.dart index fab433fc..f080ed51 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -1,4 +1,5 @@ import '../models/video/reply/data.dart'; +import '../models/video/reply/emote.dart'; import 'api.dart'; import 'init.dart'; @@ -100,4 +101,23 @@ class ReplyHttp { }; } } + + static Future getEmoteList({String? business}) async { + var res = await Request().get(Api.emojiList, data: { + 'business': business ?? 'reply', + 'web_location': '333.1245', + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': EmoteModelData.fromJson(res.data['data']), + }; + } else { + return { + 'status': false, + 'date': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/models/video/reply/emote.dart b/lib/models/video/reply/emote.dart new file mode 100644 index 00000000..b4071826 --- /dev/null +++ b/lib/models/video/reply/emote.dart @@ -0,0 +1,120 @@ +class EmoteModelData { + final List? packages; + + EmoteModelData({ + required this.packages, + }); + + factory EmoteModelData.fromJson(Map jsonRes) { + final List? packages = + jsonRes['packages'] is List ? [] : null; + if (packages != null) { + for (final dynamic item in jsonRes['packages']!) { + if (item != null) { + try { + packages.add(PackageItem.fromJson(item)); + } catch (_) {} + } + } + } + return EmoteModelData( + packages: packages, + ); + } +} + +class PackageItem { + final int? id; + final String? text; + final String? url; + final int? mtime; + final int? type; + final int? attr; + final Meta? meta; + final List? emote; + + PackageItem({ + required this.id, + required this.text, + required this.url, + required this.mtime, + required this.type, + required this.attr, + required this.meta, + required this.emote, + }); + + factory PackageItem.fromJson(Map jsonRes) { + final List? emote = jsonRes['emote'] is List ? [] : null; + if (emote != null) { + for (final dynamic item in jsonRes['emote']!) { + if (item != null) { + try { + emote.add(Emote.fromJson(item)); + } catch (_) {} + } + } + } + return PackageItem( + id: jsonRes['id'], + text: jsonRes['text'], + url: jsonRes['url'], + mtime: jsonRes['mtime'], + type: jsonRes['type'], + attr: jsonRes['attr'], + meta: Meta.fromJson(jsonRes['meta']), + emote: emote, + ); + } +} + +class Meta { + final int? size; + final List? suggest; + + Meta({ + required this.size, + required this.suggest, + }); + + factory Meta.fromJson(Map jsonRes) => Meta( + size: jsonRes['size'], + suggest: jsonRes['suggest'] is List ? [] : null, + ); +} + +class Emote { + final int? id; + final int? packageId; + final String? text; + final String? url; + final int? mtime; + final int? type; + final int? attr; + final Meta? meta; + final dynamic activity; + + Emote({ + required this.id, + required this.packageId, + required this.text, + required this.url, + required this.mtime, + required this.type, + required this.attr, + required this.meta, + required this.activity, + }); + + factory Emote.fromJson(Map jsonRes) => Emote( + id: jsonRes['id'], + packageId: jsonRes['package_id'], + text: jsonRes['text'], + url: jsonRes['url'], + mtime: jsonRes['mtime'], + type: jsonRes['type'], + attr: jsonRes['attr'], + meta: Meta.fromJson(jsonRes['meta']), + activity: jsonRes['activity'], + ); +} diff --git a/lib/pages/emote/controller.dart b/lib/pages/emote/controller.dart new file mode 100644 index 00000000..c1a4c504 --- /dev/null +++ b/lib/pages/emote/controller.dart @@ -0,0 +1,20 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import '../../http/reply.dart'; +import '../../models/video/reply/emote.dart'; + +class EmotePanelController extends GetxController + with GetTickerProviderStateMixin { + late List emotePackage; + late TabController tabController; + + Future getEmote() async { + var res = await ReplyHttp.getEmoteList(business: 'reply'); + if (res['status']) { + emotePackage = res['data'].packages; + tabController = TabController(length: emotePackage.length, vsync: this); + } + return res; + } +} diff --git a/lib/pages/emote/index.dart b/lib/pages/emote/index.dart new file mode 100644 index 00000000..32ce53e3 --- /dev/null +++ b/lib/pages/emote/index.dart @@ -0,0 +1,4 @@ +library emote; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/emote/view.dart b/lib/pages/emote/view.dart new file mode 100644 index 00000000..d30767c3 --- /dev/null +++ b/lib/pages/emote/view.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import '../../models/video/reply/emote.dart'; +import 'controller.dart'; + +class EmotePanel extends StatefulWidget { + final Function onChoose; + const EmotePanel({super.key, required this.onChoose}); + + @override + State createState() => _EmotePanelState(); +} + +class _EmotePanelState extends State + with AutomaticKeepAliveClientMixin { + final EmotePanelController _emotePanelController = + Get.put(EmotePanelController()); + late Future _futureBuilderFuture; + + @override + bool get wantKeepAlive => true; + + @override + void initState() { + _futureBuilderFuture = _emotePanelController.getEmote(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (data['status']) { + List emotePackage = + _emotePanelController.emotePackage; + + return Column( + children: [ + Expanded( + child: TabBarView( + controller: _emotePanelController.tabController, + children: emotePackage.map( + (e) { + int size = e.emote!.first.meta!.size!; + int type = e.type!; + return Padding( + padding: const EdgeInsets.fromLTRB(12, 6, 12, 0), + child: GridView.builder( + gridDelegate: + SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: size == 1 ? 40 : 60, + crossAxisSpacing: 8, + mainAxisSpacing: 8, + ), + itemCount: e.emote!.length, + itemBuilder: (context, index) { + return Material( + color: Colors.transparent, + clipBehavior: Clip.hardEdge, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(4), + ), + child: InkWell( + onTap: () { + widget.onChoose(e, e.emote![index]); + }, + child: Padding( + padding: const EdgeInsets.all(3), + child: type == 4 + ? Text( + e.emote![index].text!, + overflow: TextOverflow.clip, + maxLines: 1, + ) + : Image.network( + e.emote![index].url!, + width: size * 38, + height: size * 38, + ), + ), + ), + ); + }, + ), + ); + }, + ).toList(), + )), + Divider( + height: 1, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + TabBar( + controller: _emotePanelController.tabController, + dividerColor: Colors.transparent, + isScrollable: true, + tabs: _emotePanelController.emotePackage + .map((e) => Tab(text: e.text)) + .toList(), + ), + SizedBox(height: MediaQuery.of(context).padding.bottom + 20), + ], + ); + } else { + return Center(child: Text(data['msg'])); + } + } else { + return const Center(child: Text('加载中...')); + } + }); + } +} diff --git a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart new file mode 100644 index 00000000..c4390796 --- /dev/null +++ b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart @@ -0,0 +1,40 @@ +import 'package:flutter/material.dart'; + +class ToolbarIconButton extends StatelessWidget { + final VoidCallback onPressed; + final Icon icon; + final String toolbarType; + final bool selected; + + const ToolbarIconButton({ + super.key, + required this.onPressed, + required this.icon, + required this.toolbarType, + required this.selected, + }); + + @override + Widget build(BuildContext context) { + return SizedBox( + width: 36, + height: 36, + child: IconButton( + onPressed: onPressed, + icon: icon, + highlightColor: Theme.of(context).colorScheme.secondaryContainer, + color: selected + ? Theme.of(context).colorScheme.onSecondaryContainer + : Theme.of(context).colorScheme.outline, + style: ButtonStyle( + 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 01c95adc..83201697 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -4,9 +4,13 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/reply_type.dart'; +import 'package:pilipala/models/video/reply/emote.dart'; import 'package:pilipala/models/video/reply/item.dart'; +import 'package:pilipala/pages/emote/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'toolbar_icon_button.dart'; + class VideoReplyNewDialog extends StatefulWidget { final int? oid; final int? root; @@ -32,6 +36,10 @@ class _VideoReplyNewDialogState extends State final TextEditingController _replyContentController = TextEditingController(); final FocusNode replyContentFocusNode = FocusNode(); final GlobalKey _formKey = GlobalKey(); + late double emoteHeight = 0.0; + double keyboardHeight = 0.0; // 键盘高度 + final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间 + String toolbarType = 'input'; @override void initState() { @@ -42,6 +50,8 @@ class _VideoReplyNewDialogState extends State WidgetsBinding.instance.addObserver(this); // 自动聚焦 _autoFocus(); + // 监听聚焦状态 + _focuslistener(); } _autoFocus() async { @@ -51,6 +61,16 @@ class _VideoReplyNewDialogState extends State } } + _focuslistener() { + replyContentFocusNode.addListener(() { + if (replyContentFocusNode.hasFocus) { + setState(() { + toolbarType = 'input'; + }); + } + }); + } + Future submitReplyAdd() async { feedBack(); String message = _replyContentController.text; @@ -73,18 +93,49 @@ class _VideoReplyNewDialogState extends State } } + void onChooseEmote(PackageItem package, Emote emote) { + final int cursorPosition = _replyContentController.selection.baseOffset; + 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 + void didChangeMetrics() { + super.didChangeMetrics(); + WidgetsBinding.instance.addPostFrameCallback((_) { + // 键盘高度 + final viewInsets = EdgeInsets.fromViewPadding( + View.of(context).viewInsets, View.of(context).devicePixelRatio); + _debouncer.run(() { + if (mounted) { + if (keyboardHeight == 0 && emoteHeight == 0) { + setState(() { + emoteHeight = keyboardHeight = + keyboardHeight == 0.0 ? viewInsets.bottom : keyboardHeight; + }); + } + } + }); + }); + } + @override void dispose() { WidgetsBinding.instance.removeObserver(this); _replyContentController.dispose(); + replyContentFocusNode.removeListener(() {}); super.dispose(); } @override Widget build(BuildContext context) { - double keyboardHeight = EdgeInsets.fromViewPadding( - View.of(context).viewInsets, View.of(context).devicePixelRatio) - .bottom; return Container( clipBehavior: Clip.hardEdge, decoration: BoxDecoration( @@ -137,27 +188,32 @@ class _VideoReplyNewDialogState extends State child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - SizedBox( - width: 36, - height: 36, - child: IconButton( - onPressed: () { - FocusScope.of(context) - .requestFocus(replyContentFocusNode); - }, - icon: Icon(Icons.keyboard, - size: 22, - color: Theme.of(context).colorScheme.onBackground), - highlightColor: - Theme.of(context).colorScheme.onInverseSurface, - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: - MaterialStateProperty.resolveWith((states) { - return Theme.of(context).highlightColor; - }), - ), - ), + ToolbarIconButton( + onPressed: () { + if (toolbarType == 'emote') { + setState(() { + toolbarType = 'input'; + }); + } + FocusScope.of(context).requestFocus(replyContentFocusNode); + }, + icon: const Icon(Icons.keyboard, size: 22), + toolbarType: toolbarType, + selected: toolbarType == 'input', + ), + const SizedBox(width: 20), + ToolbarIconButton( + onPressed: () { + if (toolbarType == 'input') { + setState(() { + toolbarType = 'emote'; + }); + } + FocusScope.of(context).unfocus(); + }, + icon: const Icon(Icons.emoji_emotions, size: 22), + toolbarType: toolbarType, + selected: toolbarType == 'emote', ), const Spacer(), TextButton( @@ -170,7 +226,10 @@ class _VideoReplyNewDialogState extends State duration: const Duration(milliseconds: 300), child: SizedBox( width: double.infinity, - height: keyboardHeight, + height: toolbarType == 'input' ? keyboardHeight : emoteHeight, + child: EmotePanel( + onChoose: (package, emote) => onChooseEmote(package, emote), + ), ), ), ], @@ -178,3 +237,22 @@ class _VideoReplyNewDialogState extends State ); } } + +typedef DebounceCallback = void Function(); + +class Debouncer { + DebounceCallback? callback; + final int? milliseconds; + Timer? _timer; + + Debouncer({this.milliseconds}); + + run(DebounceCallback callback) { + if (_timer != null) { + _timer!.cancel(); + } + _timer = Timer(Duration(milliseconds: milliseconds!), () { + callback(); + }); + } +} From bf071ea9e1b869ff69dd2fbb30cc5766f2066420 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 25 Feb 2024 19:34:24 +0800 Subject: [PATCH 100/102] =?UTF-8?q?feat:=20=E6=B6=88=E6=81=AF=E6=9C=AA?= =?UTF-8?q?=E8=AF=BB=E6=A0=87=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 ++ lib/http/msg.dart | 32 +++++++++++++++ lib/pages/whisper/view.dart | 50 ++++++++++++------------ lib/pages/whisper_detail/controller.dart | 25 +++++++++++- 4 files changed, 85 insertions(+), 26 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 736030b2..258b706b 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -489,4 +489,8 @@ class Api { /// 我的订阅详情 static const userSubFolderDetail = '/x/space/fav/season/list'; + + /// 已读标记 + static const String ackSessionMsg = + '${HttpString.tUrl}/session_svr/v1/session_svr/update_ack'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 70af5b55..be0bdd3a 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -86,4 +86,36 @@ class MsgHttp { }; } } + + // 消息标记已读 + static Future ackSessionMsg({ + int? talkerId, + int? ackSeqno, + }) async { + String csrf = await Request.getCsrf(); + Map params = await WbiSign().makSign({ + 'talker_id': talkerId, + 'session_type': 1, + 'ack_seqno': ackSeqno, + 'build': 0, + 'mobi_app': 'web', + 'csrf_token': csrf, + 'csrf': csrf + }); + var res = await Request().get(Api.ackSessionMsg, data: params); + 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']}", + }; + } + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index f2779a17..f54efa58 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -110,7 +110,7 @@ class _WhisperPageState extends State { if (snapshot.connectionState == ConnectionState.done) { Map data = snapshot.data as Map; if (data['status']) { - List sessionList = _whisperController.sessionList; + RxList sessionList = _whisperController.sessionList; return Obx( () => sessionList.isEmpty ? const SizedBox() @@ -121,33 +121,35 @@ class _WhisperPageState extends State { const NeverScrollableScrollPhysics(), itemBuilder: (_, int i) { return ListTile( - onTap: () => 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(), - }, - ), + 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: false, - backgroundColor: Theme.of(context) - .colorScheme - .primary, + isLabelVisible: + sessionList[i].unreadCount > 0, label: Text(sessionList[i] .unreadCount .toString()), - alignment: Alignment.bottomRight, + alignment: Alignment.topRight, child: NetworkImgLayer( width: 45, height: 45, diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 71dd4c03..6e854712 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -25,10 +25,31 @@ class WhisperDetailController extends GetxController { var res = await MsgHttp.sessionMsg(talkerId: talkerId); if (res['status']) { messageList.value = res['data'].messages; - if (messageList.isNotEmpty && res['data'].eInfos != null) { - eInfos = res['data'].eInfos; + if (messageList.isNotEmpty) { + ackSessionMsg(); + if (res['data'].eInfos != null) { + eInfos = res['data'].eInfos; + } } + } else { + SmartDialog.showToast(res['msg']); } return res; } + + // 消息标记已读 + Future ackSessionMsg() async { + if (messageList.isEmpty) { + return; + } + var res = await MsgHttp.ackSessionMsg( + talkerId: talkerId, + ackSeqno: messageList.last.msgSeqno, + ); + if (res['status']) { + SmartDialog.showToast("已读成功"); + } else { + SmartDialog.showToast(res['msg']); + } + } } From e2489ef0e3990a8065e73ecb6a97941811fd1eb7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 25 Feb 2024 22:48:02 +0800 Subject: [PATCH 101/102] =?UTF-8?q?feat:=20=E7=A7=81=E4=BF=A1=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E8=A1=A8=E6=83=85=E9=9D=A2=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 122 +++++++- lib/models/msg/session.dart | 18 +- lib/pages/whisper/view.dart | 40 ++- lib/pages/whisper_detail/controller.dart | 34 ++- lib/pages/whisper_detail/view.dart | 273 +++++++++++++----- .../whisper_detail/widget/chat_item.dart | 6 +- 7 files changed, 377 insertions(+), 119 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 516935d3..3a23ef80 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -496,4 +496,7 @@ class Api { /// 已读标记 static const String ackSessionMsg = '${HttpString.tUrl}/session_svr/v1/session_svr/update_ack'; + + /// 发送私信 + static const String sendMsg = '${HttpString.tUrl}/web_im/v1/web_im/send_msg'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index be0bdd3a..4ba2f818 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,3 +1,4 @@ +import 'dart:math'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; import '../utils/wbi_sign.dart'; @@ -22,10 +23,18 @@ class MsgHttp { Map signParams = await WbiSign().makSign(params); var res = await Request().get(Api.sessionList, data: signParams); if (res.data['code'] == 0) { - return { - 'status': true, - 'data': SessionDataModel.fromJson(res.data['data']), - }; + try { + return { + 'status': true, + 'data': SessionDataModel.fromJson(res.data['data']), + }; + } catch (err) { + return { + 'status': false, + 'date': [], + 'msg': err.toString(), + }; + } } else { return { 'status': false, @@ -42,12 +51,16 @@ class MsgHttp { 'mobi_app': 'web', }); if (res.data['code'] == 0) { - return { - 'status': true, - 'data': res.data['data'] - .map((e) => AccountListModel.fromJson(e)) - .toList(), - }; + try { + return { + 'status': true, + 'data': res.data['data'] + .map((e) => AccountListModel.fromJson(e)) + .toList(), + }; + } catch (err) { + print('err🔟: $err'); + } } else { return { 'status': false, @@ -118,4 +131,93 @@ class MsgHttp { }; } } + + // 发送私信 + static Future sendMsg({ + int? senderUid, + 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, + }); + 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']}", + }; + } + } + + static String getDevId() { + final List b = [ + '0', + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'A', + 'B', + 'C', + 'D', + 'E', + 'F' + ]; + final List s = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".split(''); + for (int i = 0; i < s.length; i++) { + if ('-' == s[i] || '4' == s[i]) { + continue; + } + final int randomInt = Random().nextInt(16); + if ('x' == s[i]) { + s[i] = b[randomInt]; + } else { + s[i] = b[3 & randomInt | 8]; + } + } + return s.join(); + } } diff --git a/lib/models/msg/session.dart b/lib/models/msg/session.dart index ea241249..b6c1b6a6 100644 --- a/lib/models/msg/session.dart +++ b/lib/models/msg/session.dart @@ -8,7 +8,7 @@ class SessionDataModel { this.hasMore, }); - List? sessionList; + List? sessionList; int? hasMore; SessionDataModel.fromJson(Map json) { @@ -121,35 +121,37 @@ class LastMsg { this.msgKey, this.msgStatus, this.notifyCode, - this.newFaceVersion, + // this.newFaceVersion, }); int? senderIid; int? receiverType; int? receiverId; int? msgType; - Map? content; + dynamic content; int? msgSeqno; int? timestamp; String? atUids; int? msgKey; int? msgStatus; String? notifyCode; - int? newFaceVersion; + // int? newFaceVersion; LastMsg.fromJson(Map json) { senderIid = json['sender_uid']; receiverType = json['receiver_type']; receiverId = json['receiver_id']; msgType = json['msg_type']; - content = jsonDecode(json['content']); + content = json['content'] != null && json['content'] != '' + ? jsonDecode(json['content']) + : ''; msgSeqno = json['msg_seqno']; timestamp = json['timestamp']; atUids = json['at_uids']; msgKey = json['msg_key']; msgStatus = json['msg_status']; notifyCode = json['notify_code']; - newFaceVersion = json['new_face_version']; + // newFaceVersion = json['new_face_version']; } } @@ -214,7 +216,9 @@ class MessageItem { receiverId = json['receiver_id']; // 1 文本 2 图片 18 系统提示 10 系统通知 5 撤回的消息 msgType = json['msg_type']; - content = jsonDecode(json['content']); + content = json['content'] != null && json['content'] != '' + ? jsonDecode(json['content']) + : ''; msgSeqno = json['msg_seqno']; timestamp = json['timestamp']; atUids = json['at_uids']; diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index f54efa58..f1c58650 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -108,8 +108,8 @@ class _WhisperPageState extends State { future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { RxList sessionList = _whisperController.sessionList; return Obx( () => sessionList.isEmpty @@ -162,20 +162,26 @@ class _WhisperPageState extends State { title: Text( sessionList[i].accountInfo.name), subtitle: Text( - sessionList[i] - .lastMsg - .content['text'] ?? - sessionList[i] - .lastMsg - .content['content'] ?? - sessionList[i] - .lastMsg - .content['title'] ?? - sessionList[i] + sessionList[i].lastMsg.content != + null && + sessionList[i] + .lastMsg + .content != + '' + ? (sessionList[i] .lastMsg - .content[ - 'reply_content'] ?? - '', + .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) @@ -212,7 +218,9 @@ class _WhisperPageState extends State { ); } else { // 请求错误 - return const SizedBox(); + return Center( + child: Text(data?['msg'] ?? '请求异常'), + ); } } else { // 骨架屏 diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 6e854712..6e950f81 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -1,7 +1,11 @@ +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 '../../utils/feed_back.dart'; +import '../../utils/storage.dart'; class WhisperDetailController extends GetxController { late int talkerId; @@ -11,6 +15,8 @@ class WhisperDetailController extends GetxController { RxList messageList = [].obs; //表情转换图片规则 List? eInfos; + final TextEditingController replyContentController = TextEditingController(); + Box userInfoCache = GStrorage.userInfo; @override void onInit() { @@ -42,14 +48,34 @@ class WhisperDetailController extends GetxController { if (messageList.isEmpty) { return; } - var res = await MsgHttp.ackSessionMsg( + await MsgHttp.ackSessionMsg( talkerId: talkerId, ackSeqno: messageList.last.msgSeqno, ); - if (res['status']) { - SmartDialog.showToast("已读成功"); + } + + Future sendMsg() async { + feedBack(); + String message = replyContentController.text; + final userInfo = userInfoCache.get('userInfoCache'); + if (userInfo == null) { + SmartDialog.showToast('请先登录'); + return; + } + if (message == '') { + SmartDialog.showToast('请输入内容'); + return; + } + var result = await MsgHttp.sendMsg( + senderUid: userInfo.mid, + receiverId: int.parse(mid), + content: {'content': message}, + msgType: 1, + ); + if (result['status']) { + SmartDialog.showToast('发送成功'); } else { - SmartDialog.showToast(res['msg']); + SmartDialog.showToast(result['msg']); } } } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 8d2297c4..e94b7d6d 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -1,9 +1,12 @@ +import 'dart:async'; 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/pages/emote/index.dart'; import 'package:pilipala/pages/whisper_detail/controller.dart'; import 'package:pilipala/utils/feed_back.dart'; - +import '../../utils/storage.dart'; import 'widget/chat_item.dart'; class WhisperDetailPage extends StatefulWidget { @@ -13,15 +16,63 @@ class WhisperDetailPage extends StatefulWidget { State createState() => _WhisperDetailPageState(); } -class _WhisperDetailPageState extends State { +class _WhisperDetailPageState extends State + with WidgetsBindingObserver { final WhisperDetailController _whisperDetailController = Get.put(WhisperDetailController()); late Future _futureBuilderFuture; + late TextEditingController _replyContentController; + final FocusNode replyContentFocusNode = FocusNode(); + final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间 + late double emoteHeight = 0.0; + double keyboardHeight = 0.0; // 键盘高度 + String toolbarType = 'input'; + Box userInfoCache = GStrorage.userInfo; @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); _futureBuilderFuture = _whisperDetailController.querySessionMsg(); + _replyContentController = _whisperDetailController.replyContentController; + _focuslistener(); + } + + _focuslistener() { + replyContentFocusNode.addListener(() { + if (replyContentFocusNode.hasFocus) { + setState(() { + toolbarType = 'input'; + }); + } + }); + } + + @override + void didChangeMetrics() { + super.didChangeMetrics(); + WidgetsBinding.instance.addPostFrameCallback((_) { + // 键盘高度 + final viewInsets = EdgeInsets.fromViewPadding( + View.of(context).viewInsets, View.of(context).devicePixelRatio); + _debouncer.run(() { + if (mounted) { + if (keyboardHeight == 0) { + setState(() { + emoteHeight = keyboardHeight = + keyboardHeight == 0.0 ? viewInsets.bottom : keyboardHeight; + }); + } + } + }); + }); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + replyContentFocusNode.removeListener(() {}); + super.dispose(); } @override @@ -89,55 +140,63 @@ class _WhisperDetailPageState extends State { ), ), ), - body: 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(); - } + 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, + height: MediaQuery.of(context).padding.bottom + 70 + keyboardHeight, padding: EdgeInsets.only( left: 8, right: 12, @@ -152,48 +211,102 @@ class _WhisperDetailPageState extends State { ), ), ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, + child: Column( children: [ - // IconButton( - // onPressed: () {}, - // icon: Icon( - // Icons.add_circle_outline, - // color: Theme.of(context).colorScheme.outline, - // ), - // ), - IconButton( - onPressed: () {}, - icon: Icon( - Icons.emoji_emotions_outlined, - color: Theme.of(context).colorScheme.outline, - ), - ), - 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, - decoration: const InputDecoration( - border: InputBorder.none, // 移除默认边框 - hintText: '开发中 ...', // 提示文本 - contentPadding: EdgeInsets.symmetric( - horizontal: 16.0, vertical: 12.0), // 内边距 + 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, ), ), + 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), // 内边距 + ), + ), + ), + ), + IconButton( + // onPressed: _whisperDetailController.sendMsg, + onPressed: null, + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.outline, + ), + ), + // const SizedBox(width: 16), + ], + ), + AnimatedSize( + curve: Curves.easeInOut, + duration: const Duration(milliseconds: 300), + child: SizedBox( + width: double.infinity, + height: toolbarType == 'input' ? keyboardHeight : emoteHeight, + child: EmotePanel( + onChoose: (package, emote) => {}, + ), ), ), - const SizedBox(width: 16), ], ), ), ); } } + +typedef DebounceCallback = void Function(); + +class Debouncer { + DebounceCallback? callback; + final int? milliseconds; + Timer? _timer; + + Debouncer({this.milliseconds}); + + run(DebounceCallback callback) { + if (_timer != null) { + _timer!.cancel(); + } + _timer = Timer(Duration(milliseconds: milliseconds!), () { + callback(); + }); + } +} diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 0925d569..4fd49254 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -204,7 +204,7 @@ class ChatItem extends StatelessWidget { 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', + (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', arguments: { 'pic': content['thumb'], 'heroTag': heroTag, @@ -352,7 +352,9 @@ class ChatItem extends StatelessWidget { )); default: return Text( - content['content'] ?? content.toString(), + content != null && content != '' + ? (content['content'] ?? content.toString()) + : '不支持的消息类型', style: TextStyle( letterSpacing: 0.6, height: 1.5, From 90c0256766cd2fc82849c5d0db160b3583ad8a8c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 26 Feb 2024 00:00:14 +0800 Subject: [PATCH 102/102] =?UTF-8?q?opt:=20=E5=9B=BE=E7=89=87=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD&=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/network_img_layer.dart | 7 +++++-- lib/pages/about/index.dart | 2 +- lib/pages/setting/extra_setting.dart | 10 +++++++--- lib/pages/setting/style_setting.dart | 3 +++ lib/pages/subscription/view.dart | 1 - lib/utils/global_data.dart | 12 ++++++++++++ lib/utils/storage.dart | 5 +++-- 7 files changed, 31 insertions(+), 9 deletions(-) create mode 100644 lib/utils/global_data.dart diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 52c56a8a..06c35974 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -2,6 +2,7 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/utils/extension.dart'; +import 'package:pilipala/utils/global_data.dart'; import '../../utils/storage.dart'; import '../constants.dart'; @@ -32,8 +33,10 @@ class NetworkImgLayer extends StatelessWidget { @override Widget build(BuildContext context) { + final int defaultImgQuality = GlobalData().imgQuality; final String imageUrl = - '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? 100}q.webp'; + '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp'; + print(imageUrl); int? memCacheWidth, memCacheHeight; double aspectRatio = (width / height).toDouble(); @@ -81,7 +84,7 @@ class NetworkImgLayer extends StatelessWidget { fadeOutDuration ?? const Duration(milliseconds: 120), fadeInDuration: fadeInDuration ?? const Duration(milliseconds: 120), - filterQuality: FilterQuality.high, + filterQuality: FilterQuality.low, errorWidget: (BuildContext context, String url, Object error) => placeholder(context), placeholder: (BuildContext context, String url) => diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 41ee1516..7c42f5aa 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -201,11 +201,11 @@ class _AboutPageState extends State { var cleanStatus = await CacheManage().clearCacheAll(); if (cleanStatus) { getCacheSize(); + SmartDialog.showToast('清除成功'); } }, title: const Text('清除缓存'), subtitle: Text('图片及网络缓存 $cacheSize', style: subTitleStyle), - trailing: Icon(Icons.arrow_forward_ios, size: 16, color: outline), ), SizedBox(height: MediaQuery.of(context).padding.bottom + 20) ], diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index 1580ad8b..0a4dd2bf 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -1,11 +1,13 @@ 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/models/common/dynamics_type.dart'; import 'package:pilipala/models/common/reply_sort_type.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; import 'package:pilipala/utils/storage.dart'; +import '../home/index.dart'; import 'widgets/switch_item.dart'; class ExtraSetting extends StatefulWidget { @@ -138,18 +140,20 @@ class _ExtraSettingState extends State { ), body: ListView( children: [ - SetSwitchItem( + const SetSwitchItem( title: '大家都在搜', subTitle: '是否展示「大家都在搜」', setKey: SettingBoxKey.enableHotKey, defaultVal: true, - callFn: (val) => {SmartDialog.showToast('下次启动时生效')}, ), - const SetSwitchItem( + SetSwitchItem( title: '搜索默认词', subTitle: '是否展示搜索框默认词', setKey: SettingBoxKey.enableSearchWord, defaultVal: true, + callFn: (val) { + Get.find().defaultSearch.value = ''; + }, ), const SetSwitchItem( title: '快速收藏', diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index c9bffa69..849123e5 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -8,6 +8,7 @@ import 'package:pilipala/models/common/theme_type.dart'; import 'package:pilipala/pages/setting/pages/color_select.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; import 'package:pilipala/pages/setting/widgets/slide_dialog.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import '../../models/common/dynamic_badge_mode.dart'; @@ -176,6 +177,8 @@ class _StyleSettingState extends State { SettingBoxKey.defaultPicQa, picQuality); Get.back(); settingController.picQuality.value = picQuality; + GlobalData().imgQuality = picQuality; + SmartDialog.showToast('设置成功'); }, child: const Text('确定'), ) diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index b2a4965b..1eee4a4f 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -2,7 +2,6 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/pages/fav/widgets/item.dart'; import 'controller.dart'; import 'widgets/item.dart'; diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart new file mode 100644 index 00000000..a8a04eba --- /dev/null +++ b/lib/utils/global_data.dart @@ -0,0 +1,12 @@ +class GlobalData { + int imgQuality = 10; + + // 私有构造函数 + GlobalData._(); + + // 单例实例 + static final GlobalData _instance = GlobalData._(); + + // 获取全局实例 + factory GlobalData() => _instance; +} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 16cef463..09e362fd 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -1,11 +1,10 @@ -// import 'package:hive/hive.dart'; import 'dart:io'; - import 'package:hive_flutter/hive_flutter.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pilipala/models/model_owner.dart'; import 'package:pilipala/models/search/hot.dart'; import 'package:pilipala/models/user/info.dart'; +import 'global_data.dart'; class GStrorage { static late final Box userInfo; @@ -44,6 +43,8 @@ class GStrorage { ); // 视频设置 video = await Hive.openBox('video'); + GlobalData().imgQuality = + setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); // 设置全局变量 } static void regAdapter() {