From e2befb11ff5903524fa8e33ebe6fe0b8525a426d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Feb 2024 02:37:16 +0800 Subject: [PATCH 01/67] =?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 02/67] =?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 03/67] =?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 04/67] =?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 05/67] =?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 06/67] =?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 07/67] =?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() { From 542975d0ecdff82de25137f4ae81a6b29282953f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 28 Feb 2024 00:34:46 +0800 Subject: [PATCH 08/67] =?UTF-8?q?feat:=20=E5=85=A8=E5=B1=8F=E6=89=8B?= =?UTF-8?q?=E5=8A=BF=E8=AE=BE=E7=BD=AE=20issues=20#517?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/gesture_mode.dart | 12 +++ lib/models/common/index.dart | 4 + lib/pages/setting/pages/play_gesture_set.dart | 88 +++++++++++++++++++ lib/pages/setting/play_setting.dart | 12 +-- lib/plugin/pl_player/view.dart | 14 ++- lib/router/app_pages.dart | 4 + lib/utils/global_data.dart | 4 + lib/utils/storage.dart | 5 ++ 8 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 lib/models/common/gesture_mode.dart create mode 100644 lib/models/common/index.dart create mode 100644 lib/pages/setting/pages/play_gesture_set.dart diff --git a/lib/models/common/gesture_mode.dart b/lib/models/common/gesture_mode.dart new file mode 100644 index 00000000..1149ae12 --- /dev/null +++ b/lib/models/common/gesture_mode.dart @@ -0,0 +1,12 @@ +enum FullScreenGestureMode { + /// 从上滑到下 + fromToptoBottom, + + /// 从下滑到上 + fromBottomtoTop, +} + +extension FullScreenGestureModeExtension on FullScreenGestureMode { + String get values => ['fromToptoBottom', 'fromBottomtoTop'][index]; + String get labels => ['从上往下滑进入全屏', '从下往上滑进入全屏'][index]; +} diff --git a/lib/models/common/index.dart b/lib/models/common/index.dart new file mode 100644 index 00000000..89a05076 --- /dev/null +++ b/lib/models/common/index.dart @@ -0,0 +1,4 @@ +library commonn_model; + +export './business_type.dart'; +export './gesture_mode.dart'; diff --git a/lib/pages/setting/pages/play_gesture_set.dart b/lib/pages/setting/pages/play_gesture_set.dart new file mode 100644 index 00000000..f688c43c --- /dev/null +++ b/lib/pages/setting/pages/play_gesture_set.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/utils/global_data.dart'; + +import '../../../models/common/gesture_mode.dart'; +import '../../../utils/storage.dart'; +import '../widgets/select_dialog.dart'; +import '../widgets/switch_item.dart'; + +class PlayGesturePage extends StatefulWidget { + const PlayGesturePage({super.key}); + + @override + State createState() => _PlayGesturePageState(); +} + +class _PlayGesturePageState extends State { + Box setting = GStrorage.setting; + late int fullScreenGestureMode; + + @override + void initState() { + super.initState(); + fullScreenGestureMode = setting.get(SettingBoxKey.fullScreenGestureMode, + defaultValue: FullScreenGestureMode.values.last.index); + } + + @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: [ + ListTile( + dense: false, + title: Text('全屏手势', style: titleStyle), + subtitle: Text( + '通过手势快速进入全屏', + style: subTitleStyle, + ), + onTap: () async { + String? result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '全屏手势', + value: FullScreenGestureMode + .values[fullScreenGestureMode].values, + values: FullScreenGestureMode.values.map((e) { + return {'title': e.labels, 'value': e.values}; + }).toList()); + }, + ); + if (result != null) { + GlobalData().fullScreenGestureMode = FullScreenGestureMode + .values + .firstWhere((element) => element.values == result); + fullScreenGestureMode = + GlobalData().fullScreenGestureMode.index; + setting.put( + SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode); + setState(() {}); + } + }, + ), + const SetSwitchItem( + title: '双击快退/快进', + subTitle: '左侧双击快退,右侧双击快进', + setKey: SettingBoxKey.enableQuickDouble, + defaultVal: true, + ), + ], + ), + ); + } +} diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index bfd5db5f..a2a3fef2 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -73,6 +73,12 @@ class _PlaySettingState extends State { title: Text('倍速设置', style: titleStyle), subtitle: Text('设置视频播放速度', style: subTitleStyle), ), + ListTile( + dense: false, + onTap: () => Get.toNamed('/playerGestureSet'), + title: Text('手势设置', style: titleStyle), + subtitle: Text('设置播放器手势', style: subTitleStyle), + ), const SetSwitchItem( title: '开启1080P', subTitle: '免登录查看1080P视频', @@ -134,12 +140,6 @@ class _PlaySettingState extends State { setKey: SettingBoxKey.enableAutoBrightness, defaultVal: false, ), - const SetSwitchItem( - title: '双击快退/快进', - subTitle: '左侧双击快退,右侧双击快进', - setKey: SettingBoxKey.enableQuickDouble, - defaultVal: true, - ), const SetSwitchItem( title: '弹幕开关', subTitle: '展示弹幕', diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index d073945b..9c5731ec 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -9,6 +9,7 @@ import 'package:hive/hive.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:nil/nil.dart'; +import 'package:pilipala/models/common/gesture_mode.dart'; import 'package:pilipala/plugin/pl_player/controller.dart'; import 'package:pilipala/plugin/pl_player/models/duration.dart'; import 'package:pilipala/plugin/pl_player/models/fullscreen_mode.dart'; @@ -17,6 +18,7 @@ import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:screen_brightness/screen_brightness.dart'; +import '../../utils/global_data.dart'; import 'models/bottom_progress_behavior.dart'; import 'widgets/app_bar_ani.dart'; import 'widgets/backward_seek.dart'; @@ -73,6 +75,8 @@ class _PLVideoPlayerState extends State late bool enableQuickDouble; late bool enableBackgroundPlay; late double screenWidth; + final FullScreenGestureMode fullScreenGestureMode = + GlobalData().fullScreenGestureMode; // 用于记录上一次全屏切换手势触发时间,避免误触 DateTime? lastFullScreenToggleTime; @@ -520,18 +524,20 @@ class _PLVideoPlayerState extends State // 全屏 final double dy = details.delta.dy; const double threshold = 7.0; // 滑动阈值 + final bool flag = + fullScreenGestureMode != FullScreenGestureMode.values.last; if (dy > _distance && dy > threshold) { - if (_.isFullScreen.value) { + if (_.isFullScreen.value ^ flag) { lastFullScreenToggleTime = DateTime.now(); // 下滑退出全屏 - await widget.controller.triggerFullScreen(status: false); + await widget.controller.triggerFullScreen(status: flag); } _distance = 0.0; } else if (dy < _distance && dy < -threshold) { - if (!_.isFullScreen.value) { + if (!_.isFullScreen.value ^ flag) { lastFullScreenToggleTime = DateTime.now(); // 上滑进入全屏 - await widget.controller.triggerFullScreen(); + await widget.controller.triggerFullScreen(status: !flag); } _distance = 0.0; } diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 6ebaa638..1f1ea31e 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -39,6 +39,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/home_tabbar_set.dart'; +import '../pages/setting/pages/play_gesture_set.dart'; import '../pages/setting/pages/play_speed_set.dart'; import '../pages/setting/recommend_setting.dart'; import '../pages/setting/play_setting.dart'; @@ -166,6 +167,9 @@ class Routes { CustomGetPage(name: '/subscription', page: () => const SubPage()), // 订阅详情 CustomGetPage(name: '/subDetail', page: () => const SubDetailPage()), + // 播放器手势 + CustomGetPage( + name: '/playerGestureSet', page: () => const PlayGesturePage()), ]; } diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart index a8a04eba..08157db4 100644 --- a/lib/utils/global_data.dart +++ b/lib/utils/global_data.dart @@ -1,5 +1,9 @@ +import '../models/common/index.dart'; + class GlobalData { int imgQuality = 10; + FullScreenGestureMode fullScreenGestureMode = + FullScreenGestureMode.values.last; // 私有构造函数 GlobalData._(); diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 09e362fd..d69ffc20 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -4,6 +4,7 @@ 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 '../models/common/gesture_mode.dart'; import 'global_data.dart'; class GStrorage { @@ -45,6 +46,9 @@ class GStrorage { video = await Hive.openBox('video'); GlobalData().imgQuality = setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); // 设置全局变量 + GlobalData().fullScreenGestureMode = FullScreenGestureMode.values[ + setting.get(SettingBoxKey.fullScreenGestureMode, + defaultValue: FullScreenGestureMode.values.last.index) as int]; } static void regAdapter() { @@ -99,6 +103,7 @@ class SettingBoxKey { enableQuickDouble = 'enableQuickDouble', enableShowDanmaku = 'enableShowDanmaku', enableBackgroundPlay = 'enableBackgroundPlay', + fullScreenGestureMode = 'fullScreenGestureMode', /// 隐私 blackMidsList = 'blackMidsList', From db03cdd4426f5831d2491daf096083055a613b53 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 28 Feb 2024 00:45:13 +0800 Subject: [PATCH 09/67] fix: List dataType --- lib/models/user/fav_folder.dart | 2 +- lib/pages/bangumi/controller.dart | 4 ++-- lib/pages/blacklist/index.dart | 2 +- lib/pages/dynamics/controller.dart | 8 ++++---- lib/pages/dynamics/detail/controller.dart | 2 +- lib/pages/fan/controller.dart | 2 +- lib/pages/hot/controller.dart | 2 +- lib/pages/member/controller.dart | 2 +- lib/pages/search/controller.dart | 2 +- lib/pages/video/detail/reply/controller.dart | 2 +- lib/pages/video/detail/reply_reply/controller.dart | 2 +- 11 files changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/models/user/fav_folder.dart b/lib/models/user/fav_folder.dart index 6d3f9975..c45e2de9 100644 --- a/lib/models/user/fav_folder.dart +++ b/lib/models/user/fav_folder.dart @@ -15,7 +15,7 @@ class FavFolderData { ? json['list'] .map((e) => FavFolderItemData.fromJson(e)) .toList() - : [FavFolderItemData()]; + : []; hasMore = json['has_more']; } } diff --git a/lib/pages/bangumi/controller.dart b/lib/pages/bangumi/controller.dart index 09afc43a..e5748d6c 100644 --- a/lib/pages/bangumi/controller.dart +++ b/lib/pages/bangumi/controller.dart @@ -7,8 +7,8 @@ import 'package:pilipala/utils/storage.dart'; class BangumiController extends GetxController { final ScrollController scrollController = ScrollController(); - RxList bangumiList = [BangumiListItemModel()].obs; - RxList bangumiFollowList = [BangumiListItemModel()].obs; + RxList bangumiList = [].obs; + RxList bangumiFollowList = [].obs; int _currentPage = 1; bool isLoadingMore = true; Box userInfoCache = GStrorage.userInfo; diff --git a/lib/pages/blacklist/index.dart b/lib/pages/blacklist/index.dart index 3bf64146..402790f5 100644 --- a/lib/pages/blacklist/index.dart +++ b/lib/pages/blacklist/index.dart @@ -139,7 +139,7 @@ class BlackListController extends GetxController { int currentPage = 1; int pageSize = 50; RxInt total = 0.obs; - RxList blackList = [BlackListItem()].obs; + RxList blackList = [].obs; Future queryBlacklist({type = 'init'}) async { if (type == 'init') { diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 26ba2b22..1e9f2da4 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -20,7 +20,7 @@ import 'package:pilipala/utils/utils.dart'; class DynamicsController extends GetxController { int page = 1; String? offset = ''; - RxList dynamicsList = [DynamicItemModel()].obs; + RxList dynamicsList = [].obs; Rx dynamicsType = DynamicsType.values[0].obs; RxString dynamicsTypeLabel = '全部'.obs; final ScrollController scrollController = ScrollController(); @@ -105,7 +105,7 @@ class DynamicsController extends GetxController { onSelectType(value) async { dynamicsType.value = filterTypeList[value]['value']; - dynamicsList.value = [DynamicItemModel()]; + dynamicsList.value = []; page = 1; initialValue.value = value; await queryFollowDynamic(); @@ -264,7 +264,7 @@ class DynamicsController extends GetxController { onSelectUp(mid) async { dynamicsType.value = DynamicsType.values[0]; - dynamicsList.value = [DynamicItemModel()]; + dynamicsList.value = []; page = 1; queryFollowDynamic(); } @@ -293,7 +293,7 @@ class DynamicsController extends GetxController { dynamicsType.value = DynamicsType.values[0]; initialValue.value = 0; SmartDialog.showToast('还原默认加载'); - dynamicsList.value = [DynamicItemModel()]; + dynamicsList.value = []; queryFollowDynamic(); } } diff --git a/lib/pages/dynamics/detail/controller.dart b/lib/pages/dynamics/detail/controller.dart index 4c5f35d6..8e117383 100644 --- a/lib/pages/dynamics/detail/controller.dart +++ b/lib/pages/dynamics/detail/controller.dart @@ -17,7 +17,7 @@ class DynamicDetailController extends GetxController { int currentPage = 0; bool isLoadingMore = false; RxString noMore = ''.obs; - RxList replyList = [ReplyItemModel()].obs; + RxList replyList = [].obs; RxInt acount = 0.obs; final ScrollController scrollController = ScrollController(); diff --git a/lib/pages/fan/controller.dart b/lib/pages/fan/controller.dart index 8675ada7..c1c2a427 100644 --- a/lib/pages/fan/controller.dart +++ b/lib/pages/fan/controller.dart @@ -10,7 +10,7 @@ class FansController extends GetxController { int pn = 1; int ps = 20; int total = 0; - RxList fansList = [FansItemModel()].obs; + RxList fansList = [].obs; late int mid; late String name; var userInfo; diff --git a/lib/pages/hot/controller.dart b/lib/pages/hot/controller.dart index 65706c32..85072475 100644 --- a/lib/pages/hot/controller.dart +++ b/lib/pages/hot/controller.dart @@ -7,7 +7,7 @@ class HotController extends GetxController { final ScrollController scrollController = ScrollController(); final int _count = 20; int _currentPage = 1; - RxList videoList = [HotVideoItemModel()].obs; + RxList videoList = [].obs; bool isLoadingMore = false; bool flag = false; OverlayEntry? popupDialog; diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 70169e3d..0aa7166f 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -20,7 +20,7 @@ class MemberController extends GetxController { Box userInfoCache = GStrorage.userInfo; late int ownerMid; // 投稿列表 - RxList? archiveList = [VListItemModel()].obs; + RxList? archiveList = [].obs; dynamic userInfo; RxInt attribute = (-1).obs; RxString attributeText = '关注'.obs; diff --git a/lib/pages/search/controller.dart b/lib/pages/search/controller.dart index 5ec1710a..cbb86405 100644 --- a/lib/pages/search/controller.dart +++ b/lib/pages/search/controller.dart @@ -15,7 +15,7 @@ class SSearchController extends GetxController { Box histiryWord = GStrorage.historyword; List historyCacheList = []; RxList historyList = [].obs; - RxList searchSuggestList = [SearchSuggestItem()].obs; + RxList searchSuggestList = [].obs; final _debouncer = Debouncer(delay: const Duration(milliseconds: 200)); // 设置延迟时间 String hintText = '搜索'; diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index e564ef02..91344fc1 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -22,7 +22,7 @@ class VideoReplyController extends GetxController { String? replyLevel; // rpid 请求楼中楼回复 String? rpid; - RxList replyList = [ReplyItemModel()].obs; + RxList replyList = [].obs; // 当前页 int currentPage = 0; bool isLoadingMore = false; diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index 0d5b4488..6daa0b8a 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -12,7 +12,7 @@ class VideoReplyReplyController extends GetxController { // rpid 请求楼中楼回复 String? rpid; ReplyType replyType = ReplyType.video; - RxList replyList = [ReplyItemModel()].obs; + RxList replyList = [].obs; // 当前页 int currentPage = 0; bool isLoadingMore = false; From a3abed0a03d04edc16371b34ce8c70e6b9381d80 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Tue, 20 Feb 2024 13:07:27 +0800 Subject: [PATCH 10/67] =?UTF-8?q?=E6=96=B0=E5=A2=9Ealpha.yml,=20=E7=94=A8?= =?UTF-8?q?=E4=BA=8E=E7=BC=96=E8=AF=91=E6=8E=A8=E9=80=81=E8=87=B3alpha?= =?UTF-8?q?=E5=88=86=E6=94=AF=E7=9A=84=E4=BB=A3=E7=A0=81=E5=B9=B6=E5=8F=91?= =?UTF-8?q?=E9=80=81=E8=87=B3Telegram=E9=A2=91=E9=81=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 209 ++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 .github/workflows/alpha.yml diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml new file mode 100644 index 00000000..e56fb2f9 --- /dev/null +++ b/.github/workflows/alpha.yml @@ -0,0 +1,209 @@ +name: CI + +on: + workflow_dispatch: + push: + branches: + - 'alpha' + paths-ignore: + - '**.md' + - '**.txt' + - '.github/**' + - '.idea/**' + - '!.github/workflows/**' + +jobs: + update_version: + name: Read and update version + runs-on: ubuntu-latest + + outputs: + # 定义输出变量 version,以便在其他job中引用 + new_version: ${{ steps.version.outputs.new_version }} + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} + + - name: 获取first parent commit次数 + id: get-first-parent-commit-count + run: | + version=$(yq e .version pubspec.yaml | cut -d "+" -f 1) + git fetch origin "refs/tags/*:refs/tags/*" + recent_release_tag=$(git tag -l | grep $version | egrep -v "[-|+]" || true) + if [[ "x$recent_release_tag" == "x" ]]; then + echo "当前版本tag不存在,请手动生成tag." + exit 1 + fi + first_parent_commit_count=$(git rev-list --first-parent --count $recent_release_tag..FETCH_HEAD) + echo "count=$first_parent_commit_count" >> $GITHUB_OUTPUT + + - name: 更新版本号 + id: version + run: | + # 读取版本号 + VERSION=$(yq e .version pubspec.yaml | cut -d "+" -f 1) + + # 获取GitHub Actions的run_number + #RUN_NUMBER=${{ github.run_number }} + + # 构建新版本号 + NEW_VERSION=$VERSION-alpha.${{ steps.get-first-parent-commit-count.outputs.count }} + + # 输出新版本号 + echo "New version: $NEW_VERSION" + + # 设置新版本号为输出变量 + echo "new_version=$NEW_VERSION" >>$GITHUB_OUTPUT + + android: + name: Build CI (Android) + needs: update_version + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - 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 + 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: 下载项目依赖 + run: flutter pub get + + - name: 解码生成 jks + run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks + env: + KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} + + - name: 更新版本号 + id: version + run: | + # 更新pubspec.yaml文件中的版本号 + sed -i "s/version: .*+/version: ${{ needs.update_version.outputs.new_version }}+/g" pubspec.yaml + + - 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: 重命名应用 + run: | + 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]}${{ needs.update_version.outputs.new_version }}.apk" + mv "$file" "$new_file_name" + fi + done + + - name: 上传 + uses: actions/upload-artifact@v3 + with: + name: Pilipala-CI + path: | + build/app/outputs/flutter-apk/Pili-*.apk + + iOS: + name: Build CI (iOS) + needs: update_version + runs-on: macos-latest + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: 安装Flutter + if: steps.cache-flutter.outputs.cache-hit != 'true' + uses: subosito/flutter-action@v2.10.0 + with: + cache: true + flutter-version: 3.16.5 + + - name: 更新版本号 + id: version + run: | + # 更新pubspec.yaml文件中的版本号 + sed -i "" "s/version: .*+/version: ${{ needs.update_version.outputs.new_version }}+/g" pubspec.yaml + + - 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: 重命名应用 + run: | + DATE=${{ steps.date.outputs.date }} + for file in app.ipa; do + new_file_name="build/Pili-${{ needs.update_version.outputs.new_version }}.ipa" + mv "$file" "$new_file_name" + done + + - name: 上传 + uses: actions/upload-artifact@v3 + with: + if-no-files-found: error + name: Pilipala-CI + path: | + build/Pili-*.ipa + + upload: + runs-on: ubuntu-latest + + needs: + - update_version + - android + - iOS + steps: + + - uses: actions/download-artifact@v3 + with: + name: Pilipala-CI + path: ./Pilipala-CI + + # - name: Upload Pre-release + # uses: ncipollo/release-action@v1 + # with: + # name: ${{ needs.update_version.outputs.new_version }} + # token: ${{ secrets.GIT_TOKEN }} + # commit: main + # tag: ${{ needs.update_version.outputs.new_version }} + # prerelease: true + # allowUpdates: true + # artifacts: Pilipala-CI/* + + - name: 发送到Telegram频道 + uses: xireiki/channel-post@main + with: + BOT_TOKEN: ${{ secrets.BOT_TOKEN }} + CHAT_ID: ${{ secrets.CHAT_ID }} + METHOD: sendMediaGroup + path: | + ./Pilipala-CI/Pili-${{ needs.update_version.outputs.new_version }}.ipa + ./Pilipala-CI/Pili-arm64-${{ needs.update_version.outputs.new_version }}.apk + ./Pilipala-CI/Pili-armeabi-v7a-${{ needs.update_version.outputs.new_version }}.apk + ./Pilipala-CI/Pili-x86_64-${{ needs.update_version.outputs.new_version }}.apk + CONTEXT: "**Pre-release:** ${{ needs.update_version.outputs.new_version }}" + PARSE_MODE: Markdown From fc2da3ce577a03fd707e512b7bbcf8706cbf4ad1 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Tue, 20 Feb 2024 13:17:44 +0800 Subject: [PATCH 11/67] =?UTF-8?q?=E4=BD=BFcheckout=20action=E5=85=8B?= =?UTF-8?q?=E9=9A=86=E6=8C=87=E5=AE=9A=E5=88=86=E6=94=AF;=20=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E4=BB=A3=E7=A0=81=E7=BC=A9=E8=BF=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index e56fb2f9..c56c9abb 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -66,27 +66,29 @@ jobs: steps: - name: Checkout code uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} - name: 构建Java环境 uses: actions/setup-java@v3 with: - distribution: "zulu" - java-version: "17" - token: ${{secrets.GIT_TOKEN}} + distribution: "zulu" + java-version: "17" + token: ${{secrets.GIT_TOKEN}} - name: 检查缓存 uses: actions/cache@v2 id: cache-flutter with: - path: /root/flutter-sdk - key: ${{ runner.os }}-flutter-${{ hashFiles('**/pubspec.lock') }} + path: /root/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 + flutter-version: 3.16.5 + channel: any - name: 下载项目依赖 run: flutter pub get @@ -94,7 +96,7 @@ jobs: - name: 解码生成 jks run: echo $KEYSTORE_BASE64 | base64 -di > android/app/vvex.jks env: - KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} + KEYSTORE_BASE64: ${{ secrets.KEYSTORE_BASE64 }} - name: 更新版本号 id: version @@ -105,9 +107,9 @@ jobs: - 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}} + KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.KEY_PASSWORD}} - name: 重命名应用 run: | @@ -132,7 +134,9 @@ jobs: steps: - name: Checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + with: + ref: ${{ github.ref_name }} - name: 安装Flutter if: steps.cache-flutter.outputs.cache-hit != 'true' From 6c20a434ed89e2b9f8ee9106131d95b8d0a98a69 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Tue, 20 Feb 2024 15:31:40 +0800 Subject: [PATCH 12/67] =?UTF-8?q?=E5=88=97=E5=87=BA=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index c56c9abb..c32db6d4 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -197,6 +197,8 @@ jobs: # prerelease: true # allowUpdates: true # artifacts: Pilipala-CI/* + - name: 列出文件 + run: ls -1 ./Pilipala-CI - name: 发送到Telegram频道 uses: xireiki/channel-post@main From 381e832f3cba8da4027bbcf3acd7c0aa29c03f30 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Tue, 20 Feb 2024 15:59:34 +0800 Subject: [PATCH 13/67] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=9E=B6=E6=9E=84?= =?UTF-8?q?=E5=90=8D=E7=A7=B0=E6=8B=BC=E5=86=99=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index c32db6d4..4d840447 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -208,7 +208,7 @@ jobs: METHOD: sendMediaGroup path: | ./Pilipala-CI/Pili-${{ needs.update_version.outputs.new_version }}.ipa - ./Pilipala-CI/Pili-arm64-${{ needs.update_version.outputs.new_version }}.apk + ./Pilipala-CI/Pili-arm64-v8a-${{ needs.update_version.outputs.new_version }}.apk ./Pilipala-CI/Pili-armeabi-v7a-${{ needs.update_version.outputs.new_version }}.apk ./Pilipala-CI/Pili-x86_64-${{ needs.update_version.outputs.new_version }}.apk CONTEXT: "**Pre-release:** ${{ needs.update_version.outputs.new_version }}" From cfeb0588c1d8633a9c90a299b0a2976990b33816 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Tue, 20 Feb 2024 18:03:18 +0800 Subject: [PATCH 14/67] =?UTF-8?q?=E5=8F=96=E6=B6=88=E5=8F=91=E9=80=81?= =?UTF-8?q?=E5=85=B6=E4=BB=96=E6=9E=B6=E6=9E=84APK,=20=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E5=8F=91=E9=80=81=E6=96=87=E4=BB=B6=E5=A4=A7=E5=B0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 4d840447..6cda8d49 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -209,7 +209,7 @@ jobs: path: | ./Pilipala-CI/Pili-${{ needs.update_version.outputs.new_version }}.ipa ./Pilipala-CI/Pili-arm64-v8a-${{ needs.update_version.outputs.new_version }}.apk - ./Pilipala-CI/Pili-armeabi-v7a-${{ needs.update_version.outputs.new_version }}.apk - ./Pilipala-CI/Pili-x86_64-${{ needs.update_version.outputs.new_version }}.apk + #./Pilipala-CI/Pili-armeabi-v7a-${{ needs.update_version.outputs.new_version }}.apk + #./Pilipala-CI/Pili-x86_64-${{ needs.update_version.outputs.new_version }}.apk CONTEXT: "**Pre-release:** ${{ needs.update_version.outputs.new_version }}" PARSE_MODE: Markdown From 83ad11402fbf0840e3d1ded91854a4e9f4eef314 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Tue, 20 Feb 2024 18:52:41 +0800 Subject: [PATCH 15/67] =?UTF-8?q?=F0=9F=98=85=E6=B3=A8=E9=87=8A=E7=AC=A6?= =?UTF-8?q?=E8=A2=AB=E8=AF=86=E5=88=AB=E4=B8=BA=E6=96=87=E4=BB=B6=E5=90=8D?= =?UTF-8?q?=E7=9A=84=E4=B8=80=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 6cda8d49..922c6ef5 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -209,7 +209,5 @@ jobs: path: | ./Pilipala-CI/Pili-${{ needs.update_version.outputs.new_version }}.ipa ./Pilipala-CI/Pili-arm64-v8a-${{ needs.update_version.outputs.new_version }}.apk - #./Pilipala-CI/Pili-armeabi-v7a-${{ needs.update_version.outputs.new_version }}.apk - #./Pilipala-CI/Pili-x86_64-${{ needs.update_version.outputs.new_version }}.apk CONTEXT: "**Pre-release:** ${{ needs.update_version.outputs.new_version }}" PARSE_MODE: Markdown From 3bf3fd9a46b2f42e26dd4d819116853775c62493 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Wed, 21 Feb 2024 13:03:30 +0800 Subject: [PATCH 16/67] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=8F=82=E6=95=B0`fetc?= =?UTF-8?q?h-depth:=200`=E5=8F=96=E5=BE=97=E6=89=80=E6=9C=89=E5=88=86?= =?UTF-8?q?=E6=94=AF=E5=92=8Ctags,=20=E6=9C=AB=E7=AB=AF=E6=8F=90=E4=BA=A4?= =?UTF-8?q?=E6=94=B9=E5=9B=9EHEAD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 922c6ef5..9d0b4ed8 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -26,18 +26,19 @@ jobs: uses: actions/checkout@v4 with: ref: ${{ github.ref_name }} + fetch-depth: 0 - name: 获取first parent commit次数 id: get-first-parent-commit-count run: | version=$(yq e .version pubspec.yaml | cut -d "+" -f 1) - git fetch origin "refs/tags/*:refs/tags/*" recent_release_tag=$(git tag -l | grep $version | egrep -v "[-|+]" || true) if [[ "x$recent_release_tag" == "x" ]]; then echo "当前版本tag不存在,请手动生成tag." exit 1 fi - first_parent_commit_count=$(git rev-list --first-parent --count $recent_release_tag..FETCH_HEAD) + git log --oneline --first-parent $recent_release_tag..HEAD + first_parent_commit_count=$(git rev-list --first-parent --count $recent_release_tag..HEAD) echo "count=$first_parent_commit_count" >> $GITHUB_OUTPUT - name: 更新版本号 From 95bc4a9f468ace6a088a6e4c9a7bf7b3568c398a Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Wed, 21 Feb 2024 16:05:57 +0800 Subject: [PATCH 17/67] =?UTF-8?q?=E5=9C=A8Telegram=E6=B6=88=E6=81=AF?= =?UTF-8?q?=E4=B8=AD=E6=98=BE=E7=A4=BA=E6=9C=80=E5=90=8E=E4=B8=80=E6=AC=A1?= =?UTF-8?q?=E6=8F=90=E4=BA=A4=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 9d0b4ed8..81c8e611 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -20,6 +20,7 @@ jobs: outputs: # 定义输出变量 version,以便在其他job中引用 new_version: ${{ steps.version.outputs.new_version }} + last_commit: ${{ steps.get-last-commit.outputs.last_commit }} steps: - name: Checkout code @@ -41,6 +42,12 @@ jobs: first_parent_commit_count=$(git rev-list --first-parent --count $recent_release_tag..HEAD) echo "count=$first_parent_commit_count" >> $GITHUB_OUTPUT + - name: 获取最后一次提交 + id: get-last-commit + run: | + last_commit=$(git log -1 --pretty="%h %B" --first-parent) + echo "last_commit=$last_commit" >> $GITHUB_OUTPUT + - name: 更新版本号 id: version run: | @@ -210,5 +217,6 @@ jobs: path: | ./Pilipala-CI/Pili-${{ needs.update_version.outputs.new_version }}.ipa ./Pilipala-CI/Pili-arm64-v8a-${{ needs.update_version.outputs.new_version }}.apk - CONTEXT: "**Pre-release:** ${{ needs.update_version.outputs.new_version }}" + CONTEXT: "**Pre-release: ${{ needs.update_version.outputs.new_version }}**\n${{ needs.update_version.outputs.last_commit }}" + PARSE_MODE: Markdown From 40cc4e0dd1470323aa2ec6da231d8a7e497ea65a Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Wed, 21 Feb 2024 17:29:59 +0800 Subject: [PATCH 18/67] =?UTF-8?q?channel-post@v1.0.5=E9=87=8D=E5=A4=8D?= =?UTF-8?q?=E5=8F=91=E9=80=81=E6=96=87=E4=BB=B6=EF=BC=8C=E6=94=B9=E4=B8=BA?= =?UTF-8?q?v1.0.4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 81c8e611..ae207a44 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -209,7 +209,7 @@ jobs: run: ls -1 ./Pilipala-CI - name: 发送到Telegram频道 - uses: xireiki/channel-post@main + uses: xireiki/channel-post@v1.0.4 with: BOT_TOKEN: ${{ secrets.BOT_TOKEN }} CHAT_ID: ${{ secrets.CHAT_ID }} From 04186cdd5b8484eedd086f54b6a8464dca8bffcd Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Wed, 21 Feb 2024 23:53:23 +0800 Subject: [PATCH 19/67] =?UTF-8?q?=E5=B0=86alpha.yml=E7=9A=84workflow=20nam?= =?UTF-8?q?e=E6=94=B9=E4=B8=BAalpha,=20=E9=81=BF=E5=85=8D=E6=B7=B7?= =?UTF-8?q?=E6=B7=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index ae207a44..3cdc4fd5 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -1,4 +1,4 @@ -name: CI +name: alpha on: workflow_dispatch: From 4642c2a8472e41e4443d69eb829c901d9a1a3cad Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Wed, 21 Feb 2024 23:55:50 +0800 Subject: [PATCH 20/67] =?UTF-8?q?=E5=B0=86git=20log=20pretty=20format?= =?UTF-8?q?=E4=B8=ADraw=20body=E6=9B=BF=E6=8D=A2=E4=B8=BAsubject,=20?= =?UTF-8?q?=E9=81=BF=E5=85=8Drevert=20commit=E5=A4=9A=E8=A1=8C=E8=BE=93?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index 3cdc4fd5..e44bd262 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -45,7 +45,7 @@ jobs: - name: 获取最后一次提交 id: get-last-commit run: | - last_commit=$(git log -1 --pretty="%h %B" --first-parent) + last_commit=$(git log -1 --pretty="%h %s" --first-parent) echo "last_commit=$last_commit" >> $GITHUB_OUTPUT - name: 更新版本号 From 65d2bfd844e5019cd3655266063e0514ee82a561 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Sat, 24 Feb 2024 22:10:08 +0800 Subject: [PATCH 21/67] =?UTF-8?q?=E5=8D=87=E7=BA=A7=E8=87=B3channel-post@v?= =?UTF-8?q?1.0.7,=20=E6=94=AF=E6=8C=81=E4=BC=A0=E8=BE=93=E5=A4=A7=E6=96=87?= =?UTF-8?q?=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/alpha.yml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index e44bd262..c22f25c9 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -205,18 +205,16 @@ jobs: # prerelease: true # allowUpdates: true # artifacts: Pilipala-CI/* - - name: 列出文件 - run: ls -1 ./Pilipala-CI - name: 发送到Telegram频道 - uses: xireiki/channel-post@v1.0.4 + uses: xireiki/channel-post@v1.0.7 with: - BOT_TOKEN: ${{ secrets.BOT_TOKEN }} - CHAT_ID: ${{ secrets.CHAT_ID }} - METHOD: sendMediaGroup - path: | - ./Pilipala-CI/Pili-${{ needs.update_version.outputs.new_version }}.ipa - ./Pilipala-CI/Pili-arm64-v8a-${{ needs.update_version.outputs.new_version }}.apk - CONTEXT: "**Pre-release: ${{ needs.update_version.outputs.new_version }}**\n${{ needs.update_version.outputs.last_commit }}" - - PARSE_MODE: Markdown + bot_token: ${{ secrets.BOT_TOKEN }} + chat_id: ${{ secrets.CHAT_ID }} + large_file: true + api_id: ${{ secrets.TELEGRAM_API_ID }} + api_hash: ${{ secrets.TELEGRAM_API_HASH }} + method: sendFile + path: Pilipala-CI/* + parse_mode: Markdown + context: "**Pre-release: ${{ needs.update_version.outputs.new_version }}**\n${{ needs.update_version.outputs.last_commit }}" From 3f9fcabc2d7058fb21cbba9a69cace34e18a3985 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Wed, 28 Feb 2024 15:36:30 +0800 Subject: [PATCH 22/67] =?UTF-8?q?Revert=20"=E5=B0=86alpha.yml=E7=9A=84work?= =?UTF-8?q?flow=20name=E6=94=B9=E4=B8=BAalpha,=20=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E6=B7=B7=E6=B7=86"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 04186cdd5b8484eedd086f54b6a8464dca8bffcd. --- .github/workflows/alpha.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/alpha.yml b/.github/workflows/alpha.yml index c22f25c9..af155cc9 100644 --- a/.github/workflows/alpha.yml +++ b/.github/workflows/alpha.yml @@ -1,4 +1,4 @@ -name: alpha +name: CI on: workflow_dispatch: From 45cc46d6d6316eacadd69e40b1840814e4153cc9 Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Wed, 28 Feb 2024 15:38:17 +0800 Subject: [PATCH 23/67] =?UTF-8?q?=E9=87=8D=E5=91=BD=E5=90=8D=EF=BC=9A.gith?= =?UTF-8?q?ub/workflows/alpha.yml=20->=20.github/workflows/CI.yml?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/{alpha.yml => CI.yml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .github/workflows/{alpha.yml => CI.yml} (100%) diff --git a/.github/workflows/alpha.yml b/.github/workflows/CI.yml similarity index 100% rename from .github/workflows/alpha.yml rename to .github/workflows/CI.yml From 699be4125bc1265aee15d3820506369c7a463f0a Mon Sep 17 00:00:00 2001 From: VillagerTom Date: Wed, 28 Feb 2024 16:22:14 +0800 Subject: [PATCH 24/67] =?UTF-8?q?=E5=B0=86=E7=89=88=E6=9C=AC=E5=8F=B7?= =?UTF-8?q?=E4=B8=AD=E7=9A=84alpha=E6=94=B9=E4=B8=BAbeta;=20=E5=8A=A0?= =?UTF-8?q?=E5=9B=9E=E4=B9=8B=E5=89=8D=E5=88=A0=E5=8E=BB=E7=9A=84=E2=80=9C?= =?UTF-8?q?v=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/CI.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index af155cc9..231c61f5 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -4,7 +4,7 @@ on: workflow_dispatch: push: branches: - - 'alpha' + - 'main' paths-ignore: - '**.md' - '**.txt' @@ -58,7 +58,7 @@ jobs: #RUN_NUMBER=${{ github.run_number }} # 构建新版本号 - NEW_VERSION=$VERSION-alpha.${{ steps.get-first-parent-commit-count.outputs.count }} + NEW_VERSION=$VERSION-beta.${{ steps.get-first-parent-commit-count.outputs.count }} # 输出新版本号 echo "New version: $NEW_VERSION" @@ -123,7 +123,7 @@ jobs: run: | 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]}${{ needs.update_version.outputs.new_version }}.apk" + new_file_name="build/app/outputs/flutter-apk/Pili-${BASH_REMATCH[1]}v${{ needs.update_version.outputs.new_version }}.apk" mv "$file" "$new_file_name" fi done @@ -169,7 +169,7 @@ jobs: run: | DATE=${{ steps.date.outputs.date }} for file in app.ipa; do - new_file_name="build/Pili-${{ needs.update_version.outputs.new_version }}.ipa" + new_file_name="build/Pili-v${{ needs.update_version.outputs.new_version }}.ipa" mv "$file" "$new_file_name" done @@ -217,4 +217,4 @@ jobs: method: sendFile path: Pilipala-CI/* parse_mode: Markdown - context: "**Pre-release: ${{ needs.update_version.outputs.new_version }}**\n${{ needs.update_version.outputs.last_commit }}" + context: "*Pre-release: v${{ needs.update_version.outputs.new_version }}*\n${{ needs.update_version.outputs.last_commit }}" From 466214b26a6e37a9ec58b846a281230d557aa3cc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 28 Feb 2024 23:17:03 +0800 Subject: [PATCH 25/67] fix: statusBarIcon color --- lib/pages/home/view.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 91d0ea8c..020cb355 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -58,6 +58,9 @@ class _HomePageState extends State return Scaffold( extendBody: true, extendBodyBehindAppBar: true, + appBar: _homeController.enableGradientBg + ? null + : AppBar(toolbarHeight: 0, elevation: 0), body: Stack( children: [ // gradient background From 0b5397ec00ef148e08c54a5a40b71b636c1c715a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 28 Feb 2024 23:29:02 +0800 Subject: [PATCH 26/67] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E5=8C=BAseek=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 246d0688..ab385c0e 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -462,6 +462,9 @@ class ReplyItemRow extends StatelessWidget { InlineSpan buildContent( BuildContext context, replyItem, replyReply, fReplyItem) { + final String routePath = Get.currentRoute; + bool isVideoPage = routePath.startsWith('/video/detail'); + // replyItem 当前回复内容 // replyReply 查看二楼回复(回复详情)回调 // fReplyItem 父级回复内容,用作二楼回复(回复详情)展示 @@ -571,21 +574,26 @@ InlineSpan buildContent( spanChilds.add( TextSpan( text: ' $matchStr ', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), + style: isVideoPage + ? TextStyle( + color: Theme.of(context).colorScheme.primary, + ) + : null, recognizer: TapGestureRecognizer() ..onTap = () { // 跳转到指定位置 - try { - SmartDialog.showToast('跳转至:$matchStr'); - Get.find(tag: Get.arguments['heroTag']) - .plPlayerController - .seekTo( - Duration(seconds: Utils.duration(matchStr)), - ); - } catch (e) { - SmartDialog.showToast('跳转失败: $e'); + if (isVideoPage) { + try { + SmartDialog.showToast('跳转至:$matchStr'); + Get.find( + tag: Get.arguments['heroTag']) + .plPlayerController + .seekTo( + Duration(seconds: Utils.duration(matchStr)), + ); + } catch (e) { + SmartDialog.showToast('跳转失败: $e'); + } } }, ), From a9d73a9f1ba1749461b4e3132cd090993ab26023 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 28 Feb 2024 23:51:30 +0800 Subject: [PATCH 27/67] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E6=A0=87?= =?UTF-8?q?=E9=A2=98=E6=9C=AA=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/content_panel.dart | 4 +++- lib/pages/dynamics/widgets/rich_node_panel.dart | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index d10804d7..e1beaeb2 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -45,7 +45,9 @@ class _ContentState extends State { if (len == 1) { OpusPicsModel pictureItem = pics.first; picList.add(pictureItem.url!); - spanChilds.add(const TextSpan(text: '\n')); + + /// 图片上方的空白间隔 + // spanChilds.add(const TextSpan(text: '\n')); spanChilds.add( WidgetSpan( child: LayoutBuilder( diff --git a/lib/pages/dynamics/widgets/rich_node_panel.dart b/lib/pages/dynamics/widgets/rich_node_panel.dart index 8f744dd5..5ffee5f1 100644 --- a/lib/pages/dynamics/widgets/rich_node_panel.dart +++ b/lib/pages/dynamics/widgets/rich_node_panel.dart @@ -19,6 +19,17 @@ InlineSpan richNode(item, context) { // 动态页面 richTextNodes 层级可能与主页动态层级不同 richTextNodes = item.modules.moduleDynamic.major.opus.summary.richTextNodes; + if (item.modules.moduleDynamic.major.opus.title != null) { + spanChilds.add( + TextSpan( + text: item.modules.moduleDynamic.major.opus.title + '\n', + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ), + ); + } } if (richTextNodes == null || richTextNodes.isEmpty) { return spacer; From 0b0db1a2b17c056744bc28cf7d4d4f38f0501ade Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 28 Feb 2024 23:59:47 +0800 Subject: [PATCH 28/67] =?UTF-8?q?mod:=20videoPage=20path=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/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 ab385c0e..4232d4e9 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -463,7 +463,7 @@ class ReplyItemRow extends StatelessWidget { InlineSpan buildContent( BuildContext context, replyItem, replyReply, fReplyItem) { final String routePath = Get.currentRoute; - bool isVideoPage = routePath.startsWith('/video/detail'); + bool isVideoPage = routePath.startsWith('/video'); // replyItem 当前回复内容 // replyReply 查看二楼回复(回复详情)回调 From 33ef18ef1d23bbcc90022aec95f9c8cc4aed1891 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 29 Feb 2024 00:30:55 +0800 Subject: [PATCH 29/67] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BAjumpUrl=E6=AD=A3?= =?UTF-8?q?=E5=88=99=E8=BD=AC=E4=B9=89?= 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, 7 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 4232d4e9..c55380f2 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -506,21 +506,25 @@ InlineSpan buildContent( .replaceAll('"', '"') .replaceAll(''', "'") .replaceAll(' ', ' '); - // print("content.jumpUrl.keys:" + content.jumpUrl.keys.toString()); // 构建正则表达式 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('*', '\\*')), ]; + List jumpUrlKeysList = content.jumpUrl.keys.map((e) { + return e.replaceAllMapped( + RegExp(r'[?+*]'), (match) => '\\${match.group(0)}'); + }).toList(); String patternStr = specialTokens.map(RegExp.escape).join('|'); if (patternStr.isNotEmpty) { patternStr += "|"; } patternStr += r'(\b(?:\d+[::])?[0-5]?[0-9][::][0-5]?[0-9]\b)'; + if (jumpUrlKeysList.isNotEmpty) { + patternStr += '|${jumpUrlKeysList.join('|')}'; + } final RegExp pattern = RegExp(patternStr); List matchedStrs = []; void addPlainTextSpan(str) { @@ -599,7 +603,6 @@ InlineSpan buildContent( ), ); } else { - print("matchStr=$matchStr"); String appUrlSchema = ''; final bool enableWordRe = setting.get(SettingBoxKey.enableWordRe, defaultValue: false) as bool; From ce1c80fd86ad564f0fdd886e4ad8772bdeb046d0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 29 Feb 2024 23:42:09 +0800 Subject: [PATCH 30/67] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E6=9C=80?= =?UTF-8?q?=E6=96=B0=E5=85=B3=E6=B3=A8=E6=A8=AA=E8=A1=8C=E6=8B=89=E4=BC=B8?= =?UTF-8?q?=20issuse=20#580?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/up_panel.dart | 54 +++++++++++------------- 1 file changed, 25 insertions(+), 29 deletions(-) diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index 84753ff9..aa0bdd02 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -88,36 +88,32 @@ class _UpPanelState extends State { Container( height: 90, color: Theme.of(context).colorScheme.background, - child: Row( - children: [ - Expanded( - child: ListView( - scrollDirection: Axis.horizontal, - controller: scrollController, - children: [ - const SizedBox(width: 10), - if (liveList.isNotEmpty) ...[ - for (int i = 0; i < liveList.length; i++) ...[ - upItemBuild(liveList[i], i) - ], - VerticalDivider( - indent: 20, - endIndent: 40, - width: 26, - color: Theme.of(context) - .colorScheme - .primary - .withOpacity(0.5), - ), - ], - for (int i = 0; i < upList.length; i++) ...[ - upItemBuild(upList[i], i) - ], - const SizedBox(width: 10), + child: Expanded( + child: ListView( + scrollDirection: Axis.horizontal, + controller: scrollController, + children: [ + const SizedBox(width: 10), + if (liveList.isNotEmpty) ...[ + for (int i = 0; i < liveList.length; i++) ...[ + upItemBuild(liveList[i], i) ], - ), - ), - ], + VerticalDivider( + indent: 20, + endIndent: 40, + width: 26, + color: Theme.of(context) + .colorScheme + .primary + .withOpacity(0.5), + ), + ], + for (int i = 0; i < upList.length; i++) ...[ + upItemBuild(upList[i], i) + ], + const SizedBox(width: 10), + ], + ), ), ), Container( From be56fb721fd9d23b3efdebbc2123090b4a7b8464 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 1 Mar 2024 00:14:42 +0800 Subject: [PATCH 31/67] =?UTF-8?q?fix:=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=20issues=20#588?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_new/view.dart | 33 +++++++++++++--------- lib/pages/whisper_detail/view.dart | 30 +++++++++++--------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 83201697..678c66cc 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -109,21 +109,24 @@ class _VideoReplyNewDialogState extends State @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; - }); + final String routePath = Get.currentRoute; + if (mounted && routePath.startsWith('/video')) { + 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 @@ -131,11 +134,15 @@ class _VideoReplyNewDialogState extends State WidgetsBinding.instance.removeObserver(this); _replyContentController.dispose(); replyContentFocusNode.removeListener(() {}); + replyContentFocusNode.dispose(); 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( diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index e94b7d6d..1701be33 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -51,27 +51,31 @@ class _WhisperDetailPageState extends State @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; - }); + final String routePath = Get.currentRoute; + if (mounted && routePath.startsWith('/whisper_detail')) { + 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(() {}); + replyContentFocusNode.dispose(); super.dispose(); } From e5eae93a78812633adc843ac2ff7e1d17315a0db Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 1 Mar 2024 23:01:10 +0800 Subject: [PATCH 32/67] =?UTF-8?q?fix:=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=20issues=20#588?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_new/view.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 678c66cc..05351411 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -110,7 +110,9 @@ class _VideoReplyNewDialogState extends State void didChangeMetrics() { super.didChangeMetrics(); final String routePath = Get.currentRoute; - if (mounted && routePath.startsWith('/video')) { + if (mounted && + (routePath.startsWith('/video') || + routePath.startsWith('/dynamicDetail'))) { WidgetsBinding.instance.addPostFrameCallback((_) { // 键盘高度 final viewInsets = EdgeInsets.fromViewPadding( From d6da2a8a476c65456426173364d492106aeeb586 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 1 Mar 2024 23:55:19 +0800 Subject: [PATCH 33/67] =?UTF-8?q?fix:=20headerControl=20bvid=E4=B8=A2?= =?UTF-8?q?=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 1 + .../video/detail/introduction/controller.dart | 3 ++- lib/pages/video/detail/introduction/view.dart | 24 ++++++++++++++----- lib/pages/video/detail/view.dart | 12 ++++++---- .../video/detail/widgets/header_control.dart | 5 +++- 5 files changed, 33 insertions(+), 12 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 7465c6f2..d590cea9 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -128,6 +128,7 @@ class VideoDetailController extends GetxController controller: plPlayerController, videoDetailCtr: this, floating: floating, + bvid: bvid, ); // CDN优化 enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 26cbd2e0..c7f22b13 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -22,8 +22,9 @@ import '../related/index.dart'; import 'widgets/group_panel.dart'; class VideoIntroController extends GetxController { + VideoIntroController({required this.bvid}); // 视频bvid - String bvid = Get.parameters['bvid']!; + String bvid; // 是否预渲染 骨架屏 bool preRender = false; diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 0a3ac934..bed37bb5 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -24,7 +24,10 @@ import 'widgets/page.dart'; import 'widgets/season.dart'; class VideoIntroPanel extends StatefulWidget { - const VideoIntroPanel({super.key}); + final String bvid; + final String? cid; + + const VideoIntroPanel({super.key, required this.bvid, this.cid}); @override State createState() => _VideoIntroPanelState(); @@ -47,7 +50,8 @@ class _VideoIntroPanelState extends State /// fix 全屏时参数丢失 heroTag = Get.arguments['heroTag']; - videoIntroController = Get.put(VideoIntroController(), tag: heroTag); + videoIntroController = + Get.put(VideoIntroController(bvid: widget.bvid), tag: heroTag); _futureBuilderFuture = videoIntroController.queryVideoIntro(); videoIntroController.videoDetail.listen((value) { videoDetail = value; @@ -77,6 +81,7 @@ class _VideoIntroPanelState extends State loadingStatus: false, videoDetail: videoIntroController.videoDetail.value, heroTag: heroTag, + bvid: widget.bvid, ), ); } else { @@ -95,6 +100,7 @@ class _VideoIntroPanelState extends State loadingStatus: true, videoDetail: videoDetail, heroTag: heroTag, + bvid: widget.bvid, ); } }, @@ -106,10 +112,15 @@ class VideoInfo extends StatefulWidget { final bool loadingStatus; final VideoDetailData? videoDetail; final String? heroTag; + final String bvid; - const VideoInfo( - {Key? key, this.loadingStatus = false, this.videoDetail, this.heroTag}) - : super(key: key); + const VideoInfo({ + Key? key, + this.loadingStatus = false, + this.videoDetail, + this.heroTag, + required this.bvid, + }) : super(key: key); @override State createState() => _VideoInfoState(); @@ -149,7 +160,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { void initState() { super.initState(); heroTag = widget.heroTag!; - videoIntroController = Get.put(VideoIntroController(), tag: heroTag); + videoIntroController = + Get.put(VideoIntroController(bvid: widget.bvid), tag: heroTag); videoDetailCtr = Get.find(tag: heroTag); videoItem = videoIntroController.videoItem!; sheetHeight = localCache.get('sheetHeight'); diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index febca105..b82d3e96 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -23,7 +23,6 @@ import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/storage.dart'; -import 'package:pilipala/plugin/pl_player/utils/fullscreen.dart'; import '../../../services/shutdown_timer_service.dart'; import 'widgets/header_control.dart'; @@ -68,7 +67,9 @@ class _VideoDetailPageState extends State super.initState(); heroTag = Get.arguments['heroTag']; videoDetailController = Get.put(VideoDetailController(), tag: heroTag); - videoIntroController = Get.put(VideoIntroController(), tag: heroTag); + videoIntroController = Get.put( + VideoIntroController(bvid: Get.parameters['bvid']!), + tag: heroTag); videoIntroController.videoDetail.listen((value) { videoPlayerServiceHandler.onVideoDetailChange( value, videoDetailController.cid.value); @@ -227,6 +228,7 @@ class _VideoDetailPageState extends State plPlayerController!.removeStatusLister(playerListener); plPlayerController!.pause(); } + print('🐶🐶'); setState(() => isShowing = false); super.didPushNext(); } @@ -550,7 +552,8 @@ class _VideoDetailPageState extends State slivers: [ if (videoDetailController.videoType == SearchType.video) ...[ - const VideoIntroPanel(), + VideoIntroPanel( + bvid: videoDetailController.bvid), ] else if (videoDetailController.videoType == SearchType.media_bangumi) ...[ Obx(() => BangumiIntroPanel( @@ -577,7 +580,7 @@ class _VideoDetailPageState extends State .withOpacity(0.06), ), ), - RelatedVideoPanel(), + const RelatedVideoPanel(), ], ); }, @@ -627,6 +630,7 @@ class _VideoDetailPageState extends State headerControl: HeaderControl( controller: plPlayerController, videoDetailCtr: videoDetailController, + bvid: videoDetailController.bvid, ), danmuWidget: Obx( () => PlDanmaku( diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 596c567b..e2e4db8c 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -27,11 +27,13 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { this.controller, this.videoDetailCtr, this.floating, + this.bvid, super.key, }); final PlPlayerController? controller; final VideoDetailController? videoDetailCtr; final Floating? floating; + final String? bvid; @override State createState() => _HeaderControlState(); @@ -62,7 +64,8 @@ class _HeaderControlState extends State { speedsList = widget.controller!.speedsList; fullScreenStatusListener(); heroTag = Get.arguments['heroTag']; - videoIntroController = Get.put(VideoIntroController(), tag: heroTag); + videoIntroController = + Get.put(VideoIntroController(bvid: widget.bvid!), tag: heroTag); } void fullScreenStatusListener() { From f815affff9983e98ec9957df246749ddf6dba790 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 00:40:53 +0800 Subject: [PATCH 34/67] =?UTF-8?q?opt:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E6=A0=8F=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/widgets/app_bar_ani.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/plugin/pl_player/widgets/app_bar_ani.dart b/lib/plugin/pl_player/widgets/app_bar_ani.dart index 9a3af267..53eaad16 100644 --- a/lib/plugin/pl_player/widgets/app_bar_ani.dart +++ b/lib/plugin/pl_player/widgets/app_bar_ani.dart @@ -19,11 +19,11 @@ class AppBarAni extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { - visible ? controller.reverse() : controller.forward(); + visible ? controller.forward() : controller.reverse(); return SlideTransition( position: Tween( - begin: Offset.zero, - end: Offset(0, position! == 'top' ? -1 : 1), + begin: Offset(0, position! == 'top' ? -1 : 1), + end: Offset.zero, ).animate(CurvedAnimation( parent: controller, curve: Curves.linear, From f0d8e2a122b4ed060ad8c9326ca3c5de9bab616e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 11:19:18 +0800 Subject: [PATCH 35/67] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E6=A0=8F=E5=8A=A8=E7=94=BB=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/play_setting.dart | 9 +++++++++ lib/plugin/pl_player/view.dart | 6 +++++- lib/utils/global_data.dart | 1 + lib/utils/storage.dart | 3 +++ 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index a2a3fef2..469bf975 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -7,6 +7,7 @@ import 'package:pilipala/models/video/play/quality.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/services/service_locator.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import 'widgets/switch_item.dart'; @@ -146,6 +147,14 @@ class _PlaySettingState extends State { setKey: SettingBoxKey.enableShowDanmaku, defaultVal: false, ), + SetSwitchItem( + title: '控制栏动画', + subTitle: '播放器控制栏显示动画效果', + setKey: SettingBoxKey.enablePlayerControlAnimation, + defaultVal: true, + callFn: (bool val) { + GlobalData().enablePlayerControlAnimation = val; + }), ListTile( dense: false, title: Text('默认画质', style: titleStyle), diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 9c5731ec..988ec26d 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -120,7 +120,11 @@ class _PLVideoPlayerState extends State super.initState(); screenWidth = Get.size.width; animationController = AnimationController( - vsync: this, duration: const Duration(milliseconds: 300)); + vsync: this, + duration: GlobalData().enablePlayerControlAnimation + ? const Duration(milliseconds: 150) + : const Duration(milliseconds: 10), + ); videoController = widget.controller.videoController!; widget.controller.headerControl = widget.headerControl; widget.controller.bottomControl = widget.bottomControl; diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart index 08157db4..ef3daf21 100644 --- a/lib/utils/global_data.dart +++ b/lib/utils/global_data.dart @@ -4,6 +4,7 @@ class GlobalData { int imgQuality = 10; FullScreenGestureMode fullScreenGestureMode = FullScreenGestureMode.values.last; + bool enablePlayerControlAnimation = true; // 私有构造函数 GlobalData._(); diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index d69ffc20..44beb4e7 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -49,6 +49,8 @@ class GStrorage { GlobalData().fullScreenGestureMode = FullScreenGestureMode.values[ setting.get(SettingBoxKey.fullScreenGestureMode, defaultValue: FullScreenGestureMode.values.last.index) as int]; + GlobalData().enablePlayerControlAnimation = setting + .get(SettingBoxKey.enablePlayerControlAnimation, defaultValue: true); } static void regAdapter() { @@ -98,6 +100,7 @@ class SettingBoxKey { enableCDN = 'enableCDN', autoPiP = 'autoPiP', enableAutoLongPressSpeed = 'enableAutoLongPressSpeed', + enablePlayerControlAnimation = 'enablePlayerControlAnimation', // youtube 双击快进快退 enableQuickDouble = 'enableQuickDouble', From 98122aeaae1e54315a68f72e924fea016981f763 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 11:45:36 +0800 Subject: [PATCH 36/67] fix: audioHandler null --- lib/services/audio_handler.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index ad510e7d..0b37d419 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -149,6 +149,8 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { )); if (_item.isNotEmpty) { _item.removeLast(); + } + if (_item.isNotEmpty) { setMediaItem(_item.last); } if (_item.isEmpty) { From fca7c362031e46a1bc097155db69fe1d26d687d3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 12:56:16 +0800 Subject: [PATCH 37/67] =?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/models/dynamics/up.dart | 7 +++ lib/pages/dynamics/controller.dart | 5 +- lib/pages/dynamics/widgets/up_panel.dart | 60 +++++++++++++----------- 3 files changed, 41 insertions(+), 31 deletions(-) diff --git a/lib/models/dynamics/up.dart b/lib/models/dynamics/up.dart index cfd1fa7d..ce445acb 100644 --- a/lib/models/dynamics/up.dart +++ b/lib/models/dynamics/up.dart @@ -2,15 +2,22 @@ class FollowUpModel { FollowUpModel({ this.liveUsers, this.upList, + this.liveList, }); LiveUsers? liveUsers; List? upList; + List? liveList; FollowUpModel.fromJson(Map json) { liveUsers = json['live_users'] != null ? LiveUsers.fromJson(json['live_users']) : null; + liveList = json['live_users'] != null + ? json['live_users']['items'] + .map((e) => LiveUserItem.fromJson(e)) + .toList() + : []; upList = json['up_list'] != null ? json['up_list'].map((e) => UpItem.fromJson(e)).toList() : []; diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 1e9f2da4..1d415aa4 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -249,8 +249,8 @@ class DynamicsController extends GetxController { return {'status': false, 'msg': '账号未登录'}; } if (type == 'init') { - upData.value.upList = []; - upData.value.liveUsers = LiveUsers(); + upData.value.upList = []; + upData.value.liveList = []; } var res = await DynamicsHttp.followUp(); if (res['status']) { @@ -271,7 +271,6 @@ class DynamicsController extends GetxController { onRefresh() async { page = 1; - print('onRefresh'); await queryFollowUp(); await queryFollowDynamic(); } diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index aa0bdd02..ef0c3731 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -31,8 +31,8 @@ class _UpPanelState extends State { void initState() { super.initState(); upList = widget.upData!.upList!; - if (widget.upData!.liveUsers != null) { - liveList = widget.upData!.liveUsers!.items!; + if (widget.upData!.liveList!.isNotEmpty) { + liveList = widget.upData!.liveList!; } upList.insert( 0, @@ -55,7 +55,7 @@ class _UpPanelState extends State { floating: true, pinned: false, delegate: _SliverHeaderDelegate( - height: 126, + height: liveList.isNotEmpty || upList.isNotEmpty ? 126 : 0, child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, @@ -88,32 +88,36 @@ class _UpPanelState extends State { Container( height: 90, color: Theme.of(context).colorScheme.background, - child: Expanded( - child: ListView( - scrollDirection: Axis.horizontal, - controller: scrollController, - children: [ - const SizedBox(width: 10), - if (liveList.isNotEmpty) ...[ - for (int i = 0; i < liveList.length; i++) ...[ - upItemBuild(liveList[i], i) + child: Row( + children: [ + Flexible( + child: ListView( + scrollDirection: Axis.horizontal, + controller: scrollController, + children: [ + const SizedBox(width: 10), + if (liveList.isNotEmpty) ...[ + for (int i = 0; i < liveList.length; i++) ...[ + upItemBuild(liveList[i], i) + ], + VerticalDivider( + indent: 20, + endIndent: 40, + width: 26, + color: Theme.of(context) + .colorScheme + .primary + .withOpacity(0.5), + ), + ], + for (int i = 0; i < upList.length; i++) ...[ + upItemBuild(upList[i], i) + ], + const SizedBox(width: 10), ], - VerticalDivider( - indent: 20, - endIndent: 40, - width: 26, - color: Theme.of(context) - .colorScheme - .primary - .withOpacity(0.5), - ), - ], - for (int i = 0; i < upList.length; i++) ...[ - upItemBuild(upList[i], i) - ], - const SizedBox(width: 10), - ], - ), + ), + ), + ], ), ), Container( From ae33cbf7ca9c93cc93e0837f8ba5f59775804ec7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 13:02:43 +0800 Subject: [PATCH 38/67] =?UTF-8?q?fix:=20=E6=90=9C=E7=B4=A2=E6=A1=86?= =?UTF-8?q?=E9=BB=98=E8=AE=A4=E6=90=9C=E7=B4=A2=E8=AF=8D=E6=BA=A2=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/view.dart | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 020cb355..b0cef90b 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -415,13 +415,16 @@ class SearchBar extends StatelessWidget { ), const SizedBox(width: 10), Obx( - () => Text( - ctr!.defaultSearch.value, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: TextStyle(color: colorScheme.outline), + () => Expanded( + child: Text( + ctr!.defaultSearch.value, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: colorScheme.outline), + ), ), ), + const SizedBox(width: 15), ], ), ), From 4191cafe78e3b999f2ae74c2bb673767d2865f7d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 14:47:07 +0800 Subject: [PATCH 39/67] =?UTF-8?q?fix:=20=E6=8E=A8=E8=8D=90=E5=8D=A1?= =?UTF-8?q?=E7=89=87=E5=8D=95=E5=88=97=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_v.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 43dd05ca..0d96f7b7 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -231,6 +231,7 @@ class VideoContent extends StatelessWidget { const SizedBox(height: 2), VideoStat( videoItem: videoItem, + crossAxisCount: crossAxisCount, ), ], if (crossAxisCount == 1) const SizedBox(height: 4), @@ -294,6 +295,7 @@ class VideoContent extends StatelessWidget { ), VideoStat( videoItem: videoItem, + crossAxisCount: crossAxisCount, ), const Spacer(), ], @@ -317,10 +319,12 @@ class VideoContent extends StatelessWidget { class VideoStat extends StatelessWidget { final dynamic videoItem; + final int crossAxisCount; const VideoStat({ Key? key, required this.videoItem, + required this.crossAxisCount, }) : super(key: key); @override @@ -337,7 +341,7 @@ class VideoStat extends StatelessWidget { danmu: videoItem.stat.danmu, ), if (videoItem is RecVideoItemModel) ...[ - const Spacer(), + crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8), RichText( maxLines: 1, text: TextSpan( From f81f348a3e1be25cf8ff4cd0befa92c762948e3b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 15:51:24 +0800 Subject: [PATCH 40/67] =?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=E4=B8=8B=E6=8B=89=E5=88=B7?= =?UTF-8?q?=E6=96=B0=20issues=20#486?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/controller.dart | 1 + lib/pages/video/detail/reply/view.dart | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 91344fc1..06ce26ff 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -62,6 +62,7 @@ class VideoReplyController extends GetxController { noMore.value = ''; } if (noMore.value == '没有更多了') { + isLoadingMore = false; return; } final res = await ReplyHttp.replyList( diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index df8c75b1..b07a6168 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -134,13 +134,13 @@ class _VideoReplyPanelState extends State super.build(context); return RefreshIndicator( onRefresh: () async { - _videoReplyController.currentPage = 0; - return await _videoReplyController.queryReplyList(); + return await _videoReplyController.queryReplyList(type: 'init'); }, child: Stack( children: [ CustomScrollView( controller: scrollController, + physics: const AlwaysScrollableScrollPhysics(), key: const PageStorageKey('评论'), slivers: [ SliverPersistentHeader( From 370dcaf419a72495d650fcc4c91c876875f5352c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 16:05:15 +0800 Subject: [PATCH 41/67] =?UTF-8?q?mod:=20=E7=94=A8=E6=88=B7=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E7=8A=B6=E6=80=81msg=E5=8F=96=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/webview/controller.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index 37600ea7..f26f4284 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -131,13 +131,13 @@ class WebviewController extends GetxController { Get.back(); } else { // 获取用户信息失败 - SmartDialog.showToast(result.msg); - Clipboard.setData(ClipboardData(text: result.msg.toString())); + SmartDialog.showToast(result['msg']); + Clipboard.setData(ClipboardData(text: result['msg'])); } } catch (e) { SmartDialog.showNotify(msg: e.toString(), notifyType: NotifyType.warning); content = content + e.toString(); + Clipboard.setData(ClipboardData(text: content)); } - Clipboard.setData(ClipboardData(text: content)); } } From a3ce15bd9ef3e33e7df1ff139301603d76ceccce Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 22:27:02 +0800 Subject: [PATCH 42/67] mod: CI format --- .github/workflows/CI.yml | 36 ++++++++++++------------------------ 1 file changed, 12 insertions(+), 24 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 231c61f5..2fa0a6ef 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -1,16 +1,16 @@ -name: CI +name: Pilipala Beta on: workflow_dispatch: push: branches: - - 'main' + - "main" paths-ignore: - - '**.md' - - '**.txt' - - '.github/**' - - '.idea/**' - - '!.github/workflows/**' + - "**.md" + - "**.txt" + - ".github/**" + - ".idea/**" + - "!.github/workflows/**" jobs: update_version: @@ -53,16 +53,16 @@ jobs: run: | # 读取版本号 VERSION=$(yq e .version pubspec.yaml | cut -d "+" -f 1) - + # 获取GitHub Actions的run_number #RUN_NUMBER=${{ github.run_number }} - + # 构建新版本号 NEW_VERSION=$VERSION-beta.${{ steps.get-first-parent-commit-count.outputs.count }} - + # 输出新版本号 echo "New version: $NEW_VERSION" - + # 设置新版本号为输出变量 echo "new_version=$NEW_VERSION" >>$GITHUB_OUTPUT @@ -189,22 +189,10 @@ jobs: - android - iOS steps: - - uses: actions/download-artifact@v3 with: name: Pilipala-CI path: ./Pilipala-CI - - # - name: Upload Pre-release - # uses: ncipollo/release-action@v1 - # with: - # name: ${{ needs.update_version.outputs.new_version }} - # token: ${{ secrets.GIT_TOKEN }} - # commit: main - # tag: ${{ needs.update_version.outputs.new_version }} - # prerelease: true - # allowUpdates: true - # artifacts: Pilipala-CI/* - name: 发送到Telegram频道 uses: xireiki/channel-post@v1.0.7 @@ -217,4 +205,4 @@ jobs: method: sendFile path: Pilipala-CI/* parse_mode: Markdown - context: "*Pre-release: v${{ needs.update_version.outputs.new_version }}*\n${{ needs.update_version.outputs.last_commit }}" + context: "*Beta版本: v${{ needs.update_version.outputs.new_version }}*\n更新内容: [${{ needs.update_version.outputs.last_commit }}](${{ github.event.head_commit.url }})" From 0e888537e83dcf806bb1cbcc415ded7185312176 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 2 Mar 2024 22:37:56 +0800 Subject: [PATCH 43/67] mod: yml rename --- .github/workflows/{CI.yml => beta_ci.yml} | 10 +++++----- .github/workflows/{main.yml => release_ci.yml} | 0 2 files changed, 5 insertions(+), 5 deletions(-) rename .github/workflows/{CI.yml => beta_ci.yml} (97%) rename .github/workflows/{main.yml => release_ci.yml} (100%) diff --git a/.github/workflows/CI.yml b/.github/workflows/beta_ci.yml similarity index 97% rename from .github/workflows/CI.yml rename to .github/workflows/beta_ci.yml index 2fa0a6ef..e839aca1 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/beta_ci.yml @@ -131,7 +131,7 @@ jobs: - name: 上传 uses: actions/upload-artifact@v3 with: - name: Pilipala-CI + name: Pilipala-Beta path: | build/app/outputs/flutter-apk/Pili-*.apk @@ -177,7 +177,7 @@ jobs: uses: actions/upload-artifact@v3 with: if-no-files-found: error - name: Pilipala-CI + name: Pilipala-Beta path: | build/Pili-*.ipa @@ -191,8 +191,8 @@ jobs: steps: - uses: actions/download-artifact@v3 with: - name: Pilipala-CI - path: ./Pilipala-CI + name: Pilipala-Beta + path: ./Pilipala-Beta - name: 发送到Telegram频道 uses: xireiki/channel-post@v1.0.7 @@ -203,6 +203,6 @@ jobs: api_id: ${{ secrets.TELEGRAM_API_ID }} api_hash: ${{ secrets.TELEGRAM_API_HASH }} method: sendFile - path: Pilipala-CI/* + path: Pilipala-Beta/* parse_mode: Markdown context: "*Beta版本: v${{ needs.update_version.outputs.new_version }}*\n更新内容: [${{ needs.update_version.outputs.last_commit }}](${{ github.event.head_commit.url }})" diff --git a/.github/workflows/main.yml b/.github/workflows/release_ci.yml similarity index 100% rename from .github/workflows/main.yml rename to .github/workflows/release_ci.yml From 75f569cb79c25c284abdb2d98c6a669c06ab4eb7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 3 Mar 2024 00:15:16 +0800 Subject: [PATCH 44/67] =?UTF-8?q?mod:=20=E5=90=88=E9=9B=86=E5=B8=83?= =?UTF-8?q?=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/widgets/bangumi_panel.dart | 83 +++++++++++------- .../detail/introduction/widgets/page.dart | 84 +++++++++++-------- .../detail/introduction/widgets/season.dart | 68 +++++++++------ 3 files changed, 147 insertions(+), 88 deletions(-) diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 5996d6c8..05889f16 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -65,6 +65,45 @@ class _BangumiPanelState extends State { super.dispose(); } + Widget buildPageListItem( + EpisodeItem page, + int index, + bool isCurrentIndex, + ) { + Color primary = Theme.of(context).colorScheme.primary; + return ListTile( + onTap: () { + Get.back(); + setState(() { + changeFucCall(page, index); + }); + }, + dense: false, + leading: isCurrentIndex + ? Image.asset( + 'assets/images/live.gif', + color: primary, + height: 12, + ) + : null, + title: Text( + '第${index + 1}话 ${page.longTitle!}', + style: TextStyle( + fontSize: 14, + color: isCurrentIndex + ? primary + : Theme.of(context).colorScheme.onSurface, + ), + ), + trailing: page.badge != null + ? Image.asset( + 'assets/images/big-vip.png', + height: 20, + ) + : const SizedBox(), + ); + } + void showBangumiPanel() { showBottomSheet( context: context, @@ -106,37 +145,21 @@ class _BangumiPanelState extends State { child: Material( child: ScrollablePositionedList.builder( itemCount: widget.pages.length, - itemBuilder: (BuildContext context, int index) => - ListTile( - onTap: () { - setState(() { - changeFucCall(widget.pages[index], index); - }); - }, - dense: false, - leading: index == currentIndex - ? Image.asset( - 'assets/images/live.gif', - color: Theme.of(context).colorScheme.primary, - height: 12, + itemBuilder: (BuildContext context, int index) { + bool isLastItem = index == widget.pages.length - 1; + bool isCurrentIndex = currentIndex == index; + return isLastItem + ? SizedBox( + height: + MediaQuery.of(context).padding.bottom + + 20, ) - : null, - title: Text( - '第${index + 1}话 ${widget.pages[index].longTitle!}', - style: TextStyle( - fontSize: 14, - color: index == currentIndex - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurface, - ), - ), - trailing: widget.pages[index].badge != null - ? Image.asset( - 'assets/images/big-vip.png', - height: 20, - ) - : const SizedBox(), - ), + : buildPageListItem( + widget.pages[index], + index, + isCurrentIndex, + ); + }, itemScrollController: itemScrollController, ), ), diff --git a/lib/pages/video/detail/introduction/widgets/page.dart b/lib/pages/video/detail/introduction/widgets/page.dart index 2ba7f507..8d296050 100644 --- a/lib/pages/video/detail/introduction/widgets/page.dart +++ b/lib/pages/video/detail/introduction/widgets/page.dart @@ -56,6 +56,37 @@ class _PagesPanelState extends State { super.dispose(); } + Widget buildEpisodeListItem( + Part episode, + int index, + bool isCurrentIndex, + ) { + Color primary = Theme.of(context).colorScheme.primary; + return ListTile( + onTap: () { + changeFucCall(episode, index); + Get.back(); + }, + dense: false, + leading: isCurrentIndex + ? Image.asset( + 'assets/images/live.gif', + color: primary, + height: 12, + ) + : null, + title: Text( + episode.pagePart!, + style: TextStyle( + fontSize: 14, + color: isCurrentIndex + ? primary + : Theme.of(context).colorScheme.onSurface, + ), + ), + ); + } + @override Widget build(BuildContext context) { return Column( @@ -131,39 +162,25 @@ class _PagesPanelState extends State { child: Material( child: ListView.builder( controller: _scrollController, - itemCount: episodes.length, + itemCount: episodes.length + 1, itemBuilder: (BuildContext context, int index) { - return ListTile( - onTap: () { - changeFucCall( - episodes[index], index); - Get.back(); - }, - dense: false, - leading: index == currentIndex - ? Image.asset( - 'assets/images/live.gif', - color: Theme.of(context) - .colorScheme - .primary, - height: 12, - ) - : null, - title: Text( - episodes[index].pagePart!, - style: TextStyle( - fontSize: 14, - color: index == currentIndex - ? Theme.of(context) - .colorScheme - .primary - : Theme.of(context) - .colorScheme - .onSurface, - ), - ), - ); + bool isLastItem = + index == episodes.length; + bool isCurrentIndex = + currentIndex == index; + return isLastItem + ? SizedBox( + height: MediaQuery.of(context) + .padding + .bottom + + 20, + ) + : buildEpisodeListItem( + episodes[index], + index, + isCurrentIndex, + ); }, ), ), @@ -192,6 +209,7 @@ class _PagesPanelState extends State { itemCount: widget.pages.length, itemExtent: 150, itemBuilder: (BuildContext context, int i) { + bool isCurrentIndex = currentIndex == i; return Container( width: 150, margin: const EdgeInsets.only(right: 10), @@ -206,7 +224,7 @@ class _PagesPanelState extends State { vertical: 8, horizontal: 8), child: Row( children: [ - if (i == currentIndex) ...[ + if (isCurrentIndex) ...[ Image.asset( 'assets/images/live.gif', color: Theme.of(context).colorScheme.primary, @@ -220,7 +238,7 @@ class _PagesPanelState extends State { maxLines: 1, style: TextStyle( fontSize: 13, - color: i == currentIndex + color: isCurrentIndex ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.onSurface), overflow: TextOverflow.ellipsis, diff --git a/lib/pages/video/detail/introduction/widgets/season.dart b/lib/pages/video/detail/introduction/widgets/season.dart index 9e5dd34f..c6a94398 100644 --- a/lib/pages/video/detail/introduction/widgets/season.dart +++ b/lib/pages/video/detail/introduction/widgets/season.dart @@ -80,6 +80,34 @@ class _SeasonPanelState extends State { super.dispose(); } + Widget buildEpisodeListItem( + EpisodeItem episode, + int index, + bool isCurrentIndex, + ) { + Color primary = Theme.of(context).colorScheme.primary; + return ListTile( + onTap: () => changeFucCall(episode, index), + dense: false, + leading: isCurrentIndex + ? Image.asset( + 'assets/images/live.gif', + color: primary, + height: 12, + ) + : null, + title: Text( + episode.title!, + style: TextStyle( + fontSize: 14, + color: isCurrentIndex + ? primary + : Theme.of(context).colorScheme.onSurface, + ), + ), + ); + } + @override Widget build(BuildContext context) { return Builder(builder: (BuildContext context) { @@ -134,32 +162,22 @@ class _SeasonPanelState extends State { child: Material( child: ScrollablePositionedList.builder( itemCount: episodes.length, - itemBuilder: (BuildContext context, int index) => - ListTile( - onTap: () => - changeFucCall(episodes[index], index), - dense: false, - leading: index == currentIndex - ? Image.asset( - 'assets/images/live.gif', - color: Theme.of(context) - .colorScheme - .primary, - height: 12, + itemBuilder: (BuildContext context, int index) { + bool isLastItem = index == episodes.length - 1; + bool isCurrentIndex = currentIndex == index; + return isLastItem + ? SizedBox( + height: MediaQuery.of(context) + .padding + .bottom + + 20, ) - : null, - title: Text( - episodes[index].title!, - style: TextStyle( - fontSize: 14, - color: index == currentIndex - ? Theme.of(context).colorScheme.primary - : Theme.of(context) - .colorScheme - .onSurface, - ), - ), - ), + : buildEpisodeListItem( + episodes[index], + index, + isCurrentIndex, + ); + }, itemScrollController: itemScrollController, ), ), From 800f714f4ac85e09bdb8b20c1896ff4270dabee7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 3 Mar 2024 00:52:47 +0800 Subject: [PATCH 45/67] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5appBar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 76 ++++++++++++++++++++------------ 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index b82d3e96..4bedcb37 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -5,6 +5,7 @@ import 'dart:ui'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; @@ -228,7 +229,6 @@ class _VideoDetailPageState extends State plPlayerController!.removeStatusLister(playerListener); plPlayerController!.pause(); } - print('🐶🐶'); setState(() => isShowing = false); super.didPushNext(); } @@ -406,7 +406,7 @@ class _VideoDetailPageState extends State ), ); } else { - return const SizedBox(); + return buildCustomAppBar(); } }, ), @@ -449,33 +449,7 @@ class _VideoDetailPageState extends State 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) - ], - ), + child: buildCustomAppBar(), ), Positioned( right: 12, @@ -657,4 +631,48 @@ class _VideoDetailPageState extends State return childWhenDisabled; } } + + Widget buildCustomAppBar() { + return AppBar( + backgroundColor: Colors.transparent, // 使背景透明 + foregroundColor: Colors.white, + elevation: 0, + scrolledUnderElevation: 0, + primary: false, + centerTitle: false, + automaticallyImplyLeading: false, + titleSpacing: 0, + title: Container( + height: kToolbarHeight, + padding: const EdgeInsets.symmetric(horizontal: 14), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + Colors.transparent, + Colors.black54, + ], + tileMode: TileMode.mirror, + )), + child: Row( + children: [ + ComBtn( + icon: const Icon(FontAwesomeIcons.arrowLeft, size: 15), + fuc: () => Get.back(), + ), + const Spacer(), + ComBtn( + icon: const Icon(Icons.history_outlined, size: 22), + fuc: () async { + var res = await UserHttp.toViewLater( + bvid: videoDetailController.bvid); + SmartDialog.showToast(res['msg']); + }, + ), + ], + ), + ), + ); + } } From caca16a9574df11de0301075a07f7ad51b5de73d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 3 Mar 2024 09:57:41 +0800 Subject: [PATCH 46/67] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2upPanel=E4=B8=8D=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/dynamics/up.dart | 21 ++++++++++++++++ lib/pages/dynamics/controller.dart | 4 +++ lib/pages/dynamics/widgets/up_panel.dart | 32 ++++++------------------ 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/lib/models/dynamics/up.dart b/lib/models/dynamics/up.dart index ce445acb..9bb82f70 100644 --- a/lib/models/dynamics/up.dart +++ b/lib/models/dynamics/up.dart @@ -3,11 +3,13 @@ class FollowUpModel { this.liveUsers, this.upList, this.liveList, + this.myInfo, }); LiveUsers? liveUsers; List? upList; List? liveList; + MyInfo? myInfo; FollowUpModel.fromJson(Map json) { liveUsers = json['live_users'] != null @@ -21,6 +23,7 @@ class FollowUpModel { upList = json['up_list'] != null ? json['up_list'].map((e) => UpItem.fromJson(e)).toList() : []; + myInfo = json['my_info'] != null ? MyInfo.fromJson(json['my_info']) : null; } } @@ -100,3 +103,21 @@ class UpItem { uname = json['uname']; } } + +class MyInfo { + MyInfo({ + this.face, + this.mid, + this.name, + }); + + String? face; + int? mid; + String? name; + + MyInfo.fromJson(Map json) { + face = json['face']; + mid = json['mid']; + name = json['name']; + } +} diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 1d415aa4..b7676663 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -258,6 +258,10 @@ class DynamicsController extends GetxController { if (upData.value.upList!.isEmpty) { mid.value = -1; } + upData.value.upList!.insertAll(0, [ + UpItem(face: '', uname: '全部动态', mid: -1), + UpItem(face: userInfo.face, uname: '我', mid: userInfo.mid), + ]); } return res; } diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index ef0c3731..fd0ae642 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -1,16 +1,14 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/dynamics/up.dart'; import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/pages/dynamics/controller.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; class UpPanel extends StatefulWidget { - final FollowUpModel? upData; + final FollowUpModel upData; const UpPanel(this.upData, {Key? key}) : super(key: key); @override @@ -24,33 +22,17 @@ class _UpPanelState extends State { List upList = []; List liveList = []; static const itemPadding = EdgeInsets.symmetric(horizontal: 5, vertical: 0); - Box userInfoCache = GStrorage.userInfo; - var userInfo; + late MyInfo userInfo; - @override - void initState() { - super.initState(); - upList = widget.upData!.upList!; - if (widget.upData!.liveList!.isNotEmpty) { - liveList = widget.upData!.liveList!; - } - upList.insert( - 0, - UpItem(face: '', uname: '全部动态', mid: -1), - ); - userInfo = userInfoCache.get('userInfoCache'); - upList.insert( - 1, - UpItem( - face: userInfo.face, - uname: '我', - mid: userInfo.mid, - ), - ); + void listFormat() { + userInfo = widget.upData.myInfo!; + upList = widget.upData.upList!; + liveList = widget.upData.liveList!; } @override Widget build(BuildContext context) { + listFormat(); return SliverPersistentHeader( floating: true, pinned: false, From 481fa0d934b8081bda433b85c2c4bbf71d49d644 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 3 Mar 2024 11:09:52 +0800 Subject: [PATCH 47/67] =?UTF-8?q?feat:=20=E9=BB=98=E8=AE=A4=E5=90=AF?= =?UTF-8?q?=E5=8A=A8=E9=A1=B5=E8=AE=BE=E7=BD=AE=20issues=20#483?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/nav_bar_config.dart | 43 +++++++++++++++++++ lib/pages/main/controller.dart | 44 +++----------------- lib/pages/setting/controller.dart | 24 +++++++++++ lib/pages/setting/pages/home_tabbar_set.dart | 4 -- lib/pages/setting/style_setting.dart | 11 ++++- lib/utils/storage.dart | 3 +- 6 files changed, 84 insertions(+), 45 deletions(-) create mode 100644 lib/models/common/nav_bar_config.dart diff --git a/lib/models/common/nav_bar_config.dart b/lib/models/common/nav_bar_config.dart new file mode 100644 index 00000000..2d195e8b --- /dev/null +++ b/lib/models/common/nav_bar_config.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +const defaultNavigationBars = [ + { + 'id': 0, + 'icon': Icon( + Icons.home_outlined, + size: 21, + ), + 'selectIcon': Icon( + Icons.home, + size: 21, + ), + 'label': "首页", + 'count': 0, + }, + { + 'id': 1, + 'icon': Icon( + Icons.motion_photos_on_outlined, + size: 21, + ), + 'selectIcon': Icon( + Icons.motion_photos_on, + size: 21, + ), + 'label': "动态", + 'count': 0, + }, + { + 'id': 2, + 'icon': Icon( + Icons.video_collection_outlined, + size: 20, + ), + 'selectIcon': Icon( + Icons.video_collection, + size: 21, + ), + 'label': "媒体库", + 'count': 0, + } +]; diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 6f33d9cd..f3bcb75a 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -12,6 +12,7 @@ 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'; +import '../../models/common/nav_bar_config.dart'; class MainController extends GetxController { List pages = [ @@ -19,44 +20,7 @@ class MainController extends GetxController { const DynamicsPage(), const MediaPage(), ]; - RxList navigationBars = [ - { - 'icon': const Icon( - Icons.home_outlined, - size: 21, - ), - 'selectIcon': const Icon( - Icons.home, - size: 21, - ), - 'label': "首页", - 'count': 0, - }, - { - 'icon': const Icon( - Icons.motion_photos_on_outlined, - size: 21, - ), - 'selectIcon': const Icon( - Icons.motion_photos_on, - size: 21, - ), - 'label': "动态", - 'count': 0, - }, - { - 'icon': const Icon( - Icons.video_collection_outlined, - size: 20, - ), - 'selectIcon': const Icon( - Icons.video_collection, - size: 21, - ), - 'label': "媒体库", - 'count': 0, - } - ].obs; + RxList navigationBars = defaultNavigationBars.obs; final StreamController bottomBarStream = StreamController.broadcast(); Box setting = GStrorage.setting; @@ -75,6 +39,10 @@ class MainController extends GetxController { Utils.checkUpdata(); } hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true); + int defaultHomePage = + setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0) as int; + selectedIndex = defaultNavigationBars + .indexWhere((item) => item['id'] == defaultHomePage); var userInfo = userInfoCache.get('userInfoCache'); userLogin.value = userInfo != null; dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( diff --git a/lib/pages/setting/controller.dart b/lib/pages/setting/controller.dart index 2e6680e5..1fbd7efb 100644 --- a/lib/pages/setting/controller.dart +++ b/lib/pages/setting/controller.dart @@ -8,6 +8,7 @@ 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 '../../models/common/nav_bar_config.dart'; import '../main/index.dart'; import 'widgets/select_dialog.dart'; @@ -23,6 +24,7 @@ class SettingController extends GetxController { Rx themeType = ThemeType.system.obs; var userInfo; Rx dynamicBadgeType = DynamicBadgeMode.number.obs; + RxInt defaultHomePage = 0.obs; @override void onInit() { @@ -40,6 +42,8 @@ class SettingController extends GetxController { dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( SettingBoxKey.dynamicBadgeMode, defaultValue: DynamicBadgeMode.number.code)]; + defaultHomePage.value = + setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0); } loginOut() async { @@ -110,4 +114,24 @@ class SettingController extends GetxController { SmartDialog.showToast('设置成功'); } } + + // 设置默认启动页 + seteDefaultHomePage(BuildContext context) async { + int? result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '首页启动页', + value: defaultHomePage.value, + values: defaultNavigationBars.map((e) { + return {'title': e['label'], 'value': e['id']}; + }).toList()); + }, + ); + if (result != null) { + defaultHomePage.value = result; + setting.put(SettingBoxKey.defaultHomePage, result); + SmartDialog.showToast('设置成功,重启生效'); + } + } } diff --git a/lib/pages/setting/pages/home_tabbar_set.dart b/lib/pages/setting/pages/home_tabbar_set.dart index 4cb3944c..63e87d02 100644 --- a/lib/pages/setting/pages/home_tabbar_set.dart +++ b/lib/pages/setting/pages/home_tabbar_set.dart @@ -40,10 +40,6 @@ class _TabbarSetPageState extends State { .where((i) => tabbarSort.contains((i['type'] as TabType).id)) .map((i) => (i['type'] as TabType).id) .toList(); - if (sortedTabbar.isEmpty) { - SmartDialog.showToast('请至少设置一项!'); - return; - } settingStorage.put(SettingBoxKey.tabbarSort, sortedTabbar); SmartDialog.showToast('保存成功,下次启动时生效'); } diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 849123e5..30b9a30f 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -12,6 +12,7 @@ import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import '../../models/common/dynamic_badge_mode.dart'; +import '../../models/common/nav_bar_config.dart'; import 'controller.dart'; import 'widgets/switch_item.dart'; @@ -29,7 +30,6 @@ class _StyleSettingState extends State { Box setting = GStrorage.setting; late int picQuality; - late double toastOpacity; late ThemeType _tempThemeValue; late dynamic defaultCustomRows; @@ -37,7 +37,6 @@ class _StyleSettingState extends State { void initState() { super.initState(); picQuality = setting.get(SettingBoxKey.defaultPicQa, defaultValue: 10); - toastOpacity = setting.get(SettingBoxKey.defaultToastOp, defaultValue: 1.0); _tempThemeValue = settingController.themeType.value; defaultCustomRows = setting.get(SettingBoxKey.customRows, defaultValue: 2); } @@ -267,6 +266,14 @@ class _StyleSettingState extends State { '当前主题:${colorSelectController.type.value == 0 ? '动态取色' : '指定颜色'}', style: subTitleStyle)), ), + ListTile( + dense: false, + onTap: () => settingController.seteDefaultHomePage(context), + title: Text('默认启动页', style: titleStyle), + subtitle: Obx(() => Text( + '当前启动页:${defaultNavigationBars.firstWhere((e) => e['id'] == settingController.defaultHomePage.value)['label']}', + style: subTitleStyle)), + ), ListTile( dense: false, onTap: () => Get.toNamed('/fontSizeSetting'), diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 44beb4e7..fea31d56 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -130,7 +130,8 @@ class SettingBoxKey { enableWordRe = 'enableWordRe', enableSearchWord = 'enableSearchWord', enableSystemProxy = 'enableSystemProxy', - enableAi = 'enableAi'; + enableAi = 'enableAi', + defaultHomePage = 'defaultHomePage'; /// 外观 static const String themeMode = 'themeMode', From 19f0b1b28f400ef3b9cd7c4c8ae45caf4b3a5bfe Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 3 Mar 2024 11:53:15 +0800 Subject: [PATCH 48/67] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E4=B8=93?= =?UTF-8?q?=E6=A0=8F=E9=87=8D=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/article_panel.dart | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/pages/dynamics/widgets/article_panel.dart b/lib/pages/dynamics/widgets/article_panel.dart index e68d966d..19707435 100644 --- a/lib/pages/dynamics/widgets/article_panel.dart +++ b/lib/pages/dynamics/widgets/article_panel.dart @@ -34,25 +34,25 @@ Widget articlePanel(item, context, {floor = 1}) { ), const SizedBox(height: 8), ], - Text( - item.modules.moduleDynamic.major.opus.title, - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontWeight: FontWeight.bold), - ), - const SizedBox(height: 2), - if (item.modules.moduleDynamic.major.opus.summary.text != - 'undefined') ...[ - Text( - item.modules.moduleDynamic.major.opus.summary.richTextNodes.first - .text, - maxLines: 4, - style: const TextStyle(height: 1.55), - overflow: TextOverflow.ellipsis, - ), - const SizedBox(height: 2), - ], + // Text( + // item.modules.moduleDynamic.major.opus.title, + // style: Theme.of(context) + // .textTheme + // .titleMedium! + // .copyWith(fontWeight: FontWeight.bold), + // ), + // const SizedBox(height: 2), + // if (item.modules.moduleDynamic.major.opus.summary.text != + // 'undefined') ...[ + // Text( + // item.modules.moduleDynamic.major.opus.summary.richTextNodes.first + // .text, + // maxLines: 4, + // style: const TextStyle(height: 1.55), + // overflow: TextOverflow.ellipsis, + // ), + // const SizedBox(height: 2), + // ], picWidget(item, context) ], ), From c20df8fd811bf578520b1c593b55c53ec91fda57 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 3 Mar 2024 12:22:10 +0800 Subject: [PATCH 49/67] fix: enable pip --- lib/pages/video/detail/view.dart | 63 ++++++++------------------------ pubspec.lock | 2 +- 2 files changed, 16 insertions(+), 49 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index febca105..6b9ef373 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -58,9 +58,7 @@ class _VideoDetailPageState extends State late bool autoExitFullcreen; late bool autoPlayEnable; late bool autoPiP; - final Floating floating = Floating(); - // 生命周期监听 - late final AppLifecycleListener _lifecycleListener; + late Floating floating; bool isShowing = true; @override @@ -91,8 +89,11 @@ class _VideoDetailPageState extends State videoSourceInit(); appbarStreamListen(); - lifecycleListener(); fullScreenStatusListener(); + if (Platform.isAndroid) { + floating = videoDetailController.floating!; + autoEnterPip(); + } } // 获取视频资源,初始化播放器 @@ -150,6 +151,10 @@ class _VideoDetailPageState extends State } } catch (_) {} } + if (Platform.isAndroid) { + floating.toggleAutoPip( + autoEnter: status == PlayerStatus.playing && autoPiP); + } } // 继续播放或重新播放 @@ -168,27 +173,6 @@ class _VideoDetailPageState extends State videoDetailController.isShowCover.value = false; } - // 生命周期监听 - void lifecycleListener() { - _lifecycleListener = AppLifecycleListener( - onResume: () => _handleTransition('resume'), - // 后台 - onInactive: () => _handleTransition('inactive'), - // 在Android和iOS端不生效 - onHide: () => _handleTransition('hide'), - onShow: () => _handleTransition('show'), - onPause: () => _handleTransition('pause'), - onRestart: () => _handleTransition('restart'), - onDetach: () => _handleTransition('detach'), - // 只作用于桌面端 - onExitRequested: () { - ScaffoldMessenger.maybeOf(context) - ?.showSnackBar(const SnackBar(content: Text("拦截应用退出"))); - return Future.value(AppExitResponse.cancel); - }, - ); - } - void fullScreenStatusListener() { plPlayerController?.isFullScreen.listen((bool isFullScreen) { if (isFullScreen) { @@ -208,8 +192,10 @@ class _VideoDetailPageState extends State videoDetailController.floating!.dispose(); } videoPlayerServiceHandler.onVideoDetailDispose(); - floating.dispose(); - _lifecycleListener.dispose(); + if (Platform.isAndroid) { + floating.toggleAutoPip(autoEnter: false); + floating.dispose(); + } super.dispose(); } @@ -262,29 +248,10 @@ class _VideoDetailPageState extends State .subscribe(this, ModalRoute.of(context)! as PageRoute); } - void _handleTransition(String name) { - switch (name) { - case 'inactive': - if (plPlayerController != null && - playerStatus == PlayerStatus.playing) { - autoEnterPip(); - } - break; - } - } - void autoEnterPip() { final String routePath = Get.currentRoute; - final bool isPortrait = - MediaQuery.of(context).orientation == Orientation.portrait; - - /// TODO 横屏全屏状态下误触pip - if (autoPiP && routePath.startsWith('/video') && isPortrait) { - floating.enable( - aspectRatio: Rational( - videoDetailController.data.dash!.video!.first.width!, - videoDetailController.data.dash!.video!.first.height!, - )); + if (autoPiP && routePath.startsWith('/video')) { + floating.toggleAutoPip(autoEnter: autoPiP); } } diff --git a/pubspec.lock b/pubspec.lock index f5d63ca9..bfef5f26 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -502,7 +502,7 @@ packages: description: path: "." ref: main - resolved-ref: d2d8421c4d80f6113f832404109853684721e11a + resolved-ref: "8e89669eb9341f9980265306e24ef96fdbd3fd08" url: "https://github.com/guozhigq/floating.git" source: git version: "2.0.1" From 8109314aaf4e70606bae6b14c147714c612542e4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 3 Mar 2024 15:20:59 +0800 Subject: [PATCH 50/67] =?UTF-8?q?opt:=20url=20scheme=E4=BC=98=E5=8C=96=20i?= =?UTF-8?q?ssues=20#581?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/app/src/main/AndroidManifest.xml | 4 + lib/utils/app_scheme.dart | 118 ++++++++++++++++------- lib/utils/id_utils.dart | 5 +- 3 files changed, 88 insertions(+), 39 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index d82845fe..c52d8447 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -223,6 +223,10 @@ android:pathPattern="/mobile/video/.*" /> + + diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 6820050c..bb9d556f 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -5,6 +5,7 @@ import 'package:get/get.dart'; import '../http/search.dart'; import '../models/common/search_type.dart'; import 'id_utils.dart'; +import 'url_utils.dart'; import 'utils.dart'; class PiliSchame { @@ -38,23 +39,16 @@ class PiliSchame { final String path = value.path; if (scheme == 'bilibili') { - // bilibili://root if (host == 'root') { Navigator.popUntil( Get.context!, (Route route) => route.isFirst); - } - - // bilibili://space/{uid} - else if (host == 'space') { + } else if (host == 'space') { final String mid = path.split('/').last; Get.toNamed( '/member?mid=$mid', arguments: {'face': null}, ); - } - - // bilibili://video/{aid} - else if (host == 'video') { + } else if (host == 'video') { String pathQuery = path.split('/').last; final numericRegex = RegExp(r'^[0-9]+$'); if (numericRegex.hasMatch(pathQuery)) { @@ -68,24 +62,16 @@ class PiliSchame { } else { SmartDialog.showToast('投稿匹配失败'); } - } - - // bilibili://live/{roomid} - else if (host == 'live') { + } else if (host == 'live') { final String roomId = path.split('/').last; Get.toNamed('/liveRoom?roomid=$roomId', arguments: {'liveItem': null, 'heroTag': roomId}); - } - - // bilibili://bangumi/season/${ssid} - else if (host == 'bangumi') { + } else if (host == 'bangumi') { if (path.startsWith('/season')) { final String seasonId = path.split('/').last; - _bangumiPush(int.parse(seasonId)); + _bangumiPush(int.parse(seasonId), null); } - } - // 专栏 bilibili://opus/detail/883089655985078289 - else if (host == 'opus') { + } else if (host == 'opus') { if (path.startsWith('/detail')) { var opusId = path.split('/').last; Get.toNamed( @@ -101,6 +87,9 @@ class PiliSchame { Get.toNamed('/searchResult', parameters: {'keyword': ''}); } } + if (scheme == 'https') { + _fullPathPush(value); + } } // 投稿跳转 @@ -131,10 +120,10 @@ class PiliSchame { } // 番剧跳转 - static Future _bangumiPush(int seasonId) async { + static Future _bangumiPush(int? seasonId, int? epId) async { SmartDialog.showLoading(msg: '获取中...'); try { - var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: null); + var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId); if (result['status']) { var bangumiDetail = result['data']; final int cid = bangumiDetail.episodes!.first.cid; @@ -151,6 +140,8 @@ class PiliSchame { }, ), ); + } else { + SmartDialog.showToast(result['msg']); } } catch (e) { SmartDialog.showToast('番剧获取失败:$e'); @@ -163,29 +154,67 @@ class PiliSchame { // final String scheme = value.scheme!; final String host = value.host!; final String? path = value.path; - // Map query = value.query!; - if (host.startsWith('live.bilibili')) { + Map? query = value.query; + RegExp regExp = RegExp(r'^(www\.)?m?\.(bilibili\.com)$'); + if (regExp.hasMatch(host)) { + print('bilibili.com'); + } else if (host.contains('live')) { int roomId = int.parse(path!.split('/').last); - // print('直播'); - Get.toNamed('/liveRoom?roomid=$roomId', - arguments: {'liveItem': null, 'heroTag': roomId.toString()}); - return; - } - if (host.startsWith('space.bilibili')) { - print('个人空间'); + Get.toNamed( + '/liveRoom?roomid=$roomId', + arguments: {'liveItem': null, 'heroTag': roomId.toString()}, + ); + } else if (host.contains('space')) { + var mid = path!.split('/').last; + Get.toNamed('/member?mid=$mid', arguments: {'face': ''}); return; + } else if (host == 'b23.tv') { + final String fullPath = 'https://$host$path'; + final String redirectUrl = await UrlUtils.parseRedirectUrl(fullPath); + final String pathSegment = Uri.parse(redirectUrl).path; + final String lastPathSegment = pathSegment.split('/').last; + final RegExp avRegex = RegExp(r'^[aA][vV]\d+', caseSensitive: false); + if (avRegex.hasMatch(lastPathSegment)) { + final Map map = + IdUtils.matchAvorBv(input: lastPathSegment); + if (map.containsKey('AV')) { + _videoPush(map['AV']! as int, null); + } else if (map.containsKey('BV')) { + _videoPush(null, map['BV'] as String); + } else { + SmartDialog.showToast('投稿匹配失败'); + } + } else if (lastPathSegment.startsWith('ep')) { + _handleEpisodePath(lastPathSegment, redirectUrl); + } else if (lastPathSegment.startsWith('ss')) { + _handleSeasonPath(lastPathSegment, redirectUrl); + } else if (lastPathSegment.startsWith('BV')) { + UrlUtils.matchUrlPush( + lastPathSegment, + '', + redirectUrl, + ); + } else { + Get.toNamed( + '/webview', + parameters: {'url': redirectUrl, 'type': 'url', 'pageTitle': ''}, + ); + } } if (path != null) { - final String area = path.split('/')[1]; + final String area = path.split('/').last; switch (area) { case 'bangumi': - // print('番剧'); - final String seasonId = path.split('/').last; - _bangumiPush(matchNum(seasonId).first); + print('番剧'); + if (area.startsWith('ep')) { + _bangumiPush(null, matchNum(area).first); + } else if (area.startsWith('ss')) { + _bangumiPush(matchNum(area).first, null); + } break; case 'video': - // print('投稿'); + print('投稿'); final Map map = IdUtils.matchAvorBv(input: path); if (map.containsKey('AV')) { _videoPush(map['AV']! as int, null); @@ -200,6 +229,7 @@ class PiliSchame { break; case 'space': print('个人空间'); + Get.toNamed('/member?mid=$area', arguments: {'face': ''}); break; } } @@ -211,4 +241,18 @@ class PiliSchame { return matches.map((Match match) => int.parse(match.group(0)!)).toList(); } + + static void _handleEpisodePath(String lastPathSegment, String redirectUrl) { + final String seasonId = _extractIdFromPath(lastPathSegment); + _bangumiPush(null, matchNum(seasonId).first); + } + + static void _handleSeasonPath(String lastPathSegment, String redirectUrl) { + final String seasonId = _extractIdFromPath(lastPathSegment); + _bangumiPush(matchNum(seasonId).first, null); + } + + static String _extractIdFromPath(String lastPathSegment) { + return lastPathSegment.split('/').last; + } } diff --git a/lib/utils/id_utils.dart b/lib/utils/id_utils.dart index 906f6348..7fefc268 100644 --- a/lib/utils/id_utils.dart +++ b/lib/utils/id_utils.dart @@ -68,8 +68,9 @@ class IdUtils { if (input == null || input.isEmpty) { return result; } - final RegExp bvRegex = RegExp(r'BV[0-9A-Za-z]{10}', caseSensitive: false); - final RegExp avRegex = RegExp(r'AV\d+', caseSensitive: false); + final RegExp bvRegex = + RegExp(r'[bB][vV][0-9A-Za-z]{10}', caseSensitive: false); + final RegExp avRegex = RegExp(r'[aA][vV]\d+', caseSensitive: false); final Iterable bvMatches = bvRegex.allMatches(input); final Iterable avMatches = avRegex.allMatches(input); From 83b0ff02e4ddbd62d06dad8f54721ad3cecbf833 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 3 Mar 2024 18:32:13 +0800 Subject: [PATCH 51/67] =?UTF-8?q?fix:=20=E5=9B=BE=E7=89=87=E9=A2=84?= =?UTF-8?q?=E8=A7=88=E6=94=BE=E5=A4=A7=E3=80=81=E5=8F=96=E6=B6=88=E4=B8=8B?= =?UTF-8?q?=E6=BB=91=E5=85=B3=E9=97=AD=E5=9B=BE=E7=89=87=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/preview/view.dart | 272 ++++++++++++++++++------------------ 1 file changed, 138 insertions(+), 134 deletions(-) diff --git a/lib/pages/preview/view.dart b/lib/pages/preview/view.dart index dcffc973..13868d37 100644 --- a/lib/pages/preview/view.dart +++ b/lib/pages/preview/view.dart @@ -135,115 +135,103 @@ class _ImagePreviewState extends State ), body: Stack( children: [ - DismissiblePage( - backgroundColor: Colors.transparent, - onDismissed: () { - Navigator.of(context).pop(); - }, - // Note that scrollable widget inside DismissiblePage might limit the functionality - // If scroll direction matches DismissiblePage direction - direction: DismissiblePageDismissDirection.down, - disabled: _dismissDisabled, - isFullScreen: true, - child: GestureDetector( - onLongPress: () => onOpenMenu(), - child: ExtendedImageGesturePageView.builder( - controller: ExtendedPageController( - initialPage: _previewController.initialPage.value, - pageSpacing: 0, - ), - onPageChanged: (int index) => - _previewController.onChange(index), - canScrollPage: (GestureDetails? gestureDetails) => - gestureDetails!.totalScale! <= 1.0, - itemCount: widget.imgList!.length, - itemBuilder: (BuildContext context, int index) { - return ExtendedImage.network( - widget.imgList![index], - fit: BoxFit.contain, - mode: ExtendedImageMode.gesture, - onDoubleTap: (ExtendedImageGestureState state) { - final Offset? pointerDownPosition = - state.pointerDownPosition; - final double? begin = state.gestureDetails!.totalScale; - double end; - - //remove old - _doubleClickAnimation - ?.removeListener(_doubleClickAnimationListener); - - //stop pre - _doubleClickAnimationController.stop(); - - //reset to use - _doubleClickAnimationController.reset(); - - if (begin == doubleTapScales[0]) { - setState(() { - _dismissDisabled = true; - }); - end = doubleTapScales[1]; - } else { - setState(() { - _dismissDisabled = false; - }); - end = doubleTapScales[0]; - } - - _doubleClickAnimationListener = () { - state.handleDoubleTap( - scale: _doubleClickAnimation!.value, - doubleTapPosition: pointerDownPosition); - }; - _doubleClickAnimation = _doubleClickAnimationController - .drive(Tween(begin: begin, end: end)); - - _doubleClickAnimation! - .addListener(_doubleClickAnimationListener); - - _doubleClickAnimationController.forward(); - }, - // ignore: body_might_complete_normally_nullable - loadStateChanged: (ExtendedImageState state) { - if (state.extendedImageLoadState == LoadState.loading) { - final ImageChunkEvent? loadingProgress = - state.loadingProgress; - final double? progress = - loadingProgress?.expectedTotalBytes != null - ? loadingProgress!.cumulativeBytesLoaded / - loadingProgress.expectedTotalBytes! - : null; - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - SizedBox( - width: 150.0, - child: LinearProgressIndicator( - value: progress, - color: Colors.white, - ), - ), - // const SizedBox(height: 10.0), - // Text('${((progress ?? 0.0) * 100).toInt()}%',), - ], - ), - ); - } - }, - initGestureConfigHandler: (ExtendedImageState state) { - return GestureConfig( - inPageView: true, - initialScale: 1.0, - maxScale: 5.0, - animationMaxScale: 6.0, - initialAlignment: InitialAlignment.center, - ); - }, - ); - }, + GestureDetector( + onLongPress: () => onOpenMenu(), + child: ExtendedImageGesturePageView.builder( + controller: ExtendedPageController( + initialPage: _previewController.initialPage.value, + pageSpacing: 0, ), + onPageChanged: (int index) => _previewController.onChange(index), + canScrollPage: (GestureDetails? gestureDetails) => + gestureDetails!.totalScale! <= 1.0, + itemCount: widget.imgList!.length, + itemBuilder: (BuildContext context, int index) { + return ExtendedImage.network( + widget.imgList![index], + fit: BoxFit.contain, + mode: ExtendedImageMode.gesture, + onDoubleTap: (ExtendedImageGestureState state) { + final Offset? pointerDownPosition = + state.pointerDownPosition; + final double? begin = state.gestureDetails!.totalScale; + double end; + + //remove old + _doubleClickAnimation + ?.removeListener(_doubleClickAnimationListener); + + //stop pre + _doubleClickAnimationController.stop(); + + //reset to use + _doubleClickAnimationController.reset(); + + if (begin == doubleTapScales[0]) { + setState(() { + _dismissDisabled = true; + }); + end = doubleTapScales[1]; + } else { + setState(() { + _dismissDisabled = false; + }); + end = doubleTapScales[0]; + } + + _doubleClickAnimationListener = () { + state.handleDoubleTap( + scale: _doubleClickAnimation!.value, + doubleTapPosition: pointerDownPosition); + }; + _doubleClickAnimation = _doubleClickAnimationController + .drive(Tween(begin: begin, end: end)); + + _doubleClickAnimation! + .addListener(_doubleClickAnimationListener); + + _doubleClickAnimationController.forward(); + }, + // ignore: body_might_complete_normally_nullable + loadStateChanged: (ExtendedImageState state) { + if (state.extendedImageLoadState == LoadState.loading) { + final ImageChunkEvent? loadingProgress = + state.loadingProgress; + final double? progress = + loadingProgress?.expectedTotalBytes != null + ? loadingProgress!.cumulativeBytesLoaded / + loadingProgress.expectedTotalBytes! + : null; + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + SizedBox( + width: 150.0, + child: LinearProgressIndicator( + value: progress, + color: Colors.white, + ), + ), + // const SizedBox(height: 10.0), + // Text('${((progress ?? 0.0) * 100).toInt()}%',), + ], + ), + ); + } + }, + initGestureConfigHandler: (ExtendedImageState state) { + return GestureConfig( + inPageView: true, + initialScale: 1.0, + maxScale: 5.0, + animationMaxScale: 6.0, + initialAlignment: InitialAlignment.center, + ); + }, + ); + }, ), ), Positioned( @@ -251,33 +239,49 @@ class _ImagePreviewState extends State right: 0, bottom: 0, child: Container( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context).padding.bottom + 30), - decoration: const BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [ - Colors.transparent, - Colors.black87, + padding: EdgeInsets.only( + left: 20, + right: 20, + bottom: MediaQuery.of(context).padding.bottom + 30), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black87, + ], + tileMode: TileMode.mirror, + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + widget.imgList!.length > 1 + ? Obx( + () => Text.rich( + textAlign: TextAlign.center, + TextSpan( + style: const TextStyle( + color: Colors.white, fontSize: 16), + children: [ + TextSpan( + text: _previewController.currentPage + .toString()), + const TextSpan(text: ' / '), + TextSpan( + text: + widget.imgList!.length.toString()), + ]), + ), + ) + : const SizedBox(), + IconButton( + onPressed: () => Get.back(), + icon: const Icon(Icons.close, color: Colors.white), + ), ], - tileMode: TileMode.mirror, - ), - ), - child: Obx( - () => Text.rich( - textAlign: TextAlign.center, - TextSpan( - style: const TextStyle(color: Colors.white, fontSize: 15), - children: [ - TextSpan( - text: _previewController.currentPage.toString()), - const TextSpan(text: ' / '), - TextSpan(text: widget.imgList!.length.toString()), - ]), - ), - ), - ), + )), ), ], ), From b248158e627add8e94242b2546645dbd291bcc24 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 3 Mar 2024 19:48:10 +0800 Subject: [PATCH 52/67] =?UTF-8?q?v1.0.20=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.20.0303.md | 31 +++++++++++++++++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.20.0303.md diff --git a/change_log/1.0.20.0303.md b/change_log/1.0.20.0303.md new file mode 100644 index 00000000..1d8c4e00 --- /dev/null +++ b/change_log/1.0.20.0303.md @@ -0,0 +1,31 @@ +## 1.0.20 + + +### 功能 ++ 评论区增加表情 ++ 首页渐变背景开关 ++ 媒体库显示「我的订阅」 ++ 评论区链接解析 ++ 默认启动页设置 + +### 修复 ++ 评论区内容重复 ++ pip相关问题 ++ 播放多p视频评论不刷新 ++ 视频评论翻页重复 + +### 优化 ++ url scheme优化 ++ 图片预览放大 ++ 图片加载速度 ++ 视频评论区复制 ++ 全屏显示视频标题 ++ 网络异常处理 + + + + + + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index 9597eab7..d261675a 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.19+1019 +version: 1.0.20+1020 environment: sdk: ">=2.19.6 <3.0.0" From f1b829cec1f330eb9ab790c1e3d8bc6558067025 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 4 Mar 2024 08:29:01 +0800 Subject: [PATCH 53/67] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5tarbar=E6=8C=87?= =?UTF-8?q?=E7=A4=BA=E5=99=A8=E8=B7=B3=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/controller.dart | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index 9f6f8ac5..6113d901 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -91,19 +91,21 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { vsync: this, ); // 监听 tabController 切换 - tabController.animation!.addListener(() { - if (tabController.indexIsChanging) { - if (initialIndex.value != tabController.index) { - initialIndex.value = tabController.index; + if (enableGradientBg) { + tabController.animation!.addListener(() { + if (tabController.indexIsChanging) { + if (initialIndex.value != tabController.index) { + initialIndex.value = tabController.index; + } + } else { + final int temp = tabController.animation!.value.round(); + if (initialIndex.value != temp) { + initialIndex.value = temp; + tabController.index = initialIndex.value; + } } - } else { - final int temp = tabController.animation!.value.round(); - if (initialIndex.value != temp) { - initialIndex.value = temp; - tabController.index = initialIndex.value; - } - } - }); + }); + } } void searchDefault() async { From eb4435045b35074ba2f8768359e2b6c0b05a540d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 4 Mar 2024 23:48:01 +0800 Subject: [PATCH 54/67] =?UTF-8?q?fix:=20=E7=95=AA=E5=89=A7=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E6=97=B6title=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/video/detail/controller.dart | 1 + lib/pages/video/detail/view.dart | 1 + lib/pages/video/detail/widgets/header_control.dart | 11 ++++++++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index d590cea9..b4005b5a 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -129,6 +129,7 @@ class VideoDetailController extends GetxController videoDetailCtr: this, floating: floating, bvid: bvid, + videoType: videoType, ); // CDN优化 enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 0c432129..04d41b2e 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -572,6 +572,7 @@ class _VideoDetailPageState extends State controller: plPlayerController, videoDetailCtr: videoDetailController, bvid: videoDetailController.bvid, + videoType: videoDetailController.videoType, ), danmuWidget: Obx( () => PlDanmaku( diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e2e4db8c..03f7ff7f 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -19,6 +19,7 @@ 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/common/search_type.dart'; import '../../../../models/video_detail_res.dart'; import '../introduction/index.dart'; @@ -28,12 +29,14 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { this.videoDetailCtr, this.floating, this.bvid, + this.videoType, super.key, }); final PlPlayerController? controller; final VideoDetailController? videoDetailCtr; final Floating? floating; final String? bvid; + final SearchType? videoType; @override State createState() => _HeaderControlState(); @@ -1107,14 +1110,16 @@ class _HeaderControlState extends State { }, ), SizedBox(width: buttonSpace), - if (showTitle && isLandscape) ...[ + if (showTitle && + isLandscape && + widget.videoType == SearchType.video) ...[ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ ConstrainedBox( - constraints: BoxConstraints(maxWidth: 200), + constraints: const BoxConstraints(maxWidth: 200), child: Text( - videoIntroController.videoDetail.value.title!, + videoIntroController.videoDetail.value.title ?? '', style: const TextStyle( color: Colors.white, fontSize: 16, From 32cdb27f7c3ebfb8c9c94ee4a399b9d1e7b3f869 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 5 Mar 2024 22:37:52 +0800 Subject: [PATCH 55/67] =?UTF-8?q?fix:=20enableGradientBg=E6=9C=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/controller.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index 6113d901..fb85be0b 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -34,8 +34,6 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { userInfo = userInfoCache.get('userInfoCache'); userLogin.value = userInfo != null; userFace.value = userInfo != null ? userInfo.face : ''; - // 进行tabs配置 - setTabConfig(); hideSearchBar = setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) { @@ -43,6 +41,8 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { } enableGradientBg = setting.get(SettingBoxKey.enableGradientBg, defaultValue: true); + // 进行tabs配置 + setTabConfig(); } void onRefresh() { From fea70011cb1c2595bd125394f9894bb5946b3898 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 5 Mar 2024 23:01:23 +0800 Subject: [PATCH 56/67] fix: navBars unmodifiable issues #612 --- lib/models/common/nav_bar_config.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/models/common/nav_bar_config.dart b/lib/models/common/nav_bar_config.dart index 2d195e8b..7fb22e48 100644 --- a/lib/models/common/nav_bar_config.dart +++ b/lib/models/common/nav_bar_config.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; -const defaultNavigationBars = [ +List defaultNavigationBars = [ { 'id': 0, - 'icon': Icon( + 'icon': const Icon( Icons.home_outlined, size: 21, ), - 'selectIcon': Icon( + 'selectIcon': const Icon( Icons.home, size: 21, ), @@ -16,11 +16,11 @@ const defaultNavigationBars = [ }, { 'id': 1, - 'icon': Icon( + 'icon': const Icon( Icons.motion_photos_on_outlined, size: 21, ), - 'selectIcon': Icon( + 'selectIcon': const Icon( Icons.motion_photos_on, size: 21, ), @@ -29,11 +29,11 @@ const defaultNavigationBars = [ }, { 'id': 2, - 'icon': Icon( + 'icon': const Icon( Icons.video_collection_outlined, size: 20, ), - 'selectIcon': Icon( + 'selectIcon': const Icon( Icons.video_collection, size: 21, ), From 3fad86e7e33db6861b6a6c0a1ff417c8a4bbf886 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 5 Mar 2024 23:04:59 +0800 Subject: [PATCH 57/67] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E7=AE=80?= =?UTF-8?q?=E4=BB=8B=E8=A2=AB=E9=81=AE=E6=8C=A1=20issues=20#613?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/widgets/intro_detail.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index 59ed10a3..c74e27ee 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -23,7 +23,10 @@ class IntroDetail extends StatelessWidget { sheetHeight = localCache.get('sheetHeight'); return Container( color: Theme.of(context).colorScheme.background, - padding: const EdgeInsets.only(left: 14, right: 14), + padding: EdgeInsets.only( + left: 14, + right: 14, + bottom: MediaQuery.of(context).padding.bottom + 20), height: sheetHeight, child: Column( children: [ From 12e947ef849490fca3204857dccb7b4b8382352d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 5 Mar 2024 23:21:51 +0800 Subject: [PATCH 58/67] fix: reply callback null error issues #615 --- lib/pages/video/detail/reply/widgets/reply_item.dart | 6 +++--- lib/pages/video/detail/reply_reply/view.dart | 4 +++- 2 files changed, 6 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 c55380f2..174cfabb 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -280,7 +280,7 @@ class ReplyItem extends StatelessWidget { // 完成评论,数据添加 if (value != null && value['data'] != null) { - addReply!(value['data']) + addReply?.call(value['data']) // replyControl.replies.add(value['data']), } }); @@ -531,8 +531,8 @@ InlineSpan buildContent( spanChilds.add(TextSpan( text: str, recognizer: TapGestureRecognizer() - ..onTap = - () => replyReply(replyItem.root == 0 ? replyItem : fReplyItem))); + ..onTap = () => + replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem))); } // 分割文本并处理每个部分 diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index c08c81cc..820a69ec 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -92,7 +92,7 @@ class _VideoReplyReplyPanelState extends State { icon: const Icon(Icons.close, size: 20), onPressed: () { _videoReplyReplyController.currentPage = 0; - widget.closePanel!(); + widget.closePanel?.call; Navigator.pop(context); }, ), @@ -184,6 +184,8 @@ class _VideoReplyReplyPanelState extends State { .add(replyItem); }, replyType: widget.replyType, + replyReply: (replyItem) => + replyReply(replyItem), ); } }, From d728b1fb6d74ee535b57f1ca0dc87f056789387c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 5 Mar 2024 23:39:05 +0800 Subject: [PATCH 59/67] =?UTF-8?q?mod:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E9=9D=9E=E6=AD=A3=E5=B8=B8=E5=9C=B0=E5=9D=80=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 5 ++++ lib/utils/url_utils.dart | 24 +++++++++++-------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 174cfabb..f9f695d4 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -642,6 +642,11 @@ InlineSpan buildContent( } else { final String redirectUrl = await UrlUtils.parseRedirectUrl(matchStr); + if (redirectUrl == matchStr) { + Clipboard.setData(ClipboardData(text: matchStr)); + SmartDialog.showToast('地址可能有误'); + return; + } final String pathSegment = Uri.parse(redirectUrl).path; final String lastPathSegment = pathSegment.split('/').last; diff --git a/lib/utils/url_utils.dart b/lib/utils/url_utils.dart index bac6cdfa..cf0ef9e2 100644 --- a/lib/utils/url_utils.dart +++ b/lib/utils/url_utils.dart @@ -14,19 +14,23 @@ class UrlUtils { 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); + try { + 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; + } catch (err) { return url; } - return redirectUrl; } // 匹配url路由跳转 From ab9ae3a48147af32b5a7cd96e80d955b6cc45f76 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 6 Mar 2024 00:04:52 +0800 Subject: [PATCH 60/67] =?UTF-8?q?fix:=20setState()=20called=20after=20disp?= =?UTF-8?q?ose()=20=E5=AF=BC=E8=87=B4=E5=85=A8=E5=B1=8F=E5=A4=B1=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/widgets/header_control.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 03f7ff7f..e72e6a69 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -79,7 +79,11 @@ class _HeaderControlState extends State { } else { showTitle = false; } - setState(() {}); + + /// TODO setState() called after dispose() + if (mounted) { + setState(() {}); + } }); } From ed0b43eff1f6043fe00e54c141053bd7ab336794 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 6 Mar 2024 23:29:18 +0800 Subject: [PATCH 61/67] =?UTF-8?q?v1.0.21=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.21.0306.md | 9 +++++++++ pubspec.yaml | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.21.0306.md diff --git a/change_log/1.0.21.0306.md b/change_log/1.0.21.0306.md new file mode 100644 index 00000000..3a582dbb --- /dev/null +++ b/change_log/1.0.21.0306.md @@ -0,0 +1,9 @@ +## 1.0.21 + +### 修复 ++ 推荐视频全屏问题 ++ 番剧全屏播放时灰屏问题 ++ 评论回调导致页面卡死问题 + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index d261675a..a5d103f6 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.20+1020 +version: 1.0.21+1021 environment: sdk: ">=2.19.6 <3.0.0" From ab24da5f55a4ba0322fbd97a7a0dd60b7c7dfc8a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 7 Mar 2024 23:35:39 +0800 Subject: [PATCH 62/67] =?UTF-8?q?fix:=20=E5=AA=92=E4=BD=93=E9=80=9A?= =?UTF-8?q?=E7=9F=A5=E8=BF=9B=E5=BA=A6=E6=9D=A1=E6=9C=AA=E6=8C=89=E9=A2=84?= =?UTF-8?q?=E6=9C=9F=E5=81=9C=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 7 ++++--- lib/services/audio_handler.dart | 7 ++++--- lib/services/audio_session.dart | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index dfa580ab..b4b37cea 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -277,8 +277,7 @@ class PlPlayerController { danmakuDurationVal = localCache.get(LocalCacheKey.danmakuDuration, defaultValue: 4.0); // 描边粗细 - strokeWidth = - localCache.get(LocalCacheKey.strokeWidth, defaultValue: 1.5); + strokeWidth = localCache.get(LocalCacheKey.strokeWidth, defaultValue: 1.5); playRepeat = PlayRepeat.values.toList().firstWhere( (e) => e.value == @@ -535,8 +534,10 @@ class PlPlayerController { if (event) { playerStatus.status.value = PlayerStatus.playing; } else { - // playerStatus.status.value = PlayerStatus.paused; + playerStatus.status.value = PlayerStatus.paused; } + videoPlayerServiceHandler.onStatusChange( + playerStatus.status.value, isBuffering.value); /// 触发回调事件 for (var element in _statusListeners) { diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index 0b37d419..bf98298b 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -26,6 +26,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { static final List _item = []; Box setting = GStrorage.setting; bool enableBackgroundPlay = false; + PlPlayerController player = PlPlayerController.getInstance(); VideoPlayerServiceHandler() { revalidateSetting(); @@ -38,12 +39,12 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { @override Future play() async { - PlPlayerController.getInstance().play(); + player.play(); } @override Future pause() async { - PlPlayerController.getInstance().pause(); + player.pause(); } @override @@ -51,7 +52,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { playbackState.add(playbackState.value.copyWith( updatePosition: position, )); - await PlPlayerController.getInstance().seekTo(position); + await player.seekTo(position); } Future setMediaItem(MediaItem newMediaItem) async { diff --git a/lib/services/audio_session.dart b/lib/services/audio_session.dart index 57c42e05..ea83a30a 100644 --- a/lib/services/audio_session.dart +++ b/lib/services/audio_session.dart @@ -20,7 +20,7 @@ class AudioSessionHandler { session.interruptionEventStream.listen((event) { final player = PlPlayerController.getInstance(); if (event.begin) { - if (player.playerStatus != PlayerStatus.playing) return; + if (!player.playerStatus.playing) return; switch (event.type) { case AudioInterruptionType.duck: player.setVolume(player.volume.value * 0.5); @@ -52,7 +52,7 @@ class AudioSessionHandler { // 耳机拔出暂停 session.becomingNoisyEventStream.listen((_) { final player = PlPlayerController.getInstance(); - if (player.playerStatus == PlayerStatus.playing) { + if (player.playerStatus.playing) { player.pause(); } }); From 3bf6136bc67f6d1dfbc50c43ef84ca98b07fdf03 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 8 Mar 2024 00:03:34 +0800 Subject: [PATCH 63/67] =?UTF-8?q?fix:=20=E6=A5=BC=E4=B8=AD=E6=A5=BC?= =?UTF-8?q?=E8=AF=84=E8=AE=BA=E8=AF=B7=E6=B1=82=E9=87=8D=E5=A4=8D=20#284?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/reply_reply/controller.dart | 20 ++++--------------- lib/pages/video/detail/reply_reply/view.dart | 3 ++- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index 6daa0b8a..e94aaea5 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -30,6 +30,9 @@ class VideoReplyReplyController extends GetxController { if (type == 'init') { currentPage = 0; } + if (isLoadingMore) { + return; + } isLoadingMore = true; final res = await ReplyHttp.replyReplyList( oid: aid!, @@ -41,7 +44,7 @@ class VideoReplyReplyController extends GetxController { final List replies = res['data'].replies; if (replies.isNotEmpty) { noMore.value = '加载中...'; - if (replyList.length == res['data'].page.count) { + if (replies.length == res['data'].page.count) { noMore.value = '没有更多了'; } currentPage++; @@ -50,21 +53,6 @@ class VideoReplyReplyController extends GetxController { noMore.value = currentPage == 0 ? '还没有评论' : '没有更多了'; } if (type == 'init') { - // List replies = res['data'].replies; - // 添加置顶回复 - // if (res['data'].upper.top != null) { - // bool flag = false; - // for (var i = 0; i < res['data'].topReplies.length; i++) { - // if (res['data'].topReplies[i].rpid == res['data'].upper.top.rpid) { - // flag = true; - // } - // } - // if (!flag) { - // replies.insert(0, res['data'].upper.top); - // } - // } - // replies.insertAll(0, res['data'].topReplies); - // res['data'].replies = replies; replyList.value = replies; } else { // 每次回复之后,翻页请求有且只有相同的一条回复数据 diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 820a69ec..e8754a31 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -54,7 +54,8 @@ class _VideoReplyReplyPanelState extends State { () { if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 300) { - EasyThrottle.throttle('replylist', const Duration(seconds: 2), () { + EasyThrottle.throttle('replylist', const Duration(milliseconds: 200), + () { _videoReplyReplyController.queryReplyList(type: 'onLoad'); }); } From a3e1fd4e918521c4bc44720760615d65fba663a0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 8 Mar 2024 23:09:52 +0800 Subject: [PATCH 64/67] =?UTF-8?q?fix:=20=E6=B8=85=E9=99=A4=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E6=8F=90=E7=A4=BA=20issues=20#619?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/about/index.dart | 1 - lib/utils/cache_manage.dart | 6 ++---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 7c42f5aa..dcfa7a9e 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -201,7 +201,6 @@ class _AboutPageState extends State { var cleanStatus = await CacheManage().clearCacheAll(); if (cleanStatus) { getCacheSize(); - SmartDialog.showToast('清除成功'); } }, title: const Text('清除缓存'), diff --git a/lib/utils/cache_manage.dart b/lib/utils/cache_manage.dart index e250ab70..d6bbb816 100644 --- a/lib/utils/cache_manage.dart +++ b/lib/utils/cache_manage.dart @@ -99,10 +99,8 @@ class CacheManage { try { // 清除缓存 图片缓存 await clearLibraryCache(); - Timer(const Duration(milliseconds: 500), () { - SmartDialog.dismiss().then((res) { - SmartDialog.showToast('清除完成'); - }); + SmartDialog.dismiss().then((res) { + SmartDialog.showToast('清除完成'); }); } catch (err) { SmartDialog.dismiss(); From df4539a0353b44d343b633cb4fb4d73e76e523f9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 8 Mar 2024 23:15:48 +0800 Subject: [PATCH 65/67] =?UTF-8?q?fix:=20github=E9=93=BE=E6=8E=A5=20issues?= =?UTF-8?q?=20#618?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/about/index.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index dcfa7a9e..89aec795 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -276,7 +276,7 @@ class AboutController extends GetxController { githubRelease() { launchUrl( - Uri.parse('https://github.com/guozhigq/pilipala/release'), + Uri.parse('https://github.com/guozhigq/pilipala/releases'), mode: LaunchMode.externalApplication, ); } From 504be6fbdac224ad7dbb2c72f7bc06c086aa7e84 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 9 Mar 2024 01:18:26 +0800 Subject: [PATCH 66/67] =?UTF-8?q?fix:=20=E6=90=9C=E7=B4=A2=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E7=B1=BB=E5=9E=8B=E4=B8=BA=E8=AF=BE=E5=A0=82=E6=97=B6?= =?UTF-8?q?=E6=B8=B2=E6=9F=93=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_h.dart | 28 ++++++++++++++++++++++------ lib/models/search/result.dart | 4 +++- lib/utils/utils.dart | 12 +++++++++--- 3 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index c78643db..99059a9e 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -38,6 +38,10 @@ class VideoCardH extends StatelessWidget { Widget build(BuildContext context) { final int aid = videoItem.aid; final String bvid = videoItem.bvid; + String type = 'video'; + try { + type = videoItem.type; + } catch (_) {} final String heroTag = Utils.makeHeroTag(aid); return GestureDetector( onLongPress: () { @@ -53,6 +57,10 @@ class VideoCardH extends StatelessWidget { child: InkWell( onTap: () async { try { + if (type == 'ketang') { + SmartDialog.showToast('课堂视频暂不支持播放'); + return; + } final int cid = videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid); Get.toNamed('/video?bvid=$bvid&cid=$cid', @@ -95,12 +103,20 @@ class VideoCardH extends StatelessWidget { height: maxHeight, ), ), - PBadge( - text: Utils.timeFormat(videoItem.duration!), - right: 6.0, - bottom: 6.0, - type: 'gray', - ), + if (videoItem.duration != 0) + PBadge( + text: Utils.timeFormat(videoItem.duration!), + right: 6.0, + bottom: 6.0, + type: 'gray', + ), + if (type != 'video') + PBadge( + text: type, + left: 6.0, + bottom: 6.0, + type: 'primary', + ), // if (videoItem.rcmdReason != null && // videoItem.rcmdReason.content != '') // pBadge(videoItem.rcmdReason.content, context, diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart index 3d381ed9..0067791c 100644 --- a/lib/models/search/result.dart +++ b/lib/models/search/result.dart @@ -85,7 +85,9 @@ class SearchVideoItemModel { // title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); title = Em.regTitle(json['title']); description = json['description']; - pic = 'https:${json['pic']}'; + pic = json['pic'] != null && json['pic'].startsWith('//') + ? 'https:${json['pic']}' + : json['pic'] ?? ''; videoReview = json['video_review']; pubdate = json['pubdate']; senddate = json['senddate']; diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 09d8c97f..62bd4d0a 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -50,6 +50,9 @@ class Utils { return time; } if (time < 3600) { + if (time == 0) { + return time; + } final int minute = time ~/ 60; final double res = time / 60; if (minute != res) { @@ -87,6 +90,9 @@ class Utils { // 时间显示,刚刚,x分钟前 static String dateFormat(timeStamp, {formatType = 'list'}) { + if (timeStamp == 0 || timeStamp == null || timeStamp == '') { + return ''; + } // 当前时间 int time = (DateTime.now().millisecondsSinceEpoch / 1000).round(); // 对比 @@ -103,6 +109,7 @@ class Utils { toInt: false, formatType: formatType); } + print('distance: $distance'); if (distance <= 60) { return '刚刚'; } else if (distance <= 3600) { @@ -344,9 +351,8 @@ class Utils { } static List generateRandomBytes(int minLength, int maxLength) { - return List.generate( - random.nextInt(maxLength-minLength+1), (_) => random.nextInt(0x60) + 0x20 - ); + return List.generate(random.nextInt(maxLength - minLength + 1), + (_) => random.nextInt(0x60) + 0x20); } static String base64EncodeRandomString(int minLength, int maxLength) { From 06fb3e8d2f2da9d1ac0a57830f14e261679f0195 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 9 Mar 2024 01:25:54 +0800 Subject: [PATCH 67/67] =?UTF-8?q?fix:=20=E8=AF=B7=E6=B1=82github=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/about/index.dart | 6 +++++- lib/utils/utils.dart | 4 ++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/pages/about/index.dart b/lib/pages/about/index.dart index 89aec795..b381691a 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -253,12 +253,16 @@ class AboutController extends GetxController { // 获取远程版本 Future getRemoteApp() async { var result = await Request().get(Api.latestApp, extra: {'ua': 'pc'}); + isLoading.value = false; + if (result.data == null || result.data.isEmpty) { + SmartDialog.showToast('获取远程版本失败,请检查网络'); + return; + } data = LatestDataModel.fromJson(result.data); remoteAppInfo = data; remoteVersion.value = data.tagName!; isUpdate.value = Utils.needUpdate(currentVersion.value, remoteVersion.value); - isLoading.value = false; } // 跳转下载/本地更新 diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 62bd4d0a..adcc7b5a 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -243,6 +243,10 @@ class Utils { SmartDialog.dismiss(); var currentInfo = await PackageInfo.fromPlatform(); var result = await Request().get(Api.latestApp, extra: {'ua': 'mob'}); + if (result.data == null || result.data.isEmpty) { + SmartDialog.showToast('获取远程版本失败,请检查网络'); + return false; + } LatestDataModel data = LatestDataModel.fromJson(result.data); bool isUpdate = Utils.needUpdate(currentInfo.version, data.tagName!); if (isUpdate) {