From 6f628374953ff9d032e6f980f6497ca343b86026 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 2 Feb 2024 23:12:04 +0800 Subject: [PATCH 001/349] =?UTF-8?q?mod:=20media=5Fkit=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 27 +++++++++++++++------------ pubspec.yaml | 35 ++++++++++++++++++++++++++++++++--- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index d9b6a01d..e98d7443 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -848,10 +848,11 @@ packages: media_kit: dependency: "direct main" description: - name: media_kit - sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" - url: "https://pub.flutter-io.cn" - source: hosted + path: media_kit + ref: main + resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.1.10+1" media_kit_libs_android_video: dependency: transitive @@ -888,10 +889,11 @@ packages: media_kit_libs_video: dependency: "direct main" description: - name: media_kit_libs_video - sha256: "3688e0c31482074578652bf038ce6301a5d21e1eda6b54fc3117ffeb4bdba067" - url: "https://pub.flutter-io.cn" - source: hosted + path: "libs/universal/media_kit_libs_video" + ref: main + resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.0.4" media_kit_libs_windows_video: dependency: transitive @@ -912,10 +914,11 @@ packages: media_kit_video: dependency: "direct main" description: - name: media_kit_video - sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 - url: "https://pub.flutter-io.cn" - source: hosted + path: media_kit_video + ref: main + resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + url: "https://github.com/media-kit/media-kit.git" + source: git version: "1.2.4" meta: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index f4b8fe61..41cc829d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -85,9 +85,21 @@ dependencies: encrypt: ^5.0.3 # 视频播放器 - media_kit: ^1.1.10 # Primary package. - media_kit_video: ^1.2.4 # For video rendering. - media_kit_libs_video: ^1.0.4 + media_kit: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./media_kit + media_kit_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./media_kit_video + media_kit_libs_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./libs/universal/media_kit_libs_video # 媒体通知 audio_service: ^0.18.12 @@ -156,6 +168,23 @@ dev_dependencies: hive_generator: ^2.0.0 build_runner: ^2.3.3 +dependency_overrides: + media_kit: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./media_kit + media_kit_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./media_kit_video + media_kit_libs_video: + git: + url: https://github.com/media-kit/media-kit.git + ref: main + path: ./libs/universal/media_kit_libs_video + flutter_launcher_icons: android: true ios: true From ee368d348d00148edf7c5557f6171b044bc3d353 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 27 Feb 2024 22:50:02 +0800 Subject: [PATCH 002/349] =?UTF-8?q?feat:=20=E5=AD=97=E5=B9=95=E5=B1=95?= =?UTF-8?q?=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 ++ lib/http/video.dart | 16 ++++++ lib/models/video/subTitile/content.dart | 20 +++++++ lib/models/video/subTitile/result.dart | 70 +++++++++++++++++++++++++ lib/pages/video/detail/controller.dart | 36 +++++++++++++ lib/pages/video/detail/view.dart | 5 +- lib/plugin/pl_player/controller.dart | 21 +++++++- lib/plugin/pl_player/view.dart | 37 ++++++++++++- 8 files changed, 204 insertions(+), 4 deletions(-) create mode 100644 lib/models/video/subTitile/content.dart create mode 100644 lib/models/video/subTitile/result.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 2e758439..559c6fdb 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -483,4 +483,7 @@ class Api { /// 激活buvid3 static const activateBuvidApi = '/x/internal/gaia-gateway/ExClimbWuzhi'; + + /// 获取字幕配置 + static const getSubtitleConfig = '/x/player/v2'; } diff --git a/lib/http/video.dart b/lib/http/video.dart index 30df62c3..a852e74b 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -8,6 +8,7 @@ import '../models/model_rec_video_item.dart'; import '../models/user/fav_folder.dart'; import '../models/video/ai.dart'; import '../models/video/play/url.dart'; +import '../models/video/subTitile/result.dart'; import '../models/video_detail_res.dart'; import '../utils/recommend_filter.dart'; import '../utils/storage.dart'; @@ -475,4 +476,19 @@ class VideoHttp { return {'status': false, 'data': []}; } } + + static Future getSubtitle({int? cid, String? bvid}) async { + var res = await Request().get(Api.getSubtitleConfig, data: { + 'cid': cid, + 'bvid': bvid, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': SubTitlteModel.fromJson(res.data['data']), + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['msg']}; + } + } } diff --git a/lib/models/video/subTitile/content.dart b/lib/models/video/subTitile/content.dart new file mode 100644 index 00000000..b18098a4 --- /dev/null +++ b/lib/models/video/subTitile/content.dart @@ -0,0 +1,20 @@ +class SubTitileContentModel { + double? from; + double? to; + int? location; + String? content; + + SubTitileContentModel({ + this.from, + this.to, + this.location, + this.content, + }); + + SubTitileContentModel.fromJson(Map json) { + from = json['from']; + to = json['to']; + location = json['location']; + content = json['content']; + } +} diff --git a/lib/models/video/subTitile/result.dart b/lib/models/video/subTitile/result.dart new file mode 100644 index 00000000..389378fa --- /dev/null +++ b/lib/models/video/subTitile/result.dart @@ -0,0 +1,70 @@ +class SubTitlteModel { + SubTitlteModel({ + this.aid, + this.bvid, + this.cid, + this.loginMid, + this.loginMidHash, + this.isOwner, + this.name, + this.subtitles, + }); + + int? aid; + String? bvid; + int? cid; + int? loginMid; + String? loginMidHash; + bool? isOwner; + String? name; + List? subtitles; + + factory SubTitlteModel.fromJson(Map json) => SubTitlteModel( + aid: json["aid"], + bvid: json["bvid"], + cid: json["cid"], + loginMid: json["login_mid"], + loginMidHash: json["login_mid_hash"], + isOwner: json["is_owner"], + name: json["name"], + subtitles: json["subtitle"] != null + ? json["subtitle"]["subtitles"] + .map((x) => SubTitlteItemModel.fromJson(x)) + .toList() + : [], + ); +} + +class SubTitlteItemModel { + SubTitlteItemModel({ + this.id, + this.lan, + this.lanDoc, + this.isLock, + this.subtitleUrl, + this.type, + this.aiType, + this.aiStatus, + }); + + int? id; + String? lan; + String? lanDoc; + bool? isLock; + String? subtitleUrl; + int? type; + int? aiType; + int? aiStatus; + + factory SubTitlteItemModel.fromJson(Map json) => + SubTitlteItemModel( + id: json["id"], + lan: json["lan"], + lanDoc: json["lan_doc"], + isLock: json["is_lock"], + subtitleUrl: json["subtitle_url"], + type: json["type"], + aiType: json["ai_type"], + aiStatus: json["ai_status"], + ); +} diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 7465c6f2..198da362 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -19,6 +19,8 @@ import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/video_utils.dart'; import 'package:screen_brightness/screen_brightness.dart'; +import '../../../http/index.dart'; +import '../../../models/video/subTitile/content.dart'; import '../../../utils/id_utils.dart'; import 'widgets/header_control.dart'; @@ -91,6 +93,8 @@ class VideoDetailController extends GetxController late int cacheAudioQa; PersistentBottomSheetController? replyReplyBottomSheetCtr; + RxList subtitleContents = + [].obs; @override void onInit() { @@ -139,6 +143,7 @@ class VideoDetailController extends GetxController cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.hiRes.code); oid.value = IdUtils.bv2av(Get.parameters['bvid']!); + getSubtitle(); } showReplyReplyPanel() { @@ -381,4 +386,35 @@ class VideoDetailController extends GetxController ? replyReplyBottomSheetCtr!.close() : print('replyReplyBottomSheetCtr is null'); } + + // 获取字幕配置 + Future getSubtitle() async { + var result = await VideoHttp.getSubtitle(bvid: bvid, cid: cid.value); + if (result['status']) { + if (result['data'].subtitles.isNotEmpty) { + SmartDialog.showToast('字幕加载中...'); + var subtitle = result['data'].subtitles.first; + getSubtitleContent(subtitle.subtitleUrl); + } + return result['data']; + } else { + SmartDialog.showToast(result['msg'].toString()); + } + } + + // 获取字幕内容 + Future getSubtitleContent(String url) async { + var res = await Request().get('https:$url'); + subtitleContents.value = res.data['body'].map((e) { + return SubTitileContentModel.fromJson(e); + }).toList(); + setSubtitleContent(); + } + + setSubtitleContent() { + plPlayerController.subtitleContent.value = ''; + if (subtitleContents.isNotEmpty) { + plPlayerController.subtitleContents = subtitleContents; + } + } } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index febca105..449c377e 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -236,7 +236,10 @@ class _VideoDetailPageState extends State void didPopNext() async { if (plPlayerController != null && plPlayerController!.videoPlayerController != null) { - setState(() => isShowing = true); + setState(() { + videoDetailController.setSubtitleContent(); + isShowing = true; + }); } videoDetailController.isFirstTime = false; final bool autoplay = autoPlayEnable; diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index dfa580ab..081b9ad9 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -21,6 +21,7 @@ import 'package:pilipala/utils/storage.dart'; import 'package:screen_brightness/screen_brightness.dart'; import 'package:status_bar_control/status_bar_control.dart'; import 'package:universal_platform/universal_platform.dart'; +import '../../models/video/subTitile/content.dart'; // import 'package:wakelock_plus/wakelock_plus.dart'; Box videoStorage = GStrorage.video; @@ -231,6 +232,10 @@ class PlPlayerController { // 播放顺序相关 PlayRepeat playRepeat = PlayRepeat.pause; + RxList subtitleContents = + [].obs; + RxString subtitleContent = ''.obs; + void updateSliderPositionSecond() { int newSecond = _sliderPosition.value.inSeconds; if (sliderPositionSeconds.value != newSecond) { @@ -277,8 +282,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 == @@ -566,6 +570,8 @@ class PlPlayerController { _sliderPosition.value = event; updateSliderPositionSecond(); } + querySubtitleContent( + videoPlayerController!.state.position.inSeconds.toDouble()); /// 触发回调事件 for (var element in _positionListeners) { @@ -1050,6 +1056,17 @@ class PlPlayerController { } } + void querySubtitleContent(double progress) { + if (subtitleContents.isNotEmpty) { + for (var content in subtitleContents) { + if (progress >= content.from! && progress <= content.to!) { + subtitleContent.value = content.content!; + return; + } + } + } + } + setPlayRepeat(PlayRepeat type) { playRepeat = type; videoStorage.put(VideoBoxKey.playRepeat, type.value); diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index d073945b..e7a8ce73 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -16,7 +16,6 @@ import 'package:pilipala/plugin/pl_player/utils.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:screen_brightness/screen_brightness.dart'; - import 'models/bottom_progress_behavior.dart'; import 'widgets/app_bar_ani.dart'; import 'widgets/backward_seek.dart'; @@ -428,6 +427,42 @@ class _PLVideoPlayerState extends State if (widget.danmuWidget != null) Positioned.fill(top: 4, child: widget.danmuWidget!), + widget.controller.subscriptions.isNotEmpty + ? Stack( + children: [ + Positioned( + left: 0, + right: 0, + bottom: 30, + child: Align( + alignment: Alignment.center, + child: Obx( + () => Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: widget.controller.subtitleContent.value != '' + ? Colors.black.withOpacity(0.4) + : Colors.transparent, + ), + padding: const EdgeInsets.symmetric( + horizontal: 10, + vertical: 4, + ), + child: Text( + widget.controller.subtitleContent.value, + style: const TextStyle( + color: Colors.white, + fontSize: 12, + ), + ), + ), + ), + ), + ), + ], + ) + : nil, + /// 手势 Positioned.fill( left: 16, From 105a29f311e363ce0d4e529f269047cea522847e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 8 Mar 2024 22:56:16 +0800 Subject: [PATCH 003/349] feat: disable Battery Optimization --- lib/main.dart | 2 ++ lib/services/disable_battery_opt.dart | 40 +++++++++++++++++++++++++++ lib/utils/storage.dart | 4 +++ pubspec.lock | 8 ++++++ pubspec.yaml | 2 ++ 5 files changed, 56 insertions(+) create mode 100644 lib/services/disable_battery_opt.dart diff --git a/lib/main.dart b/lib/main.dart index fc2149de..44bb1dcd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -16,6 +16,7 @@ import 'package:pilipala/pages/search/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/router/app_pages.dart'; import 'package:pilipala/pages/main/view.dart'; +import 'package:pilipala/services/disable_battery_opt.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; @@ -71,6 +72,7 @@ void main() async { )); Data.init(); PiliSchame.init(); + DisableBatteryOpt(); }); } diff --git a/lib/services/disable_battery_opt.dart b/lib/services/disable_battery_opt.dart new file mode 100644 index 00000000..ae018977 --- /dev/null +++ b/lib/services/disable_battery_opt.dart @@ -0,0 +1,40 @@ +import 'dart:io'; + +import 'package:disable_battery_optimization/disable_battery_optimization.dart'; +import 'package:pilipala/utils/storage.dart'; + +void DisableBatteryOpt() async { + if (!Platform.isAndroid) { + return; + } + // 本地缓存中读取 是否禁用了电池优化 默认未禁用 + bool isDisableBatteryOptLocal = + GStrorage.localCache.get('isDisableBatteryOptLocal', defaultValue: false); + if (!isDisableBatteryOptLocal) { + final isBatteryOptimizationDisabled = + await DisableBatteryOptimization.isBatteryOptimizationDisabled; + if (isBatteryOptimizationDisabled == false) { + final hasDisabled = await DisableBatteryOptimization + .showDisableBatteryOptimizationSettings(); + // 设置为已禁用 + GStrorage.localCache.put('isDisableBatteryOptLocal', hasDisabled == true); + } + } + + bool isManufacturerBatteryOptimizationDisabled = GStrorage.localCache + .get('isManufacturerBatteryOptimizationDisabled', defaultValue: false); + if (!isManufacturerBatteryOptimizationDisabled) { + final isManBatteryOptimizationDisabled = await DisableBatteryOptimization + .isManufacturerBatteryOptimizationDisabled; + if (isManBatteryOptimizationDisabled == false) { + final hasDisabled = await DisableBatteryOptimization + .showDisableManufacturerBatteryOptimizationSettings( + "当前设备可能有额外的电池优化", + "按照步骤操作以禁用电池优化,以保证应用在后台正常运行", + ); + // 设置为已禁用 + GStrorage.localCache.put( + 'isManufacturerBatteryOptimizationDisabled', hasDisabled == true); + } + } +} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index fea31d56..44a3c3d7 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -170,6 +170,10 @@ class LocalCacheKey { // 代理host port systemProxyHost = 'systemProxyHost', systemProxyPort = 'systemProxyPort'; + + static const String isDisableBatteryOptLocal = 'isDisableBatteryOptLocal', + isManufacturerBatteryOptimizationDisabled = + 'isManufacturerBatteryOptimizationDisabled'; } class VideoBoxKey { diff --git a/pubspec.lock b/pubspec.lock index bfef5f26..8972ef45 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -393,6 +393,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.4.0" + disable_battery_optimization: + dependency: "direct main" + description: + name: disable_battery_optimization + sha256: "6b2ba802f984af141faf1b6b5fb956d5ef01f9cd555597c35b9cc335a03185ba" + url: "https://pub.flutter-io.cn" + source: hosted + version: "1.1.1" dismissible_page: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a5d103f6..5c25f044 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -140,6 +140,8 @@ dependencies: catcher_2: ^1.1.0 logger: ^2.0.2+1 path: 1.8.3 + # 电池优化 + disable_battery_optimization: ^1.1.1 dev_dependencies: flutter_test: From f41bb02bae23284a7b8c15a4b7b667ce4c46f1d3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 8 Mar 2024 23:05:09 +0800 Subject: [PATCH 004/349] =?UTF-8?q?fix:=20=E5=90=88=E9=9B=86=E6=9C=80?= =?UTF-8?q?=E5=90=8E=E4=B8=80p=E4=B8=8D=E6=98=BE=E7=A4=BA=20issues=20#620?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/widgets/bangumi_panel.dart | 4 ++-- lib/pages/video/detail/introduction/widgets/season.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 05889f16..20a153a0 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -144,9 +144,9 @@ class _BangumiPanelState extends State { Expanded( child: Material( child: ScrollablePositionedList.builder( - itemCount: widget.pages.length, + itemCount: widget.pages.length + 1, itemBuilder: (BuildContext context, int index) { - bool isLastItem = index == widget.pages.length - 1; + bool isLastItem = index == widget.pages.length; bool isCurrentIndex = currentIndex == index; return isLastItem ? SizedBox( diff --git a/lib/pages/video/detail/introduction/widgets/season.dart b/lib/pages/video/detail/introduction/widgets/season.dart index c6a94398..0f3884ed 100644 --- a/lib/pages/video/detail/introduction/widgets/season.dart +++ b/lib/pages/video/detail/introduction/widgets/season.dart @@ -161,9 +161,9 @@ class _SeasonPanelState extends State { Expanded( child: Material( child: ScrollablePositionedList.builder( - itemCount: episodes.length, + itemCount: episodes.length + 1, itemBuilder: (BuildContext context, int index) { - bool isLastItem = index == episodes.length - 1; + bool isLastItem = index == episodes.length; bool isCurrentIndex = currentIndex == index; return isLastItem ? SizedBox( From a3e1fd4e918521c4bc44720760615d65fba663a0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 8 Mar 2024 23:09:52 +0800 Subject: [PATCH 005/349] =?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 006/349] =?UTF-8?q?fix:=20github=E9=93=BE=E6=8E=A5=20issue?= =?UTF-8?q?s=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 007/349] =?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 008/349] =?UTF-8?q?fix:=20=E8=AF=B7=E6=B1=82github?= =?UTF-8?q?=E5=BC=82=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) { From bf37c33291655044834674d93768caaa724a0d90 Mon Sep 17 00:00:00 2001 From: yezi Date: Sat, 9 Mar 2024 19:39:21 +0800 Subject: [PATCH 009/349] =?UTF-8?q?feat:=E6=94=AF=E6=8C=81=E6=8E=92?= =?UTF-8?q?=E8=A1=8C=E6=A6=9C=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 + lib/http/video.dart | 23 +++ lib/models/common/nav_bar_config.dart | 15 +- lib/models/common/rank_type.dart | 240 ++++++++++++++++++++++++++ lib/pages/main/controller.dart | 2 + lib/pages/main/view.dart | 17 ++ lib/pages/rank/controller.dart | 70 ++++++++ lib/pages/rank/index.dart | 4 + lib/pages/rank/view.dart | 149 ++++++++++++++++ lib/pages/rank/zone/controller.dart | 53 ++++++ lib/pages/rank/zone/index.dart | 4 + lib/pages/rank/zone/view.dart | 148 ++++++++++++++++ 12 files changed, 728 insertions(+), 1 deletion(-) create mode 100644 lib/models/common/rank_type.dart create mode 100644 lib/pages/rank/controller.dart create mode 100644 lib/pages/rank/index.dart create mode 100644 lib/pages/rank/view.dart create mode 100644 lib/pages/rank/zone/controller.dart create mode 100644 lib/pages/rank/zone/index.dart create mode 100644 lib/pages/rank/zone/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 3a23ef80..8aa62233 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -499,4 +499,8 @@ class Api { /// 发送私信 static const String sendMsg = '${HttpString.tUrl}/web_im/v1/web_im/send_msg'; + + /// 排行榜 + static const String getRankApi = "/x/web-interface/ranking/v2"; + } diff --git a/lib/http/video.dart b/lib/http/video.dart index 30df62c3..73e8b698 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -475,4 +475,27 @@ class VideoHttp { return {'status': false, 'data': []}; } } + + // 视频排行 + static Future getRankVideoList(int rid) async { + try { + var rankApi = "${Api.getRankApi}?rid=$rid&type=all"; + var res = await Request().get(rankApi); + if (res.data['code'] == 0) { + List list = []; + List blackMidsList = + setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); + for (var i in res.data['data']['list']) { + if (!blackMidsList.contains(i['owner']['mid'])) { + list.add(HotVideoItemModel.fromJson(i)); + } + } + return {'status': true, 'data': list}; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } catch (err) { + return {'status': false, 'data': [], 'msg': err}; + } + } } diff --git a/lib/models/common/nav_bar_config.dart b/lib/models/common/nav_bar_config.dart index 7fb22e48..9ebe8e6f 100644 --- a/lib/models/common/nav_bar_config.dart +++ b/lib/models/common/nav_bar_config.dart @@ -16,6 +16,19 @@ List defaultNavigationBars = [ }, { 'id': 1, + 'icon': const Icon( + Icons.trending_up, + size: 21, + ), + 'selectIcon': const Icon( + Icons.trending_up_outlined, + size: 21, + ), + 'label': "排行榜", + 'count': 0, + }, + { + 'id': 2, 'icon': const Icon( Icons.motion_photos_on_outlined, size: 21, @@ -28,7 +41,7 @@ List defaultNavigationBars = [ 'count': 0, }, { - 'id': 2, + 'id': 3, 'icon': const Icon( Icons.video_collection_outlined, size: 20, diff --git a/lib/models/common/rank_type.dart b/lib/models/common/rank_type.dart new file mode 100644 index 00000000..2ce6d3b5 --- /dev/null +++ b/lib/models/common/rank_type.dart @@ -0,0 +1,240 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/pages/rank/zone/index.dart'; + +enum RandType { + all, + creation, + animation, + music, + dance, + game, + knowledge, + technology, + sport, + car, + life, + food, + animal, + madness, + fashion, + entertainment, + film, + origin, + rookie +} + +extension RankTypeDesc on RandType { + String get description => [ + '全站', + '国创相关', + '动画', + '音乐', + '舞蹈', + '游戏', + '知识', + '科技', + '运动', + '汽车', + '生活', + '美食', + '动物圈', + '鬼畜', + '时尚', + '娱乐', + '影视' + ][index]; + + String get id => [ + 'all', + 'creation', + 'animation', + 'music', + 'dance', + 'game', + 'knowledge', + 'technology', + 'sport', + 'car', + 'life', + 'food', + 'animal', + 'madness', + 'fashion', + 'entertainment', + 'film' + ][index]; +} + +List tabsConfig = [ + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '全站', + 'type': RandType.all, + 'ctr': Get.put, + 'page': const ZonePage(rid: 0), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '国创相关', + 'type': RandType.creation, + 'ctr': Get.put, + 'page': const ZonePage(rid: 168), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '动画', + 'type': RandType.animation, + 'ctr': Get.put, + 'page': const ZonePage(rid: 1), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '音乐', + 'type': RandType.music, + 'ctr': Get.put, + 'page': const ZonePage(rid: 3), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '舞蹈', + 'type': RandType.dance, + 'ctr': Get.put, + 'page': const ZonePage(rid: 129), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '游戏', + 'type': RandType.game, + 'ctr': Get.put, + 'page': const ZonePage(rid: 4), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '知识', + 'type': RandType.knowledge, + 'ctr': Get.put, + 'page': const ZonePage(rid: 36), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '科技', + 'type': RandType.technology, + 'ctr': Get.put, + 'page': const ZonePage(rid: 188), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '运动', + 'type': RandType.sport, + 'ctr': Get.put, + 'page': const ZonePage(rid: 234), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '汽车', + 'type': RandType.car, + 'ctr': Get.put, + 'page': const ZonePage(rid: 223), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '生活', + 'type': RandType.life, + 'ctr': Get.put, + 'page': const ZonePage(rid: 160), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '美食', + 'type': RandType.food, + 'ctr': Get.put, + 'page': const ZonePage(rid: 211), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '动物圈', + 'type': RandType.animal, + 'ctr': Get.put, + 'page': const ZonePage(rid: 217), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '鬼畜', + 'type': RandType.madness, + 'ctr': Get.put, + 'page': const ZonePage(rid: 119), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '时尚', + 'type': RandType.fashion, + 'ctr': Get.put, + 'page': const ZonePage(rid: 155), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '娱乐', + 'type': RandType.entertainment, + 'ctr': Get.put, + 'page': const ZonePage(rid: 5), + }, + { + 'icon': const Icon( + Icons.live_tv_outlined, + size: 15, + ), + 'label': '影视', + 'type': RandType.film, + 'ctr': Get.put, + 'page': const ZonePage(rid: 181), + } +]; diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index f3bcb75a..ddbd364a 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -9,6 +9,7 @@ import 'package:pilipala/http/common.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/view.dart'; import 'package:pilipala/pages/media/index.dart'; +import 'package:pilipala/pages/rank/index.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; import '../../models/common/dynamic_badge_mode.dart'; @@ -17,6 +18,7 @@ import '../../models/common/nav_bar_config.dart'; class MainController extends GetxController { List pages = [ const HomePage(), + const RankPage(), const DynamicsPage(), const MediaPage(), ]; diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 04e0f087..c551e690 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -7,6 +7,7 @@ import 'package:pilipala/models/common/dynamic_badge_mode.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/media/index.dart'; +import 'package:pilipala/pages/rank/index.dart'; import 'package:pilipala/utils/event_bus.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; @@ -22,6 +23,7 @@ class MainApp extends StatefulWidget { class _MainAppState extends State with SingleTickerProviderStateMixin { final MainController _mainController = Get.put(MainController()); final HomeController _homeController = Get.put(HomeController()); + final RankController _rankController = Get.put(RankController()); final DynamicsController _dynamicController = Get.put(DynamicsController()); final MediaController _mediaController = Get.put(MediaController()); @@ -57,6 +59,21 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { _homeController.flag = false; } + if (currentPage is RankPage) { + if (_rankController.flag) { + // 单击返回顶部 双击并刷新 + if (DateTime.now().millisecondsSinceEpoch - _lastSelectTime! < 500) { + _rankController.onRefresh(); + } else { + _rankController.animateToTop(); + } + _lastSelectTime = DateTime.now().millisecondsSinceEpoch; + } + _rankController.flag = true; + } else { + _rankController.flag = false; + } + if (currentPage is DynamicsPage) { if (_dynamicController.flag) { // 单击返回顶部 双击并刷新 diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart new file mode 100644 index 00000000..61475d97 --- /dev/null +++ b/lib/pages/rank/controller.dart @@ -0,0 +1,70 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/models/common/rank_type.dart'; +import 'package:pilipala/utils/storage.dart'; + +class RankController extends GetxController with GetTickerProviderStateMixin { + bool flag = false; + late RxList tabs = [].obs; + RxInt initialIndex = 1.obs; + late TabController tabController; + late List tabsCtrList; + late List tabsPageList; + Box setting = GStrorage.setting; + late final StreamController searchBarStream = + StreamController.broadcast(); + late bool enableGradientBg; + + @override + void onInit() { + super.onInit(); + enableGradientBg = + setting.get(SettingBoxKey.enableGradientBg, defaultValue: true); + // 进行tabs配置 + setTabConfig(); + } + + void onRefresh() { + int index = tabController.index; + var ctr = tabsCtrList[index]; + ctr().onRefresh(); + } + + void animateToTop() { + int index = tabController.index; + var ctr = tabsCtrList[index]; + ctr().animateToTop(); + } + + void setTabConfig() async { + tabs.value = tabsConfig; + initialIndex.value = 0; + tabsCtrList = tabs.map((e) => e['ctr']).toList(); + tabsPageList = tabs.map((e) => e['page']).toList(); + + tabController = TabController( + initialIndex: initialIndex.value, + length: tabs.length, + vsync: this, + ); + // 监听 tabController 切换 + 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; + } + } + }); + } + } +} diff --git a/lib/pages/rank/index.dart b/lib/pages/rank/index.dart new file mode 100644 index 00000000..eaac0a34 --- /dev/null +++ b/lib/pages/rank/index.dart @@ -0,0 +1,4 @@ +library rank; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/rank/view.dart b/lib/pages/rank/view.dart new file mode 100644 index 00000000..7b5b4906 --- /dev/null +++ b/lib/pages/rank/view.dart @@ -0,0 +1,149 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/utils/feed_back.dart'; +import './controller.dart'; + +class RankPage extends StatefulWidget { + const RankPage({Key? key}) : super(key: key); + + @override + State createState() => _RankPageState(); +} + +class _RankPageState extends State + with AutomaticKeepAliveClientMixin, TickerProviderStateMixin { + final RankController _rankController = Get.put(RankController()); + List videoList = []; + late Stream stream; + + @override + bool get wantKeepAlive => true; + + @override + void initState() { + super.initState(); + stream = _rankController.searchBarStream.stream; + } + + @override + Widget build(BuildContext context) { + super.build(context); + Brightness currentBrightness = MediaQuery.of(context).platformBrightness; + // 设置状态栏图标的亮度 + if (_rankController.enableGradientBg) { + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + statusBarIconBrightness: currentBrightness == Brightness.light + ? Brightness.dark + : Brightness.light, + )); + } + return Scaffold( + extendBody: true, + extendBodyBehindAppBar: false, + appBar: _rankController.enableGradientBg + ? null + : AppBar(toolbarHeight: 0, elevation: 0), + body: Stack( + children: [ + // gradient background + if (_rankController.enableGradientBg) ...[ + Align( + alignment: Alignment.topLeft, + child: Opacity( + opacity: 0.6, + child: Container( + width: MediaQuery.of(context).size.width, + height: MediaQuery.of(context).size.height, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Theme.of(context) + .colorScheme + .primary + .withOpacity(0.9), + Theme.of(context) + .colorScheme + .primary + .withOpacity(0.5), + Theme.of(context).colorScheme.surface + ], + begin: Alignment.topLeft, + end: Alignment.bottomRight, + stops: const [0, 0.0034, 0.34]), + ), + ), + ), + ), + ], + Column( + children: [ + const CustomAppBar(), + if (_rankController.tabs.length > 1) ...[ + const SizedBox(height: 4), + SizedBox( + width: double.infinity, + height: 42, + child: Align( + alignment: Alignment.center, + child: TabBar( + controller: _rankController.tabController, + tabs: [ + for (var i in _rankController.tabs) + Tab(text: i['label']) + ], + isScrollable: true, + dividerColor: Colors.transparent, + enableFeedback: true, + splashBorderRadius: BorderRadius.circular(10), + tabAlignment: TabAlignment.center, + onTap: (value) { + feedBack(); + if (_rankController.initialIndex.value == value) { + _rankController.tabsCtrList[value]().animateToTop(); + } + _rankController.initialIndex.value = value; + }, + ), + ), + ), + ] else ...[ + const SizedBox(height: 6), + ], + Expanded( + child: TabBarView( + controller: _rankController.tabController, + children: _rankController.tabsPageList, + ), + ), + ], + ), + ], + ), + ); + } +} + +class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { + final double height; + + const CustomAppBar({ + super.key, + this.height = kToolbarHeight, + }); + + @override + Size get preferredSize => Size.fromHeight(height); + + @override + Widget build(BuildContext context) { + final double top = MediaQuery.of(context).padding.top; + return Container( + width: MediaQuery.of(context).size.width, + height: top, + color: Colors.transparent, + ); + } +} diff --git a/lib/pages/rank/zone/controller.dart b/lib/pages/rank/zone/controller.dart new file mode 100644 index 00000000..f9f4dc6e --- /dev/null +++ b/lib/pages/rank/zone/controller.dart @@ -0,0 +1,53 @@ +import 'package:get/get.dart'; +import 'package:flutter/material.dart'; +import 'package:pilipala/http/video.dart'; +import 'package:pilipala/models/model_hot_video_item.dart'; + +class ZoneController extends GetxController { + final ScrollController scrollController = ScrollController(); + RxList videoList = [].obs; + bool isLoadingMore = false; + bool flag = false; + OverlayEntry? popupDialog; + int zoneID = 0; + + // 获取推荐 + Future queryRankFeed(type, rid) async { + zoneID = rid; + var res = await VideoHttp.getRankVideoList(zoneID); + if (res['status']) { + if (type == 'init') { + videoList.value = res['data']; + } else if (type == 'onRefresh') { + videoList.clear(); + videoList.addAll(res['data']); + } else if (type == 'onLoad') { + videoList.clear(); + videoList.addAll(res['data']); + } + } + isLoadingMore = false; + return res; + } + + // 下拉刷新 + Future onRefresh() async { + queryRankFeed('onRefresh', zoneID); + } + + // 上拉加载 + Future onLoad() async { + queryRankFeed('onLoad', zoneID); + } + + // 返回顶部并刷新 + void animateToTop() async { + if (scrollController.offset >= + MediaQuery.of(Get.context!).size.height * 5) { + scrollController.jumpTo(0); + } else { + await scrollController.animateTo(0, + duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); + } + } +} diff --git a/lib/pages/rank/zone/index.dart b/lib/pages/rank/zone/index.dart new file mode 100644 index 00000000..8f535736 --- /dev/null +++ b/lib/pages/rank/zone/index.dart @@ -0,0 +1,4 @@ +library rank.zone; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/rank/zone/view.dart b/lib/pages/rank/zone/view.dart new file mode 100644 index 00000000..58ca187f --- /dev/null +++ b/lib/pages/rank/zone/view.dart @@ -0,0 +1,148 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/animated_dialog.dart'; +import 'package:pilipala/common/widgets/overlay_pop.dart'; +import 'package:pilipala/common/skeleton/video_card_h.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/video_card_h.dart'; +import 'package:pilipala/pages/home/index.dart'; +import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/pages/rank/zone/index.dart'; + +class ZonePage extends StatefulWidget { + const ZonePage({Key? key, required this.rid}) : super(key: key); + + final int rid; + + @override + State createState() => _ZonePageState(); +} + +class _ZonePageState extends State { + final ZoneController _zoneController = Get.put(ZoneController()); + List videoList = []; + Future? _futureBuilderFuture; + late ScrollController scrollController; + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _zoneController.queryRankFeed('init', widget.rid); + scrollController = _zoneController.scrollController; + StreamController mainStream = + Get.find().bottomBarStream; + StreamController searchBarStream = + Get.find().searchBarStream; + scrollController.addListener( + () { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + if (!_zoneController.isLoadingMore) { + _zoneController.isLoadingMore = true; + _zoneController.onLoad(); + } + } + + final ScrollDirection direction = + scrollController.position.userScrollDirection; + if (direction == ScrollDirection.forward) { + mainStream.add(true); + searchBarStream.add(true); + } else if (direction == ScrollDirection.reverse) { + mainStream.add(false); + searchBarStream.add(false); + } + }, + ); + } + + @override + void dispose() { + scrollController.removeListener(() {}); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return RefreshIndicator( + onRefresh: () async { + return await _zoneController.onRefresh(); + }, + child: CustomScrollView( + controller: _zoneController.scrollController, + slivers: [ + SliverPadding( + // 单列布局 EdgeInsets.zero + padding: + const EdgeInsets.fromLTRB(0, StyleString.safeSpace - 5, 0, 0), + sliver: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (data['status']) { + return Obx( + () => SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return VideoCardH( + videoItem: _zoneController.videoList[index], + showPubdate: true, + longPress: () { + _zoneController.popupDialog = _createPopupDialog( + _zoneController.videoList[index]); + Overlay.of(context) + .insert(_zoneController.popupDialog!); + }, + longPressEnd: () { + _zoneController.popupDialog?.remove(); + }, + ); + }, childCount: _zoneController.videoList.length), + ), + ); + } else { + return HttpError( + errMsg: data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _zoneController.queryRankFeed('init', widget.rid); + }); + }, + ); + } + } else { + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoCardHSkeleton(); + }, childCount: 10), + ); + } + }, + ), + ), + SliverToBoxAdapter( + child: SizedBox( + height: MediaQuery.of(context).padding.bottom + 10, + ), + ) + ], + ), + ); + } + + OverlayEntry _createPopupDialog(videoItem) { + return OverlayEntry( + builder: (context) => AnimatedDialog( + closeFn: _zoneController.popupDialog?.remove, + child: OverlayPop( + videoItem: videoItem, closeFn: _zoneController.popupDialog?.remove), + ), + ); + } +} From b7b75e956fbc66fa02a31e9aace44ff39997421f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 10 Mar 2024 16:13:11 +0800 Subject: [PATCH 010/349] =?UTF-8?q?fix:=20=E5=88=A0=E9=99=A4=E5=8D=95?= =?UTF-8?q?=E4=B8=AA=E6=90=9C=E7=B4=A2=E5=8E=86=E5=8F=B2=E6=97=A0=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/search/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/search/controller.dart b/lib/pages/search/controller.dart index cbb86405..1853c238 100644 --- a/lib/pages/search/controller.dart +++ b/lib/pages/search/controller.dart @@ -115,7 +115,7 @@ class SSearchController extends GetxController { onLongSelect(word) { int index = historyList.indexOf(word); - historyList.value = historyList.removeAt(index); + historyList.removeAt(index); historyList.refresh(); histiryWord.put('cacheList', historyList); } From 3ece2bb173ad31caa0638cc400d49190718db254 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 10 Mar 2024 17:57:24 +0800 Subject: [PATCH 011/349] =?UTF-8?q?feat:=20=E7=B3=BB=E7=BB=9F=E5=80=8D?= =?UTF-8?q?=E9=80=9F=E5=8F=AF=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live_room/widgets/bottom_control.dart | 1 - lib/pages/setting/pages/play_speed_set.dart | 101 +++++++++--------- .../video/detail/widgets/header_control.dart | 1 - lib/plugin/pl_player/controller.dart | 26 ++--- lib/plugin/pl_player/models/play_speed.dart | 46 ++------ lib/utils/storage.dart | 2 + 6 files changed, 77 insertions(+), 100 deletions(-) diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index a00f3d92..7ec8864f 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -29,7 +29,6 @@ class BottomControl extends StatefulWidget implements PreferredSizeWidget { class _BottomControlState extends State { late PlayUrlModel videoInfo; - List playSpeed = PlaySpeed.values; TextStyle subTitleStyle = const TextStyle(fontSize: 12); TextStyle titleStyle = const TextStyle(fontSize: 14); Size get preferredSize => const Size(double.infinity, kToolbarHeight); diff --git a/lib/pages/setting/pages/play_speed_set.dart b/lib/pages/setting/pages/play_speed_set.dart index ceff07ed..eb81f586 100644 --- a/lib/pages/setting/pages/play_speed_set.dart +++ b/lib/pages/setting/pages/play_speed_set.dart @@ -17,6 +17,7 @@ class _PlaySpeedPageState extends State { Box videoStorage = GStrorage.video; Box settingStorage = GStrorage.setting; late double playSpeedDefault; + late List playSpeedSystem; late double longPressSpeedDefault; late List customSpeedsList; late bool enableAutoLongPressSpeed; @@ -53,6 +54,9 @@ class _PlaySpeedPageState extends State { @override void initState() { super.initState(); + // 系统预设倍速 + playSpeedSystem = + videoStorage.get(VideoBoxKey.playSpeedSystem, defaultValue: playSpeed); // 默认倍速 playSpeedDefault = videoStorage.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0); @@ -64,6 +68,7 @@ class _PlaySpeedPageState extends State { videoStorage.get(VideoBoxKey.customSpeedsList, defaultValue: []); enableAutoLongPressSpeed = settingStorage .get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false); + // 开启动态长按倍速时不展示 if (enableAutoLongPressSpeed) { Map newItem = sheetMenu[1]; newItem['show'] = false; @@ -123,7 +128,7 @@ class _PlaySpeedPageState extends State { } // 设定倍速弹窗 - void showBottomSheet(type, i) { + void showBottomSheet(String type, int i) { showModalBottomSheet( context: context, isScrollControlled: true, @@ -159,18 +164,11 @@ class _PlaySpeedPageState extends State { } // - void menuAction(type, index, id) async { + void menuAction(type, int index, id) async { double chooseSpeed = 1.0; - if (type == 'system' && id == -1) { - SmartDialog.showToast('系统预设倍速不支持删除'); - return; - } // 获取当前选中的倍速值 - if (type == 'system') { - chooseSpeed = PlaySpeed.values[index].value; - } else { - chooseSpeed = customSpeedsList[index]; - } + chooseSpeed = + type == 'system' ? playSpeedSystem[index] : customSpeedsList[index]; // 设置 if (id == 1) { // 设置默认倍速 @@ -182,17 +180,22 @@ class _PlaySpeedPageState extends State { videoStorage.put( VideoBoxKey.longPressSpeedDefault, longPressSpeedDefault); } else if (id == -1) { - if (customSpeedsList[index] == playSpeedDefault) { - playSpeedDefault = 1.0; - videoStorage.put(VideoBoxKey.playSpeedDefault, playSpeedDefault); + late List speedsList = + type == 'system' ? playSpeedSystem : customSpeedsList; + if (speedsList[index] == playSpeedDefault) { + SmartDialog.showToast('默认倍速不可删除'); } - if (customSpeedsList[index] == longPressSpeedDefault) { + if (speedsList[index] == longPressSpeedDefault) { longPressSpeedDefault = 2.0; videoStorage.put( VideoBoxKey.longPressSpeedDefault, longPressSpeedDefault); } - customSpeedsList.removeAt(index); - await videoStorage.put(VideoBoxKey.customSpeedsList, customSpeedsList); + speedsList.removeAt(index); + await videoStorage.put( + type == 'system' + ? VideoBoxKey.playSpeedSystem + : VideoBoxKey.customSpeedsList, + speedsList); } setState(() {}); SmartDialog.showToast('操作成功'); @@ -249,38 +252,40 @@ class _PlaySpeedPageState extends State { subtitle: Text(longPressSpeedDefault.toString()), ) : const SizedBox(), - Padding( - padding: const EdgeInsets.only( - left: 14, - right: 14, - bottom: 10, - top: 20, + if (playSpeedSystem.isNotEmpty) ...[ + Padding( + padding: const EdgeInsets.only( + left: 14, + right: 14, + bottom: 10, + top: 20, + ), + child: Text( + '系统预设倍速', + style: Theme.of(context).textTheme.titleMedium, + ), ), - child: Text( - '系统预设倍速', - style: Theme.of(context).textTheme.titleMedium, - ), - ), - Padding( - padding: const EdgeInsets.only( - left: 18, - right: 18, - bottom: 30, - ), - child: Wrap( - alignment: WrapAlignment.start, - spacing: 8, - runSpacing: 2, - children: [ - for (var i in PlaySpeed.values) ...[ - FilledButton.tonal( - onPressed: () => showBottomSheet('system', i.index), - child: Text(i.description), - ), - ] - ], - ), - ), + Padding( + padding: const EdgeInsets.only( + left: 18, + right: 18, + bottom: 30, + ), + child: Wrap( + alignment: WrapAlignment.start, + spacing: 8, + runSpacing: 2, + children: [ + for (int i = 0; i < playSpeedSystem.length; i++) ...[ + FilledButton.tonal( + onPressed: () => showBottomSheet('system', i), + child: Text(playSpeedSystem[i].toString()), + ), + ] + ], + ), + ) + ], Padding( padding: const EdgeInsets.only( left: 14, diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e72e6a69..e07e618e 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -47,7 +47,6 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { class _HeaderControlState extends State { late PlayUrlModel videoInfo; - List playSpeed = PlaySpeed.values; static const TextStyle subTitleStyle = TextStyle(fontSize: 12); static const TextStyle titleStyle = TextStyle(fontSize: 14); Size get preferredSize => const Size(double.infinity, kToolbarHeight); diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index b4b37cea..aaded67d 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -292,11 +292,19 @@ class PlPlayerController { _longPressSpeed.value = videoStorage .get(VideoBoxKey.longPressSpeedDefault, defaultValue: 2.0); } + // 自定义倍速集合 speedsList = List.from(videoStorage .get(VideoBoxKey.customSpeedsList, defaultValue: [])); - for (final PlaySpeed i in PlaySpeed.values) { - speedsList.add(i.value); - } + // 默认倍速 + speedsList = List.from(videoStorage + .get(VideoBoxKey.customSpeedsList, defaultValue: [])); + //playSpeedSystem + final List playSpeedSystem = + videoStorage.get(VideoBoxKey.playSpeedSystem, defaultValue: playSpeed); + + // for (final PlaySpeed i in PlaySpeed.values) { + speedsList.addAll(playSpeedSystem); + // } // _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) { // if (status == PlayerStatus.playing) { @@ -676,18 +684,6 @@ class PlPlayerController { _playbackSpeed.value = speed; } - /// 设置倍速 - // Future togglePlaybackSpeed() async { - // List allowedSpeeds = - // PlaySpeed.values.map((e) => e.value).toList(); - // int index = allowedSpeeds.indexOf(_playbackSpeed.value); - // if (index < allowedSpeeds.length - 1) { - // setPlaybackSpeed(allowedSpeeds[index + 1]); - // } else { - // setPlaybackSpeed(allowedSpeeds[0]); - // } - // } - /// 播放视频 /// TODO _duration.value丢失 Future play( diff --git a/lib/plugin/pl_player/models/play_speed.dart b/lib/plugin/pl_player/models/play_speed.dart index fd699eaf..8bb25118 100644 --- a/lib/plugin/pl_player/models/play_speed.dart +++ b/lib/plugin/pl_player/models/play_speed.dart @@ -1,39 +1,15 @@ -enum PlaySpeed { - pointTwoFive, - pointFive, - pointSevenFive, +List generatePlaySpeedList() { + List playSpeed = []; + double startSpeed = 0.25; + double endSpeed = 2.0; + double increment = 0.25; - one, - onePointTwoFive, - onePointFive, - onePointSevenFive, + for (double speed = startSpeed; speed <= endSpeed; speed += increment) { + playSpeed.add(speed); + } - two, + return playSpeed; } -extension PlaySpeedExtension on PlaySpeed { - static final List _descList = [ - '0.25', - '0.5', - '0.75', - '正常', - '1.25', - '1.5', - '1.75', - '2.0', - ]; - String get description => _descList[index]; - - static final List _valueList = [ - 0.25, - 0.5, - 0.75, - 1.0, - 1.25, - 1.5, - 1.75, - 2.0, - ]; - double get value => _valueList[index]; - double get defaultValue => _valueList[3]; -} +// 导出 playSpeed 列表 +List playSpeed = generatePlaySpeedList(); diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index fea31d56..6283299b 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -181,6 +181,8 @@ class VideoBoxKey { videoSpeed = 'videoSpeed', // 播放顺序 playRepeat = 'playRepeat', + // 系统预设倍速 + playSpeedSystem = 'playSpeedSystem', // 默认倍速 playSpeedDefault = 'playSpeedDefault', // 默认长按倍速 From 6fdfcb888d968a871aa80cb9dd63530fba2fa6bb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 10 Mar 2024 19:41:23 +0800 Subject: [PATCH 012/349] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E5=BA=95=E9=83=A8=E6=8E=A7=E5=88=B6=E6=A0=8F=E8=87=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 --- .../pl_player/models/bottom_control_type.dart | 10 + lib/plugin/pl_player/view.dart | 300 ++++++++++++------ .../pl_player/widgets/bottom_control.dart | 177 ++++++----- 3 files changed, 296 insertions(+), 191 deletions(-) create mode 100644 lib/plugin/pl_player/models/bottom_control_type.dart diff --git a/lib/plugin/pl_player/models/bottom_control_type.dart b/lib/plugin/pl_player/models/bottom_control_type.dart new file mode 100644 index 00000000..599f6e4f --- /dev/null +++ b/lib/plugin/pl_player/models/bottom_control_type.dart @@ -0,0 +1,10 @@ +enum BottomControlType { + pre, + playOrPause, + next, + time, + space, + fit, + speed, + fullscreen, +} diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 988ec26d..0b2e652e 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -8,7 +8,6 @@ import 'package:get/get.dart'; 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'; @@ -19,12 +18,14 @@ import 'package:pilipala/utils/storage.dart'; import 'package:screen_brightness/screen_brightness.dart'; import '../../utils/global_data.dart'; +import 'models/bottom_control_type.dart'; import 'models/bottom_progress_behavior.dart'; import 'widgets/app_bar_ani.dart'; import 'widgets/backward_seek.dart'; import 'widgets/bottom_control.dart'; import 'widgets/common_btn.dart'; import 'widgets/forward_seek.dart'; +import 'widgets/play_pause_btn.dart'; class PLVideoPlayer extends StatefulWidget { const PLVideoPlayer({ @@ -32,6 +33,7 @@ class PLVideoPlayer extends StatefulWidget { this.headerControl, this.bottomControl, this.danmuWidget, + this.bottomList, super.key, }); @@ -39,6 +41,7 @@ class PLVideoPlayer extends StatefulWidget { final PreferredSizeWidget? headerControl; final PreferredSizeWidget? bottomControl; final Widget? danmuWidget; + final List? bottomList; @override State createState() => _PLVideoPlayerState(); @@ -48,26 +51,22 @@ class _PLVideoPlayerState extends State with TickerProviderStateMixin { late AnimationController animationController; late VideoController videoController; - final PLVideoPlayerController _ctr = Get.put(PLVideoPlayerController()); - // bool _mountSeekBackwardButton = false; - // bool _mountSeekForwardButton = false; - // bool _hideSeekBackwardButton = false; - // bool _hideSeekForwardButton = false; + final RxBool _mountSeekBackwardButton = false.obs; + final RxBool _mountSeekForwardButton = false.obs; + final RxBool _hideSeekBackwardButton = false.obs; + final RxBool _hideSeekForwardButton = false.obs; - // double _brightnessValue = 0.0; - // bool _brightnessIndicator = false; + final RxDouble _brightnessValue = 0.0.obs; + final RxBool _brightnessIndicator = false.obs; Timer? _brightnessTimer; - // double _volumeValue = 0.0; - // bool _volumeIndicator = false; + final RxDouble _volumeValue = 0.0.obs; + final RxBool _volumeIndicator = false.obs; Timer? _volumeTimer; - double _distance = 0.0; - // 初始手指落下位置 - // double _initTapPositoin = 0.0; - - // bool _volumeInterceptEventStream = false; + final RxDouble _distance = 0.0.obs; + final RxBool _volumeInterceptEventStream = false.obs; Box setting = GStrorage.setting; late FullScreenMode mode; @@ -82,11 +81,11 @@ class _PLVideoPlayerState extends State DateTime? lastFullScreenToggleTime; void onDoubleTapSeekBackward() { - _ctr.onDoubleTapSeekBackward(); + _mountSeekBackwardButton.value = true; } void onDoubleTapSeekForward() { - _ctr.onDoubleTapSeekForward(); + _mountSeekForwardButton.value = true; } // 双击播放、暂停 @@ -138,10 +137,10 @@ class _PLVideoPlayerState extends State Future.microtask(() async { try { FlutterVolumeController.updateShowSystemUI(true); - _ctr.volumeValue.value = (await FlutterVolumeController.getVolume())!; + _volumeValue.value = (await FlutterVolumeController.getVolume())!; FlutterVolumeController.addListener((double value) { - if (mounted && !_ctr.volumeInterceptEventStream.value) { - _ctr.volumeValue.value = value; + if (mounted && !_volumeInterceptEventStream.value) { + _volumeValue.value = value; } }); } catch (_) {} @@ -149,10 +148,10 @@ class _PLVideoPlayerState extends State Future.microtask(() async { try { - _ctr.brightnessValue.value = await ScreenBrightness().current; + _brightnessValue.value = await ScreenBrightness().current; ScreenBrightness().onCurrentBrightnessChanged.listen((double value) { if (mounted) { - _ctr.brightnessValue.value = value; + _brightnessValue.value = value; } }); } catch (_) {} @@ -164,14 +163,14 @@ class _PLVideoPlayerState extends State FlutterVolumeController.updateShowSystemUI(false); await FlutterVolumeController.setVolume(value); } catch (_) {} - _ctr.volumeValue.value = value; - _ctr.volumeIndicator.value = true; - _ctr.volumeInterceptEventStream.value = true; + _volumeValue.value = value; + _volumeIndicator.value = true; + _volumeInterceptEventStream.value = true; _volumeTimer?.cancel(); _volumeTimer = Timer(const Duration(milliseconds: 200), () { if (mounted) { - _ctr.volumeIndicator.value = false; - _ctr.volumeInterceptEventStream.value = false; + _volumeIndicator.value = false; + _volumeInterceptEventStream.value = false; } }); } @@ -180,11 +179,11 @@ class _PLVideoPlayerState extends State try { await ScreenBrightness().setScreenBrightness(value); } catch (_) {} - _ctr.brightnessIndicator.value = true; + _brightnessIndicator.value = true; _brightnessTimer?.cancel(); _brightnessTimer = Timer(const Duration(milliseconds: 200), () { if (mounted) { - _ctr.brightnessIndicator.value = false; + _brightnessIndicator.value = false; } }); widget.controller.brightness.value = value; @@ -197,6 +196,134 @@ class _PLVideoPlayerState extends State super.dispose(); } + // 动态构建底部控制条 + List buildBottomControl() { + const TextStyle textStyle = TextStyle( + color: Colors.white, + fontSize: 12, + ); + final PlPlayerController _ = widget.controller; + Map videoProgressWidgets = { + /// 上一集 + BottomControlType.pre: ComBtn( + icon: const Icon( + Icons.skip_previous_outlined, + size: 15, + color: Colors.white, + ), + fuc: () {}, + ), + + /// 播放暂停 + BottomControlType.playOrPause: PlayOrPauseButton( + controller: _, + ), + + /// 下一集 + BottomControlType.next: ComBtn( + icon: const Icon( + Icons.last_page_outlined, + size: 15, + color: Colors.white, + ), + fuc: () {}, + ), + + /// 时间进度 + BottomControlType.time: Row( + children: [ + Obx(() { + return Text( + _.durationSeconds.value >= 3600 + ? printDurationWithHours( + Duration(seconds: _.positionSeconds.value)) + : printDuration(Duration(seconds: _.positionSeconds.value)), + style: textStyle, + ); + }), + const SizedBox(width: 2), + const Text('/', style: textStyle), + const SizedBox(width: 2), + Obx( + () => Text( + _.durationSeconds.value >= 3600 + ? printDurationWithHours( + Duration(seconds: _.durationSeconds.value)) + : printDuration(Duration(seconds: _.durationSeconds.value)), + style: textStyle, + ), + ), + ], + ), + + /// 空白占位 + BottomControlType.space: const Spacer(), + + /// 画面比例 + BottomControlType.fit: SizedBox( + height: 30, + child: TextButton( + onPressed: () => _.toggleVideoFit(), + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + child: Obx( + () => Text( + _.videoFitDEsc.value, + style: const TextStyle(color: Colors.white, fontSize: 13), + ), + ), + ), + ), + + /// 播放速度 + BottomControlType.speed: SizedBox( + width: 45, + height: 34, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () {}, + child: Obx( + () => Text( + '${_.playbackSpeed.toString()}X', + style: textStyle, + ), + ), + ), + ), + + /// 字幕 + /// 全屏 + BottomControlType.fullscreen: ComBtn( + icon: Obx( + () => Icon( + _.isFullScreen.value + ? FontAwesomeIcons.compress + : FontAwesomeIcons.expand, + size: 15, + color: Colors.white, + ), + ), + fuc: () => _.triggerFullScreen(), + ), + }; + final List list = []; + var userSpecifyItem = widget.bottomList ?? + [ + BottomControlType.playOrPause, + BottomControlType.time, + BottomControlType.space, + BottomControlType.fit, + BottomControlType.fullscreen, + ]; + for (var i = 0; i < userSpecifyItem.length; i++) { + list.add(videoProgressWidgets[userSpecifyItem[i]]!); + } + return list; + } + @override Widget build(BuildContext context) { final PlPlayerController _ = widget.controller; @@ -316,7 +443,7 @@ class _PLVideoPlayerState extends State () => Align( child: AnimatedOpacity( curve: Curves.easeInOut, - opacity: _ctr.volumeIndicator.value ? 1.0 : 0.0, + opacity: _volumeIndicator.value ? 1.0 : 0.0, duration: const Duration(milliseconds: 150), child: Container( alignment: Alignment.center, @@ -335,9 +462,9 @@ class _PLVideoPlayerState extends State width: 28.0, alignment: Alignment.centerRight, child: Icon( - _ctr.volumeValue.value == 0.0 + _volumeValue.value == 0.0 ? Icons.volume_off - : _ctr.volumeValue.value < 0.5 + : _volumeValue.value < 0.5 ? Icons.volume_down : Icons.volume_up, color: const Color(0xFFFFFFFF), @@ -346,7 +473,7 @@ class _PLVideoPlayerState extends State ), Expanded( child: Text( - '${(_ctr.volumeValue.value * 100.0).round()}%', + '${(_volumeValue.value * 100.0).round()}%', textAlign: TextAlign.center, style: const TextStyle( fontSize: 13.0, @@ -367,7 +494,7 @@ class _PLVideoPlayerState extends State () => Align( child: AnimatedOpacity( curve: Curves.easeInOut, - opacity: _ctr.brightnessIndicator.value ? 1.0 : 0.0, + opacity: _brightnessIndicator.value ? 1.0 : 0.0, duration: const Duration(milliseconds: 150), child: Container( alignment: Alignment.center, @@ -386,9 +513,9 @@ class _PLVideoPlayerState extends State width: 28.0, alignment: Alignment.centerRight, child: Icon( - _ctr.brightnessValue.value < 1.0 / 3.0 + _brightnessValue.value < 1.0 / 3.0 ? Icons.brightness_low - : _ctr.brightnessValue.value < 2.0 / 3.0 + : _brightnessValue.value < 2.0 / 3.0 ? Icons.brightness_medium : Icons.brightness_high, color: const Color(0xFFFFFFFF), @@ -398,7 +525,7 @@ class _PLVideoPlayerState extends State const SizedBox(width: 2.0), Expanded( child: Text( - '${(_ctr.brightnessValue.value * 100.0).round()}%', + '${(_brightnessValue.value * 100.0).round()}%', textAlign: TextAlign.center, style: const TextStyle( fontSize: 13.0, @@ -489,7 +616,6 @@ class _PLVideoPlayerState extends State pos.clamp(Duration.zero, _.duration.value); _.onUpdatedSliderProgress(result); _.onChangedSliderStart(); - // _initTapPositoin = tapPosition; }, onHorizontalDragEnd: (DragEndDetails details) { if (_.videoType.value == 'live' || _.controlsLock.value) { @@ -521,7 +647,7 @@ class _PLVideoPlayerState extends State : screenWidth * 9 / 16) * 3; final double brightness = - _ctr.brightnessValue.value - delta / level; + _brightnessValue.value - delta / level; final double result = brightness.clamp(0.0, 1.0); setBrightness(result); } else if (tapPosition < sectionWidth * 2) { @@ -530,29 +656,29 @@ class _PLVideoPlayerState extends State const double threshold = 7.0; // 滑动阈值 final bool flag = fullScreenGestureMode != FullScreenGestureMode.values.last; - if (dy > _distance && dy > threshold) { + if (dy > _distance.value && dy > threshold) { if (_.isFullScreen.value ^ flag) { lastFullScreenToggleTime = DateTime.now(); // 下滑退出全屏 await widget.controller.triggerFullScreen(status: flag); } - _distance = 0.0; - } else if (dy < _distance && dy < -threshold) { + _distance.value = 0.0; + } else if (dy < _distance.value && dy < -threshold) { if (!_.isFullScreen.value ^ flag) { lastFullScreenToggleTime = DateTime.now(); // 上滑进入全屏 await widget.controller.triggerFullScreen(status: !flag); } - _distance = 0.0; + _distance.value = 0.0; } - _distance = dy; + _distance.value = dy; } else { // 右边区域 👈 final double level = (_.isFullScreen.value ? Get.size.height : screenWidth * 9 / 16) * 3; - final double volume = _ctr.volumeValue.value - delta / level; + final double volume = _volumeValue.value - delta / level; final double result = volume.clamp(0.0, 1.0); setVolume(result); } @@ -585,9 +711,10 @@ class _PLVideoPlayerState extends State position: 'bottom', child: widget.bottomControl ?? BottomControl( - controller: widget.controller, - triggerFullScreen: - widget.controller.triggerFullScreen), + controller: widget.controller, + triggerFullScreen: _.triggerFullScreen, + buildBottomControl: buildBottomControl(), + ), ), ), ], @@ -607,23 +734,23 @@ class _PLVideoPlayerState extends State } if (defaultBtmProgressBehavior == BtmProgresBehavior.alwaysHide.code) { - return nil; + return const SizedBox(); } if (defaultBtmProgressBehavior == BtmProgresBehavior.onlyShowFullScreen.code && !_.isFullScreen.value) { - return nil; + return const SizedBox(); } else if (defaultBtmProgressBehavior == BtmProgresBehavior.onlyHideFullScreen.code && _.isFullScreen.value) { - return nil; + return const SizedBox(); } if (_.videoType.value == 'live') { return const SizedBox(); } if (value > max || max <= 0) { - return nil; + return const SizedBox(); } return Positioned( bottom: -1.5, @@ -720,18 +847,17 @@ class _PLVideoPlayerState extends State /// 点击 快进/快退 Obx( () => Visibility( - visible: _ctr.mountSeekBackwardButton.value || - _ctr.mountSeekForwardButton.value, + visible: + _mountSeekBackwardButton.value || _mountSeekForwardButton.value, child: Positioned.fill( child: Row( children: [ Expanded( - child: _ctr.mountSeekBackwardButton.value + child: _mountSeekBackwardButton.value ? TweenAnimationBuilder( tween: Tween( begin: 0.0, - end: - _ctr.hideSeekBackwardButton.value ? 0.0 : 1.0, + end: _hideSeekBackwardButton.value ? 0.0 : 1.0, ), duration: const Duration(milliseconds: 500), builder: (BuildContext context, double value, @@ -741,17 +867,15 @@ class _PLVideoPlayerState extends State child: child, ), onEnd: () { - if (_ctr.hideSeekBackwardButton.value) { - _ctr.hideSeekBackwardButton.value = false; - _ctr.mountSeekBackwardButton.value = false; + if (_hideSeekBackwardButton.value) { + _hideSeekBackwardButton.value = false; + _mountSeekBackwardButton.value = false; } }, child: BackwardSeekIndicator( - onChanged: (Duration value) { - // _seekBarDeltaValueNotifier.value = -value; - }, + onChanged: (Duration value) => {}, onSubmitted: (Duration value) { - _ctr.hideSeekBackwardButton.value = true; + _hideSeekBackwardButton.value = true; final Player player = widget.controller.videoPlayerController!; Duration result = player.state.position - value; @@ -764,7 +888,7 @@ class _PLVideoPlayerState extends State }, ), ) - : nil, + : const SizedBox(), ), Expanded( child: SizedBox( @@ -772,11 +896,11 @@ class _PLVideoPlayerState extends State ), ), Expanded( - child: _ctr.mountSeekForwardButton.value + child: _mountSeekForwardButton.value ? TweenAnimationBuilder( tween: Tween( begin: 0.0, - end: _ctr.hideSeekForwardButton.value ? 0.0 : 1.0, + end: _hideSeekForwardButton.value ? 0.0 : 1.0, ), duration: const Duration(milliseconds: 500), builder: (BuildContext context, double value, @@ -786,17 +910,15 @@ class _PLVideoPlayerState extends State child: child, ), onEnd: () { - if (_ctr.hideSeekForwardButton.value) { - _ctr.hideSeekForwardButton.value = false; - _ctr.mountSeekForwardButton.value = false; + if (_hideSeekForwardButton.value) { + _hideSeekForwardButton.value = false; + _mountSeekForwardButton.value = false; } }, child: ForwardSeekIndicator( - onChanged: (Duration value) { - // _seekBarDeltaValueNotifier.value = value; - }, + onChanged: (Duration value) => {}, onSubmitted: (Duration value) { - _ctr.hideSeekForwardButton.value = true; + _hideSeekForwardButton.value = true; final Player player = widget.controller.videoPlayerController!; Duration result = player.state.position + value; @@ -809,7 +931,7 @@ class _PLVideoPlayerState extends State }, ), ) - : nil, + : const SizedBox(), ), ], ), @@ -820,31 +942,3 @@ class _PLVideoPlayerState extends State ); } } - -class PLVideoPlayerController extends GetxController { - RxBool mountSeekBackwardButton = false.obs; - RxBool mountSeekForwardButton = false.obs; - RxBool hideSeekBackwardButton = false.obs; - RxBool hideSeekForwardButton = false.obs; - - RxDouble brightnessValue = 0.0.obs; - RxBool brightnessIndicator = false.obs; - - RxDouble volumeValue = 0.0.obs; - RxBool volumeIndicator = false.obs; - - RxDouble distance = 0.0.obs; - // 初始手指落下位置 - RxDouble initTapPositoin = 0.0.obs; - - RxBool volumeInterceptEventStream = false.obs; - - // 双击快进 展示样式 - void onDoubleTapSeekForward() { - mountSeekForwardButton.value = true; - } - - void onDoubleTapSeekBackward() { - mountSeekBackwardButton.value = true; - } -} diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index 8f21fc17..ebb71b54 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -1,17 +1,20 @@ import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; import 'package:flutter/material.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:nil/nil.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; -import 'package:pilipala/plugin/pl_player/widgets/play_pause_btn.dart'; import 'package:pilipala/utils/feed_back.dart'; class BottomControl extends StatelessWidget implements PreferredSizeWidget { final PlPlayerController? controller; final Function? triggerFullScreen; - const BottomControl({this.controller, this.triggerFullScreen, Key? key}) - : super(key: key); + final List? buildBottomControl; + const BottomControl({ + this.controller, + this.triggerFullScreen, + this.buildBottomControl, + Key? key, + }) : super(key: key); @override Size get preferredSize => const Size(double.infinity, kToolbarHeight); @@ -20,11 +23,6 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { Widget build(BuildContext context) { Color colorTheme = Theme.of(context).colorScheme.primary; final _ = controller!; - const textStyle = TextStyle( - color: Colors.white, - fontSize: 12, - ); - return Container( color: Colors.transparent, height: 90, @@ -71,86 +69,89 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { }, ), Row( - children: [ - PlayOrPauseButton( - controller: _, - ), - const SizedBox(width: 4), - // 播放时间 - Obx(() { - return Text( - _.durationSeconds.value >= 3600 - ? printDurationWithHours( - Duration(seconds: _.positionSeconds.value)) - : printDuration( - Duration(seconds: _.positionSeconds.value)), - style: textStyle, - ); - }), - const SizedBox(width: 2), - const Text('/', style: textStyle), - const SizedBox(width: 2), - Obx( - () => Text( - _.durationSeconds.value >= 3600 - ? printDurationWithHours( - Duration(seconds: _.durationSeconds.value)) - : printDuration( - Duration(seconds: _.durationSeconds.value)), - style: textStyle, - ), - ), - const Spacer(), - // 倍速 - // Obx( - // () => SizedBox( - // width: 45, - // height: 34, - // child: TextButton( - // style: ButtonStyle( - // padding: MaterialStateProperty.all(EdgeInsets.zero), - // ), - // onPressed: () { - // _.togglePlaybackSpeed(); - // }, - // child: Text( - // '${_.playbackSpeed.toString()}X', - // style: textStyle, - // ), - // ), - // ), - // ), - SizedBox( - height: 30, - child: TextButton( - onPressed: () => _.toggleVideoFit(), - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - child: Obx( - () => Text( - _.videoFitDEsc.value, - style: const TextStyle(color: Colors.white, fontSize: 13), - ), - ), - ), - ), - const SizedBox(width: 10), - // 全屏 - Obx( - () => ComBtn( - icon: Icon( - _.isFullScreen.value - ? FontAwesomeIcons.compress - : FontAwesomeIcons.expand, - size: 15, - color: Colors.white, - ), - fuc: () => triggerFullScreen!(), - ), - ), - ], + children: [...buildBottomControl!], ), + // Row( + // children: [ + // PlayOrPauseButton( + // controller: _, + // ), + // const SizedBox(width: 4), + // // 播放时间 + // Obx(() { + // return Text( + // _.durationSeconds.value >= 3600 + // ? printDurationWithHours( + // Duration(seconds: _.positionSeconds.value)) + // : printDuration( + // Duration(seconds: _.positionSeconds.value)), + // style: textStyle, + // ); + // }), + // const SizedBox(width: 2), + // const Text('/', style: textStyle), + // const SizedBox(width: 2), + // Obx( + // () => Text( + // _.durationSeconds.value >= 3600 + // ? printDurationWithHours( + // Duration(seconds: _.durationSeconds.value)) + // : printDuration( + // Duration(seconds: _.durationSeconds.value)), + // style: textStyle, + // ), + // ), + // const Spacer(), + // // 倍速 + // // Obx( + // // () => SizedBox( + // // width: 45, + // // height: 34, + // // child: TextButton( + // // style: ButtonStyle( + // // padding: MaterialStateProperty.all(EdgeInsets.zero), + // // ), + // // onPressed: () { + // // _.togglePlaybackSpeed(); + // // }, + // // child: Text( + // // '${_.playbackSpeed.toString()}X', + // // style: textStyle, + // // ), + // // ), + // // ), + // // ), + // SizedBox( + // height: 30, + // child: TextButton( + // onPressed: () => _.toggleVideoFit(), + // style: ButtonStyle( + // padding: MaterialStateProperty.all(EdgeInsets.zero), + // ), + // child: Obx( + // () => Text( + // _.videoFitDEsc.value, + // style: const TextStyle(color: Colors.white, fontSize: 13), + // ), + // ), + // ), + // ), + // const SizedBox(width: 10), + // // 全屏 + // Obx( + // () => ComBtn( + // icon: Icon( + // _.isFullScreen.value + // ? FontAwesomeIcons.compress + // : FontAwesomeIcons.expand, + // size: 15, + // color: Colors.white, + // ), + // fuc: () => triggerFullScreen!(), + // ), + // ), + // ], + // ), const SizedBox(height: 12), ], ), From 31405750e619914c0dabb0dfbdb015270203b1e0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 10 Mar 2024 22:44:07 +0800 Subject: [PATCH 013/349] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 84 ++++ lib/pages/video/detail/view.dart | 450 +++++++++--------- .../video/detail/widgets/header_control.dart | 119 ----- 3 files changed, 308 insertions(+), 345 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index b4005b5a..40723efa 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -5,6 +5,7 @@ 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:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/reply_type.dart'; @@ -19,6 +20,7 @@ import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/video_utils.dart'; import 'package:screen_brightness/screen_brightness.dart'; +import '../../../http/danmaku.dart'; import '../../../utils/id_utils.dart'; import 'widgets/header_control.dart'; @@ -383,4 +385,86 @@ class VideoDetailController extends GetxController ? replyReplyBottomSheetCtr!.close() : print('replyReplyBottomSheetCtr is null'); } + + /// 发送弹幕 + void showShootDanmakuSheet() { + final TextEditingController textController = TextEditingController(); + bool isSending = false; // 追踪是否正在发送 + showDialog( + context: Get.context!, + builder: (BuildContext context) { + // TODO: 支持更多类型和颜色的弹幕 + return AlertDialog( + title: const Text('发送弹幕'), + content: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return TextField( + controller: textController, + ); + }), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return TextButton( + onPressed: isSending + ? null + : () async { + final String msg = textController.text; + if (msg.isEmpty) { + SmartDialog.showToast('弹幕内容不能为空'); + return; + } else if (msg.length > 100) { + SmartDialog.showToast('弹幕内容不能超过100个字符'); + return; + } + setState(() { + isSending = true; // 开始发送,更新状态 + }); + //修改按钮文字 + // SmartDialog.showToast('弹幕发送中,\n$msg'); + final dynamic res = await DanmakaHttp.shootDanmaku( + oid: cid.value, + msg: textController.text, + bvid: bvid, + progress: + plPlayerController.position.value.inMilliseconds, + type: 1, + ); + setState(() { + isSending = false; // 发送结束,更新状态 + }); + if (res['status']) { + SmartDialog.showToast('发送成功'); + // 发送成功,自动预览该弹幕,避免重新请求 + // TODO: 暂停状态下预览弹幕仍会移动与计时,可考虑添加到dmSegList或其他方式实现 + plPlayerController.danmakuController?.addItems([ + DanmakuItem( + msg, + color: Colors.white, + time: plPlayerController + .position.value.inMilliseconds, + type: DanmakuItemType.scroll, + isSend: true, + ) + ]); + Get.back(); + } else { + SmartDialog.showToast('发送失败,错误信息为${res['msg']}'); + } + }, + child: Text(isSending ? '发送中...' : '发送'), + ); + }) + ], + ); + }, + ); + } } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 04d41b2e..e8ca935d 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -25,7 +25,7 @@ import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/storage.dart'; import '../../../services/shutdown_timer_service.dart'; -import 'widgets/header_control.dart'; +import 'widgets/app_bar.dart'; class VideoDetailPage extends StatefulWidget { const VideoDetailPage({Key? key}) : super(key: key); @@ -38,7 +38,7 @@ class VideoDetailPage extends StatefulWidget { class _VideoDetailPageState extends State with TickerProviderStateMixin, RouteAware { - late VideoDetailController videoDetailController; + late VideoDetailController vdCtr; PlPlayerController? plPlayerController; final ScrollController _extendNestCtr = ScrollController(); late StreamController appbarStream; @@ -65,20 +65,18 @@ class _VideoDetailPageState extends State void initState() { super.initState(); heroTag = Get.arguments['heroTag']; - videoDetailController = Get.put(VideoDetailController(), tag: heroTag); + vdCtr = Get.put(VideoDetailController(), tag: heroTag); videoIntroController = Get.put( VideoIntroController(bvid: Get.parameters['bvid']!), tag: heroTag); videoIntroController.videoDetail.listen((value) { - videoPlayerServiceHandler.onVideoDetailChange( - value, videoDetailController.cid.value); + videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); }); bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); bangumiIntroController.bangumiDetail.listen((value) { - videoPlayerServiceHandler.onVideoDetailChange( - value, videoDetailController.cid.value); + videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); }); - videoDetailController.cid.listen((p0) { + vdCtr.cid.listen((p0) { videoPlayerServiceHandler.onVideoDetailChange( bangumiIntroController.bangumiDetail.value, p0); }); @@ -93,16 +91,16 @@ class _VideoDetailPageState extends State appbarStreamListen(); fullScreenStatusListener(); if (Platform.isAndroid) { - floating = videoDetailController.floating!; + floating = vdCtr.floating!; autoEnterPip(); } } // 获取视频资源,初始化播放器 Future videoSourceInit() async { - _futureBuilderFuture = videoDetailController.queryVideoUrl(); - if (videoDetailController.autoPlay.value) { - plPlayerController = videoDetailController.plPlayerController; + _futureBuilderFuture = vdCtr.queryVideoUrl(); + if (vdCtr.autoPlay.value) { + plPlayerController = vdCtr.plPlayerController; plPlayerController!.addStatusLister(playerListener); } } @@ -131,10 +129,10 @@ class _VideoDetailPageState extends State /// 顺序播放 列表循环 if (plPlayerController!.playRepeat != PlayRepeat.pause && plPlayerController!.playRepeat != PlayRepeat.singleCycle) { - if (videoDetailController.videoType == SearchType.video) { + if (vdCtr.videoType == SearchType.video) { videoIntroController.nextPlay(); } - if (videoDetailController.videoType == SearchType.media_bangumi) { + if (vdCtr.videoType == SearchType.media_bangumi) { bangumiIntroController.nextPlay(); } } @@ -146,8 +144,7 @@ class _VideoDetailPageState extends State } // 播放完展示控制栏 try { - PiPStatus currentStatus = - await videoDetailController.floating!.pipStatus; + PiPStatus currentStatus = await vdCtr.floating!.pipStatus; if (currentStatus == PiPStatus.disabled) { plPlayerController!.onLockControl(false); } @@ -168,17 +165,17 @@ class _VideoDetailPageState extends State /// 未开启自动播放时触发播放 Future handlePlay() async { - await videoDetailController.playerInit(); - plPlayerController = videoDetailController.plPlayerController; + await vdCtr.playerInit(); + plPlayerController = vdCtr.plPlayerController; plPlayerController!.addStatusLister(playerListener); - videoDetailController.autoPlay.value = true; - videoDetailController.isShowCover.value = false; + vdCtr.autoPlay.value = true; + vdCtr.isShowCover.value = false; } void fullScreenStatusListener() { plPlayerController?.isFullScreen.listen((bool isFullScreen) { if (isFullScreen) { - videoDetailController.hiddenReplyReplyPanel(); + vdCtr.hiddenReplyReplyPanel(); } }); } @@ -190,8 +187,8 @@ class _VideoDetailPageState extends State plPlayerController!.removeStatusLister(playerListener); plPlayerController!.dispose(); } - if (videoDetailController.floating != null) { - videoDetailController.floating!.dispose(); + if (vdCtr.floating != null) { + vdCtr.floating!.dispose(); } videoPlayerServiceHandler.onVideoDetailDispose(); if (Platform.isAndroid) { @@ -207,10 +204,10 @@ class _VideoDetailPageState extends State /// 开启 if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false) as bool) { - videoDetailController.brightness = plPlayerController!.brightness.value; + vdCtr.brightness = plPlayerController!.brightness.value; } if (plPlayerController != null) { - videoDetailController.defaultST = plPlayerController!.position.value; + vdCtr.defaultST = plPlayerController!.position.value; videoIntroController.isPaused = true; plPlayerController!.removeStatusLister(playerListener); plPlayerController!.pause(); @@ -226,17 +223,16 @@ class _VideoDetailPageState extends State plPlayerController!.videoPlayerController != null) { setState(() => isShowing = true); } - videoDetailController.isFirstTime = false; + vdCtr.isFirstTime = false; final bool autoplay = autoPlayEnable; - videoDetailController.playerInit(autoplay: autoplay); + vdCtr.playerInit(autoplay: autoplay); /// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回 - videoDetailController.autoPlay.value = - !videoDetailController.isShowCover.value; + vdCtr.autoPlay.value = !vdCtr.isShowCover.value; videoIntroController.isPaused = false; if (_extendNestCtr.position.pixels == 0 && autoplay) { await Future.delayed(const Duration(milliseconds: 300)); - plPlayerController?.seekTo(videoDetailController.defaultST); + plPlayerController?.seekTo(vdCtr.defaultST); plPlayerController?.play(); } plPlayerController?.addStatusLister(playerListener); @@ -259,9 +255,158 @@ class _VideoDetailPageState extends State @override Widget build(BuildContext context) { - final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; + // final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; + final sizeContext = MediaQuery.sizeOf(context); + final _context = MediaQuery.of(context); + late double defaultVideoHeight = sizeContext.width * 9 / 16; + late RxDouble videoHeight = defaultVideoHeight.obs; final double pinnedHeaderHeight = - statusBarHeight + kToolbarHeight + videoHeight; + statusBarHeight + kToolbarHeight + videoHeight.value; + // ignore: no_leading_underscores_for_local_identifiers + + // 竖屏 + final bool isPortrait = _context.orientation == Orientation.portrait; + // 横屏 + final bool isLandscape = _context.orientation == Orientation.landscape; + final Rx isFullScreen = plPlayerController?.isFullScreen ?? false.obs; + // 全屏时高度撑满 + if (isLandscape || isFullScreen.value == true) { + videoHeight.value = Get.size.height; + enterFullScreen(); + } else { + videoHeight.value = defaultVideoHeight; + exitFullScreen(); + } + + /// 播放器面板 + Widget videoPlayerPanel = FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData && snapshot.data['status']) { + return Obx( + () { + return !vdCtr.autoPlay.value + ? const SizedBox() + : PLVideoPlayer( + controller: plPlayerController!, + headerControl: vdCtr.headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key(vdCtr.danmakuCid.value.toString()), + cid: vdCtr.danmakuCid.value, + playerController: plPlayerController!, + ), + ), + ); + }, + ); + } else { + // 加载失败异常处理 + return const SizedBox(); + } + }, + ); + + /// tabbar + Widget tabbarBuild = SizedBox( + width: double.infinity, + height: 45, + child: Row( + children: [ + Expanded( + child: TabBar( + controller: vdCtr.tabCtr, + dividerColor: Colors.transparent, + tabs: vdCtr.tabs.map((String name) => Tab(text: name)).toList(), + ), + ), + SizedBox( + width: 200, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => vdCtr.showShootDanmakuSheet(), + child: const Text( + '发弹幕', + ), + ), + ), + const SizedBox(width: 4), + SizedBox( + width: 34, + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + plPlayerController?.isOpenDanmu.value = + !(plPlayerController?.isOpenDanmu.value ?? false); + }, + child: Obx(() => Text( + '弹', + style: TextStyle( + color: (plPlayerController?.isOpenDanmu.value ?? + false) + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + )), + ), + ), + const SizedBox(width: 14), + ], + ), + ), + ), + ], + ), + ); + + /// 手动播放 + Widget handlePlayPanel() { + return Stack( + children: [ + GestureDetector( + onTap: () { + handlePlay(); + }, + child: NetworkImgLayer( + type: 'emote', + src: vdCtr.videoItem['pic'], + width: Get.width, + height: videoHeight.value, + ), + ), + Positioned( + top: 0, + left: 0, + right: 0, + child: buildCustomAppBar(), + ), + Positioned( + right: 12, + bottom: 10, + child: IconButton( + tooltip: '播放', + onPressed: () => handlePlay(), + icon: Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + )), + ), + ], + ); + } + Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, @@ -273,7 +418,7 @@ class _VideoDetailPageState extends State children: [ Scaffold( resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, + key: vdCtr.scaffoldKey, backgroundColor: Colors.black, appBar: PreferredSize( preferredSize: const Size.fromHeight(0), @@ -299,21 +444,19 @@ class _VideoDetailPageState extends State return SliverAppBar( automaticallyImplyLeading: false, // 假装使用一个非空变量,避免Obx检测不到而罢工 - pinned: videoDetailController.autoPlay.value ^ - false ^ - videoDetailController.autoPlay.value, + pinned: vdCtr.autoPlay.value, elevation: 0, scrolledUnderElevation: 0, forceElevated: innerBoxIsScrolled, expandedHeight: MediaQuery.of(context).orientation == Orientation.landscape || plPlayerController?.isFullScreen.value == true - ? MediaQuery.sizeOf(context).height - + ? (MediaQuery.sizeOf(context).height - (MediaQuery.of(context).orientation == Orientation.landscape ? 0 - : MediaQuery.of(context).padding.top) - : videoHeight, + : MediaQuery.of(context).padding.top)) + : videoHeight.value, backgroundColor: Colors.black, flexibleSpace: FlexibleSpaceBar( background: PopScope( @@ -333,108 +476,27 @@ class _VideoDetailPageState extends State child: LayoutBuilder( builder: (BuildContext context, BoxConstraints boxConstraints) { - final double maxWidth = - boxConstraints.maxWidth; - final double maxHeight = - boxConstraints.maxHeight; + // final double maxWidth = + // boxConstraints.maxWidth; + // final double maxHeight = + // boxConstraints.maxHeight; return Stack( children: [ - if (isShowing) - FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (snapshot.hasData && - snapshot.data['status']) { - return Obx( - () => - !videoDetailController - .autoPlay.value - ? nil - : PLVideoPlayer( - controller: - plPlayerController!, - headerControl: - videoDetailController - .headerControl, - danmuWidget: Obx( - () => PlDanmaku( - key: Key(videoDetailController - .danmakuCid - .value - .toString()), - cid: videoDetailController - .danmakuCid - .value, - playerController: - plPlayerController!, - ), - ), - ), - ); - } else { - return buildCustomAppBar(); - } - }, - ), + if (isShowing) videoPlayerPanel, /// 关闭自动播放时 手动播放 - if (!videoDetailController - .autoPlay.value) ...[ - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: GestureDetector( - onTap: () { - handlePlay(); - }, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: maxWidth, - height: maxHeight, - ), - ), - ), + Obx( + () => Visibility( + visible: !vdCtr.autoPlay.value && + vdCtr.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: handlePlayPanel(), ), ), - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value && - videoDetailController - .isEffective.value, - child: Stack( - children: [ - Positioned( - top: 0, - left: 0, - right: 0, - child: buildCustomAppBar(), - ), - Positioned( - right: 12, - bottom: 10, - child: IconButton( - tooltip: '播放', - onPressed: () => - handlePlay(), - icon: Image.asset( - 'assets/images/play.png', - width: 60, - height: 60, - )), - ), - ], - )), - ), - ] + ), ], ); }, @@ -445,18 +507,16 @@ class _VideoDetailPageState extends State ), ]; }, - // pinnedHeaderSliverHeightBuilder: () { - // return playerStatus != PlayerStatus.playing - // ? statusBarHeight + kToolbarHeight - // : pinnedHeaderHeight; - // }, + /// 不收回 pinnedHeaderSliverHeightBuilder: () { return MediaQuery.of(context).orientation == Orientation.landscape || plPlayerController?.isFullScreen.value == true ? MediaQuery.sizeOf(context).height - : pinnedHeaderHeight; + : playerStatus != PlayerStatus.playing + ? kToolbarHeight + : pinnedHeaderHeight; }, onlyOneScrollInBody: true, body: ColoredBox( @@ -464,54 +524,23 @@ class _VideoDetailPageState extends State color: Theme.of(context).colorScheme.background, child: Column( children: [ - Opacity( - opacity: 0, - child: SizedBox( - width: double.infinity, - height: 0, - child: Obx( - () => TabBar( - controller: videoDetailController.tabCtr, - dividerColor: Colors.transparent, - indicatorColor: - Theme.of(context).colorScheme.background, - tabs: videoDetailController.tabs - .map((String name) => Tab(text: name)) - .toList(), - ), - ), - ), - ), + tabbarBuild, Expanded( child: TabBarView( - controller: videoDetailController.tabCtr, + controller: vdCtr.tabCtr, children: [ Builder( builder: (BuildContext context) { return CustomScrollView( key: const PageStorageKey('简介'), slivers: [ - if (videoDetailController.videoType == - SearchType.video) ...[ - VideoIntroPanel( - bvid: videoDetailController.bvid), - ] else if (videoDetailController.videoType == + if (vdCtr.videoType == SearchType.video) ...[ + VideoIntroPanel(bvid: vdCtr.bvid), + ] else if (vdCtr.videoType == SearchType.media_bangumi) ...[ Obx(() => BangumiIntroPanel( - cid: videoDetailController.cid.value)), + cid: vdCtr.cid.value)), ], - // if (videoDetailController.videoType == - // SearchType.video) ...[ - // SliverPersistentHeader( - // floating: true, - // pinned: true, - // delegate: SliverHeaderDelegate( - // height: 50, - // child: - // const MenuRow(loadingStatus: false), - // ), - // ), - // ], SliverToBoxAdapter( child: Divider( indent: 12, @@ -528,8 +557,8 @@ class _VideoDetailPageState extends State ), Obx( () => VideoReplyPanel( - bvid: videoDetailController.bvid, - oid: videoDetailController.oid.value, + bvid: vdCtr.bvid, + oid: vdCtr.oid.value, ), ) ], @@ -543,56 +572,26 @@ class _VideoDetailPageState extends State /// 重新进入会刷新 // 播放完成/暂停播放 - // StreamBuilder( - // stream: appbarStream.stream, - // initialData: 0, - // builder: ((context, snapshot) { - // return ScrollAppBar( - // snapshot.data!.toDouble(), - // () => continuePlay(), - // playerStatus, - // null, - // ); - // }), - // ) + StreamBuilder( + stream: appbarStream.stream, + initialData: 0, + builder: ((context, snapshot) { + return ScrollAppBar( + snapshot.data!.toDouble(), + () => continuePlay(), + playerStatus, + null, + ); + }), + ) ], ), ); - Widget childWhenEnabled = FutureBuilder( - key: Key(heroTag), - future: _futureBuilderFuture, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData && snapshot.data['status']) { - return Obx( - () => !videoDetailController.autoPlay.value - ? const SizedBox() - : PLVideoPlayer( - controller: plPlayerController!, - headerControl: HeaderControl( - controller: plPlayerController, - videoDetailCtr: videoDetailController, - bvid: videoDetailController.bvid, - videoType: videoDetailController.videoType, - ), - danmuWidget: Obx( - () => PlDanmaku( - key: Key( - videoDetailController.danmakuCid.value.toString()), - cid: videoDetailController.danmakuCid.value, - playerController: plPlayerController!, - ), - ), - ), - ); - } else { - return nil; - } - }, - ); + if (Platform.isAndroid) { return PiPSwitcher( childWhenDisabled: childWhenDisabled, - childWhenEnabled: childWhenEnabled, + childWhenEnabled: videoPlayerPanel, floating: floating, ); } else { @@ -633,8 +632,7 @@ class _VideoDetailPageState extends State ComBtn( icon: const Icon(Icons.history_outlined, size: 22), fuc: () async { - var res = await UserHttp.toViewLater( - bvid: videoDetailController.bvid); + var res = await UserHttp.toViewLater(bvid: vdCtr.bvid); SmartDialog.showToast(res['msg']); }, ), diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e72e6a69..a8414c18 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'dart:math'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; @@ -17,7 +16,6 @@ import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; 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'; @@ -221,88 +219,6 @@ class _HeaderControlState extends State { ); } - /// 发送弹幕 - void showShootDanmakuSheet() { - final TextEditingController textController = TextEditingController(); - bool isSending = false; // 追踪是否正在发送 - showDialog( - context: Get.context!, - builder: (BuildContext context) { - // TODO: 支持更多类型和颜色的弹幕 - return AlertDialog( - title: const Text('发送弹幕(测试)'), - content: StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return TextField( - controller: textController, - ); - }), - actions: [ - TextButton( - onPressed: () => Get.back(), - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return TextButton( - onPressed: isSending - ? null - : () async { - final String msg = textController.text; - if (msg.isEmpty) { - SmartDialog.showToast('弹幕内容不能为空'); - return; - } else if (msg.length > 100) { - SmartDialog.showToast('弹幕内容不能超过100个字符'); - return; - } - setState(() { - isSending = true; // 开始发送,更新状态 - }); - //修改按钮文字 - // SmartDialog.showToast('弹幕发送中,\n$msg'); - final dynamic res = await DanmakaHttp.shootDanmaku( - oid: widget.videoDetailCtr!.cid.value, - msg: textController.text, - bvid: widget.videoDetailCtr!.bvid, - progress: - widget.controller!.position.value.inMilliseconds, - type: 1, - ); - setState(() { - isSending = false; // 发送结束,更新状态 - }); - if (res['status']) { - SmartDialog.showToast('发送成功'); - // 发送成功,自动预览该弹幕,避免重新请求 - // TODO: 暂停状态下预览弹幕仍会移动与计时,可考虑添加到dmSegList或其他方式实现 - widget.controller!.danmakuController!.addItems([ - DanmakuItem( - msg, - color: Colors.white, - time: widget - .controller!.position.value.inMilliseconds, - type: DanmakuItemType.scroll, - isSend: true, - ) - ]); - Get.back(); - } else { - SmartDialog.showToast('发送失败,错误信息为${res['msg']}'); - } - }, - child: Text(isSending ? '发送中...' : '发送'), - ); - }) - ], - ); - }, - ); - } - /// 定时关闭 void scheduleExit() async { const List scheduleTimeChoices = [ @@ -1166,41 +1082,6 @@ class _HeaderControlState extends State { // ), // fuc: () => _.screenshot(), // ), - SizedBox( - width: 56, - height: 34, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => showShootDanmakuSheet(), - child: const Text( - '发弹幕', - style: textStyle, - ), - ), - ), - SizedBox( - width: 34, - height: 34, - child: Obx( - () => IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - _.isOpenDanmu.value = !_.isOpenDanmu.value; - }, - icon: Icon( - _.isOpenDanmu.value - ? Icons.subtitles_outlined - : Icons.subtitles_off_outlined, - size: 19, - color: Colors.white, - ), - ), - ), - ), SizedBox(width: buttonSpace), if (Platform.isAndroid) ...[ SizedBox( From 98aaca286bd7e5b7eedf88cd95bf613606968d60 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 11 Mar 2024 00:02:11 +0800 Subject: [PATCH 014/349] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E7=94=BB?= =?UTF-8?q?=E8=B4=A8=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/live/quality.dart | 43 +++++++++++++++++++ lib/pages/live_room/controller.dart | 34 ++++++++++++++- .../live_room/widgets/bottom_control.dart | 25 +++++++++++ 3 files changed, 101 insertions(+), 1 deletion(-) create mode 100644 lib/models/live/quality.dart diff --git a/lib/models/live/quality.dart b/lib/models/live/quality.dart new file mode 100644 index 00000000..47938416 --- /dev/null +++ b/lib/models/live/quality.dart @@ -0,0 +1,43 @@ +enum LiveQuality { + dolby, + super4K, + origin, + veryHigh, + bluRay, + superHD, + smooth, + flunt, +} + +extension LiveQualityCode on LiveQuality { + static final List _codeList = [ + 30000, + 20000, + 10000, + 400, + 250, + 150, + 80, + ]; + int get code => _codeList[index]; + + static LiveQuality? fromCode(int code) { + final index = _codeList.indexOf(code); + if (index != -1) { + return LiveQuality.values[index]; + } + return null; + } +} + +extension VideoQualityDesc on LiveQuality { + static final List _descList = [ + '杜比', + '4K', + '原画', + '蓝光', + '超清', + '流畅', + ]; + get description => _descList[index]; +} diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 2bb1cd0a..a1878c74 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,6 +1,7 @@ import 'package:get/get.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/live.dart'; +import 'package:pilipala/models/live/quality.dart'; import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import '../../models/live/room_info_h5.dart'; @@ -19,10 +20,14 @@ class LiveRoomController extends GetxController { PlPlayerController.getInstance(videoType: 'live'); Rx roomInfoH5 = RoomInfoH5Model().obs; late bool enableCDN; + late int currentQn; + late List> acceptQnList; + RxString currentQnDesc = ''.obs; @override void onInit() { super.onInit(); + currentQn = 10000; roomId = int.parse(Get.parameters['roomid']!); if (Get.arguments != null) { liveItem = Get.arguments['liveItem']; @@ -57,11 +62,26 @@ class LiveRoomController extends GetxController { } Future queryLiveInfo() async { - var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: 10000); + /// TODO 默认获取预设质量的直播资源 + var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn); if (res['status']) { List codec = res['data'].playurlInfo.playurl.stream.first.format.first.codec; CodecItem item = codec.first; + // 以服务端返回的码率为准 + currentQn = item.currentQn!; + List acceptQn = item.acceptQn!; + acceptQnList = acceptQn.map((e) { + return { + 'code': e, + 'desc': LiveQuality.values + .firstWhere((element) => element.code == e) + .description, + }; + }).toList(); + currentQnDesc.value = LiveQuality.values + .firstWhere((element) => element.code == currentQn) + .description; String videoUrl = enableCDN ? VideoUtils.getCdnUrl(item) : (item.urlInfo?.first.host)! + @@ -90,4 +110,16 @@ class LiveRoomController extends GetxController { } return res; } + + // 修改画质 + void changeQn(int qn) async { + if (currentQn == qn) { + return; + } + currentQn = qn; + currentQnDesc.value = LiveQuality.values + .firstWhere((element) => element.code == currentQn) + .description; + await queryLiveInfo(); + } } diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index a00f3d92..0dcedc2c 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; +import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/video/play/url.dart'; import 'package:pilipala/pages/live_room/index.dart'; @@ -113,6 +114,30 @@ class _BottomControlState extends State { ), const SizedBox(width: 4), ], + SizedBox( + width: 30, + child: PopupMenuButton( + padding: EdgeInsets.zero, + onSelected: (value) { + widget.liveRoomCtr!.changeQn(value); + }, + child: Obx( + () => Text( + widget.liveRoomCtr!.currentQnDesc.value, + style: const TextStyle(color: Colors.white, fontSize: 13), + ), + ), + itemBuilder: (BuildContext context) { + return widget.liveRoomCtr!.acceptQnList.map((e) { + return PopupMenuItem( + value: e['code'], + child: Text(e['desc']), + ); + }).toList(); + }, + ), + ), + const SizedBox(width: 10), ComBtn( icon: const Icon( Icons.fullscreen, From ab10223eca6e0936ad6835dab86cdc6ffbef47b2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 11 Mar 2024 23:03:50 +0800 Subject: [PATCH 015/349] =?UTF-8?q?feat:=20=E6=8E=A5=E6=94=B6=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E7=BB=84=E4=BB=B6=E4=BC=A0=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pl_player/models/bottom_control_type.dart | 1 + lib/plugin/pl_player/view.dart | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/plugin/pl_player/models/bottom_control_type.dart b/lib/plugin/pl_player/models/bottom_control_type.dart index 599f6e4f..739e1d38 100644 --- a/lib/plugin/pl_player/models/bottom_control_type.dart +++ b/lib/plugin/pl_player/models/bottom_control_type.dart @@ -7,4 +7,5 @@ enum BottomControlType { fit, speed, fullscreen, + custom, } diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 0b2e652e..1f3c4156 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -34,6 +34,8 @@ class PLVideoPlayer extends StatefulWidget { this.bottomControl, this.danmuWidget, this.bottomList, + this.customWidget, + this.customWidgets, super.key, }); @@ -42,6 +44,10 @@ class PLVideoPlayer extends StatefulWidget { final PreferredSizeWidget? bottomControl; final Widget? danmuWidget; final List? bottomList; + // List or Widget + + final Widget? customWidget; + final List? customWidgets; @override State createState() => _PLVideoPlayerState(); @@ -310,7 +316,7 @@ class _PLVideoPlayerState extends State ), }; final List list = []; - var userSpecifyItem = widget.bottomList ?? + List userSpecifyItem = widget.bottomList ?? [ BottomControlType.playOrPause, BottomControlType.time, @@ -319,7 +325,16 @@ class _PLVideoPlayerState extends State BottomControlType.fullscreen, ]; for (var i = 0; i < userSpecifyItem.length; i++) { - list.add(videoProgressWidgets[userSpecifyItem[i]]!); + if (userSpecifyItem[i] == BottomControlType.custom) { + if (widget.customWidget != null && widget.customWidget is Widget) { + list.add(widget.customWidget!); + } + if (widget.customWidgets != null && widget.customWidgets!.isNotEmpty) { + list.addAll(widget.customWidgets!); + } + } else { + list.add(videoProgressWidgets[userSpecifyItem[i]]!); + } } return list; } From 99e6abdad93da78daed1bc54c50a8b6c5ee8ba58 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 11 Mar 2024 23:31:20 +0800 Subject: [PATCH 016/349] =?UTF-8?q?feat:=20=E7=9B=B8=E5=85=B3=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=8E=A8=E8=8D=90=E5=BC=80=E5=85=B3=20issues=20#585?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/extra_setting.dart | 6 ++++++ lib/pages/video/detail/controller.dart | 4 +++- lib/pages/video/detail/view.dart | 5 ++++- lib/utils/storage.dart | 3 ++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index 0a4dd2bf..aaaa8b84 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -173,6 +173,12 @@ class _ExtraSettingState extends State { setKey: SettingBoxKey.enableAi, defaultVal: true, ), + const SetSwitchItem( + title: '相关视频推荐', + subTitle: '视频详情页推荐相关视频', + setKey: SettingBoxKey.enableRelatedVideo, + defaultVal: true, + ), ListTile( dense: false, title: Text('评论展示', style: titleStyle), diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index b4005b5a..65f844ab 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -91,6 +91,7 @@ class VideoDetailController extends GetxController late int cacheAudioQa; PersistentBottomSheetController? replyReplyBottomSheetCtr; + late bool enableRelatedVideo; @override void onInit() { @@ -113,7 +114,8 @@ class VideoDetailController extends GetxController autoPlay.value = setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true); enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: true); - + enableRelatedVideo = + setting.get(SettingBoxKey.enableRelatedVideo, defaultValue: true); if (userInfo == null || localCache.get(LocalCacheKey.historyPause) == true) { enableHeart = false; diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 04d41b2e..04c50804 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -521,7 +521,10 @@ class _VideoDetailPageState extends State .withOpacity(0.06), ), ), - const RelatedVideoPanel(), + if (videoDetailController.videoType == + SearchType.video && + videoDetailController.enableRelatedVideo) + const RelatedVideoPanel(), ], ); }, diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 6283299b..835848cd 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -131,7 +131,8 @@ class SettingBoxKey { enableSearchWord = 'enableSearchWord', enableSystemProxy = 'enableSystemProxy', enableAi = 'enableAi', - defaultHomePage = 'defaultHomePage'; + defaultHomePage = 'defaultHomePage', + enableRelatedVideo = 'enableRelatedVideo'; /// 外观 static const String themeMode = 'themeMode', From d3766ae31b5465257c92aa74b0b4a5c4d30fafc8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 11 Mar 2024 23:35:07 +0800 Subject: [PATCH 017/349] typo: dynamic type --- lib/models/common/dynamics_type.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/models/common/dynamics_type.dart b/lib/models/common/dynamics_type.dart index 337f6aec..f4e20a4b 100644 --- a/lib/models/common/dynamics_type.dart +++ b/lib/models/common/dynamics_type.dart @@ -7,5 +7,5 @@ enum DynamicsType { extension BusinessTypeExtension on DynamicsType { String get values => ['all', 'video', 'pgc', 'article'][index]; - String get labels => ['全部', '视频', '追番', '专栏'][index]; + String get labels => ['全部', '投稿', '番剧', '专栏'][index]; } From 882957e2f842a0d54b1b134e21b9cb08869a80ff Mon Sep 17 00:00:00 2001 From: guohaonan <972101775@qq.com> Date: Tue, 12 Mar 2024 14:34:32 +0800 Subject: [PATCH 018/349] =?UTF-8?q?feature:=E5=8A=A8=E6=80=81=E9=A1=B5?= =?UTF-8?q?=E7=9A=84=E9=94=99=E8=AF=AF=E6=8C=89=E9=92=AE=E5=9C=A8=E6=9C=AA?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E7=8A=B6=E6=80=81=E4=B8=8B=E7=9A=84=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E4=B8=BA=E7=9B=B4=E6=8E=A5=E8=B7=B3=E8=BD=AC=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/view.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 4a92cdfb..5fa06c91 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -14,6 +14,7 @@ import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; +import '../mine/controller.dart'; import 'controller.dart'; import 'widgets/dynamic_panel.dart'; import 'widgets/up_panel.dart'; @@ -28,6 +29,7 @@ class DynamicsPage extends StatefulWidget { class _DynamicsPageState extends State with AutomaticKeepAliveClientMixin { final DynamicsController _dynamicsController = Get.put(DynamicsController()); + final MineController mineController = Get.put(MineController()); late Future _futureBuilderFuture; late Future _futureBuilderFutureUp; Box userInfoCache = GStrorage.userInfo; @@ -256,6 +258,16 @@ class _DynamicsPageState extends State } }, ); + } else if (data['msg'] == "账号未登录") { + return HttpError( + errMsg: data['msg'], + btnText: "去登录", + fn: () { + setState(() { + mineController.onLogin(); + }); + }, + ); } else { return HttpError( errMsg: data['msg'], From bc9ea43cd2c8bc5203968911da744dca10e7ad8d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 12 Mar 2024 23:45:22 +0800 Subject: [PATCH 019/349] =?UTF-8?q?feat:=20=E8=A7=86=E9=A2=91=E7=95=AA?= =?UTF-8?q?=E5=89=A7=E8=AF=A6=E6=83=85=E9=A1=B5=E4=BB=A3=E7=A0=81=E6=95=B4?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bangumi/introduction/controller.dart | 57 +- lib/pages/bangumi/introduction/view.dart | 494 ++++++---------- .../video/detail/introduction/controller.dart | 27 - lib/pages/video/detail/introduction/view.dart | 531 +++++++----------- .../introduction/widgets/action_item.dart | 28 +- 5 files changed, 406 insertions(+), 731 deletions(-) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 6b3123ea..12f0c053 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -25,13 +25,6 @@ class BangumiIntroController extends GetxController { ? int.tryParse(Get.parameters['epId']!) : null; - // 是否预渲染 骨架屏 - bool preRender = false; - - // 视频详情 上个页面传入 - Map? videoItem = {}; - BangumiInfoModel? bangumiItem; - // 请求状态 RxBool isLoading = false.obs; @@ -63,27 +56,6 @@ class BangumiIntroController extends GetxController { @override void onInit() { super.onInit(); - if (Get.arguments.isNotEmpty as bool) { - if (Get.arguments.containsKey('bangumiItem') as bool) { - preRender = true; - bangumiItem = Get.arguments['bangumiItem']; - // bangumiItem!['pic'] = args.pic; - // if (args.title is String) { - // videoItem!['title'] = args.title; - // } else { - // String str = ''; - // for (Map map in args.title) { - // str += map['text']; - // } - // videoItem!['title'] = str; - // } - // if (args.stat != null) { - // videoItem!['stat'] = args.stat; - // } - // videoItem!['pubdate'] = args.pubdate; - // videoItem!['owner'] = args.owner; - } - } userInfo = userInfoCache.get('userInfoCache'); userLogin = userInfo != null; } @@ -183,20 +155,21 @@ class BangumiIntroController extends GetxController { actions: [ TextButton(onPressed: () => Get.back(), child: const Text('取消')), TextButton( - onPressed: () async { - var res = await VideoHttp.coinVideo( - bvid: bvid, multiply: _tempThemeValue); - if (res['status']) { - SmartDialog.showToast('投币成功 👏'); - hasCoin.value = true; - bangumiDetail.value.stat!['coins'] = - bangumiDetail.value.stat!['coins'] + _tempThemeValue; - } else { - SmartDialog.showToast(res['msg']); - } - Get.back(); - }, - child: const Text('确定')) + onPressed: () async { + var res = await VideoHttp.coinVideo( + bvid: bvid, multiply: _tempThemeValue); + if (res['status']) { + SmartDialog.showToast('投币成功 👏'); + hasCoin.value = true; + bangumiDetail.value.stat!['coins'] = + bangumiDetail.value.stat!['coins'] + _tempThemeValue; + } else { + SmartDialog.showToast(res['msg']); + } + Get.back(); + }, + child: const Text('确定'), + ) ], ); }); diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index f9efc66c..551d91d7 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -12,11 +12,10 @@ import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/pages/bangumi/widgets/bangumi_panel.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/introduction/widgets/action_item.dart'; -import 'package:pilipala/pages/video/detail/introduction/widgets/action_row_item.dart'; import 'package:pilipala/pages/video/detail/introduction/widgets/fav_panel.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; - +import '../../../common/widgets/http_error.dart'; import 'controller.dart'; import 'widgets/intro_detail.dart'; @@ -51,9 +50,6 @@ class _BangumiIntroPanelState extends State cid = widget.cid!; bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); videoDetailCtr = Get.find(tag: heroTag); - bangumiIntroController.bangumiDetail.listen((BangumiInfoModel value) { - bangumiDetail = value; - }); _futureBuilderFuture = bangumiIntroController.queryBangumiIntro(); videoDetailCtr.cid.listen((int p0) { cid = p0; @@ -68,27 +64,32 @@ class _BangumiIntroPanelState extends State future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SliverToBoxAdapter(child: SizedBox()); + } if (snapshot.data['status']) { // 请求成功 - - return BangumiInfo( - loadingStatus: false, - bangumiDetail: bangumiDetail, - cid: cid, + return Obx( + () => BangumiInfo( + bangumiDetail: bangumiIntroController.bangumiDetail.value, + cid: cid, + ), ); } else { // 请求错误 - // return HttpError( - // errMsg: snapshot.data['msg'], - // fn: () => Get.back(), - // ); - return const SizedBox(); + return HttpError( + errMsg: snapshot.data['msg'], + fn: () => Get.back(), + ); } } else { - return BangumiInfo( - loadingStatus: true, - bangumiDetail: bangumiDetail, - cid: cid, + return const SliverToBoxAdapter( + child: SizedBox( + height: 100, + child: Center( + child: CircularProgressIndicator(), + ), + ), ); } }, @@ -99,12 +100,10 @@ class _BangumiIntroPanelState extends State class BangumiInfo extends StatefulWidget { const BangumiInfo({ super.key, - this.loadingStatus = false, this.bangumiDetail, this.cid, }); - final bool loadingStatus; final BangumiInfoModel? bangumiDetail; final int? cid; @@ -117,7 +116,6 @@ class _BangumiInfoState extends State { late final BangumiIntroController bangumiIntroController; late final VideoDetailController videoDetailCtr; Box localCache = GStrorage.localCache; - late final BangumiInfoModel? bangumiItem; late double sheetHeight; int? cid; bool isProcessing = false; @@ -136,13 +134,10 @@ class _BangumiInfoState extends State { super.initState(); bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); videoDetailCtr = Get.find(tag: heroTag); - bangumiItem = bangumiIntroController.bangumiItem; sheetHeight = localCache.get('sheetHeight'); cid = widget.cid!; - print('cid: $cid'); videoDetailCtr.cid.listen((p0) { cid = p0; - print('cid: $cid'); setState(() {}); }); } @@ -182,207 +177,154 @@ class _BangumiInfoState extends State { padding: const EdgeInsets.only( left: StyleString.safeSpace, right: StyleString.safeSpace, top: 20), sliver: SliverToBoxAdapter( - child: !widget.loadingStatus || bangumiItem != null - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( children: [ - Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - NetworkImgLayer( - width: 105, - height: 160, - src: !widget.loadingStatus - ? widget.bangumiDetail!.cover! - : bangumiItem!.cover!, - ), - if (bangumiItem != null && - bangumiItem!.rating != null) - PBadge( - text: - '评分 ${!widget.loadingStatus ? widget.bangumiDetail!.rating!['score']! : bangumiItem!.rating!['score']!}', - top: null, - right: 6, - bottom: 6, - left: null, + NetworkImgLayer( + width: 105, + height: 160, + src: widget.bangumiDetail!.cover!, + ), + PBadge( + text: '评分 ${widget.bangumiDetail!.rating!['score']!}', + top: null, + right: 6, + bottom: 6, + left: null, + ), + ], + ), + const SizedBox(width: 10), + Expanded( + child: InkWell( + onTap: () => showIntroDetail(), + child: SizedBox( + height: 158, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + children: [ + Expanded( + child: Text( + widget.bangumiDetail!.title!, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), ), - ], - ), - const SizedBox(width: 10), - Expanded( - child: InkWell( - onTap: () => showIntroDetail(), - child: SizedBox( - height: 158, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Expanded( - child: Text( - !widget.loadingStatus - ? widget.bangumiDetail!.title! - : bangumiItem!.title!, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - const SizedBox(width: 20), - SizedBox( - width: 34, - height: 34, - child: IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.zero), - backgroundColor: - MaterialStateProperty.resolveWith( - (Set states) { - return t - .colorScheme.primaryContainer - .withOpacity(0.7); - }), - ), - onPressed: () => - bangumiIntroController.bangumiAdd(), - icon: Icon( - Icons.favorite_border_rounded, - color: t.colorScheme.primary, - size: 22, - ), - ), - ), - ], + const SizedBox(width: 20), + SizedBox( + width: 34, + height: 34, + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all( + EdgeInsets.zero), + backgroundColor: + MaterialStateProperty.resolveWith( + (Set states) { + return t.colorScheme.primaryContainer + .withOpacity(0.7); + }), ), - Row( - children: [ - StatView( - theme: 'gray', - view: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['views'] - : bangumiItem!.stat!['views'], - size: 'medium', - ), - const SizedBox(width: 6), - StatDanMu( - theme: 'gray', - danmu: !widget.loadingStatus - ? widget - .bangumiDetail!.stat!['danmakus'] - : bangumiItem!.stat!['danmakus'], - size: 'medium', - ), - ], + onPressed: () => + bangumiIntroController.bangumiAdd(), + icon: Icon( + Icons.favorite_border_rounded, + color: t.colorScheme.primary, + size: 22, ), - const SizedBox(height: 6), - Row( - children: [ - Text( - !widget.loadingStatus - ? (widget.bangumiDetail!.areas! - .isNotEmpty - ? widget.bangumiDetail!.areas! - .first['name'] - : '') - : (bangumiItem!.areas!.isNotEmpty - ? bangumiItem! - .areas!.first['name'] - : ''), - style: TextStyle( - fontSize: 12, - color: t.colorScheme.outline, - ), - ), - const SizedBox(width: 6), - Text( - !widget.loadingStatus - ? widget.bangumiDetail! - .publish!['pub_time_show'] - : bangumiItem! - .publish!['pub_time_show'], - style: TextStyle( - fontSize: 12, - color: t.colorScheme.outline, - ), - ), - ], - ), - // const SizedBox(height: 4), - Text( - !widget.loadingStatus - ? widget.bangumiDetail!.newEp!['desc'] - : bangumiItem!.newEp!['desc'], - style: TextStyle( - fontSize: 12, - color: t.colorScheme.outline, - ), - ), - // const SizedBox(height: 10), - const Spacer(), - Text( - '简介:${!widget.loadingStatus ? widget.bangumiDetail!.evaluate! : bangumiItem!.evaluate!}', - maxLines: 3, - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 13, - color: t.colorScheme.outline, - ), - ), - ], + ), ), + ], + ), + Row( + children: [ + StatView( + theme: 'gray', + view: widget.bangumiDetail!.stat!['views'], + size: 'medium', + ), + const SizedBox(width: 6), + StatDanMu( + theme: 'gray', + danmu: widget.bangumiDetail!.stat!['danmakus'], + size: 'medium', + ), + ], + ), + const SizedBox(height: 6), + Row( + children: [ + Text( + (widget.bangumiDetail!.areas!.isNotEmpty + ? widget.bangumiDetail!.areas!.first['name'] + : ''), + style: TextStyle( + fontSize: 12, + color: t.colorScheme.outline, + ), + ), + const SizedBox(width: 6), + Text( + widget.bangumiDetail!.publish!['pub_time_show'], + style: TextStyle( + fontSize: 12, + color: t.colorScheme.outline, + ), + ), + ], + ), + Text( + widget.bangumiDetail!.newEp!['desc'], + style: TextStyle( + fontSize: 12, + color: t.colorScheme.outline, ), ), - ), - ], + const Spacer(), + Text( + '简介:${widget.bangumiDetail!.evaluate!}', + maxLines: 3, + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 13, + color: t.colorScheme.outline, + ), + ), + ], + ), ), - const SizedBox(height: 6), - // 点赞收藏转发 布局样式1 - // SingleChildScrollView( - // padding: const EdgeInsets.only(top: 7, bottom: 7), - // scrollDirection: Axis.horizontal, - // child: actionRow( - // context, - // bangumiIntroController, - // videoDetailCtr, - // ), - // ), - // 点赞收藏转发 布局样式2 - actionGrid(context, bangumiIntroController), - // 番剧分p - if ((!widget.loadingStatus && - widget.bangumiDetail!.episodes!.isNotEmpty) || - bangumiItem != null && - bangumiItem!.episodes!.isNotEmpty) ...[ - BangumiPanel( - pages: bangumiItem != null - ? bangumiItem!.episodes! - : widget.bangumiDetail!.episodes!, - cid: cid ?? - (bangumiItem != null - ? bangumiItem!.episodes!.first.cid - : widget.bangumiDetail!.episodes!.first.cid), - sheetHeight: sheetHeight, - changeFuc: (bvid, cid, aid) => bangumiIntroController - .changeSeasonOrbangu(bvid, cid, aid), - ) - ], - ], - ) - : const SizedBox( - height: 100, - child: Center( - child: CircularProgressIndicator(), ), ), - ), + ], + ), + const SizedBox(height: 6), + + /// 点赞收藏转发 + actionGrid(context, bangumiIntroController), + // 番剧分p + if (widget.bangumiDetail!.episodes!.isNotEmpty) ...[ + BangumiPanel( + pages: widget.bangumiDetail!.episodes!, + cid: cid ?? widget.bangumiDetail!.episodes!.first.cid, + sheetHeight: sheetHeight, + changeFuc: (bvid, cid, aid) => + bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid), + ) + ], + ], + )), ); } @@ -402,57 +344,44 @@ class _BangumiInfoState extends State { children: [ Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.thumbsUp), - selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), - onTap: - handleState(bangumiIntroController.actionLikeVideo), - selectStatus: bangumiIntroController.hasLike.value, - loadingStatus: false, - text: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['likes']!.toString() - : bangumiItem!.stat!['likes']!.toString()), + icon: const Icon(FontAwesomeIcons.thumbsUp), + selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), + onTap: handleState(bangumiIntroController.actionLikeVideo), + selectStatus: bangumiIntroController.hasLike.value, + text: widget.bangumiDetail!.stat!['likes']!.toString(), + ), ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.b), - selectIcon: const Icon(FontAwesomeIcons.b), - onTap: - handleState(bangumiIntroController.actionCoinVideo), - selectStatus: bangumiIntroController.hasCoin.value, - loadingStatus: false, - text: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['coins']!.toString() - : bangumiItem!.stat!['coins']!.toString()), + icon: const Icon(FontAwesomeIcons.b), + selectIcon: const Icon(FontAwesomeIcons.b), + onTap: handleState(bangumiIntroController.actionCoinVideo), + selectStatus: bangumiIntroController.hasCoin.value, + text: widget.bangumiDetail!.stat!['coins']!.toString(), + ), ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.star), - selectIcon: const Icon(FontAwesomeIcons.solidStar), - onTap: () => showFavBottomSheet(), - selectStatus: bangumiIntroController.hasFav.value, - loadingStatus: false, - text: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['favorite']!.toString() - : bangumiItem!.stat!['favorite']!.toString()), + icon: const Icon(FontAwesomeIcons.star), + selectIcon: const Icon(FontAwesomeIcons.solidStar), + onTap: () => showFavBottomSheet(), + selectStatus: bangumiIntroController.hasFav.value, + text: widget.bangumiDetail!.stat!['favorite']!.toString(), + ), ), ActionItem( icon: const Icon(FontAwesomeIcons.comment), selectIcon: const Icon(FontAwesomeIcons.reply), onTap: () => videoDetailCtr.tabCtr.animateTo(1), selectStatus: false, - loadingStatus: false, - text: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['reply']!.toString() - : bangumiItem!.stat!['reply']!.toString(), + text: widget.bangumiDetail!.stat!['reply']!.toString(), ), ActionItem( - icon: const Icon(FontAwesomeIcons.shareFromSquare), - onTap: () => bangumiIntroController.actionShareVideo(), - selectStatus: false, - loadingStatus: false, - text: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['share']!.toString() - : bangumiItem!.stat!['share']!.toString()), + icon: const Icon(FontAwesomeIcons.shareFromSquare), + onTap: () => bangumiIntroController.actionShareVideo(), + selectStatus: false, + text: widget.bangumiDetail!.stat!['share']!.toString(), + ), ], ), ), @@ -460,63 +389,4 @@ class _BangumiInfoState extends State { ); }); } - - Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) { - return Row(children: [ - Obx( - () => ActionRowItem( - icon: const Icon(FontAwesomeIcons.thumbsUp), - onTap: handleState(videoIntroController.actionLikeVideo), - selectStatus: videoIntroController.hasLike.value, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['likes']!.toString() - : '-', - ), - ), - const SizedBox(width: 8), - Obx( - () => ActionRowItem( - icon: const Icon(FontAwesomeIcons.b), - onTap: handleState(videoIntroController.actionCoinVideo), - selectStatus: videoIntroController.hasCoin.value, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['coins']!.toString() - : '-', - ), - ), - const SizedBox(width: 8), - Obx( - () => ActionRowItem( - icon: const Icon(FontAwesomeIcons.heart), - onTap: () => showFavBottomSheet(), - selectStatus: videoIntroController.hasFav.value, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['favorite']!.toString() - : '-', - ), - ), - const SizedBox(width: 8), - ActionRowItem( - icon: const Icon(FontAwesomeIcons.comment), - onTap: () { - videoDetailCtr.tabCtr.animateTo(1); - }, - selectStatus: false, - loadingStatus: widget.loadingStatus, - text: !widget.loadingStatus - ? widget.bangumiDetail!.stat!['reply']!.toString() - : '-', - ), - const SizedBox(width: 8), - ActionRowItem( - icon: const Icon(FontAwesomeIcons.share), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - loadingStatus: widget.loadingStatus, - text: '转发'), - ]); - } } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index c7f22b13..6714b887 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -25,13 +25,6 @@ class VideoIntroController extends GetxController { VideoIntroController({required this.bvid}); // 视频bvid String bvid; - - // 是否预渲染 骨架屏 - bool preRender = false; - - // 视频详情 上个页面传入 - Map? videoItem = {}; - // 请求状态 RxBool isLoading = false.obs; @@ -74,26 +67,6 @@ class VideoIntroController extends GetxController { try { heroTag = Get.arguments['heroTag']; } catch (_) {} - if (Get.arguments.isNotEmpty) { - if (Get.arguments.containsKey('videoItem')) { - preRender = true; - var args = Get.arguments['videoItem']; - var keys = Get.arguments.keys.toList(); - videoItem!['pic'] = args.pic; - if (args.title is String) { - videoItem!['title'] = args.title; - } else { - String str = ''; - for (Map map in args.title) { - str += map['text']; - } - videoItem!['title'] = str; - } - videoItem!['stat'] = keys.contains('stat') && args.stat; - videoItem!['pubdate'] = keys.contains('pubdate') && args.pubdate; - videoItem!['owner'] = keys.contains('owner') && args.owner; - } - } userLogin = userInfo != null; lastPlayCid.value = int.parse(Get.parameters['cid']!); isShowOnlineTotal = diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index bed37bb5..5e24220b 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -15,9 +15,7 @@ import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; - import 'widgets/action_item.dart'; -import 'widgets/action_row_item.dart'; import 'widgets/fav_panel.dart'; import 'widgets/intro_detail.dart'; import 'widgets/page.dart'; @@ -78,7 +76,6 @@ class _VideoIntroPanelState extends State // 请求成功 return Obx( () => VideoInfo( - loadingStatus: false, videoDetail: videoIntroController.videoDetail.value, heroTag: heroTag, bvid: widget.bvid, @@ -96,11 +93,13 @@ class _VideoIntroPanelState extends State ); } } else { - return VideoInfo( - loadingStatus: true, - videoDetail: videoDetail, - heroTag: heroTag, - bvid: widget.bvid, + return const SliverToBoxAdapter( + child: SizedBox( + height: 100, + child: Center( + child: CircularProgressIndicator(), + ), + ), ); } }, @@ -109,14 +108,12 @@ class _VideoIntroPanelState extends State } 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, required this.bvid, @@ -127,18 +124,12 @@ class VideoInfo extends StatefulWidget { } class _VideoInfoState extends State with TickerProviderStateMixin { - // final String heroTag = Get.arguments['heroTag']; late String heroTag; late final VideoIntroController videoIntroController; late final VideoDetailController videoDetailCtr; - late final Map videoItem; - final Box localCache = GStrorage.localCache; final Box setting = GStrorage.setting; late double sheetHeight; - - late final bool loadingStatus; // 加载状态 - late final dynamic owner; late final dynamic follower; late final dynamic followStatus; @@ -163,14 +154,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { videoIntroController = Get.put(VideoIntroController(bvid: widget.bvid), tag: heroTag); videoDetailCtr = Get.find(tag: heroTag); - videoItem = videoIntroController.videoItem!; sheetHeight = localCache.get('sheetHeight'); - loadingStatus = widget.loadingStatus; - owner = loadingStatus ? videoItem['owner'] : widget.videoDetail!.owner; - follower = loadingStatus - ? '-' - : Utils.numFormat(videoIntroController.userStat['follower']); + owner = widget.videoDetail!.owner; + follower = Utils.numFormat(videoIntroController.userStat['follower']); followStatus = videoIntroController.followStatus; enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); } @@ -224,9 +211,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { // 视频介绍 showIntroDetail() { - if (loadingStatus) { - return; - } feedBack(); showBottomSheet( context: context, @@ -240,13 +224,9 @@ class _VideoInfoState extends State with TickerProviderStateMixin { // 用户主页 onPushMember() { feedBack(); - mid = !loadingStatus - ? widget.videoDetail!.owner!.mid - : videoItem['owner'].mid; + mid = widget.videoDetail!.owner!.mid!; memberHeroTag = Utils.makeHeroTag(mid); - String face = !loadingStatus - ? widget.videoDetail!.owner!.face - : videoItem['owner'].face; + String face = widget.videoDetail!.owner!.face!; Get.toNamed('/member?mid=$mid', arguments: {'face': face, 'heroTag': memberHeroTag}); } @@ -270,221 +250,181 @@ class _VideoInfoState extends State with TickerProviderStateMixin { padding: const EdgeInsets.only( left: StyleString.safeSpace, right: StyleString.safeSpace, top: 10), sliver: SliverToBoxAdapter( - child: !loadingStatus - ? Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => showIntroDetail(), - child: Text( - !loadingStatus - ? widget.videoDetail!.title - : videoItem['title'], - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, - ), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - Stack( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => showIntroDetail(), + child: Text( + widget.videoDetail!.title!, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + Stack( + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () => showIntroDetail(), + child: Padding( + padding: const EdgeInsets.only(top: 7, bottom: 6), + child: Row( children: [ - GestureDetector( - behavior: HitTestBehavior.translucent, - onTap: () => showIntroDetail(), - child: Padding( - padding: const EdgeInsets.only(top: 7, bottom: 6), - child: Row( - children: [ - StatView( - theme: 'gray', - view: !loadingStatus - ? widget.videoDetail!.stat!.view - : videoItem['stat'].view, - size: 'medium', - ), - const SizedBox(width: 10), - StatDanMu( - theme: 'gray', - danmu: !loadingStatus - ? widget.videoDetail!.stat!.danmaku - : videoItem['stat'].danmaku, - size: 'medium', - ), - const SizedBox(width: 10), - Text( - Utils.dateFormat( - !loadingStatus - ? widget.videoDetail!.pubdate - : videoItem['pubdate'], - formatType: 'detail'), - style: TextStyle( - fontSize: 12, - color: t.colorScheme.outline, - ), - ), - const SizedBox(width: 10), - if (videoIntroController.isShowOnlineTotal) - Obx( - () => Text( - '${videoIntroController.total.value}人在看', - style: TextStyle( - fontSize: 12, - color: t.colorScheme.outline, - ), - ), - ), - ], - ), + StatView( + theme: 'gray', + view: widget.videoDetail!.stat!.view, + size: 'medium', + ), + const SizedBox(width: 10), + StatDanMu( + theme: 'gray', + danmu: widget.videoDetail!.stat!.danmaku, + size: 'medium', + ), + const SizedBox(width: 10), + Text( + Utils.dateFormat(widget.videoDetail!.pubdate, + formatType: 'detail'), + style: TextStyle( + fontSize: 12, + color: t.colorScheme.outline, ), ), - if (enableAi) - Positioned( - right: 10, - top: 6, - child: GestureDetector( - onTap: () async { - final res = - await videoIntroController.aiConclusion(); - if (res['status']) { - showAiBottomSheet(); - } - }, - child: - Image.asset('assets/images/ai.png', height: 22), - ), - ) - ], - ), - // 点赞收藏转发 布局样式1 - // SingleChildScrollView( - // padding: const EdgeInsets.only(top: 7, bottom: 7), - // scrollDirection: Axis.horizontal, - // child: actionRow( - // context, - // videoIntroController, - // videoDetailCtr, - // ), - // ), - // 点赞收藏转发 布局样式2 - actionGrid(context, videoIntroController), - // 合集 - if (!loadingStatus && - widget.videoDetail!.ugcSeason != null) ...[ - Obx( - () => SeasonPanel( - ugcSeason: widget.videoDetail!.ugcSeason!, - cid: videoIntroController.lastPlayCid.value != 0 - ? videoIntroController.lastPlayCid.value - : widget.videoDetail!.pages!.first.cid, - sheetHeight: sheetHeight, - changeFuc: (bvid, cid, aid) => videoIntroController - .changeSeasonOrbangu(bvid, cid, aid), - ), - ) - ], - if (!loadingStatus && - widget.videoDetail!.pages != null && - widget.videoDetail!.pages!.length > 1) ...[ - Obx(() => PagesPanel( - pages: widget.videoDetail!.pages!, - cid: videoIntroController.lastPlayCid.value, - sheetHeight: sheetHeight, - changeFuc: (cid) => - videoIntroController.changeSeasonOrbangu( - videoIntroController.bvid, cid, null), - )) - ], - GestureDetector( - onTap: onPushMember, - child: Container( - padding: const EdgeInsets.symmetric( - vertical: 12, horizontal: 4), - child: Row( - children: [ - NetworkImgLayer( - type: 'avatar', - src: loadingStatus - ? owner.face - : widget.videoDetail!.owner!.face, - width: 34, - height: 34, - fadeInDuration: Duration.zero, - fadeOutDuration: Duration.zero, - ), - const SizedBox(width: 10), - Text(owner.name, - style: const TextStyle(fontSize: 13)), - const SizedBox(width: 6), - Text( - follower, + const SizedBox(width: 10), + if (videoIntroController.isShowOnlineTotal) + Obx( + () => Text( + '${videoIntroController.total.value}人在看', style: TextStyle( - fontSize: t.textTheme.labelSmall!.fontSize, - color: outline, + fontSize: 12, + color: t.colorScheme.outline, ), ), - const Spacer(), - Obx(() => AnimatedOpacity( - opacity: loadingStatus || - videoIntroController - .followStatus.isEmpty - ? 0 - : 1, - duration: const Duration(milliseconds: 50), - child: SizedBox( - height: 32, - child: Obx( - () => videoIntroController - .followStatus.isNotEmpty - ? TextButton( - onPressed: videoIntroController - .actionRelationMod, - style: TextButton.styleFrom( - padding: const EdgeInsets.only( - left: 8, right: 8), - foregroundColor: - followStatus['attribute'] != 0 - ? outline - : t.colorScheme.onPrimary, - backgroundColor: - followStatus['attribute'] != 0 - ? t.colorScheme - .onInverseSurface - : t.colorScheme - .primary, // 设置按钮背景色 - ), - child: Text( - followStatus['attribute'] != 0 - ? '已关注' - : '关注', - style: TextStyle( - fontSize: t.textTheme - .labelMedium!.fontSize), - ), - ) - : ElevatedButton( - onPressed: videoIntroController - .actionRelationMod, - child: const Text('关注'), - ), - ), - ), - )), - ], - ), - ), + ), + ], ), - ], - ) - : const SizedBox( - height: 100, - child: Center( - child: CircularProgressIndicator(), ), ), - ), + if (enableAi) + Positioned( + right: 10, + top: 6, + child: GestureDetector( + onTap: () async { + final res = await videoIntroController.aiConclusion(); + if (res['status']) { + showAiBottomSheet(); + } + }, + child: Image.asset('assets/images/ai.png', height: 22), + ), + ) + ], + ), + + /// 点赞收藏转发 + actionGrid(context, videoIntroController), + // 合集 + if (widget.videoDetail!.ugcSeason != null) ...[ + Obx( + () => SeasonPanel( + ugcSeason: widget.videoDetail!.ugcSeason!, + cid: videoIntroController.lastPlayCid.value != 0 + ? videoIntroController.lastPlayCid.value + : widget.videoDetail!.pages!.first.cid, + sheetHeight: sheetHeight, + changeFuc: (bvid, cid, aid) => + videoIntroController.changeSeasonOrbangu(bvid, cid, aid), + ), + ) + ], + if (widget.videoDetail!.pages != null && + widget.videoDetail!.pages!.length > 1) ...[ + Obx(() => PagesPanel( + pages: widget.videoDetail!.pages!, + cid: videoIntroController.lastPlayCid.value, + sheetHeight: sheetHeight, + changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( + videoIntroController.bvid, cid, null), + )) + ], + GestureDetector( + onTap: onPushMember, + child: Container( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4), + child: Row( + children: [ + NetworkImgLayer( + type: 'avatar', + src: widget.videoDetail!.owner!.face, + width: 34, + height: 34, + fadeInDuration: Duration.zero, + fadeOutDuration: Duration.zero, + ), + const SizedBox(width: 10), + Text(owner.name, style: const TextStyle(fontSize: 13)), + const SizedBox(width: 6), + Text( + follower, + style: TextStyle( + fontSize: t.textTheme.labelSmall!.fontSize, + color: outline, + ), + ), + const Spacer(), + Obx(() => AnimatedOpacity( + opacity: + videoIntroController.followStatus.isEmpty ? 0 : 1, + duration: const Duration(milliseconds: 50), + child: SizedBox( + height: 32, + child: Obx( + () => videoIntroController.followStatus.isNotEmpty + ? TextButton( + onPressed: + videoIntroController.actionRelationMod, + style: TextButton.styleFrom( + padding: const EdgeInsets.only( + left: 8, right: 8), + foregroundColor: + followStatus['attribute'] != 0 + ? outline + : t.colorScheme.onPrimary, + backgroundColor: + followStatus['attribute'] != 0 + ? t.colorScheme.onInverseSurface + : t.colorScheme + .primary, // 设置按钮背景色 + ), + child: Text( + followStatus['attribute'] != 0 + ? '已关注' + : '关注', + style: TextStyle( + fontSize: t + .textTheme.labelMedium!.fontSize), + ), + ) + : ElevatedButton( + onPressed: + videoIntroController.actionRelationMod, + child: const Text('关注'), + ), + ), + ), + )), + ], + ), + ), + ), + ], + )), ); } @@ -506,10 +446,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), onTap: handleState(videoIntroController.actionLikeVideo), selectStatus: videoIntroController.hasLike.value, - loadingStatus: loadingStatus, - text: !loadingStatus - ? widget.videoDetail!.stat!.like!.toString() - : '-'), + text: widget.videoDetail!.stat!.like!.toString()), ), // ActionItem( // icon: const Icon(FontAwesomeIcons.clock), @@ -519,104 +456,38 @@ class _VideoInfoState extends State with TickerProviderStateMixin { // text: '稍后再看'), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.b), - selectIcon: const Icon(FontAwesomeIcons.b), - onTap: handleState(videoIntroController.actionCoinVideo), - selectStatus: videoIntroController.hasCoin.value, - loadingStatus: loadingStatus, - text: !loadingStatus - ? widget.videoDetail!.stat!.coin!.toString() - : '-'), + icon: const Icon(FontAwesomeIcons.b), + selectIcon: const Icon(FontAwesomeIcons.b), + onTap: handleState(videoIntroController.actionCoinVideo), + selectStatus: videoIntroController.hasCoin.value, + text: widget.videoDetail!.stat!.coin!.toString(), + ), ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.star), - selectIcon: const Icon(FontAwesomeIcons.solidStar), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), - selectStatus: videoIntroController.hasFav.value, - loadingStatus: loadingStatus, - text: !loadingStatus - ? widget.videoDetail!.stat!.favorite!.toString() - : '-'), + icon: const Icon(FontAwesomeIcons.star), + selectIcon: const Icon(FontAwesomeIcons.solidStar), + onTap: () => showFavBottomSheet(), + onLongPress: () => showFavBottomSheet(type: 'longPress'), + selectStatus: videoIntroController.hasFav.value, + text: widget.videoDetail!.stat!.favorite!.toString(), + ), ), ActionItem( - icon: const Icon(FontAwesomeIcons.comment), - onTap: () => videoDetailCtr.tabCtr.animateTo(1), - selectStatus: false, - loadingStatus: loadingStatus, - text: !loadingStatus - ? widget.videoDetail!.stat!.reply!.toString() - : '评论'), + icon: const Icon(FontAwesomeIcons.comment), + onTap: () => videoDetailCtr.tabCtr.animateTo(1), + selectStatus: false, + text: widget.videoDetail!.stat!.reply!.toString(), + ), ActionItem( - icon: const Icon(FontAwesomeIcons.shareFromSquare), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - loadingStatus: loadingStatus, - text: '分享'), + icon: const Icon(FontAwesomeIcons.shareFromSquare), + onTap: () => videoIntroController.actionShareVideo(), + selectStatus: false, + text: '分享', + ), ], ), ); }); } - - Widget actionRow(BuildContext context, videoIntroController, videoDetailCtr) { - return Row(children: [ - Obx( - () => ActionRowItem( - icon: const Icon(FontAwesomeIcons.thumbsUp), - onTap: handleState(videoIntroController.actionLikeVideo), - selectStatus: videoIntroController.hasLike.value, - loadingStatus: loadingStatus, - text: - !loadingStatus ? widget.videoDetail!.stat!.like!.toString() : '-', - ), - ), - const SizedBox(width: 8), - Obx( - () => ActionRowItem( - icon: const Icon(FontAwesomeIcons.b), - onTap: handleState(videoIntroController.actionCoinVideo), - selectStatus: videoIntroController.hasCoin.value, - loadingStatus: loadingStatus, - text: - !loadingStatus ? widget.videoDetail!.stat!.coin!.toString() : '-', - ), - ), - const SizedBox(width: 8), - Obx( - () => ActionRowItem( - icon: const Icon(FontAwesomeIcons.heart), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), - selectStatus: videoIntroController.hasFav.value, - loadingStatus: loadingStatus, - text: !loadingStatus - ? widget.videoDetail!.stat!.favorite!.toString() - : '-', - ), - ), - const SizedBox(width: 8), - ActionRowItem( - icon: const Icon(FontAwesomeIcons.comment), - onTap: () { - videoDetailCtr.tabCtr.animateTo(1); - }, - selectStatus: false, - loadingStatus: loadingStatus, - text: - !loadingStatus ? widget.videoDetail!.stat!.reply!.toString() : '-', - ), - const SizedBox(width: 8), - ActionRowItem( - icon: const Icon(FontAwesomeIcons.share), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - loadingStatus: loadingStatus, - // text: !loadingStatus - // ? widget.videoDetail!.stat!.share!.toString() - // : '-', - text: '转发'), - ]); - } } diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 95ac103b..022d9223 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -7,7 +7,6 @@ class ActionItem extends StatelessWidget { final Icon? selectIcon; final Function? onTap; final Function? onLongPress; - final bool? loadingStatus; final String? text; final bool selectStatus; @@ -17,7 +16,6 @@ class ActionItem extends StatelessWidget { this.selectIcon, this.onTap, this.onLongPress, - this.loadingStatus, this.text, this.selectStatus = false, }) : super(key: key); @@ -43,25 +41,15 @@ class ActionItem extends StatelessWidget { : Icon(icon!.icon!, size: 18, color: Theme.of(context).colorScheme.outline), const SizedBox(height: 6), - AnimatedOpacity( - opacity: loadingStatus! ? 0 : 1, - duration: const Duration(milliseconds: 200), - child: AnimatedSwitcher( - duration: const Duration(milliseconds: 300), - transitionBuilder: (Widget child, Animation animation) { - return ScaleTransition(scale: animation, child: child); - }, - child: Text( - text ?? '', - key: ValueKey(text ?? ''), - style: TextStyle( - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize), - ), + Text( + text ?? '', + style: TextStyle( + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, ), - ), + ) ], ), ); From 70317f92e2935e4ae5b996657df8c6af26367c92 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 13 Mar 2024 22:42:34 +0800 Subject: [PATCH 020/349] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E7=BB=93=E6=9E=84=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 84 ++++ lib/pages/video/detail/introduction/view.dart | 5 +- lib/pages/video/detail/view.dart | 459 +++++++++--------- .../video/detail/widgets/header_control.dart | 119 ----- 4 files changed, 320 insertions(+), 347 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index b4005b5a..40723efa 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -5,6 +5,7 @@ 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:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/reply_type.dart'; @@ -19,6 +20,7 @@ import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/video_utils.dart'; import 'package:screen_brightness/screen_brightness.dart'; +import '../../../http/danmaku.dart'; import '../../../utils/id_utils.dart'; import 'widgets/header_control.dart'; @@ -383,4 +385,86 @@ class VideoDetailController extends GetxController ? replyReplyBottomSheetCtr!.close() : print('replyReplyBottomSheetCtr is null'); } + + /// 发送弹幕 + void showShootDanmakuSheet() { + final TextEditingController textController = TextEditingController(); + bool isSending = false; // 追踪是否正在发送 + showDialog( + context: Get.context!, + builder: (BuildContext context) { + // TODO: 支持更多类型和颜色的弹幕 + return AlertDialog( + title: const Text('发送弹幕'), + content: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return TextField( + controller: textController, + ); + }), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return TextButton( + onPressed: isSending + ? null + : () async { + final String msg = textController.text; + if (msg.isEmpty) { + SmartDialog.showToast('弹幕内容不能为空'); + return; + } else if (msg.length > 100) { + SmartDialog.showToast('弹幕内容不能超过100个字符'); + return; + } + setState(() { + isSending = true; // 开始发送,更新状态 + }); + //修改按钮文字 + // SmartDialog.showToast('弹幕发送中,\n$msg'); + final dynamic res = await DanmakaHttp.shootDanmaku( + oid: cid.value, + msg: textController.text, + bvid: bvid, + progress: + plPlayerController.position.value.inMilliseconds, + type: 1, + ); + setState(() { + isSending = false; // 发送结束,更新状态 + }); + if (res['status']) { + SmartDialog.showToast('发送成功'); + // 发送成功,自动预览该弹幕,避免重新请求 + // TODO: 暂停状态下预览弹幕仍会移动与计时,可考虑添加到dmSegList或其他方式实现 + plPlayerController.danmakuController?.addItems([ + DanmakuItem( + msg, + color: Colors.white, + time: plPlayerController + .position.value.inMilliseconds, + type: DanmakuItemType.scroll, + isSend: true, + ) + ]); + Get.back(); + } else { + SmartDialog.showToast('发送失败,错误信息为${res['msg']}'); + } + }, + child: Text(isSending ? '发送中...' : '发送'), + ); + }) + ], + ); + }, + ); + } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index bed37bb5..6c713efe 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -268,7 +268,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { final Color outline = t.colorScheme.outline; return SliverPadding( padding: const EdgeInsets.only( - left: StyleString.safeSpace, right: StyleString.safeSpace, top: 10), + left: StyleString.safeSpace, + right: StyleString.safeSpace, + top: 16, + ), sliver: SliverToBoxAdapter( child: !loadingStatus ? Column( diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 04d41b2e..b02264cd 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -9,7 +9,6 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; -import 'package:nil/nil.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/search_type.dart'; @@ -25,7 +24,7 @@ import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/storage.dart'; import '../../../services/shutdown_timer_service.dart'; -import 'widgets/header_control.dart'; +import 'widgets/app_bar.dart'; class VideoDetailPage extends StatefulWidget { const VideoDetailPage({Key? key}) : super(key: key); @@ -38,7 +37,7 @@ class VideoDetailPage extends StatefulWidget { class _VideoDetailPageState extends State with TickerProviderStateMixin, RouteAware { - late VideoDetailController videoDetailController; + late VideoDetailController vdCtr; PlPlayerController? plPlayerController; final ScrollController _extendNestCtr = ScrollController(); late StreamController appbarStream; @@ -65,20 +64,18 @@ class _VideoDetailPageState extends State void initState() { super.initState(); heroTag = Get.arguments['heroTag']; - videoDetailController = Get.put(VideoDetailController(), tag: heroTag); + vdCtr = Get.put(VideoDetailController(), tag: heroTag); videoIntroController = Get.put( VideoIntroController(bvid: Get.parameters['bvid']!), tag: heroTag); videoIntroController.videoDetail.listen((value) { - videoPlayerServiceHandler.onVideoDetailChange( - value, videoDetailController.cid.value); + videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); }); bangumiIntroController = Get.put(BangumiIntroController(), tag: heroTag); bangumiIntroController.bangumiDetail.listen((value) { - videoPlayerServiceHandler.onVideoDetailChange( - value, videoDetailController.cid.value); + videoPlayerServiceHandler.onVideoDetailChange(value, vdCtr.cid.value); }); - videoDetailController.cid.listen((p0) { + vdCtr.cid.listen((p0) { videoPlayerServiceHandler.onVideoDetailChange( bangumiIntroController.bangumiDetail.value, p0); }); @@ -93,16 +90,16 @@ class _VideoDetailPageState extends State appbarStreamListen(); fullScreenStatusListener(); if (Platform.isAndroid) { - floating = videoDetailController.floating!; + floating = vdCtr.floating!; autoEnterPip(); } } // 获取视频资源,初始化播放器 Future videoSourceInit() async { - _futureBuilderFuture = videoDetailController.queryVideoUrl(); - if (videoDetailController.autoPlay.value) { - plPlayerController = videoDetailController.plPlayerController; + _futureBuilderFuture = vdCtr.queryVideoUrl(); + if (vdCtr.autoPlay.value) { + plPlayerController = vdCtr.plPlayerController; plPlayerController!.addStatusLister(playerListener); } } @@ -131,10 +128,10 @@ class _VideoDetailPageState extends State /// 顺序播放 列表循环 if (plPlayerController!.playRepeat != PlayRepeat.pause && plPlayerController!.playRepeat != PlayRepeat.singleCycle) { - if (videoDetailController.videoType == SearchType.video) { + if (vdCtr.videoType == SearchType.video) { videoIntroController.nextPlay(); } - if (videoDetailController.videoType == SearchType.media_bangumi) { + if (vdCtr.videoType == SearchType.media_bangumi) { bangumiIntroController.nextPlay(); } } @@ -146,8 +143,7 @@ class _VideoDetailPageState extends State } // 播放完展示控制栏 try { - PiPStatus currentStatus = - await videoDetailController.floating!.pipStatus; + PiPStatus currentStatus = await vdCtr.floating!.pipStatus; if (currentStatus == PiPStatus.disabled) { plPlayerController!.onLockControl(false); } @@ -168,17 +164,17 @@ class _VideoDetailPageState extends State /// 未开启自动播放时触发播放 Future handlePlay() async { - await videoDetailController.playerInit(); - plPlayerController = videoDetailController.plPlayerController; + await vdCtr.playerInit(); + plPlayerController = vdCtr.plPlayerController; plPlayerController!.addStatusLister(playerListener); - videoDetailController.autoPlay.value = true; - videoDetailController.isShowCover.value = false; + vdCtr.autoPlay.value = true; + vdCtr.isShowCover.value = false; } void fullScreenStatusListener() { plPlayerController?.isFullScreen.listen((bool isFullScreen) { if (isFullScreen) { - videoDetailController.hiddenReplyReplyPanel(); + vdCtr.hiddenReplyReplyPanel(); } }); } @@ -190,8 +186,8 @@ class _VideoDetailPageState extends State plPlayerController!.removeStatusLister(playerListener); plPlayerController!.dispose(); } - if (videoDetailController.floating != null) { - videoDetailController.floating!.dispose(); + if (vdCtr.floating != null) { + vdCtr.floating!.dispose(); } videoPlayerServiceHandler.onVideoDetailDispose(); if (Platform.isAndroid) { @@ -207,10 +203,10 @@ class _VideoDetailPageState extends State /// 开启 if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false) as bool) { - videoDetailController.brightness = plPlayerController!.brightness.value; + vdCtr.brightness = plPlayerController!.brightness.value; } if (plPlayerController != null) { - videoDetailController.defaultST = plPlayerController!.position.value; + vdCtr.defaultST = plPlayerController!.position.value; videoIntroController.isPaused = true; plPlayerController!.removeStatusLister(playerListener); plPlayerController!.pause(); @@ -226,17 +222,16 @@ class _VideoDetailPageState extends State plPlayerController!.videoPlayerController != null) { setState(() => isShowing = true); } - videoDetailController.isFirstTime = false; + vdCtr.isFirstTime = false; final bool autoplay = autoPlayEnable; - videoDetailController.playerInit(autoplay: autoplay); + vdCtr.playerInit(autoplay: autoplay); /// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回 - videoDetailController.autoPlay.value = - !videoDetailController.isShowCover.value; + vdCtr.autoPlay.value = !vdCtr.isShowCover.value; videoIntroController.isPaused = false; if (_extendNestCtr.position.pixels == 0 && autoplay) { await Future.delayed(const Duration(milliseconds: 300)); - plPlayerController?.seekTo(videoDetailController.defaultST); + plPlayerController?.seekTo(vdCtr.defaultST); plPlayerController?.play(); } plPlayerController?.addStatusLister(playerListener); @@ -259,9 +254,166 @@ class _VideoDetailPageState extends State @override Widget build(BuildContext context) { - final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; + // final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; + final sizeContext = MediaQuery.sizeOf(context); + final _context = MediaQuery.of(context); + late double defaultVideoHeight = sizeContext.width * 9 / 16; + late RxDouble videoHeight = defaultVideoHeight.obs; final double pinnedHeaderHeight = - statusBarHeight + kToolbarHeight + videoHeight; + statusBarHeight + kToolbarHeight + videoHeight.value; + // ignore: no_leading_underscores_for_local_identifiers + + // 竖屏 + final bool isPortrait = _context.orientation == Orientation.portrait; + // 横屏 + final bool isLandscape = _context.orientation == Orientation.landscape; + final Rx isFullScreen = plPlayerController?.isFullScreen ?? false.obs; + // 全屏时高度撑满 + if (isLandscape || isFullScreen.value == true) { + videoHeight.value = Get.size.height; + enterFullScreen(); + } else { + videoHeight.value = defaultVideoHeight; + exitFullScreen(); + } + + /// 播放器面板 + Widget videoPlayerPanel = FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData && snapshot.data['status']) { + return Obx( + () { + return !vdCtr.autoPlay.value + ? const SizedBox() + : PLVideoPlayer( + controller: plPlayerController!, + headerControl: vdCtr.headerControl, + danmuWidget: Obx( + () => PlDanmaku( + key: Key(vdCtr.danmakuCid.value.toString()), + cid: vdCtr.danmakuCid.value, + playerController: plPlayerController!, + ), + ), + ); + }, + ); + } else { + // 加载失败异常处理 + return const SizedBox(); + } + }, + ); + + /// tabbar + Widget tabbarBuild = Container( + width: double.infinity, + height: 45, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + ), + ), + child: Row( + children: [ + const SizedBox(width: 20), + Expanded( + child: TabBar( + controller: vdCtr.tabCtr, + dividerColor: Colors.transparent, + tabs: vdCtr.tabs.map((String name) => Tab(text: name)).toList(), + ), + ), + SizedBox( + width: 220, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => vdCtr.showShootDanmakuSheet(), + child: const Text('发弹幕', style: TextStyle(fontSize: 12)), + ), + ), + const SizedBox(width: 4), + SizedBox( + width: 34, + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + plPlayerController?.isOpenDanmu.value = + !(plPlayerController?.isOpenDanmu.value ?? false); + }, + child: Obx(() => Text( + '弹', + style: TextStyle( + fontSize: 12, + color: (plPlayerController?.isOpenDanmu.value ?? + false) + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + )), + ), + ), + const SizedBox(width: 14), + ], + ), + ), + ), + ], + ), + ); + + /// 手动播放 + Widget handlePlayPanel() { + return Stack( + children: [ + GestureDetector( + onTap: () { + handlePlay(); + }, + child: NetworkImgLayer( + type: 'emote', + src: vdCtr.videoItem['pic'], + width: Get.width, + height: videoHeight.value, + ), + ), + Positioned( + top: 0, + left: 0, + right: 0, + child: buildCustomAppBar(), + ), + Positioned( + right: 12, + bottom: 10, + child: IconButton( + tooltip: '播放', + onPressed: () => handlePlay(), + icon: Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + )), + ), + ], + ); + } + Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, @@ -273,7 +425,7 @@ class _VideoDetailPageState extends State children: [ Scaffold( resizeToAvoidBottomInset: false, - key: videoDetailController.scaffoldKey, + key: vdCtr.scaffoldKey, backgroundColor: Colors.black, appBar: PreferredSize( preferredSize: const Size.fromHeight(0), @@ -299,21 +451,19 @@ class _VideoDetailPageState extends State return SliverAppBar( automaticallyImplyLeading: false, // 假装使用一个非空变量,避免Obx检测不到而罢工 - pinned: videoDetailController.autoPlay.value ^ - false ^ - videoDetailController.autoPlay.value, + pinned: vdCtr.autoPlay.value, elevation: 0, scrolledUnderElevation: 0, forceElevated: innerBoxIsScrolled, expandedHeight: MediaQuery.of(context).orientation == Orientation.landscape || plPlayerController?.isFullScreen.value == true - ? MediaQuery.sizeOf(context).height - + ? (MediaQuery.sizeOf(context).height - (MediaQuery.of(context).orientation == Orientation.landscape ? 0 - : MediaQuery.of(context).padding.top) - : videoHeight, + : MediaQuery.of(context).padding.top)) + : videoHeight.value, backgroundColor: Colors.black, flexibleSpace: FlexibleSpaceBar( background: PopScope( @@ -333,108 +483,27 @@ class _VideoDetailPageState extends State child: LayoutBuilder( builder: (BuildContext context, BoxConstraints boxConstraints) { - final double maxWidth = - boxConstraints.maxWidth; - final double maxHeight = - boxConstraints.maxHeight; + // final double maxWidth = + // boxConstraints.maxWidth; + // final double maxHeight = + // boxConstraints.maxHeight; return Stack( children: [ - if (isShowing) - FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, - AsyncSnapshot snapshot) { - if (snapshot.hasData && - snapshot.data['status']) { - return Obx( - () => - !videoDetailController - .autoPlay.value - ? nil - : PLVideoPlayer( - controller: - plPlayerController!, - headerControl: - videoDetailController - .headerControl, - danmuWidget: Obx( - () => PlDanmaku( - key: Key(videoDetailController - .danmakuCid - .value - .toString()), - cid: videoDetailController - .danmakuCid - .value, - playerController: - plPlayerController!, - ), - ), - ), - ); - } else { - return buildCustomAppBar(); - } - }, - ), + if (isShowing) videoPlayerPanel, /// 关闭自动播放时 手动播放 - if (!videoDetailController - .autoPlay.value) ...[ - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: GestureDetector( - onTap: () { - handlePlay(); - }, - child: NetworkImgLayer( - type: 'emote', - src: videoDetailController - .videoItem['pic'], - width: maxWidth, - height: maxHeight, - ), - ), - ), + Obx( + () => Visibility( + visible: !vdCtr.autoPlay.value && + vdCtr.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: handlePlayPanel(), ), ), - Obx( - () => Visibility( - visible: videoDetailController - .isShowCover.value && - videoDetailController - .isEffective.value, - child: Stack( - children: [ - Positioned( - top: 0, - left: 0, - right: 0, - child: buildCustomAppBar(), - ), - Positioned( - right: 12, - bottom: 10, - child: IconButton( - tooltip: '播放', - onPressed: () => - handlePlay(), - icon: Image.asset( - 'assets/images/play.png', - width: 60, - height: 60, - )), - ), - ], - )), - ), - ] + ), ], ); }, @@ -445,18 +514,16 @@ class _VideoDetailPageState extends State ), ]; }, - // pinnedHeaderSliverHeightBuilder: () { - // return playerStatus != PlayerStatus.playing - // ? statusBarHeight + kToolbarHeight - // : pinnedHeaderHeight; - // }, + /// 不收回 pinnedHeaderSliverHeightBuilder: () { return MediaQuery.of(context).orientation == Orientation.landscape || plPlayerController?.isFullScreen.value == true ? MediaQuery.sizeOf(context).height - : pinnedHeaderHeight; + : playerStatus != PlayerStatus.playing + ? kToolbarHeight + : pinnedHeaderHeight; }, onlyOneScrollInBody: true, body: ColoredBox( @@ -464,54 +531,23 @@ class _VideoDetailPageState extends State color: Theme.of(context).colorScheme.background, child: Column( children: [ - Opacity( - opacity: 0, - child: SizedBox( - width: double.infinity, - height: 0, - child: Obx( - () => TabBar( - controller: videoDetailController.tabCtr, - dividerColor: Colors.transparent, - indicatorColor: - Theme.of(context).colorScheme.background, - tabs: videoDetailController.tabs - .map((String name) => Tab(text: name)) - .toList(), - ), - ), - ), - ), + tabbarBuild, Expanded( child: TabBarView( - controller: videoDetailController.tabCtr, + controller: vdCtr.tabCtr, children: [ Builder( builder: (BuildContext context) { return CustomScrollView( key: const PageStorageKey('简介'), slivers: [ - if (videoDetailController.videoType == - SearchType.video) ...[ - VideoIntroPanel( - bvid: videoDetailController.bvid), - ] else if (videoDetailController.videoType == + if (vdCtr.videoType == SearchType.video) ...[ + VideoIntroPanel(bvid: vdCtr.bvid), + ] else if (vdCtr.videoType == SearchType.media_bangumi) ...[ Obx(() => BangumiIntroPanel( - cid: videoDetailController.cid.value)), + cid: vdCtr.cid.value)), ], - // if (videoDetailController.videoType == - // SearchType.video) ...[ - // SliverPersistentHeader( - // floating: true, - // pinned: true, - // delegate: SliverHeaderDelegate( - // height: 50, - // child: - // const MenuRow(loadingStatus: false), - // ), - // ), - // ], SliverToBoxAdapter( child: Divider( indent: 12, @@ -528,8 +564,8 @@ class _VideoDetailPageState extends State ), Obx( () => VideoReplyPanel( - bvid: videoDetailController.bvid, - oid: videoDetailController.oid.value, + bvid: vdCtr.bvid, + oid: vdCtr.oid.value, ), ) ], @@ -543,56 +579,26 @@ class _VideoDetailPageState extends State /// 重新进入会刷新 // 播放完成/暂停播放 - // StreamBuilder( - // stream: appbarStream.stream, - // initialData: 0, - // builder: ((context, snapshot) { - // return ScrollAppBar( - // snapshot.data!.toDouble(), - // () => continuePlay(), - // playerStatus, - // null, - // ); - // }), - // ) + StreamBuilder( + stream: appbarStream.stream, + initialData: 0, + builder: ((context, snapshot) { + return ScrollAppBar( + snapshot.data!.toDouble(), + () => continuePlay(), + playerStatus, + null, + ); + }), + ) ], ), ); - Widget childWhenEnabled = FutureBuilder( - key: Key(heroTag), - future: _futureBuilderFuture, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData && snapshot.data['status']) { - return Obx( - () => !videoDetailController.autoPlay.value - ? const SizedBox() - : PLVideoPlayer( - controller: plPlayerController!, - headerControl: HeaderControl( - controller: plPlayerController, - videoDetailCtr: videoDetailController, - bvid: videoDetailController.bvid, - videoType: videoDetailController.videoType, - ), - danmuWidget: Obx( - () => PlDanmaku( - key: Key( - videoDetailController.danmakuCid.value.toString()), - cid: videoDetailController.danmakuCid.value, - playerController: plPlayerController!, - ), - ), - ), - ); - } else { - return nil; - } - }, - ); + if (Platform.isAndroid) { return PiPSwitcher( childWhenDisabled: childWhenDisabled, - childWhenEnabled: childWhenEnabled, + childWhenEnabled: videoPlayerPanel, floating: floating, ); } else { @@ -633,8 +639,7 @@ class _VideoDetailPageState extends State ComBtn( icon: const Icon(Icons.history_outlined, size: 22), fuc: () async { - var res = await UserHttp.toViewLater( - bvid: videoDetailController.bvid); + var res = await UserHttp.toViewLater(bvid: vdCtr.bvid); SmartDialog.showToast(res['msg']); }, ), diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e72e6a69..a8414c18 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1,5 +1,4 @@ import 'dart:io'; -import 'dart:math'; import 'package:floating/floating.dart'; import 'package:flutter/material.dart'; @@ -17,7 +16,6 @@ import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; 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'; @@ -221,88 +219,6 @@ class _HeaderControlState extends State { ); } - /// 发送弹幕 - void showShootDanmakuSheet() { - final TextEditingController textController = TextEditingController(); - bool isSending = false; // 追踪是否正在发送 - showDialog( - context: Get.context!, - builder: (BuildContext context) { - // TODO: 支持更多类型和颜色的弹幕 - return AlertDialog( - title: const Text('发送弹幕(测试)'), - content: StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return TextField( - controller: textController, - ); - }), - actions: [ - TextButton( - onPressed: () => Get.back(), - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return TextButton( - onPressed: isSending - ? null - : () async { - final String msg = textController.text; - if (msg.isEmpty) { - SmartDialog.showToast('弹幕内容不能为空'); - return; - } else if (msg.length > 100) { - SmartDialog.showToast('弹幕内容不能超过100个字符'); - return; - } - setState(() { - isSending = true; // 开始发送,更新状态 - }); - //修改按钮文字 - // SmartDialog.showToast('弹幕发送中,\n$msg'); - final dynamic res = await DanmakaHttp.shootDanmaku( - oid: widget.videoDetailCtr!.cid.value, - msg: textController.text, - bvid: widget.videoDetailCtr!.bvid, - progress: - widget.controller!.position.value.inMilliseconds, - type: 1, - ); - setState(() { - isSending = false; // 发送结束,更新状态 - }); - if (res['status']) { - SmartDialog.showToast('发送成功'); - // 发送成功,自动预览该弹幕,避免重新请求 - // TODO: 暂停状态下预览弹幕仍会移动与计时,可考虑添加到dmSegList或其他方式实现 - widget.controller!.danmakuController!.addItems([ - DanmakuItem( - msg, - color: Colors.white, - time: widget - .controller!.position.value.inMilliseconds, - type: DanmakuItemType.scroll, - isSend: true, - ) - ]); - Get.back(); - } else { - SmartDialog.showToast('发送失败,错误信息为${res['msg']}'); - } - }, - child: Text(isSending ? '发送中...' : '发送'), - ); - }) - ], - ); - }, - ); - } - /// 定时关闭 void scheduleExit() async { const List scheduleTimeChoices = [ @@ -1166,41 +1082,6 @@ class _HeaderControlState extends State { // ), // fuc: () => _.screenshot(), // ), - SizedBox( - width: 56, - height: 34, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => showShootDanmakuSheet(), - child: const Text( - '发弹幕', - style: textStyle, - ), - ), - ), - SizedBox( - width: 34, - height: 34, - child: Obx( - () => IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - _.isOpenDanmu.value = !_.isOpenDanmu.value; - }, - icon: Icon( - _.isOpenDanmu.value - ? Icons.subtitles_outlined - : Icons.subtitles_off_outlined, - size: 19, - color: Colors.white, - ), - ), - ), - ), SizedBox(width: buttonSpace), if (Platform.isAndroid) ...[ SizedBox( From da2bbeedffdfd08143a4353a1cf6abd0719d41c0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 13 Mar 2024 23:12:34 +0800 Subject: [PATCH 021/349] =?UTF-8?q?mod:=20=E9=BB=98=E8=AE=A4=E7=9B=B4?= =?UTF-8?q?=E6=92=AD=E7=94=BB=E8=B4=A8=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/live/quality.dart | 2 +- lib/pages/live_room/controller.dart | 4 +- .../live_room/widgets/bottom_control.dart | 50 +++++++++---------- lib/pages/setting/play_setting.dart | 36 +++++++++++-- lib/utils/storage.dart | 1 + 5 files changed, 62 insertions(+), 31 deletions(-) diff --git a/lib/models/live/quality.dart b/lib/models/live/quality.dart index 47938416..677d615b 100644 --- a/lib/models/live/quality.dart +++ b/lib/models/live/quality.dart @@ -2,7 +2,6 @@ enum LiveQuality { dolby, super4K, origin, - veryHigh, bluRay, superHD, smooth, @@ -37,6 +36,7 @@ extension VideoQualityDesc on LiveQuality { '原画', '蓝光', '超清', + '高清', '流畅', ]; get description => _descList[index]; diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index a1878c74..f785da63 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -27,7 +27,8 @@ class LiveRoomController extends GetxController { @override void onInit() { super.onInit(); - currentQn = 10000; + currentQn = setting.get(SettingBoxKey.defaultLiveQa, + defaultValue: LiveQuality.values.last.code); roomId = int.parse(Get.parameters['roomid']!); if (Get.arguments != null) { liveItem = Get.arguments['liveItem']; @@ -62,7 +63,6 @@ class LiveRoomController extends GetxController { } Future queryLiveInfo() async { - /// TODO 默认获取预设质量的直播资源 var res = await LiveHttp.liveRoomInfo(roomId: roomId, qn: currentQn); if (res['status']) { List codec = diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 0dcedc2c..c98a80ae 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -85,6 +85,30 @@ class _BottomControlState extends State { // ), // ), // const SizedBox(width: 4), + SizedBox( + width: 30, + child: PopupMenuButton( + padding: EdgeInsets.zero, + onSelected: (value) { + widget.liveRoomCtr!.changeQn(value); + }, + child: Obx( + () => Text( + widget.liveRoomCtr!.currentQnDesc.value, + style: const TextStyle(color: Colors.white, fontSize: 13), + ), + ), + itemBuilder: (BuildContext context) { + return widget.liveRoomCtr!.acceptQnList.map((e) { + return PopupMenuItem( + value: e['code'], + child: Text(e['desc']), + ); + }).toList(); + }, + ), + ), + const SizedBox(width: 10), if (Platform.isAndroid) ...[ SizedBox( width: 34, @@ -112,32 +136,8 @@ class _BottomControlState extends State { ), ), ), - const SizedBox(width: 4), + const SizedBox(width: 10), ], - SizedBox( - width: 30, - child: PopupMenuButton( - padding: EdgeInsets.zero, - onSelected: (value) { - widget.liveRoomCtr!.changeQn(value); - }, - child: Obx( - () => Text( - widget.liveRoomCtr!.currentQnDesc.value, - style: const TextStyle(color: Colors.white, fontSize: 13), - ), - ), - itemBuilder: (BuildContext context) { - return widget.liveRoomCtr!.acceptQnList.map((e) { - return PopupMenuItem( - value: e['code'], - child: Text(e['desc']), - ); - }).toList(); - }, - ), - ), - const SizedBox(width: 10), ComBtn( icon: const Icon( Icons.fullscreen, diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index bfd5db5f..03e91212 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -9,6 +9,7 @@ import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/storage.dart'; +import '../../models/live/quality.dart'; import 'widgets/switch_item.dart'; class PlaySetting extends StatefulWidget { @@ -21,6 +22,7 @@ class PlaySetting extends StatefulWidget { class _PlaySettingState extends State { Box setting = GStrorage.setting; late dynamic defaultVideoQa; + late dynamic defaultLiveQa; late dynamic defaultAudioQa; late dynamic defaultDecode; late int defaultFullScreenMode; @@ -31,6 +33,8 @@ class _PlaySettingState extends State { super.initState(); defaultVideoQa = setting.get(SettingBoxKey.defaultVideoQa, defaultValue: VideoQuality.values.last.code); + defaultLiveQa = setting.get(SettingBoxKey.defaultLiveQa, + defaultValue: LiveQuality.values.last.code); defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.values.last.code); defaultDecode = setting.get(SettingBoxKey.defaultDecode, @@ -148,9 +152,9 @@ class _PlaySettingState extends State { ), ListTile( dense: false, - title: Text('默认画质', style: titleStyle), + title: Text('默认视频画质', style: titleStyle), subtitle: Text( - '当前画质${VideoQualityCode.fromCode(defaultVideoQa)!.description!}', + '当前默认画质${VideoQualityCode.fromCode(defaultVideoQa)!.description!}', style: subTitleStyle, ), onTap: () async { @@ -158,7 +162,7 @@ class _PlaySettingState extends State { context: context, builder: (context) { return SelectDialog( - title: '默认画质', + title: '默认视频画质', value: defaultVideoQa, values: VideoQuality.values.reversed.map((e) { return {'title': e.description, 'value': e.code}; @@ -172,6 +176,32 @@ class _PlaySettingState extends State { } }, ), + ListTile( + dense: false, + title: Text('默认直播画质', style: titleStyle), + subtitle: Text( + '当前默认画质${LiveQualityCode.fromCode(defaultLiveQa)!.description!}', + style: subTitleStyle, + ), + onTap: () async { + int? result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '默认直播画质', + value: defaultLiveQa, + values: LiveQuality.values.reversed.map((e) { + return {'title': e.description, 'value': e.code}; + }).toList()); + }, + ); + if (result != null) { + defaultLiveQa = result; + setting.put(SettingBoxKey.defaultLiveQa, result); + setState(() {}); + } + }, + ), ListTile( dense: false, title: Text('默认音质', style: titleStyle), diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 16cef463..47fd7596 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -77,6 +77,7 @@ class SettingBoxKey { autoUpgradeEnable = 'autoUpgradeEnable', feedBackEnable = 'feedBackEnable', defaultVideoQa = 'defaultVideoQa', + defaultLiveQa = 'defaultLiveQa', defaultAudioQa = 'defaultAudioQa', autoPlayEnable = 'autoPlayEnable', fullScreenMode = 'fullScreenMode', From 337cdafef3d30876d78d4983cc6d69868f647a6e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 14 Mar 2024 00:18:03 +0800 Subject: [PATCH 022/349] typo: vdCtr --- lib/pages/video/detail/view.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index eee9af3e..71e7edd3 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -557,9 +557,8 @@ class _VideoDetailPageState extends State .withOpacity(0.06), ), ), - if (videoDetailController.videoType == - SearchType.video && - videoDetailController.enableRelatedVideo) + if (vdCtr.videoType == SearchType.video && + vdCtr.enableRelatedVideo) const RelatedVideoPanel(), ], ); From 357133fa975d0c059e897117db849536d5444be4 Mon Sep 17 00:00:00 2001 From: guohaonan <972101775@qq.com> Date: Thu, 14 Mar 2024 10:00:32 +0800 Subject: [PATCH 023/349] =?UTF-8?q?fix:=E5=8E=BB=E9=99=A4=E6=97=A0?= =?UTF-8?q?=E7=94=A8=E7=9A=84setState?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/view.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 5fa06c91..fe594a43 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -263,9 +263,7 @@ class _DynamicsPageState extends State errMsg: data['msg'], btnText: "去登录", fn: () { - setState(() { - mineController.onLogin(); - }); + mineController.onLogin(); }, ); } else { From 606f1b5c64bd00fedbfba28d7d20ccc627a46410 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 14 Mar 2024 23:30:18 +0800 Subject: [PATCH 024/349] =?UTF-8?q?fix:=20=E6=94=B6=E8=97=8F=E5=A4=B9?= =?UTF-8?q?=E8=AF=A6=E6=83=85=E8=AE=A1=E6=95=B0=E9=94=99=E8=AF=AF=20issues?= =?UTF-8?q?=20#637?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav_detail/controller.dart | 8 ++++---- lib/pages/fav_detail/view.dart | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 69cc939e..55d5b884 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -16,7 +16,7 @@ class FavDetailController extends GetxController { RxMap favInfo = {}.obs; RxList favList = [].obs; RxString loadingText = '加载中...'.obs; - int mediaCount = 0; + RxInt mediaCount = 0.obs; @override void onInit() { @@ -29,7 +29,7 @@ class FavDetailController extends GetxController { } Future queryUserFavFolderDetail({type = 'init'}) async { - if (type == 'onLoad' && favList.length >= mediaCount) { + if (type == 'onLoad' && favList.length >= mediaCount.value) { loadingText.value = '没有更多了'; return; } @@ -43,11 +43,11 @@ class FavDetailController extends GetxController { favInfo.value = res['data'].info; if (currentPage == 1 && type == 'init') { favList.value = res['data'].medias; - mediaCount = res['data'].info['media_count']; + mediaCount.value = res['data'].info['media_count']; } else if (type == 'onLoad') { favList.addAll(res['data'].medias); } - if (favList.length >= mediaCount) { + if (favList.length >= mediaCount.value) { loadingText.value = '没有更多了'; } } diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 27d7182b..d94f5149 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -84,7 +84,7 @@ class _FavDetailPageState extends State { style: Theme.of(context).textTheme.titleMedium, ), Text( - '共${_favDetailController.item!.mediaCount!}条视频', + '共${_favDetailController.mediaCount}条视频', style: Theme.of(context).textTheme.labelMedium, ) ], @@ -175,7 +175,7 @@ class _FavDetailPageState extends State { padding: const EdgeInsets.only(top: 15, bottom: 8, left: 14), child: Obx( () => Text( - '共${_favDetailController.favList.length}条视频', + '共${_favDetailController.mediaCount}条视频', style: TextStyle( fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, From b13d7b475b47203e9172065103db02816744406e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 14 Mar 2024 23:49:36 +0800 Subject: [PATCH 025/349] =?UTF-8?q?feat:=20=E9=9D=9E=E5=85=A8=E5=B1=8F?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E4=B8=8B=E9=9A=90=E8=97=8F=E9=94=81=E5=AE=9A?= =?UTF-8?q?=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 988ec26d..203f6c97 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -672,7 +672,7 @@ class _PLVideoPlayerState extends State // 锁 Obx( () => Visibility( - visible: _.videoType.value != 'live', + visible: _.videoType.value != 'live' && _.isFullScreen.value, child: Align( alignment: Alignment.centerLeft, child: FractionalTranslation( From 491bc87251047a6d1284660337101798f7323cd0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 15 Mar 2024 23:26:43 +0800 Subject: [PATCH 026/349] =?UTF-8?q?fix:=20=E7=A7=81=E4=BF=A1=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E6=B8=B2=E6=9F=93=E5=BC=82=E5=B8=B8=20issues=20#295?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/msg.dart | 4 ++-- lib/pages/whisper/view.dart | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 4ba2f818..d1d31958 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -31,14 +31,14 @@ class MsgHttp { } catch (err) { return { 'status': false, - 'date': [], + 'data': [], 'msg': err.toString(), }; } } else { return { 'status': false, - 'date': [], + 'data': [], 'msg': res.data['message'], }; } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index f1c58650..fa7ad60b 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -180,7 +180,8 @@ class _WhisperPageState extends State { sessionList[i] .lastMsg .content[ - 'reply_content']) + 'reply_content'] ?? + '不支持的消息类型') : '不支持的消息类型', maxLines: 1, overflow: TextOverflow.ellipsis, From 13c77957fe3de669896b8dc130c0ffaaab6cbdd6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 16 Mar 2024 22:48:47 +0800 Subject: [PATCH 027/349] =?UTF-8?q?fix:=20=E7=95=AA=E5=89=A7badge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/introduction/view.dart | 22 +++++----- lib/pages/bangumi/widgets/bangumi_panel.dart | 42 +++++++++----------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index f9efc66c..3b2cf122 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -362,17 +362,17 @@ class _BangumiInfoState extends State { bangumiItem != null && bangumiItem!.episodes!.isNotEmpty) ...[ BangumiPanel( - pages: bangumiItem != null - ? bangumiItem!.episodes! - : widget.bangumiDetail!.episodes!, - cid: cid ?? - (bangumiItem != null - ? bangumiItem!.episodes!.first.cid - : widget.bangumiDetail!.episodes!.first.cid), - sheetHeight: sheetHeight, - changeFuc: (bvid, cid, aid) => bangumiIntroController - .changeSeasonOrbangu(bvid, cid, aid), - ) + pages: bangumiItem != null + ? bangumiItem!.episodes! + : widget.bangumiDetail!.episodes!, + cid: cid ?? + (bangumiItem != null + ? bangumiItem!.episodes!.first.cid + : widget.bangumiDetail!.episodes!.first.cid), + sheetHeight: sheetHeight, + changeFuc: (bvid, cid, aid) => bangumiIntroController + .changeSeasonOrbangu(bvid, cid, aid), + bangumiDetail: widget.bangumiDetail ?? bangumiItem) ], ], ) diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 05889f16..791cc108 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -14,12 +14,14 @@ class BangumiPanel extends StatefulWidget { this.cid, this.sheetHeight, this.changeFuc, + this.bangumiDetail, }); final List pages; final int? cid; final double? sheetHeight; final Function? changeFuc; + final BangumiInfoModel? bangumiDetail; @override State createState() => _BangumiPanelState(); @@ -87,7 +89,7 @@ class _BangumiPanelState extends State { ) : null, title: Text( - '第${index + 1}话 ${page.longTitle!}', + '第${page.title}话 ${page.longTitle!}', style: TextStyle( fontSize: 14, color: isCurrentIndex @@ -96,9 +98,11 @@ class _BangumiPanelState extends State { ), ), trailing: page.badge != null - ? Image.asset( - 'assets/images/big-vip.png', - height: 20, + ? Text( + page.badge!, + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), ) : const SizedBox(), ); @@ -201,11 +205,11 @@ class _BangumiPanelState extends State { return Column( children: [ Padding( - padding: const EdgeInsets.only(top: 10, bottom: 6), + padding: const EdgeInsets.only(top: 10, bottom: 10), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - const Text('合集 '), + const Text('选集 '), Expanded( child: Text( ' 正在播放:${widget.pages[currentIndex].longTitle}', @@ -225,7 +229,7 @@ class _BangumiPanelState extends State { ), onPressed: () => showBangumiPanel(), child: Text( - '全${widget.pages.length}话', + '${widget.bangumiDetail!.newEp!['desc']}', style: const TextStyle(fontSize: 13), ), ), @@ -278,23 +282,15 @@ class _BangumiPanelState extends State { ), const SizedBox(width: 2), if (widget.pages[i].badge != null) ...[ - if (widget.pages[i].badge == '会员') ...[ - Image.asset( - 'assets/images/big-vip.png', - height: 16, + const Spacer(), + Text( + widget.pages[i].badge!, + style: TextStyle( + fontSize: 12, + color: + Theme.of(context).colorScheme.primary, ), - ], - if (widget.pages[i].badge != '会员') ...[ - const Spacer(), - Text( - widget.pages[i].badge!, - style: TextStyle( - fontSize: 11, - color: - Theme.of(context).colorScheme.primary, - ), - ), - ], + ), ] ], ), From 2985c624ab1207a9b780a05dfd35da8f6ec492c6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 16 Mar 2024 23:02:21 +0800 Subject: [PATCH 028/349] mod: stream close --- lib/pages/video/detail/view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 71e7edd3..1dff9060 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -194,6 +194,7 @@ class _VideoDetailPageState extends State floating.toggleAutoPip(autoEnter: false); floating.dispose(); } + appbarStream.close(); super.dispose(); } From 33d28f51d1819f9f28451bf315a0ad8ee16188ad Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 17 Mar 2024 00:36:00 +0800 Subject: [PATCH 029/349] =?UTF-8?q?feat:=20=E6=9C=AA=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=88=87=E6=8D=A2=E7=9B=B4=E6=92=AD=E7=94=BB?= =?UTF-8?q?=E8=B4=A8=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/controller.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index f785da63..5c2a9800 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/live.dart'; @@ -21,6 +22,7 @@ class LiveRoomController extends GetxController { Rx roomInfoH5 = RoomInfoH5Model().obs; late bool enableCDN; late int currentQn; + int? tempCurrentQn; late List> acceptQnList; RxString currentQnDesc = ''.obs; @@ -70,6 +72,9 @@ class LiveRoomController extends GetxController { CodecItem item = codec.first; // 以服务端返回的码率为准 currentQn = item.currentQn!; + if (tempCurrentQn != null && tempCurrentQn == currentQn) { + SmartDialog.showToast('画质切换失败,请检查登录状态'); + } List acceptQn = item.acceptQn!; acceptQnList = acceptQn.map((e) { return { @@ -113,6 +118,7 @@ class LiveRoomController extends GetxController { // 修改画质 void changeQn(int qn) async { + tempCurrentQn = currentQn; if (currentQn == qn) { return; } From e04a7e5702961340cdcbde71f9b187b42bdeae0f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 17 Mar 2024 14:29:39 +0800 Subject: [PATCH 030/349] =?UTF-8?q?fix:=20=E5=9B=BE=E7=89=87=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E5=91=BD=E5=90=8D=E3=80=81=E8=B4=A8=E9=87=8F=E3=80=81?= =?UTF-8?q?=E6=9D=83=E9=99=90=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/download.dart | 76 +++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 11 deletions(-) diff --git a/lib/utils/download.dart b/lib/utils/download.dart index ad008f6d..a9c56ec0 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -1,40 +1,94 @@ import 'dart:typed_data'; import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:saver_gallery/saver_gallery.dart'; class DownloadUtils { // 获取存储权限 - static requestStoragePer() async { - Map statuses = await [ - Permission.storage, - Permission.photos, - ].request(); - statuses[Permission.storage].toString(); + static Future requestStoragePer() async { + await Permission.storage.request(); + PermissionStatus status = await Permission.storage.status; + if (status == PermissionStatus.denied || + status == PermissionStatus.permanentlyDenied) { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('存储权限未授权'), + actions: [ + TextButton( + onPressed: () async { + openAppSettings(); + }, + child: const Text('去授权'), + ) + ], + ); + }, + ); + return false; + } else { + return true; + } + } + + // 获取相册权限 + static Future requestPhotoPer() async { + await Permission.photos.request(); + PermissionStatus status = await Permission.photos.status; + if (status == PermissionStatus.denied || + status == PermissionStatus.permanentlyDenied) { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('相册权限未授权'), + actions: [ + TextButton( + onPressed: () async { + openAppSettings(); + }, + child: const Text('去授权'), + ) + ], + ); + }, + ); + return false; + } else { + return true; + } } static Future downloadImg(String imgUrl, {String imgType = 'cover'}) async { try { - await requestStoragePer(); + if (!await requestPhotoPer()) { + return false; + } SmartDialog.showLoading(msg: '保存中'); var response = await Dio() .get(imgUrl, options: Options(responseType: ResponseType.bytes)); + final String imgSuffix = imgUrl.split('.').last; String picName = - "plpl_${imgType}_${DateTime.now().toString().split('-').join()}"; + "plpl_${imgType}_${DateTime.now().toString().replaceAll(RegExp(r'[- :]'), '').split('.').first}"; final SaveResult result = await SaverGallery.saveImage( Uint8List.fromList(response.data), - quality: 60, - name: picName, + name: '$picName.$imgSuffix', // 保存到 PiliPala文件夹 androidRelativePath: "Pictures/PiliPala", androidExistNotSave: false, ); SmartDialog.dismiss(); if (result.isSuccess) { - await SmartDialog.showToast('「$picName」已保存 '); + await SmartDialog.showToast('「${'$picName.$imgSuffix'}」已保存 '); } return true; } catch (err) { From 64292d523f26bd679447d1d34f5c3c51087c8760 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 18 Mar 2024 22:13:15 +0800 Subject: [PATCH 031/349] fix: player fit rebuild --- lib/plugin/pl_player/view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 0b2e652e..02f442d4 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -346,6 +346,7 @@ class _PLVideoPlayerState extends State children: [ Obx( () => Video( + key: ValueKey(_.videoFit.value), controller: videoController, controls: NoVideoControls, pauseUponEnteringBackgroundMode: !enableBackgroundPlay, From 2dbef3fee24ff5e9320b2c28f4e2e1835aa38416 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 19 Mar 2024 23:23:14 +0800 Subject: [PATCH 032/349] =?UTF-8?q?mod:=20media=5Fkit=E5=BC=95=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.lock | 12 ++++++------ pubspec.yaml | 27 ++++++--------------------- 2 files changed, 12 insertions(+), 27 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index a42bd421..fc479e1a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -858,8 +858,8 @@ packages: dependency: "direct main" description: path: media_kit - ref: main - resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + ref: HEAD + resolved-ref: "77a130b1d7ce733b47d2133b57563716090450d0" url: "https://github.com/media-kit/media-kit.git" source: git version: "1.1.10+1" @@ -899,8 +899,8 @@ packages: dependency: "direct main" description: path: "libs/universal/media_kit_libs_video" - ref: main - resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + ref: HEAD + resolved-ref: "77a130b1d7ce733b47d2133b57563716090450d0" url: "https://github.com/media-kit/media-kit.git" source: git version: "1.0.4" @@ -924,8 +924,8 @@ packages: dependency: "direct main" description: path: media_kit_video - ref: main - resolved-ref: "63c6ebe8366db7ecfbd13ab9ce76b11dd86dae48" + ref: HEAD + resolved-ref: "77a130b1d7ce733b47d2133b57563716090450d0" url: "https://github.com/media-kit/media-kit.git" source: git version: "1.2.4" diff --git a/pubspec.yaml b/pubspec.yaml index 23cc509f..e6690a37 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -85,21 +85,9 @@ dependencies: encrypt: ^5.0.3 # 视频播放器 - media_kit: - git: - url: https://github.com/media-kit/media-kit.git - ref: main - path: ./media_kit - media_kit_video: - git: - url: https://github.com/media-kit/media-kit.git - ref: main - path: ./media_kit_video - media_kit_libs_video: - git: - url: https://github.com/media-kit/media-kit.git - ref: main - path: ./libs/universal/media_kit_libs_video + media_kit: ^1.1.10 # Primary package. + media_kit_video: ^1.2.4 # For video rendering. + media_kit_libs_video: ^1.0.4 # 媒体通知 audio_service: ^0.18.12 @@ -177,18 +165,15 @@ dependency_overrides: media_kit: git: url: https://github.com/media-kit/media-kit.git - ref: main - path: ./media_kit + path: media_kit media_kit_video: git: url: https://github.com/media-kit/media-kit.git - ref: main - path: ./media_kit_video + path: media_kit_video media_kit_libs_video: git: url: https://github.com/media-kit/media-kit.git - ref: main - path: ./libs/universal/media_kit_libs_video + path: libs/universal/media_kit_libs_video flutter_launcher_icons: android: true From 76784ee6647893098512226f39e41c57c22b2dc9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 19 Mar 2024 23:33:32 +0800 Subject: [PATCH 033/349] mod: seekTo --- lib/plugin/pl_player/controller.dart | 30 +++++++++++++--------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index aaded67d..465b3003 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -375,7 +375,13 @@ class PlPlayerController { } // 配置Player 音轨、字幕等等 _videoPlayerController = await _createVideoController( - dataSource, _looping, enableHA, width, height); + dataSource, + _looping, + enableHA, + width, + height, + seekTo, + ); // 获取视频时长 00:00 _duration.value = duration ?? _videoPlayerController!.state.duration; updateDurationSecond(); @@ -386,7 +392,7 @@ class PlPlayerController { if (!_listenersInitialized) { startListeners(); } - await _initializePlayer(seekTo: seekTo, duration: _duration.value); + await _initializePlayer(duration: _duration.value); bool autoEnterFullcreen = setting.get(SettingBoxKey.enableAutoEnter, defaultValue: false); if (autoEnterFullcreen && _isFirstTime) { @@ -406,6 +412,7 @@ class PlPlayerController { bool enableHA, double? width, double? height, + Duration? seekTo, ) async { // 每次配置时先移除监听 removeListeners(); @@ -488,7 +495,11 @@ class PlPlayerController { ); } player.open( - Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders), + Media( + dataSource.videoSource!, + httpHeaders: dataSource.httpHeaders, + start: seekTo ?? Duration.zero, + ), play: false, ); // 音轨 @@ -501,7 +512,6 @@ class PlPlayerController { // 开始播放 Future _initializePlayer({ - Duration seekTo = Duration.zero, Duration? duration, }) async { // 设置倍速 @@ -519,11 +529,6 @@ class PlPlayerController { // await setLooping(_looping); // } - // 跳转播放 - if (seekTo != Duration.zero) { - await this.seekTo(seekTo); - } - // 自动播放 if (_autoPlay) { await play(duration: duration); @@ -637,21 +642,14 @@ class PlPlayerController { await _videoPlayerController?.stream.buffer.first; } await _videoPlayerController?.seek(position); - // if (playerStatus.stopped) { - // play(); - // } } else { print('seek duration else'); _timerForSeek?.cancel(); _timerForSeek = Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { - //_timerForSeek = null; if (duration.value.inSeconds != 0) { await _videoPlayerController!.stream.buffer.first; await _videoPlayerController?.seek(position); - // if (playerStatus.status.value == PlayerStatus.paused) { - // play(); - // } t.cancel(); _timerForSeek = null; } From 79fcd017ab16e182cee8a52ebdf05f68b6370958 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 20 Mar 2024 00:07:00 +0800 Subject: [PATCH 034/349] =?UTF-8?q?mod:=20tabbar=E6=A0=B7=E5=BC=8F&?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=84=E8=AE=BA=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/controller.dart | 7 +- lib/pages/video/detail/view.dart | 124 ++++++++++-------- 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index c7f22b13..babc18aa 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -112,10 +112,9 @@ class VideoIntroController extends GetxController { if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) { lastPlayCid.value = videoDetail.value.pages!.first.cid!; } - // Get.find(tag: heroTag).tabs.value = [ - // '简介', - // '评论 ${result['data']!.stat!.reply}' - // ]; + final VideoDetailController videoDetailCtr = + Get.find(tag: heroTag); + videoDetailCtr.tabs.value = ['简介', '评论 ${result['data']?.stat?.reply}']; // 获取到粉丝数再返回 await queryUserStat(); } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 1dff9060..af901cd2 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -319,62 +319,78 @@ class _VideoDetailPageState extends State ), ), ), - child: Row( - children: [ - const SizedBox(width: 20), - Expanded( - child: TabBar( - controller: vdCtr.tabCtr, - dividerColor: Colors.transparent, - tabs: vdCtr.tabs.map((String name) => Tab(text: name)).toList(), - ), - ), - SizedBox( - width: 220, - child: Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SizedBox( - height: 32, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => vdCtr.showShootDanmakuSheet(), - child: const Text('发弹幕', style: TextStyle(fontSize: 12)), - ), - ), - const SizedBox(width: 4), - SizedBox( - width: 34, - height: 32, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - plPlayerController?.isOpenDanmu.value = - !(plPlayerController?.isOpenDanmu.value ?? false); - }, - child: Obx(() => Text( - '弹', - style: TextStyle( - fontSize: 12, - color: (plPlayerController?.isOpenDanmu.value ?? - false) - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ), - )), - ), - ), - const SizedBox(width: 14), - ], + child: Material( + child: Row( + children: [ + Flexible( + flex: 1, + child: Obx( + () => TabBar( + padding: EdgeInsets.zero, + controller: vdCtr.tabCtr, + labelStyle: const TextStyle(fontSize: 13), + labelPadding: + const EdgeInsets.symmetric(horizontal: 10.0), // 设置每个标签的宽度 + dividerColor: Colors.transparent, + tabs: vdCtr.tabs + .map( + (String name) => Flexible( + child: Tab(text: name), + ), + ) + .toList(), + ), ), ), - ), - ], + Flexible( + flex: 1, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => vdCtr.showShootDanmakuSheet(), + child: + const Text('发弹幕', style: TextStyle(fontSize: 12)), + ), + ), + const SizedBox(width: 4), + SizedBox( + width: 34, + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + plPlayerController?.isOpenDanmu.value = + !(plPlayerController?.isOpenDanmu.value ?? + false); + }, + child: Obx(() => Text( + '弹', + style: TextStyle( + fontSize: 12, + color: (plPlayerController + ?.isOpenDanmu.value ?? + false) + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + )), + ), + ), + const SizedBox(width: 14), + ], + ), + )), + ], + ), ), ); From 710361caeac2f3aa155fad8d8a2633890d01be10 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 20 Mar 2024 23:21:41 +0800 Subject: [PATCH 035/349] =?UTF-8?q?fix:=20=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E7=9B=B8=E5=85=B3=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/interceptor.dart | 12 ++++++++---- lib/pages/history/controller.dart | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart index 362ff17a..fbc60ddf 100644 --- a/lib/http/interceptor.dart +++ b/lib/http/interceptor.dart @@ -45,10 +45,14 @@ class ApiInterceptor extends Interceptor { void onError(DioException err, ErrorInterceptorHandler handler) async { // 处理网络请求错误 // handler.next(err); - SmartDialog.showToast( - await dioError(err), - displayType: SmartToastType.onlyRefresh, - ); + String url = err.requestOptions.uri.toString(); + print('🌹🌹ApiInterceptor: $url'); + if (!url.contains('heartBeat')) { + SmartDialog.showToast( + await dioError(err), + displayType: SmartToastType.onlyRefresh, + ); + } super.onError(err, handler); } diff --git a/lib/pages/history/controller.dart b/lib/pages/history/controller.dart index e7822cd9..a1f18113 100644 --- a/lib/pages/history/controller.dart +++ b/lib/pages/history/controller.dart @@ -88,8 +88,10 @@ class HistoryController extends GetxController { // 观看历史暂停状态 Future historyStatus() async { var res = await UserHttp.historyStatus(); - pauseStatus.value = res.data['data']; - localCache.put(LocalCacheKey.historyPause, res.data['data']); + if (res.data['code'] == 0) { + pauseStatus.value = res.data['data']; + localCache.put(LocalCacheKey.historyPause, res.data['data']); + } } // 清空观看历史 From 6d276fce4c01e6a24d7abcd8313a0b653916c777 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 21 Mar 2024 00:03:20 +0800 Subject: [PATCH 036/349] =?UTF-8?q?fix:=20=E5=AA=92=E4=BD=93=E5=BA=93?= =?UTF-8?q?=E6=97=A0=E6=B3=95=E6=BB=91=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/media/view.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index c3bad4b9..460c5648 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; +import 'package:media_kit/media_kit.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/pages/main/index.dart'; @@ -102,7 +103,11 @@ class _MediaPageState extends State ], Obx(() => mediaController.userLogin.value ? favFolder(mediaController, context) - : const SizedBox()) + : const SizedBox()), + SizedBox( + height: MediaQuery.of(context).padding.bottom + + kBottomNavigationBarHeight, + ) ], ), ), From 4dbcd2e0ec1bfd99d305651b295f8390778ae845 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 21 Mar 2024 23:39:56 +0800 Subject: [PATCH 037/349] =?UTF-8?q?fix:=20=E5=B0=9D=E8=AF=95=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E9=9F=B3=E9=87=8F=E8=B0=83=E8=8A=82=E6=8A=96=E5=8A=A8?= =?UTF-8?q?=20issues=20#647=20#498=20#104=20#198?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 4b6627b8..b0933af2 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; +import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:flutter_volume_controller/flutter_volume_controller.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; @@ -674,13 +675,16 @@ class _PLVideoPlayerState extends State _distance.value = dy; } else { // 右边区域 👈 - final double level = (_.isFullScreen.value - ? Get.size.height - : screenWidth * 9 / 16) * - 3; - final double volume = _volumeValue.value - delta / level; - final double result = volume.clamp(0.0, 1.0); - setVolume(result); + EasyThrottle.throttle( + 'setVolume', const Duration(milliseconds: 20), () { + final double level = (_.isFullScreen.value + ? Get.size.height + : screenWidth * 9 / 16); + final double volume = _volumeValue.value - + double.parse(delta.toStringAsFixed(1)) / level; + final double result = volume.clamp(0.0, 1.0); + setVolume(result); + }); } }, onVerticalDragEnd: (DragEndDetails details) {}, From c216c9bd653524bcd6a9cc613b4fd0bc96b35630 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 20 Mar 2024 00:07:00 +0800 Subject: [PATCH 038/349] =?UTF-8?q?mod:=20tabbar=E6=A0=B7=E5=BC=8F&?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=AF=84=E8=AE=BA=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/controller.dart | 7 +- lib/pages/video/detail/view.dart | 124 ++++++++++-------- 2 files changed, 73 insertions(+), 58 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 6714b887..8114bdaf 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -85,10 +85,9 @@ class VideoIntroController extends GetxController { if (videoDetail.value.pages!.isNotEmpty && lastPlayCid.value == 0) { lastPlayCid.value = videoDetail.value.pages!.first.cid!; } - // Get.find(tag: heroTag).tabs.value = [ - // '简介', - // '评论 ${result['data']!.stat!.reply}' - // ]; + final VideoDetailController videoDetailCtr = + Get.find(tag: heroTag); + videoDetailCtr.tabs.value = ['简介', '评论 ${result['data']?.stat?.reply}']; // 获取到粉丝数再返回 await queryUserStat(); } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 1dff9060..af901cd2 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -319,62 +319,78 @@ class _VideoDetailPageState extends State ), ), ), - child: Row( - children: [ - const SizedBox(width: 20), - Expanded( - child: TabBar( - controller: vdCtr.tabCtr, - dividerColor: Colors.transparent, - tabs: vdCtr.tabs.map((String name) => Tab(text: name)).toList(), - ), - ), - SizedBox( - width: 220, - child: Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - SizedBox( - height: 32, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => vdCtr.showShootDanmakuSheet(), - child: const Text('发弹幕', style: TextStyle(fontSize: 12)), - ), - ), - const SizedBox(width: 4), - SizedBox( - width: 34, - height: 32, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - plPlayerController?.isOpenDanmu.value = - !(plPlayerController?.isOpenDanmu.value ?? false); - }, - child: Obx(() => Text( - '弹', - style: TextStyle( - fontSize: 12, - color: (plPlayerController?.isOpenDanmu.value ?? - false) - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ), - )), - ), - ), - const SizedBox(width: 14), - ], + child: Material( + child: Row( + children: [ + Flexible( + flex: 1, + child: Obx( + () => TabBar( + padding: EdgeInsets.zero, + controller: vdCtr.tabCtr, + labelStyle: const TextStyle(fontSize: 13), + labelPadding: + const EdgeInsets.symmetric(horizontal: 10.0), // 设置每个标签的宽度 + dividerColor: Colors.transparent, + tabs: vdCtr.tabs + .map( + (String name) => Flexible( + child: Tab(text: name), + ), + ) + .toList(), + ), ), ), - ), - ], + Flexible( + flex: 1, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => vdCtr.showShootDanmakuSheet(), + child: + const Text('发弹幕', style: TextStyle(fontSize: 12)), + ), + ), + const SizedBox(width: 4), + SizedBox( + width: 34, + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + plPlayerController?.isOpenDanmu.value = + !(plPlayerController?.isOpenDanmu.value ?? + false); + }, + child: Obx(() => Text( + '弹', + style: TextStyle( + fontSize: 12, + color: (plPlayerController + ?.isOpenDanmu.value ?? + false) + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + )), + ), + ), + const SizedBox(width: 14), + ], + ), + )), + ], + ), ), ); From 14338dc33d508dbd06d8766310fbf197493c6332 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 23 Mar 2024 17:23:51 +0800 Subject: [PATCH 039/349] =?UTF-8?q?fix:=20=E8=AF=A6=E6=83=85=E9=A1=B5TabBa?= =?UTF-8?q?r=E5=B8=83=E5=B1=80=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/images/video/danmu_close.svg | 1 + assets/images/video/danmu_open.svg | 1 + lib/pages/video/detail/view.dart | 47 ++++++++++++++--------------- pubspec.yaml | 1 + 4 files changed, 25 insertions(+), 25 deletions(-) create mode 100644 assets/images/video/danmu_close.svg create mode 100644 assets/images/video/danmu_open.svg diff --git a/assets/images/video/danmu_close.svg b/assets/images/video/danmu_close.svg new file mode 100644 index 00000000..9f48027b --- /dev/null +++ b/assets/images/video/danmu_close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/images/video/danmu_open.svg b/assets/images/video/danmu_open.svg new file mode 100644 index 00000000..24e8d7a9 --- /dev/null +++ b/assets/images/video/danmu_open.svg @@ -0,0 +1 @@ +Layer 1 \ No newline at end of file diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index af901cd2..a403e298 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:flutter_svg/svg.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; @@ -334,9 +335,7 @@ class _VideoDetailPageState extends State dividerColor: Colors.transparent, tabs: vdCtr.tabs .map( - (String name) => Flexible( - child: Tab(text: name), - ), + (String name) => Tab(text: name), ) .toList(), ), @@ -359,30 +358,28 @@ class _VideoDetailPageState extends State const Text('发弹幕', style: TextStyle(fontSize: 12)), ), ), - const SizedBox(width: 4), SizedBox( - width: 34, - height: 32, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + width: 38, + height: 38, + child: Obx( + () => IconButton( + onPressed: () { + plPlayerController?.isOpenDanmu.value = + !(plPlayerController?.isOpenDanmu.value ?? + false); + }, + icon: (plPlayerController?.isOpenDanmu.value ?? + false) + ? SvgPicture.asset( + 'assets/images/video/danmu_close.svg', + ) + : SvgPicture.asset( + 'assets/images/video/danmu_open.svg', + // ignore: deprecated_member_use + color: + Theme.of(context).colorScheme.primary, + ), ), - onPressed: () { - plPlayerController?.isOpenDanmu.value = - !(plPlayerController?.isOpenDanmu.value ?? - false); - }, - child: Obx(() => Text( - '弹', - style: TextStyle( - fontSize: 12, - color: (plPlayerController - ?.isOpenDanmu.value ?? - false) - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ), - )), ), ), const SizedBox(width: 14), diff --git a/pubspec.yaml b/pubspec.yaml index 5c25f044..82166403 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -191,6 +191,7 @@ flutter: - assets/images/lv/ - assets/images/logo/ - assets/images/live/ + - assets/images/video/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware From b5a46d1be02ab94811d8c37bf7a14ab49ffb31d1 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 11 Mar 2024 23:03:50 +0800 Subject: [PATCH 040/349] =?UTF-8?q?feat:=20=E6=8E=A5=E6=94=B6=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E7=BB=84=E4=BB=B6=E4=BC=A0=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pl_player/models/bottom_control_type.dart | 1 + lib/plugin/pl_player/view.dart | 19 +++++++++++++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/plugin/pl_player/models/bottom_control_type.dart b/lib/plugin/pl_player/models/bottom_control_type.dart index 599f6e4f..739e1d38 100644 --- a/lib/plugin/pl_player/models/bottom_control_type.dart +++ b/lib/plugin/pl_player/models/bottom_control_type.dart @@ -7,4 +7,5 @@ enum BottomControlType { fit, speed, fullscreen, + custom, } diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 5f47ca4a..90861204 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -35,6 +35,8 @@ class PLVideoPlayer extends StatefulWidget { this.bottomControl, this.danmuWidget, this.bottomList, + this.customWidget, + this.customWidgets, super.key, }); @@ -43,6 +45,10 @@ class PLVideoPlayer extends StatefulWidget { final PreferredSizeWidget? bottomControl; final Widget? danmuWidget; final List? bottomList; + // List or Widget + + final Widget? customWidget; + final List? customWidgets; @override State createState() => _PLVideoPlayerState(); @@ -311,7 +317,7 @@ class _PLVideoPlayerState extends State ), }; final List list = []; - var userSpecifyItem = widget.bottomList ?? + List userSpecifyItem = widget.bottomList ?? [ BottomControlType.playOrPause, BottomControlType.time, @@ -320,7 +326,16 @@ class _PLVideoPlayerState extends State BottomControlType.fullscreen, ]; for (var i = 0; i < userSpecifyItem.length; i++) { - list.add(videoProgressWidgets[userSpecifyItem[i]]!); + if (userSpecifyItem[i] == BottomControlType.custom) { + if (widget.customWidget != null && widget.customWidget is Widget) { + list.add(widget.customWidget!); + } + if (widget.customWidgets != null && widget.customWidgets!.isNotEmpty) { + list.addAll(widget.customWidgets!); + } + } else { + list.add(videoProgressWidgets[userSpecifyItem[i]]!); + } } return list; } From 72f1a82650b30053f807da017c6f44fa36d4ff48 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 23 Mar 2024 19:44:37 +0800 Subject: [PATCH 041/349] bump: sdk&dependencies --- lib/http/interceptor.dart | 34 ++++---- macos/Flutter/GeneratedPluginRegistrant.swift | 2 +- pubspec.lock | 84 +++++++++---------- pubspec.yaml | 26 +++--- 4 files changed, 73 insertions(+), 73 deletions(-) diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart index fbc60ddf..a5359283 100644 --- a/lib/http/interceptor.dart +++ b/lib/http/interceptor.dart @@ -46,7 +46,6 @@ class ApiInterceptor extends Interceptor { // 处理网络请求错误 // handler.next(err); String url = err.requestOptions.uri.toString(); - print('🌹🌹ApiInterceptor: $url'); if (!url.contains('heartBeat')) { SmartDialog.showToast( await dioError(err), @@ -79,23 +78,24 @@ class ApiInterceptor extends Interceptor { } static Future checkConnect() async { - final ConnectivityResult connectivityResult = + final List connectivityResult = await Connectivity().checkConnectivity(); - switch (connectivityResult) { - case ConnectivityResult.mobile: - return '正在使用移动流量'; - case ConnectivityResult.wifi: - return '正在使用wifi'; - case ConnectivityResult.ethernet: - return '正在使用局域网'; - case ConnectivityResult.vpn: - return '正在使用代理网络'; - case ConnectivityResult.other: - return '正在使用其他网络'; - case ConnectivityResult.none: - return '未连接到任何网络'; - default: - return ''; + if (connectivityResult.contains(ConnectivityResult.mobile)) { + return '正在使用移动流量'; + } else if (connectivityResult.contains(ConnectivityResult.wifi)) { + return '正在使用wifi'; + } else if (connectivityResult.contains(ConnectivityResult.ethernet)) { + return '正在使用局域网'; + } else if (connectivityResult.contains(ConnectivityResult.vpn)) { + return '正在使用代理网络'; + } else if (connectivityResult.contains(ConnectivityResult.bluetooth)) { + return '正在使用蓝牙网络'; + } else if (connectivityResult.contains(ConnectivityResult.other)) { + return '正在使用其他网络'; + } else if (connectivityResult.contains(ConnectivityResult.none)) { + return '未连接到任何网络'; + } else { + return ''; } } } diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 3e5f82f7..8af2f922 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -24,7 +24,7 @@ import wakelock_plus func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { AudioServicePlugin.register(with: registry.registrar(forPlugin: "AudioServicePlugin")) AudioSessionPlugin.register(with: registry.registrar(forPlugin: "AudioSessionPlugin")) - ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin")) + ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: "DeviceInfoPlusMacosPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) FlutterVolumeControllerPlugin.register(with: registry.registrar(forPlugin: "FlutterVolumeControllerPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 8972ef45..695505d7 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: "direct main" description: name: animations - sha256: "708e4b68c23228c264b038fe7003a2f5d01ce85fc64d8cae090e86b27fcea6c5" + sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.10" + version: "2.0.11" appscheme: dependency: "direct main" description: @@ -101,10 +101,10 @@ packages: dependency: "direct main" description: name: audio_video_progress_bar - sha256: "3384875247cdbea748bd9ae8330631cd06a6cabfcda4945d45c9b406da92bc66" + sha256: ccc7d7b83d2a16c52d4a7fb332faabd1baa053fb0e4c16815aefd3945ab33b81 url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.1" + version: "2.0.2" auto_orientation: dependency: "direct main" description: @@ -157,10 +157,10 @@ packages: dependency: "direct dev" description: name: build_runner - sha256: "67d591d602906ef9201caf93452495ad1812bea2074f04e25dbd7c133785821b" + sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.7" + version: "2.4.8" build_runner_core: dependency: transitive description: @@ -213,10 +213,10 @@ packages: dependency: "direct main" description: name: catcher_2 - sha256: ca94d45ffb52bf4b16a425cdff6734ae8443d36d5f06c276f1c2a593120b11ed + sha256: "9cf33d2befd10058374e5fc6177577fdd938d73d9c06810de81cf91311a7ce98" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.2.3" characters: dependency: transitive description: @@ -269,18 +269,18 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: "77a180d6938f78ca7d2382d2240eb626c0f6a735d0bfdce227d8ffb80f95c48b" + sha256: e9feae83b1849f61bad9f6f33ee00646e3410d54ce0821e02f262f9901dad3c9 url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.2" + version: "6.0.1" connectivity_plus_platform_interface: dependency: transitive description: name: connectivity_plus_platform_interface - sha256: cf1d1c28f4416f8c654d7dc3cd638ec586076255d407cef3ddbdaf178272a71a + sha256: b6a56efe1e6675be240de39107281d4034b64ac23438026355b4234042a35adb url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.4" + version: "2.0.0" convert: dependency: transitive description: @@ -373,10 +373,10 @@ packages: dependency: "direct main" description: name: dio - sha256: "797e1e341c3dd2f69f2dad42564a6feff3bfb87187d05abb93b9609e6f1645c3" + sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8" url: "https://pub.flutter-io.cn" source: hosted - version: "5.4.0" + version: "5.4.1" dio_cookie_manager: dependency: "direct main" description: @@ -413,10 +413,10 @@ packages: dependency: "direct main" description: name: dynamic_color - sha256: "8b8bd1d798bd393e11eddeaa8ae95b12ff028bf7d5998fc5d003488cd5f4ce2f" + sha256: eae98052fa6e2826bdac3dd2e921c6ce2903be15c6b7f8b6d8a5d49b5086298d url: "https://pub.flutter-io.cn" source: hosted - version: "1.6.8" + version: "1.7.0" easy_debounce: dependency: "direct main" description: @@ -469,10 +469,10 @@ packages: dependency: "direct main" description: name: extended_nested_scroll_view - sha256: "444a6f883e6e07effc7639e69a309e1fb491b6c19b095e9281714a51ace2b384" + sha256: "835580d40c2c62b448bd14adecd316acba469ba61f1510ef559d17668a85e777" url: "https://pub.flutter-io.cn" source: hosted - version: "6.1.2" + version: "6.2.1" fake_async: dependency: transitive description: @@ -592,10 +592,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: d39e7f95621fc84376bc0f7d504f05c3a41488c562f4a8ad410569127507402c + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.9" + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter @@ -1067,50 +1067,50 @@ packages: dependency: "direct main" description: name: permission_handler - sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78" + sha256: "74e962b7fad7ff75959161bb2c0ad8fe7f2568ee82621c9c2660b751146bfe44" url: "https://pub.flutter-io.cn" source: hosted - version: "11.1.0" + version: "11.3.0" permission_handler_android: dependency: transitive description: name: permission_handler_android - sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6" + sha256: "1acac6bae58144b442f11e66621c062aead9c99841093c38f5bcdcc24c1c3474" url: "https://pub.flutter-io.cn" source: hosted - version: "12.0.1" + version: "12.0.5" permission_handler_apple: dependency: transitive description: name: permission_handler_apple - sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306" + sha256: "92861b0f0c2443dd8898398c2baa4f1ae925109b5909ae4a17d0108a6a788932" url: "https://pub.flutter-io.cn" source: hosted - version: "9.2.0" + version: "9.4.2" permission_handler_html: dependency: transitive description: name: permission_handler_html - sha256: "11b762a8c123dced6461933a88ea1edbbe036078c3f9f41b08886e678e7864df" + sha256: "54bf176b90f6eddd4ece307e2c06cf977fb3973719c35a93b85cc7093eb6070d" url: "https://pub.flutter-io.cn" source: hosted - version: "0.1.0+2" + version: "0.1.1" permission_handler_platform_interface: dependency: transitive description: name: permission_handler_platform_interface - sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1 + sha256: "23dfba8447c076ab5be3dee9ceb66aad345c4a648f0cac292c77b1eb0e800b78" url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.2" + version: "4.2.0" permission_handler_windows: dependency: transitive description: name: permission_handler_windows - sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004" + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" url: "https://pub.flutter-io.cn" source: hosted - version: "0.2.0" + version: "0.2.1" petitparser: dependency: transitive description: @@ -1536,26 +1536,26 @@ packages: dependency: transitive description: name: vector_graphics - sha256: "0f0c746dd2d6254a0057218ff980fc7f5670fd0fcf5e4db38a490d31eed4ad43" + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.9+1" + version: "1.1.11+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: "0edf6d630d1bfd5589114138ed8fada3234deacc37966bec033d3047c29248b7" + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.9+1" + version: "1.1.11+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: d24333727332d9bd20990f1483af4e09abdb9b1fc7c3db940b56ab5c42790c26 + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.9+1" + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -1640,10 +1640,10 @@ packages: dependency: "direct main" description: name: webview_flutter - sha256: "42393b4492e629aa3a88618530a4a00de8bb46e50e7b3993fedbfdc5352f0dbf" + sha256: d81b68e88cc353e546afb93fb38958e3717282c5ac6e5d3be4a4aef9fc3c1413 url: "https://pub.flutter-io.cn" source: hosted - version: "4.4.2" + version: "4.5.0" webview_flutter_android: dependency: transitive description: @@ -1664,10 +1664,10 @@ packages: dependency: transitive description: name: webview_flutter_wkwebview - sha256: accdaaa49a2aca2dc3c3230907988954cdd23fed0a19525d6c9789d380f4dc76 + sha256: "4d062ad505390ecef1c4bfb6001cd857a51e00912cc9dfb66edb1886a9ebd80c" url: "https://pub.flutter-io.cn" source: hosted - version: "3.9.4" + version: "3.10.2" win32: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 82166403..5e19b56b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.21+1021 environment: - sdk: ">=2.19.6 <3.0.0" + sdk: ">=3.0.0 <4.0.0" # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -36,15 +36,15 @@ dependencies: # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.5 # 动态取色 - dynamic_color: ^1.6.8 + dynamic_color: ^1.7.0 get: ^4.6.5 # 网络 - dio: ^5.3.3 + dio: ^5.4.1 cookie_jar: ^4.0.8 dio_cookie_manager: ^3.1.1 - connectivity_plus: ^4.0.1 + connectivity_plus: ^6.0.1 dio_http2_adapter: ^2.3.1+1 # 图片 @@ -58,17 +58,17 @@ dependencies: hive_flutter: ^1.1.0 # 设备信息 - device_info_plus: ^9.0.2 + device_info_plus: ^9.0.0 # 权限 - permission_handler: ^11.0.1 + permission_handler: ^11.3.0 # 分享 share_plus: ^7.0.2 # cookie 管理 webview_cookie_manager: ^2.0.6 # 浏览器 - webview_flutter: ^4.2.2 + webview_flutter: ^4.5.0 # 解决sliver滑动不同步 - extended_nested_scroll_view: ^6.1.2 + extended_nested_scroll_view: ^6.2.1 # 上拉加载 loading_more_list: ^6.0.0 # 下拉刷新 @@ -99,15 +99,15 @@ dependencies: wakelock_plus: ^1.1.1 universal_platform: ^1.0.0+1 # 进度条 - audio_video_progress_bar: ^2.0.1 + audio_video_progress_bar: ^2.0.2 auto_orientation: ^2.3.1 protobuf: ^3.0.0 - animations: ^2.0.8 + animations: ^2.0.11 # 获取appx信息 package_info_plus: ^4.1.0 url_launcher: ^6.1.14 - flutter_svg: ^2.0.7 + flutter_svg: ^2.0.10+1 # 防抖节流 easy_debounce: ^2.0.3 # 高帧率 @@ -137,7 +137,7 @@ dependencies: uuid: ^3.0.7 scrollable_positioned_list: ^0.3.8 nil: ^1.1.1 - catcher_2: ^1.1.0 + catcher_2: ^1.2.3 logger: ^2.0.2+1 path: 1.8.3 # 电池优化 @@ -159,7 +159,7 @@ dev_dependencies: # url: https://github.com/nvi9/flutter_launcher_icons.git # ref: e045d40 hive_generator: ^2.0.0 - build_runner: ^2.3.3 + build_runner: ^2.4.8 flutter_launcher_icons: android: true From 11e907d74bc1bbcb68dea7579fe0242dea035791 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 10:48:45 +0800 Subject: [PATCH 042/349] =?UTF-8?q?mod:=20=E5=BC=B9=E5=B9=95=E5=BC=80?= =?UTF-8?q?=E5=85=B3=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a403e298..69801beb 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -368,7 +368,7 @@ class _VideoDetailPageState extends State !(plPlayerController?.isOpenDanmu.value ?? false); }, - icon: (plPlayerController?.isOpenDanmu.value ?? + icon: !(plPlayerController?.isOpenDanmu.value ?? false) ? SvgPicture.asset( 'assets/images/video/danmu_close.svg', From 8ff387d54a2b7e31fb1150a7030b61c9f97a3ecd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 11:30:50 +0800 Subject: [PATCH 043/349] =?UTF-8?q?mod:=20=E8=AF=84=E8=AE=BA=E5=A4=B4?= =?UTF-8?q?=E9=83=A8=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/view.dart | 45 ++++++++------------------ 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index b07a6168..38203f7e 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -148,35 +148,14 @@ class _VideoReplyPanelState extends State floating: true, delegate: _MySliverPersistentHeaderDelegate( child: Container( - height: 45, - padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - border: Border( - bottom: BorderSide( - color: Theme.of(context) - .colorScheme - .outline - .withOpacity(0.1)), - ), - ), + height: 40, + padding: const EdgeInsets.fromLTRB(12, 6, 6, 0), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Obx( - () => AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - '共${_videoReplyController.count.value}条回复', - key: ValueKey( - _videoReplyController.count.value), - ), - ), + Text( + '${_videoReplyController.sortTypeLabel.value}评论', + style: const TextStyle(fontSize: 13), ), SizedBox( height: 35, @@ -184,10 +163,12 @@ class _VideoReplyPanelState extends State onPressed: () => _videoReplyController.queryBySort(), icon: const Icon(Icons.sort, size: 16), - label: Obx(() => Text( - _videoReplyController.sortTypeLabel.value, - style: const TextStyle(fontSize: 13), - )), + label: Obx( + () => Text( + _videoReplyController.sortTypeLabel.value, + style: const TextStyle(fontSize: 13), + ), + ), ), ) ], @@ -329,8 +310,8 @@ class _VideoReplyPanelState extends State class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { _MySliverPersistentHeaderDelegate({required this.child}); - final double _minExtent = 45; - final double _maxExtent = 45; + final double _minExtent = 40; + final double _maxExtent = 40; final Widget child; @override From c9327c97e5ae7f044426bbb3847c988f71046558 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 10:59:00 +0800 Subject: [PATCH 044/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E6=8A=95?= =?UTF-8?q?=E7=A5=A8message=E9=87=8D=E5=A4=8D?= 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 f9f695d4..e79b6159 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -498,7 +498,7 @@ InlineSpan buildContent( return str; }); } - // content.message = content.message.replaceAll(RegExp(r"\{vote:.*?\}"), ' '); + content.message = content.message.replaceAll(RegExp(r"\{vote:.*?\}"), ' '); content.message = content.message .replaceAll('&', '&') .replaceAll('<', '<') From 4db5a950f344af1aa5aee4b309cd626a2de2fbfc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 10:48:45 +0800 Subject: [PATCH 045/349] =?UTF-8?q?mod:=20=E5=BC=B9=E5=B9=95=E5=BC=80?= =?UTF-8?q?=E5=85=B3=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a403e298..69801beb 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -368,7 +368,7 @@ class _VideoDetailPageState extends State !(plPlayerController?.isOpenDanmu.value ?? false); }, - icon: (plPlayerController?.isOpenDanmu.value ?? + icon: !(plPlayerController?.isOpenDanmu.value ?? false) ? SvgPicture.asset( 'assets/images/video/danmu_close.svg', From 53b72bec2543218e638bb62447ec9502c1311765 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 13:16:13 +0800 Subject: [PATCH 046/349] =?UTF-8?q?mod:=20read=20schame=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 13 +++++++------ lib/common/widgets/network_img_layer.dart | 2 +- lib/utils/app_scheme.dart | 18 ++++++++++++++++-- 3 files changed, 24 insertions(+), 9 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8db59815..2c1a635b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,6 +9,7 @@ PODS: - Flutter - connectivity_plus (0.0.1): - Flutter + - FlutterMacOS - ReachabilitySwift - device_info_plus (0.0.1): - Flutter @@ -38,7 +39,7 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - permission_handler_apple (9.1.1): + - permission_handler_apple (9.3.0): - Flutter - ReachabilitySwift (5.0.0) - saver_gallery (0.0.1): @@ -71,7 +72,7 @@ DEPENDENCIES: - audio_service (from `.symlinks/plugins/audio_service/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`) - auto_orientation (from `.symlinks/plugins/auto_orientation/ios`) - - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`) @@ -113,7 +114,7 @@ EXTERNAL SOURCES: auto_orientation: :path: ".symlinks/plugins/auto_orientation/ios" connectivity_plus: - :path: ".symlinks/plugins/connectivity_plus/ios" + :path: ".symlinks/plugins/connectivity_plus/darwin" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: @@ -166,7 +167,7 @@ SPEC CHECKSUMS: audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_session: 4f3e461722055d21515cf3261b64c973c062f345 auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d - connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a + connectivity_plus: e2dad488011aeb593e219360e804c43cc1af5770 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 @@ -180,7 +181,7 @@ SPEC CHECKSUMS: media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 - permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 @@ -193,7 +194,7 @@ SPEC CHECKSUMS: volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7 - webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a + webview_flutter_wkwebview: 4f3e50f7273d31e5500066ed267e3ae4309c5ae4 PODFILE CHECKSUM: 637cd290bed23275b5f5ffcc7eb1e73d0a5fb2be diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 06c35974..173db853 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -36,7 +36,7 @@ class NetworkImgLayer extends StatelessWidget { final int defaultImgQuality = GlobalData().imgQuality; final String imageUrl = '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp'; - print(imageUrl); + // print(imageUrl); int? memCacheWidth, memCacheHeight; double aspectRatio = (width / height).toDouble(); diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index bb9d556f..ffe90e35 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -20,7 +20,7 @@ class PiliSchame { /// 完整链接进入 b23.无效 appScheme.getLatestScheme().then((SchemeEntity? value) { if (value != null) { - _fullPathPush(value); + _routePush(value); } }); @@ -37,7 +37,6 @@ class PiliSchame { final String scheme = value.scheme; final String host = value.host; final String path = value.path; - if (scheme == 'bilibili') { if (host == 'root') { Navigator.popUntil( @@ -85,6 +84,14 @@ class PiliSchame { } } else if (host == 'search') { Get.toNamed('/searchResult', parameters: {'keyword': ''}); + } else if (host == 'article') { + final String id = path.split('/').last.split('?').first; + Get.toNamed('/htmlRender', parameters: { + 'url': 'https://www.bilibili.com/read/cv$id', + 'title': 'cv$id', + 'id': 'cv$id', + 'dynamicType': 'read' + }); } } if (scheme == 'https') { @@ -226,6 +233,13 @@ class PiliSchame { break; case 'read': print('专栏'); + String id = 'cv${matchNum(query!['id']!).first}'; + Get.toNamed('/htmlRender', parameters: { + 'url': value.dataString!, + 'title': '', + 'id': id, + 'dynamicType': 'read' + }); break; case 'space': print('个人空间'); From 77b509fd1703b464fd557b1ca87983f19c62c324 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 13:42:40 +0800 Subject: [PATCH 047/349] =?UTF-8?q?opt:=20=E6=8E=92=E8=A1=8C=E6=A6=9C?= =?UTF-8?q?=E5=88=87=E6=8D=A2tab=E6=95=B0=E6=8D=AE=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/rank/controller.dart | 2 +- lib/pages/rank/zone/view.dart | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index 61475d97..da73ea02 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -9,7 +9,7 @@ import 'package:pilipala/utils/storage.dart'; class RankController extends GetxController with GetTickerProviderStateMixin { bool flag = false; late RxList tabs = [].obs; - RxInt initialIndex = 1.obs; + RxInt initialIndex = 0.obs; late TabController tabController; late List tabsCtrList; late List tabsPageList; diff --git a/lib/pages/rank/zone/view.dart b/lib/pages/rank/zone/view.dart index 58ca187f..fbf8a524 100644 --- a/lib/pages/rank/zone/view.dart +++ b/lib/pages/rank/zone/view.dart @@ -22,15 +22,20 @@ class ZonePage extends StatefulWidget { State createState() => _ZonePageState(); } -class _ZonePageState extends State { - final ZoneController _zoneController = Get.put(ZoneController()); +class _ZonePageState extends State + with AutomaticKeepAliveClientMixin { + late ZoneController _zoneController; List videoList = []; Future? _futureBuilderFuture; late ScrollController scrollController; + @override + bool get wantKeepAlive => true; + @override void initState() { super.initState(); + _zoneController = Get.put(ZoneController(), tag: widget.rid.toString()); _futureBuilderFuture = _zoneController.queryRankFeed('init', widget.rid); scrollController = _zoneController.scrollController; StreamController mainStream = @@ -68,6 +73,7 @@ class _ZonePageState extends State { @override Widget build(BuildContext context) { + super.build(context); return RefreshIndicator( onRefresh: () async { return await _zoneController.onRefresh(); From 031d57e1fdf34e64a6d2f989a500d17ab63e43e2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 13:42:40 +0800 Subject: [PATCH 048/349] =?UTF-8?q?opt:=20=E6=8E=92=E8=A1=8C=E6=A6=9C?= =?UTF-8?q?=E5=88=87=E6=8D=A2tab=E6=95=B0=E6=8D=AE=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/rank/controller.dart | 2 +- lib/pages/rank/zone/view.dart | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index 61475d97..da73ea02 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -9,7 +9,7 @@ import 'package:pilipala/utils/storage.dart'; class RankController extends GetxController with GetTickerProviderStateMixin { bool flag = false; late RxList tabs = [].obs; - RxInt initialIndex = 1.obs; + RxInt initialIndex = 0.obs; late TabController tabController; late List tabsCtrList; late List tabsPageList; diff --git a/lib/pages/rank/zone/view.dart b/lib/pages/rank/zone/view.dart index 58ca187f..fbf8a524 100644 --- a/lib/pages/rank/zone/view.dart +++ b/lib/pages/rank/zone/view.dart @@ -22,15 +22,20 @@ class ZonePage extends StatefulWidget { State createState() => _ZonePageState(); } -class _ZonePageState extends State { - final ZoneController _zoneController = Get.put(ZoneController()); +class _ZonePageState extends State + with AutomaticKeepAliveClientMixin { + late ZoneController _zoneController; List videoList = []; Future? _futureBuilderFuture; late ScrollController scrollController; + @override + bool get wantKeepAlive => true; + @override void initState() { super.initState(); + _zoneController = Get.put(ZoneController(), tag: widget.rid.toString()); _futureBuilderFuture = _zoneController.queryRankFeed('init', widget.rid); scrollController = _zoneController.scrollController; StreamController mainStream = @@ -68,6 +73,7 @@ class _ZonePageState extends State { @override Widget build(BuildContext context) { + super.build(context); return RefreshIndicator( onRefresh: () async { return await _zoneController.onRefresh(); From 02d2598d0192e32a4de297d952053956f214f7f3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 16:03:18 +0800 Subject: [PATCH 049/349] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E7=AE=80=E4=BB=8B=E6=9F=A5=E7=9C=8B&?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 33 ++--- .../introduction/widgets/intro_detail.dart | 130 ++++-------------- .../detail/widgets/expandable_section.dart | 20 +-- 3 files changed, 50 insertions(+), 133 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9c1b7db0..831491f6 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -15,6 +15,7 @@ import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; +import '../widgets/expandable_section.dart'; import 'widgets/action_item.dart'; import 'widgets/fav_panel.dart'; import 'widgets/intro_detail.dart'; @@ -137,6 +138,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late String memberHeroTag; late bool enableAi; bool isProcessing = false; + RxBool isExpand = false.obs; void Function()? handleState(Future Function() action) { return isProcessing ? null @@ -212,13 +214,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { // 视频介绍 showIntroDetail() { feedBack(); - showBottomSheet( - context: context, - enableDrag: true, - builder: (BuildContext context) { - return IntroDetail(videoDetail: widget.videoDetail!); - }, - ); + isExpand.value = !(isExpand.value); } // 用户主页 @@ -330,6 +326,16 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ], ), + /// 视频简介 + Obx( + () => ExpandedSection( + expand: isExpand.value, + begin: 0, + end: 1, + child: IntroDetail(videoDetail: widget.videoDetail!), + ), + ), + /// 点赞收藏转发 actionGrid(context, videoIntroController), // 合集 @@ -438,6 +444,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { margin: const EdgeInsets.only(top: 6, bottom: 4), height: constraints.maxWidth / 5 * 0.8, child: GridView.count( + physics: const NeverScrollableScrollPhysics(), primary: false, padding: EdgeInsets.zero, crossAxisCount: 5, @@ -451,12 +458,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { selectStatus: videoIntroController.hasLike.value, text: widget.videoDetail!.stat!.like!.toString()), ), - // ActionItem( - // icon: const Icon(FontAwesomeIcons.clock), - // onTap: () => videoIntroController.actionShareVideo(), - // selectStatus: false, - // loadingStatus: loadingStatus, - // text: '稍后再看'), Obx( () => ActionItem( icon: const Icon(FontAwesomeIcons.b), @@ -477,10 +478,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ActionItem( - icon: const Icon(FontAwesomeIcons.comment), - onTap: () => videoDetailCtr.tabCtr.animateTo(1), + icon: const Icon(FontAwesomeIcons.clock), + onTap: () => videoIntroController.actionShareVideo(), selectStatus: false, - text: widget.videoDetail!.stat!.reply!.toString(), + text: '稍后看', ), ActionItem( icon: const Icon(FontAwesomeIcons.shareFromSquare), diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index c74e27ee..1e9bb842 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -1,16 +1,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; -import 'package:pilipala/common/widgets/stat/danmu.dart'; -import 'package:pilipala/common/widgets/stat/view.dart'; -import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; -Box localCache = GStrorage.localCache; -late double sheetHeight; - class IntroDetail extends StatelessWidget { const IntroDetail({ super.key, @@ -20,105 +14,39 @@ class IntroDetail extends StatelessWidget { @override Widget build(BuildContext context) { - sheetHeight = localCache.get('sheetHeight'); - return Container( - color: Theme.of(context).colorScheme.background, - padding: EdgeInsets.only( - left: 14, - right: 14, - bottom: MediaQuery.of(context).padding.bottom + 20), - height: sheetHeight, + return SizedBox( + width: double.infinity, + child: SelectableRegion( + focusNode: FocusNode(), + selectionControls: MaterialTextSelectionControls(), child: Column( - children: [ - InkWell( - onTap: () => Get.back(), - child: Container( - height: 35, - padding: const EdgeInsets.only(bottom: 2), - child: Center( - child: Container( - width: 32, - height: 3, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: - const BorderRadius.all(Radius.circular(3))), - ), - ), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 4), + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.bvid!, + style: TextStyle( + fontSize: 13, color: Theme.of(context).colorScheme.primary), ), ), - Expanded( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - videoDetail!.title, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 6), - Row( - children: [ - StatView( - theme: 'gray', - view: videoDetail!.stat!.view, - size: 'medium', - ), - const SizedBox(width: 10), - StatDanMu( - theme: 'gray', - danmu: videoDetail!.stat!.danmaku, - size: 'medium', - ), - const SizedBox(width: 10), - Text( - Utils.dateFormat(videoDetail!.pubdate, - formatType: 'detail'), - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.outline, - ), - ), - ], - ), - const SizedBox(height: 20), - SizedBox( - width: double.infinity, - child: SelectableRegion( - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - videoDetail!.bvid!, - style: const TextStyle(fontSize: 13), - ), - const SizedBox(height: 4), - Text.rich( - style: const TextStyle( - height: 1.4, - // fontSize: 13, - ), - TextSpan( - children: [ - buildContent(context, videoDetail!), - ], - ), - ), - ], - ), - ), - ), - ], - ), + const SizedBox(height: 4), + Text.rich( + style: const TextStyle(height: 1.4), + TextSpan( + children: [ + buildContent(context, videoDetail!), + ], ), - ) + ), ], - )); + ), + ), + ); } InlineSpan buildContent(BuildContext context, content) { diff --git a/lib/pages/video/detail/widgets/expandable_section.dart b/lib/pages/video/detail/widgets/expandable_section.dart index afa68cc9..69e73e20 100644 --- a/lib/pages/video/detail/widgets/expandable_section.dart +++ b/lib/pages/video/detail/widgets/expandable_section.dart @@ -32,28 +32,14 @@ class _ExpandedSectionState extends State _runExpandCheck(); } - ///Setting up the animation - // void prepareAnimations() { - // expandController = AnimationController( - // vsync: this, duration: const Duration(milliseconds: 500)); - // animation = CurvedAnimation( - // parent: expandController, - // curve: Curves.fastOutSlowIn, - // ); - // } - void prepareAnimations() { expandController = AnimationController( vsync: this, duration: const Duration(milliseconds: 400)); Animation curve = CurvedAnimation( parent: expandController, - curve: Curves.fastOutSlowIn, + curve: Curves.linear, ); animation = Tween(begin: widget.begin, end: widget.end).animate(curve); - // animation = CurvedAnimation( - // parent: expandController, - // curve: Curves.fastOutSlowIn, - // ); } void _runExpandCheck() { @@ -67,7 +53,9 @@ class _ExpandedSectionState extends State @override void didUpdateWidget(ExpandedSection oldWidget) { super.didUpdateWidget(oldWidget); - _runExpandCheck(); + if (widget.expand != oldWidget.expand) { + _runExpandCheck(); + } } @override From f8326e7cb5aea074f772836acc42894df94a6d41 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 16:17:02 +0800 Subject: [PATCH 050/349] =?UTF-8?q?fix:=20appbar=E6=BB=91=E5=8A=A8?= =?UTF-8?q?=E8=B7=9D=E7=A6=BB=E7=A7=BB=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/widgets/app_bar.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/widgets/app_bar.dart b/lib/pages/video/detail/widgets/app_bar.dart index 17f4bec7..efc0b593 100644 --- a/lib/pages/video/detail/widgets/app_bar.dart +++ b/lib/pages/video/detail/widgets/app_bar.dart @@ -17,12 +17,16 @@ class ScrollAppBar extends StatelessWidget { Widget build(BuildContext context) { final double statusBarHeight = MediaQuery.of(context).padding.top; final videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; + double scrollDistance = scrollVal; + if (scrollVal > videoHeight - kToolbarHeight) { + scrollDistance = videoHeight - kToolbarHeight; + } return Positioned( - top: -videoHeight + scrollVal + kToolbarHeight + 0.5, + top: -videoHeight + scrollDistance + kToolbarHeight + 0.5, left: 0, right: 0, child: Opacity( - opacity: scrollVal / (videoHeight - kToolbarHeight), + opacity: scrollDistance / (videoHeight - kToolbarHeight), child: Container( height: statusBarHeight + kToolbarHeight, color: Theme.of(context).colorScheme.background, From a925ef63eb09ebaae30f5558f37999b00d443df3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 16:03:18 +0800 Subject: [PATCH 051/349] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E7=AE=80=E4=BB=8B=E6=9F=A5=E7=9C=8B&?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 33 ++--- .../introduction/widgets/intro_detail.dart | 130 ++++-------------- .../detail/widgets/expandable_section.dart | 20 +-- 3 files changed, 50 insertions(+), 133 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9c1b7db0..831491f6 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -15,6 +15,7 @@ import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; +import '../widgets/expandable_section.dart'; import 'widgets/action_item.dart'; import 'widgets/fav_panel.dart'; import 'widgets/intro_detail.dart'; @@ -137,6 +138,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late String memberHeroTag; late bool enableAi; bool isProcessing = false; + RxBool isExpand = false.obs; void Function()? handleState(Future Function() action) { return isProcessing ? null @@ -212,13 +214,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { // 视频介绍 showIntroDetail() { feedBack(); - showBottomSheet( - context: context, - enableDrag: true, - builder: (BuildContext context) { - return IntroDetail(videoDetail: widget.videoDetail!); - }, - ); + isExpand.value = !(isExpand.value); } // 用户主页 @@ -330,6 +326,16 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ], ), + /// 视频简介 + Obx( + () => ExpandedSection( + expand: isExpand.value, + begin: 0, + end: 1, + child: IntroDetail(videoDetail: widget.videoDetail!), + ), + ), + /// 点赞收藏转发 actionGrid(context, videoIntroController), // 合集 @@ -438,6 +444,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { margin: const EdgeInsets.only(top: 6, bottom: 4), height: constraints.maxWidth / 5 * 0.8, child: GridView.count( + physics: const NeverScrollableScrollPhysics(), primary: false, padding: EdgeInsets.zero, crossAxisCount: 5, @@ -451,12 +458,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { selectStatus: videoIntroController.hasLike.value, text: widget.videoDetail!.stat!.like!.toString()), ), - // ActionItem( - // icon: const Icon(FontAwesomeIcons.clock), - // onTap: () => videoIntroController.actionShareVideo(), - // selectStatus: false, - // loadingStatus: loadingStatus, - // text: '稍后再看'), Obx( () => ActionItem( icon: const Icon(FontAwesomeIcons.b), @@ -477,10 +478,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ActionItem( - icon: const Icon(FontAwesomeIcons.comment), - onTap: () => videoDetailCtr.tabCtr.animateTo(1), + icon: const Icon(FontAwesomeIcons.clock), + onTap: () => videoIntroController.actionShareVideo(), selectStatus: false, - text: widget.videoDetail!.stat!.reply!.toString(), + text: '稍后看', ), ActionItem( icon: const Icon(FontAwesomeIcons.shareFromSquare), diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index c74e27ee..1e9bb842 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -1,16 +1,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; -import 'package:pilipala/common/widgets/stat/danmu.dart'; -import 'package:pilipala/common/widgets/stat/view.dart'; -import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; -Box localCache = GStrorage.localCache; -late double sheetHeight; - class IntroDetail extends StatelessWidget { const IntroDetail({ super.key, @@ -20,105 +14,39 @@ class IntroDetail extends StatelessWidget { @override Widget build(BuildContext context) { - sheetHeight = localCache.get('sheetHeight'); - return Container( - color: Theme.of(context).colorScheme.background, - padding: EdgeInsets.only( - left: 14, - right: 14, - bottom: MediaQuery.of(context).padding.bottom + 20), - height: sheetHeight, + return SizedBox( + width: double.infinity, + child: SelectableRegion( + focusNode: FocusNode(), + selectionControls: MaterialTextSelectionControls(), child: Column( - children: [ - InkWell( - onTap: () => Get.back(), - child: Container( - height: 35, - padding: const EdgeInsets.only(bottom: 2), - child: Center( - child: Container( - width: 32, - height: 3, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: - const BorderRadius.all(Radius.circular(3))), - ), - ), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 4), + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.bvid!, + style: TextStyle( + fontSize: 13, color: Theme.of(context).colorScheme.primary), ), ), - Expanded( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - videoDetail!.title, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 6), - Row( - children: [ - StatView( - theme: 'gray', - view: videoDetail!.stat!.view, - size: 'medium', - ), - const SizedBox(width: 10), - StatDanMu( - theme: 'gray', - danmu: videoDetail!.stat!.danmaku, - size: 'medium', - ), - const SizedBox(width: 10), - Text( - Utils.dateFormat(videoDetail!.pubdate, - formatType: 'detail'), - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.outline, - ), - ), - ], - ), - const SizedBox(height: 20), - SizedBox( - width: double.infinity, - child: SelectableRegion( - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - videoDetail!.bvid!, - style: const TextStyle(fontSize: 13), - ), - const SizedBox(height: 4), - Text.rich( - style: const TextStyle( - height: 1.4, - // fontSize: 13, - ), - TextSpan( - children: [ - buildContent(context, videoDetail!), - ], - ), - ), - ], - ), - ), - ), - ], - ), + const SizedBox(height: 4), + Text.rich( + style: const TextStyle(height: 1.4), + TextSpan( + children: [ + buildContent(context, videoDetail!), + ], ), - ) + ), ], - )); + ), + ), + ); } InlineSpan buildContent(BuildContext context, content) { diff --git a/lib/pages/video/detail/widgets/expandable_section.dart b/lib/pages/video/detail/widgets/expandable_section.dart index afa68cc9..69e73e20 100644 --- a/lib/pages/video/detail/widgets/expandable_section.dart +++ b/lib/pages/video/detail/widgets/expandable_section.dart @@ -32,28 +32,14 @@ class _ExpandedSectionState extends State _runExpandCheck(); } - ///Setting up the animation - // void prepareAnimations() { - // expandController = AnimationController( - // vsync: this, duration: const Duration(milliseconds: 500)); - // animation = CurvedAnimation( - // parent: expandController, - // curve: Curves.fastOutSlowIn, - // ); - // } - void prepareAnimations() { expandController = AnimationController( vsync: this, duration: const Duration(milliseconds: 400)); Animation curve = CurvedAnimation( parent: expandController, - curve: Curves.fastOutSlowIn, + curve: Curves.linear, ); animation = Tween(begin: widget.begin, end: widget.end).animate(curve); - // animation = CurvedAnimation( - // parent: expandController, - // curve: Curves.fastOutSlowIn, - // ); } void _runExpandCheck() { @@ -67,7 +53,9 @@ class _ExpandedSectionState extends State @override void didUpdateWidget(ExpandedSection oldWidget) { super.didUpdateWidget(oldWidget); - _runExpandCheck(); + if (widget.expand != oldWidget.expand) { + _runExpandCheck(); + } } @override From 7e7bb1f43a4c23f0b3473ca58506c93da51912ec Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 16:17:02 +0800 Subject: [PATCH 052/349] =?UTF-8?q?fix:=20appbar=E6=BB=91=E5=8A=A8?= =?UTF-8?q?=E8=B7=9D=E7=A6=BB=E7=A7=BB=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/widgets/app_bar.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/widgets/app_bar.dart b/lib/pages/video/detail/widgets/app_bar.dart index 17f4bec7..efc0b593 100644 --- a/lib/pages/video/detail/widgets/app_bar.dart +++ b/lib/pages/video/detail/widgets/app_bar.dart @@ -17,12 +17,16 @@ class ScrollAppBar extends StatelessWidget { Widget build(BuildContext context) { final double statusBarHeight = MediaQuery.of(context).padding.top; final videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; + double scrollDistance = scrollVal; + if (scrollVal > videoHeight - kToolbarHeight) { + scrollDistance = videoHeight - kToolbarHeight; + } return Positioned( - top: -videoHeight + scrollVal + kToolbarHeight + 0.5, + top: -videoHeight + scrollDistance + kToolbarHeight + 0.5, left: 0, right: 0, child: Opacity( - opacity: scrollVal / (videoHeight - kToolbarHeight), + opacity: scrollDistance / (videoHeight - kToolbarHeight), child: Container( height: statusBarHeight + kToolbarHeight, color: Theme.of(context).colorScheme.background, From 2cd8ab7d27fcf9c6a686725827242d76091b3207 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 16:03:18 +0800 Subject: [PATCH 053/349] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E7=AE=80=E4=BB=8B=E6=9F=A5=E7=9C=8B&?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 33 ++--- .../introduction/widgets/intro_detail.dart | 130 ++++-------------- .../detail/widgets/expandable_section.dart | 20 +-- 3 files changed, 50 insertions(+), 133 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9c1b7db0..831491f6 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -15,6 +15,7 @@ import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; +import '../widgets/expandable_section.dart'; import 'widgets/action_item.dart'; import 'widgets/fav_panel.dart'; import 'widgets/intro_detail.dart'; @@ -137,6 +138,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late String memberHeroTag; late bool enableAi; bool isProcessing = false; + RxBool isExpand = false.obs; void Function()? handleState(Future Function() action) { return isProcessing ? null @@ -212,13 +214,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { // 视频介绍 showIntroDetail() { feedBack(); - showBottomSheet( - context: context, - enableDrag: true, - builder: (BuildContext context) { - return IntroDetail(videoDetail: widget.videoDetail!); - }, - ); + isExpand.value = !(isExpand.value); } // 用户主页 @@ -330,6 +326,16 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ], ), + /// 视频简介 + Obx( + () => ExpandedSection( + expand: isExpand.value, + begin: 0, + end: 1, + child: IntroDetail(videoDetail: widget.videoDetail!), + ), + ), + /// 点赞收藏转发 actionGrid(context, videoIntroController), // 合集 @@ -438,6 +444,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { margin: const EdgeInsets.only(top: 6, bottom: 4), height: constraints.maxWidth / 5 * 0.8, child: GridView.count( + physics: const NeverScrollableScrollPhysics(), primary: false, padding: EdgeInsets.zero, crossAxisCount: 5, @@ -451,12 +458,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { selectStatus: videoIntroController.hasLike.value, text: widget.videoDetail!.stat!.like!.toString()), ), - // ActionItem( - // icon: const Icon(FontAwesomeIcons.clock), - // onTap: () => videoIntroController.actionShareVideo(), - // selectStatus: false, - // loadingStatus: loadingStatus, - // text: '稍后再看'), Obx( () => ActionItem( icon: const Icon(FontAwesomeIcons.b), @@ -477,10 +478,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ActionItem( - icon: const Icon(FontAwesomeIcons.comment), - onTap: () => videoDetailCtr.tabCtr.animateTo(1), + icon: const Icon(FontAwesomeIcons.clock), + onTap: () => videoIntroController.actionShareVideo(), selectStatus: false, - text: widget.videoDetail!.stat!.reply!.toString(), + text: '稍后看', ), ActionItem( icon: const Icon(FontAwesomeIcons.shareFromSquare), diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index c74e27ee..1e9bb842 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -1,16 +1,10 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:hive/hive.dart'; -import 'package:pilipala/common/widgets/stat/danmu.dart'; -import 'package:pilipala/common/widgets/stat/view.dart'; -import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; -Box localCache = GStrorage.localCache; -late double sheetHeight; - class IntroDetail extends StatelessWidget { const IntroDetail({ super.key, @@ -20,105 +14,39 @@ class IntroDetail extends StatelessWidget { @override Widget build(BuildContext context) { - sheetHeight = localCache.get('sheetHeight'); - return Container( - color: Theme.of(context).colorScheme.background, - padding: EdgeInsets.only( - left: 14, - right: 14, - bottom: MediaQuery.of(context).padding.bottom + 20), - height: sheetHeight, + return SizedBox( + width: double.infinity, + child: SelectableRegion( + focusNode: FocusNode(), + selectionControls: MaterialTextSelectionControls(), child: Column( - children: [ - InkWell( - onTap: () => Get.back(), - child: Container( - height: 35, - padding: const EdgeInsets.only(bottom: 2), - child: Center( - child: Container( - width: 32, - height: 3, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.primary, - borderRadius: - const BorderRadius.all(Radius.circular(3))), - ), - ), + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 4), + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.bvid!, + style: TextStyle( + fontSize: 13, color: Theme.of(context).colorScheme.primary), ), ), - Expanded( - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - videoDetail!.title, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.w500, - ), - ), - const SizedBox(height: 6), - Row( - children: [ - StatView( - theme: 'gray', - view: videoDetail!.stat!.view, - size: 'medium', - ), - const SizedBox(width: 10), - StatDanMu( - theme: 'gray', - danmu: videoDetail!.stat!.danmaku, - size: 'medium', - ), - const SizedBox(width: 10), - Text( - Utils.dateFormat(videoDetail!.pubdate, - formatType: 'detail'), - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.outline, - ), - ), - ], - ), - const SizedBox(height: 20), - SizedBox( - width: double.infinity, - child: SelectableRegion( - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - videoDetail!.bvid!, - style: const TextStyle(fontSize: 13), - ), - const SizedBox(height: 4), - Text.rich( - style: const TextStyle( - height: 1.4, - // fontSize: 13, - ), - TextSpan( - children: [ - buildContent(context, videoDetail!), - ], - ), - ), - ], - ), - ), - ), - ], - ), + const SizedBox(height: 4), + Text.rich( + style: const TextStyle(height: 1.4), + TextSpan( + children: [ + buildContent(context, videoDetail!), + ], ), - ) + ), ], - )); + ), + ), + ); } InlineSpan buildContent(BuildContext context, content) { diff --git a/lib/pages/video/detail/widgets/expandable_section.dart b/lib/pages/video/detail/widgets/expandable_section.dart index afa68cc9..69e73e20 100644 --- a/lib/pages/video/detail/widgets/expandable_section.dart +++ b/lib/pages/video/detail/widgets/expandable_section.dart @@ -32,28 +32,14 @@ class _ExpandedSectionState extends State _runExpandCheck(); } - ///Setting up the animation - // void prepareAnimations() { - // expandController = AnimationController( - // vsync: this, duration: const Duration(milliseconds: 500)); - // animation = CurvedAnimation( - // parent: expandController, - // curve: Curves.fastOutSlowIn, - // ); - // } - void prepareAnimations() { expandController = AnimationController( vsync: this, duration: const Duration(milliseconds: 400)); Animation curve = CurvedAnimation( parent: expandController, - curve: Curves.fastOutSlowIn, + curve: Curves.linear, ); animation = Tween(begin: widget.begin, end: widget.end).animate(curve); - // animation = CurvedAnimation( - // parent: expandController, - // curve: Curves.fastOutSlowIn, - // ); } void _runExpandCheck() { @@ -67,7 +53,9 @@ class _ExpandedSectionState extends State @override void didUpdateWidget(ExpandedSection oldWidget) { super.didUpdateWidget(oldWidget); - _runExpandCheck(); + if (widget.expand != oldWidget.expand) { + _runExpandCheck(); + } } @override From 1f75a7e781dd452eae763bd2fc38fb409e97e973 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 16:17:02 +0800 Subject: [PATCH 054/349] =?UTF-8?q?fix:=20appbar=E6=BB=91=E5=8A=A8?= =?UTF-8?q?=E8=B7=9D=E7=A6=BB=E7=A7=BB=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/widgets/app_bar.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/widgets/app_bar.dart b/lib/pages/video/detail/widgets/app_bar.dart index 17f4bec7..efc0b593 100644 --- a/lib/pages/video/detail/widgets/app_bar.dart +++ b/lib/pages/video/detail/widgets/app_bar.dart @@ -17,12 +17,16 @@ class ScrollAppBar extends StatelessWidget { Widget build(BuildContext context) { final double statusBarHeight = MediaQuery.of(context).padding.top; final videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; + double scrollDistance = scrollVal; + if (scrollVal > videoHeight - kToolbarHeight) { + scrollDistance = videoHeight - kToolbarHeight; + } return Positioned( - top: -videoHeight + scrollVal + kToolbarHeight + 0.5, + top: -videoHeight + scrollDistance + kToolbarHeight + 0.5, left: 0, right: 0, child: Opacity( - opacity: scrollVal / (videoHeight - kToolbarHeight), + opacity: scrollDistance / (videoHeight - kToolbarHeight), child: Container( height: statusBarHeight + kToolbarHeight, color: Theme.of(context).colorScheme.background, From 955d8f54019ee6f0d97672fcc4f06a03776baddc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 24 Mar 2024 23:25:45 +0800 Subject: [PATCH 055/349] =?UTF-8?q?feat:=20=E7=AE=80=E5=8D=95=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E5=AD=97=E5=B9=95=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 27 +++++-- lib/models/common/subtitle_type.dart | 47 ++++++++++++ lib/models/video/subTitile/result.dart | 21 +++++- lib/pages/video/detail/controller.dart | 42 +++++++---- lib/pages/video/detail/view.dart | 1 + .../video/detail/widgets/header_control.dart | 75 +++++++++++++++++++ lib/plugin/pl_player/controller.dart | 66 ++++++++++++++-- lib/plugin/pl_player/view.dart | 69 +++++++++-------- lib/utils/subtitle.dart | 32 ++++++++ 9 files changed, 318 insertions(+), 62 deletions(-) create mode 100644 lib/models/common/subtitle_type.dart create mode 100644 lib/utils/subtitle.dart diff --git a/lib/http/video.dart b/lib/http/video.dart index 4ac886c7..d43656b2 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -12,6 +12,7 @@ import '../models/video/subTitile/result.dart'; import '../models/video_detail_res.dart'; import '../utils/recommend_filter.dart'; import '../utils/storage.dart'; +import '../utils/subtitle.dart'; import '../utils/wbi_sign.dart'; import 'api.dart'; import 'init.dart'; @@ -482,13 +483,17 @@ class VideoHttp { 'cid': cid, 'bvid': bvid, }); - if (res.data['code'] == 0) { - return { - 'status': true, - 'data': SubTitlteModel.fromJson(res.data['data']), - }; - } else { - return {'status': false, 'data': [], 'msg': res.data['msg']}; + try { + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': SubTitlteModel.fromJson(res.data['data']), + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['msg']}; + } + } catch (err) { + print(err); } } @@ -514,4 +519,12 @@ class VideoHttp { return {'status': false, 'data': [], 'msg': err}; } } + + // 获取字幕内容 + static Future> getSubtitleContent(url) async { + var res = await Request().get('https:$url'); + final String content = SubTitleUtils.convertToWebVTT(res.data['body']); + final List body = res.data['body']; + return {'content': content, 'body': body}; + } } diff --git a/lib/models/common/subtitle_type.dart b/lib/models/common/subtitle_type.dart new file mode 100644 index 00000000..11716351 --- /dev/null +++ b/lib/models/common/subtitle_type.dart @@ -0,0 +1,47 @@ +enum SubtitleType { + // 中文(中国) + zhCN, + // 中文(自动翻译) + aizh, + // 英语(自动生成) + aien, +} + +extension SubtitleTypeExtension on SubtitleType { + String get description { + switch (this) { + case SubtitleType.zhCN: + return '中文(中国)'; + case SubtitleType.aizh: + return '中文(自动翻译)'; + case SubtitleType.aien: + return '英语(自动生成)'; + } + } +} + +extension SubtitleIdExtension on SubtitleType { + String get id { + switch (this) { + case SubtitleType.zhCN: + return 'zh-CN'; + case SubtitleType.aizh: + return 'ai-zh'; + case SubtitleType.aien: + return 'ai-en'; + } + } +} + +extension SubtitleCodeExtension on SubtitleType { + int get code { + switch (this) { + case SubtitleType.zhCN: + return 1; + case SubtitleType.aizh: + return 2; + case SubtitleType.aien: + return 3; + } + } +} diff --git a/lib/models/video/subTitile/result.dart b/lib/models/video/subTitile/result.dart index 389378fa..d3e32e55 100644 --- a/lib/models/video/subTitile/result.dart +++ b/lib/models/video/subTitile/result.dart @@ -1,3 +1,6 @@ +import 'package:get/get.dart'; +import '../../common/subtitle_type.dart'; + class SubTitlteModel { SubTitlteModel({ this.aid, @@ -45,6 +48,10 @@ class SubTitlteItemModel { this.type, this.aiType, this.aiStatus, + this.title, + this.code, + this.content, + this.body, }); int? id; @@ -55,16 +62,28 @@ class SubTitlteItemModel { int? type; int? aiType; int? aiStatus; + String? title; + int? code; + String? content; + List? body; factory SubTitlteItemModel.fromJson(Map json) => SubTitlteItemModel( id: json["id"], - lan: json["lan"], + lan: json["lan"].replaceAll('-', ''), lanDoc: json["lan_doc"], isLock: json["is_lock"], subtitleUrl: json["subtitle_url"], type: json["type"], aiType: json["ai_type"], aiStatus: json["ai_status"], + title: json["lan_doc"], + code: SubtitleType.values + .firstWhereOrNull( + (element) => element.id.toString() == json["lan"]) + ?.index ?? + -1, + content: '', + body: [], ); } diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 5eda1e77..5c4ac14b 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -20,7 +20,6 @@ import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/video_utils.dart'; import 'package:screen_brightness/screen_brightness.dart'; -import '../../../http/index.dart'; import '../../../models/video/subTitile/content.dart'; import '../../../http/danmaku.dart'; import '../../../utils/id_utils.dart'; @@ -98,6 +97,7 @@ class VideoDetailController extends GetxController RxList subtitleContents = [].obs; late bool enableRelatedVideo; + List subtitles = []; @override void onInit() { @@ -256,6 +256,8 @@ class VideoDetailController extends GetxController /// 开启自动全屏时,在player初始化完成后立即传入headerControl plPlayerController.headerControl = headerControl; + + plPlayerController.subtitles.value = subtitles; } // 视频链接 @@ -398,30 +400,38 @@ class VideoDetailController extends GetxController var result = await VideoHttp.getSubtitle(bvid: bvid, cid: cid.value); if (result['status']) { if (result['data'].subtitles.isNotEmpty) { - SmartDialog.showToast('字幕加载中...'); - var subtitle = result['data'].subtitles.first; - getSubtitleContent(subtitle.subtitleUrl); + subtitles = result['data'].subtitles; + if (subtitles.isNotEmpty) { + for (var i in subtitles) { + final Map res = await VideoHttp.getSubtitleContent( + i.subtitleUrl, + ); + i.content = res['content']; + i.body = res['body']; + } + } } return result['data']; - } else { - SmartDialog.showToast(result['msg'].toString()); } } // 获取字幕内容 - Future getSubtitleContent(String url) async { - var res = await Request().get('https:$url'); - subtitleContents.value = res.data['body'].map((e) { - return SubTitileContentModel.fromJson(e); - }).toList(); - setSubtitleContent(); - } + // Future getSubtitleContent(String url) async { + // var res = await Request().get('https:$url'); + // subtitleContents.value = res.data['body'].map((e) { + // return SubTitileContentModel.fromJson(e); + // }).toList(); + // setSubtitleContent(); + // } setSubtitleContent() { plPlayerController.subtitleContent.value = ''; - if (subtitleContents.isNotEmpty) { - plPlayerController.subtitleContents = subtitleContents; - } + plPlayerController.subtitles.value = subtitles; + } + + clearSubtitleContent() { + plPlayerController.subtitleContent.value = ''; + plPlayerController.subtitles.value = []; } /// 发送弹幕 diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 08826763..6958a62d 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -212,6 +212,7 @@ class _VideoDetailPageState extends State videoIntroController.isPaused = true; plPlayerController!.removeStatusLister(playerListener); plPlayerController!.pause(); + vdCtr.clearSubtitleContent(); } setState(() => isShowing = false); super.didPushNext(); diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 858ca2df..b0b7db17 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -344,6 +344,56 @@ class _HeaderControlState extends State { ); } + /// 选择字幕 + void showSubtitleDialog() async { + int tempThemeValue = widget.controller!.subTitleCode.value; + int len = widget.videoDetailCtr!.subtitles.length; + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('选择字幕'), + contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 18), + content: StatefulBuilder(builder: (context, StateSetter setState) { + return len == 0 + ? const SizedBox( + height: 60, + child: Center( + child: Text('没有字幕'), + ), + ) + : Column( + mainAxisSize: MainAxisSize.min, + children: [ + RadioListTile( + value: -1, + title: const Text('关闭弹幕'), + groupValue: tempThemeValue, + onChanged: (value) { + tempThemeValue = value!; + widget.controller?.toggleSubtitle(value); + Get.back(); + }, + ), + ...widget.videoDetailCtr!.subtitles + .map((e) => RadioListTile( + value: e.code, + title: Text(e.title), + groupValue: tempThemeValue, + onChanged: (value) { + tempThemeValue = value!; + widget.controller?.toggleSubtitle(value); + Get.back(); + }, + )) + .toList(), + ], + ); + }), + ); + }); + } + /// 选择倍速 void showSetSpeedSheet() { final double currentSpeed = widget.controller!.playbackSpeed; @@ -1115,6 +1165,31 @@ class _HeaderControlState extends State { ), SizedBox(width: buttonSpace), ], + + /// 字幕 + // SizedBox( + // width: 34, + // height: 34, + // child: IconButton( + // style: ButtonStyle( + // padding: MaterialStateProperty.all(EdgeInsets.zero), + // ), + // onPressed: () => showSubtitleDialog(), + // icon: const Icon( + // Icons.closed_caption_off, + // size: 22, + // ), + // ), + // ), + ComBtn( + icon: const Icon( + Icons.closed_caption_off, + size: 22, + color: Colors.white, + ), + fuc: () => showSubtitleDialog(), + ), + SizedBox(width: buttonSpace), Obx( () => SizedBox( width: 45, diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 6f73f6aa..b385fca8 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -22,6 +22,7 @@ import 'package:screen_brightness/screen_brightness.dart'; import 'package:status_bar_control/status_bar_control.dart'; import 'package:universal_platform/universal_platform.dart'; import '../../models/video/subTitile/content.dart'; +import '../../models/video/subTitile/result.dart'; // import 'package:wakelock_plus/wakelock_plus.dart'; Box videoStorage = GStrorage.video; @@ -74,6 +75,8 @@ class PlPlayerController { final Rx _doubleSpeedStatus = false.obs; final Rx _controlsLock = false.obs; final Rx _isFullScreen = false.obs; + final Rx _subTitleOpen = false.obs; + final Rx _subTitleCode = (-1).obs; // 默认投稿视频格式 static Rx _videoType = 'archive'.obs; @@ -119,6 +122,7 @@ class PlPlayerController { PreferredSizeWidget? headerControl; PreferredSizeWidget? bottomControl; Widget? danmuWidget; + late RxList subtitles; /// 数据加载监听 Stream get onDataStatusChanged => dataStatus.status.stream; @@ -148,6 +152,11 @@ class PlPlayerController { Rx get mute => _mute; Stream get onMuteChanged => _mute.stream; + /// 字幕开启状态 + Rx get subTitleOpen => _subTitleOpen; + Rx get subTitleCode => _subTitleCode; + // Stream get onSubTitleOpenChanged => _subTitleOpen.stream; + /// [videoPlayerController] instace of Player Player? get videoPlayerController => _videoPlayerController; @@ -355,6 +364,8 @@ class PlPlayerController { bool enableHeart = true, // 是否首次加载 bool isFirstTime = true, + // 是否开启字幕 + bool enableSubTitle = false, }) async { try { _autoPlay = autoplay; @@ -369,7 +380,9 @@ class PlPlayerController { _cid = cid; _enableHeart = enableHeart; _isFirstTime = isFirstTime; - + _subTitleOpen.value = enableSubTitle; + subtitles = [].obs; + subtitleContent.value = ''; if (_videoPlayerController != null && _videoPlayerController!.state.playing) { await pause(notify: false); @@ -616,6 +629,10 @@ class PlPlayerController { const Duration(seconds: 1), () => videoPlayerServiceHandler.onPositionChange(event)); }), + + // onSubTitleOpenChanged.listen((bool event) { + // toggleSubtitle(event ? subTitleCode.value : -1); + // }) ], ); } @@ -1054,11 +1071,46 @@ class PlPlayerController { } } + /// 字幕 + void toggleSubtitle(int code) { + _subTitleOpen.value = code != -1; + _subTitleCode.value = code; + // if (code == -1) { + // // 关闭字幕 + // _subTitleOpen.value = false; + // _subTitleCode.value = code; + // _videoPlayerController?.setSubtitleTrack(SubtitleTrack.no()); + // return; + // } + // final SubTitlteItemModel? subtitle = subtitles?.firstWhereOrNull( + // (element) => element.code == code, + // ); + // _subTitleOpen.value = true; + // _subTitleCode.value = code; + // _videoPlayerController?.setSubtitleTrack( + // SubtitleTrack.data( + // subtitle!.content!, + // title: subtitle.title, + // language: subtitle.lan, + // ), + // ); + } + void querySubtitleContent(double progress) { - if (subtitleContents.isNotEmpty) { - for (var content in subtitleContents) { - if (progress >= content.from! && progress <= content.to!) { - subtitleContent.value = content.content!; + if (subTitleCode.value == -1) { + subtitleContent.value = ''; + return; + } + if (subtitles.isEmpty) { + return; + } + final SubTitlteItemModel? subtitle = subtitles.firstWhereOrNull( + (element) => element.code == subTitleCode.value, + ); + if (subtitle != null && subtitle.body!.isNotEmpty) { + for (var content in subtitle.body!) { + if (progress >= content['from']! && progress <= content['to']!) { + subtitleContent.value = content['content']!; return; } } @@ -1071,6 +1123,9 @@ class PlPlayerController { } Future dispose({String type = 'single'}) async { + print('dispose'); + print('dispose: ${playerCount.value}'); + // 每次减1,最后销毁 if (type == 'single' && playerCount.value > 1) { _playerCount.value -= 1; @@ -1080,6 +1135,7 @@ class PlPlayerController { } _playerCount.value = 0; try { + print('dispose dispose ---------'); _timer?.cancel(); _timerForVolume?.cancel(); _timerForGettingVolume?.cancel(); diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index aad686e3..be24b105 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -580,41 +580,44 @@ class _PLVideoPlayerState extends State if (widget.danmuWidget != null) Positioned.fill(top: 4, child: widget.danmuWidget!), - widget.controller.subscriptions.isNotEmpty - ? Stack( - children: [ - Positioned( - left: 0, - right: 0, - bottom: 30, - child: Align( - alignment: Alignment.center, - child: Obx( - () => Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: widget.controller.subtitleContent.value != '' - ? Colors.black.withOpacity(0.4) - : Colors.transparent, - ), - padding: const EdgeInsets.symmetric( - horizontal: 10, - vertical: 4, - ), - child: Text( - widget.controller.subtitleContent.value, - style: const TextStyle( - color: Colors.white, - fontSize: 12, - ), + /// 开启且有字幕时展示 + Stack( + children: [ + Positioned( + left: 0, + right: 0, + bottom: 30, + child: Align( + alignment: Alignment.center, + child: Obx( + () => Visibility( + visible: widget.controller.subTitleCode.value != -1, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: widget.controller.subtitleContent.value != '' + ? Colors.black.withOpacity(0.6) + : Colors.transparent, + ), + padding: widget.controller.subTitleCode.value != -1 + ? const EdgeInsets.symmetric( + horizontal: 10, + vertical: 4, + ) + : EdgeInsets.zero, + child: Text( + widget.controller.subtitleContent.value, + style: const TextStyle( + color: Colors.white, + fontSize: 12, ), ), - ), - ), - ), - ], - ) - : const SizedBox(), + )), + ), + ), + ), + ], + ), /// 手势 Positioned.fill( diff --git a/lib/utils/subtitle.dart b/lib/utils/subtitle.dart new file mode 100644 index 00000000..452be542 --- /dev/null +++ b/lib/utils/subtitle.dart @@ -0,0 +1,32 @@ +class SubTitleUtils { + // 格式整理 + static String convertToWebVTT(List jsonData) { + String webVTTContent = 'WEBVTT FILE\n\n'; + + for (int i = 0; i < jsonData.length; i++) { + final item = jsonData[i]; + double from = item['from'] as double; + double to = item['to'] as double; + int sid = (item['sid'] ?? 0) as int; + String content = item['content'] as String; + + webVTTContent += '$sid\n'; + webVTTContent += '${formatTime(from)} --> ${formatTime(to)}\n'; + webVTTContent += '$content\n\n'; + } + + return webVTTContent; + } + + static String formatTime(num seconds) { + final String h = (seconds / 3600).floor().toString().padLeft(2, '0'); + final String m = (seconds % 3600 / 60).floor().toString().padLeft(2, '0'); + final String s = (seconds % 60).floor().toString().padLeft(2, '0'); + final String ms = + (seconds * 1000 % 1000).floor().toString().padLeft(3, '0'); + if (h == '00') { + return "$m:$s.$ms"; + } + return "$h:$m:$s.$ms"; + } +} From d6fd2993950779a57a75f4e4fe533f3b8c8f2bc9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 25 Mar 2024 22:25:05 +0800 Subject: [PATCH 056/349] =?UTF-8?q?fix:=20tabbar=E6=8C=87=E7=A4=BA?= =?UTF-8?q?=E5=99=A8=E6=8A=96=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/rank/controller.dart | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index da73ea02..6fe3d424 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -50,21 +50,5 @@ class RankController extends GetxController with GetTickerProviderStateMixin { length: tabs.length, vsync: this, ); - // 监听 tabController 切换 - 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; - } - } - }); - } } } From d6b972a8ab7e6036f82b91fd866f77792996aa24 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 26 Mar 2024 22:13:01 +0800 Subject: [PATCH 057/349] =?UTF-8?q?mod:=20=E5=BC=B9=E5=B9=95=E5=9B=BE?= =?UTF-8?q?=E6=A0=87=E9=A2=9C=E8=89=B2&=E7=A8=8D=E5=90=8E=E5=86=8D?= =?UTF-8?q?=E7=9C=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 7 ++++++- lib/pages/video/detail/view.dart | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 831491f6..344b4b3a 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -15,6 +15,7 @@ import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; +import '../../../../http/user.dart'; import '../widgets/expandable_section.dart'; import 'widgets/action_item.dart'; import 'widgets/fav_panel.dart'; @@ -479,7 +480,11 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ActionItem( icon: const Icon(FontAwesomeIcons.clock), - onTap: () => videoIntroController.actionShareVideo(), + onTap: () async { + final res = + await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); + SmartDialog.showToast(res['msg']); + }, selectStatus: false, text: '稍后看', ), diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 69801beb..ffa50476 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -372,6 +372,9 @@ class _VideoDetailPageState extends State false) ? SvgPicture.asset( 'assets/images/video/danmu_close.svg', + // ignore: deprecated_member_use + color: + Theme.of(context).colorScheme.outline, ) : SvgPicture.asset( 'assets/images/video/danmu_open.svg', From ed8443ba02a2660d5e33e77eee314dcfad98a0b6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 27 Mar 2024 22:26:47 +0800 Subject: [PATCH 058/349] =?UTF-8?q?feat:=20navigation=20Bar=E7=BC=96?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/nav_bar_config.dart | 9 ++ lib/pages/main/controller.dart | 38 ++++--- .../setting/pages/navigation_bar_set.dart | 100 ++++++++++++++++++ lib/pages/setting/style_setting.dart | 7 +- lib/router/app_pages.dart | 4 + lib/utils/storage.dart | 3 +- 6 files changed, 144 insertions(+), 17 deletions(-) create mode 100644 lib/pages/setting/pages/navigation_bar_set.dart diff --git a/lib/models/common/nav_bar_config.dart b/lib/models/common/nav_bar_config.dart index 9ebe8e6f..64cebafb 100644 --- a/lib/models/common/nav_bar_config.dart +++ b/lib/models/common/nav_bar_config.dart @@ -1,5 +1,10 @@ import 'package:flutter/material.dart'; +import '../../pages/dynamics/index.dart'; +import '../../pages/home/index.dart'; +import '../../pages/media/index.dart'; +import '../../pages/rank/index.dart'; + List defaultNavigationBars = [ { 'id': 0, @@ -13,6 +18,7 @@ List defaultNavigationBars = [ ), 'label': "首页", 'count': 0, + 'page': const HomePage(), }, { 'id': 1, @@ -26,6 +32,7 @@ List defaultNavigationBars = [ ), 'label': "排行榜", 'count': 0, + 'page': const RankPage(), }, { 'id': 2, @@ -39,6 +46,7 @@ List defaultNavigationBars = [ ), 'label': "动态", 'count': 0, + 'page': const DynamicsPage(), }, { 'id': 3, @@ -52,5 +60,6 @@ List defaultNavigationBars = [ ), 'label': "媒体库", 'count': 0, + 'page': const MediaPage(), } ]; diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index ddbd364a..f929a1aa 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -6,23 +6,16 @@ import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/common.dart'; -import 'package:pilipala/pages/dynamics/index.dart'; -import 'package:pilipala/pages/home/view.dart'; -import 'package:pilipala/pages/media/index.dart'; -import 'package:pilipala/pages/rank/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 = [ - const HomePage(), - const RankPage(), - const DynamicsPage(), - const MediaPage(), - ]; - RxList navigationBars = defaultNavigationBars.obs; + List pages = []; + RxList navigationBars = [].obs; + late List defaultNavTabs; + late List navBarSort; final StreamController bottomBarStream = StreamController.broadcast(); Box setting = GStrorage.setting; @@ -41,10 +34,7 @@ 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( @@ -53,6 +43,7 @@ class MainController extends GetxController { if (dynamicBadgeType.value != DynamicBadgeMode.hidden) { getUnreadDynamic(); } + setNavBarConfig(); } void onBackPressed(BuildContext context) { @@ -93,4 +84,21 @@ class MainController extends GetxController { } navigationBars.refresh(); } + + void setNavBarConfig() async { + defaultNavTabs = [...defaultNavigationBars]; + navBarSort = + setting.get(SettingBoxKey.navBarSort, defaultValue: [0, 1, 2, 3]); + defaultNavTabs.retainWhere((item) => navBarSort.contains(item['id'])); + defaultNavTabs.sort((a, b) => + navBarSort.indexOf(a['id']).compareTo(navBarSort.indexOf(b['id']))); + navigationBars.value = defaultNavTabs; + int defaultHomePage = + setting.get(SettingBoxKey.defaultHomePage, defaultValue: 0) as int; + int defaultIndex = + navigationBars.indexWhere((item) => item['id'] == defaultHomePage); + // 如果找不到匹配项,默认索引设置为0或其他合适的值 + selectedIndex = defaultIndex != -1 ? defaultIndex : 0; + pages = navigationBars.map((e) => e['page']).toList(); + } } diff --git a/lib/pages/setting/pages/navigation_bar_set.dart b/lib/pages/setting/pages/navigation_bar_set.dart new file mode 100644 index 00000000..8e1771e3 --- /dev/null +++ b/lib/pages/setting/pages/navigation_bar_set.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/models/common/tab_type.dart'; +import 'package:pilipala/utils/storage.dart'; + +import '../../../models/common/nav_bar_config.dart'; + +class NavigationBarSetPage extends StatefulWidget { + const NavigationBarSetPage({super.key}); + + @override + State createState() => _NavigationbarSetPageState(); +} + +class _NavigationbarSetPageState extends State { + Box settingStorage = GStrorage.setting; + late List defaultNavTabs; + late List navBarSort; + + @override + void initState() { + super.initState(); + defaultNavTabs = defaultNavigationBars; + navBarSort = settingStorage + .get(SettingBoxKey.navBarSort, defaultValue: [0, 1, 2, 3]); + // 对 tabData 进行排序 + defaultNavTabs.sort((a, b) { + int indexA = navBarSort.indexOf(a['id']); + int indexB = navBarSort.indexOf(b['id']); + + // 如果类型在 sortOrder 中不存在,则放在末尾 + if (indexA == -1) indexA = navBarSort.length; + if (indexB == -1) indexB = navBarSort.length; + + return indexA.compareTo(indexB); + }); + } + + void saveEdit() { + List sortedTabbar = defaultNavTabs + .where((i) => navBarSort.contains(i['id'])) + .map((i) => i['id']) + .toList(); + settingStorage.put(SettingBoxKey.navBarSort, sortedTabbar); + SmartDialog.showToast('保存成功,下次启动时生效'); + } + + void onReorder(int oldIndex, int newIndex) { + setState(() { + if (newIndex > oldIndex) { + newIndex -= 1; + } + final tabsItem = defaultNavTabs.removeAt(oldIndex); + defaultNavTabs.insert(newIndex, tabsItem); + }); + } + + @override + Widget build(BuildContext context) { + final listTiles = [ + for (int i = 0; i < defaultNavTabs.length; i++) ...[ + CheckboxListTile( + key: Key(defaultNavTabs[i]['label']), + value: navBarSort.contains(defaultNavTabs[i]['id']), + onChanged: (bool? newValue) { + int tabTypeId = defaultNavTabs[i]['id']; + if (!newValue!) { + navBarSort.remove(tabTypeId); + } else { + navBarSort.add(tabTypeId); + } + setState(() {}); + }, + title: Text(defaultNavTabs[i]['label']), + secondary: const Icon(Icons.drag_indicator_rounded), + enabled: defaultNavTabs[i]['id'] != 3, + ) + ] + ]; + + return Scaffold( + appBar: AppBar( + title: const Text('Navbar编辑'), + actions: [ + TextButton(onPressed: () => saveEdit(), child: const Text('保存')), + const SizedBox(width: 12) + ], + ), + body: ReorderableListView( + onReorder: onReorder, + physics: const NeverScrollableScrollPhysics(), + footer: SizedBox( + height: MediaQuery.of(context).padding.bottom + 30, + ), + children: listTiles, + ), + ); + } +} diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 30b9a30f..d2403cff 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -284,12 +284,17 @@ class _StyleSettingState extends State { onTap: () => Get.toNamed('/tabbarSetting'), title: Text('首页tabbar', style: titleStyle), ), + ListTile( + dense: false, + onTap: () => Get.toNamed('/navbarSetting'), + title: Text('navbar设置', style: titleStyle), + ), if (Platform.isAndroid) ListTile( dense: false, onTap: () => Get.toNamed('/displayModeSetting'), title: Text('屏幕帧率', style: titleStyle), - ) + ), ], ), ); diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 1f1ea31e..7fda1bd8 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/navigation_bar_set.dart'; import '../pages/setting/pages/play_gesture_set.dart'; import '../pages/setting/pages/play_speed_set.dart'; import '../pages/setting/recommend_setting.dart'; @@ -170,6 +171,9 @@ class Routes { // 播放器手势 CustomGetPage( name: '/playerGestureSet', page: () => const PlayGesturePage()), + // navigation bar + CustomGetPage( + name: '/navbarSetting', page: () => const NavigationBarSetPage()), ]; } diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index a82972e0..29cf1846 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -148,7 +148,8 @@ class SettingBoxKey { hideTabBar = 'hideTabBar', // 收起底栏 tabbarSort = 'tabbarSort', // 首页tabbar dynamicBadgeMode = 'dynamicBadgeMode', - enableGradientBg = 'enableGradientBg'; + enableGradientBg = 'enableGradientBg', + navBarSort = 'navBarSort'; } class LocalCacheKey { From 463ee1d5b5b7630eb7c974a5374c50d4852d9694 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 27 Mar 2024 23:27:53 +0800 Subject: [PATCH 059/349] =?UTF-8?q?mod:=20=E6=A0=87=E9=A2=98=E8=BD=AC?= =?UTF-8?q?=E4=B9=89=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/em.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/utils/em.dart b/lib/utils/em.dart index 733f5c35..4e2ed9f2 100644 --- a/lib/utils/em.dart +++ b/lib/utils/em.dart @@ -27,7 +27,8 @@ class Em { .replaceAll('"', '"') .replaceAll(''', "'") .replaceAll(' ', " ") - .replaceAll('&', "&"); + .replaceAll('&', "&") + .replaceAll(''', "'"); Map map = {'type': 'text', 'text': str}; res.add(map); } From 6b028c36af68d308eeab024a8394db61cac090ba Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 27 Mar 2024 23:34:59 +0800 Subject: [PATCH 060/349] =?UTF-8?q?mod:=20=E6=90=9C=E7=B4=A2=E4=B8=93?= =?UTF-8?q?=E6=A0=8F=E5=89=AF=E6=A0=87=E9=A2=98=E8=BD=AC=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/search/result.dart | 3 ++- lib/utils/em.dart | 24 ++++++++++++++---------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart index 0067791c..418fb99d 100644 --- a/lib/models/search/result.dart +++ b/lib/models/search/result.dart @@ -437,7 +437,8 @@ class SearchArticleItemModel { pubTime = json['pub_time']; like = json['like']; title = Em.regTitle(json['title']); - subTitle = json['title'].replaceAll(RegExp(r'<[^>]*>'), ''); + subTitle = + Em.decodeHtmlEntities(json['title'].replaceAll(RegExp(r'<[^>]*>'), '')); rankOffset = json['rank_offset']; mid = json['mid']; imageUrls = json['image_urls']; diff --git a/lib/utils/em.dart b/lib/utils/em.dart index 4e2ed9f2..2c5af8ba 100644 --- a/lib/utils/em.dart +++ b/lib/utils/em.dart @@ -19,16 +19,7 @@ class Em { return regCate(matchStr); }, onNonMatch: (String str) { if (str != '') { - str = str - .replaceAll('<', '<') - .replaceAll('>', '>') - .replaceAll('"', '"') - .replaceAll(''', "'") - .replaceAll('"', '"') - .replaceAll(''', "'") - .replaceAll(' ', " ") - .replaceAll('&', "&") - .replaceAll(''', "'"); + str = decodeHtmlEntities(str); Map map = {'type': 'text', 'text': str}; res.add(map); } @@ -36,4 +27,17 @@ class Em { }); return res; } + + static String decodeHtmlEntities(String title) { + return title + .replaceAll('<', '<') + .replaceAll('>', '>') + .replaceAll('"', '"') + .replaceAll(''', "'") + .replaceAll('"', '"') + .replaceAll(''', "'") + .replaceAll(' ', " ") + .replaceAll('&', "&") + .replaceAll(''', "'"); + } } From aae08d068852bad5d0844f81423deb1f90b8530f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 27 Mar 2024 23:44:07 +0800 Subject: [PATCH 061/349] =?UTF-8?q?fix:=20=E6=9C=80=E7=83=AD/=E6=9C=80?= =?UTF-8?q?=E6=96=B0=E8=AF=84=E8=AE=BA=E6=A0=87=E8=AF=86=E6=9C=AA=E5=88=B7?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/view.dart | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 38203f7e..2a167fe9 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -149,13 +149,16 @@ class _VideoReplyPanelState extends State delegate: _MySliverPersistentHeaderDelegate( child: Container( height: 40, - padding: const EdgeInsets.fromLTRB(12, 6, 6, 0), + padding: const EdgeInsets.fromLTRB(12, 0, 6, 0), + color: Theme.of(context).colorScheme.surface, child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - Text( - '${_videoReplyController.sortTypeLabel.value}评论', - style: const TextStyle(fontSize: 13), + Obx( + () => Text( + '${_videoReplyController.sortTypeLabel.value}评论', + style: const TextStyle(fontSize: 13), + ), ), SizedBox( height: 35, From fb3be848b4d0769af856983a8788b41af3c55884 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 28 Mar 2024 00:00:27 +0800 Subject: [PATCH 062/349] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E8=BF=9B=E5=BA=A6=E6=9D=A1=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/history/widgets/item.dart | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index a83e118b..f4bd9221 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -185,7 +185,7 @@ class HistoryItem extends StatelessWidget { ? '已看完' : '${Utils.timeFormat(videoItem.progress!)}/${Utils.timeFormat(videoItem.duration!)}', right: 6.0, - bottom: 6.0, + bottom: 8.0, type: 'gray', ), // 右上角 @@ -258,6 +258,24 @@ class HistoryItem extends StatelessWidget { ), ), ), + Positioned( + left: 3, + right: 3, + bottom: 0, + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: + Radius.circular(StyleString.imgRadius.x), + bottomRight: + Radius.circular(StyleString.imgRadius.x), + ), + child: LinearProgressIndicator( + value: videoItem.progress == -1 + ? 100 + : videoItem.progress / videoItem.duration, + ), + ), + ) ], ), VideoContent(videoItem: videoItem, ctr: ctr) From 53941469cec5405c36ed23acb112e7410cbe0b6c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 28 Mar 2024 00:29:22 +0800 Subject: [PATCH 063/349] =?UTF-8?q?opt:=20=E5=90=91=E4=B8=8B=E6=9F=A5?= =?UTF-8?q?=E6=89=BE=E5=8F=AF=E7=94=A8=E8=A7=86=E9=A2=91=E6=B8=85=E6=99=B0?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/utils.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index adcc7b5a..bc3252b8 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -208,14 +208,16 @@ class Utils { static int findClosestNumber(int target, List numbers) { int minDiff = 127; - late int closestNumber; + int closestNumber = 0; // 初始化为0,表示没有找到比目标值小的整数 try { for (int number in numbers) { - int diff = (number - target).abs(); + if (number < target) { + int diff = target - number; // 计算目标值与当前整数的差值 - if (diff < minDiff) { - minDiff = diff; - closestNumber = number; + if (diff < minDiff) { + minDiff = diff; + closestNumber = number; + } } } } catch (_) {} From 8f9fbf5d41c7daa1ed17e774e1710236218a9c23 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 29 Mar 2024 00:01:17 +0800 Subject: [PATCH 064/349] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E6=A0=87?= =?UTF-8?q?=E9=A2=98=E5=B1=95=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 13 ++-- lib/pages/video/detail/introduction/view.dart | 62 ++++++++++++++----- pubspec.lock | 8 +++ pubspec.yaml | 2 + 4 files changed, 65 insertions(+), 20 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 8db59815..2c1a635b 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -9,6 +9,7 @@ PODS: - Flutter - connectivity_plus (0.0.1): - Flutter + - FlutterMacOS - ReachabilitySwift - device_info_plus (0.0.1): - Flutter @@ -38,7 +39,7 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - permission_handler_apple (9.1.1): + - permission_handler_apple (9.3.0): - Flutter - ReachabilitySwift (5.0.0) - saver_gallery (0.0.1): @@ -71,7 +72,7 @@ DEPENDENCIES: - audio_service (from `.symlinks/plugins/audio_service/ios`) - audio_session (from `.symlinks/plugins/audio_session/ios`) - auto_orientation (from `.symlinks/plugins/auto_orientation/ios`) - - connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`) + - connectivity_plus (from `.symlinks/plugins/connectivity_plus/darwin`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) - flutter_mailer (from `.symlinks/plugins/flutter_mailer/ios`) @@ -113,7 +114,7 @@ EXTERNAL SOURCES: auto_orientation: :path: ".symlinks/plugins/auto_orientation/ios" connectivity_plus: - :path: ".symlinks/plugins/connectivity_plus/ios" + :path: ".symlinks/plugins/connectivity_plus/darwin" device_info_plus: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: @@ -166,7 +167,7 @@ SPEC CHECKSUMS: audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_session: 4f3e461722055d21515cf3261b64c973c062f345 auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d - connectivity_plus: 07c49e96d7fc92bc9920617b83238c4d178b446a + connectivity_plus: e2dad488011aeb593e219360e804c43cc1af5770 device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 @@ -180,7 +181,7 @@ SPEC CHECKSUMS: media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 - permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 + permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 @@ -193,7 +194,7 @@ SPEC CHECKSUMS: volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7 - webview_flutter_wkwebview: 2e2d318f21a5e036e2c3f26171342e95908bd60a + webview_flutter_wkwebview: 4f3e50f7273d31e5500066ed267e3ae4309c5ae4 PODFILE CHECKSUM: 637cd290bed23275b5f5ffcc7eb1e73d0a5fb2be diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 344b4b3a..a990aab8 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,4 @@ +import 'package:expandable/expandable.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; @@ -16,7 +17,6 @@ import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; import '../../../../http/user.dart'; -import '../widgets/expandable_section.dart'; import 'widgets/action_item.dart'; import 'widgets/fav_panel.dart'; import 'widgets/intro_detail.dart'; @@ -140,6 +140,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late bool enableAi; bool isProcessing = false; RxBool isExpand = false.obs; + late ExpandableController _expandableCtr; + void Function()? handleState(Future Function() action) { return isProcessing ? null @@ -163,6 +165,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { follower = Utils.numFormat(videoIntroController.userStat['follower']); followStatus = videoIntroController.followStatus; enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); + _expandableCtr = ExpandableController(initialExpanded: false); } // 收藏 @@ -216,6 +219,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { showIntroDetail() { feedBack(); isExpand.value = !(isExpand.value); + _expandableCtr.toggle(); } // 用户主页 @@ -239,6 +243,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); } + @override + void dispose() { + _expandableCtr.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final ThemeData t = Theme.of(context); @@ -256,14 +266,34 @@ class _VideoInfoState extends State with TickerProviderStateMixin { GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => showIntroDetail(), - child: Text( - widget.videoDetail!.title!, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, + child: ExpandablePanel( + controller: _expandableCtr, + collapsed: Text( + widget.videoDetail!.title!, + softWrap: true, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + expanded: Text( + widget.videoDetail!.title!, + softWrap: true, + maxLines: 4, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + theme: const ExpandableThemeData( + animationDuration: Duration(milliseconds: 300), + scrollAnimationDuration: Duration(milliseconds: 300), + crossFadePoint: 0, + fadeCurve: Curves.ease, + sizeCurve: Curves.linear, ), - maxLines: 2, - overflow: TextOverflow.ellipsis, ), ), Stack( @@ -328,12 +358,16 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), /// 视频简介 - Obx( - () => ExpandedSection( - expand: isExpand.value, - begin: 0, - end: 1, - child: IntroDetail(videoDetail: widget.videoDetail!), + ExpandablePanel( + controller: _expandableCtr, + collapsed: const SizedBox(height: 0), + expanded: IntroDetail(videoDetail: widget.videoDetail!), + theme: const ExpandableThemeData( + animationDuration: Duration(milliseconds: 300), + scrollAnimationDuration: Duration(milliseconds: 300), + crossFadePoint: 0, + fadeCurve: Curves.ease, + sizeCurve: Curves.linear, ), ), diff --git a/pubspec.lock b/pubspec.lock index 695505d7..84556c06 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -433,6 +433,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "5.0.3" + expandable: + dependency: "direct main" + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.1" extended_image: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 5e19b56b..ba5976eb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -142,6 +142,8 @@ dependencies: path: 1.8.3 # 电池优化 disable_battery_optimization: ^1.1.1 + # 展开/收起 + expandable: ^5.0.1 dev_dependencies: flutter_test: From d806de7d8f982fe0ea6a650692a63408d853bd8d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 28 Mar 2024 00:00:27 +0800 Subject: [PATCH 065/349] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E8=BF=9B=E5=BA=A6=E6=9D=A1=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/history/widgets/item.dart | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index a83e118b..f4bd9221 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -185,7 +185,7 @@ class HistoryItem extends StatelessWidget { ? '已看完' : '${Utils.timeFormat(videoItem.progress!)}/${Utils.timeFormat(videoItem.duration!)}', right: 6.0, - bottom: 6.0, + bottom: 8.0, type: 'gray', ), // 右上角 @@ -258,6 +258,24 @@ class HistoryItem extends StatelessWidget { ), ), ), + Positioned( + left: 3, + right: 3, + bottom: 0, + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: + Radius.circular(StyleString.imgRadius.x), + bottomRight: + Radius.circular(StyleString.imgRadius.x), + ), + child: LinearProgressIndicator( + value: videoItem.progress == -1 + ? 100 + : videoItem.progress / videoItem.duration, + ), + ), + ) ], ), VideoContent(videoItem: videoItem, ctr: ctr) From 6c2eab86e949c8c518fba3acde0456f60eb3b535 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 29 Mar 2024 00:01:17 +0800 Subject: [PATCH 066/349] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E6=A0=87?= =?UTF-8?q?=E9=A2=98=E5=B1=95=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 62 ++++++++++++++----- pubspec.lock | 8 +++ pubspec.yaml | 2 + 3 files changed, 58 insertions(+), 14 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 344b4b3a..a990aab8 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,4 @@ +import 'package:expandable/expandable.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; @@ -16,7 +17,6 @@ import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; import '../../../../http/user.dart'; -import '../widgets/expandable_section.dart'; import 'widgets/action_item.dart'; import 'widgets/fav_panel.dart'; import 'widgets/intro_detail.dart'; @@ -140,6 +140,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late bool enableAi; bool isProcessing = false; RxBool isExpand = false.obs; + late ExpandableController _expandableCtr; + void Function()? handleState(Future Function() action) { return isProcessing ? null @@ -163,6 +165,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { follower = Utils.numFormat(videoIntroController.userStat['follower']); followStatus = videoIntroController.followStatus; enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); + _expandableCtr = ExpandableController(initialExpanded: false); } // 收藏 @@ -216,6 +219,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { showIntroDetail() { feedBack(); isExpand.value = !(isExpand.value); + _expandableCtr.toggle(); } // 用户主页 @@ -239,6 +243,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); } + @override + void dispose() { + _expandableCtr.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final ThemeData t = Theme.of(context); @@ -256,14 +266,34 @@ class _VideoInfoState extends State with TickerProviderStateMixin { GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => showIntroDetail(), - child: Text( - widget.videoDetail!.title!, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.bold, + child: ExpandablePanel( + controller: _expandableCtr, + collapsed: Text( + widget.videoDetail!.title!, + softWrap: true, + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + expanded: Text( + widget.videoDetail!.title!, + softWrap: true, + maxLines: 4, + style: const TextStyle( + fontSize: 18, + fontWeight: FontWeight.bold, + ), + ), + theme: const ExpandableThemeData( + animationDuration: Duration(milliseconds: 300), + scrollAnimationDuration: Duration(milliseconds: 300), + crossFadePoint: 0, + fadeCurve: Curves.ease, + sizeCurve: Curves.linear, ), - maxLines: 2, - overflow: TextOverflow.ellipsis, ), ), Stack( @@ -328,12 +358,16 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), /// 视频简介 - Obx( - () => ExpandedSection( - expand: isExpand.value, - begin: 0, - end: 1, - child: IntroDetail(videoDetail: widget.videoDetail!), + ExpandablePanel( + controller: _expandableCtr, + collapsed: const SizedBox(height: 0), + expanded: IntroDetail(videoDetail: widget.videoDetail!), + theme: const ExpandableThemeData( + animationDuration: Duration(milliseconds: 300), + scrollAnimationDuration: Duration(milliseconds: 300), + crossFadePoint: 0, + fadeCurve: Curves.ease, + sizeCurve: Curves.linear, ), ), diff --git a/pubspec.lock b/pubspec.lock index 695505d7..84556c06 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -433,6 +433,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "5.0.3" + expandable: + dependency: "direct main" + description: + name: expandable + sha256: "9604d612d4d1146dafa96c6d8eec9c2ff0994658d6d09fed720ab788c7f5afc2" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.1" extended_image: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 5e19b56b..ba5976eb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -142,6 +142,8 @@ dependencies: path: 1.8.3 # 电池优化 disable_battery_optimization: ^1.1.1 + # 展开/收起 + expandable: ^5.0.1 dev_dependencies: flutter_test: From d003f864cec8076bb97fc63b99df8eee139ad06c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 30 Mar 2024 17:01:32 +0800 Subject: [PATCH 067/349] =?UTF-8?q?feat:=20=E8=AE=A2=E9=98=85=E5=8F=96?= =?UTF-8?q?=E6=B6=88=20issues=20#658?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 ++ lib/http/user.dart | 17 +++++++++++ lib/pages/subscription/controller.dart | 36 ++++++++++++++++++++++++ lib/pages/subscription/view.dart | 3 +- lib/pages/subscription/widgets/item.dart | 33 ++++++++++++++++++++-- 5 files changed, 87 insertions(+), 4 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 445f6102..fa4cc1e8 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -506,4 +506,6 @@ class Api { /// 排行榜 static const String getRankApi = "/x/web-interface/ranking/v2"; + /// 取消订阅 + static const String cancelSub = '/x/v3/fav/season/unfav'; } diff --git a/lib/http/user.dart b/lib/http/user.dart index 7d3def4e..bae61720 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -349,4 +349,21 @@ class UserHttp { return {'status': false, 'msg': res.data['message']}; } } + + // 取消订阅 + static Future cancelSub({required int seasonId}) async { + var res = await Request().post( + Api.cancelSub, + queryParameters: { + 'platform': 'web', + 'season_id': seasonId, + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return {'status': true}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/subscription/controller.dart b/lib/pages/subscription/controller.dart index bf0c593c..7be8d22c 100644 --- a/lib/pages/subscription/controller.dart +++ b/lib/pages/subscription/controller.dart @@ -46,4 +46,40 @@ class SubController extends GetxController { Future onLoad() async { querySubFolder(type: 'onload'); } + + // 取消订阅 + Future cancelSub(SubFolderItemData subFolderItem) async { + showDialog( + context: Get.context!, + builder: (context) => AlertDialog( + title: const Text('提示'), + content: const Text('确定取消订阅吗?'), + actions: [ + TextButton( + onPressed: () { + Get.back(); + }, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await UserHttp.cancelSub(seasonId: subFolderItem.id!); + if (res['status']) { + subFolderData.value.list!.remove(subFolderItem); + subFolderData.update((val) {}); + SmartDialog.showToast('取消订阅成功'); + } else { + SmartDialog.showToast(res['msg']); + } + Get.back(); + }, + child: const Text('确定'), + ), + ], + ), + ); + } } diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index 1eee4a4f..2d7d0cb5 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -58,7 +58,8 @@ class _SubPageState extends State { itemBuilder: (context, index) { return SubItem( subFolderItem: - _subController.subFolderData.value.list![index]); + _subController.subFolderData.value.list![index], + cancelSub: _subController.cancelSub); }, ), ); diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index fd08ffa5..5b2a0134 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -8,7 +8,12 @@ import '../../../models/user/sub_folder.dart'; class SubItem extends StatelessWidget { final SubFolderItemData subFolderItem; - const SubItem({super.key, required this.subFolderItem}); + final Function(SubFolderItemData) cancelSub; + const SubItem({ + super.key, + required this.subFolderItem, + required this.cancelSub, + }); @override Widget build(BuildContext context) { @@ -51,7 +56,10 @@ class SubItem extends StatelessWidget { }, ), ), - VideoContent(subFolderItem: subFolderItem) + VideoContent( + subFolderItem: subFolderItem, + cancelSub: cancelSub, + ) ], ), ); @@ -64,7 +72,8 @@ class SubItem extends StatelessWidget { class VideoContent extends StatelessWidget { final SubFolderItemData subFolderItem; - const VideoContent({super.key, required this.subFolderItem}); + final Function(SubFolderItemData)? cancelSub; + const VideoContent({super.key, required this.subFolderItem, this.cancelSub}); @override Widget build(BuildContext context) { @@ -100,6 +109,24 @@ class VideoContent extends StatelessWidget { color: Theme.of(context).colorScheme.outline, ), ), + const Spacer(), + Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + SizedBox( + height: 35, + width: 35, + child: IconButton( + onPressed: () => cancelSub?.call(subFolderItem), + style: TextButton.styleFrom( + foregroundColor: Theme.of(context).colorScheme.outline, + padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), + ), + icon: const Icon(Icons.delete_outline, size: 18), + ), + ) + ], + ) ], ), ), From af1163f6e05193e6023b217e8b85fcbacc942721 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 30 Mar 2024 22:17:37 +0800 Subject: [PATCH 068/349] =?UTF-8?q?fix:=20=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E8=BF=9B=E5=BA=A6=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/history/widgets/item.dart | 39 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index f4bd9221..39c6931d 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -258,24 +258,27 @@ class HistoryItem extends StatelessWidget { ), ), ), - Positioned( - left: 3, - right: 3, - bottom: 0, - child: ClipRRect( - borderRadius: BorderRadius.only( - bottomLeft: - Radius.circular(StyleString.imgRadius.x), - bottomRight: - Radius.circular(StyleString.imgRadius.x), - ), - child: LinearProgressIndicator( - value: videoItem.progress == -1 - ? 100 - : videoItem.progress / videoItem.duration, - ), - ), - ) + videoItem.progress != 0 + ? Positioned( + left: 3, + right: 3, + bottom: 0, + child: ClipRRect( + borderRadius: BorderRadius.only( + bottomLeft: Radius.circular( + StyleString.imgRadius.x), + bottomRight: Radius.circular( + StyleString.imgRadius.x), + ), + child: LinearProgressIndicator( + value: videoItem.progress == -1 + ? 100 + : videoItem.progress / + videoItem.duration, + ), + ), + ) + : const SizedBox() ], ), VideoContent(videoItem: videoItem, ctr: ctr) From 53b103b8535b1938569a55c8f460815404cccdbb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 31 Mar 2024 00:27:39 +0800 Subject: [PATCH 069/349] fix: utils timeFormat error --- lib/utils/utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index adcc7b5a..ecba771f 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -51,7 +51,7 @@ class Utils { } if (time < 3600) { if (time == 0) { - return time; + return '00:00'; } final int minute = time ~/ 60; final double res = time / 60; From 336feb4fda028b7ae7ea022af4628ce3b5d4adbe Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 31 Mar 2024 00:27:39 +0800 Subject: [PATCH 070/349] fix: utils timeFormat error --- lib/utils/utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index adcc7b5a..ecba771f 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -51,7 +51,7 @@ class Utils { } if (time < 3600) { if (time == 0) { - return time; + return '00:00'; } final int minute = time ~/ 60; final double res = time / 60; From 8897c4dd5b523599a3a3af75a03aac9c28ef51de Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 31 Mar 2024 11:11:50 +0800 Subject: [PATCH 071/349] =?UTF-8?q?feat:=20=E5=90=AF=E5=8A=A8=E6=97=B6?= =?UTF-8?q?=E6=B8=85=E9=99=A4=E6=97=A5=E5=BF=97=20issues=20#656?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/main.dart b/lib/main.dart index 44bb1dcd..7fdaeeb0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,6 +34,7 @@ void main() async { .then((_) async { await GStrorage.init(); await setupServiceLocator(); + clearLogs(); Request(); await Request.setCookie(); RecommendFilter(); From 469a5ec691f9f96e0a7250ee1d258dde502099a0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 1 Apr 2024 23:03:31 +0800 Subject: [PATCH 072/349] =?UTF-8?q?mod:=20=E4=B8=AA=E4=BA=BA=E4=B8=BB?= =?UTF-8?q?=E9=A1=B5=E6=A0=B7=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/member/info.dart | 5 +++ lib/pages/member/view.dart | 13 +++++-- lib/pages/member/widgets/seasons.dart | 53 ++++++++++----------------- 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/lib/models/member/info.dart b/lib/models/member/info.dart index 789131ee..83f94c54 100644 --- a/lib/models/member/info.dart +++ b/lib/models/member/info.dart @@ -47,18 +47,23 @@ class Vip { this.status, this.dueDate, this.label, + this.nicknameColor, }); int? type; int? status; int? dueDate; Map? label; + int? nicknameColor; Vip.fromJson(Map json) { type = json['type']; status = json['status']; dueDate = json['due_date']; label = json['label']; + nicknameColor = json['nickname_color'] == '' + ? null + : int.parse("0xFF${json['nickname_color'].replaceAll('#', '')}"); } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 0663e94e..c8a9f406 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -281,8 +281,8 @@ class _MemberPageState extends State future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data!; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { return Obx( () => Stack( alignment: AlignmentDirectional.center, @@ -302,7 +302,14 @@ class _MemberPageState extends State style: Theme.of(context) .textTheme .titleMedium! - .copyWith(fontWeight: FontWeight.bold), + .copyWith( + fontWeight: FontWeight.bold, + color: _memberController.memberInfo.value + .vip!.nicknameColor != + null + ? Color(_memberController.memberInfo + .value.vip!.nicknameColor!) + : null), )), const SizedBox(width: 2), if (_memberController.memberInfo.value.sex == '女') diff --git a/lib/pages/member/widgets/seasons.dart b/lib/pages/member/widgets/seasons.dart index 68c4077f..125c978f 100644 --- a/lib/pages/member/widgets/seasons.dart +++ b/lib/pages/member/widgets/seasons.dart @@ -18,45 +18,32 @@ class MemberSeasonsPanel extends StatelessWidget { itemBuilder: (context, index) { MemberSeasonsList item = data!.seasonsList![index]; return Padding( - padding: const EdgeInsets.only(bottom: 12, right: 4), + padding: const EdgeInsets.only(bottom: 12), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(bottom: 12, left: 4), - child: Row( - children: [ - Text( - item.meta!.name!, - maxLines: 1, - style: Theme.of(context).textTheme.titleSmall!, - ), - const SizedBox(width: 10), - PBadge( - stack: 'relative', - size: 'small', - text: item.meta!.total.toString(), - ), - const Spacer(), - SizedBox( - width: 35, - height: 35, - child: IconButton( - onPressed: () => Get.toNamed( - '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'), - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - icon: const Icon( - Icons.arrow_forward, - size: 20, - ), - ), - ) - ], + ListTile( + onTap: () => Get.toNamed( + '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'), + title: Text( + item.meta!.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.titleSmall!, + ), + dense: true, + leading: PBadge( + stack: 'relative', + size: 'small', + text: item.meta!.total.toString(), + ), + trailing: const Icon( + Icons.arrow_forward, + size: 20, ), ), + const SizedBox(height: 10), LayoutBuilder( builder: (context, boxConstraints) { return GridView.builder( From a20217bf3962d309b4d2a19018a01769f5734f57 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 28 Mar 2024 00:29:22 +0800 Subject: [PATCH 073/349] =?UTF-8?q?opt:=20=E5=90=91=E4=B8=8B=E6=9F=A5?= =?UTF-8?q?=E6=89=BE=E5=8F=AF=E7=94=A8=E8=A7=86=E9=A2=91=E6=B8=85=E6=99=B0?= =?UTF-8?q?=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/utils.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index ecba771f..cb7cbf25 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -208,14 +208,16 @@ class Utils { static int findClosestNumber(int target, List numbers) { int minDiff = 127; - late int closestNumber; + int closestNumber = 0; // 初始化为0,表示没有找到比目标值小的整数 try { for (int number in numbers) { - int diff = (number - target).abs(); + if (number < target) { + int diff = target - number; // 计算目标值与当前整数的差值 - if (diff < minDiff) { - minDiff = diff; - closestNumber = number; + if (diff < minDiff) { + minDiff = diff; + closestNumber = number; + } } } } catch (_) {} From e212a327635ba608a148bdfacbbe9df9e33e11de Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 31 Mar 2024 11:11:50 +0800 Subject: [PATCH 074/349] =?UTF-8?q?feat:=20=E5=90=AF=E5=8A=A8=E6=97=B6?= =?UTF-8?q?=E6=B8=85=E9=99=A4=E6=97=A5=E5=BF=97=20issues=20#656?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/main.dart b/lib/main.dart index 44bb1dcd..7fdaeeb0 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,6 +34,7 @@ void main() async { .then((_) async { await GStrorage.init(); await setupServiceLocator(); + clearLogs(); Request(); await Request.setCookie(); RecommendFilter(); From da9828a295b51f267be2464f03f070ed7968b4da Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 1 Apr 2024 23:03:31 +0800 Subject: [PATCH 075/349] =?UTF-8?q?mod:=20=E4=B8=AA=E4=BA=BA=E4=B8=BB?= =?UTF-8?q?=E9=A1=B5=E6=A0=B7=E5=BC=8F=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/member/info.dart | 5 +++ lib/pages/member/view.dart | 13 +++++-- lib/pages/member/widgets/seasons.dart | 53 ++++++++++----------------- 3 files changed, 35 insertions(+), 36 deletions(-) diff --git a/lib/models/member/info.dart b/lib/models/member/info.dart index 789131ee..83f94c54 100644 --- a/lib/models/member/info.dart +++ b/lib/models/member/info.dart @@ -47,18 +47,23 @@ class Vip { this.status, this.dueDate, this.label, + this.nicknameColor, }); int? type; int? status; int? dueDate; Map? label; + int? nicknameColor; Vip.fromJson(Map json) { type = json['type']; status = json['status']; dueDate = json['due_date']; label = json['label']; + nicknameColor = json['nickname_color'] == '' + ? null + : int.parse("0xFF${json['nickname_color'].replaceAll('#', '')}"); } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 0663e94e..c8a9f406 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -281,8 +281,8 @@ class _MemberPageState extends State future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data!; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { return Obx( () => Stack( alignment: AlignmentDirectional.center, @@ -302,7 +302,14 @@ class _MemberPageState extends State style: Theme.of(context) .textTheme .titleMedium! - .copyWith(fontWeight: FontWeight.bold), + .copyWith( + fontWeight: FontWeight.bold, + color: _memberController.memberInfo.value + .vip!.nicknameColor != + null + ? Color(_memberController.memberInfo + .value.vip!.nicknameColor!) + : null), )), const SizedBox(width: 2), if (_memberController.memberInfo.value.sex == '女') diff --git a/lib/pages/member/widgets/seasons.dart b/lib/pages/member/widgets/seasons.dart index 68c4077f..125c978f 100644 --- a/lib/pages/member/widgets/seasons.dart +++ b/lib/pages/member/widgets/seasons.dart @@ -18,45 +18,32 @@ class MemberSeasonsPanel extends StatelessWidget { itemBuilder: (context, index) { MemberSeasonsList item = data!.seasonsList![index]; return Padding( - padding: const EdgeInsets.only(bottom: 12, right: 4), + padding: const EdgeInsets.only(bottom: 12), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, children: [ - Padding( - padding: const EdgeInsets.only(bottom: 12, left: 4), - child: Row( - children: [ - Text( - item.meta!.name!, - maxLines: 1, - style: Theme.of(context).textTheme.titleSmall!, - ), - const SizedBox(width: 10), - PBadge( - stack: 'relative', - size: 'small', - text: item.meta!.total.toString(), - ), - const Spacer(), - SizedBox( - width: 35, - height: 35, - child: IconButton( - onPressed: () => Get.toNamed( - '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'), - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - icon: const Icon( - Icons.arrow_forward, - size: 20, - ), - ), - ) - ], + ListTile( + onTap: () => Get.toNamed( + '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'), + title: Text( + item.meta!.name!, + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context).textTheme.titleSmall!, + ), + dense: true, + leading: PBadge( + stack: 'relative', + size: 'small', + text: item.meta!.total.toString(), + ), + trailing: const Icon( + Icons.arrow_forward, + size: 20, ), ), + const SizedBox(height: 10), LayoutBuilder( builder: (context, boxConstraints) { return GridView.builder( From c6de1fa95a80a63734f0a1b39b46555557796208 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 1 Apr 2024 23:55:35 +0800 Subject: [PATCH 076/349] =?UTF-8?q?mod:=20=E8=AF=84=E8=AE=BAb23.tv?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E5=8C=B9=E9=85=8D=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 40 +++++++++++++++++-- 1 file changed, 36 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 e79b6159..50fe20d4 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -525,14 +525,18 @@ InlineSpan buildContent( if (jumpUrlKeysList.isNotEmpty) { patternStr += '|${jumpUrlKeysList.join('|')}'; } + RegExp bv23Regex = RegExp(r'https://b23\.tv/[a-zA-Z0-9]{7}'); final RegExp pattern = RegExp(patternStr); List matchedStrs = []; void addPlainTextSpan(str) { - spanChilds.add(TextSpan( + spanChilds.add( + TextSpan( text: str, recognizer: TapGestureRecognizer() ..onTap = () => - replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem))); + replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem), + ), + ); } // 分割文本并处理每个部分 @@ -734,8 +738,36 @@ InlineSpan buildContent( return ''; }, onNonMatch: (String nonMatchStr) { - addPlainTextSpan(nonMatchStr); - return nonMatchStr; + return nonMatchStr.splitMapJoin( + bv23Regex, + onMatch: (Match match) { + String matchStr = match[0]!; + spanChilds.add( + TextSpan( + text: ' $matchStr ', + style: isVideoPage + ? TextStyle( + color: Theme.of(context).colorScheme.primary, + ) + : null, + recognizer: TapGestureRecognizer() + ..onTap = () => Get.toNamed( + '/webview', + parameters: { + 'url': matchStr, + 'type': 'url', + 'pageTitle': matchStr + }, + ), + ), + ); + return ''; + }, + onNonMatch: (String nonMatchOtherStr) { + addPlainTextSpan(nonMatchOtherStr); + return nonMatchOtherStr; + }, + ); }, ); From c0f3b4f3a2052eb6b4aa4f73022f6f884d432d3e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 2 Apr 2024 23:13:15 +0800 Subject: [PATCH 077/349] =?UTF-8?q?fix:=20=E5=85=A8=E5=B1=8F=E6=97=B6?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=BC=B9=E5=B9=95=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/widgets/header_control.dart | 132 ++++++++++++++++-- 1 file changed, 123 insertions(+), 9 deletions(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 858ca2df..9448a62a 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -17,6 +17,7 @@ import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/services/shutdown_timer_service.dart'; +import '../../../../http/danmaku.dart'; import '../../../../models/common/search_type.dart'; import '../../../../models/video_detail_res.dart'; import '../introduction/index.dart'; @@ -52,7 +53,7 @@ class _HeaderControlState extends State { final Box videoStorage = GStrorage.video; late List speedsList; double buttonSpace = 8; - bool showTitle = false; + RxBool isFullScreen = false.obs; late String heroTag; late VideoIntroController videoIntroController; late VideoDetailData videoDetail; @@ -69,13 +70,8 @@ class _HeaderControlState extends State { } void fullScreenStatusListener() { - widget.videoDetailCtr!.plPlayerController.isFullScreen - .listen((bool isFullScreen) { - if (isFullScreen) { - showTitle = true; - } else { - showTitle = false; - } + widget.videoDetailCtr!.plPlayerController.isFullScreen.listen((bool val) { + isFullScreen.value = val; /// TODO setState() called after dispose() if (mounted) { @@ -218,6 +214,87 @@ class _HeaderControlState extends State { ); } + /// 发送弹幕 + void showShootDanmakuSheet() { + final TextEditingController textController = TextEditingController(); + bool isSending = false; // 追踪是否正在发送 + showDialog( + context: Get.context!, + builder: (BuildContext context) { + // TODO: 支持更多类型和颜色的弹幕 + return AlertDialog( + title: const Text('发送弹幕(测试)'), + content: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return TextField( + controller: textController, + ); + }), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return TextButton( + onPressed: isSending + ? null + : () async { + final String msg = textController.text; + if (msg.isEmpty) { + SmartDialog.showToast('弹幕内容不能为空'); + return; + } else if (msg.length > 100) { + SmartDialog.showToast('弹幕内容不能超过100个字符'); + return; + } + setState(() { + isSending = true; // 开始发送,更新状态 + }); + //修改按钮文字 + final dynamic res = await DanmakaHttp.shootDanmaku( + oid: widget.videoDetailCtr!.cid.value, + msg: textController.text, + bvid: widget.videoDetailCtr!.bvid, + progress: + widget.controller!.position.value.inMilliseconds, + type: 1, + ); + setState(() { + isSending = false; // 发送结束,更新状态 + }); + if (res['status']) { + SmartDialog.showToast('发送成功'); + // 发送成功,自动预览该弹幕,避免重新请求 + // TODO: 暂停状态下预览弹幕仍会移动与计时,可考虑添加到dmSegList或其他方式实现 + widget.controller!.danmakuController!.addItems([ + DanmakuItem( + msg, + color: Colors.white, + time: widget + .controller!.position.value.inMilliseconds, + type: DanmakuItemType.scroll, + isSend: true, + ) + ]); + Get.back(); + } else { + SmartDialog.showToast('发送失败,错误信息为${res['msg']}'); + } + }, + child: Text(isSending ? '发送中...' : '发送'), + ); + }) + ], + ); + }, + ); + } + /// 定时关闭 void scheduleExit() async { const List scheduleTimeChoices = [ @@ -1029,7 +1106,7 @@ class _HeaderControlState extends State { }, ), SizedBox(width: buttonSpace), - if (showTitle && + if (isFullScreen.value && isLandscape && widget.videoType == SearchType.video) ...[ Column( @@ -1081,6 +1158,43 @@ class _HeaderControlState extends State { // ), // fuc: () => _.screenshot(), // ), + if (isFullScreen.value) ...[ + SizedBox( + width: 56, + height: 34, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => showShootDanmakuSheet(), + child: const Text( + '发弹幕', + style: textStyle, + ), + ), + ), + SizedBox( + width: 34, + height: 34, + child: Obx( + () => IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + _.isOpenDanmu.value = !_.isOpenDanmu.value; + }, + icon: Icon( + _.isOpenDanmu.value + ? Icons.subtitles_outlined + : Icons.subtitles_off_outlined, + size: 19, + color: Colors.white, + ), + ), + ), + ), + ], SizedBox(width: buttonSpace), if (Platform.isAndroid) ...[ SizedBox( From 74f6b0ad1e3aaf0d80b917c68fddb0f853755dc0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 2 Apr 2024 23:58:27 +0800 Subject: [PATCH 078/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E6=A1=86?= =?UTF-8?q?=E5=88=87=E6=8D=A2action=E6=97=B6=E9=AB=98=E5=BA=A6=E8=B7=B3?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_new/view.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 05351411..a94b6071 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -142,9 +142,10 @@ class _VideoReplyNewDialogState extends State @override Widget build(BuildContext context) { - double keyboardHeight = EdgeInsets.fromViewPadding( + double _keyboardHeight = EdgeInsets.fromViewPadding( View.of(context).viewInsets, View.of(context).devicePixelRatio) .bottom; + print('_keyboardHeight: $_keyboardHeight'); return Container( clipBehavior: Clip.hardEdge, decoration: BoxDecoration( @@ -235,7 +236,11 @@ class _VideoReplyNewDialogState extends State duration: const Duration(milliseconds: 300), child: SizedBox( width: double.infinity, - height: toolbarType == 'input' ? keyboardHeight : emoteHeight, + height: toolbarType == 'input' + ? (_keyboardHeight > keyboardHeight + ? _keyboardHeight + : keyboardHeight) + : emoteHeight, child: EmotePanel( onChoose: (package, emote) => onChooseEmote(package, emote), ), From 2ba72e3792ef32f45ea9d1ac6568e584e7e22243 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 3 Apr 2024 23:59:24 +0800 Subject: [PATCH 079/349] Update README.md --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 237bd07f..470e9a35 100644 --- a/README.md +++ b/README.md @@ -26,13 +26,13 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码 ```bash -[✓] Flutter (Channel stable, 3.16.4, on macOS 14.1.2 23B92 darwin-arm64, locale +[✓] Flutter (Channel stable, 3.16.5, on macOS 14.1.2 23B92 darwin-arm64, locale zh-Hans-CN) [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [✓] Xcode - develop for iOS and macOS (Xcode 15.1) [✓] Chrome - develop for the web [✓] Android Studio (version 2022.3) -[✓] VS Code (version 1.85.1) +[✓] VS Code (version 1.87.2) [✓] Connected device (3 available) [✓] Network resources @@ -44,6 +44,9 @@ Xcode 13.4 不支持**auto_orientation**,请注释相关代码 ## 技术交流 Telegram: https://t.me/+lm_oOVmF0RJiODk1 + +Tg Beta版本:@PiliPala_Beta + QQ频道: https://pd.qq.com/s/365esodk3 From b58df720fd7d8d45836662f2f39ecc42f3300c97 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 4 Apr 2024 23:41:16 +0800 Subject: [PATCH 080/349] =?UTF-8?q?mod:=20=E4=BC=98=E5=8C=96selectDialog?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/widgets/select_dialog.dart | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/pages/setting/widgets/select_dialog.dart b/lib/pages/setting/widgets/select_dialog.dart index 72119755..50229f9e 100644 --- a/lib/pages/setting/widgets/select_dialog.dart +++ b/lib/pages/setting/widgets/select_dialog.dart @@ -44,6 +44,7 @@ class _SelectDialogState extends State> { setState(() { _tempValue = value as T; }); + Navigator.pop(context, _tempValue); }, ), ] @@ -51,19 +52,6 @@ class _SelectDialogState extends State> { ), ); }), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () => Navigator.pop(context, _tempValue), - child: const Text('确定'), - ) - ], ); } } From 247f6ee0a7c077342e09c07cf7ac6eb22ab097fe Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 4 Apr 2024 23:59:53 +0800 Subject: [PATCH 081/349] mod: findClosestNumber --- lib/pages/video/detail/controller.dart | 10 +++++----- lib/utils/utils.dart | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 5c4ac14b..c1a96132 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -91,7 +91,7 @@ class VideoDetailController extends GetxController late bool enableCDN; late int? cacheVideoQa; late String cacheDecode; - late int cacheAudioQa; + late int defaultAudioQa; PersistentBottomSheetController? replyReplyBottomSheetCtr; RxList subtitleContents = @@ -146,7 +146,7 @@ class VideoDetailController extends GetxController // 预设的解码格式 cacheDecode = setting.get(SettingBoxKey.defaultDecode, defaultValue: VideoDecodeFormats.values.last.code); - cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa, + defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.hiRes.code); oid.value = IdUtils.bv2av(Get.parameters['bvid']!); getSubtitle(); @@ -353,9 +353,9 @@ class VideoDetailController extends GetxController if (audiosList.isNotEmpty) { final List numbers = audiosList.map((map) => map.id!).toList(); - int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers); - if (!numbers.contains(cacheAudioQa) && - numbers.any((e) => e > cacheAudioQa)) { + int closestNumber = Utils.findClosestNumber(defaultAudioQa, numbers); + if (!numbers.contains(defaultAudioQa) && + numbers.any((e) => e > defaultAudioQa)) { closestNumber = 30280; } firstAudio = audiosList.firstWhere((e) => e.id == closestNumber); diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index cb7cbf25..a7273f05 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -209,6 +209,8 @@ class Utils { static int findClosestNumber(int target, List numbers) { int minDiff = 127; int closestNumber = 0; // 初始化为0,表示没有找到比目标值小的整数 + + // 向下查找 try { for (int number in numbers) { if (number < target) { @@ -221,6 +223,20 @@ class Utils { } } } catch (_) {} + + // 向上查找 + if (closestNumber == 0) { + try { + for (int number in numbers) { + int diff = (number - target).abs(); + + if (diff < minDiff) { + minDiff = diff; + closestNumber = number; + } + } + } catch (_) {} + } return closestNumber; } From 5500a58c32a23dfe5f684e6ecf5fd6a2b13d5b05 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 4 Apr 2024 23:41:16 +0800 Subject: [PATCH 082/349] =?UTF-8?q?mod:=20=E4=BC=98=E5=8C=96selectDialog?= =?UTF-8?q?=E9=80=89=E6=8B=A9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/widgets/select_dialog.dart | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/lib/pages/setting/widgets/select_dialog.dart b/lib/pages/setting/widgets/select_dialog.dart index 72119755..50229f9e 100644 --- a/lib/pages/setting/widgets/select_dialog.dart +++ b/lib/pages/setting/widgets/select_dialog.dart @@ -44,6 +44,7 @@ class _SelectDialogState extends State> { setState(() { _tempValue = value as T; }); + Navigator.pop(context, _tempValue); }, ), ] @@ -51,19 +52,6 @@ class _SelectDialogState extends State> { ), ); }), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context), - child: Text( - '取消', - style: TextStyle(color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () => Navigator.pop(context, _tempValue), - child: const Text('确定'), - ) - ], ); } } From ec7762644b25fc6fdd4e6c3666391f390daacdac Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 5 Apr 2024 00:02:25 +0800 Subject: [PATCH 083/349] mod: findClosestNumber --- lib/pages/video/detail/controller.dart | 10 +++++----- lib/utils/utils.dart | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 5c4ac14b..c1a96132 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -91,7 +91,7 @@ class VideoDetailController extends GetxController late bool enableCDN; late int? cacheVideoQa; late String cacheDecode; - late int cacheAudioQa; + late int defaultAudioQa; PersistentBottomSheetController? replyReplyBottomSheetCtr; RxList subtitleContents = @@ -146,7 +146,7 @@ class VideoDetailController extends GetxController // 预设的解码格式 cacheDecode = setting.get(SettingBoxKey.defaultDecode, defaultValue: VideoDecodeFormats.values.last.code); - cacheAudioQa = setting.get(SettingBoxKey.defaultAudioQa, + defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.hiRes.code); oid.value = IdUtils.bv2av(Get.parameters['bvid']!); getSubtitle(); @@ -353,9 +353,9 @@ class VideoDetailController extends GetxController if (audiosList.isNotEmpty) { final List numbers = audiosList.map((map) => map.id!).toList(); - int closestNumber = Utils.findClosestNumber(cacheAudioQa, numbers); - if (!numbers.contains(cacheAudioQa) && - numbers.any((e) => e > cacheAudioQa)) { + int closestNumber = Utils.findClosestNumber(defaultAudioQa, numbers); + if (!numbers.contains(defaultAudioQa) && + numbers.any((e) => e > defaultAudioQa)) { closestNumber = 30280; } firstAudio = audiosList.firstWhere((e) => e.id == closestNumber); diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index cb7cbf25..a7273f05 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -209,6 +209,8 @@ class Utils { static int findClosestNumber(int target, List numbers) { int minDiff = 127; int closestNumber = 0; // 初始化为0,表示没有找到比目标值小的整数 + + // 向下查找 try { for (int number in numbers) { if (number < target) { @@ -221,6 +223,20 @@ class Utils { } } } catch (_) {} + + // 向上查找 + if (closestNumber == 0) { + try { + for (int number in numbers) { + int diff = (number - target).abs(); + + if (diff < minDiff) { + minDiff = diff; + closestNumber = number; + } + } + } catch (_) {} + } return closestNumber; } From 0d0e0b9adb184ae61a4c2e73b60b85e31f3efe94 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 5 Apr 2024 22:19:45 +0800 Subject: [PATCH 084/349] =?UTF-8?q?fix:=20=E5=85=B3=E6=B3=A8=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E7=8A=B6=E6=80=81=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/widgets/profile.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pages/member/widgets/profile.dart b/lib/pages/member/widgets/profile.dart index 4bf7b7db..a708a35e 100644 --- a/lib/pages/member/widgets/profile.dart +++ b/lib/pages/member/widgets/profile.dart @@ -180,7 +180,9 @@ class ProfilePanel extends StatelessWidget { Obx( () => Expanded( child: TextButton( - onPressed: () => ctr.actionRelationMod(), + onPressed: () => loadingStatus + ? null + : ctr.actionRelationMod(), style: TextButton.styleFrom( foregroundColor: ctr.attribute.value == -1 ? Colors.transparent From 99645c7b4aee81a7165a8708ed5e296658a660c1 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 5 Apr 2024 23:20:44 +0800 Subject: [PATCH 085/349] fix: seekTo multiple trigger --- lib/plugin/pl_player/controller.dart | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index b385fca8..f936526b 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -101,7 +101,7 @@ class PlPlayerController { bool _isFirstTime = true; Timer? _timer; - Timer? _timerForSeek; + late Timer? _timerForSeek; Timer? _timerForVolume; Timer? _timerForShowingVolume; Timer? _timerForGettingVolume; @@ -646,9 +646,6 @@ class PlPlayerController { /// 跳转至指定位置 Future seekTo(Duration position, {type = 'seek'}) async { - // if (position >= duration.value) { - // position = duration.value - const Duration(milliseconds: 100); - // } if (position < Duration.zero) { position = Duration.zero; } @@ -661,21 +658,13 @@ class PlPlayerController { await _videoPlayerController?.stream.buffer.first; } await _videoPlayerController?.seek(position); - // if (playerStatus.stopped) { - // play(); - // } } else { - print('seek duration else'); _timerForSeek?.cancel(); - _timerForSeek = + _timerForSeek ??= Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { - //_timerForSeek = null; if (duration.value.inSeconds != 0) { await _videoPlayerController!.stream.buffer.first; await _videoPlayerController?.seek(position); - // if (playerStatus.status.value == PlayerStatus.paused) { - // play(); - // } t.cancel(); _timerForSeek = null; } From a5494484aefe83fc4cb122ec1dcec9c72b9c67c8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Apr 2024 11:30:53 +0800 Subject: [PATCH 086/349] =?UTF-8?q?mod:=20=E6=9B=B4=E6=96=B0=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=99=A8=E5=BA=95=E6=A0=8F=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 9 ++ lib/pages/video/detail/view.dart | 2 + lib/plugin/pl_player/view.dart | 9 +- .../pl_player/widgets/bottom_control.dart | 87 +------------------ 4 files changed, 18 insertions(+), 89 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index fe870873..227d6b84 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -21,6 +21,7 @@ import 'package:pilipala/utils/video_utils.dart'; import 'package:screen_brightness/screen_brightness.dart'; import '../../../http/danmaku.dart'; +import '../../../plugin/pl_player/models/bottom_control_type.dart'; import '../../../utils/id_utils.dart'; import 'widgets/header_control.dart'; @@ -94,6 +95,14 @@ class VideoDetailController extends GetxController PersistentBottomSheetController? replyReplyBottomSheetCtr; late bool enableRelatedVideo; + List subtitles = []; + RxList bottomList = [ + BottomControlType.playOrPause, + BottomControlType.time, + BottomControlType.space, + BottomControlType.fit, + BottomControlType.fullscreen, + ].obs; @override void onInit() { diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a403e298..5ebbf62e 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -24,6 +24,7 @@ import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/storage.dart'; +import '../../../plugin/pl_player/models/bottom_control_type.dart'; import '../../../services/shutdown_timer_service.dart'; import 'widgets/app_bar.dart'; @@ -298,6 +299,7 @@ class _VideoDetailPageState extends State playerController: plPlayerController!, ), ), + bottomList: vdCtr.bottomList, ); }, ); diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 90861204..d32fc8e4 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -214,8 +214,8 @@ class _PLVideoPlayerState extends State /// 上一集 BottomControlType.pre: ComBtn( icon: const Icon( - Icons.skip_previous_outlined, - size: 15, + Icons.skip_previous_rounded, + size: 21, color: Colors.white, ), fuc: () {}, @@ -229,8 +229,8 @@ class _PLVideoPlayerState extends State /// 下一集 BottomControlType.next: ComBtn( icon: const Icon( - Icons.last_page_outlined, - size: 15, + Icons.skip_next_rounded, + size: 21, color: Colors.white, ), fuc: () {}, @@ -239,6 +239,7 @@ class _PLVideoPlayerState extends State /// 时间进度 BottomControlType.time: Row( children: [ + const SizedBox(width: 8), Obx(() { return Text( _.durationSeconds.value >= 3600 diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index ebb71b54..35e7792a 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -68,91 +68,8 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { ); }, ), - Row( - children: [...buildBottomControl!], - ), - // Row( - // children: [ - // PlayOrPauseButton( - // controller: _, - // ), - // const SizedBox(width: 4), - // // 播放时间 - // Obx(() { - // return Text( - // _.durationSeconds.value >= 3600 - // ? printDurationWithHours( - // Duration(seconds: _.positionSeconds.value)) - // : printDuration( - // Duration(seconds: _.positionSeconds.value)), - // style: textStyle, - // ); - // }), - // const SizedBox(width: 2), - // const Text('/', style: textStyle), - // const SizedBox(width: 2), - // Obx( - // () => Text( - // _.durationSeconds.value >= 3600 - // ? printDurationWithHours( - // Duration(seconds: _.durationSeconds.value)) - // : printDuration( - // Duration(seconds: _.durationSeconds.value)), - // style: textStyle, - // ), - // ), - // const Spacer(), - // // 倍速 - // // Obx( - // // () => SizedBox( - // // width: 45, - // // height: 34, - // // child: TextButton( - // // style: ButtonStyle( - // // padding: MaterialStateProperty.all(EdgeInsets.zero), - // // ), - // // onPressed: () { - // // _.togglePlaybackSpeed(); - // // }, - // // child: Text( - // // '${_.playbackSpeed.toString()}X', - // // style: textStyle, - // // ), - // // ), - // // ), - // // ), - // SizedBox( - // height: 30, - // child: TextButton( - // onPressed: () => _.toggleVideoFit(), - // style: ButtonStyle( - // padding: MaterialStateProperty.all(EdgeInsets.zero), - // ), - // child: Obx( - // () => Text( - // _.videoFitDEsc.value, - // style: const TextStyle(color: Colors.white, fontSize: 13), - // ), - // ), - // ), - // ), - // const SizedBox(width: 10), - // // 全屏 - // Obx( - // () => ComBtn( - // icon: Icon( - // _.isFullScreen.value - // ? FontAwesomeIcons.compress - // : FontAwesomeIcons.expand, - // size: 15, - // color: Colors.white, - // ), - // fuc: () => triggerFullScreen!(), - // ), - // ), - // ], - // ), - const SizedBox(height: 12), + Row(children: [...buildBottomControl!]), + const SizedBox(height: 10), ], ), ); From d4212f88c52704960b384e8565aae81ace88e47f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Apr 2024 15:26:53 +0800 Subject: [PATCH 087/349] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BF=E3=80=81?= =?UTF-8?q?=E7=95=AA=E5=89=A7=E5=90=88=E9=9B=86=E6=95=B4=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/pages_bottom_sheet.dart | 135 +++++++++ lib/models/common/video_episode_type.dart | 5 + lib/pages/bangumi/introduction/view.dart | 5 +- lib/pages/bangumi/widgets/bangumi_panel.dart | 227 +++++---------- .../video/detail/introduction/controller.dart | 10 +- lib/pages/video/detail/introduction/view.dart | 25 +- .../detail/introduction/widgets/page.dart | 258 ------------------ .../introduction/widgets/page_panel.dart | 184 +++++++++++++ .../{season.dart => season_panel.dart} | 96 ++----- lib/pages/video/detail/view.dart | 1 + .../video/detail/widgets/header_control.dart | 3 +- .../video/detail/widgets/right_drawer.dart | 19 ++ 12 files changed, 458 insertions(+), 510 deletions(-) create mode 100644 lib/common/pages_bottom_sheet.dart create mode 100644 lib/models/common/video_episode_type.dart delete mode 100644 lib/pages/video/detail/introduction/widgets/page.dart create mode 100644 lib/pages/video/detail/introduction/widgets/page_panel.dart rename lib/pages/video/detail/introduction/widgets/{season.dart => season_panel.dart} (56%) create mode 100644 lib/pages/video/detail/widgets/right_drawer.dart diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart new file mode 100644 index 00000000..b31ec4b1 --- /dev/null +++ b/lib/common/pages_bottom_sheet.dart @@ -0,0 +1,135 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import '../models/common/video_episode_type.dart'; + +class EpisodeBottomSheet { + final List episodes; + final int currentCid; + final dynamic dataType; + final BuildContext context; + final Function changeFucCall; + final int? cid; + final double? sheetHeight; + + EpisodeBottomSheet({ + required this.episodes, + required this.currentCid, + required this.dataType, + required this.context, + required this.changeFucCall, + this.cid, + this.sheetHeight, + }); + + Widget buildEpisodeListItem( + dynamic episode, + int index, + bool isCurrentIndex, + ) { + Color primary = Theme.of(context).colorScheme.primary; + Color onSurface = Theme.of(context).colorScheme.onSurface; + + String title = ''; + switch (dataType) { + case VideoEpidoesType.videoEpisode: + title = episode.title; + break; + case VideoEpidoesType.videoPart: + title = episode.pagePart; + break; + case VideoEpidoesType.bangumiEpisode: + title = '第${episode.title}话 ${episode.longTitle!}'; + break; + } + return ListTile( + onTap: () { + SmartDialog.showToast('切换至「$title」'); + changeFucCall.call(episode, index); + }, + dense: false, + leading: isCurrentIndex + ? Image.asset( + 'assets/images/live.gif', + color: primary, + height: 12, + ) + : null, + title: Text( + title, + style: TextStyle( + fontSize: 14, + color: isCurrentIndex ? primary : onSurface, + ), + ), + ); + } + + Widget buildTitle() { + return AppBar( + toolbarHeight: 45, + automaticallyImplyLeading: false, + centerTitle: false, + title: Text( + '合集(${episodes.length})', + style: Theme.of(context).textTheme.titleMedium, + ), + actions: [ + IconButton( + icon: const Icon(Icons.close, size: 20), + onPressed: () => Navigator.pop(context), + ), + const SizedBox(width: 14), + ], + ); + } + + /// The [BuildContext] of the widget that calls the bottom sheet. + PersistentBottomSheetController show(BuildContext context) { + final ItemScrollController itemScrollController = ItemScrollController(); + int currentIndex = episodes.indexWhere((dynamic e) => e.cid == currentCid); + final PersistentBottomSheetController btmSheetCtr = showBottomSheet( + context: context, + builder: (BuildContext context) { + return StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + WidgetsBinding.instance.addPostFrameCallback((_) { + itemScrollController.jumpTo(index: currentIndex); + }); + return Container( + height: sheetHeight, + color: Theme.of(context).colorScheme.background, + child: Column( + children: [ + buildTitle(), + Expanded( + child: Material( + child: ScrollablePositionedList.builder( + itemScrollController: itemScrollController, + itemCount: episodes.length + 1, + itemBuilder: (BuildContext context, int index) { + bool isLastItem = index == episodes.length; + bool isCurrentIndex = currentIndex == index; + return isLastItem + ? SizedBox( + height: + MediaQuery.of(context).padding.bottom + 20, + ) + : buildEpisodeListItem( + episodes[index], + index, + isCurrentIndex, + ); + }, + ), + ), + ), + ], + ), + ); + }); + }, + ); + return btmSheetCtr; + } +} diff --git a/lib/models/common/video_episode_type.dart b/lib/models/common/video_episode_type.dart new file mode 100644 index 00000000..4875438f --- /dev/null +++ b/lib/models/common/video_episode_type.dart @@ -0,0 +1,5 @@ +enum VideoEpidoesType { + videoEpisode, + videoPart, + bangumiEpisode, +} diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 6255ffda..13db7432 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -138,6 +138,9 @@ class _BangumiInfoState extends State { cid = widget.cid!; videoDetailCtr.cid.listen((p0) { cid = p0; + if (!mounted) { + return; + } setState(() {}); }); } @@ -317,7 +320,7 @@ class _BangumiInfoState extends State { if (widget.bangumiDetail!.episodes!.isNotEmpty) ...[ BangumiPanel( pages: widget.bangumiDetail!.episodes!, - cid: cid ?? widget.bangumiDetail!.episodes!.first.cid, + cid: cid! ?? widget.bangumiDetail!.episodes!.first.cid!, sheetHeight: sheetHeight, changeFuc: (bvid, cid, aid) => bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid), diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 05fd814c..988f8d4f 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -6,19 +8,21 @@ import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import '../../../common/pages_bottom_sheet.dart'; +import '../../../models/common/video_episode_type.dart'; class BangumiPanel extends StatefulWidget { const BangumiPanel({ super.key, required this.pages, - this.cid, + required this.cid, this.sheetHeight, this.changeFuc, this.bangumiDetail, }); final List pages; - final int? cid; + final int cid; final double? sheetHeight; final Function? changeFuc; final BangumiInfoModel? bangumiDetail; @@ -28,9 +32,8 @@ class BangumiPanel extends StatefulWidget { } class _BangumiPanelState extends State { - late int currentIndex; + late RxInt currentIndex = (-1).obs; final ScrollController listViewScrollCtr = ScrollController(); - final ScrollController listViewScrollCtr_2 = ScrollController(); Box userInfoCache = GStrorage.userInfo; dynamic userInfo; // 默认未开通 @@ -39,169 +42,68 @@ class _BangumiPanelState extends State { String heroTag = Get.arguments['heroTag']; late final VideoDetailController videoDetailCtr; final ItemScrollController itemScrollController = ItemScrollController(); + late PersistentBottomSheetController? _bottomSheetController; @override void initState() { super.initState(); - cid = widget.cid!; - currentIndex = widget.pages.indexWhere((e) => e.cid == cid); + cid = widget.cid; + videoDetailCtr = Get.find(tag: heroTag); + currentIndex.value = + widget.pages.indexWhere((EpisodeItem e) => e.cid == cid); scrollToIndex(); + videoDetailCtr.cid.listen((int p0) { + cid = p0; + currentIndex.value = + widget.pages.indexWhere((EpisodeItem e) => e.cid == cid); + scrollToIndex(); + }); + + /// 获取大会员状态 userInfo = userInfoCache.get('userInfoCache'); if (userInfo != null) { vipStatus = userInfo.vipStatus; } - videoDetailCtr = Get.find(tag: heroTag); - - videoDetailCtr.cid.listen((int p0) { - cid = p0; - setState(() {}); - currentIndex = widget.pages.indexWhere((EpisodeItem e) => e.cid == cid); - scrollToIndex(); - }); } @override void dispose() { listViewScrollCtr.dispose(); - listViewScrollCtr_2.dispose(); 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( - '第${page.title}话 ${page.longTitle!}', - style: TextStyle( - fontSize: 14, - color: isCurrentIndex - ? primary - : Theme.of(context).colorScheme.onSurface, - ), - ), - trailing: page.badge != null - ? Text( - page.badge!, - style: TextStyle( - color: Theme.of(context).colorScheme.primary, - ), - ) - : const SizedBox(), - ); - } - - void showBangumiPanel() { - showBottomSheet( - context: context, - builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - WidgetsBinding.instance.addPostFrameCallback((_) async { - // await Future.delayed(const Duration(milliseconds: 200)); - // listViewScrollCtr_2.animateTo(currentIndex * 56, - // duration: const Duration(milliseconds: 500), - // curve: Curves.easeInOut); - itemScrollController.jumpTo(index: currentIndex); - }); - // 在这里使用 setState 更新状态 - return Container( - height: widget.sheetHeight, - color: Theme.of(context).colorScheme.background, - child: Column( - children: [ - AppBar( - toolbarHeight: 45, - automaticallyImplyLeading: false, - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '合集(${widget.pages.length})', - style: Theme.of(context).textTheme.titleMedium, - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: () => Navigator.pop(context), - ), - ], - ), - titleSpacing: 10, - ), - Expanded( - child: Material( - child: ScrollablePositionedList.builder( - itemCount: widget.pages.length + 1, - itemBuilder: (BuildContext context, int index) { - bool isLastItem = index == widget.pages.length; - bool isCurrentIndex = currentIndex == index; - return isLastItem - ? SizedBox( - height: - MediaQuery.of(context).padding.bottom + - 20, - ) - : buildPageListItem( - widget.pages[index], - index, - isCurrentIndex, - ); - }, - itemScrollController: itemScrollController, - ), - ), - ), - ], - ), - ); - }, - ); - }, - ); - } - void changeFucCall(item, i) async { if (item.badge != null && item.badge == '会员' && vipStatus != 1) { SmartDialog.showToast('需要大会员'); return; } - await widget.changeFuc!( + widget.changeFuc?.call( item.bvid, item.cid, item.aid, ); + _bottomSheetController?.close(); currentIndex = i; - setState(() {}); scrollToIndex(); } void scrollToIndex() { WidgetsBinding.instance.addPostFrameCallback((_) { // 在回调函数中获取更新后的状态 - listViewScrollCtr.animateTo(currentIndex * 150, - duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); + final double offset = min((currentIndex * 150) - 75, + listViewScrollCtr.position.maxScrollExtent); + listViewScrollCtr.animateTo( + offset, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); }); } @override Widget build(BuildContext context) { + Color primary = Theme.of(context).colorScheme.primary; + Color onSurface = Theme.of(context).colorScheme.onSurface; return Column( children: [ Padding( @@ -211,12 +113,14 @@ class _BangumiPanelState extends State { children: [ const Text('选集 '), Expanded( - child: Text( - ' 正在播放:${widget.pages[currentIndex].longTitle}', - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.outline, + child: Obx( + () => Text( + ' 正在播放:${widget.pages[currentIndex.value].longTitle}', + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.outline, + ), ), ), ), @@ -227,7 +131,16 @@ class _BangumiPanelState extends State { style: ButtonStyle( padding: MaterialStateProperty.all(EdgeInsets.zero), ), - onPressed: () => showBangumiPanel(), + onPressed: () { + _bottomSheetController = EpisodeBottomSheet( + currentCid: cid, + episodes: widget.pages, + changeFucCall: changeFucCall, + sheetHeight: widget.sheetHeight, + dataType: VideoEpidoesType.bangumiEpisode, + context: context, + ).show(context); + }, child: Text( '${widget.bangumiDetail!.newEp!['desc']}', style: const TextStyle(fontSize: 13), @@ -245,6 +158,8 @@ class _BangumiPanelState extends State { itemCount: widget.pages.length, itemExtent: 150, itemBuilder: (BuildContext context, int i) { + var page = widget.pages[i]; + bool isSelected = i == currentIndex.value; return Container( width: 150, margin: const EdgeInsets.only(right: 10), @@ -253,42 +168,37 @@ class _BangumiPanelState extends State { borderRadius: BorderRadius.circular(6), clipBehavior: Clip.hardEdge, child: InkWell( - onTap: () => changeFucCall(widget.pages[i], i), + onTap: () => changeFucCall(page, i), child: Padding( padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 10), + vertical: 8, + horizontal: 10, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - if (i == currentIndex) ...[ - Image.asset( - 'assets/images/live.png', - color: Theme.of(context).colorScheme.primary, - height: 12, - ), + if (isSelected) ...[ + Image.asset('assets/images/live.png', + color: primary, height: 12), const SizedBox(width: 6) ], Text( '第${i + 1}话', style: TextStyle( - fontSize: 13, - color: i == currentIndex - ? Theme.of(context).colorScheme.primary - : Theme.of(context) - .colorScheme - .onSurface), + fontSize: 13, + color: isSelected ? primary : onSurface, + ), ), const SizedBox(width: 2), - if (widget.pages[i].badge != null) ...[ + if (page.badge != null) ...[ const Spacer(), Text( - widget.pages[i].badge!, + page.badge!, style: TextStyle( fontSize: 12, - color: - Theme.of(context).colorScheme.primary, + color: primary, ), ), ] @@ -296,13 +206,12 @@ class _BangumiPanelState extends State { ), const SizedBox(height: 3), Text( - widget.pages[i].longTitle!, + page.longTitle!, maxLines: 1, style: TextStyle( - fontSize: 13, - color: i == currentIndex - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurface), + fontSize: 13, + color: isSelected ? primary : onSurface, + ), overflow: TextOverflow.ellipsis, ) ], diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 8114bdaf..241605ab 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -25,15 +25,10 @@ class VideoIntroController extends GetxController { VideoIntroController({required this.bvid}); // 视频bvid String bvid; - // 请求状态 - RxBool isLoading = false.obs; - // 视频详情 请求返回 Rx videoDetail = VideoDetailData().obs; - // up主粉丝数 Map userStat = {'follower': '-'}; - // 是否点赞 RxBool hasLike = false.obs; // 是否投币 @@ -59,6 +54,7 @@ class VideoIntroController extends GetxController { bool isPaused = false; String heroTag = ''; late ModelResult modelResult; + late PersistentBottomSheetController? bottomSheetController; @override void onInit() { @@ -562,4 +558,8 @@ class VideoIntroController extends GetxController { } return res; } + + hiddenEpisodeBottomSheet() { + bottomSheetController?.close(); + } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index a990aab8..608beec8 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -20,8 +20,8 @@ import '../../../../http/user.dart'; import 'widgets/action_item.dart'; import 'widgets/fav_panel.dart'; import 'widgets/intro_detail.dart'; -import 'widgets/page.dart'; -import 'widgets/season.dart'; +import 'widgets/page_panel.dart'; +import 'widgets/season_panel.dart'; class VideoIntroPanel extends StatefulWidget { final String bvid; @@ -384,18 +384,25 @@ class _VideoInfoState extends State with TickerProviderStateMixin { sheetHeight: sheetHeight, changeFuc: (bvid, cid, aid) => videoIntroController.changeSeasonOrbangu(bvid, cid, aid), + videoIntroCtr: videoIntroController, ), ) ], if (widget.videoDetail!.pages != null && widget.videoDetail!.pages!.length > 1) ...[ - Obx(() => PagesPanel( - pages: widget.videoDetail!.pages!, - cid: videoIntroController.lastPlayCid.value, - sheetHeight: sheetHeight, - changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( - videoIntroController.bvid, cid, null), - )) + Obx( + () => PagesPanel( + pages: widget.videoDetail!.pages!, + cid: videoIntroController.lastPlayCid.value, + sheetHeight: sheetHeight, + changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( + videoIntroController.bvid, + cid, + null, + ), + videoIntroCtr: videoIntroController, + ), + ) ], GestureDetector( onTap: onPushMember, diff --git a/lib/pages/video/detail/introduction/widgets/page.dart b/lib/pages/video/detail/introduction/widgets/page.dart deleted file mode 100644 index 8d296050..00000000 --- a/lib/pages/video/detail/introduction/widgets/page.dart +++ /dev/null @@ -1,258 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:pilipala/models/video_detail_res.dart'; -import 'package:pilipala/pages/video/detail/index.dart'; - -class PagesPanel extends StatefulWidget { - const PagesPanel({ - super.key, - required this.pages, - this.cid, - this.sheetHeight, - this.changeFuc, - }); - final List pages; - final int? cid; - final double? sheetHeight; - final Function? changeFuc; - - @override - State createState() => _PagesPanelState(); -} - -class _PagesPanelState extends State { - late List episodes; - late int cid; - late int currentIndex; - final String heroTag = Get.arguments['heroTag']; - late VideoDetailController _videoDetailController; - final ScrollController _scrollController = ScrollController(); - - @override - void initState() { - super.initState(); - cid = widget.cid!; - episodes = widget.pages; - _videoDetailController = Get.find(tag: heroTag); - currentIndex = episodes.indexWhere((Part e) => e.cid == cid); - _videoDetailController.cid.listen((int p0) { - cid = p0; - setState(() {}); - currentIndex = episodes.indexWhere((Part e) => e.cid == cid); - }); - } - - void changeFucCall(item, i) async { - await widget.changeFuc!( - item.cid, - ); - currentIndex = i; - setState(() {}); - } - - @override - void dispose() { - _scrollController.dispose(); - 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( - children: [ - Padding( - padding: const EdgeInsets.only(top: 10, bottom: 2), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('视频选集 '), - Expanded( - child: Text( - ' 正在播放:${widget.pages[currentIndex].pagePart}', - overflow: TextOverflow.ellipsis, - style: TextStyle( - fontSize: 12, - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - const SizedBox(width: 10), - SizedBox( - height: 34, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () { - showBottomSheet( - context: context, - builder: (BuildContext context) { - return StatefulBuilder(builder: - (BuildContext context, StateSetter setState) { - WidgetsBinding.instance - .addPostFrameCallback((_) async { - await Future.delayed( - const Duration(milliseconds: 200)); - _scrollController.jumpTo(currentIndex * 56); - }); - return Container( - height: widget.sheetHeight, - color: Theme.of(context).colorScheme.background, - child: Column( - children: [ - Container( - height: 45, - padding: const EdgeInsets.only( - left: 14, right: 14), - child: Row( - mainAxisAlignment: - MainAxisAlignment.spaceBetween, - children: [ - Text( - '合集(${episodes.length})', - style: Theme.of(context) - .textTheme - .titleMedium, - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ), - Divider( - height: 1, - color: Theme.of(context) - .dividerColor - .withOpacity(0.1), - ), - Expanded( - child: Material( - child: ListView.builder( - controller: _scrollController, - itemCount: episodes.length + 1, - itemBuilder: - (BuildContext context, int index) { - bool isLastItem = - index == episodes.length; - bool isCurrentIndex = - currentIndex == index; - return isLastItem - ? SizedBox( - height: MediaQuery.of(context) - .padding - .bottom + - 20, - ) - : buildEpisodeListItem( - episodes[index], - index, - isCurrentIndex, - ); - }, - ), - ), - ), - ], - ), - ); - }); - }, - ); - }, - child: Text( - '共${widget.pages.length}集', - style: const TextStyle(fontSize: 13), - ), - ), - ), - ], - ), - ), - Container( - height: 35, - margin: const EdgeInsets.only(bottom: 8), - child: ListView.builder( - scrollDirection: Axis.horizontal, - 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), - child: Material( - color: Theme.of(context).colorScheme.onInverseSurface, - borderRadius: BorderRadius.circular(6), - clipBehavior: Clip.hardEdge, - child: InkWell( - onTap: () => changeFucCall(widget.pages[i], i), - child: Padding( - padding: const EdgeInsets.symmetric( - vertical: 8, horizontal: 8), - child: Row( - children: [ - if (isCurrentIndex) ...[ - Image.asset( - 'assets/images/live.gif', - color: Theme.of(context).colorScheme.primary, - height: 12, - ), - const SizedBox(width: 6) - ], - Expanded( - child: Text( - widget.pages[i].pagePart!, - maxLines: 1, - style: TextStyle( - fontSize: 13, - color: isCurrentIndex - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.onSurface), - overflow: TextOverflow.ellipsis, - )) - ], - ), - ), - ), - ), - ); - }, - ), - ) - ], - ); - } -} diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart new file mode 100644 index 00000000..730a6105 --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -0,0 +1,184 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/models/video_detail_res.dart'; +import 'package:pilipala/pages/video/detail/index.dart'; +import 'package:pilipala/pages/video/detail/introduction/index.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import '../../../../../common/pages_bottom_sheet.dart'; +import '../../../../../models/common/video_episode_type.dart'; + +class PagesPanel extends StatefulWidget { + const PagesPanel({ + super.key, + required this.pages, + required this.cid, + this.sheetHeight, + this.changeFuc, + required this.videoIntroCtr, + }); + final List pages; + final int cid; + final double? sheetHeight; + final Function? changeFuc; + final VideoIntroController videoIntroCtr; + + @override + State createState() => _PagesPanelState(); +} + +class _PagesPanelState extends State { + late List episodes; + late int cid; + late RxInt currentIndex = (-1).obs; + final String heroTag = Get.arguments['heroTag']; + late VideoDetailController _videoDetailController; + final ScrollController listViewScrollCtr = ScrollController(); + final ItemScrollController itemScrollController = ItemScrollController(); + late PersistentBottomSheetController? _bottomSheetController; + + @override + void initState() { + super.initState(); + cid = widget.cid; + episodes = widget.pages; + _videoDetailController = Get.find(tag: heroTag); + currentIndex.value = episodes.indexWhere((Part e) => e.cid == cid); + scrollToIndex(); + _videoDetailController.cid.listen((int p0) { + cid = p0; + currentIndex.value = episodes.indexWhere((Part e) => e.cid == cid); + scrollToIndex(); + }); + } + + @override + void dispose() { + listViewScrollCtr.dispose(); + super.dispose(); + } + + void changeFucCall(item, i) async { + widget.changeFuc?.call(item.cid); + currentIndex.value = i; + _bottomSheetController?.close(); + scrollToIndex(); + } + + void scrollToIndex() { + WidgetsBinding.instance.addPostFrameCallback((_) { + // 在回调函数中获取更新后的状态 + final double offset = min((currentIndex * 150) - 75, + listViewScrollCtr.position.maxScrollExtent); + listViewScrollCtr.animateTo( + offset, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + }); + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 10, bottom: 2), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text('视频选集 '), + Expanded( + child: Obx(() => Text( + ' 正在播放:${widget.pages[currentIndex.value].pagePart}', + overflow: TextOverflow.ellipsis, + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.outline, + ), + )), + ), + const SizedBox(width: 10), + SizedBox( + height: 34, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () { + widget.videoIntroCtr.bottomSheetController = + _bottomSheetController = EpisodeBottomSheet( + currentCid: cid, + episodes: episodes, + changeFucCall: changeFucCall, + sheetHeight: widget.sheetHeight, + dataType: VideoEpidoesType.videoPart, + context: context, + ).show(context); + }, + child: Text( + '共${widget.pages.length}集', + style: const TextStyle(fontSize: 13), + ), + ), + ), + ], + ), + ), + Container( + height: 35, + margin: const EdgeInsets.only(bottom: 8), + child: ListView.builder( + scrollDirection: Axis.horizontal, + controller: listViewScrollCtr, + itemCount: widget.pages.length, + itemExtent: 150, + itemBuilder: (BuildContext context, int i) { + bool isCurrentIndex = currentIndex.value == i; + return Container( + width: 150, + margin: const EdgeInsets.only(right: 10), + child: Material( + color: Theme.of(context).colorScheme.onInverseSurface, + borderRadius: BorderRadius.circular(6), + clipBehavior: Clip.hardEdge, + child: InkWell( + onTap: () => changeFucCall(widget.pages[i], i), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, horizontal: 8), + child: Row( + children: [ + if (isCurrentIndex) ...[ + Image.asset( + 'assets/images/live.gif', + color: Theme.of(context).colorScheme.primary, + height: 12, + ), + const SizedBox(width: 6) + ], + Expanded( + child: Text( + widget.pages[i].pagePart!, + maxLines: 1, + style: TextStyle( + fontSize: 13, + 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_panel.dart similarity index 56% rename from lib/pages/video/detail/introduction/widgets/season.dart rename to lib/pages/video/detail/introduction/widgets/season_panel.dart index 0f3884ed..cd8bc954 100644 --- a/lib/pages/video/detail/introduction/widgets/season.dart +++ b/lib/pages/video/detail/introduction/widgets/season_panel.dart @@ -1,9 +1,12 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/pages_bottom_sheet.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; +import '../../../../../models/common/video_episode_type.dart'; +import '../controller.dart'; class SeasonPanel extends StatefulWidget { const SeasonPanel({ @@ -12,11 +15,13 @@ class SeasonPanel extends StatefulWidget { this.cid, this.sheetHeight, this.changeFuc, + required this.videoIntroCtr, }); final UgcSeason ugcSeason; final int? cid; final double? sheetHeight; final Function? changeFuc; + final VideoIntroController videoIntroCtr; @override State createState() => _SeasonPanelState(); @@ -28,8 +33,8 @@ class _SeasonPanelState extends State { late int currentIndex; final String heroTag = Get.arguments['heroTag']; late VideoDetailController _videoDetailController; - final ScrollController _scrollController = ScrollController(); final ItemScrollController itemScrollController = ItemScrollController(); + late PersistentBottomSheetController? _bottomSheetController; @override void initState() { @@ -52,9 +57,6 @@ class _SeasonPanelState extends State { } /// 取对应 season_id 的 episodes - // episodes = widget.ugcSeason.sections! - // .firstWhere((e) => e.seasonId == widget.ugcSeason.id) - // .episodes!; currentIndex = episodes.indexWhere((EpisodeItem e) => e.cid == cid); _videoDetailController.cid.listen((int p0) { cid = p0; @@ -64,22 +66,16 @@ class _SeasonPanelState extends State { } void changeFucCall(item, int i) async { - await widget.changeFuc!( + widget.changeFuc?.call( IdUtils.av2bv(item.aid), item.cid, item.aid, ); currentIndex = i; - Get.back(); + _bottomSheetController?.close(); setState(() {}); } - @override - void dispose() { - _scrollController.dispose(); - super.dispose(); - } - Widget buildEpisodeListItem( EpisodeItem episode, int index, @@ -123,71 +119,17 @@ class _SeasonPanelState extends State { borderRadius: BorderRadius.circular(6), clipBehavior: Clip.hardEdge, child: InkWell( - onTap: () => showBottomSheet( - context: context, - builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - WidgetsBinding.instance.addPostFrameCallback((_) async { - itemScrollController.jumpTo(index: currentIndex); - }); - return Container( - height: widget.sheetHeight, - color: Theme.of(context).colorScheme.background, - child: Column( - children: [ - Container( - height: 45, - padding: const EdgeInsets.only(left: 14, right: 14), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - '合集(${episodes.length})', - style: Theme.of(context).textTheme.titleMedium, - ), - IconButton( - icon: const Icon(Icons.close), - onPressed: () => Navigator.pop(context), - ), - ], - ), - ), - Divider( - height: 1, - color: - Theme.of(context).dividerColor.withOpacity(0.1), - ), - Expanded( - child: Material( - child: ScrollablePositionedList.builder( - itemCount: episodes.length + 1, - itemBuilder: (BuildContext context, int index) { - bool isLastItem = index == episodes.length; - bool isCurrentIndex = currentIndex == index; - return isLastItem - ? SizedBox( - height: MediaQuery.of(context) - .padding - .bottom + - 20, - ) - : buildEpisodeListItem( - episodes[index], - index, - isCurrentIndex, - ); - }, - itemScrollController: itemScrollController, - ), - ), - ), - ], - ), - ); - }); - }, - ), + onTap: () { + widget.videoIntroCtr.bottomSheetController = + _bottomSheetController = EpisodeBottomSheet( + currentCid: cid, + episodes: episodes, + changeFucCall: changeFucCall, + sheetHeight: widget.sheetHeight, + dataType: VideoEpidoesType.videoEpisode, + context: context, + ).show(context); + }, child: Padding( padding: const EdgeInsets.fromLTRB(8, 12, 8, 12), child: Row( diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index c2379f20..725639ff 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -176,6 +176,7 @@ class _VideoDetailPageState extends State plPlayerController?.isFullScreen.listen((bool isFullScreen) { if (isFullScreen) { vdCtr.hiddenReplyReplyPanel(); + videoIntroController.hiddenEpisodeBottomSheet(); } }); } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 1ee65d83..4a8f4759 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -82,6 +82,7 @@ class _HeaderControlState extends State { /// 设置面板 void showSettingSheet() { + // Scaffold.of(context).openDrawer(); showModalBottomSheet( elevation: 0, context: context, @@ -158,7 +159,7 @@ class _HeaderControlState extends State { dense: true, leading: const Icon(Icons.hourglass_top_outlined, size: 20), - title: const Text('定时关闭(测试)', style: titleStyle), + title: const Text('定时关闭', style: titleStyle), ), ListTile( onTap: () => {Get.back(), showSetVideoQa()}, diff --git a/lib/pages/video/detail/widgets/right_drawer.dart b/lib/pages/video/detail/widgets/right_drawer.dart new file mode 100644 index 00000000..ca0d34ef --- /dev/null +++ b/lib/pages/video/detail/widgets/right_drawer.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class RightDrawer extends StatefulWidget { + const RightDrawer({super.key}); + + @override + State createState() => _RightDrawerState(); +} + +class _RightDrawerState extends State { + @override + Widget build(BuildContext context) { + return Drawer( + shadowColor: Colors.transparent, + elevation: 0, + backgroundColor: + Theme.of(context).colorScheme.surface.withOpacity(0.8)); + } +} From c2a4d80c79c9a2824c13505cc559035bb9d8d046 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Apr 2024 21:32:29 +0800 Subject: [PATCH 088/349] =?UTF-8?q?feat:=20=E5=85=A8=E5=B1=8F=E9=80=89?= =?UTF-8?q?=E6=8B=A9=E5=90=88=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/pages_bottom_sheet.dart | 103 ++++++++++-------- .../bangumi/introduction/controller.dart | 29 +++++ .../video/detail/introduction/controller.dart | 49 ++++++++- lib/pages/video/detail/introduction/view.dart | 9 +- .../introduction/widgets/page_panel.dart | 1 + .../introduction/widgets/season_panel.dart | 18 ++- lib/pages/video/detail/view.dart | 18 ++- .../pl_player/models/bottom_control_type.dart | 1 + lib/plugin/pl_player/view.dart | 20 ++++ lib/utils/drawer.dart | 39 +++++++ 10 files changed, 222 insertions(+), 65 deletions(-) create mode 100644 lib/utils/drawer.dart diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index b31ec4b1..c64b58b6 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -11,6 +11,7 @@ class EpisodeBottomSheet { final Function changeFucCall; final int? cid; final double? sheetHeight; + bool isFullScreen = false; EpisodeBottomSheet({ required this.episodes, @@ -20,6 +21,7 @@ class EpisodeBottomSheet { required this.changeFucCall, this.cid, this.sheetHeight, + this.isFullScreen = false, }); Widget buildEpisodeListItem( @@ -74,60 +76,69 @@ class EpisodeBottomSheet { '合集(${episodes.length})', style: Theme.of(context).textTheme.titleMedium, ), - actions: [ - IconButton( - icon: const Icon(Icons.close, size: 20), - onPressed: () => Navigator.pop(context), - ), - const SizedBox(width: 14), - ], + actions: !isFullScreen + ? [ + IconButton( + icon: const Icon(Icons.close, size: 20), + onPressed: () => Navigator.pop(context), + ), + const SizedBox(width: 14), + ] + : null, ); } + Widget buildShowContent(BuildContext context) { + final ItemScrollController itemScrollController = ItemScrollController(); + int currentIndex = episodes.indexWhere((dynamic e) => e.cid == currentCid); + return StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + WidgetsBinding.instance.addPostFrameCallback((_) { + itemScrollController.jumpTo(index: currentIndex); + }); + return Container( + height: sheetHeight, + color: Theme.of(context).colorScheme.background, + child: Column( + children: [ + buildTitle(), + Expanded( + child: Material( + child: PageStorage( + bucket: PageStorageBucket(), + child: ScrollablePositionedList.builder( + itemScrollController: itemScrollController, + itemCount: episodes.length + 1, + itemBuilder: (BuildContext context, int index) { + bool isLastItem = index == episodes.length; + bool isCurrentIndex = currentIndex == index; + return isLastItem + ? SizedBox( + height: + MediaQuery.of(context).padding.bottom + 20, + ) + : buildEpisodeListItem( + episodes[index], + index, + isCurrentIndex, + ); + }, + ), + ), + ), + ), + ], + ), + ); + }); + } + /// The [BuildContext] of the widget that calls the bottom sheet. PersistentBottomSheetController show(BuildContext context) { - final ItemScrollController itemScrollController = ItemScrollController(); - int currentIndex = episodes.indexWhere((dynamic e) => e.cid == currentCid); final PersistentBottomSheetController btmSheetCtr = showBottomSheet( context: context, builder: (BuildContext context) { - return StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - WidgetsBinding.instance.addPostFrameCallback((_) { - itemScrollController.jumpTo(index: currentIndex); - }); - return Container( - height: sheetHeight, - color: Theme.of(context).colorScheme.background, - child: Column( - children: [ - buildTitle(), - Expanded( - child: Material( - child: ScrollablePositionedList.builder( - itemScrollController: itemScrollController, - itemCount: episodes.length + 1, - itemBuilder: (BuildContext context, int index) { - bool isLastItem = index == episodes.length; - bool isCurrentIndex = currentIndex == index; - return isLastItem - ? SizedBox( - height: - MediaQuery.of(context).padding.bottom + 20, - ) - : buildEpisodeListItem( - episodes[index], - index, - isCurrentIndex, - ); - }, - ), - ), - ), - ], - ), - ); - }); + return buildShowContent(context); }, ); return btmSheetCtr; diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 12f0c053..63eadacf 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -15,6 +15,10 @@ import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:share_plus/share_plus.dart'; +import '../../../common/pages_bottom_sheet.dart'; +import '../../../models/common/video_episode_type.dart'; +import '../../../utils/drawer.dart'; + class BangumiIntroController extends GetxController { // 视频bvid String bvid = Get.parameters['bvid']!; @@ -291,4 +295,29 @@ class BangumiIntroController extends GetxController { int aid = episodes[nextIndex].aid!; changeSeasonOrbangu(bvid, cid, aid); } + + // 播放器底栏 选集 回调 + void showEposideHandler() { + late List episodes = bangumiDetail.value.episodes!; + VideoEpidoesType dataType = VideoEpidoesType.bangumiEpisode; + if (episodes.isEmpty) { + return; + } + VideoDetailController videoDetailCtr = + Get.find(tag: Get.arguments['heroTag']); + DrawerUtils.showRightDialog( + child: EpisodeBottomSheet( + episodes: episodes, + currentCid: videoDetailCtr.cid.value, + dataType: dataType, + context: Get.context!, + sheetHeight: Get.size.height, + isFullScreen: true, + changeFucCall: (item, index) { + changeSeasonOrbangu(item.bvid, item.cid, item.aid); + SmartDialog.dismiss(); + }, + ).buildShowContent(Get.context!), + ); + } } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 241605ab..d81bda00 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -18,6 +18,9 @@ import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:share_plus/share_plus.dart'; +import '../../../../common/pages_bottom_sheet.dart'; +import '../../../../models/common/video_episode_type.dart'; +import '../../../../utils/drawer.dart'; import '../related/index.dart'; import 'widgets/group_panel.dart'; @@ -54,7 +57,7 @@ class VideoIntroController extends GetxController { bool isPaused = false; String heroTag = ''; late ModelResult modelResult; - late PersistentBottomSheetController? bottomSheetController; + PersistentBottomSheetController? bottomSheetController; @override void onInit() { @@ -562,4 +565,48 @@ class VideoIntroController extends GetxController { hiddenEpisodeBottomSheet() { bottomSheetController?.close(); } + + // 播放器底栏 选集 回调 + void showEposideHandler() { + late List episodes; + VideoEpidoesType dataType = VideoEpidoesType.videoEpisode; + if (videoDetail.value.ugcSeason != null) { + dataType = VideoEpidoesType.videoEpisode; + final List sections = videoDetail.value.ugcSeason!.sections!; + for (int i = 0; i < sections.length; i++) { + final List episodesList = sections[i].episodes!; + for (int j = 0; j < episodesList.length; j++) { + if (episodesList[j].cid == lastPlayCid.value) { + episodes = episodesList; + continue; + } + } + } + } + if (videoDetail.value.pages != null && + videoDetail.value.pages!.length > 1) { + dataType = VideoEpidoesType.videoPart; + episodes = videoDetail.value.pages!; + } + + DrawerUtils.showRightDialog( + child: EpisodeBottomSheet( + episodes: episodes, + currentCid: lastPlayCid.value, + dataType: dataType, + context: Get.context!, + sheetHeight: Get.size.height, + isFullScreen: true, + changeFucCall: (item, index) { + if (dataType == VideoEpidoesType.videoEpisode) { + changeSeasonOrbangu(IdUtils.av2bv(item.aid), item.cid, item.aid); + } + if (dataType == VideoEpidoesType.videoPart) { + changeSeasonOrbangu(bvid, item.cid, null); + } + SmartDialog.dismiss(); + }, + ).buildShowContent(Get.context!), + ); + } } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 608beec8..70fa578d 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -373,7 +373,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { /// 点赞收藏转发 actionGrid(context, videoIntroController), - // 合集 + // 合集 videoPart 简洁 if (widget.videoDetail!.ugcSeason != null) ...[ Obx( () => SeasonPanel( @@ -383,11 +383,16 @@ class _VideoInfoState extends State with TickerProviderStateMixin { : widget.videoDetail!.pages!.first.cid, sheetHeight: sheetHeight, changeFuc: (bvid, cid, aid) => - videoIntroController.changeSeasonOrbangu(bvid, cid, aid), + videoIntroController.changeSeasonOrbangu( + bvid, + cid, + aid, + ), videoIntroCtr: videoIntroController, ), ) ], + // 合集 videoEpisode if (widget.videoDetail!.pages != null && widget.videoDetail!.pages!.length > 1) ...[ Obx( diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index 730a6105..fc999ba8 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -60,6 +60,7 @@ class _PagesPanelState extends State { } void changeFucCall(item, i) async { + print('pages changeFucCall'); widget.changeFuc?.call(item.cid); currentIndex.value = i; _bottomSheetController?.close(); diff --git a/lib/pages/video/detail/introduction/widgets/season_panel.dart b/lib/pages/video/detail/introduction/widgets/season_panel.dart index cd8bc954..745c081d 100644 --- a/lib/pages/video/detail/introduction/widgets/season_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/season_panel.dart @@ -30,7 +30,7 @@ class SeasonPanel extends StatefulWidget { class _SeasonPanelState extends State { late List episodes; late int cid; - late int currentIndex; + late RxInt currentIndex = (-1).obs; final String heroTag = Get.arguments['heroTag']; late VideoDetailController _videoDetailController; final ItemScrollController itemScrollController = ItemScrollController(); @@ -57,11 +57,10 @@ class _SeasonPanelState extends State { } /// 取对应 season_id 的 episodes - currentIndex = episodes.indexWhere((EpisodeItem e) => e.cid == cid); + currentIndex.value = episodes.indexWhere((EpisodeItem e) => e.cid == cid); _videoDetailController.cid.listen((int p0) { cid = p0; - setState(() {}); - currentIndex = episodes.indexWhere((EpisodeItem e) => e.cid == cid); + currentIndex.value = episodes.indexWhere((EpisodeItem e) => e.cid == cid); }); } @@ -71,9 +70,8 @@ class _SeasonPanelState extends State { item.cid, item.aid, ); - currentIndex = i; + currentIndex.value = i; _bottomSheetController?.close(); - setState(() {}); } Widget buildEpisodeListItem( @@ -148,10 +146,10 @@ class _SeasonPanelState extends State { height: 12, ), const SizedBox(width: 10), - Text( - '${currentIndex + 1}/${episodes.length}', - style: Theme.of(context).textTheme.labelMedium, - ), + Obx(() => Text( + '${currentIndex.value + 1}/${episodes.length}', + style: Theme.of(context).textTheme.labelMedium, + )), const SizedBox(width: 6), const Icon( Icons.arrow_forward_ios_outlined, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index ebbaeec9..7cbc7fed 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -178,6 +178,9 @@ class _VideoDetailPageState extends State if (isFullScreen) { vdCtr.hiddenReplyReplyPanel(); videoIntroController.hiddenEpisodeBottomSheet(); + vdCtr.bottomList.insert(3, BottomControlType.episode); + } else { + vdCtr.bottomList.removeAt(3); } }); } @@ -294,17 +297,20 @@ class _VideoDetailPageState extends State () { return !vdCtr.autoPlay.value ? const SizedBox() - : PLVideoPlayer( - controller: plPlayerController!, - headerControl: vdCtr.headerControl, - danmuWidget: Obx( - () => PlDanmaku( + : Obx( + () => PLVideoPlayer( + controller: plPlayerController!, + headerControl: vdCtr.headerControl, + danmuWidget: PlDanmaku( key: Key(vdCtr.danmakuCid.value.toString()), cid: vdCtr.danmakuCid.value, playerController: plPlayerController!, ), + bottomList: vdCtr.bottomList, + showEposideCb: () => vdCtr.videoType == SearchType.video + ? videoIntroController.showEposideHandler() + : bangumiIntroController.showEposideHandler(), ), - bottomList: vdCtr.bottomList, ); }, ); diff --git a/lib/plugin/pl_player/models/bottom_control_type.dart b/lib/plugin/pl_player/models/bottom_control_type.dart index 739e1d38..d724c5de 100644 --- a/lib/plugin/pl_player/models/bottom_control_type.dart +++ b/lib/plugin/pl_player/models/bottom_control_type.dart @@ -4,6 +4,7 @@ enum BottomControlType { next, time, space, + episode, fit, speed, fullscreen, diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 0e411f2e..6a5f22ec 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -37,6 +37,7 @@ class PLVideoPlayer extends StatefulWidget { this.bottomList, this.customWidget, this.customWidgets, + this.showEposideCb, super.key, }); @@ -49,6 +50,7 @@ class PLVideoPlayer extends StatefulWidget { final Widget? customWidget; final List? customWidgets; + final Function? showEposideCb; @override State createState() => _PLVideoPlayerState(); @@ -267,6 +269,24 @@ class _PLVideoPlayerState extends State /// 空白占位 BottomControlType.space: const Spacer(), + /// 选集 + BottomControlType.episode: SizedBox( + height: 30, + width: 30, + child: TextButton( + onPressed: () { + widget.showEposideCb?.call(); + }, + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + child: const Text( + '选集', + style: TextStyle(color: Colors.white, fontSize: 13), + ), + ), + ), + /// 画面比例 BottomControlType.fit: SizedBox( height: 30, diff --git a/lib/utils/drawer.dart b/lib/utils/drawer.dart new file mode 100644 index 00000000..f4aa17f8 --- /dev/null +++ b/lib/utils/drawer.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; + +class DrawerUtils { + static void showRightDialog({ + required Widget child, + double width = 400, + bool useSystem = false, + }) { + SmartDialog.show( + alignment: Alignment.topRight, + animationBuilder: (controller, child, animationParam) { + return SlideTransition( + position: Tween( + begin: const Offset(1, 0), + end: Offset.zero, + ).animate(controller.view), + child: child, + ); + }, + useSystem: useSystem, + maskColor: Colors.black.withOpacity(0.5), + animationTime: const Duration(milliseconds: 200), + builder: (context) => Container( + width: width, + color: Theme.of(context).scaffoldBackgroundColor, + child: SafeArea( + left: false, + right: false, + bottom: false, + child: MediaQuery( + data: const MediaQueryData(padding: EdgeInsets.zero), + child: child, + ), + ), + ), + ); + } +} From 46c975bbbb5b5b53dd5554617c79de0f823f2b75 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Apr 2024 21:57:57 +0800 Subject: [PATCH 089/349] =?UTF-8?q?mod:=20=E5=90=88=E9=9B=86=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E5=B1=95=E7=A4=BA=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 7cbc7fed..822d0a45 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -178,9 +178,15 @@ class _VideoDetailPageState extends State if (isFullScreen) { vdCtr.hiddenReplyReplyPanel(); videoIntroController.hiddenEpisodeBottomSheet(); - vdCtr.bottomList.insert(3, BottomControlType.episode); + if (videoIntroController.videoDetail.value.ugcSeason != null || + (videoIntroController.videoDetail.value.pages != null && + videoIntroController.videoDetail.value.pages!.length > 1)) { + vdCtr.bottomList.insert(3, BottomControlType.episode); + } } else { - vdCtr.bottomList.removeAt(3); + if (vdCtr.bottomList.contains(BottomControlType.episode)) { + vdCtr.bottomList.removeAt(3); + } } }); } From 9c12c2179623091028d14bc2823b6e47eb577d1e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 7 Apr 2024 23:13:51 +0800 Subject: [PATCH 090/349] =?UTF-8?q?mod:=20navigation=20Bar=E7=BC=96?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/pages/navigation_bar_set.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/setting/pages/navigation_bar_set.dart b/lib/pages/setting/pages/navigation_bar_set.dart index 8e1771e3..09e92bc3 100644 --- a/lib/pages/setting/pages/navigation_bar_set.dart +++ b/lib/pages/setting/pages/navigation_bar_set.dart @@ -74,7 +74,7 @@ class _NavigationbarSetPageState extends State { }, title: Text(defaultNavTabs[i]['label']), secondary: const Icon(Icons.drag_indicator_rounded), - enabled: defaultNavTabs[i]['id'] != 3, + enabled: defaultNavTabs[i]['id'] != 0, ) ] ]; From 9ee0f6526c8b7aecb39c9b6df4e7c4d77e25c03b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 7 Apr 2024 23:48:27 +0800 Subject: [PATCH 091/349] =?UTF-8?q?mod:=20=E8=AE=A2=E9=98=85=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E7=B1=BB=E5=9E=8B=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 7 +++- lib/http/user.dart | 33 +++++++++++++++- lib/pages/subscription_detail/controller.dart | 21 ++++++---- lib/pages/subscription_detail/view.dart | 39 +++++++++---------- 4 files changed, 68 insertions(+), 32 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index fa4cc1e8..b6975c4b 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -490,8 +490,11 @@ class Api { /// 我的订阅 static const userSubFolder = '/x/v3/fav/folder/collected/list'; - /// 我的订阅详情 - static const userSubFolderDetail = '/x/space/fav/season/list'; + /// 我的订阅详情 type 21 + static const userSeasonList = '/x/space/fav/season/list'; + + /// 我的订阅详情 type 11 + static const userResourceList = '/x/v3/fav/resource/list'; /// 表情 static const emojiList = '/x/emote/user/panel/web'; diff --git a/lib/http/user.dart b/lib/http/user.dart index bae61720..fea0a22e 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -330,12 +330,12 @@ class UserHttp { } } - static Future userSubFolderDetail({ + static Future userSeasonList({ required int seasonId, required int pn, required int ps, }) async { - var res = await Request().get(Api.userSubFolderDetail, data: { + var res = await Request().get(Api.userSeasonList, data: { 'season_id': seasonId, 'ps': ps, 'pn': pn, @@ -350,6 +350,35 @@ class UserHttp { } } + static Future userResourceList({ + required int seasonId, + required int pn, + required int ps, + }) async { + var res = await Request().get(Api.userResourceList, data: { + 'media_id': seasonId, + 'ps': ps, + 'pn': pn, + 'keyword': '', + 'order': 'mtime', + 'type': 0, + 'tid': 0, + 'platform': 'web', + }); + if (res.data['code'] == 0) { + try { + return { + 'status': true, + 'data': SubDetailModelData.fromJson(res.data['data']) + }; + } catch (err) { + return {'status': false, 'msg': err}; + } + } else { + return {'status': false, 'msg': res.data['message']}; + } + } + // 取消订阅 static Future cancelSub({required int seasonId}) async { var res = await Request().post( diff --git a/lib/pages/subscription_detail/controller.dart b/lib/pages/subscription_detail/controller.dart index 6ecb894e..4245df2c 100644 --- a/lib/pages/subscription_detail/controller.dart +++ b/lib/pages/subscription_detail/controller.dart @@ -6,7 +6,6 @@ import '../../models/user/sub_folder.dart'; class SubDetailController extends GetxController { late SubFolderItemData item; - late int seasonId; late String heroTag; int currentPage = 1; @@ -26,17 +25,23 @@ class SubDetailController extends GetxController { super.onInit(); } - Future queryUserSubFolderDetail({type = 'init'}) async { + Future queryUserSeasonList({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, - ); + var res = type == 21 + ? await UserHttp.userSeasonList( + seasonId: seasonId, + ps: 20, + pn: currentPage, + ) + : await UserHttp.userResourceList( + seasonId: seasonId, + ps: 20, + pn: currentPage, + ); if (res['status']) { subInfo.value = res['data'].info; if (currentPage == 1 && type == 'init') { @@ -55,6 +60,6 @@ class SubDetailController extends GetxController { } onLoad() { - queryUserSubFolderDetail(type: 'onLoad'); + queryUserSeasonList(type: 'onLoad'); } } diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index d56125cd..93e0abbb 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -26,13 +26,11 @@ class _SubDetailPageState extends State { 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(); + _futureBuilderFuture = _subDetailController.queryUserSeasonList(); titleStreamC = StreamController(); _controller.addListener( () { @@ -161,15 +159,18 @@ class _SubDetailPageState extends State { ), ), 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), - ), + Obx( + () => Text( + '${Utils.numFormat(_subDetailController.subInfo.value.cntInfo?['play'])}次播放', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .labelSmall! + .fontSize, + color: + Theme.of(context).colorScheme.outline), + ), + ) ], ), ), @@ -182,14 +183,12 @@ class _SubDetailPageState extends State { 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), + child: Text( + '共${_subDetailController.item.mediaCount}条视频', + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + letterSpacing: 1, ), ), ), From 48f0b59701011e478b2d3f8efea17459ae865c0c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 8 Apr 2024 23:33:43 +0800 Subject: [PATCH 092/349] =?UTF-8?q?mod:=20=E7=9B=B8=E5=86=8C=E3=80=81?= =?UTF-8?q?=E5=AD=98=E5=82=A8=E6=9D=83=E9=99=90=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/download.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/utils/download.dart b/lib/utils/download.dart index a9c56ec0..e27335d0 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -11,8 +11,7 @@ class DownloadUtils { static Future requestStoragePer() async { await Permission.storage.request(); PermissionStatus status = await Permission.storage.status; - if (status == PermissionStatus.denied || - status == PermissionStatus.permanentlyDenied) { + if (status == PermissionStatus.denied) { SmartDialog.show( useSystem: true, animationType: SmartAnimationType.centerFade_otherSlide, @@ -41,8 +40,7 @@ class DownloadUtils { static Future requestPhotoPer() async { await Permission.photos.request(); PermissionStatus status = await Permission.photos.status; - if (status == PermissionStatus.denied || - status == PermissionStatus.permanentlyDenied) { + if (status == PermissionStatus.denied) { SmartDialog.show( useSystem: true, animationType: SmartAnimationType.centerFade_otherSlide, From 2bd97f800efb4e88dda04e307739cefb2dcc9b58 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 8 Apr 2024 23:43:25 +0800 Subject: [PATCH 093/349] Update beta_ci.yml --- .github/workflows/beta_ci.yml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/.github/workflows/beta_ci.yml b/.github/workflows/beta_ci.yml index e839aca1..80fe8afb 100644 --- a/.github/workflows/beta_ci.yml +++ b/.github/workflows/beta_ci.yml @@ -1,17 +1,5 @@ name: Pilipala Beta -on: - workflow_dispatch: - push: - branches: - - "main" - paths-ignore: - - "**.md" - - "**.txt" - - ".github/**" - - ".idea/**" - - "!.github/workflows/**" - jobs: update_version: name: Read and update version From b9e93dabe62884251fd617719e3b647406b97097 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 8 Apr 2024 23:45:54 +0800 Subject: [PATCH 094/349] Update beta_ci.yml --- .github/workflows/beta_ci.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/beta_ci.yml b/.github/workflows/beta_ci.yml index 80fe8afb..40f3f042 100644 --- a/.github/workflows/beta_ci.yml +++ b/.github/workflows/beta_ci.yml @@ -1,5 +1,18 @@ name: Pilipala Beta +on: + workflow_dispatch: + push: + branches: + - "never" + paths-ignore: + - "**.md" + - "**.txt" + - ".github/**" + - ".idea/**" + - "!.github/workflows/**" + + jobs: update_version: name: Read and update version From 84f83c260ab092e9040093bf894b9d9d035192c9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 8 Apr 2024 23:55:29 +0800 Subject: [PATCH 095/349] =?UTF-8?q?feat:=20=E7=AE=80=E5=8D=95=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=E6=8A=95=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dlna/index.dart | 111 ++++++++++++++++++ .../video/detail/widgets/header_control.dart | 17 +++ pubspec.lock | 8 ++ pubspec.yaml | 2 + 4 files changed, 138 insertions(+) create mode 100644 lib/pages/dlna/index.dart diff --git a/lib/pages/dlna/index.dart b/lib/pages/dlna/index.dart new file mode 100644 index 00000000..3ec5965b --- /dev/null +++ b/lib/pages/dlna/index.dart @@ -0,0 +1,111 @@ +import 'dart:async'; + +import 'package:dlna_dart/dlna.dart'; +import 'package:flutter/material.dart'; + +class LiveDlnaPage extends StatefulWidget { + final String datasource; + + const LiveDlnaPage({Key? key, required this.datasource}) : super(key: key); + + @override + State createState() => _LiveDlnaPageState(); +} + +class _LiveDlnaPageState extends State { + final Map _deviceList = {}; + final DLNAManager searcher = DLNAManager(); + late final Timer stopSearchTimer; + String selectDeviceKey = ''; + bool isSearching = true; + + DLNADevice? get device => _deviceList[selectDeviceKey]; + + @override + void initState() { + stopSearchTimer = Timer(const Duration(seconds: 20), () { + setState(() => isSearching = false); + searcher.stop(); + }); + searcher.stop(); + startSearch(); + super.initState(); + } + + @override + void dispose() { + super.dispose(); + searcher.stop(); + stopSearchTimer.cancel(); + } + + void startSearch() async { + // clear old devices + isSearching = true; + selectDeviceKey = ''; + _deviceList.clear(); + setState(() {}); + // start search server + final m = await searcher.start(); + m.devices.stream.listen((deviceList) { + deviceList.forEach((key, value) { + _deviceList[key] = value; + }); + setState(() {}); + }); + // close the server, the closed server can be start by call searcher.start() + } + + void selectDevice(String key) { + if (selectDeviceKey.isNotEmpty) device?.pause(); + + selectDeviceKey = key; + device?.setUrl(widget.datasource); + device?.play(); + setState(() {}); + } + + @override + Widget build(BuildContext context) { + Widget cur; + if (isSearching && _deviceList.isEmpty) { + cur = const Center(child: CircularProgressIndicator()); + } else if (_deviceList.isEmpty) { + cur = Center( + child: Text( + '没有找到设备', + style: Theme.of(context).textTheme.bodyLarge, + ), + ); + } else { + cur = ListView( + children: _deviceList.keys + .map((key) => ListTile( + contentPadding: const EdgeInsets.all(2), + title: Text(_deviceList[key]!.info.friendlyName), + subtitle: Text(key), + onTap: () => selectDevice(key), + )) + .toList(), + ); + } + + return AlertDialog( + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('查找设备'), + IconButton( + onPressed: startSearch, + icon: const Icon(Icons.refresh_rounded), + ), + ], + ), + content: SizedBox( + height: 200, + width: 200, + child: cur, + ), + ); + } +} diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 4a8f4759..7a12b0cf 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -11,6 +11,7 @@ import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/video/play/quality.dart'; import 'package:pilipala/models/video/play/url.dart'; +import 'package:pilipala/pages/dlna/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/introduction/widgets/menu_row.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; @@ -1209,6 +1210,22 @@ class _HeaderControlState extends State { // ), // fuc: () => _.screenshot(), // ), + ComBtn( + icon: const Icon( + Icons.cast, + size: 19, + color: Colors.white, + ), + fuc: () async { + showDialog( + context: context, + builder: (BuildContext context) { + return LiveDlnaPage( + datasource: widget.videoDetailCtr!.videoUrl); + }, + ); + }, + ), if (isFullScreen.value) ...[ SizedBox( width: 56, diff --git a/pubspec.lock b/pubspec.lock index 84556c06..a64c85c0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -409,6 +409,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.2" + dlna_dart: + dependency: "direct main" + description: + name: dlna_dart + sha256: ae07c1c53077bbf58756fa589f936968719b0085441981d33e74f82f89d1d281 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.8" dynamic_color: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index ba5976eb..27b8b720 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -144,6 +144,8 @@ dependencies: disable_battery_optimization: ^1.1.1 # 展开/收起 expandable: ^5.0.1 + # 投屏 + dlna_dart: ^0.0.8 dev_dependencies: flutter_test: From ca37d45eb94007be0fa666ab993fcf3a0049fe7b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 9 Apr 2024 23:22:57 +0800 Subject: [PATCH 096/349] =?UTF-8?q?fix:=20=E5=85=A8=E5=B1=8F=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=90=88=E9=9B=86=E8=A7=86=E9=A2=91=E6=A0=87=E9=A2=98?= =?UTF-8?q?=E6=9C=AA=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/widgets/header_control.dart | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 7a12b0cf..e6a324cb 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -83,7 +83,6 @@ class _HeaderControlState extends State { /// 设置面板 void showSettingSheet() { - // Scaffold.of(context).openDrawer(); showModalBottomSheet( elevation: 0, context: context, @@ -732,9 +731,12 @@ class _HeaderControlState extends State { margin: const EdgeInsets.all(12), child: Column( children: [ - SizedBox( - height: 45, - child: Center(child: Text('选择解码格式', style: titleStyle))), + const SizedBox( + height: 45, + child: Center( + child: Text('选择解码格式', style: titleStyle), + ), + ), Expanded( child: Material( child: ListView( @@ -1079,9 +1081,12 @@ class _HeaderControlState extends State { margin: const EdgeInsets.all(12), child: Column( children: [ - SizedBox( - height: 45, - child: Center(child: Text('选择播放顺序', style: titleStyle))), + const SizedBox( + height: 45, + child: Center( + child: Text('选择播放顺序', style: titleStyle), + ), + ), Expanded( child: Material( child: ListView( @@ -1166,11 +1171,13 @@ class _HeaderControlState extends State { children: [ ConstrainedBox( constraints: const BoxConstraints(maxWidth: 200), - child: Text( - videoIntroController.videoDetail.value.title ?? '', - style: const TextStyle( - color: Colors.white, - fontSize: 16, + child: Obx( + () => Text( + videoIntroController.videoDetail.value.title ?? '', + style: const TextStyle( + color: Colors.white, + fontSize: 16, + ), ), ), ), From 04bfc294525d47511ea9c005fbcfa2c9addae142 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 9 Apr 2024 23:33:13 +0800 Subject: [PATCH 097/349] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E9=97=B4?= =?UTF-8?q?=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/view.dart | 5 ++++ .../live_room/widgets/bottom_control.dart | 28 +++++++------------ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 1e5c29c5..37981b1d 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -62,6 +62,11 @@ class _LiveRoomPageState extends State { controller: plPlayerController, liveRoomCtr: _liveRoomController, floating: floating, + onRefresh: () { + setState(() { + _futureBuilderFuture = _liveRoomController.queryLiveInfo(); + }); + }, ), ); } else { diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 3c908d71..e5a9d6c9 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -14,10 +14,12 @@ class BottomControl extends StatefulWidget implements PreferredSizeWidget { final PlPlayerController? controller; final LiveRoomController? liveRoomCtr; final Floating? floating; + final Function? onRefresh; const BottomControl({ this.controller, this.liveRoomCtr, this.floating, + this.onRefresh, Key? key, }) : super(key: key); @@ -61,6 +63,14 @@ class _BottomControlState extends State { // ), // fuc: () => Get.back(), // ), + ComBtn( + icon: const Icon( + Icons.refresh_outlined, + size: 18, + color: Colors.white, + ), + fuc: widget.onRefresh, + ), const Spacer(), // ComBtn( // icon: const Icon( @@ -150,21 +160,3 @@ class _BottomControlState extends State { ); } } - -class MSliderTrackShape extends RoundedRectSliderTrackShape { - @override - Rect getPreferredRect({ - required RenderBox parentBox, - Offset offset = Offset.zero, - SliderThemeData? sliderTheme, - bool isEnabled = false, - bool isDiscrete = false, - }) { - const double trackHeight = 3; - final double trackLeft = offset.dx; - final double trackTop = - offset.dy + (parentBox.size.height - trackHeight) / 2 + 4; - final double trackWidth = parentBox.size.width; - return Rect.fromLTWH(trackLeft, trackTop, trackWidth, trackHeight); - } -} From 9f9471d7f9961332077b98380c5f8afa096e94cc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 9 Apr 2024 23:42:09 +0800 Subject: [PATCH 098/349] =?UTF-8?q?fix:=20up=E4=B8=BB=E9=A1=B5=E4=B8=93?= =?UTF-8?q?=E6=A0=8F=E8=A7=86=E9=A2=91=E6=97=B6=E9=95=BF=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_seasons/widgets/item.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/pages/member_seasons/widgets/item.dart b/lib/pages/member_seasons/widgets/item.dart index 6398c5eb..4df74b70 100644 --- a/lib/pages/member_seasons/widgets/item.dart +++ b/lib/pages/member_seasons/widgets/item.dart @@ -25,7 +25,7 @@ class MemberSeasonsItem extends StatelessWidget { child: InkWell( onTap: () async { int cid = - await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid); + await SearchHttp.ab2c(aid: seasonItem.aid, bvid: seasonItem.bvid); Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid', arguments: {'videoItem': seasonItem, 'heroTag': heroTag}); }, @@ -51,8 +51,7 @@ class MemberSeasonsItem extends StatelessWidget { bottom: 6, right: 6, type: 'gray', - text: Utils.CustomStamp_str( - timestamp: seasonItem.pubdate, date: 'YY-MM-DD'), + text: Utils.timeFormat(seasonItem.duration), ) ], ); From 3f16a36bacc6d140ed6355ba95e9ad1e3f1c4de4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 10 Apr 2024 23:34:59 +0800 Subject: [PATCH 099/349] =?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=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/reply_reply/view.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index e8754a31..344ca1b1 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -140,8 +140,8 @@ class _VideoReplyReplyPanelState extends State { future: _futureBuilderFuture, builder: (BuildContext context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - final Map data = snapshot.data as Map; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { // 请求成功 return Obx( () => SliverList( @@ -199,7 +199,7 @@ class _VideoReplyReplyPanelState extends State { } else { // 请求错误 return HttpError( - errMsg: data['msg'], + errMsg: data?['msg'] ?? '请求错误', fn: () => setState(() {}), ); } From 7fd23044313690d15b7b07586d251605c024191a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 10 Apr 2024 23:52:21 +0800 Subject: [PATCH 100/349] =?UTF-8?q?fix:=20pip=E8=BF=9B=E5=85=A5=E6=97=B6ap?= =?UTF-8?q?pbarStream=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index ffa50476..a061a0e2 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -107,7 +107,7 @@ class _VideoDetailPageState extends State // 流 appbarStreamListen() { - appbarStream = StreamController(); + appbarStream = StreamController.broadcast(); _extendNestCtr.addListener( () { final double offset = _extendNestCtr.position.pixels; From 4b3dd3ca599793d74e76abd0e71f943b2fd0631a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 11 Apr 2024 23:26:06 +0800 Subject: [PATCH 101/349] =?UTF-8?q?opt:=20navigationBar=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/main/view.dart | 130 +++++++++++++-------------- lib/pages/setting/style_setting.dart | 2 +- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index c551e690..e4e980af 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -127,81 +127,81 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { }, children: _mainController.pages, ), - bottomNavigationBar: StreamBuilder( - stream: _mainController.hideTabBar - ? _mainController.bottomBarStream.stream - : StreamController.broadcast().stream, - initialData: true, - builder: (context, AsyncSnapshot snapshot) { - return AnimatedSlide( - curve: Curves.easeInOutCubicEmphasized, - duration: const Duration(milliseconds: 500), - offset: Offset(0, snapshot.data ? 0 : 1), - child: Obx( - () => enableMYBar - ? NavigationBar( - onDestinationSelected: (value) => setIndex(value), - selectedIndex: _mainController.selectedIndex, - destinations: [ - ..._mainController.navigationBars.map((e) { - return NavigationDestination( - icon: Obx( - () => Badge( - label: - _mainController.dynamicBadgeType.value == + bottomNavigationBar: _mainController.navigationBars.length > 1 + ? StreamBuilder( + stream: _mainController.hideTabBar + ? _mainController.bottomBarStream.stream + : StreamController.broadcast().stream, + initialData: true, + builder: (context, AsyncSnapshot snapshot) { + return AnimatedSlide( + curve: Curves.easeInOutCubicEmphasized, + duration: const Duration(milliseconds: 500), + offset: Offset(0, snapshot.data ? 0 : 1), + child: enableMYBar + ? NavigationBar( + onDestinationSelected: (value) => setIndex(value), + selectedIndex: _mainController.selectedIndex, + destinations: [ + ..._mainController.navigationBars.map((e) { + return NavigationDestination( + icon: Obx( + () => Badge( + label: _mainController + .dynamicBadgeType.value == DynamicBadgeMode.number ? Text(e['count'].toString()) : null, - padding: - const EdgeInsets.fromLTRB(6, 0, 6, 0), - isLabelVisible: - _mainController.dynamicBadgeType.value != + padding: + const EdgeInsets.fromLTRB(6, 0, 6, 0), + isLabelVisible: _mainController + .dynamicBadgeType.value != DynamicBadgeMode.hidden && e['count'] > 0, - child: e['icon'], - ), - ), - selectedIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], - ) - : BottomNavigationBar( - currentIndex: _mainController.selectedIndex, - onTap: (value) => setIndex(value), - iconSize: 16, - selectedFontSize: 12, - unselectedFontSize: 12, - items: [ - ..._mainController.navigationBars.map((e) { - return BottomNavigationBarItem( - icon: Obx( - () => Badge( - label: - _mainController.dynamicBadgeType.value == + child: e['icon'], + ), + ), + selectedIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ) + : BottomNavigationBar( + currentIndex: _mainController.selectedIndex, + onTap: (value) => setIndex(value), + iconSize: 16, + selectedFontSize: 12, + unselectedFontSize: 12, + items: [ + ..._mainController.navigationBars.map((e) { + return BottomNavigationBarItem( + icon: Obx( + () => Badge( + label: _mainController + .dynamicBadgeType.value == DynamicBadgeMode.number ? Text(e['count'].toString()) : null, - padding: - const EdgeInsets.fromLTRB(6, 0, 6, 0), - isLabelVisible: - _mainController.dynamicBadgeType.value != + padding: + const EdgeInsets.fromLTRB(6, 0, 6, 0), + isLabelVisible: _mainController + .dynamicBadgeType.value != DynamicBadgeMode.hidden && e['count'] > 0, - child: e['icon'], - ), - ), - activeIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], - ), - ), - ); - }, - ), + child: e['icon'], + ), + ), + activeIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ), + ); + }, + ) + : null, ), ); } diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index d2403cff..364eabf0 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -287,7 +287,7 @@ class _StyleSettingState extends State { ListTile( dense: false, onTap: () => Get.toNamed('/navbarSetting'), - title: Text('navbar设置', style: titleStyle), + title: Text('底部导航栏设置', style: titleStyle), ), if (Platform.isAndroid) ListTile( From a74647eb61a36e1f0795f92593185c57c4e73bf6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 11 Apr 2024 23:29:51 +0800 Subject: [PATCH 102/349] =?UTF-8?q?opt:=20=E9=BB=98=E8=AE=A4=E4=B8=8D?= =?UTF-8?q?=E6=94=B6=E8=B5=B7=E9=A1=B6=E6=A0=8F&=E5=BA=95=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/controller.dart | 2 +- lib/pages/main/controller.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index fb85be0b..ca70e1c4 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -35,7 +35,7 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { userLogin.value = userInfo != null; userFace.value = userInfo != null ? userInfo.face : ''; hideSearchBar = - setting.get(SettingBoxKey.hideSearchBar, defaultValue: true); + setting.get(SettingBoxKey.hideSearchBar, defaultValue: false); if (setting.get(SettingBoxKey.enableSearchWord, defaultValue: true)) { searchDefault(); } diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index f929a1aa..c2e5c322 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -33,7 +33,7 @@ class MainController extends GetxController { if (setting.get(SettingBoxKey.autoUpdate, defaultValue: false)) { Utils.checkUpdata(); } - hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: true); + hideTabBar = setting.get(SettingBoxKey.hideTabBar, defaultValue: false); var userInfo = userInfoCache.get('userInfoCache'); userLogin.value = userInfo != null; From 10435bb7b1fecff8552d5b5f523db10a2637da06 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 12 Apr 2024 00:01:32 +0800 Subject: [PATCH 103/349] =?UTF-8?q?mod:=20=E6=9C=80=E8=BF=91=E6=8A=95?= =?UTF-8?q?=E5=B8=81=E8=A7=86=E9=A2=91=E6=A0=87=E9=A2=98=E5=B7=A6=E5=AF=B9?= =?UTF-8?q?=E9=BD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_coin/widgets/item.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/member_coin/widgets/item.dart b/lib/pages/member_coin/widgets/item.dart index ea6e7ee1..de28585c 100644 --- a/lib/pages/member_coin/widgets/item.dart +++ b/lib/pages/member_coin/widgets/item.dart @@ -59,6 +59,7 @@ class MemberCoinsItem extends StatelessWidget { padding: const EdgeInsets.fromLTRB(5, 6, 0, 0), child: Column( mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( coinItem.title!, From 1076c02a58173d4cdc25fbc85f7b03a9a2171557 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 12 Apr 2024 23:12:12 +0800 Subject: [PATCH 104/349] =?UTF-8?q?fix:=20android=2012=E6=9D=83=E9=99=90?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/download.dart | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/lib/utils/download.dart b/lib/utils/download.dart index e27335d0..2aff8999 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -1,5 +1,7 @@ +import 'dart:io'; import 'dart:typed_data'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -11,7 +13,8 @@ class DownloadUtils { static Future requestStoragePer() async { await Permission.storage.request(); PermissionStatus status = await Permission.storage.status; - if (status == PermissionStatus.denied) { + if (status == PermissionStatus.denied || + status == PermissionStatus.permanentlyDenied) { SmartDialog.show( useSystem: true, animationType: SmartAnimationType.centerFade_otherSlide, @@ -40,7 +43,8 @@ class DownloadUtils { static Future requestPhotoPer() async { await Permission.photos.request(); PermissionStatus status = await Permission.photos.status; - if (status == PermissionStatus.denied) { + if (status == PermissionStatus.denied || + status == PermissionStatus.permanentlyDenied) { SmartDialog.show( useSystem: true, animationType: SmartAnimationType.centerFade_otherSlide, @@ -68,9 +72,20 @@ class DownloadUtils { static Future downloadImg(String imgUrl, {String imgType = 'cover'}) async { try { - if (!await requestPhotoPer()) { + if (!Platform.isAndroid || !await requestPhotoPer()) { return false; } + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt <= 32) { + if (!await requestStoragePer()) { + return false; + } + } else { + if (!await requestPhotoPer()) { + return false; + } + } + SmartDialog.showLoading(msg: '保存中'); var response = await Dio() .get(imgUrl, options: Options(responseType: ResponseType.bytes)); From 297ad6a46d600bdfc664f54d44a3ccb9ab6ba2bd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 01:41:24 +0800 Subject: [PATCH 105/349] =?UTF-8?q?opt:=20navBar=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 85 +++++++++++++++++++------------------- lib/pages/main/view.dart | 6 +-- lib/utils/global_data.dart | 6 +++ 3 files changed, 52 insertions(+), 45 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 44bb1dcd..c3f5814a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,6 +20,7 @@ import 'package:pilipala/services/disable_battery_opt.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. import 'package:pilipala/utils/recommend_filter.dart'; @@ -63,14 +64,8 @@ void main() async { }, ); - // 小白条、导航栏沉浸 - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - systemNavigationBarDividerColor: Colors.transparent, - statusBarColor: Colors.transparent, - )); Data.init(); + GlobalData(); PiliSchame.init(); DisableBatteryOpt(); }); @@ -133,45 +128,51 @@ class MyApp extends StatelessWidget { brightness: Brightness.dark, ); } + + final SnackBarThemeData snackBarThemeData = SnackBarThemeData( + actionTextColor: darkColorScheme.primary, + backgroundColor: darkColorScheme.secondaryContainer, + closeIconColor: darkColorScheme.secondary, + contentTextStyle: TextStyle(color: darkColorScheme.secondary), + elevation: 20, + ); + + ThemeData themeData = ThemeData( + // fontFamily: 'HarmonyOS', + colorScheme: currentThemeValue == ThemeType.dark + ? darkColorScheme + : lightColorScheme, + snackBarTheme: snackBarThemeData, + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: ZoomPageTransitionsBuilder( + allowEnterRouteSnapshotting: false, + ), + }, + ), + ); + + // 小白条、导航栏沉浸 + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + systemNavigationBarColor: GlobalData().enableMYBar + ? themeData.colorScheme.surfaceVariant + : themeData.canvasColor, + systemNavigationBarDividerColor: GlobalData().enableMYBar + ? themeData.colorScheme.surfaceVariant + : themeData.canvasColor, + systemNavigationBarIconBrightness: currentThemeValue == ThemeType.dark + ? Brightness.light + : Brightness.dark, + statusBarColor: Colors.transparent, + )); + // 图片缓存 // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; return GetMaterialApp( title: 'PiLiPaLa', - theme: ThemeData( - // fontFamily: 'HarmonyOS', - colorScheme: currentThemeValue == ThemeType.dark - ? darkColorScheme - : lightColorScheme, - useMaterial3: true, - snackBarTheme: SnackBarThemeData( - actionTextColor: lightColorScheme.primary, - backgroundColor: lightColorScheme.secondaryContainer, - closeIconColor: lightColorScheme.secondary, - contentTextStyle: TextStyle(color: lightColorScheme.secondary), - elevation: 20, - ), - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.android: ZoomPageTransitionsBuilder( - allowEnterRouteSnapshotting: false, - ), - }, - ), - ), - darkTheme: ThemeData( - // fontFamily: 'HarmonyOS', - colorScheme: currentThemeValue == ThemeType.light - ? lightColorScheme - : darkColorScheme, - useMaterial3: true, - snackBarTheme: SnackBarThemeData( - actionTextColor: darkColorScheme.primary, - backgroundColor: darkColorScheme.secondaryContainer, - closeIconColor: darkColorScheme.secondary, - contentTextStyle: TextStyle(color: darkColorScheme.secondary), - elevation: 20, - ), - ), + theme: themeData, + darkTheme: themeData, localizationsDelegates: const [ GlobalCupertinoLocalizations.delegate, GlobalMaterialLocalizations.delegate, diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index e4e980af..731134e9 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -10,6 +10,7 @@ import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/rank/index.dart'; import 'package:pilipala/utils/event_bus.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import './controller.dart'; @@ -29,7 +30,6 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { int? _lastSelectTime; //上次点击时间 Box setting = GStrorage.setting; - late bool enableMYBar; @override void initState() { @@ -37,7 +37,6 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { _lastSelectTime = DateTime.now().millisecondsSinceEpoch; _mainController.pageController = PageController(initialPage: _mainController.selectedIndex); - enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); } void setIndex(int value) async { @@ -138,7 +137,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { curve: Curves.easeInOutCubicEmphasized, duration: const Duration(milliseconds: 500), offset: Offset(0, snapshot.data ? 0 : 1), - child: enableMYBar + child: GlobalData().enableMYBar ? NavigationBar( onDestinationSelected: (value) => setIndex(value), selectedIndex: _mainController.selectedIndex, @@ -169,6 +168,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { ) : BottomNavigationBar( currentIndex: _mainController.selectedIndex, + type: BottomNavigationBarType.fixed, onTap: (value) => setIndex(value), iconSize: 16, selectedFontSize: 12, diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart index ef3daf21..29791210 100644 --- a/lib/utils/global_data.dart +++ b/lib/utils/global_data.dart @@ -1,10 +1,16 @@ +import 'package:hive/hive.dart'; +import 'package:pilipala/utils/storage.dart'; import '../models/common/index.dart'; +Box setting = GStrorage.setting; + class GlobalData { int imgQuality = 10; FullScreenGestureMode fullScreenGestureMode = FullScreenGestureMode.values.last; bool enablePlayerControlAnimation = true; + final bool enableMYBar = + setting.get(SettingBoxKey.enableMYBar, defaultValue: true); // 私有构造函数 GlobalData._(); From ef6070aa8b1a6b52d840ce9e8cceb40d4ee4fd34 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 10:35:06 +0800 Subject: [PATCH 106/349] mod: systemNavBarColor --- lib/main.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index c3f5814a..3996a5fd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -156,10 +156,10 @@ class MyApp extends StatelessWidget { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( systemNavigationBarColor: GlobalData().enableMYBar - ? themeData.colorScheme.surfaceVariant + ? const Color(0x00010000) : themeData.canvasColor, systemNavigationBarDividerColor: GlobalData().enableMYBar - ? themeData.colorScheme.surfaceVariant + ? const Color(0x00010000) : themeData.canvasColor, systemNavigationBarIconBrightness: currentThemeValue == ThemeType.dark ? Brightness.light From 504d9e20653980e174876e6a002b9b6186da75c9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 10:55:14 +0800 Subject: [PATCH 107/349] opt: stream listener --- lib/pages/bangumi/view.dart | 19 ++------------ lib/pages/dynamics/detail/view.dart | 2 +- lib/pages/dynamics/view.dart | 14 ++-------- lib/pages/fav_detail/view.dart | 2 +- lib/pages/home/view.dart | 2 +- lib/pages/hot/view.dart | 19 ++------------ lib/pages/live/view.dart | 19 ++------------ lib/pages/main/view.dart | 2 +- lib/pages/media/view.dart | 19 ++------------ lib/pages/member/view.dart | 2 +- lib/pages/rank/zone/view.dart | 19 ++------------ lib/pages/rcmd/view.dart | 18 ++----------- lib/pages/subscription_detail/view.dart | 2 +- lib/pages/video/detail/view.dart | 2 +- lib/utils/main_stream.dart | 34 +++++++++++++++++++++++++ 15 files changed, 55 insertions(+), 120 deletions(-) create mode 100644 lib/utils/main_stream.dart diff --git a/lib/pages/bangumi/view.dart b/lib/pages/bangumi/view.dart index f59f94a2..8759af65 100644 --- a/lib/pages/bangumi/view.dart +++ b/lib/pages/bangumi/view.dart @@ -2,13 +2,11 @@ import 'dart:async'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:nil/nil.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'controller.dart'; import 'widgets/bangumu_card_v.dart'; @@ -34,10 +32,6 @@ class _BangumiPageState extends State void initState() { super.initState(); scrollController = _bangumidController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; _futureBuilderFuture = _bangumidController.queryBangumiListFeed(); _futureBuilderFutureFollow = _bangumidController.queryBangumiFollow(); scrollController.addListener( @@ -49,16 +43,7 @@ class _BangumiPageState extends State _bangumidController.onLoad(); }); } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 840cd33f..9da085f4 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -194,7 +194,7 @@ class _DynamicDetailPageState extends State centerTitle: false, titleSpacing: 0, title: StreamBuilder( - stream: titleStreamC.stream, + stream: titleStreamC.stream.distinct(), initialData: false, builder: (context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index fe594a43..82a555b1 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -3,15 +3,14 @@ import 'dart:async'; import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/skeleton/dynamic_card.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/models/dynamics/result.dart'; -import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'package:pilipala/utils/storage.dart'; import '../mine/controller.dart'; @@ -44,8 +43,6 @@ class _DynamicsPageState extends State _futureBuilderFuture = _dynamicsController.queryFollowDynamic(); _futureBuilderFutureUp = _dynamicsController.queryFollowUp(); scrollController = _dynamicsController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; scrollController.addListener( () async { if (scrollController.position.pixels >= @@ -55,14 +52,7 @@ class _DynamicsPageState extends State _dynamicsController.queryFollowDynamic(type: 'onLoad'); }); } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - } + handleScrollEvent(scrollController); }, ); diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index d94f5149..74faa829 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -67,7 +67,7 @@ class _FavDetailPageState extends State { pinned: true, titleSpacing: 0, title: StreamBuilder( - stream: titleStreamC.stream, + stream: titleStreamC.stream.distinct(), initialData: false, builder: (context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index b0cef90b..cc228f6b 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -171,7 +171,7 @@ class CustomAppBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { return StreamBuilder( - stream: stream, + stream: stream!.distinct(), initialData: true, builder: (BuildContext context, AsyncSnapshot snapshot) { final RxBool isUserLoggedIn = ctr!.userLogin; diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index 7a0a57ea..e2e20e73 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart'; @@ -9,9 +8,8 @@ import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; -import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/hot/controller.dart'; -import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; class HotPage extends StatefulWidget { const HotPage({Key? key}) : super(key: key); @@ -34,10 +32,6 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { super.initState(); _futureBuilderFuture = _hotController.queryHotFeed('init'); scrollController = _hotController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -47,16 +41,7 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { _hotController.onLoad(); } } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index f3f91c9e..c61d20b3 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -2,15 +2,13 @@ import 'dart:async'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/overlay_pop.dart'; -import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'controller.dart'; import 'widgets/live_item.dart'; @@ -36,10 +34,6 @@ class _LivePageState extends State super.initState(); _futureBuilderFuture = _liveController.queryLiveList('init'); scrollController = _liveController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -49,16 +43,7 @@ class _LivePageState extends State _liveController.onLoad(); }); } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index c551e690..5353ac52 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -129,7 +129,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { ), bottomNavigationBar: StreamBuilder( stream: _mainController.hideTabBar - ? _mainController.bottomBarStream.stream + ? _mainController.bottomBarStream.stream.distinct() : StreamController.broadcast().stream, initialData: true, builder: (context, AsyncSnapshot snapshot) { diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 460c5648..0bb12039 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -1,13 +1,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; -import 'package:media_kit/media_kit.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/user/fav_folder.dart'; -import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/media/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'package:pilipala/utils/utils.dart'; class MediaPage extends StatefulWidget { @@ -31,25 +29,12 @@ class _MediaPageState extends State mediaController = Get.put(MediaController()); _futureBuilderFuture = mediaController.queryFavFolder(); ScrollController scrollController = mediaController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - mediaController.userLogin.listen((status) { setState(() { _futureBuilderFuture = mediaController.queryFavFolder(); }); }); - scrollController.addListener( - () { - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - } - }, - ); + handleScrollEvent(scrollController); } @override diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index c8a9f406..015750db 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -65,7 +65,7 @@ class _MemberPageState extends State children: [ AppBar( title: StreamBuilder( - stream: appbarStream.stream, + stream: appbarStream.stream.distinct(), initialData: false, builder: (BuildContext context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/rank/zone/view.dart b/lib/pages/rank/zone/view.dart index fbf8a524..04631a8c 100644 --- a/lib/pages/rank/zone/view.dart +++ b/lib/pages/rank/zone/view.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/animated_dialog.dart'; @@ -9,9 +8,8 @@ import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; -import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/rank/zone/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; class ZonePage extends StatefulWidget { const ZonePage({Key? key, required this.rid}) : super(key: key); @@ -38,10 +36,6 @@ class _ZonePageState extends State _zoneController = Get.put(ZoneController(), tag: widget.rid.toString()); _futureBuilderFuture = _zoneController.queryRankFeed('init', widget.rid); scrollController = _zoneController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -51,16 +45,7 @@ class _ZonePageState extends State _zoneController.onLoad(); } } - - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index d732f370..acc1e654 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; @@ -10,8 +9,7 @@ import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_v.dart'; -import 'package:pilipala/pages/home/index.dart'; -import 'package:pilipala/pages/main/index.dart'; +import 'package:pilipala/utils/main_stream.dart'; import 'controller.dart'; @@ -35,10 +33,6 @@ class _RcmdPageState extends State super.initState(); _futureBuilderFuture = _rcmdController.queryRcmdFeed('init'); ScrollController scrollController = _rcmdController.scrollController; - StreamController mainStream = - Get.find().bottomBarStream; - StreamController searchBarStream = - Get.find().searchBarStream; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -49,15 +43,7 @@ class _RcmdPageState extends State _rcmdController.onLoad(); }); } - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + handleScrollEvent(scrollController); }, ); } diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index 93e0abbb..2c219e58 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -67,7 +67,7 @@ class _SubDetailPageState extends State { pinned: true, titleSpacing: 0, title: StreamBuilder( - stream: titleStreamC.stream, + stream: titleStreamC.stream.distinct(), initialData: false, builder: (context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 822d0a45..7bb4d909 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -618,7 +618,7 @@ class _VideoDetailPageState extends State /// 重新进入会刷新 // 播放完成/暂停播放 StreamBuilder( - stream: appbarStream.stream, + stream: appbarStream.stream.distinct(), initialData: 0, builder: ((context, snapshot) { return ScrollAppBar( diff --git a/lib/utils/main_stream.dart b/lib/utils/main_stream.dart new file mode 100644 index 00000000..20c160de --- /dev/null +++ b/lib/utils/main_stream.dart @@ -0,0 +1,34 @@ +import 'dart:async'; +import 'package:flutter/material.dart'; +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/rendering.dart'; +import 'package:get/get.dart'; + +import '../pages/home/index.dart'; +import '../pages/main/index.dart'; + +void handleScrollEvent( + ScrollController scrollController, + // StreamController mainStream, + // StreamController? searchBarStream, +) { + StreamController mainStream = + Get.find().bottomBarStream; + StreamController searchBarStream = + Get.find().searchBarStream; + EasyThrottle.throttle( + 'stream-throttler', + const Duration(milliseconds: 300), + () { + final ScrollDirection direction = + scrollController.position.userScrollDirection; + if (direction == ScrollDirection.forward) { + mainStream.add(true); + searchBarStream.add(true); + } else if (direction == ScrollDirection.reverse) { + mainStream.add(false); + searchBarStream.add(false); + } + }, + ); +} From 83d121fd9cd0d2978df8ce7ed9b5327c63f61c0b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 15:04:33 +0800 Subject: [PATCH 108/349] opt: sheetHeight --- lib/pages/video/detail/controller.dart | 2 + lib/pages/video/detail/introduction/view.dart | 6 +-- lib/pages/video/detail/reply_reply/view.dart | 44 +++++++++---------- lib/pages/video/detail/view.dart | 4 +- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 2f750a24..4d40e535 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -106,6 +106,7 @@ class VideoDetailController extends GetxController BottomControlType.fit, BottomControlType.fullscreen, ].obs; + RxDouble sheetHeight = 0.0.obs; @override void onInit() { @@ -172,6 +173,7 @@ class VideoDetailController extends GetxController firstFloor: firstFloor, replyType: ReplyType.video, source: 'videoDetail', + sheetHeight: sheetHeight.value, ); }); replyReplyBottomSheetCtr?.closed.then((value) { diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 70fa578d..a7eae6d2 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -372,7 +372,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), /// 点赞收藏转发 - actionGrid(context, videoIntroController), + Material(child: actionGrid(context, videoIntroController)), // 合集 videoPart 简洁 if (widget.videoDetail!.ugcSeason != null) ...[ Obx( @@ -381,7 +381,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { cid: videoIntroController.lastPlayCid.value != 0 ? videoIntroController.lastPlayCid.value : widget.videoDetail!.pages!.first.cid, - sheetHeight: sheetHeight, + sheetHeight: videoDetailCtr.sheetHeight.value, changeFuc: (bvid, cid, aid) => videoIntroController.changeSeasonOrbangu( bvid, @@ -399,7 +399,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { () => PagesPanel( pages: widget.videoDetail!.pages!, cid: videoIntroController.lastPlayCid.value, - sheetHeight: sheetHeight, + sheetHeight: videoDetailCtr.sheetHeight.value, changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( videoIntroController.bvid, cid, diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 344ca1b1..3fe84c71 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -19,6 +19,7 @@ class VideoReplyReplyPanel extends StatefulWidget { this.firstFloor, this.source, this.replyType, + this.sheetHeight, super.key, }); final int? oid; @@ -27,6 +28,7 @@ class VideoReplyReplyPanel extends StatefulWidget { final ReplyItemModel? firstFloor; final String? source; final ReplyType? replyType; + final double? sheetHeight; @override State createState() => _VideoReplyReplyPanelState(); @@ -36,7 +38,6 @@ class _VideoReplyReplyPanelState extends State { late VideoReplyReplyController _videoReplyReplyController; late AnimationController replyAnimationCtl; final Box localCache = GStrorage.localCache; - late double sheetHeight; Future? _futureBuilderFuture; late ScrollController scrollController; @@ -62,7 +63,6 @@ class _VideoReplyReplyPanelState extends State { }, ); - sheetHeight = localCache.get('sheetHeight'); _futureBuilderFuture = _videoReplyReplyController.queryReplyList(); } @@ -77,33 +77,31 @@ class _VideoReplyReplyPanelState extends State { @override Widget build(BuildContext context) { return Container( - height: widget.source == 'videoDetail' ? sheetHeight : null, + height: widget.source == 'videoDetail' ? widget.sheetHeight : null, color: Theme.of(context).colorScheme.background, child: Column( children: [ if (widget.source == 'videoDetail') - Container( - height: 45, - padding: const EdgeInsets.only(left: 12, right: 2), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text('评论详情'), - IconButton( - icon: const Icon(Icons.close, size: 20), - onPressed: () { - _videoReplyReplyController.currentPage = 0; - widget.closePanel?.call; - Navigator.pop(context); - }, - ), - ], + AppBar( + toolbarHeight: 45, + automaticallyImplyLeading: false, + centerTitle: false, + title: Text( + '评论详情', + style: Theme.of(context).textTheme.titleSmall, ), + actions: [ + IconButton( + icon: const Icon(Icons.close, size: 20), + onPressed: () { + _videoReplyReplyController.currentPage = 0; + widget.closePanel?.call; + Navigator.pop(context); + }, + ), + const SizedBox(width: 14), + ], ), - Divider( - height: 1, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), Expanded( child: RefreshIndicator( onRefresh: () async { diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 10b60c10..05ce2f5e 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -67,6 +67,7 @@ class _VideoDetailPageState extends State super.initState(); heroTag = Get.arguments['heroTag']; vdCtr = Get.put(VideoDetailController(), tag: heroTag); + vdCtr.sheetHeight.value = localCache.get('sheetHeight'); videoIntroController = Get.put( VideoIntroController(bvid: Get.parameters['bvid']!), tag: heroTag); @@ -112,6 +113,8 @@ class _VideoDetailPageState extends State _extendNestCtr.addListener( () { final double offset = _extendNestCtr.position.pixels; + vdCtr.sheetHeight.value = + Get.size.height - videoHeight - statusBarHeight + offset; appbarStream.add(offset); }, ); @@ -271,7 +274,6 @@ class _VideoDetailPageState extends State @override Widget build(BuildContext context) { - // final double videoHeight = MediaQuery.sizeOf(context).width * 9 / 16; final sizeContext = MediaQuery.sizeOf(context); final _context = MediaQuery.of(context); late double defaultVideoHeight = sizeContext.width * 9 / 16; From ff5101d3d0cf71e9f2dd856f8886d8bd8ab86f11 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 15:46:12 +0800 Subject: [PATCH 109/349] =?UTF-8?q?opt:=20=E5=8F=AF=E6=8B=96=E5=8A=A8?= =?UTF-8?q?=E6=A0=87=E8=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 05ce2f5e..7a1de4e4 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -47,7 +47,7 @@ class _VideoDetailPageState extends State late BangumiIntroController bangumiIntroController; late String heroTag; - PlayerStatus playerStatus = PlayerStatus.playing; + Rx playerStatus = PlayerStatus.playing.obs; double doubleOffset = 0; final Box localCache = GStrorage.localCache; @@ -122,7 +122,7 @@ class _VideoDetailPageState extends State // 播放器状态监听 void playerListener(PlayerStatus? status) async { - playerStatus = status!; + playerStatus.value = status!; if (status == PlayerStatus.completed) { // 结束播放退出全屏 if (autoExitFullcreen) { @@ -368,6 +368,18 @@ class _VideoDetailPageState extends State child: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ + Obx(() => AnimatedOpacity( + opacity: playerStatus.value != PlayerStatus.playing + ? 1 + : 0, + duration: const Duration(milliseconds: 100), + child: const Icon( + Icons.drag_handle_rounded, + size: 20, + color: Colors.grey, + ), + )), + const SizedBox(width: 8), SizedBox( height: 32, child: TextButton( @@ -406,7 +418,7 @@ class _VideoDetailPageState extends State ), ), ), - const SizedBox(width: 14), + const SizedBox(width: 18), ], ), )), @@ -559,7 +571,7 @@ class _VideoDetailPageState extends State Orientation.landscape || plPlayerController?.isFullScreen.value == true ? MediaQuery.sizeOf(context).height - : playerStatus != PlayerStatus.playing + : playerStatus.value != PlayerStatus.playing ? kToolbarHeight : pinnedHeaderHeight; }, @@ -626,7 +638,7 @@ class _VideoDetailPageState extends State return ScrollAppBar( snapshot.data!.toDouble(), () => continuePlay(), - playerStatus, + playerStatus.value, null, ); }), From b1c801c8c82f79006fb7e4157c9f686572c79f3b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 16:31:28 +0800 Subject: [PATCH 110/349] fix: mediaPage stream error --- lib/pages/media/view.dart | 2 -- lib/utils/main_stream.dart | 26 ++++++++++++-------------- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 0bb12039..6541680a 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -28,13 +28,11 @@ class _MediaPageState extends State super.initState(); mediaController = Get.put(MediaController()); _futureBuilderFuture = mediaController.queryFavFolder(); - ScrollController scrollController = mediaController.scrollController; mediaController.userLogin.listen((status) { setState(() { _futureBuilderFuture = mediaController.queryFavFolder(); }); }); - handleScrollEvent(scrollController); } @override diff --git a/lib/utils/main_stream.dart b/lib/utils/main_stream.dart index 20c160de..e63248f8 100644 --- a/lib/utils/main_stream.dart +++ b/lib/utils/main_stream.dart @@ -7,11 +7,7 @@ import 'package:get/get.dart'; import '../pages/home/index.dart'; import '../pages/main/index.dart'; -void handleScrollEvent( - ScrollController scrollController, - // StreamController mainStream, - // StreamController? searchBarStream, -) { +void handleScrollEvent(ScrollController scrollController) { StreamController mainStream = Get.find().bottomBarStream; StreamController searchBarStream = @@ -20,15 +16,17 @@ void handleScrollEvent( 'stream-throttler', const Duration(milliseconds: 300), () { - final ScrollDirection direction = - scrollController.position.userScrollDirection; - if (direction == ScrollDirection.forward) { - mainStream.add(true); - searchBarStream.add(true); - } else if (direction == ScrollDirection.reverse) { - mainStream.add(false); - searchBarStream.add(false); - } + try { + final ScrollDirection direction = + scrollController.position.userScrollDirection; + if (direction == ScrollDirection.forward) { + mainStream.add(true); + searchBarStream.add(true); + } else if (direction == ScrollDirection.reverse) { + mainStream.add(false); + searchBarStream.add(false); + } + } catch (_) {} }, ); } From 894f4fa68da085eeb30353d69ef69d980ba47c94 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 22:57:18 +0800 Subject: [PATCH 111/349] =?UTF-8?q?opt:=20index=E4=B8=BA0=E6=97=B6?= =?UTF-8?q?=E5=90=88=E9=9B=86=E8=B7=B3=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/widgets/bangumi_panel.dart | 14 +++++++++----- .../detail/introduction/widgets/page_panel.dart | 17 +++++++++-------- .../introduction/widgets/season_panel.dart | 2 -- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 988f8d4f..58078d80 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -92,11 +92,15 @@ class _BangumiPanelState extends State { // 在回调函数中获取更新后的状态 final double offset = min((currentIndex * 150) - 75, listViewScrollCtr.position.maxScrollExtent); - listViewScrollCtr.animateTo( - offset, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); + if (currentIndex.value == 0) { + listViewScrollCtr.jumpTo(0); + } else { + listViewScrollCtr.animateTo( + offset, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } }); } diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index fc999ba8..83db2d52 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -5,7 +5,6 @@ import 'package:get/get.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; -import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../../../../../common/pages_bottom_sheet.dart'; import '../../../../../models/common/video_episode_type.dart'; @@ -35,7 +34,6 @@ class _PagesPanelState extends State { final String heroTag = Get.arguments['heroTag']; late VideoDetailController _videoDetailController; final ScrollController listViewScrollCtr = ScrollController(); - final ItemScrollController itemScrollController = ItemScrollController(); late PersistentBottomSheetController? _bottomSheetController; @override @@ -60,7 +58,6 @@ class _PagesPanelState extends State { } void changeFucCall(item, i) async { - print('pages changeFucCall'); widget.changeFuc?.call(item.cid); currentIndex.value = i; _bottomSheetController?.close(); @@ -72,11 +69,15 @@ class _PagesPanelState extends State { // 在回调函数中获取更新后的状态 final double offset = min((currentIndex * 150) - 75, listViewScrollCtr.position.maxScrollExtent); - listViewScrollCtr.animateTo( - offset, - duration: const Duration(milliseconds: 300), - curve: Curves.easeInOut, - ); + if (currentIndex.value == 0) { + listViewScrollCtr.jumpTo(0); + } else { + listViewScrollCtr.animateTo( + offset, + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + ); + } }); } diff --git a/lib/pages/video/detail/introduction/widgets/season_panel.dart b/lib/pages/video/detail/introduction/widgets/season_panel.dart index 745c081d..2afefcb4 100644 --- a/lib/pages/video/detail/introduction/widgets/season_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/season_panel.dart @@ -4,7 +4,6 @@ import 'package:pilipala/common/pages_bottom_sheet.dart'; import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/utils/id_utils.dart'; -import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../../../../../models/common/video_episode_type.dart'; import '../controller.dart'; @@ -33,7 +32,6 @@ class _SeasonPanelState extends State { late RxInt currentIndex = (-1).obs; final String heroTag = Get.arguments['heroTag']; late VideoDetailController _videoDetailController; - final ItemScrollController itemScrollController = ItemScrollController(); late PersistentBottomSheetController? _bottomSheetController; @override From f1334b550554801074cac46bd8d22318f3164c9d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Apr 2024 23:34:46 +0800 Subject: [PATCH 112/349] =?UTF-8?q?feat:=20=E7=95=AA=E5=89=A7=E9=80=89?= =?UTF-8?q?=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bangumi/introduction/controller.dart | 5 +++++ lib/pages/bangumi/introduction/view.dart | 1 + lib/pages/bangumi/widgets/bangumi_panel.dart | 6 +++++- lib/pages/video/detail/view.dart | 19 ++++++++++++++----- 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 63eadacf..2098302d 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -56,6 +56,7 @@ class BangumiIntroController extends GetxController { RxMap followStatus = {}.obs; int _tempThemeValue = -1; var userInfo; + PersistentBottomSheetController? bottomSheetController; @override void onInit() { @@ -320,4 +321,8 @@ class BangumiIntroController extends GetxController { ).buildShowContent(Get.context!), ); } + + hiddenEpisodeBottomSheet() { + bottomSheetController?.close(); + } } diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 13db7432..e47db480 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -325,6 +325,7 @@ class _BangumiInfoState extends State { changeFuc: (bvid, cid, aid) => bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid), bangumiDetail: bangumiIntroController.bangumiDetail.value, + bangumiIntroController: bangumiIntroController, ) ], ], diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 58078d80..b01f3be7 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -10,6 +10,7 @@ import 'package:pilipala/utils/storage.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../../../common/pages_bottom_sheet.dart'; import '../../../models/common/video_episode_type.dart'; +import '../introduction/controller.dart'; class BangumiPanel extends StatefulWidget { const BangumiPanel({ @@ -19,6 +20,7 @@ class BangumiPanel extends StatefulWidget { this.sheetHeight, this.changeFuc, this.bangumiDetail, + this.bangumiIntroController, }); final List pages; @@ -26,6 +28,7 @@ class BangumiPanel extends StatefulWidget { final double? sheetHeight; final Function? changeFuc; final BangumiInfoModel? bangumiDetail; + final BangumiIntroController? bangumiIntroController; @override State createState() => _BangumiPanelState(); @@ -136,7 +139,8 @@ class _BangumiPanelState extends State { padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { - _bottomSheetController = EpisodeBottomSheet( + widget.bangumiIntroController?.bottomSheetController = + _bottomSheetController = EpisodeBottomSheet( currentCid: cid, episodes: widget.pages, changeFucCall: changeFucCall, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 7a1de4e4..687baaf0 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -180,11 +180,20 @@ class _VideoDetailPageState extends State plPlayerController?.isFullScreen.listen((bool isFullScreen) { if (isFullScreen) { vdCtr.hiddenReplyReplyPanel(); - videoIntroController.hiddenEpisodeBottomSheet(); - if (videoIntroController.videoDetail.value.ugcSeason != null || - (videoIntroController.videoDetail.value.pages != null && - videoIntroController.videoDetail.value.pages!.length > 1)) { - vdCtr.bottomList.insert(3, BottomControlType.episode); + if (vdCtr.videoType == SearchType.video) { + videoIntroController.hiddenEpisodeBottomSheet(); + if (videoIntroController.videoDetail.value.ugcSeason != null || + (videoIntroController.videoDetail.value.pages != null && + videoIntroController.videoDetail.value.pages!.length > 1)) { + vdCtr.bottomList.insert(3, BottomControlType.episode); + } + } + if (vdCtr.videoType == SearchType.media_bangumi) { + bangumiIntroController.hiddenEpisodeBottomSheet(); + if (bangumiIntroController.bangumiDetail.value.episodes != null && + bangumiIntroController.bangumiDetail.value.episodes!.length > 1) { + vdCtr.bottomList.insert(3, BottomControlType.episode); + } } } else { if (vdCtr.bottomList.contains(BottomControlType.episode)) { From cc5e3705101f35390d2aad0f11af26824ec56cd1 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Apr 2024 00:47:40 +0800 Subject: [PATCH 113/349] =?UTF-8?q?fix:=20=E8=B7=9F=E9=9A=8F=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E4=B8=BB=E9=A2=98=E6=A8=A1=E5=BC=8F=E5=A4=B1=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 52 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 604441e8..3877685c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -130,27 +130,10 @@ class MyApp extends StatelessWidget { ); } - final SnackBarThemeData snackBarThemeData = SnackBarThemeData( - actionTextColor: darkColorScheme.primary, - backgroundColor: darkColorScheme.secondaryContainer, - closeIconColor: darkColorScheme.secondary, - contentTextStyle: TextStyle(color: darkColorScheme.secondary), - elevation: 20, - ); - ThemeData themeData = ThemeData( - // fontFamily: 'HarmonyOS', colorScheme: currentThemeValue == ThemeType.dark ? darkColorScheme : lightColorScheme, - snackBarTheme: snackBarThemeData, - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.android: ZoomPageTransitionsBuilder( - allowEnterRouteSnapshotting: false, - ), - }, - ), ); // 小白条、导航栏沉浸 @@ -171,9 +154,38 @@ class MyApp extends StatelessWidget { // 图片缓存 // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; return GetMaterialApp( - title: 'PiLiPaLa', - theme: themeData, - darkTheme: themeData, + title: 'PiliPala', + theme: ThemeData( + colorScheme: currentThemeValue == ThemeType.dark + ? darkColorScheme + : lightColorScheme, + snackBarTheme: SnackBarThemeData( + actionTextColor: lightColorScheme.primary, + backgroundColor: lightColorScheme.secondaryContainer, + closeIconColor: lightColorScheme.secondary, + contentTextStyle: TextStyle(color: lightColorScheme.secondary), + elevation: 20, + ), + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: ZoomPageTransitionsBuilder( + allowEnterRouteSnapshotting: false, + ), + }, + ), + ), + darkTheme: ThemeData( + colorScheme: currentThemeValue == ThemeType.light + ? lightColorScheme + : darkColorScheme, + snackBarTheme: SnackBarThemeData( + actionTextColor: darkColorScheme.primary, + backgroundColor: darkColorScheme.secondaryContainer, + closeIconColor: darkColorScheme.secondary, + contentTextStyle: TextStyle(color: darkColorScheme.secondary), + elevation: 20, + ), + ), localizationsDelegates: const [ GlobalCupertinoLocalizations.delegate, GlobalMaterialLocalizations.delegate, From e7f46883ed94bbae03687432997845f1228946ca Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Apr 2024 17:00:29 +0800 Subject: [PATCH 114/349] =?UTF-8?q?fix:=20=E6=9C=AA=E5=BC=80=E5=90=AF?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E6=92=AD=E6=94=BE=E6=97=B6=E5=BC=B9=E5=B9=95?= =?UTF-8?q?=E5=BC=80=E5=85=B3=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 46 +++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 687baaf0..0152a2cb 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -404,27 +404,41 @@ class _VideoDetailPageState extends State width: 38, height: 38, child: Obx( - () => IconButton( - onPressed: () { - plPlayerController?.isOpenDanmu.value = - !(plPlayerController?.isOpenDanmu.value ?? - false); - }, - icon: !(plPlayerController?.isOpenDanmu.value ?? - false) - ? SvgPicture.asset( + () => !vdCtr.isShowCover.value + ? IconButton( + onPressed: () { + plPlayerController?.isOpenDanmu.value = + !(plPlayerController + ?.isOpenDanmu.value ?? + false); + }, + icon: + !(plPlayerController?.isOpenDanmu.value ?? + false) + ? SvgPicture.asset( + 'assets/images/video/danmu_close.svg', + // ignore: deprecated_member_use + color: Theme.of(context) + .colorScheme + .outline, + ) + : SvgPicture.asset( + 'assets/images/video/danmu_open.svg', + // ignore: deprecated_member_use + color: Theme.of(context) + .colorScheme + .primary, + ), + ) + : IconButton( + icon: SvgPicture.asset( 'assets/images/video/danmu_close.svg', // ignore: deprecated_member_use color: Theme.of(context).colorScheme.outline, - ) - : SvgPicture.asset( - 'assets/images/video/danmu_open.svg', - // ignore: deprecated_member_use - color: - Theme.of(context).colorScheme.primary, ), - ), + onPressed: () {}, + ), ), ), const SizedBox(width: 18), From d943e156d2337b5f1e81cec4bcfbb91f4c7513aa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Apr 2024 17:10:47 +0800 Subject: [PATCH 115/349] =?UTF-8?q?fix:=20=E5=85=B3=E9=97=AD=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E6=97=B6=E5=88=87=E6=8D=A2=E5=90=88=E9=9B=86=E5=A4=B1?= =?UTF-8?q?=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/controller.dart | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index d81bda00..6469b5c5 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -58,6 +58,7 @@ class VideoIntroController extends GetxController { String heroTag = ''; late ModelResult modelResult; PersistentBottomSheetController? bottomSheetController; + late bool enableRelatedVideo; @override void onInit() { @@ -74,6 +75,8 @@ class VideoIntroController extends GetxController { queryOnlineTotal(); startTimer(); // 在页面加载时启动定时器 } + enableRelatedVideo = + setting.get(SettingBoxKey.enableRelatedVideo, defaultValue: true); } // 获取视频简介&分p @@ -447,15 +450,18 @@ class VideoIntroController extends GetxController { // 重新获取视频资源 final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); - final ReleatedController releatedCtr = - Get.find(tag: heroTag); + if (enableRelatedVideo) { + final ReleatedController releatedCtr = + Get.find(tag: heroTag); + releatedCtr.bvid = bvid; + releatedCtr.queryRelatedVideo(); + } + videoDetailCtr.bvid = bvid; videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid); videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.queryVideoUrl(); - releatedCtr.bvid = bvid; - releatedCtr.queryRelatedVideo(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 From adba7b33d31a04a6720f4d6c7b671ee97988ed29 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 15 Apr 2024 22:59:35 +0800 Subject: [PATCH 116/349] =?UTF-8?q?fix:=20=E9=A2=84=E8=AE=BE=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E7=94=BB=E8=B4=A8=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/utils.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index a7273f05..987f57c1 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -210,15 +210,18 @@ class Utils { int minDiff = 127; int closestNumber = 0; // 初始化为0,表示没有找到比目标值小的整数 + if (numbers.contains(target)) { + return target; + } // 向下查找 try { for (int number in numbers) { if (number < target) { int diff = target - number; // 计算目标值与当前整数的差值 - if (diff < minDiff) { minDiff = diff; closestNumber = number; + return closestNumber; } } } From 76f0d5fba2f8152cec2f16a68ba4601439872c38 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 16 Apr 2024 00:11:36 +0800 Subject: [PATCH 117/349] fix: zoneController error --- lib/models/common/rank_type.dart | 18 ------------------ lib/pages/rank/controller.dart | 13 ++++++++----- lib/pages/rank/view.dart | 2 +- lib/pages/rank/zone/controller.dart | 15 +++++++++------ 4 files changed, 18 insertions(+), 30 deletions(-) diff --git a/lib/models/common/rank_type.dart b/lib/models/common/rank_type.dart index 2ce6d3b5..07be15c2 100644 --- a/lib/models/common/rank_type.dart +++ b/lib/models/common/rank_type.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:get/get.dart'; import 'package:pilipala/pages/rank/zone/index.dart'; enum RandType { @@ -74,7 +73,6 @@ List tabsConfig = [ ), 'label': '全站', 'type': RandType.all, - 'ctr': Get.put, 'page': const ZonePage(rid: 0), }, { @@ -84,7 +82,6 @@ List tabsConfig = [ ), 'label': '国创相关', 'type': RandType.creation, - 'ctr': Get.put, 'page': const ZonePage(rid: 168), }, { @@ -94,7 +91,6 @@ List tabsConfig = [ ), 'label': '动画', 'type': RandType.animation, - 'ctr': Get.put, 'page': const ZonePage(rid: 1), }, { @@ -104,7 +100,6 @@ List tabsConfig = [ ), 'label': '音乐', 'type': RandType.music, - 'ctr': Get.put, 'page': const ZonePage(rid: 3), }, { @@ -114,7 +109,6 @@ List tabsConfig = [ ), 'label': '舞蹈', 'type': RandType.dance, - 'ctr': Get.put, 'page': const ZonePage(rid: 129), }, { @@ -124,7 +118,6 @@ List tabsConfig = [ ), 'label': '游戏', 'type': RandType.game, - 'ctr': Get.put, 'page': const ZonePage(rid: 4), }, { @@ -134,7 +127,6 @@ List tabsConfig = [ ), 'label': '知识', 'type': RandType.knowledge, - 'ctr': Get.put, 'page': const ZonePage(rid: 36), }, { @@ -144,7 +136,6 @@ List tabsConfig = [ ), 'label': '科技', 'type': RandType.technology, - 'ctr': Get.put, 'page': const ZonePage(rid: 188), }, { @@ -154,7 +145,6 @@ List tabsConfig = [ ), 'label': '运动', 'type': RandType.sport, - 'ctr': Get.put, 'page': const ZonePage(rid: 234), }, { @@ -164,7 +154,6 @@ List tabsConfig = [ ), 'label': '汽车', 'type': RandType.car, - 'ctr': Get.put, 'page': const ZonePage(rid: 223), }, { @@ -174,7 +163,6 @@ List tabsConfig = [ ), 'label': '生活', 'type': RandType.life, - 'ctr': Get.put, 'page': const ZonePage(rid: 160), }, { @@ -184,7 +172,6 @@ List tabsConfig = [ ), 'label': '美食', 'type': RandType.food, - 'ctr': Get.put, 'page': const ZonePage(rid: 211), }, { @@ -194,7 +181,6 @@ List tabsConfig = [ ), 'label': '动物圈', 'type': RandType.animal, - 'ctr': Get.put, 'page': const ZonePage(rid: 217), }, { @@ -204,7 +190,6 @@ List tabsConfig = [ ), 'label': '鬼畜', 'type': RandType.madness, - 'ctr': Get.put, 'page': const ZonePage(rid: 119), }, { @@ -214,7 +199,6 @@ List tabsConfig = [ ), 'label': '时尚', 'type': RandType.fashion, - 'ctr': Get.put, 'page': const ZonePage(rid: 155), }, { @@ -224,7 +208,6 @@ List tabsConfig = [ ), 'label': '娱乐', 'type': RandType.entertainment, - 'ctr': Get.put, 'page': const ZonePage(rid: 5), }, { @@ -234,7 +217,6 @@ List tabsConfig = [ ), 'label': '影视', 'type': RandType.film, - 'ctr': Get.put, 'page': const ZonePage(rid: 181), } ]; diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index 6fe3d424..fa906fc9 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/rank_type.dart'; +import 'package:pilipala/pages/rank/zone/index.dart'; import 'package:pilipala/utils/storage.dart'; class RankController extends GetxController with GetTickerProviderStateMixin { @@ -29,20 +30,22 @@ class RankController extends GetxController with GetTickerProviderStateMixin { void onRefresh() { int index = tabController.index; - var ctr = tabsCtrList[index]; - ctr().onRefresh(); + final ZoneController ctr = tabsCtrList[index]; + ctr.onRefresh(); } void animateToTop() { int index = tabController.index; - var ctr = tabsCtrList[index]; - ctr().animateToTop(); + final ZoneController ctr = tabsCtrList[index]; + ctr.animateToTop(); } void setTabConfig() async { tabs.value = tabsConfig; initialIndex.value = 0; - tabsCtrList = tabs.map((e) => e['ctr']).toList(); + tabsCtrList = tabs + .map((e) => Get.put(ZoneController(), tag: e['rid'].toString())) + .toList(); tabsPageList = tabs.map((e) => e['page']).toList(); tabController = TabController( diff --git a/lib/pages/rank/view.dart b/lib/pages/rank/view.dart index 7b5b4906..4efa2b4e 100644 --- a/lib/pages/rank/view.dart +++ b/lib/pages/rank/view.dart @@ -102,7 +102,7 @@ class _RankPageState extends State onTap: (value) { feedBack(); if (_rankController.initialIndex.value == value) { - _rankController.tabsCtrList[value]().animateToTop(); + _rankController.tabsCtrList[value].animateToTop(); } _rankController.initialIndex.value = value; }, diff --git a/lib/pages/rank/zone/controller.dart b/lib/pages/rank/zone/controller.dart index f9f4dc6e..71f27b93 100644 --- a/lib/pages/rank/zone/controller.dart +++ b/lib/pages/rank/zone/controller.dart @@ -42,12 +42,15 @@ class ZoneController extends GetxController { // 返回顶部并刷新 void animateToTop() async { - if (scrollController.offset >= - MediaQuery.of(Get.context!).size.height * 5) { - scrollController.jumpTo(0); - } else { - await scrollController.animateTo(0, - duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); + if (scrollController.hasClients) { + if (scrollController.offset >= + MediaQuery.of(Get.context!).size.height * 5) { + scrollController.jumpTo(0); + } else { + await scrollController.animateTo(0, + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut); + } } } } From a8cfe9fa1afa946454c70134666e68a34ede3816 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 16 Apr 2024 23:38:03 +0800 Subject: [PATCH 118/349] =?UTF-8?q?fix:=20seekTo=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 74 +++++++++++++++------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index f936526b..1ede7045 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -473,17 +473,17 @@ class PlPlayerController { } // 字幕 - if (dataSource.subFiles != '' && dataSource.subFiles != null) { - await pp.setProperty( - 'sub-files', - UniversalPlatform.isWindows - ? dataSource.subFiles!.replaceAll(';', '\\;') - : dataSource.subFiles!.replaceAll(':', '\\:'), - ); - await pp.setProperty("subs-with-matching-audio", "no"); - await pp.setProperty("sub-forced-only", "yes"); - await pp.setProperty("blend-subtitles", "video"); - } + // if (dataSource.subFiles != '' && dataSource.subFiles != null) { + // await pp.setProperty( + // 'sub-files', + // UniversalPlatform.isWindows + // ? dataSource.subFiles!.replaceAll(';', '\\;') + // : dataSource.subFiles!.replaceAll(':', '\\:'), + // ); + // await pp.setProperty("subs-with-matching-audio", "no"); + // await pp.setProperty("sub-forced-only", "yes"); + // await pp.setProperty("blend-subtitles", "video"); + // } _videoController = _videoController ?? VideoController( @@ -603,7 +603,9 @@ class PlPlayerController { makeHeartBeat(event.inSeconds); }), videoPlayerController!.stream.duration.listen((event) { - duration.value = event; + if (event > Duration.zero) { + duration.value = event; + } }), videoPlayerController!.stream.buffer.listen((event) { _buffered.value = event; @@ -646,32 +648,38 @@ class PlPlayerController { /// 跳转至指定位置 Future seekTo(Duration position, {type = 'seek'}) async { - if (position < Duration.zero) { - position = Duration.zero; - } - _position.value = position; - updatePositionSecond(); - _heartDuration = position.inSeconds; - if (duration.value.inSeconds != 0) { - if (type != 'slider') { - /// 拖动进度条调节时,不等待第一帧,防止抖动 - await _videoPlayerController?.stream.buffer.first; + try { + if (position < Duration.zero) { + position = Duration.zero; } - await _videoPlayerController?.seek(position); - } else { - _timerForSeek?.cancel(); - _timerForSeek ??= - Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { - if (duration.value.inSeconds != 0) { - await _videoPlayerController!.stream.buffer.first; - await _videoPlayerController?.seek(position); - t.cancel(); - _timerForSeek = null; + _position.value = position; + updatePositionSecond(); + _heartDuration = position.inSeconds; + if (duration.value.inSeconds != 0) { + if (type != 'slider') { + await _videoPlayerController?.stream.buffer.first; } - }); + await _videoPlayerController?.seek(position); + } else { + _timerForSeek?.cancel(); + _timerForSeek ??= _startSeekTimer(position); + } + } catch (err) { + print('Error while seeking: $err'); } } + Timer? _startSeekTimer(Duration position) { + return Timer.periodic(const Duration(milliseconds: 200), (Timer t) async { + if (duration.value.inSeconds != 0) { + await _videoPlayerController!.stream.buffer.first; + await _videoPlayerController?.seek(position); + t.cancel(); + _timerForSeek = null; + } + }); + } + /// 设置倍速 Future setPlaybackSpeed(double speed) async { /// TODO _duration.value丢失 From 038801a3b9c114d781d0d25fa542051554b46027 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 16 Apr 2024 23:41:45 +0800 Subject: [PATCH 119/349] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 34 ++++++++++++++-------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 1ede7045..be72eccb 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -522,7 +522,22 @@ class PlPlayerController { Duration seekTo = Duration.zero, Duration? duration, }) async { - // 设置倍速 + getVideoFit(); + // if (_looping) { + // await setLooping(_looping); + // } + + /// 跳转播放 + if (seekTo != Duration.zero) { + await this.seekTo(seekTo); + } + + /// 自动播放 + if (_autoPlay) { + await play(duration: duration); + } + + /// 设置倍速 if (videoType.value == 'live') { await setPlaybackSpeed(1.0); } else { @@ -532,20 +547,6 @@ class PlPlayerController { await setPlaybackSpeed(1.0); } } - getVideoFit(); - // if (_looping) { - // await setLooping(_looping); - // } - - // 跳转播放 - if (seekTo != Duration.zero) { - await this.seekTo(seekTo); - } - - // 自动播放 - if (_autoPlay) { - await play(duration: duration); - } } List subscriptions = []; @@ -716,11 +717,10 @@ class PlPlayerController { await seekTo(Duration.zero); } await _videoPlayerController?.play(); - + playerStatus.status.value = PlayerStatus.playing; await getCurrentVolume(); await getCurrentBrightness(); - playerStatus.status.value = PlayerStatus.playing; // screenManager.setOverlays(false); /// 临时fix _duration.value丢失 From 706c13462755ed230c7e55be62bb4b253299302c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 17 Apr 2024 23:53:00 +0800 Subject: [PATCH 120/349] Update beta_ci.yml --- .github/workflows/beta_ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/beta_ci.yml b/.github/workflows/beta_ci.yml index 40f3f042..14b51780 100644 --- a/.github/workflows/beta_ci.yml +++ b/.github/workflows/beta_ci.yml @@ -206,4 +206,4 @@ jobs: method: sendFile 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 }})" + context: "*Beta版本: v${{ needs.update_version.outputs.new_version }}*\n更新内容: [${{ needs.update_version.outputs.last_commit }}]" From 7fc2765471f74fd0f1fdc7ac89fab506a7d6a9a7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 18 Apr 2024 00:00:19 +0800 Subject: [PATCH 121/349] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E7=B1=BB?= =?UTF-8?q?=E5=88=AB=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/subtitle_type.dart | 16 ++++++++++++++++ .../video/detail/widgets/header_control.dart | 14 -------------- lib/utils/subtitle.dart | 4 ++-- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/lib/models/common/subtitle_type.dart b/lib/models/common/subtitle_type.dart index 11716351..54b52e8e 100644 --- a/lib/models/common/subtitle_type.dart +++ b/lib/models/common/subtitle_type.dart @@ -5,6 +5,10 @@ enum SubtitleType { aizh, // 英语(自动生成) aien, + // 中文(简体) + zhHans, + // 英文(美国) + enUS, } extension SubtitleTypeExtension on SubtitleType { @@ -16,6 +20,10 @@ extension SubtitleTypeExtension on SubtitleType { return '中文(自动翻译)'; case SubtitleType.aien: return '英语(自动生成)'; + case SubtitleType.zhHans: + return '中文(简体)'; + case SubtitleType.enUS: + return '英文(美国)'; } } } @@ -29,6 +37,10 @@ extension SubtitleIdExtension on SubtitleType { return 'ai-zh'; case SubtitleType.aien: return 'ai-en'; + case SubtitleType.zhHans: + return 'zh-Hans'; + case SubtitleType.enUS: + return 'en-US'; } } } @@ -42,6 +54,10 @@ extension SubtitleCodeExtension on SubtitleType { return 2; case SubtitleType.aien: return 3; + case SubtitleType.zhHans: + return 4; + case SubtitleType.enUS: + return 5; } } } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e6a324cb..e00311c5 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1306,20 +1306,6 @@ class _HeaderControlState extends State { ], /// 字幕 - // SizedBox( - // width: 34, - // height: 34, - // child: IconButton( - // style: ButtonStyle( - // padding: MaterialStateProperty.all(EdgeInsets.zero), - // ), - // onPressed: () => showSubtitleDialog(), - // icon: const Icon( - // Icons.closed_caption_off, - // size: 22, - // ), - // ), - // ), ComBtn( icon: const Icon( Icons.closed_caption_off, diff --git a/lib/utils/subtitle.dart b/lib/utils/subtitle.dart index 452be542..1b4088f3 100644 --- a/lib/utils/subtitle.dart +++ b/lib/utils/subtitle.dart @@ -5,8 +5,8 @@ class SubTitleUtils { for (int i = 0; i < jsonData.length; i++) { final item = jsonData[i]; - double from = item['from'] as double; - double to = item['to'] as double; + double from = double.parse(item['from'].toString()); + double to = double.parse(item['to'].toString()); int sid = (item['sid'] ?? 0) as int; String content = item['content'] as String; From 3e105804d77bcbe9378907383d4d587d204cfe04 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 18 Apr 2024 23:33:55 +0800 Subject: [PATCH 122/349] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E5=9B=9E?= =?UTF-8?q?=E5=A4=8D=E9=A1=B5=E9=9D=A2=E9=94=AE=E7=9B=98=E6=94=B6=E8=B5=B7?= =?UTF-8?q?=E5=90=8E=E6=97=A0=E6=B3=95=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 367 ++++++++++++++-------------- 1 file changed, 177 insertions(+), 190 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 9da085f4..a620b5ed 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -16,6 +16,7 @@ import 'package:pilipala/pages/video/detail/reply_reply/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; +import '../../../models/video/reply/item.dart'; import '../widgets/dynamic_panel.dart'; class DynamicDetailPage extends StatefulWidget { @@ -210,208 +211,194 @@ class _DynamicDetailPageState extends State onRefresh: () async { await _dynamicDetailController.queryReplyList(); }, - child: Stack( - children: [ - CustomScrollView( - controller: scrollController, - slivers: [ - if (action != 'comment') - SliverToBoxAdapter( - child: DynamicPanel( - item: _dynamicDetailController.item, - source: 'detail', + child: CustomScrollView( + controller: scrollController, + slivers: [ + if (action != 'comment') + SliverToBoxAdapter( + child: DynamicPanel( + item: _dynamicDetailController.item, + source: 'detail', + ), + ), + SliverPersistentHeader( + delegate: _MySliverPersistentHeaderDelegate( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border( + top: BorderSide( + width: 0.6, + color: Theme.of(context).dividerColor.withOpacity(0.05), + ), ), ), - SliverPersistentHeader( - delegate: _MySliverPersistentHeaderDelegate( - child: Container( - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - border: Border( - top: BorderSide( - width: 0.6, - color: Theme.of(context) - .dividerColor - .withOpacity(0.05), + height: 45, + padding: const EdgeInsets.only(left: 12, right: 6), + child: Row( + children: [ + Obx( + () => AnimatedSwitcher( + duration: const Duration(milliseconds: 400), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Text( + '${_dynamicDetailController.acount.value}', + key: ValueKey( + _dynamicDetailController.acount.value), ), ), ), - height: 45, - padding: const EdgeInsets.only(left: 12, right: 6), - child: Row( - children: [ - Obx( - () => AnimatedSwitcher( - duration: const Duration(milliseconds: 400), - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: Text( - '${_dynamicDetailController.acount.value}', - key: ValueKey( - _dynamicDetailController.acount.value), - ), - ), - ), - const Text('条回复'), - const Spacer(), - SizedBox( - height: 35, - child: TextButton.icon( - onPressed: () => - _dynamicDetailController.queryBySort(), - icon: const Icon(Icons.sort, size: 16), - label: Obx(() => Text( - _dynamicDetailController - .sortTypeLabel.value, - style: const TextStyle(fontSize: 13), - )), - ), - ) - ], - ), - ), + const Text('条回复'), + const Spacer(), + SizedBox( + height: 35, + child: TextButton.icon( + onPressed: () => + _dynamicDetailController.queryBySort(), + icon: const Icon(Icons.sort, size: 16), + label: Obx(() => Text( + _dynamicDetailController.sortTypeLabel.value, + style: const TextStyle(fontSize: 13), + )), + ), + ) + ], ), - pinned: true, - ), - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (snapshot.data['status']) { - // 请求成功 - return Obx( - () => _dynamicDetailController.replyList.isEmpty && - _dynamicDetailController.isLoadingMore - ? SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - return const VideoReplySkeleton(); - }, childCount: 8), - ) - : SliverList( - delegate: SliverChildBuilderDelegate( - (context, index) { - if (index == - _dynamicDetailController - .replyList.length) { - return Container( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context) - .padding - .bottom), - height: MediaQuery.of(context) - .padding - .bottom + - 100, - child: Center( - child: Obx( - () => Text( - _dynamicDetailController - .noMore.value, - style: TextStyle( - fontSize: 12, - color: Theme.of(context) - .colorScheme - .outline, - ), - ), - ), - ), - ); - } else { - return ReplyItem( - replyItem: _dynamicDetailController - .replyList[index], - showReplyRow: true, - replyLevel: '1', - replyReply: (replyItem) => - replyReply(replyItem), - replyType: - ReplyType.values[replyType], - addReply: (replyItem) { - _dynamicDetailController - .replyList[index].replies! - .add(replyItem); - }, - ); - } - }, - childCount: _dynamicDetailController - .replyList.length + - 1, - ), - ), - ); - } else { - // 请求错误 - return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ); - } - } else { - // 骨架屏 - return SliverList( - delegate: SliverChildBuilderDelegate((context, index) { - return const VideoReplySkeleton(); - }, childCount: 8), - ); - } - }, - ) - ], - ), - Positioned( - bottom: MediaQuery.of(context).padding.bottom + 14, - right: 14, - child: SlideTransition( - position: Tween( - begin: const Offset(0, 2), - end: const Offset(0, 0), - ).animate(CurvedAnimation( - parent: fabAnimationCtr, - curve: Curves.easeInOut, - )), - child: FloatingActionButton( - heroTag: null, - onPressed: () { - feedBack(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (BuildContext context) { - return VideoReplyNewDialog( - oid: _dynamicDetailController.oid ?? - IdUtils.bv2av(Get.parameters['bvid']!), - root: 0, - parent: 0, - replyType: ReplyType.values[replyType], - ); - }, - ).then( - (value) => { - // 完成评论,数据添加 - if (value != null && value['data'] != null) - { - _dynamicDetailController.replyList - .add(value['data']), - _dynamicDetailController.acount.value++ - } - }, - ); - }, - tooltip: '评论动态', - child: const Icon(Icons.reply), ), ), + pinned: true, ), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (snapshot.data['status']) { + RxList replyList = + _dynamicDetailController.replyList; + // 请求成功 + return Obx( + () => replyList.isEmpty && + _dynamicDetailController.isLoadingMore + ? SliverList( + delegate: + SliverChildBuilderDelegate((context, index) { + return const VideoReplySkeleton(); + }, childCount: 8), + ) + : SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + if (index == replyList.length) { + return Container( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context) + .padding + .bottom), + height: MediaQuery.of(context) + .padding + .bottom + + 100, + child: Center( + child: Obx( + () => Text( + _dynamicDetailController + .noMore.value, + style: TextStyle( + fontSize: 12, + color: Theme.of(context) + .colorScheme + .outline, + ), + ), + ), + ), + ); + } else { + return ReplyItem( + replyItem: replyList[index], + showReplyRow: true, + replyLevel: '1', + replyReply: (replyItem) => + replyReply(replyItem), + replyType: ReplyType.values[replyType], + addReply: (replyItem) { + replyList[index] + .replies! + .add(replyItem); + }, + ); + } + }, + childCount: replyList.length + 1, + ), + ), + ); + } else { + // 请求错误 + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ); + } + } else { + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoReplySkeleton(); + }, childCount: 8), + ); + } + }, + ) ], ), ), + floatingActionButton: SlideTransition( + position: Tween( + begin: const Offset(0, 2), + end: const Offset(0, 0), + ).animate( + CurvedAnimation( + parent: fabAnimationCtr, + curve: Curves.easeInOut, + ), + ), + child: FloatingActionButton( + heroTag: null, + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (BuildContext context) { + return VideoReplyNewDialog( + oid: _dynamicDetailController.oid ?? + IdUtils.bv2av(Get.parameters['bvid']!), + root: 0, + parent: 0, + replyType: ReplyType.values[replyType], + ); + }, + ).then( + (value) => { + // 完成评论,数据添加 + if (value != null && value['data'] != null) + { + _dynamicDetailController.replyList.add(value['data']), + _dynamicDetailController.acount.value++ + } + }, + ); + }, + tooltip: '评论动态', + child: const Icon(Icons.reply), + ), + ), ); } } From 350e34902336c3541c2544e410fa498e51f976dd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 19 Apr 2024 00:01:09 +0800 Subject: [PATCH 123/349] =?UTF-8?q?feat:=20ai=E6=80=BB=E7=BB=93=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/widgets/ai_detail.dart | 109 ++++++++---------- 1 file changed, 47 insertions(+), 62 deletions(-) diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index fb280d91..882a9a8b 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -49,7 +49,7 @@ class AiDetail extends StatelessWidget { child: SingleChildScrollView( child: Column( children: [ - Text( + SelectableText( modelResult!.summary!, style: const TextStyle( fontSize: 15, @@ -60,13 +60,15 @@ class AiDetail extends StatelessWidget { const SizedBox(height: 20), ListView.builder( shrinkWrap: true, - itemCount: modelResult!.outline!.length, physics: const NeverScrollableScrollPhysics(), + itemCount: modelResult!.outline!.length, itemBuilder: (context, index) { + final outline = modelResult!.outline![index]; return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - modelResult!.outline![index].title!, + SelectableText( + outline.title!, style: const TextStyle( fontSize: 14, fontWeight: FontWeight.bold, @@ -77,76 +79,59 @@ class AiDetail extends StatelessWidget { ListView.builder( shrinkWrap: true, physics: const NeverScrollableScrollPhysics(), - itemCount: modelResult! - .outline![index].partOutline!.length, + itemCount: outline.partOutline!.length, itemBuilder: (context, i) { + final part = outline.partOutline![i]; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Wrap( - children: [ - RichText( - text: TextSpan( - style: TextStyle( - fontSize: 13, - color: Theme.of(context) - .colorScheme - .onBackground, - height: 1.5, + GestureDetector( + onTap: () { + try { + final controller = + Get.find( + tag: Get.arguments['heroTag'], + ); + controller.plPlayerController.seekTo( + Duration( + seconds: Utils.duration( + Utils.tampToSeektime( + part.timestamp!), + ).toInt(), ), - children: [ - TextSpan( - text: Utils.tampToSeektime( - modelResult! - .outline![index] - .partOutline![i] - .timestamp!), - style: TextStyle( - color: Theme.of(context) - .colorScheme - .primary, - ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 跳转到指定位置 - try { - Get.find( - tag: Get.arguments[ - 'heroTag']) - .plPlayerController - .seekTo( - Duration( - seconds: - Utils.duration( - Utils.tampToSeektime(modelResult! - .outline![ - index] - .partOutline![ - i] - .timestamp!) - .toString(), - ), - ), - ); - } catch (_) {} - }, - ), - const TextSpan(text: ' '), - TextSpan( - text: modelResult! - .outline![index] - .partOutline![i] - .content!), - ], + ); + } catch (_) {} + }, + child: SelectableText.rich( + TextSpan( + style: TextStyle( + fontSize: 13, + color: Theme.of(context) + .colorScheme + .onBackground, + height: 1.5, ), + children: [ + TextSpan( + text: Utils.tampToSeektime( + part.timestamp!), + style: TextStyle( + color: Theme.of(context) + .colorScheme + .primary, + ), + ), + const TextSpan(text: ' '), + TextSpan(text: part.content!), + ], ), - ], + ), ), + const SizedBox(height: 20), ], ); }, ), - const SizedBox(height: 20), ], ); }, From 5f0bb44bf156708cafef644440cb4c6dd6b202c3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 19 Apr 2024 23:43:23 +0800 Subject: [PATCH 124/349] =?UTF-8?q?mod:=20=E8=8E=B7=E5=8F=96=E6=94=B6?= =?UTF-8?q?=E8=97=8F=E5=A4=B9pageSize?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index a5f94525..6ff0afc8 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -13,7 +13,7 @@ class FavController extends GetxController { Box userInfoCache = GStrorage.userInfo; UserInfoData? userInfo; int currentPage = 1; - int pageSize = 10; + int pageSize = 60; RxBool hasMore = true.obs; Future queryFavFolder({type = 'init'}) async { From fcd7781789a1f53fc7ab7f80af85c60675103a68 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 20 Apr 2024 00:55:18 +0800 Subject: [PATCH 125/349] =?UTF-8?q?feat:=20=E6=94=B6=E8=97=8F=E5=A4=B9?= =?UTF-8?q?=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 +++ lib/http/user.dart | 17 ++++++++++++ lib/pages/fav/controller.dart | 13 +++++++++- lib/pages/fav/view.dart | 5 ++-- lib/pages/fav/widgets/item.dart | 18 +++++++------ lib/pages/fav_detail/controller.dart | 39 ++++++++++++++++++++++++++++ lib/pages/fav_detail/view.dart | 18 +++++++++---- 7 files changed, 96 insertions(+), 17 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index b6975c4b..ad314acf 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -511,4 +511,7 @@ class Api { /// 取消订阅 static const String cancelSub = '/x/v3/fav/season/unfav'; + + /// 删除收藏夹 + static const String delFavFolder = '/x/v3/fav/folder/del'; } diff --git a/lib/http/user.dart b/lib/http/user.dart index fea0a22e..dfdf187e 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -395,4 +395,21 @@ class UserHttp { return {'status': false, 'msg': res.data['message']}; } } + + // 删除文件夹 + static Future delFavFolder({required int mediaIds}) async { + var res = await Request().post( + Api.delFavFolder, + queryParameters: { + 'media_ids': mediaIds, + 'platform': 'web', + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return {'status': true}; + } else { + return {'status': false, 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index 6ff0afc8..2307d303 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -10,6 +10,7 @@ import 'package:pilipala/utils/storage.dart'; class FavController extends GetxController { final ScrollController scrollController = ScrollController(); Rx favFolderData = FavFolderData().obs; + RxList favFolderList = [].obs; Box userInfoCache = GStrorage.userInfo; UserInfoData? userInfo; int currentPage = 1; @@ -32,9 +33,10 @@ class FavController extends GetxController { if (res['status']) { if (type == 'init') { favFolderData.value = res['data']; + favFolderList.value = res['data'].list; } else { if (res['data'].list.isNotEmpty) { - favFolderData.value.list!.addAll(res['data'].list); + favFolderList.addAll(res['data'].list); favFolderData.update((val) {}); } } @@ -49,4 +51,13 @@ class FavController extends GetxController { Future onLoad() async { queryFavFolder(type: 'onload'); } + + removeFavFolder({required int mediaIds}) async { + for (var i in favFolderList) { + if (i.id == mediaIds) { + favFolderList.remove(i); + break; + } + } + } } diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index b980914a..424a885d 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -62,11 +62,10 @@ class _FavPageState extends State { return Obx( () => ListView.builder( controller: scrollController, - itemCount: _favController.favFolderData.value.list!.length, + itemCount: _favController.favFolderList.length, itemBuilder: (context, index) { return FavItem( - favFolderItem: - _favController.favFolderData.value.list![index]); + favFolderItem: _favController.favFolderList[index]); }, ), ); diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/widgets/item.dart index 08730d7b..3c44ec9d 100644 --- a/lib/pages/fav/widgets/item.dart +++ b/lib/pages/fav/widgets/item.dart @@ -13,14 +13,16 @@ class FavItem extends StatelessWidget { Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(favFolderItem.fid); return InkWell( - onTap: () => Get.toNamed( - '/favDetail', - arguments: favFolderItem, - parameters: { - 'heroTag': heroTag, - 'mediaId': favFolderItem.id.toString(), - }, - ), + onTap: () async { + Get.toNamed( + '/favDetail', + arguments: favFolderItem, + parameters: { + 'heroTag': heroTag, + 'mediaId': favFolderItem.id.toString(), + }, + ); + }, child: Padding( padding: const EdgeInsets.fromLTRB(12, 7, 12, 7), child: LayoutBuilder( diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 55d5b884..7af398e8 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -1,9 +1,11 @@ +import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/user/fav_detail.dart'; import 'package:pilipala/models/user/fav_folder.dart'; +import 'package:pilipala/pages/fav/index.dart'; class FavDetailController extends GetxController { FavFolderItemData? item; @@ -74,4 +76,41 @@ class FavDetailController extends GetxController { onLoad() { queryUserFavFolderDetail(type: 'onLoad'); } + + onDelFavFolder() async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: const Text('确定删除这个收藏夹吗?'), + actions: [ + TextButton( + onPressed: () async { + SmartDialog.dismiss(); + }, + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await UserHttp.delFavFolder(mediaIds: mediaId!); + SmartDialog.dismiss(); + SmartDialog.showToast(res['status'] ? '操作成功' : res['msg']); + if (res['status']) { + FavController favController = Get.find(); + await favController.removeFavFolder(mediaIds: mediaId!); + Get.back(); + } + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } } diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 74faa829..5f2d92af 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -100,11 +100,19 @@ class _FavDetailPageState extends State { Get.toNamed('/favSearch?searchType=0&mediaId=$mediaId'), icon: const Icon(Icons.search_outlined), ), - // IconButton( - // onPressed: () {}, - // icon: const Icon(Icons.more_vert), - // ), - const SizedBox(width: 6), + PopupMenuButton( + icon: const Icon(Icons.more_vert_outlined), + position: PopupMenuPosition.under, + onSelected: (String type) {}, + itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + onTap: () => _favDetailController.onDelFavFolder(), + value: 'pause', + child: const Text('删除收藏夹'), + ), + ], + ), + const SizedBox(width: 14), ], flexibleSpace: FlexibleSpaceBar( background: Container( From 03ce69b140d33e0399114c591adbc0bb705a0ffc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 20 Apr 2024 22:40:54 +0800 Subject: [PATCH 126/349] =?UTF-8?q?feat:=20=E5=8A=A8=E6=80=81=E8=BD=AC?= =?UTF-8?q?=E5=8F=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 6 + lib/http/dynamics.dart | 79 ++++++ lib/pages/dynamics/widgets/action_panel.dart | 241 ++++++++++++++++++- 3 files changed, 319 insertions(+), 7 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index b6975c4b..e2e973f2 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -511,4 +511,10 @@ class Api { /// 取消订阅 static const String cancelSub = '/x/v3/fav/season/unfav'; + + /// 动态转发 + static const String dynamicForwardUrl = '/x/dynamic/feed/create/submit_check'; + + /// 创建动态 + static const String dynamicCreate = '/x/dynamic/feed/create/dyn'; } diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index d62de12f..f0740d36 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -1,3 +1,4 @@ +import 'dart:math'; import '../models/dynamics/result.dart'; import '../models/dynamics/up.dart'; import 'index.dart'; @@ -117,4 +118,82 @@ class DynamicsHttp { }; } } + + static Future dynamicForward() async { + var res = await Request().post( + Api.dynamicForwardUrl, + queryParameters: { + 'csrf': await Request.getCsrf(), + 'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'}, + 'x-bili-web-req-json': {'spm_id': '333.999'}, + }, + data: { + 'attach_card': null, + 'scene': 4, + 'content': { + 'conetents': [ + {'raw_text': "2", 'type': 1, 'biz_id': ""} + ] + } + }, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } + + static Future dynamicCreate({ + required String dynIdStr, + required int mid, + String? rawText, + }) async { + DateTime now = DateTime.now(); + int timestamp = now.millisecondsSinceEpoch ~/ 1000; + Random random = Random(); + int randomNumber = random.nextInt(9000) + 1000; + String uploadId = + mid.toString() + timestamp.toString() + randomNumber.toString(); + var res = await Request().post(Api.dynamicCreate, queryParameters: { + 'platform': 'web', + 'csrf': await Request.getCsrf(), + 'x-bili-device-req-json': {'platform': 'web', 'device': 'pc'}, + 'x-bili-web-req-json': {'spm_id': '333.999'}, + }, data: { + 'dyn_req': { + 'content': { + 'contents': [ + {'raw_text': rawText ?? '', 'type': 1, 'biz_id': ''} + ] + }, + 'scene': 4, + 'attach_card': null, + 'upload_id': uploadId, + 'meta': { + 'app_meta': {'from': 'create.dynamic.web', 'mobi_app': 'web'} + } + }, + 'web_repost_src': {'dyn_id_str': dynIdStr} + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 0ca09b8c..0e67b0a7 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -3,10 +3,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/dynamics.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:status_bar_control/status_bar_control.dart'; +import 'rich_node_panel.dart'; class ActionPanel extends StatefulWidget { const ActionPanel({ @@ -20,21 +23,37 @@ class ActionPanel extends StatefulWidget { State createState() => _ActionPanelState(); } -class _ActionPanelState extends State { +class _ActionPanelState extends State + with TickerProviderStateMixin { final DynamicsController _dynamicsController = Get.put(DynamicsController()); late ModuleStatModel stat; bool isProcessing = false; + RxDouble height = (310.0).obs; + RxBool isExpand = false.obs; + late double statusHeight; + TextEditingController _inputController = TextEditingController(); + FocusNode myFocusNode = FocusNode(); + String _inputText = ''; + void Function()? handleState(Future Function() action) { - return isProcessing ? null : () async { - setState(() => isProcessing = true); - await action(); - setState(() => isProcessing = false); - }; + return isProcessing + ? null + : () async { + isProcessing = true; + await action(); + isProcessing = false; + }; } + @override void initState() { super.initState(); stat = widget.item!.modules.moduleStat; + onInit(); + } + + onInit() async { + statusHeight = await StatusBarControl.getHeight; } // 动态点赞 @@ -67,6 +86,214 @@ class _ActionPanelState extends State { } } + // 转发动态预览 + Widget dynamicPreview() { + return Padding( + padding: const EdgeInsets.fromLTRB(12, 0, 14, 12), + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: + Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4), + borderRadius: BorderRadius.circular(12), + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '@${widget.item.modules.moduleAuthor.name}', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox(height: 6), + Row( + children: [ + NetworkImgLayer( + src: widget.item.modules.moduleAuthor.face, + width: 34, + height: 34, + type: 'emote', + ), + const SizedBox(width: 10), + Expanded( + child: Text.rich( + style: const TextStyle(height: 0), + richNode(widget.item, context), + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + ), + // Text(data) + ], + ) + ], + )), + ), + ); + } + + // 动态转发 + void forwardHandler() async { + showModalBottomSheet( + context: context, + enableDrag: false, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return Obx( + () => AnimatedContainer( + duration: Durations.medium1, + onEnd: () async { + if (isExpand.value) { + await Future.delayed(const Duration(milliseconds: 120)); + myFocusNode.requestFocus(); + } + }, + height: height.value, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + AnimatedContainer( + duration: Durations.medium1, + height: isExpand.value ? statusHeight : 0, + ), + Padding( + padding: const EdgeInsets.fromLTRB(16, 10, 12, 0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + AnimatedSwitcher( + duration: Durations.medium1, + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: isExpand.value + ? IconButton( + onPressed: () => togglePanelState(false), + icon: const Icon(Icons.close)) + : const Text( + '转发动态', + style: TextStyle(fontWeight: FontWeight.bold), + ), + ), + AnimatedSwitcher( + duration: Durations.medium1, + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: isExpand.value + ? FilledButton( + onPressed: () => dynamicForward('forward'), + child: const Text('转发'), + ) + : TextButton( + onPressed: () {}, + child: const Text('立即转发'), + ), + ), + ], + ), + ), + if (!isExpand.value) ...[ + GestureDetector( + onTap: () => togglePanelState(true), + behavior: HitTestBehavior.translucent, + child: Container( + width: double.infinity, + alignment: Alignment.centerLeft, + padding: const EdgeInsets.fromLTRB(16, 0, 10, 15), + child: Text( + '说点什么吧', + textAlign: TextAlign.start, + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ), + ), + ] else ...[ + Padding( + padding: const EdgeInsets.fromLTRB(16, 10, 16, 0), + child: TextField( + maxLines: 5, + focusNode: myFocusNode, + controller: _inputController, + onChanged: (value) { + setState(() { + _inputText = value; + }); + }, + decoration: const InputDecoration( + border: InputBorder.none, + hintText: '说点什么吧', + ), + ), + ), + ], + dynamicPreview(), + if (!isExpand.value) ...[ + const Divider(thickness: 0.1, height: 1), + ListTile( + onTap: () => Get.back(), + minLeadingWidth: 0, + dense: true, + title: Text( + '取消', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + textAlign: TextAlign.center, + ), + ), + SizedBox( + height: MediaQuery.of(context).padding.bottom + 20, + ) + ] + ], + ), + ), + ); + }, + ); + } + + togglePanelState(status) { + if (!status) { + Get.back(); + height.value = 310; + _inputText = ''; + _inputController.clear(); + } else { + height.value = Get.size.height; + } + isExpand.value = !(isExpand.value); + } + + dynamicForward(String type) async { + String dynamicId = widget.item.idStr!; + var res = await DynamicsHttp.dynamicCreate( + dynIdStr: dynamicId, + mid: _dynamicsController.userInfo.mid, + rawText: _inputText, + ); + if (res['status']) { + SmartDialog.showToast(type == 'forward' ? '转发成功' : '发布成功'); + togglePanelState(false); + } + } + + @override + void dispose() { + myFocusNode.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { var color = Theme.of(context).colorScheme.outline; @@ -77,7 +304,7 @@ class _ActionPanelState extends State { Expanded( flex: 1, child: TextButton.icon( - onPressed: () {}, + onPressed: forwardHandler, icon: const Icon( FontAwesomeIcons.shareFromSquare, size: 16, From bc29f691333bb9f84c897a8168a9d09669ad4266 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 20 Apr 2024 23:32:28 +0800 Subject: [PATCH 127/349] opt: streamController close --- lib/pages/dynamics/detail/view.dart | 1 + lib/pages/fav_detail/view.dart | 1 + lib/pages/home/controller.dart | 6 ++++++ lib/pages/main/controller.dart | 6 ++++++ lib/pages/member/view.dart | 1 + lib/pages/rank/controller.dart | 6 ++++++ lib/pages/subscription_detail/view.dart | 1 + 7 files changed, 22 insertions(+) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index a620b5ed..c6ec682a 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -183,6 +183,7 @@ class _DynamicDetailPageState extends State scrollController.removeListener(() {}); fabAnimationCtr.dispose(); scrollController.dispose(); + titleStreamC.close(); super.dispose(); } diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 74faa829..993ff63b 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -53,6 +53,7 @@ class _FavDetailPageState extends State { @override void dispose() { _controller.dispose(); + titleStreamC.close(); super.dispose(); } diff --git a/lib/pages/home/controller.dart b/lib/pages/home/controller.dart index ca70e1c4..f197fdfa 100644 --- a/lib/pages/home/controller.dart +++ b/lib/pages/home/controller.dart @@ -114,4 +114,10 @@ class HomeController extends GetxController with GetTickerProviderStateMixin { defaultSearch.value = res.data['data']['name']; } } + + @override + void onClose() { + searchBarStream.close(); + super.onClose(); + } } diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index c2e5c322..a77d9304 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -101,4 +101,10 @@ class MainController extends GetxController { selectedIndex = defaultIndex != -1 ? defaultIndex : 0; pages = navigationBars.map((e) => e['page']).toList(); } + + @override + void onClose() { + bottomBarStream.close(); + super.onClose(); + } } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 015750db..b6648647 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -54,6 +54,7 @@ class _MemberPageState extends State @override void dispose() { _extendNestCtr.removeListener(() {}); + appbarStream.close(); super.dispose(); } diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index fa906fc9..64395ec7 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -54,4 +54,10 @@ class RankController extends GetxController with GetTickerProviderStateMixin { vsync: this, ); } + + @override + void onClose() { + searchBarStream.close(); + super.onClose(); + } } diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index 2c219e58..63352429 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -53,6 +53,7 @@ class _SubDetailPageState extends State { @override void dispose() { _controller.dispose(); + titleStreamC.close(); super.dispose(); } From 22d8cf12a0db7d7002abbddbdffe18d7e5d28e5f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Apr 2024 15:52:16 +0800 Subject: [PATCH 128/349] opt: forward panel --- lib/pages/dynamics/widgets/action_panel.dart | 264 ++++++++++++------ lib/pages/dynamics/widgets/dynamic_panel.dart | 9 +- lib/pages/member_dynamics/view.dart | 4 +- 3 files changed, 181 insertions(+), 96 deletions(-) diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index 0e67b0a7..e94757ab 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -14,10 +14,10 @@ import 'rich_node_panel.dart'; class ActionPanel extends StatefulWidget { const ActionPanel({ super.key, - this.item, + required this.item, }); // ignore: prefer_typing_uninitialized_variables - final item; + final DynamicItemModel item; @override State createState() => _ActionPanelState(); @@ -28,7 +28,8 @@ class _ActionPanelState extends State final DynamicsController _dynamicsController = Get.put(DynamicsController()); late ModuleStatModel stat; bool isProcessing = false; - RxDouble height = (310.0).obs; + double defaultHeight = 260; + RxDouble height = 0.0.obs; RxBool isExpand = false.obs; late double statusHeight; TextEditingController _inputController = TextEditingController(); @@ -48,7 +49,7 @@ class _ActionPanelState extends State @override void initState() { super.initState(); - stat = widget.item!.modules.moduleStat; + stat = widget.item.modules!.moduleStat!; onInit(); } @@ -62,7 +63,7 @@ class _ActionPanelState extends State var item = widget.item!; String dynamicId = item.idStr!; // 1 已点赞 2 不喜欢 0 未操作 - Like like = item.modules.moduleStat.like; + Like like = item.modules!.moduleStat!.like!; int count = like.count == '点赞' ? 0 : int.parse(like.count ?? '0'); bool status = like.status!; int up = status ? 2 : 1; @@ -70,15 +71,15 @@ class _ActionPanelState extends State if (res['status']) { SmartDialog.showToast(!status ? '点赞成功' : '取消赞'); if (up == 1) { - item.modules.moduleStat.like.count = (count + 1).toString(); - item.modules.moduleStat.like.status = true; + item.modules!.moduleStat!.like!.count = (count + 1).toString(); + item.modules!.moduleStat!.like!.status = true; } else { if (count == 1) { - item.modules.moduleStat.like.count = '点赞'; + item.modules!.moduleStat!.like!.count = '点赞'; } else { - item.modules.moduleStat.like.count = (count - 1).toString(); + item.modules!.moduleStat!.like!.count = (count - 1).toString(); } - item.modules.moduleStat.like.status = false; + item.modules!.moduleStat!.like!.status = false; } setState(() {}); } else { @@ -88,50 +89,134 @@ class _ActionPanelState extends State // 转发动态预览 Widget dynamicPreview() { - return Padding( - padding: const EdgeInsets.fromLTRB(12, 0, 14, 12), - child: Container( - width: double.infinity, - decoration: BoxDecoration( - color: - Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4), - borderRadius: BorderRadius.circular(12), + ItemModulesModel? modules = widget.item.modules; + final String type = widget.item.type!; + String? cover = modules?.moduleAuthor?.face; + switch (type) { + /// 图文动态 + case 'DYNAMIC_TYPE_DRAW': + cover = modules?.moduleDynamic?.major?.opus?.pics?.first.url; + + /// 投稿 + case 'DYNAMIC_TYPE_AV': + cover = modules?.moduleDynamic?.major?.archive?.cover; + + /// 转发的动态 + case 'DYNAMIC_TYPE_FORWARD': + String forwardType = widget.item.orig!.type!; + switch (forwardType) { + /// 图文动态 + case 'DYNAMIC_TYPE_DRAW': + cover = modules?.moduleDynamic?.major?.opus?.pics?.first.url; + + /// 投稿 + case 'DYNAMIC_TYPE_AV': + cover = modules?.moduleDynamic?.major?.archive?.cover; + + /// 专栏文章 + case 'DYNAMIC_TYPE_ARTICLE': + cover = ''; + + /// 番剧 + case 'DYNAMIC_TYPE_PGC': + cover = ''; + + /// 纯文字动态 + case 'DYNAMIC_TYPE_WORD': + cover = ''; + + /// 直播 + case 'DYNAMIC_TYPE_LIVE_RCMD': + cover = ''; + + /// 合集查看 + case 'DYNAMIC_TYPE_UGC_SEASON': + cover = ''; + + /// 番剧 + case 'DYNAMIC_TYPE_PGC_UNION': + cover = modules?.moduleDynamic?.major?.pgc?.cover; + + default: + cover = ''; + } + + /// 专栏文章 + case 'DYNAMIC_TYPE_ARTICLE': + cover = ''; + + /// 番剧 + case 'DYNAMIC_TYPE_PGC': + cover = ''; + + /// 纯文字动态 + case 'DYNAMIC_TYPE_WORD': + cover = ''; + + /// 直播 + case 'DYNAMIC_TYPE_LIVE_RCMD': + cover = ''; + + /// 合集查看 + case 'DYNAMIC_TYPE_UGC_SEASON': + cover = ''; + + /// 番剧查看 + case 'DYNAMIC_TYPE_PGC_UNION': + cover = ''; + + default: + cover = ''; + } + return Container( + width: double.infinity, + height: 95, + margin: const EdgeInsets.fromLTRB(12, 0, 12, 14), + decoration: BoxDecoration( + color: + Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.4), + borderRadius: BorderRadius.circular(6), + border: Border( + left: BorderSide( + width: 4, + color: Theme.of(context).colorScheme.primary.withOpacity(0.8)), ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, + ), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '@${widget.item.modules!.moduleAuthor!.name}', + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox(height: 8), + Row( children: [ - Text( - '@${widget.item.modules.moduleAuthor.name}', - style: TextStyle( - color: Theme.of(context).colorScheme.primary, + NetworkImgLayer( + src: cover ?? '', + width: 34, + height: 34, + type: 'emote', + ), + const SizedBox(width: 10), + Expanded( + child: Text.rich( + style: const TextStyle(height: 0), + richNode(widget.item, context), + maxLines: 2, + overflow: TextOverflow.ellipsis, ), ), - const SizedBox(height: 6), - Row( - children: [ - NetworkImgLayer( - src: widget.item.modules.moduleAuthor.face, - width: 34, - height: 34, - type: 'emote', - ), - const SizedBox(width: 10), - Expanded( - child: Text.rich( - style: const TextStyle(height: 0), - richNode(widget.item, context), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - // Text(data) - ], - ) + // Text(data) ], - )), + ) + ], + ), ), ); } @@ -149,56 +234,54 @@ class _ActionPanelState extends State duration: Durations.medium1, onEnd: () async { if (isExpand.value) { - await Future.delayed(const Duration(milliseconds: 120)); + await Future.delayed(const Duration(milliseconds: 80)); myFocusNode.requestFocus(); } }, - height: height.value, + height: height.value + MediaQuery.of(context).padding.bottom, child: Column( - mainAxisSize: MainAxisSize.min, children: [ AnimatedContainer( duration: Durations.medium1, height: isExpand.value ? statusHeight : 0, ), Padding( - padding: const EdgeInsets.fromLTRB(16, 10, 12, 0), + padding: EdgeInsets.fromLTRB( + isExpand.value ? 10 : 16, + 10, + isExpand.value ? 14 : 12, + 0, + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - AnimatedSwitcher( - duration: Durations.medium1, - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: isExpand.value - ? IconButton( - onPressed: () => togglePanelState(false), - icon: const Icon(Icons.close)) - : const Text( - '转发动态', - style: TextStyle(fontWeight: FontWeight.bold), - ), - ), - AnimatedSwitcher( - duration: Durations.medium1, - transitionBuilder: - (Widget child, Animation animation) { - return ScaleTransition( - scale: animation, child: child); - }, - child: isExpand.value - ? FilledButton( - onPressed: () => dynamicForward('forward'), - child: const Text('转发'), - ) - : TextButton( - onPressed: () {}, - child: const Text('立即转发'), - ), - ), + if (isExpand.value) ...[ + IconButton( + onPressed: () => togglePanelState(false), + icon: const Icon(Icons.close), + ), + Text( + '转发动态', + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ) + ] else ...[ + const Text( + '转发动态', + style: TextStyle(fontWeight: FontWeight.bold), + ) + ], + isExpand.value + ? FilledButton( + onPressed: () => dynamicForward('forward'), + child: const Text('转发'), + ) + : TextButton( + onPressed: () {}, + child: const Text('立即转发'), + ) ], ), ), @@ -209,7 +292,7 @@ class _ActionPanelState extends State child: Container( width: double.infinity, alignment: Alignment.centerLeft, - padding: const EdgeInsets.fromLTRB(16, 0, 10, 15), + padding: const EdgeInsets.fromLTRB(16, 0, 10, 14), child: Text( '说点什么吧', textAlign: TextAlign.start, @@ -251,9 +334,6 @@ class _ActionPanelState extends State textAlign: TextAlign.center, ), ), - SizedBox( - height: MediaQuery.of(context).padding.bottom + 20, - ) ] ], ), @@ -266,7 +346,7 @@ class _ActionPanelState extends State togglePanelState(status) { if (!status) { Get.back(); - height.value = 310; + height.value = defaultHeight; _inputText = ''; _inputController.clear(); } else { @@ -298,6 +378,8 @@ class _ActionPanelState extends State Widget build(BuildContext context) { var color = Theme.of(context).colorScheme.outline; var primary = Theme.of(context).colorScheme.primary; + height.value = defaultHeight; + print('height.value: ${height.value}'); return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index c85cad45..d273a1a6 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -1,15 +1,16 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/pages/dynamics/index.dart'; +import '../../../models/dynamics/result.dart'; import 'action_panel.dart'; import 'author_panel.dart'; import 'content_panel.dart'; import 'forward_panel.dart'; class DynamicPanel extends StatelessWidget { - final dynamic item; + final DynamicItemModel item; final String? source; - DynamicPanel({this.item, this.source, Key? key}) : super(key: key); + DynamicPanel({required this.item, this.source, Key? key}) : super(key: key); final DynamicsController _dynamicsController = Get.put(DynamicsController()); @override @@ -41,8 +42,8 @@ class DynamicPanel extends StatelessWidget { padding: const EdgeInsets.fromLTRB(12, 12, 12, 8), child: AuthorPanel(item: item), ), - if (item!.modules!.moduleDynamic!.desc != null || - item!.modules!.moduleDynamic!.major != null) + if (item.modules!.moduleDynamic!.desc != null || + item.modules!.moduleDynamic!.major != null) Content(item: item, source: source), forWard(item, context, _dynamicsController, source), const SizedBox(height: 2), diff --git a/lib/pages/member_dynamics/view.dart b/lib/pages/member_dynamics/view.dart index 68aa72d7..2e093bcc 100644 --- a/lib/pages/member_dynamics/view.dart +++ b/lib/pages/member_dynamics/view.dart @@ -5,6 +5,7 @@ import 'package:pilipala/pages/member_dynamics/index.dart'; import 'package:pilipala/utils/utils.dart'; import '../../common/widgets/http_error.dart'; +import '../../models/dynamics/result.dart'; import '../dynamics/widgets/dynamic_panel.dart'; class MemberDynamicsPage extends StatefulWidget { @@ -66,7 +67,8 @@ class _MemberDynamicsPageState extends State { if (snapshot.connectionState == ConnectionState.done) { if (snapshot.data != null) { Map data = snapshot.data as Map; - List list = _memberDynamicController.dynamicsList; + RxList list = + _memberDynamicController.dynamicsList; if (data['status']) { return Obx( () => list.isNotEmpty From 9dc7256a005247cb24b112d4850bc70b19ec9734 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Apr 2024 16:47:38 +0800 Subject: [PATCH 129/349] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BF=E7=95=AA?= =?UTF-8?q?=E5=89=A7=E8=AF=84=E8=AE=BA=E6=97=B6=E8=BD=AC=E5=8F=91=E5=88=B0?= =?UTF-8?q?=E5=8A=A8=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/dynamics.dart | 22 +++++++-- lib/pages/dynamics/widgets/action_panel.dart | 1 + lib/pages/video/detail/reply_new/view.dart | 50 ++++++++++++++++++-- 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index f0740d36..63dea4ff 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -152,16 +152,28 @@ class DynamicsHttp { } static Future dynamicCreate({ - required String dynIdStr, required int mid, + required int scene, + int? oid, + String? dynIdStr, String? rawText, }) async { DateTime now = DateTime.now(); int timestamp = now.millisecondsSinceEpoch ~/ 1000; Random random = Random(); int randomNumber = random.nextInt(9000) + 1000; - String uploadId = - mid.toString() + timestamp.toString() + randomNumber.toString(); + String uploadId = '${mid}_${timestamp}_$randomNumber'; + + Map webRepostSrc = { + 'dyn_id_str': dynIdStr ?? '', + }; + + /// 投稿转发 + if (scene == 5) { + webRepostSrc = { + 'revs_id': {'dyn_type': 8, 'rid': oid} + }; + } var res = await Request().post(Api.dynamicCreate, queryParameters: { 'platform': 'web', 'csrf': await Request.getCsrf(), @@ -174,14 +186,14 @@ class DynamicsHttp { {'raw_text': rawText ?? '', 'type': 1, 'biz_id': ''} ] }, - 'scene': 4, + 'scene': scene, 'attach_card': null, 'upload_id': uploadId, 'meta': { 'app_meta': {'from': 'create.dynamic.web', 'mobi_app': 'web'} } }, - 'web_repost_src': {'dyn_id_str': dynIdStr} + 'web_repost_src': webRepostSrc }); if (res.data['code'] == 0) { return { diff --git a/lib/pages/dynamics/widgets/action_panel.dart b/lib/pages/dynamics/widgets/action_panel.dart index e94757ab..51ef3952 100644 --- a/lib/pages/dynamics/widgets/action_panel.dart +++ b/lib/pages/dynamics/widgets/action_panel.dart @@ -361,6 +361,7 @@ class _ActionPanelState extends State dynIdStr: dynamicId, mid: _dynamicsController.userInfo.mid, rawText: _inputText, + scene: 4, ); if (res['status']) { SmartDialog.showToast(type == 'forward' ? '转发成功' : '发布成功'); diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index a94b6071..029e015a 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/http/dynamics.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/emote.dart'; @@ -40,6 +41,8 @@ class _VideoReplyNewDialogState extends State double keyboardHeight = 0.0; // 键盘高度 final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间 String toolbarType = 'input'; + RxBool isForward = false.obs; + RxBool showForward = false.obs; @override void initState() { @@ -52,6 +55,10 @@ class _VideoReplyNewDialogState extends State _autoFocus(); // 监听聚焦状态 _focuslistener(); + final String routePath = Get.currentRoute; + if (routePath.startsWith('/video')) { + showForward.value = true; + } } _autoFocus() async { @@ -88,6 +95,16 @@ class _VideoReplyNewDialogState extends State Get.back(result: { 'data': ReplyItemModel.fromJson(result['data']['reply'], ''), }); + + /// 投稿、番剧页面 + if (isForward.value) { + await DynamicsHttp.dynamicCreate( + mid: 0, + rawText: message, + oid: widget.oid!, + scene: 5, + ); + } } else { SmartDialog.showToast(result['msg']); } @@ -145,7 +162,6 @@ class _VideoReplyNewDialogState extends State double _keyboardHeight = EdgeInsets.fromViewPadding( View.of(context).viewInsets, View.of(context).devicePixelRatio) .bottom; - print('_keyboardHeight: $_keyboardHeight'); return Container( clipBehavior: Clip.hardEdge, decoration: BoxDecoration( @@ -225,9 +241,37 @@ class _VideoReplyNewDialogState extends State toolbarType: toolbarType, selected: toolbarType == 'emote', ), + const SizedBox(width: 6), + Obx( + () => showForward.value + ? TextButton.icon( + onPressed: () { + isForward.value = !isForward.value; + }, + icon: Icon( + isForward.value + ? Icons.check_box + : Icons.check_box_outline_blank, + size: 22), + label: const Text('转发到动态'), + style: ButtonStyle( + foregroundColor: MaterialStateProperty.all( + isForward.value + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + ), + ) + : const SizedBox(), + ), const Spacer(), - TextButton( - onPressed: () => submitReplyAdd(), child: const Text('发送')) + SizedBox( + height: 36, + child: FilledButton( + onPressed: () => submitReplyAdd(), + child: const Text('发送'), + ), + ), ], ), ), From c42ce2f1f96c797237b4fca38690d5fae2053810 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Apr 2024 17:38:07 +0800 Subject: [PATCH 130/349] =?UTF-8?q?mod:=20=E7=95=AA=E5=89=A7=E4=BC=A0?= =?UTF-8?q?=E9=80=92aid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/introduction/controller.dart | 1 + lib/pages/bangumi/widgets/bangumi_panel.dart | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 2098302d..b26028cf 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -236,6 +236,7 @@ class BangumiIntroController extends GetxController { videoDetailCtr.bvid = bvid; videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; + videoDetailCtr.oid.value = aid; videoDetailCtr.queryVideoUrl(); // 重新请求评论 try { diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index b01f3be7..3e965f34 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -85,7 +85,9 @@ class _BangumiPanelState extends State { item.cid, item.aid, ); - _bottomSheetController?.close(); + if (_bottomSheetController != null) { + _bottomSheetController?.close(); + } currentIndex = i; scrollToIndex(); } From b83ac38c6099a6cadf9a38f7542ccc5c24706cad Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Apr 2024 19:25:42 +0800 Subject: [PATCH 131/349] =?UTF-8?q?mod:=20scheme=20=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index ffe90e35..9009df6f 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -162,9 +162,14 @@ class PiliSchame { final String host = value.host!; final String? path = value.path; Map? query = value.query; - RegExp regExp = RegExp(r'^(www\.)?m?\.(bilibili\.com)$'); + RegExp regExp = RegExp(r'^((www\.)|(m\.))?bilibili\.com$'); if (regExp.hasMatch(host)) { - print('bilibili.com'); + print('bilibili.com host: $host'); + print('bilibili.com path: $path'); + final String lastPathSegment = path!.split('/').last; + if (lastPathSegment.contains('BV')) { + _videoPush(null, lastPathSegment); + } } else if (host.contains('live')) { int roomId = int.parse(path!.split('/').last); Get.toNamed( From 935073ce756ef21a8f07d2ec6d338742fff54a8b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 22 Apr 2024 23:51:31 +0800 Subject: [PATCH 132/349] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5=E5=B8=83=E5=B1=80=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 335 +++++++++++++++---------------- 1 file changed, 166 insertions(+), 169 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 0152a2cb..f216fbcf 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -23,6 +23,7 @@ import 'package:pilipala/plugin/pl_player/index.dart'; 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:status_bar_control/status_bar_control.dart'; import '../../../plugin/pl_player/models/bottom_control_type.dart'; import '../../../services/shutdown_timer_service.dart'; @@ -61,10 +62,12 @@ class _VideoDetailPageState extends State late bool autoPiP; late Floating floating; bool isShowing = true; + late double statusHeight; @override void initState() { super.initState(); + getStatusHeight(); heroTag = Get.arguments['heroTag']; vdCtr = Get.put(VideoDetailController(), tag: heroTag); vdCtr.sheetHeight.value = localCache.get('sheetHeight'); @@ -203,6 +206,10 @@ class _VideoDetailPageState extends State }); } + getStatusHeight() async { + statusHeight = await StatusBarControl.getHeight; + } + @override void dispose() { shutdownTimerService.handleWaitingFinished(); @@ -281,6 +288,143 @@ class _VideoDetailPageState extends State } } + /// 手动播放 + Widget handlePlayPanel() { + return Stack( + children: [ + GestureDetector( + onTap: handlePlay, + child: Image.network( + vdCtr.videoItem['pic'], + width: Get.width, + height: videoHeight, + fit: BoxFit.cover, // 适应方式根据需要调整 + ), + ), + buildCustomAppBar(), + Positioned( + right: 12, + bottom: 10, + child: GestureDetector( + onTap: handlePlay, + child: Image.asset( + 'assets/images/play.png', + width: 60, + height: 60, + ), + ), + ), + ], + ); + } + + /// tabbar + Widget tabbarBuild() { + return Container( + width: double.infinity, + height: 45, + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: Theme.of(context).dividerColor.withOpacity(0.1), + ), + ), + ), + child: Material( + child: Row( + children: [ + Expanded( + child: Obx( + () => TabBar( + padding: EdgeInsets.zero, + controller: vdCtr.tabCtr, + labelStyle: const TextStyle(fontSize: 13), + labelPadding: const EdgeInsets.symmetric(horizontal: 10.0), + dividerColor: Colors.transparent, + tabs: + vdCtr.tabs.map((String name) => Tab(text: name)).toList(), + ), + ), + ), + Flexible( + flex: 1, + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Obx(() => AnimatedOpacity( + opacity: playerStatus.value != PlayerStatus.playing + ? 1 + : 0, + duration: const Duration(milliseconds: 100), + child: const Icon( + Icons.drag_handle_rounded, + size: 20, + color: Colors.grey, + ), + )), + const SizedBox(width: 8), + SizedBox( + height: 32, + child: TextButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => vdCtr.showShootDanmakuSheet(), + child: + const Text('发弹幕', style: TextStyle(fontSize: 12)), + ), + ), + SizedBox( + width: 38, + height: 38, + child: Obx( + () => !vdCtr.isShowCover.value + ? IconButton( + onPressed: () { + plPlayerController?.isOpenDanmu.value = + !(plPlayerController?.isOpenDanmu.value ?? + false); + }, + icon: !(plPlayerController?.isOpenDanmu.value ?? + false) + ? SvgPicture.asset( + 'assets/images/video/danmu_close.svg', + // ignore: deprecated_member_use + color: Theme.of(context) + .colorScheme + .outline, + ) + : SvgPicture.asset( + 'assets/images/video/danmu_open.svg', + // ignore: deprecated_member_use + color: Theme.of(context) + .colorScheme + .primary, + ), + ) + : IconButton( + icon: SvgPicture.asset( + 'assets/images/video/danmu_close.svg', + // ignore: deprecated_member_use + color: Theme.of(context).colorScheme.outline, + ), + onPressed: () {}, + ), + ), + ), + const SizedBox(width: 18), + ], + ), + ), + ), + ], + ), + ), + ); + } + @override Widget build(BuildContext context) { final sizeContext = MediaQuery.sizeOf(context); @@ -338,162 +482,13 @@ class _VideoDetailPageState extends State }, ); - /// tabbar - Widget tabbarBuild = Container( - width: double.infinity, - height: 45, - decoration: BoxDecoration( - border: Border( - bottom: BorderSide( - width: 1, - color: Theme.of(context).dividerColor.withOpacity(0.1), - ), - ), - ), - child: Material( - child: Row( - children: [ - Flexible( - flex: 1, - child: Obx( - () => TabBar( - padding: EdgeInsets.zero, - controller: vdCtr.tabCtr, - labelStyle: const TextStyle(fontSize: 13), - labelPadding: - const EdgeInsets.symmetric(horizontal: 10.0), // 设置每个标签的宽度 - dividerColor: Colors.transparent, - tabs: vdCtr.tabs - .map( - (String name) => Tab(text: name), - ) - .toList(), - ), - ), - ), - Flexible( - flex: 1, - child: Center( - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Obx(() => AnimatedOpacity( - opacity: playerStatus.value != PlayerStatus.playing - ? 1 - : 0, - duration: const Duration(milliseconds: 100), - child: const Icon( - Icons.drag_handle_rounded, - size: 20, - color: Colors.grey, - ), - )), - const SizedBox(width: 8), - SizedBox( - height: 32, - child: TextButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => vdCtr.showShootDanmakuSheet(), - child: - const Text('发弹幕', style: TextStyle(fontSize: 12)), - ), - ), - SizedBox( - width: 38, - height: 38, - child: Obx( - () => !vdCtr.isShowCover.value - ? IconButton( - onPressed: () { - plPlayerController?.isOpenDanmu.value = - !(plPlayerController - ?.isOpenDanmu.value ?? - false); - }, - icon: - !(plPlayerController?.isOpenDanmu.value ?? - false) - ? SvgPicture.asset( - 'assets/images/video/danmu_close.svg', - // ignore: deprecated_member_use - color: Theme.of(context) - .colorScheme - .outline, - ) - : SvgPicture.asset( - 'assets/images/video/danmu_open.svg', - // ignore: deprecated_member_use - color: Theme.of(context) - .colorScheme - .primary, - ), - ) - : IconButton( - icon: SvgPicture.asset( - 'assets/images/video/danmu_close.svg', - // ignore: deprecated_member_use - color: - Theme.of(context).colorScheme.outline, - ), - onPressed: () {}, - ), - ), - ), - const SizedBox(width: 18), - ], - ), - )), - ], - ), - ), - ); - - /// 手动播放 - Widget handlePlayPanel() { - return Stack( - children: [ - GestureDetector( - onTap: () { - handlePlay(); - }, - child: NetworkImgLayer( - type: 'emote', - src: vdCtr.videoItem['pic'], - width: Get.width, - height: videoHeight.value, - ), - ), - Positioned( - top: 0, - left: 0, - right: 0, - child: buildCustomAppBar(), - ), - Positioned( - right: 12, - bottom: 10, - child: IconButton( - tooltip: '播放', - onPressed: () => handlePlay(), - icon: Image.asset( - 'assets/images/play.png', - width: 60, - height: 60, - )), - ), - ], - ); - } - Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, bottom: MediaQuery.of(context).orientation == Orientation.portrait && plPlayerController?.isFullScreen.value == true, - left: false, //plPlayerController?.isFullScreen.value != true, - right: false, //plPlayerController?.isFullScreen.value != true, + left: false, + right: false, child: Stack( children: [ Scaffold( @@ -511,12 +506,22 @@ class _VideoDetailPageState extends State controller: _extendNestCtr, headerSliverBuilder: (BuildContext context2, bool innerBoxIsScrolled) { + final Orientation orientation = + MediaQuery.of(context).orientation; + final bool isFullScreen = + plPlayerController?.isFullScreen.value == true; + final double expandedHeight = + orientation == Orientation.landscape || isFullScreen + ? (MediaQuery.sizeOf(context).height - + (orientation == Orientation.landscape + ? 0 + : MediaQuery.of(context).padding.top)) + : videoHeight.value; return [ Obx( () { - if (MediaQuery.of(context).orientation == - Orientation.landscape || - plPlayerController?.isFullScreen.value == true) { + if (orientation == Orientation.landscape || + isFullScreen) { enterFullScreen(); } else { exitFullScreen(); @@ -528,15 +533,7 @@ class _VideoDetailPageState extends State elevation: 0, scrolledUnderElevation: 0, forceElevated: innerBoxIsScrolled, - expandedHeight: MediaQuery.of(context).orientation == - Orientation.landscape || - plPlayerController?.isFullScreen.value == true - ? (MediaQuery.sizeOf(context).height - - (MediaQuery.of(context).orientation == - Orientation.landscape - ? 0 - : MediaQuery.of(context).padding.top)) - : videoHeight.value, + expandedHeight: expandedHeight, backgroundColor: Colors.black, flexibleSpace: FlexibleSpaceBar( background: PopScope( @@ -556,13 +553,13 @@ class _VideoDetailPageState extends State child: LayoutBuilder( builder: (BuildContext context, BoxConstraints boxConstraints) { - // final double maxWidth = - // boxConstraints.maxWidth; - // final double maxHeight = - // boxConstraints.maxHeight; return Stack( children: [ - if (isShowing) videoPlayerPanel, + if (isShowing) + Padding( + padding: EdgeInsets.only(top: 0), + child: videoPlayerPanel, + ), /// 关闭自动播放时 手动播放 Obx( @@ -604,7 +601,7 @@ class _VideoDetailPageState extends State color: Theme.of(context).colorScheme.background, child: Column( children: [ - tabbarBuild, + tabbarBuild(), Expanded( child: TabBarView( controller: vdCtr.tabCtr, From 926c99001ebd47b860073029419f3d93338ab346 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 22 Apr 2024 23:58:36 +0800 Subject: [PATCH 133/349] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E7=AE=80?= =?UTF-8?q?=E4=BB=8B=E5=A2=9E=E5=8A=A0aid=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../introduction/widgets/intro_detail.dart | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index 1e9bb842..3ec92023 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -23,16 +23,34 @@ class IntroDetail extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ const SizedBox(height: 4), - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); - SmartDialog.showToast('已复制'); - }, - child: Text( - videoDetail!.bvid!, - style: TextStyle( - fontSize: 13, color: Theme.of(context).colorScheme.primary), - ), + Row( + children: [ + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.bvid!, + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), + ), + ), + const SizedBox(width: 10), + GestureDetector( + onTap: () { + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.aid!.toString(), + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), + ), + ) + ], ), const SizedBox(height: 4), Text.rich( From 31125435b11501e4236456e31541ab48aa996118 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 23 Apr 2024 23:40:40 +0800 Subject: [PATCH 134/349] =?UTF-8?q?opt:=20=E9=A6=96=E9=A1=B5=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E6=A0=8F=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/overlay_pop.dart | 1 + lib/common/widgets/video_card_v.dart | 283 +++++++++++---------------- lib/pages/home/view.dart | 87 ++++---- lib/pages/rcmd/view.dart | 20 -- 4 files changed, 160 insertions(+), 231 deletions(-) diff --git a/lib/common/widgets/overlay_pop.dart b/lib/common/widgets/overlay_pop.dart index fe9b9377..4f0a3899 100644 --- a/lib/common/widgets/overlay_pop.dart +++ b/lib/common/widgets/overlay_pop.dart @@ -62,6 +62,7 @@ class OverlayPop extends StatelessWidget { Expanded( child: Text( videoItem.title! as String, + style: Theme.of(context).textTheme.titleSmall, ), ), const SizedBox(width: 4), diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 0d96f7b7..0598f973 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import '../../models/model_rec_video_item.dart'; +import 'overlay_pop.dart'; import 'stat/danmu.dart'; import 'stat/view.dart'; import '../../http/dynamics.dart'; @@ -19,15 +20,15 @@ import 'network_img_layer.dart'; class VideoCardV extends StatelessWidget { final dynamic videoItem; final int crossAxisCount; - final Function()? longPress; - final Function()? longPressEnd; + // final Function()? longPress; + // final Function()? longPressEnd; const VideoCardV({ Key? key, required this.videoItem, required this.crossAxisCount, - this.longPress, - this.longPressEnd, + // this.longPress, + // this.longPressEnd, }) : super(key: key); bool isStringNumeric(String str) { @@ -131,59 +132,55 @@ class VideoCardV extends StatelessWidget { elevation: 0, clipBehavior: Clip.hardEdge, margin: EdgeInsets.zero, - child: GestureDetector( + child: InkWell( + onTap: () async => onPushDetail(heroTag), onLongPress: () { - if (longPress != null) { - longPress!(); - } + SmartDialog.show( + builder: (context) => OverlayPop( + videoItem: videoItem, + closeFn: () => SmartDialog.dismiss(), + ), + ); }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async => onPushDetail(heroTag), - child: Column( - 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.pic, - width: maxWidth, - height: maxHeight, - ), + child: Column( + 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.pic, + width: maxWidth, + height: maxHeight, ), - if (videoItem.duration > 0) - if (crossAxisCount == 1) ...[ - PBadge( - bottom: 10, - right: 10, - text: Utils.timeFormat(videoItem.duration), - ) - ] else ...[ - PBadge( - bottom: 6, - right: 7, - size: 'small', - type: 'gray', - text: Utils.timeFormat(videoItem.duration), - ) - ], - ], - ); - }), - ), - VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) - ], - ), + ), + if (videoItem.duration > 0) + if (crossAxisCount == 1) ...[ + PBadge( + bottom: 10, + right: 10, + text: Utils.timeFormat(videoItem.duration), + ) + ] else ...[ + PBadge( + bottom: 6, + right: 7, + size: 'small', + type: 'gray', + text: Utils.timeFormat(videoItem.duration), + ) + ], + ], + ); + }), + ), + VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) + ], ), ), ); @@ -196,122 +193,73 @@ class VideoContent extends StatelessWidget { const VideoContent( {Key? key, required this.videoItem, required this.crossAxisCount}) : super(key: key); + + Widget _buildBadge(String text, String type, [double fs = 12]) { + return PBadge( + text: text, + stack: 'normal', + size: 'small', + type: type, + fs: fs, + ); + } + @override Widget build(BuildContext context) { - return Expanded( - flex: crossAxisCount == 1 ? 0 : 1, - child: Padding( - padding: crossAxisCount == 1 - ? const EdgeInsets.fromLTRB(9, 9, 9, 4) - : const EdgeInsets.fromLTRB(5, 8, 5, 4), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Expanded( - child: Text( - videoItem.title, - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - if (videoItem.goto == 'av' && crossAxisCount == 1) ...[ - const SizedBox(width: 10), - VideoPopupMenu( - size: 32, - iconSize: 18, - videoItem: videoItem, - ), - ], - ], - ), - if (crossAxisCount > 1) ...[ - const SizedBox(height: 2), - VideoStat( - videoItem: videoItem, - crossAxisCount: crossAxisCount, - ), - ], - if (crossAxisCount == 1) const SizedBox(height: 4), - Row( - children: [ - if (videoItem.goto == 'bangumi') ...[ - PBadge( - text: videoItem.bangumiBadge, - stack: 'normal', - size: 'small', - type: 'line', - fs: 9, - ) - ], - if (videoItem.rcmdReason != null && - videoItem.rcmdReason.content != '') ...[ - PBadge( - text: videoItem.rcmdReason.content, - stack: 'normal', - size: 'small', - type: 'color', - ) - ], - if (videoItem.goto == 'picture') ...[ - const PBadge( - text: '动态', - stack: 'normal', - size: 'small', - type: 'line', - fs: 9, - ) - ], - if (videoItem.isFollowed == 1) ...[ - const PBadge( - text: '已关注', - stack: 'normal', - size: 'small', - type: 'color', - ) - ], - Expanded( - flex: crossAxisCount == 1 ? 0 : 1, - child: Text( - videoItem.owner.name, - maxLines: 1, - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - ), - if (crossAxisCount == 1) ...[ - Text( - ' • ', - style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, - ), - ), - VideoStat( - videoItem: videoItem, - crossAxisCount: crossAxisCount, - ), - const Spacer(), - ], - if (videoItem.goto == 'av' && crossAxisCount != 1) ...[ - VideoPopupMenu( - size: 24, - iconSize: 14, - videoItem: videoItem, - ), - ] else ...[ - const SizedBox(height: 24) - ] - ], - ), + return Padding( + padding: crossAxisCount == 1 + ? const EdgeInsets.fromLTRB(9, 9, 9, 4) + : const EdgeInsets.fromLTRB(5, 8, 5, 4), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + videoItem.title, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + if (crossAxisCount > 1) ...[ + const SizedBox(height: 2), + VideoStat(videoItem: videoItem, crossAxisCount: crossAxisCount), ], - ), + if (crossAxisCount == 1) const SizedBox(height: 4), + Row( + children: [ + if (videoItem.goto == 'bangumi') + _buildBadge(videoItem.bangumiBadge, 'line', 9), + if (videoItem.rcmdReason?.content != null && + videoItem.rcmdReason.content != '') + _buildBadge(videoItem.rcmdReason.content, 'color'), + if (videoItem.goto == 'picture') _buildBadge('动态', 'line', 9), + if (videoItem.isFollowed == 1) _buildBadge('已关注', 'color'), + Expanded( + flex: crossAxisCount == 1 ? 0 : 1, + child: Text( + videoItem.owner.name, + maxLines: 1, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + if (crossAxisCount == 1) ...[ + const SizedBox(width: 10), + VideoStat( + videoItem: videoItem, + crossAxisCount: crossAxisCount, + ), + const Spacer(), + ], + if (videoItem.goto == 'av') + VideoPopupMenu( + size: 24, + iconSize: 14, + videoItem: videoItem, + ), + ], + ), + ], ), ); } @@ -383,7 +331,6 @@ class VideoPopupMenu extends StatelessWidget { size: iconSize, ), position: PopupMenuPosition.under, - // constraints: const BoxConstraints(maxHeight: 35), onSelected: (String type) {}, itemBuilder: (BuildContext context) => >[ PopupMenuItem( diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index cc228f6b..a25389bd 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -214,6 +214,34 @@ class UserInfoWidget extends StatelessWidget { final VoidCallback? callback; final HomeController? ctr; + Widget buildLoggedInWidget(context) { + return Stack( + children: [ + NetworkImgLayer( + type: 'avatar', + width: 34, + height: 34, + src: userFace, + ), + Positioned.fill( + child: Material( + color: Colors.transparent, + child: InkWell( + onTap: () => callback?.call(), + splashColor: Theme.of(context) + .colorScheme + .primaryContainer + .withOpacity(0.3), + borderRadius: const BorderRadius.all( + Radius.circular(50), + ), + ), + ), + ) + ], + ); + } + @override Widget build(BuildContext context) { return Row( @@ -231,31 +259,7 @@ class UserInfoWidget extends StatelessWidget { const SizedBox(width: 8), Obx( () => userLogin.value - ? Stack( - children: [ - NetworkImgLayer( - type: 'avatar', - width: 34, - height: 34, - src: userFace, - ), - Positioned.fill( - child: Material( - color: Colors.transparent, - child: InkWell( - onTap: () => callback?.call(), - splashColor: Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.3), - borderRadius: const BorderRadius.all( - Radius.circular(50), - ), - ), - ), - ) - ], - ) + ? buildLoggedInWidget(context) : DefaultUser(callback: () => callback!()), ), ], @@ -402,30 +406,27 @@ class SearchBar extends StatelessWidget { color: colorScheme.onSecondaryContainer.withOpacity(0.05), child: InkWell( splashColor: colorScheme.primaryContainer.withOpacity(0.3), - onTap: () => Get.toNamed( - '/search', - parameters: {'hintText': ctr!.defaultSearch.value}, - ), - child: Row( - children: [ - const SizedBox(width: 14), - Icon( - Icons.search_outlined, - color: colorScheme.onSecondaryContainer, - ), - const SizedBox(width: 10), - Obx( - () => Expanded( - child: Text( + onTap: () => Get.toNamed('/search', + parameters: {'hintText': ctr!.defaultSearch.value}), + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 14), + child: Row( + children: [ + Icon( + Icons.search_outlined, + color: colorScheme.onSecondaryContainer, + ), + const SizedBox(width: 10), + Obx( + () => Text( ctr!.defaultSearch.value, maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(color: colorScheme.outline), ), ), - ), - const SizedBox(width: 15), - ], + ], + ), ), ), ), diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index acc1e654..67567870 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -5,9 +5,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_v.dart'; import 'package:pilipala/utils/main_stream.dart'; @@ -118,16 +116,6 @@ class _RcmdPageState extends State ); } - OverlayEntry _createPopupDialog(videoItem) { - return OverlayEntry( - builder: (context) => AnimatedDialog( - closeFn: _rcmdController.popupDialog?.remove, - child: OverlayPop( - videoItem: videoItem, closeFn: _rcmdController.popupDialog?.remove), - ), - ); - } - Widget contentGrid(ctr, videoList) { // double maxWidth = Get.size.width; // int baseWidth = 500; @@ -158,14 +146,6 @@ class _RcmdPageState extends State ? VideoCardV( videoItem: videoList[index], crossAxisCount: crossAxisCount, - longPress: () { - _rcmdController.popupDialog = - _createPopupDialog(videoList[index]); - Overlay.of(context).insert(_rcmdController.popupDialog!); - }, - longPressEnd: () { - _rcmdController.popupDialog?.remove(); - }, ) : const VideoCardVSkeleton(); }, From ab45d2e6a68a5000c82e038a3f1308ab88b4be0d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 24 Apr 2024 23:56:07 +0800 Subject: [PATCH 135/349] =?UTF-8?q?opt:=20=E9=A6=96=E9=A1=B5=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E5=8D=A1=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_v.dart | 310 ++++++++++++++------------- 1 file changed, 157 insertions(+), 153 deletions(-) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 0598f973..9916aa7a 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/feed_back.dart'; import '../../models/model_rec_video_item.dart'; import 'overlay_pop.dart'; import 'stat/danmu.dart'; @@ -20,15 +21,11 @@ import 'network_img_layer.dart'; class VideoCardV extends StatelessWidget { final dynamic videoItem; final int crossAxisCount; - // final Function()? longPress; - // final Function()? longPressEnd; const VideoCardV({ Key? key, required this.videoItem, required this.crossAxisCount, - // this.longPress, - // this.longPressEnd, }) : super(key: key); bool isStringNumeric(String str) { @@ -128,60 +125,56 @@ class VideoCardV extends StatelessWidget { @override Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(videoItem.id); - return Card( - elevation: 0, - clipBehavior: Clip.hardEdge, - margin: EdgeInsets.zero, - child: InkWell( - onTap: () async => onPushDetail(heroTag), - onLongPress: () { - SmartDialog.show( - builder: (context) => OverlayPop( - videoItem: videoItem, - closeFn: () => SmartDialog.dismiss(), - ), - ); - }, - child: Column( - 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.pic, - width: maxWidth, - height: maxHeight, - ), + return InkWell( + onTap: () async => onPushDetail(heroTag), + onLongPress: () { + SmartDialog.show( + builder: (context) => OverlayPop( + videoItem: videoItem, + closeFn: () => SmartDialog.dismiss(), + ), + ); + }, + borderRadius: BorderRadius.circular(16), + child: Column( + 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.pic, + width: maxWidth, + height: maxHeight, ), - if (videoItem.duration > 0) - if (crossAxisCount == 1) ...[ - PBadge( - bottom: 10, - right: 10, - text: Utils.timeFormat(videoItem.duration), - ) - ] else ...[ - PBadge( - bottom: 6, - right: 7, - size: 'small', - type: 'gray', - text: Utils.timeFormat(videoItem.duration), - ) - ], - ], - ); - }), - ), - VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) - ], - ), + ), + if (videoItem.duration > 0) + if (crossAxisCount == 1) ...[ + PBadge( + bottom: 10, + right: 10, + text: Utils.timeFormat(videoItem.duration), + ) + ] else ...[ + PBadge( + bottom: 6, + right: 7, + size: 'small', + type: 'gray', + text: Utils.timeFormat(videoItem.duration), + ) + ], + ], + ); + }), + ), + VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) + ], ), ); } @@ -252,11 +245,28 @@ class VideoContent extends StatelessWidget { const Spacer(), ], if (videoItem.goto == 'av') - VideoPopupMenu( - size: 24, - iconSize: 14, - videoItem: videoItem, - ), + SizedBox( + width: 24, + height: 24, + child: IconButton( + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return MorePanel(videoItem: videoItem); + }, + ); + }, + icon: Icon( + Icons.more_vert_outlined, + color: Theme.of(context).colorScheme.outline, + size: 14, + ), + ), + ) ], ), ], @@ -279,15 +289,9 @@ class VideoStat extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - StatView( - theme: 'gray', - view: videoItem.stat.view, - ), + StatView(theme: 'gray', view: videoItem.stat.view), const SizedBox(width: 8), - StatDanMu( - theme: 'gray', - danmu: videoItem.stat.danmu, - ), + StatDanMu(theme: 'gray', danmu: videoItem.stat.danmu), if (videoItem is RecVideoItemModel) ...[ crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8), RichText( @@ -306,98 +310,98 @@ class VideoStat extends StatelessWidget { } } -class VideoPopupMenu extends StatelessWidget { - final double? size; - final double? iconSize; +class MorePanel extends StatelessWidget { final dynamic videoItem; + const MorePanel({super.key, required this.videoItem}); - const VideoPopupMenu({ - Key? key, - required this.size, - required this.iconSize, - required this.videoItem, - }) : super(key: key); + Future menuActionHandler(String type) async { + switch (type) { + case 'block': + blockUser(); + break; + case 'watchLater': + var res = await UserHttp.toViewLater(bvid: videoItem.bvid as String); + SmartDialog.showToast(res['msg']); + Get.back(); + break; + default: + } + } + + void blockUser() async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: Text('确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' + '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), + actions: [ + TextButton( + onPressed: () => SmartDialog.dismiss(), + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await VideoHttp.relationMod( + mid: videoItem.owner.mid, + act: 5, + reSrc: 11, + ); + SmartDialog.dismiss(); + SmartDialog.showToast(res['msg'] ?? '成功'); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } @override Widget build(BuildContext context) { - return SizedBox( - width: size, - height: size, - child: PopupMenuButton( - padding: EdgeInsets.zero, - icon: Icon( - Icons.more_vert_outlined, - color: Theme.of(context).colorScheme.outline, - size: iconSize, - ), - position: PopupMenuPosition.under, - onSelected: (String type) {}, - itemBuilder: (BuildContext context) => >[ - PopupMenuItem( - onTap: () async { - var res = - await UserHttp.toViewLater(bvid: videoItem.bvid as String); - SmartDialog.showToast(res['msg']); - }, - value: 'pause', - height: 40, - child: const Row( - children: [ - Icon(Icons.watch_later_outlined, size: 16), - SizedBox(width: 6), - Text('稍后再看', style: TextStyle(fontSize: 13)) - ], + return Container( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () => Get.back(), + child: Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.outline, + borderRadius: const BorderRadius.all(Radius.circular(3))), + ), + ), ), ), - const PopupMenuDivider(), - PopupMenuItem( - onTap: () async { - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text( - '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' - '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: Text( - '点错了', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), - ), - TextButton( - onPressed: () async { - var res = await VideoHttp.relationMod( - mid: videoItem.owner.mid, - act: 5, - reSrc: 11, - ); - SmartDialog.dismiss(); - SmartDialog.showToast(res['msg'] ?? '成功'); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); - }, - value: 'pause', - height: 40, - child: Row( - children: [ - const Icon(Icons.block, size: 16), - const SizedBox(width: 6), - Text('拉黑:${videoItem.owner.name}', - style: const TextStyle(fontSize: 13)) - ], + ListTile( + onTap: () async => await menuActionHandler('block'), + minLeadingWidth: 0, + leading: const Icon(Icons.block, size: 19), + title: Text( + '拉黑up主 「${videoItem.owner.name}」', + style: Theme.of(context).textTheme.titleSmall, ), ), + ListTile( + onTap: () async => await menuActionHandler('watchLater'), + minLeadingWidth: 0, + leading: const Icon(Icons.watch_later_outlined, size: 19), + title: + Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall), + ), ], ), ); From 1f426881f2c4ae43f5f58695ff342d175460f2a8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 25 Apr 2024 22:42:42 +0800 Subject: [PATCH 136/349] =?UTF-8?q?mod:=20=E4=BD=BF=E7=94=A8feed=5Fversion?= =?UTF-8?q?=20V3=E6=8E=A8=E8=8D=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index d43656b2..f8e19060 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -35,7 +35,7 @@ class VideoHttp { Api.recommendListWeb, data: { 'version': 1, - 'feed_version': 'V8', + 'feed_version': 'V3', 'homepage_ver': 1, 'ps': ps, 'fresh_idx': freshIdx, From c6f5bc561c5f4f5c275b569338132be9df41f6f8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 25 Apr 2024 23:18:21 +0800 Subject: [PATCH 137/349] =?UTF-8?q?feat:=20up=E6=8A=95=E7=A8=BF=E5=B1=95?= =?UTF-8?q?=E7=A4=BA=E5=85=85=E7=94=B5=E4=B8=93=E5=B1=9E=E6=A0=87=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_h.dart | 9 +++++++++ lib/pages/member_archive/view.dart | 1 + 2 files changed, 10 insertions(+) diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 99059a9e..25e701ac 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -23,6 +23,7 @@ class VideoCardH extends StatelessWidget { this.showView = true, this.showDanmaku = true, this.showPubdate = false, + this.showCharge = false, }); // ignore: prefer_typing_uninitialized_variables final videoItem; @@ -33,6 +34,7 @@ class VideoCardH extends StatelessWidget { final bool showView; final bool showDanmaku; final bool showPubdate; + final bool showCharge; @override Widget build(BuildContext context) { @@ -121,6 +123,13 @@ class VideoCardH extends StatelessWidget { // videoItem.rcmdReason.content != '') // pBadge(videoItem.rcmdReason.content, context, // 6.0, 6.0, null, null), + if (showCharge && videoItem?.isChargingSrc) + const PBadge( + text: '充电专属', + right: 6.0, + top: 6.0, + type: 'primary', + ), ], ); }, diff --git a/lib/pages/member_archive/view.dart b/lib/pages/member_archive/view.dart index 3103683a..f38ca0cb 100644 --- a/lib/pages/member_archive/view.dart +++ b/lib/pages/member_archive/view.dart @@ -79,6 +79,7 @@ class _MemberArchivePageState extends State { videoItem: list[index], showOwner: false, showPubdate: true, + showCharge: true, ); }, childCount: list.length, From 4e66a3f9e7ab379a5501f40ea5fabd983e0a8480 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 26 Apr 2024 23:22:26 +0800 Subject: [PATCH 138/349] =?UTF-8?q?opt:=20=E8=AF=84=E8=AE=BA=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E4=BC=98=E5=85=88=E5=B1=95=E7=A4=BA=E4=BA=8C=E7=BA=A7?= =?UTF-8?q?=E8=AF=84=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 6 +- lib/pages/video/detail/controller.dart | 5 +- lib/pages/video/detail/reply/view.dart | 9 +- .../detail/reply/widgets/reply_item.dart | 133 +++++++++--------- .../video/detail/reply_reply/controller.dart | 10 +- lib/pages/video/detail/reply_reply/view.dart | 14 +- 6 files changed, 99 insertions(+), 78 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index c6ec682a..757ba0ce 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -106,7 +106,7 @@ class _DynamicDetailPageState extends State } // 查看二级评论 - void replyReply(replyItem) { + void replyReply(replyItem, currentReply) { int oid = replyItem.oid; int rpid = replyItem.rpid!; Get.to( @@ -324,8 +324,8 @@ class _DynamicDetailPageState extends State replyItem: replyList[index], showReplyRow: true, replyLevel: '1', - replyReply: (replyItem) => - replyReply(replyItem), + replyReply: (replyItem, currentReply) => + replyReply(replyItem, currentReply), replyType: ReplyType.values[replyType], addReply: (replyItem) { replyList[index] diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 4d40e535..4613c7ca 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -161,11 +161,11 @@ class VideoDetailController extends GetxController getSubtitle(); } - showReplyReplyPanel() { + showReplyReplyPanel(oid, fRpid, firstFloor, currentReply) { replyReplyBottomSheetCtr = scaffoldKey.currentState?.showBottomSheet((BuildContext context) { return VideoReplyReplyPanel( - oid: oid.value, + oid: oid, rpid: fRpid, closePanel: () => { fRpid = 0, @@ -174,6 +174,7 @@ class VideoDetailController extends GetxController replyType: ReplyType.video, source: 'videoDetail', sheetHeight: sheetHeight.value, + currentReply: currentReply, ); }); replyReplyBottomSheetCtr?.closed.then((value) { diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 2a167fe9..3b4891b8 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -110,14 +110,15 @@ class _VideoReplyPanelState extends State } // 展示二级回复 - void replyReply(replyItem) { + void replyReply(replyItem, currentReply) { final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); if (replyItem != null) { videoDetailCtr.oid.value = replyItem.oid; videoDetailCtr.fRpid = replyItem.rpid!; videoDetailCtr.firstFloor = replyItem; - videoDetailCtr.showReplyReplyPanel(); + videoDetailCtr.showReplyReplyPanel( + replyItem.oid, replyItem.rpid!, replyItem, currentReply); } } @@ -228,8 +229,8 @@ class _VideoReplyPanelState extends State .replyList[index], showReplyRow: true, replyLevel: replyLevel, - replyReply: (replyItem) => - replyReply(replyItem), + replyReply: (replyItem, currentReply) => + replyReply(replyItem, currentReply), replyType: ReplyType.video, ); } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 50fe20d4..8a8e4af7 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -12,7 +12,6 @@ import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; @@ -45,7 +44,7 @@ class ReplyItem extends StatelessWidget { onTap: () { feedBack(); if (replyReply != null) { - replyReply!(replyItem); + replyReply!(replyItem, null); } }, onLongPress: () { @@ -59,28 +58,23 @@ class ReplyItem extends StatelessWidget { }, ); }, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(12, 14, 8, 5), - child: content(context), - ), - Divider( - indent: 55, - endIndent: 15, - height: 0.3, - color: Theme.of(context) - .colorScheme - .onInverseSurface - .withOpacity(0.5), - ) - ], + child: Container( + padding: const EdgeInsets.fromLTRB(12, 14, 8, 5), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + width: 1, + color: + Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.5), + ))), + child: content(context), ), ), ); } Widget lfAvtar(BuildContext context, String heroTag) { + ColorScheme colorScheme = Theme.of(context).colorScheme; return Stack( children: [ Hero( @@ -100,11 +94,11 @@ class ReplyItem extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), - color: Theme.of(context).colorScheme.background, + color: colorScheme.background, ), child: Icon( Icons.offline_bolt, - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, size: 16, ), ), @@ -117,7 +111,7 @@ class ReplyItem extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), - color: Theme.of(context).colorScheme.background, + color: colorScheme.background, ), child: Image.asset( 'assets/images/big-vip.png', @@ -131,6 +125,8 @@ class ReplyItem extends StatelessWidget { Widget content(BuildContext context) { final String heroTag = Utils.makeHeroTag(replyItem!.mid); + ColorScheme colorScheme = Theme.of(context).colorScheme; + TextTheme textTheme = Theme.of(context).textTheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -160,16 +156,17 @@ class ReplyItem extends StatelessWidget { style: TextStyle( color: replyItem!.member!.vip!['vipStatus'] > 0 ? const Color.fromARGB(255, 251, 100, 163) - : Theme.of(context).colorScheme.outline, + : colorScheme.outline, fontSize: 13, ), ), - const SizedBox(width: 6), - Image.asset( - 'assets/images/lv/lv${replyItem!.member!.level}.png', - height: 11, + Padding( + padding: const EdgeInsets.only(left: 6, right: 6), + child: Image.asset( + 'assets/images/lv/lv${replyItem!.member!.level}.png', + height: 11, + ), ), - const SizedBox(width: 6), if (replyItem!.isUp!) const PBadge( text: 'UP', @@ -184,9 +181,8 @@ class ReplyItem extends StatelessWidget { Text( Utils.dateFormat(replyItem!.ctime), style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelSmall!.fontSize, - color: Theme.of(context).colorScheme.outline, + fontSize: textTheme.labelSmall!.fontSize, + color: colorScheme.outline, ), ), if (replyItem!.replyControl != null && @@ -194,11 +190,8 @@ class ReplyItem extends StatelessWidget { Text( ' • ${replyItem!.replyControl!.location!}', style: TextStyle( - fontSize: Theme.of(context) - .textTheme - .labelSmall! - .fontSize, - color: Theme.of(context).colorScheme.outline), + fontSize: textTheme.labelSmall!.fontSize, + color: colorScheme.outline), ), ], ) @@ -256,6 +249,8 @@ class ReplyItem extends StatelessWidget { // 感谢、回复、复制 Widget bottonAction(BuildContext context, replyControl) { + ColorScheme colorScheme = Theme.of(context).colorScheme; + TextTheme textTheme = Theme.of(context).textTheme; return Row( children: [ const SizedBox(width: 32), @@ -287,15 +282,13 @@ class ReplyItem extends StatelessWidget { }, child: Row(children: [ Icon(Icons.reply, - size: 18, - color: - Theme.of(context).colorScheme.outline.withOpacity(0.8)), + size: 18, color: colorScheme.outline.withOpacity(0.8)), const SizedBox(width: 3), Text( '回复', style: TextStyle( - fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, - color: Theme.of(context).colorScheme.outline, + fontSize: textTheme.labelMedium!.fontSize, + color: colorScheme.outline, ), ), ]), @@ -306,8 +299,8 @@ class ReplyItem extends StatelessWidget { Text( 'up主觉得很赞', style: TextStyle( - color: Theme.of(context).colorScheme.primary, - fontSize: Theme.of(context).textTheme.labelMedium!.fontSize), + color: colorScheme.primary, + fontSize: textTheme.labelMedium!.fontSize), ), const SizedBox(width: 2), ], @@ -316,8 +309,8 @@ class ReplyItem extends StatelessWidget { Text( '热评', style: TextStyle( - color: Theme.of(context).colorScheme.primary, - fontSize: Theme.of(context).textTheme.labelMedium!.fontSize), + color: colorScheme.primary, + fontSize: textTheme.labelMedium!.fontSize), ), const Spacer(), ZanButton(replyItem: replyItem, replyType: replyType), @@ -347,10 +340,13 @@ class ReplyItemRow extends StatelessWidget { Widget build(BuildContext context) { final bool isShow = replyControl!.isShow!; final int extraRow = replyControl != null && isShow ? 1 : 0; + ColorScheme colorScheme = Theme.of(context).colorScheme; + TextTheme textTheme = Theme.of(context).textTheme; + return Container( margin: const EdgeInsets.only(left: 42, right: 4, top: 0), child: Material( - color: Theme.of(context).colorScheme.onInverseSurface, + color: colorScheme.onInverseSurface, borderRadius: BorderRadius.circular(6), clipBehavior: Clip.hardEdge, animationDuration: Duration.zero, @@ -361,7 +357,9 @@ class ReplyItemRow extends StatelessWidget { for (int i = 0; i < replies!.length; i++) ...[ InkWell( // 一楼点击评论展开评论详情 - onTap: () => replyReply!(replyItem), + // onTap: () { + // replyReply?.call(replyItem, replies![i]); + // }, onLongPress: () { feedBack(); showModalBottomSheet( @@ -379,7 +377,7 @@ class ReplyItemRow extends StatelessWidget { 8, i == 0 && (extraRow == 1 || replies!.length > 1) ? 8 : 5, 8, - i == 0 && (extraRow == 1 || replies!.length > 1) ? 5 : 6, + 6, ), child: Text.rich( overflow: TextOverflow.ellipsis, @@ -393,7 +391,7 @@ class ReplyItemRow extends StatelessWidget { .textTheme .titleSmall! .fontSize, - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () { @@ -429,15 +427,14 @@ class ReplyItemRow extends StatelessWidget { if (extraRow == 1) InkWell( // 一楼点击【共xx条回复】展开评论详情 - onTap: () => replyReply!(replyItem), + onTap: () => replyReply!(replyItem, null), child: Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(8, 5, 8, 8), child: Text.rich( TextSpan( style: TextStyle( - fontSize: - Theme.of(context).textTheme.labelMedium!.fontSize, + fontSize: textTheme.labelMedium!.fontSize, ), children: [ if (replyControl!.upReply!) @@ -445,7 +442,7 @@ class ReplyItemRow extends StatelessWidget { TextSpan( text: replyControl!.entryText!, style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), ) ], @@ -464,6 +461,7 @@ InlineSpan buildContent( BuildContext context, replyItem, replyReply, fReplyItem) { final String routePath = Get.currentRoute; bool isVideoPage = routePath.startsWith('/video'); + ColorScheme colorScheme = Theme.of(context).colorScheme; // replyItem 当前回复内容 // replyReply 查看二楼回复(回复详情)回调 @@ -533,8 +531,10 @@ InlineSpan buildContent( TextSpan( text: str, recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem), + ..onTap = () => replyReply?.call( + replyItem.root == 0 ? replyItem : fReplyItem, + replyItem, + ), ), ); } @@ -564,7 +564,7 @@ InlineSpan buildContent( TextSpan( text: matchStr, style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () { @@ -584,7 +584,7 @@ InlineSpan buildContent( text: ' $matchStr ', style: isVideoPage ? TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ) : null, recognizer: TapGestureRecognizer() @@ -624,14 +624,14 @@ InlineSpan buildContent( child: Image.network( content.jumpUrl[matchStr]['prefix_icon'], height: 19, - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), ) ], TextSpan( text: content.jumpUrl[matchStr]['title'], style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () async { @@ -721,7 +721,7 @@ InlineSpan buildContent( TextSpan( text: matchStr, style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () { @@ -747,7 +747,7 @@ InlineSpan buildContent( text: ' $matchStr ', style: isVideoPage ? TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ) : null, recognizer: TapGestureRecognizer() @@ -786,14 +786,14 @@ InlineSpan buildContent( child: Image.network( content.jumpUrl[patternStr]['prefix_icon'], height: 19, - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), ) ], TextSpan( text: content.jumpUrl[patternStr]['title'], style: TextStyle( - color: Theme.of(context).colorScheme.primary, + color: colorScheme.primary, ), recognizer: TapGestureRecognizer() ..onTap = () { @@ -997,7 +997,8 @@ class MorePanel extends StatelessWidget { @override Widget build(BuildContext context) { - Color errorColor = Theme.of(context).colorScheme.error; + ColorScheme colorScheme = Theme.of(context).colorScheme; + TextTheme textTheme = Theme.of(context).textTheme; return Container( padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), child: Column( @@ -1013,7 +1014,7 @@ class MorePanel extends StatelessWidget { width: 32, height: 3, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.outline, + color: colorScheme.outline, borderRadius: const BorderRadius.all(Radius.circular(3))), ), ), @@ -1023,13 +1024,13 @@ class MorePanel extends StatelessWidget { onTap: () async => await menuActionHandler('copyAll'), minLeadingWidth: 0, leading: const Icon(Icons.copy_all_outlined, size: 19), - title: Text('复制全部', style: Theme.of(context).textTheme.titleSmall), + title: Text('复制全部', style: textTheme.titleSmall), ), ListTile( onTap: () async => await menuActionHandler('copyFreedom'), minLeadingWidth: 0, leading: const Icon(Icons.copy_outlined, size: 19), - title: Text('自由复制', style: Theme.of(context).textTheme.titleSmall), + title: Text('自由复制', style: textTheme.titleSmall), ), // ListTile( // onTap: () async => await menuActionHandler('block'), diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index e94aaea5..4c553e29 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -26,7 +26,7 @@ class VideoReplyReplyController extends GetxController { currentPage = 0; } - Future queryReplyList({type = 'init'}) async { + Future queryReplyList({type = 'init', currentReply}) async { if (type == 'init') { currentPage = 0; } @@ -63,6 +63,14 @@ class VideoReplyReplyController extends GetxController { // res['data'].replies.addAll(replyList); } } + if (replyList.isNotEmpty && currentReply != null) { + int indexToRemove = + replyList.indexWhere((item) => currentReply.rpid == item.rpid); + // 如果找到了指定ID的项,则移除 + if (indexToRemove != -1) { + replyList.removeAt(indexToRemove); + } + } isLoadingMore = false; return res; } diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 3fe84c71..6441c600 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -20,6 +20,7 @@ class VideoReplyReplyPanel extends StatefulWidget { this.source, this.replyType, this.sheetHeight, + this.currentReply, super.key, }); final int? oid; @@ -29,6 +30,7 @@ class VideoReplyReplyPanel extends StatefulWidget { final String? source; final ReplyType? replyType; final double? sheetHeight; + final dynamic currentReply; @override State createState() => _VideoReplyReplyPanelState(); @@ -63,7 +65,9 @@ class _VideoReplyReplyPanelState extends State { }, ); - _futureBuilderFuture = _videoReplyReplyController.queryReplyList(); + _futureBuilderFuture = _videoReplyReplyController.queryReplyList( + currentReply: widget.currentReply, + ); } void replyReply(replyItem) {} @@ -107,7 +111,9 @@ class _VideoReplyReplyPanelState extends State { onRefresh: () async { setState(() {}); _videoReplyReplyController.currentPage = 0; - return await _videoReplyReplyController.queryReplyList(); + return await _videoReplyReplyController.queryReplyList( + currentReply: widget.currentReply, + ); }, child: CustomScrollView( controller: _videoReplyReplyController.scrollController, @@ -140,6 +146,10 @@ class _VideoReplyReplyPanelState extends State { if (snapshot.connectionState == ConnectionState.done) { Map? data = snapshot.data; if (data != null && data['status']) { + if (widget.currentReply != null) { + _videoReplyReplyController.replyList + .insert(0, widget.currentReply); + } // 请求成功 return Obx( () => SliverList( From 8273ff2e02f3900425465485c31d99ef3e5b5b15 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 26 Apr 2024 23:46:54 +0800 Subject: [PATCH 139/349] =?UTF-8?q?opt:=20=E8=AF=84=E8=AE=BA=E6=A1=86?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/reply.dart | 11 +--- lib/pages/dynamics/detail/controller.dart | 2 + lib/pages/dynamics/detail/view.dart | 63 +++++++++++--------- lib/pages/video/detail/reply/controller.dart | 2 + lib/pages/video/detail/reply/view.dart | 59 ++++++++++-------- lib/pages/video/detail/reply_new/view.dart | 22 ++++--- 6 files changed, 88 insertions(+), 71 deletions(-) diff --git a/lib/http/reply.dart b/lib/http/reply.dart index f080ed51..880f9072 100644 --- a/lib/http/reply.dart +++ b/lib/http/reply.dart @@ -22,19 +22,14 @@ class ReplyHttp { return { 'status': true, 'data': ReplyData.fromJson(res.data['data']), + 'code': 200, }; } else { - Map errMap = { - -400: '请求错误', - -404: '无此项', - 12002: '当前页面评论功能已关闭', - 12009: '评论主体的type不合法', - 12061: 'UP主已关闭评论区', - }; return { 'status': false, 'date': [], - 'msg': errMap[res.data['code']] ?? res.data['message'], + 'code': res.data['code'], + 'msg': res.data['message'], }; } } diff --git a/lib/pages/dynamics/detail/controller.dart b/lib/pages/dynamics/detail/controller.dart index 8e117383..f34de061 100644 --- a/lib/pages/dynamics/detail/controller.dart +++ b/lib/pages/dynamics/detail/controller.dart @@ -25,6 +25,7 @@ class DynamicDetailController extends GetxController { RxString sortTypeTitle = ReplySortType.time.titles.obs; RxString sortTypeLabel = ReplySortType.time.labels.obs; Box setting = GStrorage.setting; + RxInt replyReqCode = 200.obs; @override void onInit() { @@ -84,6 +85,7 @@ class DynamicDetailController extends GetxController { replyList.addAll(replies); } } + replyReqCode.value = res['code']; isLoadingMore = false; return res; } diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 757ba0ce..11636b5b 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -369,35 +369,40 @@ class _DynamicDetailPageState extends State curve: Curves.easeInOut, ), ), - child: FloatingActionButton( - heroTag: null, - onPressed: () { - feedBack(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (BuildContext context) { - return VideoReplyNewDialog( - oid: _dynamicDetailController.oid ?? - IdUtils.bv2av(Get.parameters['bvid']!), - root: 0, - parent: 0, - replyType: ReplyType.values[replyType], - ); - }, - ).then( - (value) => { - // 完成评论,数据添加 - if (value != null && value['data'] != null) - { - _dynamicDetailController.replyList.add(value['data']), - _dynamicDetailController.acount.value++ - } - }, - ); - }, - tooltip: '评论动态', - child: const Icon(Icons.reply), + child: Obx( + () => _dynamicDetailController.replyReqCode.value == 12061 + ? const SizedBox() + : FloatingActionButton( + heroTag: null, + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (BuildContext context) { + return VideoReplyNewDialog( + oid: _dynamicDetailController.oid ?? + IdUtils.bv2av(Get.parameters['bvid']!), + root: 0, + parent: 0, + replyType: ReplyType.values[replyType], + ); + }, + ).then( + (value) => { + // 完成评论,数据添加 + if (value != null && value['data'] != null) + { + _dynamicDetailController.replyList + .add(value['data']), + _dynamicDetailController.acount.value++ + } + }, + ); + }, + tooltip: '评论动态', + child: const Icon(Icons.reply), + ), ), ), ); diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index 06ce26ff..b718c840 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -37,6 +37,7 @@ class VideoReplyController extends GetxController { RxString sortTypeLabel = ReplySortType.time.labels.obs; Box setting = GStrorage.setting; + RxInt replyReqCode = 200.obs; @override void onInit() { @@ -106,6 +107,7 @@ class VideoReplyController extends GetxController { replyList.addAll(replies); } } + replyReqCode.value = res['code']; isLoadingMore = false; return res; } diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 3b4891b8..420f349c 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -277,32 +277,39 @@ class _VideoReplyPanelState extends State parent: fabAnimationCtr, curve: Curves.easeInOut, )), - child: FloatingActionButton( - heroTag: null, - onPressed: () { - feedBack(); - showModalBottomSheet( - context: context, - isScrollControlled: true, - builder: (BuildContext context) { - return VideoReplyNewDialog( - oid: _videoReplyController.aid ?? - IdUtils.bv2av(Get.parameters['bvid']!), - root: 0, - parent: 0, - replyType: ReplyType.video, - ); - }, - ).then( - (value) => { - // 完成评论,数据添加 - if (value != null && value['data'] != null) - {_videoReplyController.replyList.add(value['data'])} - }, - ); - }, - tooltip: '发表评论', - child: const Icon(Icons.reply), + child: Obx( + () => _videoReplyController.replyReqCode.value == 12061 + ? const SizedBox() + : FloatingActionButton( + heroTag: null, + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + isScrollControlled: true, + builder: (BuildContext context) { + return VideoReplyNewDialog( + oid: _videoReplyController.aid ?? + IdUtils.bv2av(Get.parameters['bvid']!), + root: 0, + parent: 0, + replyType: ReplyType.video, + ); + }, + ).then( + (value) => { + // 完成评论,数据添加 + if (value != null && value['data'] != null) + { + _videoReplyController.replyList + .add(value['data']) + } + }, + ); + }, + tooltip: '发表评论', + child: const Icon(Icons.reply), + ), ), ), ), diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 029e015a..3da15f64 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -43,6 +43,7 @@ class _VideoReplyNewDialogState extends State String toolbarType = 'input'; RxBool isForward = false.obs; RxBool showForward = false.obs; + RxString message = ''.obs; @override void initState() { @@ -80,15 +81,15 @@ class _VideoReplyNewDialogState extends State Future submitReplyAdd() async { feedBack(); - String message = _replyContentController.text; + // String message = _replyContentController.text; var result = await VideoHttp.replyAdd( type: widget.replyType ?? ReplyType.video, oid: widget.oid!, root: widget.root!, parent: widget.parent!, message: widget.replyItem != null && widget.replyItem!.root != 0 - ? ' 回复 @${widget.replyItem!.member!.uname!} : $message' - : message, + ? ' 回复 @${widget.replyItem!.member!.uname!} : ${message.value}' + : message.value, ); if (result['status']) { SmartDialog.showToast(result['data']['success_toast']); @@ -100,7 +101,7 @@ class _VideoReplyNewDialogState extends State if (isForward.value) { await DynamicsHttp.dynamicCreate( mid: 0, - rawText: message, + rawText: message.value, oid: widget.oid!, scene: 5, ); @@ -188,7 +189,7 @@ class _VideoReplyNewDialogState extends State autovalidateMode: AutovalidateMode.onUserInteraction, child: TextField( controller: _replyContentController, - minLines: 1, + minLines: 3, maxLines: null, autofocus: false, focusNode: replyContentFocusNode, @@ -199,6 +200,9 @@ class _VideoReplyNewDialogState extends State fontSize: 14, )), style: Theme.of(context).textTheme.bodyLarge, + onChanged: (text) { + message.value = text; + }, ), ), ), @@ -267,9 +271,11 @@ class _VideoReplyNewDialogState extends State const Spacer(), SizedBox( height: 36, - child: FilledButton( - onPressed: () => submitReplyAdd(), - child: const Text('发送'), + child: Obx( + () => FilledButton( + onPressed: message.isNotEmpty ? submitReplyAdd : null, + child: const Text('发送'), + ), ), ), ], From b752214af3c09f885e312bf23ed99567e07c4a68 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 01:04:11 +0800 Subject: [PATCH 140/349] =?UTF-8?q?fix:=20durl=E6=A0=BC=E5=BC=8F=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/play/quality.dart | 8 ++ lib/pages/video/detail/controller.dart | 84 ++++++++++++------- .../video/detail/widgets/header_control.dart | 43 ++++++---- 3 files changed, 87 insertions(+), 48 deletions(-) diff --git a/lib/models/video/play/quality.dart b/lib/models/video/play/quality.dart index 6cae84cc..7bedf62a 100644 --- a/lib/models/video/play/quality.dart +++ b/lib/models/video/play/quality.dart @@ -39,6 +39,14 @@ extension VideoQualityCode on VideoQuality { } return null; } + + static int? toCode(VideoQuality quality) { + final index = VideoQuality.values.indexOf(quality); + if (index != -1 && index < _codeList.length) { + return _codeList[index]; + } + return null; + } } extension VideoQualityDesc on VideoQuality { diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 4d40e535..3cc43005 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -51,7 +51,7 @@ class VideoDetailController extends GetxController /// 播放器配置 画质 音质 解码格式 late VideoQuality currentVideoQa; AudioQuality? currentAudioQa; - late VideoDecodeFormats currentDecodeFormats; + VideoDecodeFormats? currentDecodeFormats; // 是否开始自动播放 存在多p的情况下,第二p需要为true RxBool autoPlay = true.obs; // 视频资源是否有效 @@ -107,6 +107,7 @@ class VideoDetailController extends GetxController BottomControlType.fullscreen, ].obs; RxDouble sheetHeight = 0.0.obs; + RxString archiveSourceType = 'dash'.obs; @override void onInit() { @@ -189,37 +190,43 @@ class VideoDetailController extends GetxController plPlayerController.isBuffering.value = false; plPlayerController.buffered.value = Duration.zero; - /// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl - List videoList = - data.dash!.video!.where((i) => i.id == currentVideoQa.code).toList(); - try { - firstVideo = videoList - .firstWhere((i) => i.codecs!.startsWith(currentDecodeFormats.code)); - } catch (_) { - if (currentVideoQa == VideoQuality.dolbyVision) { - firstVideo = videoList.first; - currentDecodeFormats = - VideoDecodeFormatsCode.fromString(videoList.first.codecs!)!; - } else { - // 当前格式不可用 - currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get( - SettingBoxKey.defaultDecode, - defaultValue: VideoDecodeFormats.values.last.code))!; - firstVideo = videoList - .firstWhere((i) => i.codecs!.startsWith(currentDecodeFormats.code)); + if (archiveSourceType.value == 'dash') { + /// 根据currentVideoQa和currentDecodeFormats 重新设置videoUrl + List videoList = + data.dash!.video!.where((i) => i.id == currentVideoQa.code).toList(); + try { + firstVideo = videoList.firstWhere( + (i) => i.codecs!.startsWith(currentDecodeFormats?.code)); + } catch (_) { + if (currentVideoQa == VideoQuality.dolbyVision) { + firstVideo = videoList.first; + currentDecodeFormats = + VideoDecodeFormatsCode.fromString(videoList.first.codecs!)!; + } else { + // 当前格式不可用 + currentDecodeFormats = VideoDecodeFormatsCode.fromString(setting.get( + SettingBoxKey.defaultDecode, + defaultValue: VideoDecodeFormats.values.last.code))!; + firstVideo = videoList.firstWhere( + (i) => i.codecs!.startsWith(currentDecodeFormats?.code)); + } + } + videoUrl = firstVideo.baseUrl!; + + /// 根据currentAudioQa 重新设置audioUrl + if (currentAudioQa != null) { + final AudioItem firstAudio = data.dash!.audio!.firstWhere( + (AudioItem i) => i.id == currentAudioQa!.code, + orElse: () => data.dash!.audio!.first, + ); + audioUrl = firstAudio.baseUrl ?? ''; } } - videoUrl = firstVideo.baseUrl!; - /// 根据currentAudioQa 重新设置audioUrl - if (currentAudioQa != null) { - final AudioItem firstAudio = data.dash!.audio!.firstWhere( - (AudioItem i) => i.id == currentAudioQa!.code, - orElse: () => data.dash!.audio!.first, - ); - audioUrl = firstAudio.baseUrl ?? ''; + if (archiveSourceType.value == 'durl') { + cacheVideoQa = VideoQualityCode.toCode(currentVideoQa); + queryVideoUrl(); } - playerInit(); } @@ -272,7 +279,8 @@ class VideoDetailController extends GetxController // 视频链接 Future queryVideoUrl() async { - var result = await VideoHttp.videoUrl(cid: cid.value, bvid: bvid); + var result = + await VideoHttp.videoUrl(cid: cid.value, bvid: bvid, qn: cacheVideoQa); if (result['status']) { data = result['data']; if (data.acceptDesc!.isNotEmpty && data.acceptDesc!.contains('试看')) { @@ -290,8 +298,22 @@ class VideoDetailController extends GetxController } return result; } + if (data.durl != null) { + archiveSourceType.value = 'durl'; + videoUrl = data.durl!.first.url!; + audioUrl = ''; + defaultST = Duration.zero; + firstVideo = VideoItem(); + currentVideoQa = VideoQualityCode.fromCode(data.quality!)!; + if (autoPlay.value) { + await playerInit(); + isShowCover.value = false; + } + return result; + } final List allVideosList = data.dash!.video!; try { + archiveSourceType.value = 'dash'; // 当前可播放的最高质量视频 int currentHighVideoQa = allVideosList.first.quality!.code; // 预设的画质为null,则当前可用的最高质量 @@ -321,7 +343,7 @@ class VideoDetailController extends GetxController // 当前视频没有对应格式返回第一个 bool flag = false; for (var i in supportDecodeFormats) { - if (i.startsWith(currentDecodeFormats.code)) { + if (i.startsWith(currentDecodeFormats?.code)) { flag = true; } } @@ -335,7 +357,7 @@ class VideoDetailController extends GetxController /// 取出符合当前解码格式的videoItem try { firstVideo = videosList.firstWhere( - (e) => e.codecs!.startsWith(currentDecodeFormats.code)); + (e) => e.codecs!.startsWith(currentDecodeFormats?.code)); } catch (_) { firstVideo = videosList.first; } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index e00311c5..6acd20d7 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -180,15 +180,16 @@ class _HeaderControlState extends State { '当前音质 ${widget.videoDetailCtr!.currentAudioQa!.description}', style: subTitleStyle), ), - ListTile( - onTap: () => {Get.back(), showSetDecodeFormats()}, - dense: true, - leading: const Icon(Icons.av_timer_outlined, size: 20), - title: const Text('解码格式', style: titleStyle), - subtitle: Text( - '当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats.description}', - style: subTitleStyle), - ), + if (widget.videoDetailCtr!.currentDecodeFormats != null) + ListTile( + onTap: () => {Get.back(), showSetDecodeFormats()}, + dense: true, + leading: const Icon(Icons.av_timer_outlined, size: 20), + title: const Text('解码格式', style: titleStyle), + subtitle: Text( + '当前解码格式 ${widget.videoDetailCtr!.currentDecodeFormats!.description}', + style: subTitleStyle), + ), ListTile( onTap: () => {Get.back(), showSetRepeat()}, dense: true, @@ -541,16 +542,24 @@ class _HeaderControlState extends State { /// 可用的质量分类 int userfulQaSam = 0; - final List video = videoInfo.dash!.video!; - final Set idSet = {}; - for (final VideoItem item in video) { - final int id = item.id!; - if (!idSet.contains(id)) { - idSet.add(id); - userfulQaSam++; + if (videoInfo.dash != null) { + // dash格式视频一次请求会返回所有可播放的清晰度video + final List video = videoInfo.dash!.video!; + final Set idSet = {}; + for (final VideoItem item in video) { + final int id = item.id!; + if (!idSet.contains(id)) { + idSet.add(id); + userfulQaSam++; + } } } + if (videoInfo.durl != null) { + // durl格式视频一次请求返回对应清晰度video + userfulQaSam = videoFormat.length - 1; + } + showModalBottomSheet( context: context, elevation: 0, @@ -707,7 +716,7 @@ class _HeaderControlState extends State { void showSetDecodeFormats() { // 当前选中的解码格式 final VideoDecodeFormats currentDecodeFormats = - widget.videoDetailCtr!.currentDecodeFormats; + widget.videoDetailCtr!.currentDecodeFormats!; final VideoItem firstVideo = widget.videoDetailCtr!.firstVideo; // 当前视频可用的解码格式 final List videoFormat = videoInfo.supportFormats!; From 0312c8d01d6d8623de6f23c3fc9af8b58f2a81e3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 12:35:43 +0800 Subject: [PATCH 141/349] =?UTF-8?q?feat:=20=E6=98=BE=E7=A4=BA=E8=81=94?= =?UTF-8?q?=E5=90=88=E6=8A=95=E7=A8=BFup?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 15 +-- lib/models/video_detail_res.dart | 45 +++++++ lib/pages/video/detail/introduction/view.dart | 111 +++++++++++++----- .../introduction/widgets/staff_up_item.dart | 74 ++++++++++++ 4 files changed, 206 insertions(+), 39 deletions(-) create mode 100644 lib/pages/video/detail/introduction/widgets/staff_up_item.dart diff --git a/lib/http/video.dart b/lib/http/video.dart index d43656b2..bdfaedf7 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -192,22 +192,15 @@ class VideoHttp { // 视频信息 标题、简介 static Future videoIntro({required String bvid}) async { var res = await Request().get(Api.videoIntro, data: {'bvid': bvid}); - VideoDetailResponse result = VideoDetailResponse.fromJson(res.data); - if (result.code == 0) { + if (res.data['code'] == 0) { + VideoDetailResponse result = VideoDetailResponse.fromJson(res.data); return {'status': true, 'data': result.data!}; } else { - Map errMap = { - -400: '请求错误', - -403: '权限不足', - -404: '视频资源失效', - 62002: '稿件不可见', - 62004: '稿件审核中', - }; return { 'status': false, 'data': null, - 'code': result.code, - 'msg': errMap[result.code] ?? '请求异常', + 'code': res.data['code'], + 'msg': res.data['message'], }; } } diff --git a/lib/models/video_detail_res.dart b/lib/models/video_detail_res.dart index 38e0b877..a82b6fbb 100644 --- a/lib/models/video_detail_res.dart +++ b/lib/models/video_detail_res.dart @@ -67,6 +67,7 @@ class VideoDetailData { String? likeIcon; bool? needJumpBv; String? epId; + List? staff; VideoDetailData({ this.bvid, @@ -103,6 +104,7 @@ class VideoDetailData { this.likeIcon, this.needJumpBv, this.epId, + this.staff, }); VideoDetailData.fromJson(Map json) { @@ -155,6 +157,9 @@ class VideoDetailData { if (json['redirect_url'] != null) { epId = resolveEpId(json['redirect_url']); } + staff = json["staff"] != null + ? List.from(json["staff"]!.map((e) => Staff.fromJson(e))) + : null; } String resolveEpId(url) { @@ -652,3 +657,43 @@ class EpisodeItem { bvid = json['bvid']; } } + +class Staff { + Staff({ + this.mid, + this.title, + this.name, + this.face, + this.vip, + }); + + int? mid; + String? title; + String? name; + String? face; + int? status; + Vip? vip; + + Staff.fromJson(Map json) { + mid = json['mid']; + title = json['title']; + name = json['name']; + face = json['face']; + vip = Vip.fromJson(json['vip']); + } +} + +class Vip { + Vip({ + this.type, + this.status, + }); + + int? type; + int? status; + + Vip.fromJson(Map json) { + type = json['type']; + status = json['status']; + } +} diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index a7eae6d2..597b6def 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -22,6 +22,7 @@ import 'widgets/fav_panel.dart'; import 'widgets/intro_detail.dart'; import 'widgets/page_panel.dart'; import 'widgets/season_panel.dart'; +import 'widgets/staff_up_item.dart'; class VideoIntroPanel extends StatefulWidget { final String bvid; @@ -409,32 +410,35 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ) ], - GestureDetector( - onTap: onPushMember, - child: Container( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4), - child: Row( - children: [ - NetworkImgLayer( - type: 'avatar', - src: widget.videoDetail!.owner!.face, - width: 34, - height: 34, - fadeInDuration: Duration.zero, - fadeOutDuration: Duration.zero, - ), - const SizedBox(width: 10), - Text(owner.name, style: const TextStyle(fontSize: 13)), - const SizedBox(width: 6), - Text( - follower, - style: TextStyle( - fontSize: t.textTheme.labelSmall!.fontSize, - color: outline, + if (widget.videoDetail!.staff == null) + GestureDetector( + onTap: onPushMember, + child: Container( + padding: + const EdgeInsets.symmetric(vertical: 12, horizontal: 4), + child: Row( + children: [ + NetworkImgLayer( + type: 'avatar', + src: widget.videoDetail!.owner!.face, + width: 34, + height: 34, + fadeInDuration: Duration.zero, + fadeOutDuration: Duration.zero, ), - ), - const Spacer(), - Obx(() => AnimatedOpacity( + const SizedBox(width: 10), + Text(owner.name, style: const TextStyle(fontSize: 13)), + const SizedBox(width: 6), + Text( + follower, + style: TextStyle( + fontSize: t.textTheme.labelSmall!.fontSize, + color: outline, + ), + ), + const Spacer(), + Obx( + () => AnimatedOpacity( opacity: videoIntroController.followStatus.isEmpty ? 0 : 1, duration: const Duration(milliseconds: 50), @@ -474,11 +478,58 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ), - )), - ], + ), + ), + ], + ), ), ), - ), + if (widget.videoDetail!.staff != null) ...[ + const SizedBox(height: 15), + Column( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich( + TextSpan( + style: TextStyle( + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + ), + children: [ + TextSpan( + text: '创作团队', + style: Theme.of(context) + .textTheme + .titleSmall! + .copyWith(fontWeight: FontWeight.bold), + ), + const WidgetSpan(child: SizedBox(width: 6)), + TextSpan( + text: '${widget.videoDetail!.staff!.length}人', + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + ), + ) + ], + ), + ), + SizedBox( + height: 120, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + for (int i = 0; + i < widget.videoDetail!.staff!.length; + i++) ...[ + StaffUpItem(item: widget.videoDetail!.staff![i]) + ], + ], + ), + ), + ], + ), + ] ], )), ); @@ -545,4 +596,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); }); } + + // Widget StaffPanel(BuildContext context, videoIntroController) { + // return + // } } diff --git a/lib/pages/video/detail/introduction/widgets/staff_up_item.dart b/lib/pages/video/detail/introduction/widgets/staff_up_item.dart new file mode 100644 index 00000000..7b18d95d --- /dev/null +++ b/lib/pages/video/detail/introduction/widgets/staff_up_item.dart @@ -0,0 +1,74 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/video_detail_res.dart'; +import 'package:pilipala/utils/utils.dart'; + +class StaffUpItem extends StatelessWidget { + final Staff item; + + const StaffUpItem({ + super.key, + required this.item, + }); + + @override + Widget build(BuildContext context) { + final String heroTag = Utils.makeHeroTag(item.mid); + return Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 15), + GestureDetector( + onTap: () => Get.toNamed( + '/member?mid=${item.mid}', + arguments: {'face': item.face, 'heroTag': heroTag}, + ), + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + width: 45, + height: 45, + src: item.face, + type: 'avatar', + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 4), + child: SizedBox( + width: 85, + child: Text( + item.name!, + overflow: TextOverflow.ellipsis, + softWrap: false, + textAlign: TextAlign.center, + style: TextStyle( + color: item.vip!.status == 1 + ? const Color.fromARGB(255, 251, 100, 163) + : null, + ), + ), + ), + ), + Padding( + padding: const EdgeInsets.only(top: 4), + child: SizedBox( + width: 85, + child: Text( + item.title!, + overflow: TextOverflow.ellipsis, + softWrap: false, + textAlign: TextAlign.center, + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: 12, + ), + ), + ), + ), + ], + ); + } +} From 29bdbbfe18d86fe3c5ee1755bb25902247525b89 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 15:32:05 +0800 Subject: [PATCH 142/349] mod: playCount calc --- lib/plugin/pl_player/controller.dart | 12 +++++------- lib/services/audio_handler.dart | 2 +- lib/services/audio_session.dart | 4 ++-- lib/services/shutdown_timer_service.dart | 18 +++++++++++------- 4 files changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index be72eccb..ca147fd1 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -101,7 +101,7 @@ class PlPlayerController { bool _isFirstTime = true; Timer? _timer; - late Timer? _timerForSeek; + Timer? _timerForSeek; Timer? _timerForVolume; Timer? _timerForShowingVolume; Timer? _timerForGettingVolume; @@ -335,8 +335,10 @@ class PlPlayerController { }) { // 如果实例尚未创建,则创建一个新实例 _instance ??= PlPlayerController._(); - _instance!._playerCount.value += 1; - _videoType.value = videoType; + if (videoType != 'none') { + _instance!._playerCount.value += 1; + _videoType.value = videoType; + } return _instance!; } @@ -1120,9 +1122,6 @@ class PlPlayerController { } Future dispose({String type = 'single'}) async { - print('dispose'); - print('dispose: ${playerCount.value}'); - // 每次减1,最后销毁 if (type == 'single' && playerCount.value > 1) { _playerCount.value -= 1; @@ -1132,7 +1131,6 @@ class PlPlayerController { } _playerCount.value = 0; try { - print('dispose dispose ---------'); _timer?.cancel(); _timerForVolume?.cancel(); _timerForGettingVolume?.cancel(); diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index bf98298b..b0ca8cd7 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -26,7 +26,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { static final List _item = []; Box setting = GStrorage.setting; bool enableBackgroundPlay = false; - PlPlayerController player = PlPlayerController.getInstance(); + PlPlayerController player = PlPlayerController.getInstance(videoType: 'none'); VideoPlayerServiceHandler() { revalidateSetting(); diff --git a/lib/services/audio_session.dart b/lib/services/audio_session.dart index ea83a30a..53b497ae 100644 --- a/lib/services/audio_session.dart +++ b/lib/services/audio_session.dart @@ -18,7 +18,7 @@ class AudioSessionHandler { session.configure(const AudioSessionConfiguration.music()); session.interruptionEventStream.listen((event) { - final player = PlPlayerController.getInstance(); + final player = PlPlayerController.getInstance(videoType: 'none'); if (event.begin) { if (!player.playerStatus.playing) return; switch (event.type) { @@ -51,7 +51,7 @@ class AudioSessionHandler { // 耳机拔出暂停 session.becomingNoisyEventStream.listen((_) { - final player = PlPlayerController.getInstance(); + final player = PlPlayerController.getInstance(videoType: 'none'); if (player.playerStatus.playing) { player.pause(); } diff --git a/lib/services/shutdown_timer_service.dart b/lib/services/shutdown_timer_service.dart index aa9c5ceb..156b63c8 100644 --- a/lib/services/shutdown_timer_service.dart +++ b/lib/services/shutdown_timer_service.dart @@ -29,8 +29,8 @@ class ShutdownTimerService { return; } SmartDialog.showToast("设置 $scheduledExitInMinutes 分钟后定时关闭"); - _shutdownTimer = Timer(Duration(minutes: scheduledExitInMinutes), - () => _shutdownDecider()); + _shutdownTimer = Timer( + Duration(minutes: scheduledExitInMinutes), () => _shutdownDecider()); } void _showTimeUpButPauseDialog() { @@ -59,7 +59,7 @@ class ShutdownTimerService { // Start the 10-second timer to auto close the dialog _autoCloseDialogTimer?.cancel(); _autoCloseDialogTimer = Timer(const Duration(seconds: 10), () { - SmartDialog.dismiss();// Close the dialog + SmartDialog.dismiss(); // Close the dialog _executeShutdown(); }); return AlertDialog( @@ -88,7 +88,8 @@ class ShutdownTimerService { _showShutdownDialog(); return; } - PlPlayerController plPlayerController = PlPlayerController.getInstance(); + PlPlayerController plPlayerController = + PlPlayerController.getInstance(videoType: 'none'); if (!exitApp && !waitForPlayingCompleted) { if (!plPlayerController.playerStatus.playing) { //仅提示用户 @@ -108,19 +109,22 @@ class ShutdownTimerService { //该方法依赖耦合实现,不够优雅 isWaiting = true; } - void handleWaitingFinished(){ - if(isWaiting){ + + void handleWaitingFinished() { + if (isWaiting) { _showShutdownDialog(); isWaiting = false; } } + void _executeShutdown() { if (exitApp) { //退出app exit(0); } else { //暂停播放 - PlPlayerController plPlayerController = PlPlayerController.getInstance(); + PlPlayerController plPlayerController = + PlPlayerController.getInstance(videoType: 'none'); if (plPlayerController.playerStatus.playing) { plPlayerController.pause(); waitForPlayingCompleted = true; From 0e52e0288c0d9772df5615bf0121788a201cc7b7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 16:08:10 +0800 Subject: [PATCH 143/349] =?UTF-8?q?opt:=20=E4=BC=98=E5=8C=96=E6=8A=95?= =?UTF-8?q?=E5=B8=81Dialog=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../bangumi/introduction/controller.dart | 66 ++++++++----------- lib/pages/setting/widgets/select_dialog.dart | 2 +- .../video/detail/introduction/controller.dart | 64 +++++++----------- 3 files changed, 52 insertions(+), 80 deletions(-) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index b26028cf..575a77de 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -131,51 +131,37 @@ class BangumiIntroController extends GetxController { builder: (context) { return AlertDialog( title: const Text('选择投币个数'), - contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), + contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 24), content: StatefulBuilder(builder: (context, StateSetter setState) { return Column( mainAxisSize: MainAxisSize.min, - children: [ - RadioListTile( - value: 1, - title: const Text('1枚'), - groupValue: _tempThemeValue, - onChanged: (value) { - _tempThemeValue = value!; - Get.appUpdate(); - }, - ), - RadioListTile( - value: 2, - title: const Text('2枚'), - groupValue: _tempThemeValue, - onChanged: (value) { - _tempThemeValue = value!; - Get.appUpdate(); - }, - ), - ], + children: [1, 2] + .map( + (e) => RadioListTile( + value: e, + title: Text('$e枚'), + groupValue: _tempThemeValue, + onChanged: (value) async { + _tempThemeValue = value!; + setState(() {}); + var res = await VideoHttp.coinVideo( + bvid: bvid, multiply: _tempThemeValue); + if (res['status']) { + SmartDialog.showToast('投币成功 👏'); + hasCoin.value = true; + bangumiDetail.value.stat!['coins'] = + bangumiDetail.value.stat!['coins'] + + _tempThemeValue; + } else { + SmartDialog.showToast(res['msg']); + } + Get.back(); + }, + ), + ) + .toList(), ); }), - actions: [ - TextButton(onPressed: () => Get.back(), child: const Text('取消')), - TextButton( - onPressed: () async { - var res = await VideoHttp.coinVideo( - bvid: bvid, multiply: _tempThemeValue); - if (res['status']) { - SmartDialog.showToast('投币成功 👏'); - hasCoin.value = true; - bangumiDetail.value.stat!['coins'] = - bangumiDetail.value.stat!['coins'] + _tempThemeValue; - } else { - SmartDialog.showToast(res['msg']); - } - Get.back(); - }, - child: const Text('确定'), - ) - ], ); }); } diff --git a/lib/pages/setting/widgets/select_dialog.dart b/lib/pages/setting/widgets/select_dialog.dart index 50229f9e..ab5f2ce4 100644 --- a/lib/pages/setting/widgets/select_dialog.dart +++ b/lib/pages/setting/widgets/select_dialog.dart @@ -29,7 +29,7 @@ class _SelectDialogState extends State> { return AlertDialog( title: Text(widget.title), - contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), + contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 24), content: StatefulBuilder(builder: (context, StateSetter setState) { return SingleChildScrollView( child: Column( diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 6469b5c5..8d602b83 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -219,50 +219,36 @@ class VideoIntroController extends GetxController { builder: (context) { return AlertDialog( title: const Text('选择投币个数'), - contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), + contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 24), content: StatefulBuilder(builder: (context, StateSetter setState) { return Column( mainAxisSize: MainAxisSize.min, - children: [ - RadioListTile( - value: 1, - title: const Text('1枚'), - groupValue: _tempThemeValue, - onChanged: (value) { - _tempThemeValue = value!; - Get.appUpdate(); - }, - ), - RadioListTile( - value: 2, - title: const Text('2枚'), - groupValue: _tempThemeValue, - onChanged: (value) { - _tempThemeValue = value!; - Get.appUpdate(); - }, - ), - ], + children: [1, 2] + .map( + (e) => RadioListTile( + value: e, + title: Text('$e枚'), + groupValue: _tempThemeValue, + onChanged: (value) async { + _tempThemeValue = value!; + setState(() {}); + var res = await VideoHttp.coinVideo( + bvid: bvid, multiply: _tempThemeValue); + if (res['status']) { + SmartDialog.showToast('投币成功 👏'); + hasCoin.value = true; + videoDetail.value.stat!.coin = + videoDetail.value.stat!.coin! + _tempThemeValue; + } else { + SmartDialog.showToast(res['msg']); + } + Get.back(); + }, + ), + ) + .toList(), ); }), - actions: [ - TextButton(onPressed: () => Get.back(), child: const Text('取消')), - TextButton( - onPressed: () async { - var res = await VideoHttp.coinVideo( - bvid: bvid, multiply: _tempThemeValue); - if (res['status']) { - SmartDialog.showToast('投币成功 👏'); - hasCoin.value = true; - videoDetail.value.stat!.coin = - videoDetail.value.stat!.coin! + _tempThemeValue; - } else { - SmartDialog.showToast(res['msg']); - } - Get.back(); - }, - child: const Text('确定')) - ], ); }); } From f24291134c84c864b5047ed90f1d7a628c30722b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 17:44:19 +0800 Subject: [PATCH 144/349] =?UTF-8?q?feat:=20=E5=90=88=E9=9B=86=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E5=B1=95=E7=A4=BA=E3=80=81=E5=B0=81=E9=9D=A2=E5=88=87?= =?UTF-8?q?=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/pages_bottom_sheet.dart | 44 ++++++++++++------- lib/models/model_hot_video_item.dart | 3 ++ lib/models/video_detail_res.dart | 6 +++ .../bangumi/introduction/controller.dart | 8 ++-- lib/pages/bangumi/introduction/view.dart | 4 +- lib/pages/bangumi/widgets/bangumi_panel.dart | 3 +- lib/pages/video/detail/controller.dart | 3 ++ .../video/detail/introduction/controller.dart | 12 +++-- lib/pages/video/detail/introduction/view.dart | 7 ++- .../introduction/widgets/page_panel.dart | 6 +-- .../introduction/widgets/season_panel.dart | 1 + lib/pages/video/detail/view.dart | 16 ++++--- 12 files changed, 78 insertions(+), 35 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index c64b58b6..54f14654 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import '../models/common/video_episode_type.dart'; @@ -44,25 +45,38 @@ class EpisodeBottomSheet { title = '第${episode.title}话 ${episode.longTitle!}'; break; } - return ListTile( + return InkWell( onTap: () { SmartDialog.showToast('切换至「$title」'); changeFucCall.call(episode, index); }, - dense: false, - leading: isCurrentIndex - ? Image.asset( - 'assets/images/live.gif', - color: primary, - height: 12, - ) - : null, - title: Text( - title, - style: TextStyle( - fontSize: 14, - color: isCurrentIndex ? primary : onSurface, - ), + child: Padding( + padding: const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8), + child: isFullScreen + ? Text( + title, + maxLines: 1, + style: TextStyle( + fontSize: 14, + color: isCurrentIndex ? primary : onSurface, + ), + ) + : Row( + children: [ + NetworkImgLayer(width: 130, height: 75, src: episode.cover), + const SizedBox(width: 10), + Expanded( + child: Text( + title, + maxLines: 2, + style: TextStyle( + fontSize: 14, + color: isCurrentIndex ? primary : onSurface, + ), + ), + ), + ], + ), ), ); } diff --git a/lib/models/model_hot_video_item.dart b/lib/models/model_hot_video_item.dart index db331a4c..ab7e6e04 100644 --- a/lib/models/model_hot_video_item.dart +++ b/lib/models/model_hot_video_item.dart @@ -23,6 +23,7 @@ class HotVideoItemModel { this.dimension, this.shortLinkV2, this.firstFrame, + this.cover, this.pubLocation, this.seasontype, this.isOgv, @@ -50,6 +51,7 @@ class HotVideoItemModel { Dimension? dimension; String? shortLinkV2; String? firstFrame; + String? cover; String? pubLocation; int? seasontype; bool? isOgv; @@ -77,6 +79,7 @@ class HotVideoItemModel { dimension = Dimension.fromMap(json['dimension']); shortLinkV2 = json["short_link_v2"]; firstFrame = json["first_frame"]; + cover = json["first_frame"]; pubLocation = json["pub_location"]; seasontype = json["seasontype"]; isOgv = json["isOgv"]; diff --git a/lib/models/video_detail_res.dart b/lib/models/video_detail_res.dart index a82b6fbb..b7b1b481 100644 --- a/lib/models/video_detail_res.dart +++ b/lib/models/video_detail_res.dart @@ -382,6 +382,7 @@ class Part { String? weblink; Dimension? dimension; String? firstFrame; + String? cover; Part({ this.cid, @@ -393,6 +394,7 @@ class Part { this.weblink, this.dimension, this.firstFrame, + this.cover, }); fromRawJson(String str) => Part.fromJson(json.decode(str)); @@ -411,6 +413,7 @@ class Part { ? null : Dimension.fromJson(json["dimension"]); firstFrame = json["first_frame"]; + cover = json["first_frame"]; } Map toJson() { @@ -634,6 +637,7 @@ class EpisodeItem { this.attribute, this.page, this.bvid, + this.cover, }); int? seasonId; int? sectionId; @@ -644,6 +648,7 @@ class EpisodeItem { int? attribute; Part? page; String? bvid; + String? cover; EpisodeItem.fromJson(Map json) { seasonId = json['season_id']; @@ -655,6 +660,7 @@ class EpisodeItem { attribute = json['attribute']; page = Part.fromJson(json['page']); bvid = json['bvid']; + cover = json['arc']['pic']; } } diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 575a77de..65cd5dd8 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -215,7 +215,7 @@ class BangumiIntroController extends GetxController { } // 修改分P或番剧分集 - Future changeSeasonOrbangu(bvid, cid, aid) async { + Future changeSeasonOrbangu(bvid, cid, aid, cover) async { // 重新获取视频资源 VideoDetailController videoDetailCtr = Get.find(tag: Get.arguments['heroTag']); @@ -223,6 +223,7 @@ class BangumiIntroController extends GetxController { videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.oid.value = aid; + videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); // 重新请求评论 try { @@ -281,7 +282,8 @@ class BangumiIntroController extends GetxController { int cid = episodes[nextIndex].cid!; String bvid = episodes[nextIndex].bvid!; int aid = episodes[nextIndex].aid!; - changeSeasonOrbangu(bvid, cid, aid); + String cover = episodes[nextIndex].cover!; + changeSeasonOrbangu(bvid, cid, aid, cover); } // 播放器底栏 选集 回调 @@ -302,7 +304,7 @@ class BangumiIntroController extends GetxController { sheetHeight: Get.size.height, isFullScreen: true, changeFucCall: (item, index) { - changeSeasonOrbangu(item.bvid, item.cid, item.aid); + changeSeasonOrbangu(item.bvid, item.cid, item.aid, item.cover); SmartDialog.dismiss(); }, ).buildShowContent(Get.context!), diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index e47db480..6876da79 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -322,8 +322,8 @@ class _BangumiInfoState extends State { pages: widget.bangumiDetail!.episodes!, cid: cid! ?? widget.bangumiDetail!.episodes!.first.cid!, sheetHeight: sheetHeight, - changeFuc: (bvid, cid, aid) => - bangumiIntroController.changeSeasonOrbangu(bvid, cid, aid), + changeFuc: (bvid, cid, aid, cover) => bangumiIntroController + .changeSeasonOrbangu(bvid, cid, aid, cover), bangumiDetail: bangumiIntroController.bangumiDetail.value, bangumiIntroController: bangumiIntroController, ) diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 3e965f34..3df7ce25 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -84,11 +84,12 @@ class _BangumiPanelState extends State { item.bvid, item.cid, item.aid, + item.cover, ); if (_bottomSheetController != null) { _bottomSheetController?.close(); } - currentIndex = i; + currentIndex.value = i; scrollToIndex(); } diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 3cc43005..5fecde8d 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -73,6 +73,7 @@ class VideoDetailController extends GetxController ReplyItemModel? firstFloor; final scaffoldKey = GlobalKey(); RxString bgCover = ''.obs; + RxString cover = ''.obs; PlPlayerController plPlayerController = PlPlayerController.getInstance(); late VideoItem firstVideo; @@ -120,10 +121,12 @@ class VideoDetailController extends GetxController var args = argMap['videoItem']; if (args.pic != null && args.pic != '') { videoItem['pic'] = args.pic; + cover.value = args.pic; } } if (keys.contains('pic')) { videoItem['pic'] = argMap['pic']; + cover.value = argMap['pic']; } } tabCtr = TabController(length: 2, vsync: this); diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 8d602b83..b9a3ef93 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -432,7 +432,7 @@ class VideoIntroController extends GetxController { } // 修改分P或番剧分集 - Future changeSeasonOrbangu(bvid, cid, aid) async { + Future changeSeasonOrbangu(bvid, cid, aid, cover) async { // 重新获取视频资源 final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); @@ -447,6 +447,7 @@ class VideoIntroController extends GetxController { videoDetailCtr.oid.value = aid ?? IdUtils.bv2av(bvid); videoDetailCtr.cid.value = cid; videoDetailCtr.danmakuCid.value = cid; + videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); // 重新请求评论 try { @@ -494,6 +495,7 @@ class VideoIntroController extends GetxController { void nextPlay() { final List episodes = []; bool isPages = false; + late String cover; if (videoDetail.value.ugcSeason != null) { final UgcSeason ugcSeason = videoDetail.value.ugcSeason!; final List sections = ugcSeason.sections!; @@ -510,6 +512,7 @@ class VideoIntroController extends GetxController { final int currentIndex = episodes.indexWhere((e) => e.cid == lastPlayCid.value); int nextIndex = currentIndex + 1; + cover = episodes[nextIndex].cover; final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); final PlayRepeat platRepeat = videoDetailCtr.plPlayerController.playRepeat; @@ -526,7 +529,7 @@ class VideoIntroController extends GetxController { final int cid = episodes[nextIndex].cid!; final String rBvid = isPages ? bvid : episodes[nextIndex].bvid; final int rAid = isPages ? IdUtils.bv2av(bvid) : episodes[nextIndex].aid!; - changeSeasonOrbangu(rBvid, cid, rAid); + changeSeasonOrbangu(rBvid, cid, rAid, cover); } // 设置关注分组 @@ -591,10 +594,11 @@ class VideoIntroController extends GetxController { isFullScreen: true, changeFucCall: (item, index) { if (dataType == VideoEpidoesType.videoEpisode) { - changeSeasonOrbangu(IdUtils.av2bv(item.aid), item.cid, item.aid); + changeSeasonOrbangu( + IdUtils.av2bv(item.aid), item.cid, item.aid, item.cover); } if (dataType == VideoEpidoesType.videoPart) { - changeSeasonOrbangu(bvid, item.cid, null); + changeSeasonOrbangu(bvid, item.cid, null, item.cover); } SmartDialog.dismiss(); }, diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 597b6def..47404658 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -383,11 +383,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ? videoIntroController.lastPlayCid.value : widget.videoDetail!.pages!.first.cid, sheetHeight: videoDetailCtr.sheetHeight.value, - changeFuc: (bvid, cid, aid) => + changeFuc: (bvid, cid, aid, cover) => videoIntroController.changeSeasonOrbangu( bvid, cid, aid, + cover, ), videoIntroCtr: videoIntroController, ), @@ -401,10 +402,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { pages: widget.videoDetail!.pages!, cid: videoIntroController.lastPlayCid.value, sheetHeight: videoDetailCtr.sheetHeight.value, - changeFuc: (cid) => videoIntroController.changeSeasonOrbangu( + changeFuc: (cid, cover) => + videoIntroController.changeSeasonOrbangu( videoIntroController.bvid, cid, null, + cover, ), videoIntroCtr: videoIntroController, ), diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index 83db2d52..266f5566 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -58,7 +58,7 @@ class _PagesPanelState extends State { } void changeFucCall(item, i) async { - widget.changeFuc?.call(item.cid); + widget.changeFuc?.call(item.cid, item.cover); currentIndex.value = i; _bottomSheetController?.close(); scrollToIndex(); @@ -129,7 +129,7 @@ class _PagesPanelState extends State { ), ), Container( - height: 35, + height: 55, margin: const EdgeInsets.only(bottom: 8), child: ListView.builder( scrollDirection: Axis.horizontal, @@ -163,7 +163,7 @@ class _PagesPanelState extends State { Expanded( child: Text( widget.pages[i].pagePart!, - maxLines: 1, + maxLines: 2, style: TextStyle( fontSize: 13, color: isCurrentIndex diff --git a/lib/pages/video/detail/introduction/widgets/season_panel.dart b/lib/pages/video/detail/introduction/widgets/season_panel.dart index 2afefcb4..fdfec6f9 100644 --- a/lib/pages/video/detail/introduction/widgets/season_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/season_panel.dart @@ -67,6 +67,7 @@ class _SeasonPanelState extends State { IdUtils.av2bv(item.aid), item.cid, item.aid, + item.cover, ); currentIndex.value = i; _bottomSheetController?.close(); diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 0152a2cb..7f1d6039 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -458,11 +458,17 @@ class _VideoDetailPageState extends State onTap: () { handlePlay(); }, - child: NetworkImgLayer( - type: 'emote', - src: vdCtr.videoItem['pic'], - width: Get.width, - height: videoHeight.value, + child: Obx( + () => AnimatedOpacity( + duration: const Duration(milliseconds: 100), // 渐变动画的持续时间 + opacity: 1, // 设置不透明度 + child: NetworkImgLayer( + type: 'emote', + src: vdCtr.cover.value, + width: Get.width, + height: videoHeight.value, + ), + ), ), ), Positioned( From 257596cb9eb025ccc674c413c575e59fe4c0cf7e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 20:40:23 +0800 Subject: [PATCH 145/349] =?UTF-8?q?mod:=20=E9=BB=98=E8=AE=A4=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E7=A1=AC=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/setting/play_setting.dart | 2 +- lib/pages/video/detail/controller.dart | 4 ++-- lib/plugin/pl_player/controller.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 07d736e3..4a8495e5 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -131,7 +131,7 @@ class _PlaySettingState extends State { title: '开启硬解', subTitle: '以较低功耗播放视频', setKey: SettingBoxKey.enableHA, - defaultVal: true, + defaultVal: false, ), const SetSwitchItem( title: '观看人数', diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 4613c7ca..127e81a7 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -59,7 +59,7 @@ class VideoDetailController extends GetxController // 封面图的展示 RxBool isShowCover = true.obs; // 硬解 - RxBool enableHA = true.obs; + RxBool enableHA = false.obs; /// 本地存储 Box userInfoCache = GStrorage.userInfo; @@ -128,7 +128,7 @@ class VideoDetailController extends GetxController tabCtr = TabController(length: 2, vsync: this); autoPlay.value = setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true); - enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: true); + enableHA.value = setting.get(SettingBoxKey.enableHA, defaultValue: false); enableRelatedVideo = setting.get(SettingBoxKey.enableRelatedVideo, defaultValue: true); if (userInfo == null || diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index be72eccb..a31ce0b8 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -351,7 +351,7 @@ class PlPlayerController { // 初始化播放速度 double speed = 1.0, // 硬件加速 - bool enableHA = true, + bool enableHA = false, double? width, double? height, Duration? duration, From cbb7b90bf9ce8678fc2a9f585a5b4f8a3a21a016 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 22:11:56 +0800 Subject: [PATCH 146/349] mod --- lib/common/widgets/network_img_layer.dart | 1 - lib/pages/video/detail/widgets/header_control.dart | 2 +- lib/utils/utils.dart | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index 173db853..bda8ee5c 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -36,7 +36,6 @@ class NetworkImgLayer extends StatelessWidget { final int defaultImgQuality = GlobalData().imgQuality; final String imageUrl = '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp'; - // print(imageUrl); int? memCacheWidth, memCacheHeight; double aspectRatio = (width / height).toDouble(); diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 6acd20d7..544d93e0 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -446,7 +446,7 @@ class _HeaderControlState extends State { children: [ RadioListTile( value: -1, - title: const Text('关闭弹幕'), + title: const Text('关闭字幕'), groupValue: tempThemeValue, onChanged: (value) { tempThemeValue = value!; diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index 987f57c1..b40e9bfe 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -109,7 +109,6 @@ class Utils { toInt: false, formatType: formatType); } - print('distance: $distance'); if (distance <= 60) { return '刚刚'; } else if (distance <= 3600) { From aff39c291dc669158277301541cb98c9ed8ec0b0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Apr 2024 22:17:10 +0800 Subject: [PATCH 147/349] =?UTF-8?q?fix:=20=E5=90=88=E9=9B=86cover=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/pages_bottom_sheet.dart | 46 +++++++++++++++++++----------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index 54f14654..00233a07 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -45,25 +45,37 @@ class EpisodeBottomSheet { title = '第${episode.title}话 ${episode.longTitle!}'; break; } - return InkWell( - onTap: () { - SmartDialog.showToast('切换至「$title」'); - changeFucCall.call(episode, index); - }, - child: Padding( - padding: const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8), - child: isFullScreen - ? Text( - title, - maxLines: 1, + return isFullScreen || episode?.cover == null + ? ListTile( + onTap: () { + SmartDialog.showToast('切换至「$title」'); + changeFucCall.call(episode, index); + }, + dense: false, + leading: isCurrentIndex + ? Image.asset( + 'assets/images/live.gif', + color: primary, + height: 12, + ) + : null, + title: Text(title, style: TextStyle( fontSize: 14, color: isCurrentIndex ? primary : onSurface, - ), - ) - : Row( + ))) + : InkWell( + onTap: () { + SmartDialog.showToast('切换至「$title」'); + changeFucCall.call(episode, index); + }, + child: Padding( + padding: + const EdgeInsets.only(left: 14, right: 14, top: 8, bottom: 8), + child: Row( children: [ - NetworkImgLayer(width: 130, height: 75, src: episode.cover), + NetworkImgLayer( + width: 130, height: 75, src: episode?.cover ?? ''), const SizedBox(width: 10), Expanded( child: Text( @@ -77,8 +89,8 @@ class EpisodeBottomSheet { ), ], ), - ), - ); + ), + ); } Widget buildTitle() { From c653a60f05501f983bb0d98c529fb240409bba6e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 00:06:08 +0800 Subject: [PATCH 148/349] =?UTF-8?q?feat:=20=E6=B4=BB=E5=8A=A8=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E5=8A=A8=E6=80=81=E6=B8=B2=E6=9F=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/dynamics/result.dart | 3 + lib/pages/dynamics/widgets/forward_panel.dart | 56 +++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index 2f7c2d40..bc7105d1 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -414,6 +414,7 @@ class DynamicMajorModel { this.none, this.type, this.courses, + this.common, }); DynamicArchiveModel? archive; @@ -429,6 +430,7 @@ class DynamicMajorModel { // MAJOR_TYPE_OPUS 图文/文章 String? type; Map? courses; + Map? common; DynamicMajorModel.fromJson(Map json) { archive = json['archive'] != null @@ -452,6 +454,7 @@ class DynamicMajorModel { json['none'] != null ? DynamicNoneModel.fromJson(json['none']) : null; type = json['type']; courses = json['courses'] ?? {}; + common = json['common'] ?? {}; } } diff --git a/lib/pages/dynamics/widgets/forward_panel.dart b/lib/pages/dynamics/widgets/forward_panel.dart index 5a8d9f17..76c7b24e 100644 --- a/lib/pages/dynamics/widgets/forward_panel.dart +++ b/lib/pages/dynamics/widgets/forward_panel.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; import 'additional_panel.dart'; @@ -182,6 +183,61 @@ Widget forWard(item, context, ctr, source, {floor = 1}) { ) ], ); + // 活动 + case 'DYNAMIC_TYPE_COMMON_SQUARE': + return Padding( + padding: const EdgeInsets.only(top: 8), + child: InkWell( + onTap: () { + Get.toNamed('/webview', parameters: { + 'url': item.modules.moduleDynamic.major.common['jump_url'], + 'type': 'url', + 'pageTitle': item.modules.moduleDynamic.major.common['title'] + }); + }, + child: Container( + width: double.infinity, + padding: + const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10), + color: Theme.of(context).dividerColor.withOpacity(0.08), + child: Row( + children: [ + NetworkImgLayer( + width: 45, + height: 45, + src: item.modules.moduleDynamic.major.common['cover'], + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.modules.moduleDynamic.major.common['title'], + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + Text( + item.modules.moduleDynamic.major.common['desc'], + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ) + ], + ), + // TextButton(onPressed: () {}, child: Text('123')) + ), + ), + ); default: return const SizedBox( width: double.infinity, From 750e9d2b58635d70c0627d64adaf1ea5dd7621db Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 07:39:55 +0800 Subject: [PATCH 149/349] =?UTF-8?q?revert:=20=E8=AF=84=E8=AE=BA=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E4=BC=98=E5=85=88=E5=B1=95=E7=A4=BA=E4=BA=8C=E7=BA=A7?= =?UTF-8?q?=E8=AF=84=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 6 +++--- lib/pages/video/detail/controller.dart | 3 +-- lib/pages/video/detail/reply/view.dart | 8 ++++---- .../video/detail/reply/widgets/reply_item.dart | 6 ++---- lib/pages/video/detail/reply_reply/controller.dart | 10 +--------- lib/pages/video/detail/reply_reply/view.dart | 14 ++------------ 6 files changed, 13 insertions(+), 34 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 11636b5b..bd07921b 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -106,7 +106,7 @@ class _DynamicDetailPageState extends State } // 查看二级评论 - void replyReply(replyItem, currentReply) { + void replyReply(replyItem) { int oid = replyItem.oid; int rpid = replyItem.rpid!; Get.to( @@ -324,8 +324,8 @@ class _DynamicDetailPageState extends State replyItem: replyList[index], showReplyRow: true, replyLevel: '1', - replyReply: (replyItem, currentReply) => - replyReply(replyItem, currentReply), + replyReply: (replyItem) => + replyReply(replyItem), replyType: ReplyType.values[replyType], addReply: (replyItem) { replyList[index] diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index c6c4bef6..8efc8ea1 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -165,7 +165,7 @@ class VideoDetailController extends GetxController getSubtitle(); } - showReplyReplyPanel(oid, fRpid, firstFloor, currentReply) { + showReplyReplyPanel(oid, fRpid, firstFloor) { replyReplyBottomSheetCtr = scaffoldKey.currentState?.showBottomSheet((BuildContext context) { return VideoReplyReplyPanel( @@ -178,7 +178,6 @@ class VideoDetailController extends GetxController replyType: ReplyType.video, source: 'videoDetail', sheetHeight: sheetHeight.value, - currentReply: currentReply, ); }); replyReplyBottomSheetCtr?.closed.then((value) { diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 420f349c..4c533647 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -110,7 +110,7 @@ class _VideoReplyPanelState extends State } // 展示二级回复 - void replyReply(replyItem, currentReply) { + void replyReply(replyItem) { final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); if (replyItem != null) { @@ -118,7 +118,7 @@ class _VideoReplyPanelState extends State videoDetailCtr.fRpid = replyItem.rpid!; videoDetailCtr.firstFloor = replyItem; videoDetailCtr.showReplyReplyPanel( - replyItem.oid, replyItem.rpid!, replyItem, currentReply); + replyItem.oid, replyItem.rpid!, replyItem); } } @@ -229,8 +229,8 @@ class _VideoReplyPanelState extends State .replyList[index], showReplyRow: true, replyLevel: replyLevel, - replyReply: (replyItem, currentReply) => - replyReply(replyItem, currentReply), + replyReply: (replyItem) => + replyReply(replyItem), replyType: ReplyType.video, ); } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 8a8e4af7..ecc2dd02 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -531,10 +531,8 @@ InlineSpan buildContent( TextSpan( text: str, recognizer: TapGestureRecognizer() - ..onTap = () => replyReply?.call( - replyItem.root == 0 ? replyItem : fReplyItem, - replyItem, - ), + ..onTap = () => + replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem), ), ); } diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index 4c553e29..e94aaea5 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -26,7 +26,7 @@ class VideoReplyReplyController extends GetxController { currentPage = 0; } - Future queryReplyList({type = 'init', currentReply}) async { + Future queryReplyList({type = 'init'}) async { if (type == 'init') { currentPage = 0; } @@ -63,14 +63,6 @@ class VideoReplyReplyController extends GetxController { // res['data'].replies.addAll(replyList); } } - if (replyList.isNotEmpty && currentReply != null) { - int indexToRemove = - replyList.indexWhere((item) => currentReply.rpid == item.rpid); - // 如果找到了指定ID的项,则移除 - if (indexToRemove != -1) { - replyList.removeAt(indexToRemove); - } - } isLoadingMore = false; return res; } diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 6441c600..3fe84c71 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -20,7 +20,6 @@ class VideoReplyReplyPanel extends StatefulWidget { this.source, this.replyType, this.sheetHeight, - this.currentReply, super.key, }); final int? oid; @@ -30,7 +29,6 @@ class VideoReplyReplyPanel extends StatefulWidget { final String? source; final ReplyType? replyType; final double? sheetHeight; - final dynamic currentReply; @override State createState() => _VideoReplyReplyPanelState(); @@ -65,9 +63,7 @@ class _VideoReplyReplyPanelState extends State { }, ); - _futureBuilderFuture = _videoReplyReplyController.queryReplyList( - currentReply: widget.currentReply, - ); + _futureBuilderFuture = _videoReplyReplyController.queryReplyList(); } void replyReply(replyItem) {} @@ -111,9 +107,7 @@ class _VideoReplyReplyPanelState extends State { onRefresh: () async { setState(() {}); _videoReplyReplyController.currentPage = 0; - return await _videoReplyReplyController.queryReplyList( - currentReply: widget.currentReply, - ); + return await _videoReplyReplyController.queryReplyList(); }, child: CustomScrollView( controller: _videoReplyReplyController.scrollController, @@ -146,10 +140,6 @@ class _VideoReplyReplyPanelState extends State { if (snapshot.connectionState == ConnectionState.done) { Map? data = snapshot.data; if (data != null && data['status']) { - if (widget.currentReply != null) { - _videoReplyReplyController.replyList - .insert(0, widget.currentReply); - } // 请求成功 return Obx( () => SliverList( From 8a6b4f987f4aeabe91c0d2d98adc898be044521f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 22:03:19 +0800 Subject: [PATCH 150/349] revert: NavigationBar Color --- lib/main.dart | 59 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 3877685c..43bcf930 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -65,6 +65,20 @@ void main() async { }, ); + // 小白条、导航栏沉浸 + if (Platform.isAndroid) { + List versionParts = Platform.version.split('.'); + int androidVersion = int.parse(versionParts[0]); + if (androidVersion >= 29) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + } + SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarDividerColor: Colors.transparent, + statusBarColor: Colors.transparent, + )); + } + Data.init(); GlobalData(); PiliSchame.init(); @@ -130,26 +144,33 @@ class MyApp extends StatelessWidget { ); } - ThemeData themeData = ThemeData( - colorScheme: currentThemeValue == ThemeType.dark - ? darkColorScheme - : lightColorScheme, - ); + // ThemeData themeData = ThemeData( + // colorScheme: currentThemeValue == ThemeType.dark + // ? darkColorScheme + // : lightColorScheme, + // ); - // 小白条、导航栏沉浸 - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - systemNavigationBarColor: GlobalData().enableMYBar - ? const Color(0x00010000) - : themeData.canvasColor, - systemNavigationBarDividerColor: GlobalData().enableMYBar - ? const Color(0x00010000) - : themeData.canvasColor, - systemNavigationBarIconBrightness: currentThemeValue == ThemeType.dark - ? Brightness.light - : Brightness.dark, - statusBarColor: Colors.transparent, - )); + // // 小白条、导航栏沉浸 + // if (Platform.isAndroid) { + // List versionParts = Platform.version.split('.'); + // int androidVersion = int.parse(versionParts[0]); + // if (androidVersion >= 29) { + // SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); + // } + // SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( + // systemNavigationBarColor: GlobalData().enableMYBar + // ? const Color(0x00010000) + // : themeData.canvasColor, + // systemNavigationBarDividerColor: GlobalData().enableMYBar + // ? const Color(0x00010000) + // : themeData.canvasColor, + // systemNavigationBarIconBrightness: + // currentThemeValue == ThemeType.dark + // ? Brightness.light + // : Brightness.dark, + // statusBarColor: Colors.transparent, + // )); + // } // 图片缓存 // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; From bc4fb0e0ae40cb9a77ebe18893f8a4cbd34071c5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 22:27:14 +0800 Subject: [PATCH 151/349] fix: videoDetail cover null --- lib/pages/video/detail/controller.dart | 27 ++++++++++--------- .../video/detail/introduction/controller.dart | 1 + lib/utils/app_scheme.dart | 2 +- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index c6c4bef6..bb099255 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -115,20 +115,15 @@ class VideoDetailController extends GetxController super.onInit(); final Map argMap = Get.arguments; userInfo = userInfoCache.get('userInfoCache'); - var keys = argMap.keys.toList(); - if (keys.isNotEmpty) { - if (keys.contains('videoItem')) { - var args = argMap['videoItem']; - if (args.pic != null && args.pic != '') { - videoItem['pic'] = args.pic; - cover.value = args.pic; - } - } - if (keys.contains('pic')) { - videoItem['pic'] = argMap['pic']; - cover.value = argMap['pic']; - } + if (argMap.containsKey('videoItem')) { + var args = argMap['videoItem']; + updateCover(args.pic); } + + if (argMap.containsKey('pic')) { + updateCover(argMap['pic']); + } + tabCtr = TabController(length: 2, vsync: this); autoPlay.value = setting.get(SettingBoxKey.autoPlayEnable, defaultValue: true); @@ -551,4 +546,10 @@ class VideoDetailController extends GetxController }, ); } + + void updateCover(String? pic) { + if (pic != null && pic != '') { + cover.value = videoItem['pic'] = pic; + } + } } diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index b9a3ef93..2ced79f0 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -90,6 +90,7 @@ class VideoIntroController extends GetxController { final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); videoDetailCtr.tabs.value = ['简介', '评论 ${result['data']?.stat?.reply}']; + videoDetailCtr.cover.value = result['data'].pic ?? ''; // 获取到粉丝数再返回 await queryUserStat(); } diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 9009df6f..69fa717d 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -117,7 +117,7 @@ class PiliSchame { // ignore: always_specify_types (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', arguments: { - 'pic': null, + 'pic': '', 'heroTag': heroTag, }), ); From 811487bdd42f696b7ee56c4dd8c43fed2c787217 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 28 Apr 2024 23:03:33 +0800 Subject: [PATCH 152/349] =?UTF-8?q?fix:=20=E9=87=8D=E5=9B=9E=E5=89=8D?= =?UTF-8?q?=E5=8F=B0=E5=BC=B9=E5=B9=95=E5=A0=86=E7=A7=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 7f1d6039..199e6b88 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -38,7 +38,7 @@ class VideoDetailPage extends StatefulWidget { } class _VideoDetailPageState extends State - with TickerProviderStateMixin, RouteAware { + with TickerProviderStateMixin, RouteAware, WidgetsBindingObserver { late VideoDetailController vdCtr; PlPlayerController? plPlayerController; final ScrollController _extendNestCtr = ScrollController(); @@ -61,6 +61,8 @@ class _VideoDetailPageState extends State late bool autoPiP; late Floating floating; bool isShowing = true; + // 生命周期监听 + late final AppLifecycleListener _lifecycleListener; @override void initState() { @@ -96,6 +98,8 @@ class _VideoDetailPageState extends State floating = vdCtr.floating!; autoEnterPip(); } + WidgetsBinding.instance.addObserver(this); + lifecycleListener(); } // 获取视频资源,初始化播放器 @@ -219,6 +223,8 @@ class _VideoDetailPageState extends State floating.dispose(); } appbarStream.close(); + WidgetsBinding.instance.removeObserver(this); + _lifecycleListener.dispose(); super.dispose(); } @@ -281,6 +287,29 @@ class _VideoDetailPageState extends State } } + // 生命周期监听 + 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'), + ); + } + + void _handleTransition(String name) { + switch (name) { + case 'show' || 'restart': + plPlayerController?.danmakuController?.clear(); + break; + } + } + @override Widget build(BuildContext context) { final sizeContext = MediaQuery.sizeOf(context); From 0bb26aa46757eec6b3e5855e2e90e5f0fe4d167b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 29 Apr 2024 13:16:25 +0800 Subject: [PATCH 153/349] fix: videoDetail cover null --- lib/common/pages_bottom_sheet.dart | 2 +- lib/models/video_detail_res.dart | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index 00233a07..2a56eb41 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -45,7 +45,7 @@ class EpisodeBottomSheet { title = '第${episode.title}话 ${episode.longTitle!}'; break; } - return isFullScreen || episode?.cover == null + return isFullScreen || episode?.cover == null || episode?.cover == '' ? ListTile( onTap: () { SmartDialog.showToast('切换至「$title」'); diff --git a/lib/models/video_detail_res.dart b/lib/models/video_detail_res.dart index b7b1b481..ae272375 100644 --- a/lib/models/video_detail_res.dart +++ b/lib/models/video_detail_res.dart @@ -412,8 +412,8 @@ class Part { dimension = json["dimension"] == null ? null : Dimension.fromJson(json["dimension"]); - firstFrame = json["first_frame"]; - cover = json["first_frame"]; + firstFrame = json["first_frame"] ?? ''; + cover = json["first_frame"] ?? ''; } Map toJson() { From 3c8a92209fa5b1725df883b9b9cc62a369708e76 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 29 Apr 2024 13:41:25 +0800 Subject: [PATCH 154/349] =?UTF-8?q?revert:=20=E8=AF=84=E8=AE=BA=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E4=BC=98=E5=85=88=E5=B1=95=E7=A4=BA=E4=BA=8C=E7=BA=A7?= =?UTF-8?q?=E8=AF=84=E8=AE=BA?= 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 ecc2dd02..468d2541 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -427,7 +427,7 @@ class ReplyItemRow extends StatelessWidget { if (extraRow == 1) InkWell( // 一楼点击【共xx条回复】展开评论详情 - onTap: () => replyReply!(replyItem, null), + onTap: () => replyReply!(replyItem), child: Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(8, 5, 8, 8), From bab0985e86f147087d8f0793ad5e9da08f67d0df Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Apr 2024 15:49:00 +0800 Subject: [PATCH 155/349] =?UTF-8?q?mod:=20=E5=85=B3=E6=B3=A8=E6=8C=89?= =?UTF-8?q?=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 74 ++++++++----------- 1 file changed, 32 insertions(+), 42 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index a7eae6d2..8a92e9be 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -134,7 +134,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { late double sheetHeight; late final dynamic owner; late final dynamic follower; - late final dynamic followStatus; late int mid; late String memberHeroTag; late bool enableAi; @@ -163,7 +162,6 @@ class _VideoInfoState extends State with TickerProviderStateMixin { owner = widget.videoDetail!.owner; follower = Utils.numFormat(videoIntroController.userStat['follower']); - followStatus = videoIntroController.followStatus; enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); _expandableCtr = ExpandableController(initialExpanded: false); } @@ -434,47 +432,39 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), const Spacer(), - Obx(() => AnimatedOpacity( - opacity: - videoIntroController.followStatus.isEmpty ? 0 : 1, - duration: const Duration(milliseconds: 50), - child: SizedBox( - height: 32, - child: Obx( - () => videoIntroController.followStatus.isNotEmpty - ? TextButton( - onPressed: - videoIntroController.actionRelationMod, - style: TextButton.styleFrom( - padding: const EdgeInsets.only( - left: 8, right: 8), - foregroundColor: - followStatus['attribute'] != 0 - ? outline - : t.colorScheme.onPrimary, - backgroundColor: - followStatus['attribute'] != 0 - ? t.colorScheme.onInverseSurface - : t.colorScheme - .primary, // 设置按钮背景色 - ), - child: Text( - followStatus['attribute'] != 0 - ? '已关注' - : '关注', - style: TextStyle( - fontSize: t - .textTheme.labelMedium!.fontSize), - ), - ) - : ElevatedButton( - onPressed: - videoIntroController.actionRelationMod, - child: const Text('关注'), + Obx( + () { + final bool isFollowed = + videoIntroController.followStatus['attribute'] != 0; + return videoIntroController.followStatus.isEmpty + ? const SizedBox() + : SizedBox( + height: 32, + child: TextButton( + onPressed: + videoIntroController.actionRelationMod, + style: TextButton.styleFrom( + padding: const EdgeInsets.only( + left: 8, + right: 8, ), - ), - ), - )), + foregroundColor: isFollowed + ? outline + : t.colorScheme.onPrimary, + backgroundColor: isFollowed + ? t.colorScheme.onInverseSurface + : t.colorScheme.primary, // 设置按钮背景色 + ), + child: Text( + isFollowed ? '已关注' : '关注', + style: TextStyle( + fontSize: t.textTheme.labelMedium!.fontSize, + ), + ), + ), + ); + }, + ) ], ), ), From ba1a5486bfd259651b230f79e9d6963caf0d2f06 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Apr 2024 22:40:22 +0800 Subject: [PATCH 156/349] =?UTF-8?q?opt:=20=E5=88=9D=E5=A7=8B=E5=8C=96?= =?UTF-8?q?=E6=97=B6=E8=AE=B0=E5=BF=86=E6=92=AD=E6=94=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 14 ++++++++------ pubspec.lock | 27 +++++++++++++++------------ pubspec.yaml | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 18 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 8ee6faa0..4b6c44a1 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -395,7 +395,7 @@ class PlPlayerController { } // 配置Player 音轨、字幕等等 _videoPlayerController = await _createVideoController( - dataSource, _looping, enableHA, width, height); + dataSource, _looping, enableHA, width, height, seekTo); // 获取视频时长 00:00 _duration.value = duration ?? _videoPlayerController!.state.duration; updateDurationSecond(); @@ -426,6 +426,7 @@ class PlPlayerController { bool enableHA, double? width, double? height, + Duration seekTo, ) async { // 每次配置时先移除监听 removeListeners(); @@ -507,8 +508,9 @@ class PlPlayerController { play: false, ); } - player.open( - Media(dataSource.videoSource!, httpHeaders: dataSource.httpHeaders), + await player.open( + Media(dataSource.videoSource!, + httpHeaders: dataSource.httpHeaders, start: seekTo), play: false, ); // 音轨 @@ -530,9 +532,9 @@ class PlPlayerController { // } /// 跳转播放 - if (seekTo != Duration.zero) { - await this.seekTo(seekTo); - } + // if (seekTo != Duration.zero) { + // await this.seekTo(seekTo); + // } /// 自动播放 if (_autoPlay) { diff --git a/pubspec.lock b/pubspec.lock index a64c85c0..46d1e646 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -873,10 +873,11 @@ packages: media_kit: dependency: "direct main" description: - name: media_kit - sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a" - url: "https://pub.flutter-io.cn" - source: hosted + path: media_kit + ref: HEAD + resolved-ref: "7775f8b1aa5ec77815d5739bf25549fe37f17cae" + url: "https://github.com/media-kit/media-kit" + source: git version: "1.1.10+1" media_kit_libs_android_video: dependency: transitive @@ -913,10 +914,11 @@ packages: media_kit_libs_video: dependency: "direct main" description: - name: media_kit_libs_video - sha256: "3688e0c31482074578652bf038ce6301a5d21e1eda6b54fc3117ffeb4bdba067" - url: "https://pub.flutter-io.cn" - source: hosted + path: "libs/universal/media_kit_libs_video" + ref: HEAD + resolved-ref: "7775f8b1aa5ec77815d5739bf25549fe37f17cae" + url: "https://github.com/media-kit/media-kit" + source: git version: "1.0.4" media_kit_libs_windows_video: dependency: transitive @@ -937,10 +939,11 @@ packages: media_kit_video: dependency: "direct main" description: - name: media_kit_video - sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882 - url: "https://pub.flutter-io.cn" - source: hosted + path: media_kit_video + ref: HEAD + resolved-ref: "7775f8b1aa5ec77815d5739bf25549fe37f17cae" + url: "https://github.com/media-kit/media-kit" + source: git version: "1.2.4" meta: dependency: transitive diff --git a/pubspec.yaml b/pubspec.yaml index 27b8b720..5ea6386e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -165,6 +165,20 @@ dev_dependencies: hive_generator: ^2.0.0 build_runner: ^2.4.8 +dependency_overrides: + media_kit: + git: + url: https://github.com/media-kit/media-kit + path: media_kit + media_kit_video: + git: + url: https://github.com/media-kit/media-kit + path: media_kit_video + media_kit_libs_video: + git: + url: https://github.com/media-kit/media-kit + path: libs/universal/media_kit_libs_video + flutter_launcher_icons: android: true ios: true From 68c73fde5655dfdac36ad311ea08955462dfc3a8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Apr 2024 22:47:02 +0800 Subject: [PATCH 157/349] fix: videoDetail info copy --- lib/pages/video/detail/introduction/view.dart | 7 ++ .../introduction/widgets/intro_detail.dart | 82 ++++++++++--------- 2 files changed, 50 insertions(+), 39 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 168e6ea7..9103c130 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,4 +1,5 @@ import 'package:expandable/expandable.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; @@ -265,6 +266,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { GestureDetector( behavior: HitTestBehavior.translucent, onTap: () => showIntroDetail(), + onLongPress: () async { + feedBack(); + await Clipboard.setData( + ClipboardData(text: widget.videoDetail!.title!)); + SmartDialog.showToast('标题已复制'); + }, child: ExpandablePanel( controller: _expandableCtr, collapsed: Text( diff --git a/lib/pages/video/detail/introduction/widgets/intro_detail.dart b/lib/pages/video/detail/introduction/widgets/intro_detail.dart index 3ec92023..78c621c0 100644 --- a/lib/pages/video/detail/introduction/widgets/intro_detail.dart +++ b/lib/pages/video/detail/introduction/widgets/intro_detail.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/utils.dart'; class IntroDetail extends StatelessWidget { @@ -16,44 +17,47 @@ class IntroDetail extends StatelessWidget { Widget build(BuildContext context) { return SizedBox( width: double.infinity, - child: SelectableRegion( - focusNode: FocusNode(), - selectionControls: MaterialTextSelectionControls(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 4), - Row( - children: [ - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); - SmartDialog.showToast('已复制'); - }, - child: Text( - videoDetail!.bvid!, - style: TextStyle( - fontSize: 13, - color: Theme.of(context).colorScheme.primary), - ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 4), + Row( + children: [ + GestureDetector( + onTap: () { + feedBack(); + Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.bvid!, + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), ), - const SizedBox(width: 10), - GestureDetector( - onTap: () { - Clipboard.setData(ClipboardData(text: videoDetail!.bvid!)); - SmartDialog.showToast('已复制'); - }, - child: Text( - videoDetail!.aid!.toString(), - style: TextStyle( - fontSize: 13, - color: Theme.of(context).colorScheme.primary), - ), - ) - ], - ), - const SizedBox(height: 4), - Text.rich( + ), + const SizedBox(width: 10), + GestureDetector( + onTap: () { + feedBack(); + Clipboard.setData( + ClipboardData(text: videoDetail!.aid!.toString())); + SmartDialog.showToast('已复制'); + }, + child: Text( + videoDetail!.aid!.toString(), + style: TextStyle( + fontSize: 13, + color: Theme.of(context).colorScheme.primary), + ), + ) + ], + ), + const SizedBox(height: 4), + SelectableRegion( + focusNode: FocusNode(), + selectionControls: MaterialTextSelectionControls(), + child: Text.rich( style: const TextStyle(height: 1.4), TextSpan( children: [ @@ -61,8 +65,8 @@ class IntroDetail extends StatelessWidget { ], ), ), - ], - ), + ), + ], ), ); } From 389747d6f47b4b7a4e76049d1db1ec5c35ae9910 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 30 Apr 2024 23:30:28 +0800 Subject: [PATCH 158/349] =?UTF-8?q?v1.0.22=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.22.0430.md | 27 +++++++++++++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.22.0430.md diff --git a/change_log/1.0.22.0430.md b/change_log/1.0.22.0430.md new file mode 100644 index 00000000..29f8aecf --- /dev/null +++ b/change_log/1.0.22.0430.md @@ -0,0 +1,27 @@ +## 1.0.22 + +### 功能 ++ 字幕 ++ 全屏时选集 ++ 动态转发 ++ 评论视频并转发 ++ 收藏夹删除 ++ 合集显示封面 ++ 底部导航栏编辑、排序功能 ++ 历史记录进度条展示 ++ 直播画质切换 ++ 排行榜功能 ++ 视频详情页推荐视频开关 ++ 显示联合投稿up + +### 修复 ++ 收藏夹个数错误 ++ 封面保存权限问题 ++ 合集最后1p未展示 ++ up主页关注按钮触发灰屏 + +### 优化 ++ 视频简介查看逻辑 + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index 5ea6386e..7cfbbca8 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.21+1021 +version: 1.0.22+1022 environment: sdk: ">=3.0.0 <4.0.0" From 7dbd832a8077d6abf3106a91bfc32355aa394eb3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 19:46:27 +0800 Subject: [PATCH 159/349] =?UTF-8?q?feat:=20=E9=95=BF=E6=8C=89=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E5=B0=81=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner/Info.plist | 2 + lib/common/widgets/overlay_pop.dart | 87 ------- lib/common/widgets/video_card_h.dart | 219 +++++++++--------- lib/common/widgets/video_card_v.dart | 15 +- lib/models/bangumi/list.dart | 3 + lib/models/search/result.dart | 24 +- lib/pages/bangumi/widgets/bangumu_card_v.dart | 170 ++++++-------- .../fav_detail/widget/fav_video_card.dart | 7 + lib/pages/hot/view.dart | 21 -- lib/pages/later/view.dart | 2 +- lib/pages/live/view.dart | 20 -- lib/pages/live/widgets/live_item.dart | 111 ++++----- lib/pages/member_seasons/widgets/item.dart | 7 + lib/pages/rank/zone/view.dart | 21 -- .../search_panel/widgets/live_panel.dart | 16 +- .../widgets/media_bangumi_panel.dart | 2 +- .../search_panel/widgets/video_panel.dart | 6 +- .../widget/sub_video_card.dart | 7 + lib/pages/video/detail/related/view.dart | 27 --- lib/utils/download.dart | 90 ++++--- lib/utils/image_save.dart | 88 +++++++ 21 files changed, 430 insertions(+), 515 deletions(-) delete mode 100644 lib/common/widgets/overlay_pop.dart create mode 100644 lib/utils/image_save.dart diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 65906625..1e7d9fed 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -49,6 +49,8 @@ NSPhotoLibraryAddUsageDescription 请允许APP保存图片到相册 + NSPhotoLibraryUsageDescription + 请允许APP保存图片到相册 NSCameraUsageDescription App需要您的同意,才能访问相册 NSAppleMusicUsageDescription diff --git a/lib/common/widgets/overlay_pop.dart b/lib/common/widgets/overlay_pop.dart deleted file mode 100644 index 4f0a3899..00000000 --- a/lib/common/widgets/overlay_pop.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter/material.dart'; -import '../../utils/download.dart'; -import '../constants.dart'; -import 'network_img_layer.dart'; - -class OverlayPop extends StatelessWidget { - const OverlayPop({super.key, this.videoItem, this.closeFn}); - - final dynamic videoItem; - final Function? closeFn; - - @override - Widget build(BuildContext context) { - final double imgWidth = MediaQuery.sizeOf(context).width - 8 * 2; - return Container( - margin: const EdgeInsets.symmetric(horizontal: 8), - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, - borderRadius: BorderRadius.circular(10.0), - ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Stack( - children: [ - NetworkImgLayer( - width: imgWidth, - height: imgWidth / StyleString.aspectRatio, - src: videoItem.pic! as String, - quality: 100, - ), - Positioned( - right: 8, - top: 8, - child: Container( - width: 30, - height: 30, - decoration: BoxDecoration( - color: Colors.black.withOpacity(0.3), - borderRadius: - const BorderRadius.all(Radius.circular(20))), - child: IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => closeFn!(), - icon: const Icon( - Icons.close, - size: 18, - color: Colors.white, - ), - ), - ), - ), - ], - ), - Padding( - padding: const EdgeInsets.fromLTRB(12, 10, 8, 10), - child: Row( - children: [ - Expanded( - child: Text( - videoItem.title! as String, - style: Theme.of(context).textTheme.titleSmall, - ), - ), - const SizedBox(width: 4), - IconButton( - tooltip: '保存封面图', - onPressed: () async { - await DownloadUtils.downloadImg( - videoItem.pic != null - ? videoItem.pic as String - : videoItem.cover as String, - ); - // closeFn!(); - }, - icon: const Icon(Icons.download, size: 20), - ) - ], - )), - ], - ), - ); - } -} diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 25e701ac..df0c29b7 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/image_save.dart'; import '../../http/search.dart'; import '../../http/user.dart'; import '../../http/video.dart'; @@ -16,8 +17,7 @@ class VideoCardH extends StatelessWidget { const VideoCardH({ super.key, required this.videoItem, - this.longPress, - this.longPressEnd, + this.onPressedFn, this.source = 'normal', this.showOwner = true, this.showView = true, @@ -27,8 +27,8 @@ class VideoCardH extends StatelessWidget { }); // ignore: prefer_typing_uninitialized_variables final videoItem; - final Function()? longPress; - final Function()? longPressEnd; + final Function()? onPressedFn; + // normal 推荐, later 稍后再看, search 搜索 final String source; final bool showOwner; final bool showView; @@ -45,109 +45,103 @@ class VideoCardH extends StatelessWidget { type = videoItem.type; } catch (_) {} final String heroTag = Utils.makeHeroTag(aid); - return GestureDetector( - onLongPress: () { - if (longPress != null) { - longPress!(); + return 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', + arguments: {'videoItem': videoItem, 'heroTag': heroTag}); + } catch (err) { + SmartDialog.showToast(err.toString()); } }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - 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', - arguments: {'videoItem': videoItem, 'heroTag': heroTag}); - } catch (err) { - SmartDialog.showToast(err.toString()); - } - }, - child: Padding( - padding: const EdgeInsets.fromLTRB( - StyleString.safeSpace, 5, StyleString.safeSpace, 5), - child: LayoutBuilder( - builder: (BuildContext context, BoxConstraints boxConstraints) { - final double width = (boxConstraints.maxWidth - - StyleString.cardSpace * - 6 / - MediaQuery.textScalerOf(context).scale(1.0)) / - 2; - return Container( - constraints: const BoxConstraints(minHeight: 88), - height: width / StyleString.aspectRatio, - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - AspectRatio( - aspectRatio: StyleString.aspectRatio, - child: LayoutBuilder( - builder: (BuildContext context, - BoxConstraints boxConstraints) { - final double maxWidth = boxConstraints.maxWidth; - final double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: videoItem.pic as String, - width: maxWidth, - height: maxHeight, - ), + onLongPress: () => imageSaveDialog( + context, + videoItem, + SmartDialog.dismiss, + ), + child: Padding( + padding: const EdgeInsets.fromLTRB( + StyleString.safeSpace, 5, StyleString.safeSpace, 5), + child: LayoutBuilder( + builder: (BuildContext context, BoxConstraints boxConstraints) { + final double width = (boxConstraints.maxWidth - + StyleString.cardSpace * + 6 / + MediaQuery.textScalerOf(context).scale(1.0)) / + 2; + return Container( + constraints: const BoxConstraints(minHeight: 88), + height: width / StyleString.aspectRatio, + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints boxConstraints) { + final double maxWidth = boxConstraints.maxWidth; + final double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: videoItem.pic as String, + width: maxWidth, + height: maxHeight, ), - 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, - // 6.0, 6.0, null, null), - if (showCharge && videoItem?.isChargingSrc) - const PBadge( - text: '充电专属', - right: 6.0, - top: 6.0, - type: 'primary', - ), - ], - ); - }, - ), + ), + 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, + // 6.0, 6.0, null, null), + if (showCharge && videoItem?.isChargingSrc) + const PBadge( + text: '充电专属', + right: 6.0, + top: 6.0, + type: 'primary', + ), + ], + ); + }, ), - VideoContent( - videoItem: videoItem, - source: source, - showOwner: showOwner, - showView: showView, - showDanmaku: showDanmaku, - showPubdate: showPubdate, - ) - ], - ), - ); - }, - ), + ), + VideoContent( + videoItem: videoItem, + source: source, + showOwner: showOwner, + showView: showView, + showDanmaku: showDanmaku, + showPubdate: showPubdate, + onPressedFn: onPressedFn, + ) + ], + ), + ); + }, ), ), ); @@ -162,6 +156,7 @@ class VideoContent extends StatelessWidget { final bool showView; final bool showDanmaku; final bool showPubdate; + final Function()? onPressedFn; const VideoContent({ super.key, @@ -171,6 +166,7 @@ class VideoContent extends StatelessWidget { this.showView = true, this.showDanmaku = true, this.showPubdate = false, + this.onPressedFn, }); @override @@ -181,7 +177,7 @@ class VideoContent extends StatelessWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (videoItem.title is String) ...[ + if (source == 'normal' || source == 'later') ...[ Text( videoItem.title as String, textAlign: TextAlign.start, @@ -196,7 +192,7 @@ class VideoContent extends StatelessWidget { maxLines: 2, text: TextSpan( children: [ - for (final i in videoItem.title) ...[ + for (final i in videoItem.titleList) ...[ TextSpan( text: i['text'] as String, style: TextStyle( @@ -374,6 +370,19 @@ class VideoContent extends StatelessWidget { ], ), ), + if (source == 'later') ...[ + IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => onPressedFn?.call(), + icon: Icon( + Icons.clear_outlined, + color: Theme.of(context).colorScheme.outline, + size: 18, + ), + ) + ], ], ), ], diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 9916aa7a..6a97d7e7 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/image_save.dart'; import '../../models/model_rec_video_item.dart'; -import 'overlay_pop.dart'; import 'stat/danmu.dart'; import 'stat/view.dart'; import '../../http/dynamics.dart'; @@ -127,14 +127,11 @@ class VideoCardV extends StatelessWidget { String heroTag = Utils.makeHeroTag(videoItem.id); return InkWell( onTap: () async => onPushDetail(heroTag), - onLongPress: () { - SmartDialog.show( - builder: (context) => OverlayPop( - videoItem: videoItem, - closeFn: () => SmartDialog.dismiss(), - ), - ); - }, + onLongPress: () => imageSaveDialog( + context, + videoItem, + SmartDialog.dismiss, + ), borderRadius: BorderRadius.circular(16), child: Column( children: [ diff --git a/lib/models/bangumi/list.dart b/lib/models/bangumi/list.dart index c15014d0..fe71bb61 100644 --- a/lib/models/bangumi/list.dart +++ b/lib/models/bangumi/list.dart @@ -30,6 +30,7 @@ class BangumiListItemModel { BangumiListItemModel({ this.badge, this.badgeType, + this.pic, this.cover, // this.firstEp, this.indexShow, @@ -50,6 +51,7 @@ class BangumiListItemModel { String? badge; int? badgeType; + String? pic; String? cover; String? indexShow; int? isFinish; @@ -70,6 +72,7 @@ class BangumiListItemModel { BangumiListItemModel.fromJson(Map json) { badge = json['badge'] == '' ? null : json['badge']; badgeType = json['badge_type']; + pic = json['cover']; cover = json['cover']; indexShow = json['index_show']; isFinish = json['is_finish']; diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart index 418fb99d..81917b72 100644 --- a/lib/models/search/result.dart +++ b/lib/models/search/result.dart @@ -25,6 +25,7 @@ class SearchVideoItemModel { this.aid, this.bvid, this.title, + this.titleList, this.description, this.pic, // this.play, @@ -54,8 +55,8 @@ class SearchVideoItemModel { String? arcurl; int? aid; String? bvid; - List? title; - // List? titleList; + String? title; + List? titleList; String? description; String? pic; // String? play; @@ -82,8 +83,9 @@ class SearchVideoItemModel { aid = json['aid']; bvid = json['bvid']; mid = json['mid']; - // title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); - title = Em.regTitle(json['title']); + title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); + // title = Em.regTitle(json['title']); + titleList = Em.regTitle(json['title']); description = json['description']; pic = json['pic'] != null && json['pic'].startsWith('//') ? 'https:${json['pic']}' @@ -232,6 +234,7 @@ class SearchLiveItemModel { this.userCover, this.type, this.title, + this.titleList, this.cover, this.pic, this.online, @@ -251,7 +254,8 @@ class SearchLiveItemModel { String? face; String? userCover; String? type; - List? title; + String? title; + List? titleList; String? cover; String? pic; int? online; @@ -272,7 +276,8 @@ class SearchLiveItemModel { face = json['uface']; userCover = json['user_cover']; type = json['type']; - title = Em.regTitle(json['title']); + title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); + titleList = Em.regTitle(json['title']); cover = json['cover']; pic = json['cover']; online = json['online']; @@ -302,6 +307,7 @@ class SearchMBangumiItemModel { this.type, this.mediaId, this.title, + this.titleList, this.orgTitle, this.mediaType, this.cv, @@ -328,7 +334,8 @@ class SearchMBangumiItemModel { String? type; int? mediaId; - List? title; + String? title; + List? titleList; String? orgTitle; int? mediaType; String? cv; @@ -355,7 +362,8 @@ class SearchMBangumiItemModel { SearchMBangumiItemModel.fromJson(Map json) { type = json['type']; mediaId = json['media_id']; - title = Em.regTitle(json['title']); + title = json['title'].replaceAll(RegExp(r'<.*?>'), ''); + titleList = Em.regTitle(json['title']); orgTitle = json['org_title']; mediaType = json['media_type']; cv = json['cv']; diff --git a/lib/pages/bangumi/widgets/bangumu_card_v.dart b/lib/pages/bangumi/widgets/bangumu_card_v.dart index c1233ddf..3c8f6d2a 100644 --- a/lib/pages/bangumi/widgets/bangumu_card_v.dart +++ b/lib/pages/bangumi/widgets/bangumu_card_v.dart @@ -5,7 +5,9 @@ import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/models/bangumi/info.dart'; +import 'package:pilipala/models/bangumi/list.dart'; import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -14,109 +16,87 @@ class BangumiCardV extends StatelessWidget { const BangumiCardV({ super.key, required this.bangumiItem, - this.longPress, - this.longPressEnd, }); - final bangumiItem; - final Function()? longPress; - final Function()? longPressEnd; + final BangumiListItemModel bangumiItem; @override Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(bangumiItem.mediaId); - return Card( - elevation: 0, - clipBehavior: Clip.hardEdge, - margin: EdgeInsets.zero, - child: GestureDetector( - // onLongPress: () { - // if (longPress != null) { - // longPress!(); - // } - // }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async { - final int seasonId = bangumiItem.seasonId; - SmartDialog.showLoading(msg: '获取中...'); - final res = await SearchHttp.bangumiInfo(seasonId: seasonId); - SmartDialog.dismiss().then((value) { - if (res['status']) { - if (res['data'].episodes.isEmpty) { - SmartDialog.showToast('资源加载失败'); - return; - } - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, + return InkWell( + onTap: () async { + final int seasonId = bangumiItem.seasonId!; + SmartDialog.showLoading(msg: '获取中...'); + final res = await SearchHttp.bangumiInfo(seasonId: seasonId); + SmartDialog.dismiss().then((value) { + if (res['status']) { + if (res['data'].episodes.isEmpty) { + SmartDialog.showToast('资源加载失败'); + return; + } + EpisodeItem episode = res['data'].episodes.first; + String bvid = episode.bvid!; + int cid = episode.cid!; + String pic = episode.cover!; + String heroTag = Utils.makeHeroTag(cid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId', + arguments: { + 'pic': pic, + 'heroTag': heroTag, + 'videoType': SearchType.media_bangumi, + 'bangumiItem': res['data'], + }, + ); + } + }); + }, + onLongPress: () => + imageSaveDialog(context, bangumiItem, SmartDialog.dismiss), + child: Column( + children: [ + ClipRRect( + borderRadius: const BorderRadius.all( + StyleString.imgRadius, + ), + child: AspectRatio( + aspectRatio: 0.65, + child: LayoutBuilder(builder: (context, boxConstraints) { + final double maxWidth = boxConstraints.maxWidth; + final double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + Hero( + tag: heroTag, + child: NetworkImgLayer( + src: bangumiItem.cover, + width: maxWidth, + height: maxHeight, + ), + ), + if (bangumiItem.badge != null) + PBadge( + text: bangumiItem.badge, + top: 6, + right: 6, + bottom: null, + left: null), + if (bangumiItem.order != null) + PBadge( + text: bangumiItem.order, + top: null, + right: null, + bottom: 6, + left: 6, + type: 'gray', + ), + ], ); - } - }); - }, - child: Column( - children: [ - ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: StyleString.imgRadius, - topRight: StyleString.imgRadius, - bottomLeft: StyleString.imgRadius, - bottomRight: StyleString.imgRadius, - ), - child: AspectRatio( - aspectRatio: 0.65, - child: LayoutBuilder(builder: (context, boxConstraints) { - final double maxWidth = boxConstraints.maxWidth; - final double maxHeight = boxConstraints.maxHeight; - return Stack( - children: [ - Hero( - tag: heroTag, - child: NetworkImgLayer( - src: bangumiItem.cover, - width: maxWidth, - height: maxHeight, - ), - ), - if (bangumiItem.badge != null) - PBadge( - text: bangumiItem.badge, - top: 6, - right: 6, - bottom: null, - left: null), - if (bangumiItem.order != null) - PBadge( - text: bangumiItem.order, - top: null, - right: null, - bottom: 6, - left: 6, - type: 'gray', - ), - ], - ); - }), - ), - ), - BangumiContent(bangumiItem: bangumiItem) - ], + }), + ), ), - ), + BangumiContent(bangumiItem: bangumiItem) + ], ), ); } diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 1c4008ff..79e5c073 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -1,3 +1,4 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/constants.dart'; @@ -7,6 +8,7 @@ import 'package:pilipala/http/search.dart'; import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/utils/id_utils.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import '../../../common/widgets/badge.dart'; @@ -61,6 +63,11 @@ class FavVideoCardH extends StatelessWidget { epId != null ? SearchType.media_bangumi : SearchType.video, }); }, + onLongPress: () => imageSaveDialog( + context, + videoItem, + SmartDialog.dismiss, + ), child: Column( children: [ Padding( diff --git a/lib/pages/hot/view.dart b/lib/pages/hot/view.dart index e2e20e73..80d08e7b 100644 --- a/lib/pages/hot/view.dart +++ b/lib/pages/hot/view.dart @@ -3,8 +3,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; @@ -78,15 +76,6 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { return VideoCardH( videoItem: _hotController.videoList[index], showPubdate: true, - longPress: () { - _hotController.popupDialog = _createPopupDialog( - _hotController.videoList[index]); - Overlay.of(context) - .insert(_hotController.popupDialog!); - }, - longPressEnd: () { - _hotController.popupDialog?.remove(); - }, ); }, childCount: _hotController.videoList.length), ), @@ -122,14 +111,4 @@ class _HotPageState extends State with AutomaticKeepAliveClientMixin { ), ); } - - OverlayEntry _createPopupDialog(videoItem) { - return OverlayEntry( - builder: (context) => AnimatedDialog( - closeFn: _hotController.popupDialog?.remove, - child: OverlayPop( - videoItem: videoItem, closeFn: _hotController.popupDialog?.remove), - ), - ); - } } diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index d4695154..7c6e158d 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -84,7 +84,7 @@ class _LaterPageState extends State { return VideoCardH( videoItem: videoItem, source: 'later', - longPress: () => _laterController.toViewDel( + onPressedFn: () => _laterController.toViewDel( aid: videoItem.aid)); }, childCount: _laterController.laterList.length), ) diff --git a/lib/pages/live/view.dart b/lib/pages/live/view.dart index c61d20b3..83605495 100644 --- a/lib/pages/live/view.dart +++ b/lib/pages/live/view.dart @@ -5,9 +5,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/video_card_v.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/utils/main_stream.dart'; import 'controller.dart'; @@ -112,16 +110,6 @@ class _LivePageState extends State ); } - OverlayEntry _createPopupDialog(liveItem) { - return OverlayEntry( - builder: (context) => AnimatedDialog( - closeFn: _liveController.popupDialog?.remove, - child: OverlayPop( - videoItem: liveItem, closeFn: _liveController.popupDialog?.remove), - ), - ); - } - Widget contentGrid(ctr, liveList) { // double maxWidth = Get.size.width; // int baseWidth = 500; @@ -152,14 +140,6 @@ class _LivePageState extends State ? LiveCardV( liveItem: liveList[index], crossAxisCount: crossAxisCount, - longPress: () { - _liveController.popupDialog = - _createPopupDialog(liveList[index]); - Overlay.of(context).insert(_liveController.popupDialog!); - }, - longPressEnd: () { - _liveController.popupDialog?.remove(); - }, ) : const VideoCardVSkeleton(); }, diff --git a/lib/pages/live/widgets/live_item.dart b/lib/pages/live/widgets/live_item.dart index 9218d4fb..f70ba82b 100644 --- a/lib/pages/live/widgets/live_item.dart +++ b/lib/pages/live/widgets/live_item.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/models/live/item.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -9,81 +11,66 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; class LiveCardV extends StatelessWidget { final LiveItemModel liveItem; final int crossAxisCount; - final Function()? longPress; - final Function()? longPressEnd; const LiveCardV({ Key? key, required this.liveItem, required this.crossAxisCount, - this.longPress, - this.longPressEnd, }) : super(key: key); @override Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(liveItem.roomId); - return Card( - elevation: 0, - clipBehavior: Clip.hardEdge, - margin: EdgeInsets.zero, - child: GestureDetector( - onLongPress: () { - if (longPress != null) { - longPress!(); - } - }, - // onLongPressEnd: (details) { - // if (longPressEnd != null) { - // longPressEnd!(); - // } - // }, - child: InkWell( - onTap: () async { - Get.toNamed('/liveRoom?roomid=${liveItem.roomId}', - arguments: {'liveItem': liveItem, 'heroTag': heroTag}); - }, - child: Column( - children: [ - ClipRRect( - borderRadius: const BorderRadius.all(StyleString.imgRadius), - child: 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: liveItem.cover!, - width: maxWidth, - height: maxHeight, + return InkWell( + onLongPress: () => imageSaveDialog( + context, + liveItem, + SmartDialog.dismiss, + ), + borderRadius: BorderRadius.circular(16), + onTap: () async { + Get.toNamed('/liveRoom?roomid=${liveItem.roomId}', + arguments: {'liveItem': liveItem, 'heroTag': heroTag}); + }, + child: Column( + children: [ + ClipRRect( + borderRadius: const BorderRadius.all(StyleString.imgRadius), + child: 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: liveItem.cover!, + width: maxWidth, + height: maxHeight, + ), + ), + if (crossAxisCount != 1) + Positioned( + left: 0, + right: 0, + bottom: 0, + child: AnimatedOpacity( + opacity: 1, + duration: const Duration(milliseconds: 200), + child: VideoStat( + liveItem: liveItem, ), ), - if (crossAxisCount != 1) - Positioned( - left: 0, - right: 0, - bottom: 0, - child: AnimatedOpacity( - opacity: 1, - duration: const Duration(milliseconds: 200), - child: VideoStat( - liveItem: liveItem, - ), - ), - ), - ], - ); - }), - ), - ), - LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount) - ], + ), + ], + ); + }), + ), ), - ), + LiveContent(liveItem: liveItem, crossAxisCount: crossAxisCount) + ], ), ); } diff --git a/lib/pages/member_seasons/widgets/item.dart b/lib/pages/member_seasons/widgets/item.dart index 4df74b70..85b763b7 100644 --- a/lib/pages/member_seasons/widgets/item.dart +++ b/lib/pages/member_seasons/widgets/item.dart @@ -1,10 +1,12 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/common/widgets/stat/view.dart'; import 'package:pilipala/http/search.dart'; +import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/utils.dart'; class MemberSeasonsItem extends StatelessWidget { @@ -29,6 +31,11 @@ class MemberSeasonsItem extends StatelessWidget { Get.toNamed('/video?bvid=${seasonItem.bvid}&cid=$cid', arguments: {'videoItem': seasonItem, 'heroTag': heroTag}); }, + onLongPress: () => imageSaveDialog( + context, + seasonItem, + SmartDialog.dismiss, + ), child: Column( children: [ AspectRatio( diff --git a/lib/pages/rank/zone/view.dart b/lib/pages/rank/zone/view.dart index 04631a8c..72d81f95 100644 --- a/lib/pages/rank/zone/view.dart +++ b/lib/pages/rank/zone/view.dart @@ -3,8 +3,6 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; @@ -82,15 +80,6 @@ class _ZonePageState extends State return VideoCardH( videoItem: _zoneController.videoList[index], showPubdate: true, - longPress: () { - _zoneController.popupDialog = _createPopupDialog( - _zoneController.videoList[index]); - Overlay.of(context) - .insert(_zoneController.popupDialog!); - }, - longPressEnd: () { - _zoneController.popupDialog?.remove(); - }, ); }, childCount: _zoneController.videoList.length), ), @@ -126,14 +115,4 @@ class _ZonePageState extends State ), ); } - - OverlayEntry _createPopupDialog(videoItem) { - return OverlayEntry( - builder: (context) => AnimatedDialog( - closeFn: _zoneController.popupDialog?.remove, - child: OverlayPop( - videoItem: videoItem, closeFn: _zoneController.popupDialog?.remove), - ), - ); - } } diff --git a/lib/pages/search_panel/widgets/live_panel.dart b/lib/pages/search_panel/widgets/live_panel.dart index 6fb5f5b8..5f797f09 100644 --- a/lib/pages/search_panel/widgets/live_panel.dart +++ b/lib/pages/search_panel/widgets/live_panel.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.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/image_save.dart'; import 'package:pilipala/utils/utils.dart'; Widget searchLivePanel(BuildContext context, ctr, list) { @@ -42,15 +44,15 @@ class LiveItem extends StatelessWidget { Get.toNamed('/liveRoom?roomid=${liveItem.roomid}', arguments: {'liveItem': liveItem, 'heroTag': heroTag}); }, + onLongPress: () => imageSaveDialog( + context, + liveItem, + SmartDialog.dismiss, + ), child: Column( children: [ ClipRRect( - borderRadius: const BorderRadius.only( - topLeft: StyleString.imgRadius, - topRight: StyleString.imgRadius, - bottomLeft: StyleString.imgRadius, - bottomRight: StyleString.imgRadius, - ), + borderRadius: const BorderRadius.all(StyleString.imgRadius), child: AspectRatio( aspectRatio: StyleString.aspectRatio, child: LayoutBuilder(builder: (context, boxConstraints) { @@ -108,7 +110,7 @@ class LiveContent extends StatelessWidget { RichText( text: TextSpan( children: [ - for (var i in liveItem.title) ...[ + for (var i in liveItem.titleList) ...[ TextSpan( text: i['text'], style: TextStyle( diff --git a/lib/pages/search_panel/widgets/media_bangumi_panel.dart b/lib/pages/search_panel/widgets/media_bangumi_panel.dart index 18799d3a..7d88b183 100644 --- a/lib/pages/search_panel/widgets/media_bangumi_panel.dart +++ b/lib/pages/search_panel/widgets/media_bangumi_panel.dart @@ -63,7 +63,7 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) { style: TextStyle( color: Theme.of(context).colorScheme.onSurface), children: [ - for (var i in i.title) ...[ + for (var i in i.titleList) ...[ TextSpan( text: i['text'], style: TextStyle( diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index b96ff004..c24a007c 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -35,7 +35,11 @@ class SearchVideoPanel extends StatelessWidget { padding: index == 0 ? const EdgeInsets.only(top: 2) : EdgeInsets.zero, - child: VideoCardH(videoItem: i, showPubdate: true), + child: VideoCardH( + videoItem: i, + showPubdate: true, + source: 'search', + ), ); }, ), diff --git a/lib/pages/subscription_detail/widget/sub_video_card.dart b/lib/pages/subscription_detail/widget/sub_video_card.dart index 11aebc39..dcdee4ef 100644 --- a/lib/pages/subscription_detail/widget/sub_video_card.dart +++ b/lib/pages/subscription_detail/widget/sub_video_card.dart @@ -1,3 +1,4 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:pilipala/common/constants.dart'; @@ -5,6 +6,7 @@ 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/image_save.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import '../../../common/widgets/badge.dart'; @@ -40,6 +42,11 @@ class SubVideoCardH extends StatelessWidget { 'videoType': SearchType.video, }); }, + onLongPress: () => imageSaveDialog( + context, + videoItem, + SmartDialog.dismiss, + ), child: Column( children: [ Padding( diff --git a/lib/pages/video/detail/related/view.dart b/lib/pages/video/detail/related/view.dart index 0912724e..fe3b0dca 100644 --- a/lib/pages/video/detail/related/view.dart +++ b/lib/pages/video/detail/related/view.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/skeleton/video_card_h.dart'; -import 'package:pilipala/common/widgets/animated_dialog.dart'; import 'package:pilipala/common/widgets/http_error.dart'; -import 'package:pilipala/common/widgets/overlay_pop.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import './controller.dart'; @@ -54,20 +52,6 @@ class _RelatedVideoPanelState extends State child: VideoCardH( videoItem: relatedVideoList[index], showPubdate: true, - longPress: () { - try { - _releatedController.popupDialog = - _createPopupDialog(_releatedController - .relatedVideoList[index]); - Overlay.of(context) - .insert(_releatedController.popupDialog!); - } catch (err) { - return {}; - } - }, - longPressEnd: () { - _releatedController.popupDialog?.remove(); - }, ), ); } @@ -89,15 +73,4 @@ class _RelatedVideoPanelState extends State }, ); } - - OverlayEntry _createPopupDialog(videoItem) { - return OverlayEntry( - builder: (BuildContext context) => AnimatedDialog( - closeFn: _releatedController.popupDialog?.remove, - child: OverlayPop( - videoItem: videoItem, - closeFn: _releatedController.popupDialog?.remove), - ), - ); - } } diff --git a/lib/utils/download.dart b/lib/utils/download.dart index 2aff8999..42dbbecf 100644 --- a/lib/utils/download.dart +++ b/lib/utils/download.dart @@ -15,24 +15,7 @@ class DownloadUtils { PermissionStatus status = await Permission.storage.status; if (status == PermissionStatus.denied || status == PermissionStatus.permanentlyDenied) { - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('存储权限未授权'), - actions: [ - TextButton( - onPressed: () async { - openAppSettings(); - }, - child: const Text('去授权'), - ) - ], - ); - }, - ); + await permissionDialog('提示', '存储权限未授权'); return false; } else { return true; @@ -45,24 +28,7 @@ class DownloadUtils { PermissionStatus status = await Permission.photos.status; if (status == PermissionStatus.denied || status == PermissionStatus.permanentlyDenied) { - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('相册权限未授权'), - actions: [ - TextButton( - onPressed: () async { - openAppSettings(); - }, - child: const Text('去授权'), - ) - ], - ); - }, - ); + await permissionDialog('提示', '相册权限未授权'); return false; } else { return true; @@ -72,17 +38,16 @@ class DownloadUtils { static Future downloadImg(String imgUrl, {String imgType = 'cover'}) async { try { - if (!Platform.isAndroid || !await requestPhotoPer()) { - return false; - } - final androidInfo = await DeviceInfoPlugin().androidInfo; - if (androidInfo.version.sdkInt <= 32) { - if (!await requestStoragePer()) { - return false; - } - } else { - if (!await requestPhotoPer()) { - return false; + if (Platform.isAndroid) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt <= 32) { + if (!await requestStoragePer()) { + return false; + } + } else { + if (!await requestPhotoPer()) { + return false; + } } } @@ -101,13 +66,38 @@ class DownloadUtils { ); SmartDialog.dismiss(); if (result.isSuccess) { - await SmartDialog.showToast('「${'$picName.$imgSuffix'}」已保存 '); + SmartDialog.showToast('「${'$picName.$imgSuffix'}」已保存 '); + return true; + } else { + await permissionDialog('保存失败', '相册权限未授权'); + return false; } - return true; } catch (err) { SmartDialog.dismiss(); SmartDialog.showToast(err.toString()); - return true; + return false; } } + + static Future permissionDialog(String title, String content, + {Function? onGranted}) async { + await SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: Text(title), + content: Text(content), + actions: [ + TextButton( + onPressed: () async { + openAppSettings(); + }, + child: const Text('去授权'), + ) + ], + ); + }, + ); + } } diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart new file mode 100644 index 00000000..0cd6915c --- /dev/null +++ b/lib/utils/image_save.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/utils/download.dart'; + +Future imageSaveDialog(context, videoItem, closeFn) { + final double imgWidth = + MediaQuery.sizeOf(context).width - StyleString.safeSpace * 2; + return SmartDialog.show( + animationType: SmartAnimationType.centerScale_otherSlide, + builder: (context) => Container( + margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: BorderRadius.circular(10.0), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Stack( + children: [ + NetworkImgLayer( + width: imgWidth, + height: imgWidth / StyleString.aspectRatio, + src: videoItem.pic! as String, + quality: 100, + ), + Positioned( + right: 8, + top: 8, + child: Container( + width: 30, + height: 30, + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.3), + borderRadius: + const BorderRadius.all(Radius.circular(20))), + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => closeFn!(), + icon: const Icon( + Icons.close, + size: 18, + color: Colors.white, + ), + ), + ), + ), + ], + ), + Padding( + padding: const EdgeInsets.fromLTRB(12, 10, 8, 10), + child: Row( + children: [ + Expanded( + child: Text( + videoItem.title! as String, + style: Theme.of(context).textTheme.titleSmall, + ), + ), + const SizedBox(width: 4), + IconButton( + tooltip: '保存封面图', + onPressed: () async { + bool saveStatus = await DownloadUtils.downloadImg( + videoItem.pic != null + ? videoItem.pic as String + : videoItem.cover as String, + ); + // 保存成功,自动关闭弹窗 + if (saveStatus) { + closeFn?.call(); + } + }, + icon: const Icon(Icons.download, size: 20), + ) + ], + ), + ), + ], + ), + ), + ); +} From e361e09ddfefb964ebd0f7135b8ce788d1e00f60 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 20:02:34 +0800 Subject: [PATCH 160/349] =?UTF-8?q?fix:=20=E8=AE=A2=E9=98=85=E8=AF=A6?= =?UTF-8?q?=E6=83=85type=E5=8C=BA=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/subscription/widgets/item.dart | 1 + lib/pages/subscription_detail/controller.dart | 11 +++++++---- lib/pages/subscription_detail/view.dart | 6 +++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index 5b2a0134..0d424611 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -25,6 +25,7 @@ class SubItem extends StatelessWidget { parameters: { 'heroTag': heroTag, 'seasonId': subFolderItem.id.toString(), + 'type': subFolderItem.type.toString(), }, ), child: Padding( diff --git a/lib/pages/subscription_detail/controller.dart b/lib/pages/subscription_detail/controller.dart index 4245df2c..e69f4be0 100644 --- a/lib/pages/subscription_detail/controller.dart +++ b/lib/pages/subscription_detail/controller.dart @@ -14,13 +14,16 @@ class SubDetailController extends GetxController { RxList subList = [].obs; RxString loadingText = '加载中...'.obs; int mediaCount = 0; + late int channelType; @override void onInit() { item = Get.arguments; - if (Get.parameters.keys.isNotEmpty) { - seasonId = int.parse(Get.parameters['seasonId']!); - heroTag = Get.parameters['heroTag']!; + final parameters = Get.parameters; + if (parameters.isNotEmpty) { + seasonId = int.tryParse(parameters['seasonId'] ?? '') ?? 0; + heroTag = parameters['heroTag'] ?? ''; + channelType = int.tryParse(parameters['type'] ?? '') ?? 0; } super.onInit(); } @@ -31,7 +34,7 @@ class SubDetailController extends GetxController { return; } isLoadingMore = true; - var res = type == 21 + var res = channelType == 21 ? await UserHttp.userSeasonList( seasonId: seasonId, ps: 20, diff --git a/lib/pages/subscription_detail/view.dart b/lib/pages/subscription_detail/view.dart index 63352429..4ca241fe 100644 --- a/lib/pages/subscription_detail/view.dart +++ b/lib/pages/subscription_detail/view.dart @@ -198,8 +198,8 @@ class _SubDetailPageState extends State { future: _futureBuilderFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { if (_subDetailController.item.mediaCount == 0) { return const NoData(); } else { @@ -219,7 +219,7 @@ class _SubDetailPageState extends State { } } else { return HttpError( - errMsg: data['msg'], + errMsg: data?['msg'] ?? '请求异常', fn: () => setState(() {}), ); } From 91ad0fa03dc44ddc3694367b8902db0be34003b0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 20:22:52 +0800 Subject: [PATCH 161/349] =?UTF-8?q?fix:=20=E6=9F=A5=E7=9C=8B=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E8=AF=A6=E6=83=85=E7=82=B9=E5=87=BB=E5=8C=BA=E5=9F=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 468d2541..ef4cd15c 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -44,7 +44,7 @@ class ReplyItem extends StatelessWidget { onTap: () { feedBack(); if (replyReply != null) { - replyReply!(replyItem, null); + replyReply!(replyItem); } }, onLongPress: () { @@ -358,7 +358,7 @@ class ReplyItemRow extends StatelessWidget { InkWell( // 一楼点击评论展开评论详情 // onTap: () { - // replyReply?.call(replyItem, replies![i]); + // replyReply?.call(replyItem); // }, onLongPress: () { feedBack(); From 291312bde70df30258692759fcb31ed030f0b133 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 20:44:38 +0800 Subject: [PATCH 162/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E8=A7=86=E9=A2=91=E9=A1=B5pic=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/search.dart | 16 ++++++++++++++++ lib/pages/video/detail/controller.dart | 2 +- lib/utils/url_utils.dart | 6 ++++-- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/http/search.dart b/lib/http/search.dart index 18481ea8..cf1a1b49 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -163,4 +163,20 @@ class SearchHttp { }; } } + + static Future> ab2cWithPic( + {int? aid, String? bvid}) async { + Map data = {}; + if (aid != null) { + data['aid'] = aid; + } else if (bvid != null) { + data['bvid'] = bvid; + } + final dynamic res = + await Request().get(Api.ab2c, data: {...data}); + return { + 'cid': res.data['data'].first['cid'], + 'pic': res.data['data'].first['first_frame'], + }; + } } diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 6dd6425f..f7f9e848 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -547,7 +547,7 @@ class VideoDetailController extends GetxController } void updateCover(String? pic) { - if (pic != null && pic != '') { + if (pic != null) { cover.value = videoItem['pic'] = pic; } } diff --git a/lib/utils/url_utils.dart b/lib/utils/url_utils.dart index cf0ef9e2..20f9cb0d 100644 --- a/lib/utils/url_utils.dart +++ b/lib/utils/url_utils.dart @@ -42,12 +42,14 @@ class UrlUtils { final Map matchRes = IdUtils.matchAvorBv(input: pathSegment); if (matchRes.containsKey('BV')) { final String bv = matchRes['BV']; - final int cid = await SearchHttp.ab2c(bvid: bv); + final Map res = await SearchHttp.ab2cWithPic(bvid: bv); + final int cid = res['cid']; + final String pic = res['pic']; final String heroTag = Utils.makeHeroTag(bv); await Get.toNamed( '/video?bvid=$bv&cid=$cid', arguments: { - 'pic': '', + 'pic': pic, 'heroTag': heroTag, }, ); From 8082550da6d4a8c6a722243085418186fd57ab7d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 22:51:55 +0800 Subject: [PATCH 163/349] =?UTF-8?q?fix:=20=E5=85=A8=E5=B1=8F=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index b87350a5..bcacbbbb 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -535,20 +535,20 @@ class _VideoDetailPageState extends State controller: _extendNestCtr, headerSliverBuilder: (BuildContext context2, bool innerBoxIsScrolled) { - final Orientation orientation = - MediaQuery.of(context).orientation; - final bool isFullScreen = - plPlayerController?.isFullScreen.value == true; - final double expandedHeight = - orientation == Orientation.landscape || isFullScreen - ? (MediaQuery.sizeOf(context).height - - (orientation == Orientation.landscape - ? 0 - : MediaQuery.of(context).padding.top)) - : videoHeight.value; return [ Obx( () { + final Orientation orientation = + MediaQuery.of(context).orientation; + final bool isFullScreen = + plPlayerController?.isFullScreen.value == true; + final double expandedHeight = + orientation == Orientation.landscape || isFullScreen + ? (MediaQuery.sizeOf(context).height - + (orientation == Orientation.landscape + ? 0 + : MediaQuery.of(context).padding.top)) + : videoHeight.value; if (orientation == Orientation.landscape || isFullScreen) { enterFullScreen(); From b85067f7ebdc2e257e4a826f0c74ff41125f40f3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 1 May 2024 23:41:18 +0800 Subject: [PATCH 164/349] =?UTF-8?q?opt:=20=E7=B3=BB=E7=BB=9F=E7=BA=A7?= =?UTF-8?q?=E8=B7=B3=E8=BD=ACav=E3=80=81bangumi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 69fa717d..5bfb955c 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -167,8 +167,21 @@ class PiliSchame { print('bilibili.com host: $host'); print('bilibili.com path: $path'); final String lastPathSegment = path!.split('/').last; - if (lastPathSegment.contains('BV')) { - _videoPush(null, lastPathSegment); + if (path.startsWith('/video')) { + if (lastPathSegment.contains('BV')) { + _videoPush(null, lastPathSegment); + } + if (lastPathSegment.contains('av')) { + _videoPush(matchNum(lastPathSegment)[0], null); + } + } + if (path.startsWith('/bangumi')) { + if (lastPathSegment.contains('ss')) { + _bangumiPush(matchNum(lastPathSegment).first, null); + } + if (lastPathSegment.contains('ep')) { + _bangumiPush(null, matchNum(lastPathSegment).first); + } } } else if (host.contains('live')) { int roomId = int.parse(path!.split('/').last); From 61505c61273dab58fd804ccce6485af4afc19e84 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 2 May 2024 22:19:20 +0800 Subject: [PATCH 165/349] opt: RoutePush bangumi --- lib/common/widgets/video_card_v.dart | 25 +++-------- lib/pages/bangumi/widgets/bangumu_card_v.dart | 37 +++------------ lib/pages/dynamics/controller.dart | 21 +-------- lib/pages/history/widgets/item.dart | 27 +++-------- .../widgets/media_bangumi_panel.dart | 25 ++--------- .../whisper_detail/widget/chat_item.dart | 45 ++++++++++++++----- lib/utils/app_scheme.dart | 44 ++++-------------- lib/utils/route_push.dart | 44 ++++++++++++++++++ 8 files changed, 111 insertions(+), 157 deletions(-) create mode 100644 lib/utils/route_push.dart diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 6a97d7e7..15fbc6fc 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -3,14 +3,13 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/image_save.dart'; +import 'package:pilipala/utils/route_push.dart'; import '../../models/model_rec_video_item.dart'; import 'stat/danmu.dart'; import 'stat/view.dart'; import '../../http/dynamics.dart'; -import '../../http/search.dart'; import '../../http/user.dart'; import '../../http/video.dart'; -import '../../models/common/search_type.dart'; import '../../utils/id_utils.dart'; import '../../utils/utils.dart'; import '../constants.dart'; @@ -42,23 +41,11 @@ class VideoCardV extends StatelessWidget { return; } int epId = videoItem.param; - SmartDialog.showLoading(msg: '资源获取中'); - var result = await SearchHttp.bangumiInfo(seasonId: null, epId: epId); - if (result['status']) { - var bangumiDetail = result['data']; - int cid = bangumiDetail.episodes!.first.cid; - String bvid = IdUtils.av2bv(bangumiDetail.episodes!.first.aid); - SmartDialog.dismiss().then( - (value) => Get.toNamed( - '/video?bvid=$bvid&cid=$cid&epId=$epId', - arguments: { - 'pic': videoItem.pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - }, - ), - ); - } + RoutePush.bangumiPush( + null, + epId, + heroTag: heroTag, + ); break; case 'av': String bvid = videoItem.bvid ?? IdUtils.av2bv(videoItem.aid); diff --git a/lib/pages/bangumi/widgets/bangumu_card_v.dart b/lib/pages/bangumi/widgets/bangumu_card_v.dart index 3c8f6d2a..a1d7a931 100644 --- a/lib/pages/bangumi/widgets/bangumu_card_v.dart +++ b/lib/pages/bangumi/widgets/bangumu_card_v.dart @@ -1,13 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; -import 'package:pilipala/http/search.dart'; -import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/bangumi/list.dart'; -import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/utils/image_save.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -24,32 +21,12 @@ class BangumiCardV extends StatelessWidget { Widget build(BuildContext context) { String heroTag = Utils.makeHeroTag(bangumiItem.mediaId); return InkWell( - onTap: () async { - final int seasonId = bangumiItem.seasonId!; - SmartDialog.showLoading(msg: '获取中...'); - final res = await SearchHttp.bangumiInfo(seasonId: seasonId); - SmartDialog.dismiss().then((value) { - if (res['status']) { - if (res['data'].episodes.isEmpty) { - SmartDialog.showToast('资源加载失败'); - return; - } - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=$seasonId', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, - ); - } - }); + onTap: () { + RoutePush.bangumiPush( + bangumiItem.seasonId, + null, + heroTag: heroTag, + ); }, onLongPress: () => imageSaveDialog(context, bangumiItem, SmartDialog.dismiss), diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index b7676663..6814704c 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -14,6 +14,7 @@ import 'package:pilipala/models/dynamics/up.dart'; import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; @@ -220,25 +221,7 @@ class DynamicsController extends GetxController { print('DYNAMIC_TYPE_PGC_UNION 番剧'); DynamicArchiveModel pgc = item.modules.moduleDynamic.major.pgc; if (pgc.epid != null) { - SmartDialog.showLoading(msg: '获取中...'); - var res = await SearchHttp.bangumiInfo(epId: pgc.epid); - SmartDialog.dismiss(); - if (res['status']) { - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=${res['data'].seasonId}', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, - ); - } + RoutePush.bangumiPush(null, pgc.epid); } break; } diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 39c6931d..ba5f6b5c 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -7,13 +7,13 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/http/video.dart'; -import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/common/business_type.dart'; import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/models/live/item.dart'; import 'package:pilipala/pages/history_search/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; class HistoryItem extends StatelessWidget { @@ -101,26 +101,11 @@ class HistoryItem extends StatelessWidget { } } else { if (videoItem.history.epid != '') { - SmartDialog.showLoading(msg: '获取中...'); - var res = - await SearchHttp.bangumiInfo(epId: videoItem.history.epid); - SmartDialog.dismiss(); - if (res['status']) { - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=${res['data'].seasonId}', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, - ); - } + RoutePush.bangumiPush( + null, + videoItem.history.epid, + heroTag: heroTag, + ); } } } else { diff --git a/lib/pages/search_panel/widgets/media_bangumi_panel.dart b/lib/pages/search_panel/widgets/media_bangumi_panel.dart index 7d88b183..5bba0ab8 100644 --- a/lib/pages/search_panel/widgets/media_bangumi_panel.dart +++ b/lib/pages/search_panel/widgets/media_bangumi_panel.dart @@ -7,6 +7,7 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/search.dart'; import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; Widget searchMbangumiPanel(BuildContext context, ctr, list) { @@ -108,28 +109,8 @@ Widget searchMbangumiPanel(BuildContext context, ctr, list) { SizedBox( height: 32, child: ElevatedButton( - onPressed: () async { - SmartDialog.showLoading(msg: '获取中...'); - var res = await SearchHttp.bangumiInfo( - seasonId: i.seasonId); - SmartDialog.dismiss().then((value) { - if (res['status']) { - EpisodeItem episode = res['data'].episodes.first; - String bvid = episode.bvid!; - int cid = episode.cid!; - String pic = episode.cover!; - String heroTag = Utils.makeHeroTag(cid); - Get.toNamed( - '/video?bvid=$bvid&cid=$cid&seasonId=${i.seasonId}', - arguments: { - 'pic': pic, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - 'bangumiItem': res['data'], - }, - ); - } - }); + onPressed: () { + RoutePush.bangumiPush(i.seasonId, null); }, child: const Text('观看'), ), diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 4fd49254..743cf901 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -6,6 +6,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/storage.dart'; @@ -49,6 +50,13 @@ class ChatItem extends StatelessWidget { this.e_infos, }); + static List matchNum(String str) { + final RegExp regExp = RegExp(r'\d+'); + final Iterable matches = regExp.allMatches(str); + + return matches.map((Match match) => int.parse(match.group(0)!)).toList(); + } + @override Widget build(BuildContext context) { bool isOwner = @@ -154,16 +162,33 @@ class ChatItem extends StatelessWidget { GestureDetector( onTap: () async { SmartDialog.showLoading(); - var bvid = content["bvid"]; + final String bvid = content["bvid"]; + // 16番剧 5投稿 + final int source = content["source"]; + final String? url = content["url"]; + final int cid = await SearchHttp.ab2c(bvid: bvid); final String heroTag = Utils.makeHeroTag(bvid); - SmartDialog.dismiss().then( - (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', - arguments: { - 'pic': content['thumb'], - 'heroTag': heroTag, - }), - ); + await SmartDialog.dismiss(); + if (source == 5) { + Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': content['thumb'], + 'heroTag': heroTag, + }, + ); + } + if (source == 16) { + if (url != null) { + final String area = url.split('/').last; + if (area.startsWith('ep')) { + RoutePush.bangumiPush(null, matchNum(area).first); + } else if (area.startsWith('ss')) { + RoutePush.bangumiPush(matchNum(area).first, null); + } + } + } }, child: NetworkImgLayer( width: 220, @@ -183,7 +208,7 @@ class ChatItem extends StatelessWidget { ), const SizedBox(height: 1), Text( - content['author'], + content['author'] ?? '', style: TextStyle( letterSpacing: 0.6, height: 1.5, @@ -206,7 +231,7 @@ class ChatItem extends StatelessWidget { SmartDialog.dismiss().then( (e) => Get.toNamed('/video?bvid=$bvid&cid=$cid', arguments: { - 'pic': content['thumb'], + 'pic': content['thumb'] ?? '', 'heroTag': heroTag, }), ); diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 5bfb955c..5b77434d 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -2,6 +2,7 @@ import 'package:appscheme/appscheme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/route_push.dart'; import '../http/search.dart'; import '../models/common/search_type.dart'; import 'id_utils.dart'; @@ -68,7 +69,7 @@ class PiliSchame { } else if (host == 'bangumi') { if (path.startsWith('/season')) { final String seasonId = path.split('/').last; - _bangumiPush(int.parse(seasonId), null); + RoutePush.bangumiPush(int.parse(seasonId), null); } } else if (host == 'opus') { if (path.startsWith('/detail')) { @@ -126,35 +127,6 @@ class PiliSchame { } } - // 番剧跳转 - static Future _bangumiPush(int? seasonId, int? epId) async { - SmartDialog.showLoading(msg: '获取中...'); - try { - var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId); - if (result['status']) { - var bangumiDetail = result['data']; - final int cid = bangumiDetail.episodes!.first.cid; - final String bvid = IdUtils.av2bv(bangumiDetail.episodes!.first.aid); - final String heroTag = Utils.makeHeroTag(cid); - var epId = bangumiDetail.episodes!.first.id; - SmartDialog.dismiss().then( - (e) => Get.toNamed( - '/video?bvid=$bvid&cid=$cid&epId=$epId', - arguments: { - 'pic': bangumiDetail.cover, - 'heroTag': heroTag, - 'videoType': SearchType.media_bangumi, - }, - ), - ); - } else { - SmartDialog.showToast(result['msg']); - } - } catch (e) { - SmartDialog.showToast('番剧获取失败:$e'); - } - } - static Future _fullPathPush(SchemeEntity value) async { // https://m.bilibili.com/bangumi/play/ss39708 // https | m.bilibili.com | /bangumi/play/ss39708 @@ -177,10 +149,10 @@ class PiliSchame { } if (path.startsWith('/bangumi')) { if (lastPathSegment.contains('ss')) { - _bangumiPush(matchNum(lastPathSegment).first, null); + RoutePush.bangumiPush(matchNum(lastPathSegment).first, null); } if (lastPathSegment.contains('ep')) { - _bangumiPush(null, matchNum(lastPathSegment).first); + RoutePush.bangumiPush(null, matchNum(lastPathSegment).first); } } } else if (host.contains('live')) { @@ -233,9 +205,9 @@ class PiliSchame { case 'bangumi': print('番剧'); if (area.startsWith('ep')) { - _bangumiPush(null, matchNum(area).first); + RoutePush.bangumiPush(null, matchNum(area).first); } else if (area.startsWith('ss')) { - _bangumiPush(matchNum(area).first, null); + RoutePush.bangumiPush(matchNum(area).first, null); } break; case 'video': @@ -276,12 +248,12 @@ class PiliSchame { static void _handleEpisodePath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); - _bangumiPush(null, matchNum(seasonId).first); + RoutePush.bangumiPush(null, matchNum(seasonId).first); } static void _handleSeasonPath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); - _bangumiPush(matchNum(seasonId).first, null); + RoutePush.bangumiPush(matchNum(seasonId).first, null); } static String _extractIdFromPath(String lastPathSegment) { diff --git a/lib/utils/route_push.dart b/lib/utils/route_push.dart new file mode 100644 index 00000000..e00123b4 --- /dev/null +++ b/lib/utils/route_push.dart @@ -0,0 +1,44 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/bangumi/info.dart'; +import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/utils/utils.dart'; + +class RoutePush { + // 番剧跳转 + static Future bangumiPush(int? seasonId, int? epId, + {String? heroTag}) async { + SmartDialog.showLoading(msg: '获取中...'); + try { + var result = await SearchHttp.bangumiInfo(seasonId: seasonId, epId: epId); + await SmartDialog.dismiss(); + if (result['status']) { + if (result['data'].episodes.isEmpty) { + SmartDialog.showToast('资源获取失败'); + return; + } + final BangumiInfoModel bangumiDetail = result['data']; + final EpisodeItem episode = bangumiDetail.episodes!.first; + final int epId = episode.id!; + final int cid = episode.cid!; + final String bvid = episode.bvid!; + final String cover = episode.cover!; + final Map arguments = { + 'pic': cover, + 'videoType': SearchType.media_bangumi, + // 'bangumiItem': bangumiDetail, + }; + arguments['heroTag'] = heroTag ?? Utils.makeHeroTag(cid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid&epId=$epId', + arguments: arguments, + ); + } else { + SmartDialog.showToast(result['msg']); + } + } catch (e) { + SmartDialog.showToast('番剧获取失败:$e'); + } + } +} From b8efe249deae10161c0c6c5218bfa49f0e2c4bae Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 2 May 2024 23:33:58 +0800 Subject: [PATCH 166/349] =?UTF-8?q?opt:=20up=E4=B8=BB=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E7=95=AA=E5=89=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_h.dart | 17 ++++++++++++++ .../whisper_detail/widget/chat_item.dart | 11 ++------- lib/utils/app_scheme.dart | 23 +++++++------------ lib/utils/url_utils.dart | 2 +- lib/utils/utils.dart | 7 ++++++ 5 files changed, 35 insertions(+), 25 deletions(-) diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index df0c29b7..4c0729a3 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -1,7 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/http/constants.dart'; import 'package:pilipala/utils/image_save.dart'; +import 'package:pilipala/utils/route_push.dart'; +import 'package:pilipala/utils/url_utils.dart'; import '../../http/search.dart'; import '../../http/user.dart'; import '../../http/video.dart'; @@ -52,6 +55,20 @@ class VideoCardH extends StatelessWidget { SmartDialog.showToast('课堂视频暂不支持播放'); return; } + if (showCharge && videoItem?.typeid == 33) { + final String redirectUrl = await UrlUtils.parseRedirectUrl( + '${HttpString.baseUrl}/video/$bvid/'); + final String lastPathSegment = redirectUrl.split('/').last; + if (lastPathSegment.contains('ss')) { + RoutePush.bangumiPush( + Utils.matchNum(lastPathSegment).first, null); + } + if (lastPathSegment.contains('ep')) { + RoutePush.bangumiPush( + null, Utils.matchNum(lastPathSegment).first); + } + return; + } final int cid = videoItem.cid ?? await SearchHttp.ab2c(aid: aid, bvid: bvid); Get.toNamed('/video?bvid=$bvid&cid=$cid', diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 743cf901..ad11e4c3 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -50,13 +50,6 @@ class ChatItem extends StatelessWidget { this.e_infos, }); - static List matchNum(String str) { - final RegExp regExp = RegExp(r'\d+'); - final Iterable matches = regExp.allMatches(str); - - return matches.map((Match match) => int.parse(match.group(0)!)).toList(); - } - @override Widget build(BuildContext context) { bool isOwner = @@ -183,9 +176,9 @@ class ChatItem extends StatelessWidget { if (url != null) { final String area = url.split('/').last; if (area.startsWith('ep')) { - RoutePush.bangumiPush(null, matchNum(area).first); + RoutePush.bangumiPush(null, Utils.matchNum(area).first); } else if (area.startsWith('ss')) { - RoutePush.bangumiPush(matchNum(area).first, null); + RoutePush.bangumiPush(Utils.matchNum(area).first, null); } } } diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 5b77434d..fff78b39 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -144,15 +144,15 @@ class PiliSchame { _videoPush(null, lastPathSegment); } if (lastPathSegment.contains('av')) { - _videoPush(matchNum(lastPathSegment)[0], null); + _videoPush(Utils.matchNum(lastPathSegment)[0], null); } } if (path.startsWith('/bangumi')) { if (lastPathSegment.contains('ss')) { - RoutePush.bangumiPush(matchNum(lastPathSegment).first, null); + RoutePush.bangumiPush(Utils.matchNum(lastPathSegment).first, null); } if (lastPathSegment.contains('ep')) { - RoutePush.bangumiPush(null, matchNum(lastPathSegment).first); + RoutePush.bangumiPush(null, Utils.matchNum(lastPathSegment).first); } } } else if (host.contains('live')) { @@ -205,9 +205,9 @@ class PiliSchame { case 'bangumi': print('番剧'); if (area.startsWith('ep')) { - RoutePush.bangumiPush(null, matchNum(area).first); + RoutePush.bangumiPush(null, Utils.matchNum(area).first); } else if (area.startsWith('ss')) { - RoutePush.bangumiPush(matchNum(area).first, null); + RoutePush.bangumiPush(Utils.matchNum(area).first, null); } break; case 'video': @@ -223,7 +223,7 @@ class PiliSchame { break; case 'read': print('专栏'); - String id = 'cv${matchNum(query!['id']!).first}'; + String id = 'cv${Utils.matchNum(query!['id']!).first}'; Get.toNamed('/htmlRender', parameters: { 'url': value.dataString!, 'title': '', @@ -239,21 +239,14 @@ class PiliSchame { } } - static List matchNum(String str) { - final RegExp regExp = RegExp(r'\d+'); - final Iterable matches = regExp.allMatches(str); - - return matches.map((Match match) => int.parse(match.group(0)!)).toList(); - } - static void _handleEpisodePath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); - RoutePush.bangumiPush(null, matchNum(seasonId).first); + RoutePush.bangumiPush(null, Utils.matchNum(seasonId).first); } static void _handleSeasonPath(String lastPathSegment, String redirectUrl) { final String seasonId = _extractIdFromPath(lastPathSegment); - RoutePush.bangumiPush(matchNum(seasonId).first, null); + RoutePush.bangumiPush(Utils.matchNum(seasonId).first, null); } static String _extractIdFromPath(String lastPathSegment) { diff --git a/lib/utils/url_utils.dart b/lib/utils/url_utils.dart index 20f9cb0d..99886e09 100644 --- a/lib/utils/url_utils.dart +++ b/lib/utils/url_utils.dart @@ -16,7 +16,7 @@ class UrlUtils { }; try { final response = await dio.get(url); - if (response.statusCode == 302) { + if (response.statusCode == 302 || response.statusCode == 301) { redirectUrl = response.headers['location']?.first as String; if (redirectUrl.endsWith('/')) { redirectUrl = redirectUrl.substring(0, redirectUrl.length - 1); diff --git a/lib/utils/utils.dart b/lib/utils/utils.dart index b40e9bfe..e50c295c 100644 --- a/lib/utils/utils.dart +++ b/lib/utils/utils.dart @@ -383,4 +383,11 @@ class Utils { List randomBytes = generateRandomBytes(minLength, maxLength); return base64.encode(randomBytes); } + + static List matchNum(String str) { + final RegExp regExp = RegExp(r'\d+'); + final Iterable matches = regExp.allMatches(str); + + return matches.map((Match match) => int.parse(match.group(0)!)).toList(); + } } From b2eaec15b165187a201ddb8d1c0b17d948f025f7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 3 May 2024 15:58:54 +0800 Subject: [PATCH 167/349] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E5=8D=A1?= =?UTF-8?q?=E7=89=87=E5=8A=9F=E8=83=BD=E6=8B=93=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_h.dart | 218 +++++++++++++++------------ lib/common/widgets/video_card_v.dart | 10 ++ 2 files changed, 130 insertions(+), 98 deletions(-) diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 4c0729a3..1265477f 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/constants.dart'; +import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/image_save.dart'; import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/url_utils.dart'; @@ -276,115 +277,29 @@ class VideoContent extends StatelessWidget { theme: 'gray', danmu: videoItem.stat.danmaku as int, ), - const Spacer(), - // SizedBox( - // width: 20, - // height: 20, - // child: IconButton( - // tooltip: '稍后再看', - // style: ButtonStyle( - // padding: MaterialStateProperty.all(EdgeInsets.zero), - // ), - // onPressed: () async { - // var res = - // await UserHttp.toViewLater(bvid: videoItem.bvid); - // SmartDialog.showToast(res['msg']); - // }, - // icon: Icon( - // Icons.more_vert_outlined, - // color: Theme.of(context).colorScheme.outline, - // size: 14, - // ), - // ), - // ), if (source == 'normal') SizedBox( width: 24, height: 24, - child: PopupMenuButton( + child: IconButton( padding: EdgeInsets.zero, + onPressed: () { + feedBack(); + showModalBottomSheet( + context: context, + useRootNavigator: true, + isScrollControlled: true, + builder: (context) { + return MorePanel(videoItem: videoItem); + }, + ); + }, icon: Icon( Icons.more_vert_outlined, color: Theme.of(context).colorScheme.outline, size: 14, ), - position: PopupMenuPosition.under, - // constraints: const BoxConstraints(maxHeight: 35), - onSelected: (String type) {}, - itemBuilder: (BuildContext context) => - >[ - PopupMenuItem( - onTap: () async { - var res = await UserHttp.toViewLater( - bvid: videoItem.bvid as String); - SmartDialog.showToast(res['msg']); - }, - value: 'pause', - height: 40, - child: const Row( - children: [ - Icon(Icons.watch_later_outlined, size: 16), - SizedBox(width: 6), - Text('稍后再看', style: TextStyle(fontSize: 13)) - ], - ), - ), - const PopupMenuDivider(), - PopupMenuItem( - onTap: () async { - SmartDialog.show( - useSystem: true, - animationType: - SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: Text( - '确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' - '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: Text( - '点错了', - style: TextStyle( - color: Theme.of(context) - .colorScheme - .outline), - ), - ), - TextButton( - onPressed: () async { - var res = await VideoHttp.relationMod( - mid: videoItem.owner.mid, - act: 5, - reSrc: 11, - ); - SmartDialog.dismiss(); - SmartDialog.showToast(res['code'] == 0 - ? '成功' - : res['msg']); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); - }, - value: 'pause', - height: 40, - child: Row( - children: [ - const Icon(Icons.block, size: 16), - const SizedBox(width: 6), - Text('拉黑:${videoItem.owner.name}', - style: const TextStyle(fontSize: 13)) - ], - ), - ), - ], ), ), if (source == 'later') ...[ @@ -408,3 +323,110 @@ class VideoContent extends StatelessWidget { ); } } + +class MorePanel extends StatelessWidget { + final dynamic videoItem; + const MorePanel({super.key, required this.videoItem}); + + Future menuActionHandler(String type) async { + switch (type) { + case 'block': + blockUser(); + break; + case 'watchLater': + var res = await UserHttp.toViewLater(bvid: videoItem.bvid as String); + SmartDialog.showToast(res['msg']); + Get.back(); + break; + default: + } + } + + void blockUser() async { + SmartDialog.show( + useSystem: true, + animationType: SmartAnimationType.centerFade_otherSlide, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('提示'), + content: Text('确定拉黑:${videoItem.owner.name}(${videoItem.owner.mid})?' + '\n\n注:被拉黑的Up可以在隐私设置-黑名单管理中解除'), + actions: [ + TextButton( + onPressed: () => SmartDialog.dismiss(), + child: Text( + '点错了', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await VideoHttp.relationMod( + mid: videoItem.owner.mid, + act: 5, + reSrc: 11, + ); + SmartDialog.dismiss(); + SmartDialog.showToast(res['msg'] ?? '成功'); + }, + child: const Text('确认'), + ) + ], + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + return Container( + padding: EdgeInsets.only(bottom: MediaQuery.of(context).padding.bottom), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + InkWell( + onTap: () => Get.back(), + child: Container( + height: 35, + padding: const EdgeInsets.only(bottom: 2), + child: Center( + child: Container( + width: 32, + height: 3, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.outline, + borderRadius: const BorderRadius.all(Radius.circular(3))), + ), + ), + ), + ), + ListTile( + onTap: () async => await menuActionHandler('block'), + minLeadingWidth: 0, + leading: const Icon(Icons.block, size: 19), + title: Text( + '拉黑up主 「${videoItem.owner.name}」', + style: Theme.of(context).textTheme.titleSmall, + ), + ), + ListTile( + onTap: () async => await menuActionHandler('watchLater'), + minLeadingWidth: 0, + leading: const Icon(Icons.watch_later_outlined, size: 19), + title: + Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall), + ), + ListTile( + onTap: () => + imageSaveDialog(context, videoItem, SmartDialog.dismiss), + minLeadingWidth: 0, + leading: const Icon(Icons.photo_outlined, size: 19), + title: + Text('查看视频封面', style: Theme.of(context).textTheme.titleSmall), + ), + const SizedBox(height: 20), + ], + ), + ); + } +} diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 15fbc6fc..7b56152f 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -233,6 +233,7 @@ class VideoContent extends StatelessWidget { width: 24, height: 24, child: IconButton( + padding: EdgeInsets.zero, onPressed: () { feedBack(); showModalBottomSheet( @@ -386,6 +387,15 @@ class MorePanel extends StatelessWidget { title: Text('添加至稍后再看', style: Theme.of(context).textTheme.titleSmall), ), + ListTile( + onTap: () => + imageSaveDialog(context, videoItem, SmartDialog.dismiss), + minLeadingWidth: 0, + leading: const Icon(Icons.photo_outlined, size: 19), + title: + Text('查看视频封面', style: Theme.of(context).textTheme.titleSmall), + ), + const SizedBox(height: 20), ], ), ); From 81af23e870a4fcb5ff11ee2981c508d520548999 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 3 May 2024 21:04:03 +0800 Subject: [PATCH 168/349] mod: MainActivity --- android/app/src/main/AndroidManifest.xml | 2 +- .../kotlin/com/guozhigq/pilipala/MainActivity.kt | 6 ++++-- pubspec.lock | 14 +++++++------- pubspec.yaml | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c52d8447..f119eb1e 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -45,7 +45,7 @@ android:fullBackupContent="false" tools:replace="android:allowBackup"> Date: Fri, 3 May 2024 21:41:04 +0800 Subject: [PATCH 169/349] =?UTF-8?q?mod:=20=E5=BA=95=E9=83=A8=E5=AF=BC?= =?UTF-8?q?=E8=88=AA=E6=A0=8Fsdk=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 43bcf930..066bb599 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:flutter/services.dart'; import 'package:flutter_displaymode/flutter_displaymode.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -67,9 +68,8 @@ void main() async { // 小白条、导航栏沉浸 if (Platform.isAndroid) { - List versionParts = Platform.version.split('.'); - int androidVersion = int.parse(versionParts[0]); - if (androidVersion >= 29) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt >= 29) { SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); } SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( From a32be2c5c622f5f05544e2c6d214efa7bdf4efb7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 3 May 2024 21:42:01 +0800 Subject: [PATCH 170/349] =?UTF-8?q?fix:=20=E5=8D=8A=E5=B1=8F=E6=97=B6?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=AE=8C=E6=88=90=E7=8A=B6=E6=80=81=E6=A0=8F?= =?UTF-8?q?=E6=B6=88=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/controller.dart | 35 +--------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 4b6c44a1..2fc16924 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -978,41 +978,8 @@ class PlPlayerController { } else { await landScape(); } - - // bool isValid = - // direction.value == 'vertical' || mode == FullScreenMode.vertical - // ? true - // : false; - // var result = await showDialog( - // context: Get.context!, - // useSafeArea: false, - // builder: (context) => Dialog.fullscreen( - // backgroundColor: Colors.black, - // child: SafeArea( - // // 忽略手机安全区域 - // top: isValid, - // left: false, - // right: false, - // bottom: isValid, - // child: PLVideoPlayer( - // controller: this, - // headerControl: headerControl, - // bottomControl: bottomControl, - // danmuWidget: danmuWidget, - // ), - // ), - // ), - // ); - // if (result == null) { - // // 退出全屏 - // StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE); - // exitFullScreen(); - // await verticalScreen(); - // toggleFullScreen(false); - // } - } else if (isFullScreen.value) { + } else if (isFullScreen.value && !status) { StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE); - // Get.back(); exitFullScreen(); await verticalScreen(); toggleFullScreen(false); From dafbb838636298a75edd14d98e1079cf2f2a805e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 3 May 2024 22:58:14 +0800 Subject: [PATCH 171/349] =?UTF-8?q?opt:=20=E5=AA=92=E4=BD=93=E5=BA=93?= =?UTF-8?q?=E9=A1=B5=E9=9D=A2=E7=99=BB=E5=BD=95=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/dynamics.dart | 1 + lib/http/user.dart | 24 +++++++++++++++---- lib/pages/dynamics/controller.dart | 4 ++-- lib/pages/dynamics/view.dart | 32 ++++++++++++-------------- lib/pages/fav/controller.dart | 9 ++++++-- lib/pages/fav/view.dart | 19 +++++++++++---- lib/pages/history/controller.dart | 7 ++++++ lib/pages/history/view.dart | 19 +++++++++++---- lib/pages/later/controller.dart | 14 +++++++++++ lib/pages/later/view.dart | 21 ++++++++++++----- lib/pages/mine/controller.dart | 10 ++------ lib/pages/subscription/controller.dart | 9 ++++++-- lib/pages/subscription/view.dart | 15 ++++++++++-- lib/utils/route_push.dart | 24 +++++++++++++++++++ 14 files changed, 157 insertions(+), 51 deletions(-) diff --git a/lib/http/dynamics.dart b/lib/http/dynamics.dart index 63dea4ff..69619361 100644 --- a/lib/http/dynamics.dart +++ b/lib/http/dynamics.dart @@ -41,6 +41,7 @@ class DynamicsHttp { 'status': false, 'data': [], 'msg': res.data['message'], + 'code': res.data['code'], }; } } diff --git a/lib/http/user.dart b/lib/http/user.dart index dfdf187e..972acfdd 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -62,7 +62,8 @@ class UserHttp { return { 'status': false, 'data': [], - 'msg': res.data['message'] ?? '账号未登录' + 'msg': res.data['message'], + 'code': res.data['code'], }; } } @@ -111,7 +112,12 @@ class UserHttp { 'data': {'list': list, 'count': res.data['data']['count']} }; } else { - return {'status': false, 'data': [], 'msg': res.data['message']}; + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + 'code': res.data['code'], + }; } } @@ -126,7 +132,12 @@ class UserHttp { if (res.data['code'] == 0) { return {'status': true, 'data': HistoryData.fromJson(res.data['data'])}; } else { - return {'status': false, 'data': [], 'msg': res.data['message']}; + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + 'code': res.data['code'], + }; } } @@ -326,7 +337,12 @@ class UserHttp { 'data': SubFolderModelData.fromJson(res.data['data']) }; } else { - return {'status': false, 'msg': res.data['message']}; + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + 'code': res.data['code'], + }; } } diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 6814704c..9bed3685 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -71,7 +71,7 @@ class DynamicsController extends GetxController { Future queryFollowDynamic({type = 'init'}) async { if (!userLogin.value) { - return {'status': false, 'msg': '账号未登录'}; + return {'status': false, 'msg': '账号未登录', 'code': -101}; } if (type == 'init') { dynamicsList.clear(); @@ -229,7 +229,7 @@ class DynamicsController extends GetxController { Future queryFollowUp({type = 'init'}) async { if (!userLogin.value) { - return {'status': false, 'msg': '账号未登录'}; + return {'status': false, 'msg': '账号未登录', 'code': -101}; } if (type == 'init') { upData.value.upList = []; diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 82a555b1..da15239d 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -11,6 +11,7 @@ import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/main_stream.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; import '../mine/controller.dart'; @@ -224,8 +225,8 @@ class _DynamicsPageState extends State if (snapshot.data == null) { return const SliverToBoxAdapter(child: SizedBox()); } - Map data = snapshot.data; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { List list = _dynamicsController.dynamicsList; return Obx( @@ -248,24 +249,21 @@ class _DynamicsPageState extends State } }, ); - } else if (data['msg'] == "账号未登录") { - return HttpError( - errMsg: data['msg'], - btnText: "去登录", - fn: () { - mineController.onLogin(); - }, - ); } else { return HttpError( - errMsg: data['msg'], + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, fn: () { - setState(() { - _futureBuilderFuture = - _dynamicsController.queryFollowDynamic(); - _futureBuilderFutureUp = - _dynamicsController.queryFollowUp(); - }); + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _dynamicsController.queryFollowDynamic(); + _futureBuilderFutureUp = + _dynamicsController.queryFollowUp(); + }); + } }, ); } diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index 2307d303..8fcbf971 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -17,10 +17,15 @@ class FavController extends GetxController { int pageSize = 60; RxBool hasMore = true.obs; - Future queryFavFolder({type = 'init'}) async { + @override + void onInit() { userInfo = userInfoCache.get('userInfoCache'); + super.onInit(); + } + + Future queryFavFolder({type = 'init'}) async { if (userInfo == null) { - return {'status': false, 'msg': '账号未登录'}; + return {'status': false, 'msg': '账号未登录', 'code': -101}; } if (!hasMore.value) { return; diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index 424a885d..b8d37f50 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -4,6 +4,7 @@ import 'package:get/get.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/fav/index.dart'; import 'package:pilipala/pages/fav/widgets/item.dart'; +import 'package:pilipala/utils/route_push.dart'; class FavPage extends StatefulWidget { const FavPage({super.key}); @@ -57,8 +58,8 @@ class _FavPageState 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']) { return Obx( () => ListView.builder( controller: scrollController, @@ -74,8 +75,18 @@ class _FavPageState extends State { physics: const NeverScrollableScrollPhysics(), slivers: [ HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _favController.queryFavFolder(); + }); + } + }, ), ], ); diff --git a/lib/pages/history/controller.dart b/lib/pages/history/controller.dart index a1f18113..64953e3b 100644 --- a/lib/pages/history/controller.dart +++ b/lib/pages/history/controller.dart @@ -4,6 +4,7 @@ import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/user/history.dart'; +import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/utils/storage.dart'; class HistoryController extends GetxController { @@ -15,14 +16,20 @@ class HistoryController extends GetxController { RxBool isLoading = false.obs; RxBool enableMultiple = false.obs; RxInt checkedCount = 0.obs; + Box userInfoCache = GStrorage.userInfo; + UserInfoData? userInfo; @override void onInit() { super.onInit(); historyStatus(); + userInfo = userInfoCache.get('userInfoCache'); } Future queryHistoryList({type = 'init'}) async { + if (userInfo == null) { + return {'status': false, 'msg': '账号未登录', 'code': -101}; + } int max = 0; int viewAt = 0; if (type == 'onload') { diff --git a/lib/pages/history/view.dart b/lib/pages/history/view.dart index 92e1eee7..f9695aed 100644 --- a/lib/pages/history/view.dart +++ b/lib/pages/history/view.dart @@ -5,6 +5,7 @@ import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/pages/history/index.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'widgets/item.dart'; @@ -183,8 +184,8 @@ class _HistoryPageState extends State { if (snapshot.data == null) { return const SliverToBoxAdapter(child: SizedBox()); } - Map data = snapshot.data; - if (data['status']) { + Map? data = snapshot.data; + if (data != null && data['status']) { return Obx( () => _historyController.historyList.isNotEmpty ? SliverList( @@ -209,8 +210,18 @@ class _HistoryPageState extends State { ); } else { return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _historyController.queryHistoryList(); + }); + } + }, ); } } else { diff --git a/lib/pages/later/controller.dart b/lib/pages/later/controller.dart index 3de51901..f7dfea9f 100644 --- a/lib/pages/later/controller.dart +++ b/lib/pages/later/controller.dart @@ -1,16 +1,30 @@ 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/model_hot_video_item.dart'; +import 'package:pilipala/models/user/info.dart'; +import 'package:pilipala/utils/storage.dart'; class LaterController extends GetxController { final ScrollController scrollController = ScrollController(); RxList laterList = [].obs; int count = 0; RxBool isLoading = false.obs; + Box userInfoCache = GStrorage.userInfo; + UserInfoData? userInfo; + + @override + void onInit() { + super.onInit(); + userInfo = userInfoCache.get('userInfoCache'); + } Future queryLaterList() async { + if (userInfo == null) { + return {'status': false, 'msg': '账号未登录', 'code': -101}; + } isLoading.value = true; var res = await UserHttp.seeYouLater(); if (res['status']) { diff --git a/lib/pages/later/view.dart b/lib/pages/later/view.dart index 7c6e158d..0a02cb79 100644 --- a/lib/pages/later/view.dart +++ b/lib/pages/later/view.dart @@ -5,6 +5,7 @@ import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/pages/later/index.dart'; +import 'package:pilipala/utils/route_push.dart'; class LaterPage extends StatefulWidget { const LaterPage({super.key}); @@ -72,8 +73,8 @@ class _LaterPageState 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']) { return Obx( () => _laterController.laterList.isNotEmpty && !_laterController.isLoading.value @@ -96,10 +97,18 @@ class _LaterPageState extends State { ); } else { return HttpError( - errMsg: data['msg'], - fn: () => setState(() { - _futureBuilderFuture = _laterController.queryLaterList(); - }), + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _laterController.queryLaterList(); + }); + } + }, ); } } else { diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 5ad9e852..a61bb820 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -6,6 +6,7 @@ import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/theme_type.dart'; import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/models/user/stat.dart'; +import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; class MineController extends GetxController { @@ -33,14 +34,7 @@ class MineController extends GetxController { onLogin() async { if (!userLogin.value) { - Get.toNamed( - '/webview', - parameters: { - 'url': 'https://passport.bilibili.com/h5-app/passport/login', - 'type': 'login', - 'pageTitle': '登录bilibili', - }, - ); + RoutePush.loginPush(); // Get.toNamed('/loginPage'); } else { int mid = userInfo.value.mid!; diff --git a/lib/pages/subscription/controller.dart b/lib/pages/subscription/controller.dart index 7be8d22c..d8a76d44 100644 --- a/lib/pages/subscription/controller.dart +++ b/lib/pages/subscription/controller.dart @@ -17,10 +17,15 @@ class SubController extends GetxController { int pageSize = 20; RxBool hasMore = true.obs; - Future querySubFolder({type = 'init'}) async { + @override + void onInit() { + super.onInit(); userInfo = userInfoCache.get('userInfoCache'); + } + + Future querySubFolder({type = 'init'}) async { if (userInfo == null) { - return {'status': false, 'msg': '账号未登录'}; + return {'status': false, 'msg': '账号未登录', 'code': -101}; } var res = await UserHttp.userSubFolder( pn: currentPage, diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index 2d7d0cb5..5e6e4664 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -2,6 +2,7 @@ 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/utils/route_push.dart'; import 'controller.dart'; import 'widgets/item.dart'; @@ -68,8 +69,18 @@ class _SubPageState extends State { physics: const NeverScrollableScrollPhysics(), slivers: [ HttpError( - errMsg: data?['msg'], - fn: () => setState(() {}), + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () { + if (data?['code'] == -101) { + RoutePush.loginRedirectPush(); + } else { + setState(() { + _futureBuilderFuture = + _subController.querySubFolder(); + }); + } + }, ), ], ); diff --git a/lib/utils/route_push.dart b/lib/utils/route_push.dart index e00123b4..9ee28846 100644 --- a/lib/utils/route_push.dart +++ b/lib/utils/route_push.dart @@ -41,4 +41,28 @@ class RoutePush { SmartDialog.showToast('番剧获取失败:$e'); } } + + // 登录跳转 + static Future loginPush() async { + await Get.toNamed( + '/webview', + parameters: { + 'url': 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + } + + // 登录跳转 + static Future loginRedirectPush() async { + await Get.offAndToNamed( + '/webview', + parameters: { + 'url': 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + } } From 15f7c4a8021658bfb5012d14b9a04b94cc5ea24e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 4 May 2024 23:22:24 +0800 Subject: [PATCH 172/349] =?UTF-8?q?v1.0.23=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.23.0504.md | 14 ++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.23.0504.md diff --git a/change_log/1.0.23.0504.md b/change_log/1.0.23.0504.md new file mode 100644 index 00000000..afd401fa --- /dev/null +++ b/change_log/1.0.23.0504.md @@ -0,0 +1,14 @@ +## 1.0.23 + +### 功能 ++ 封面下载 + + +### 修复 ++ 全屏问题 ++ 视频播放器灰屏问题 ++ 评论区点击区域问题 + + +更多更新日志可在Github上查看 +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index fd34a8f6..a269cd6f 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.22+1022 +version: 1.0.23+1023 environment: sdk: ">=3.0.0 <4.0.0" From bb4325768f0e12d018e2d5ff33d434f056b35fed Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 5 May 2024 10:06:26 +0800 Subject: [PATCH 173/349] =?UTF-8?q?fix:=20=E8=A7=86=E9=A2=91=E6=92=AD?= =?UTF-8?q?=E6=94=BE=E5=99=A8=E5=B0=81=E9=9D=A2=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/network_img_layer.dart | 3 +++ lib/pages/video/detail/view.dart | 12 +++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index bda8ee5c..d2772478 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -34,6 +34,9 @@ class NetworkImgLayer extends StatelessWidget { @override Widget build(BuildContext context) { final int defaultImgQuality = GlobalData().imgQuality; + if (src == '' || src == null) { + return placeholder(context); + } final String imageUrl = '${src!.startsWith('//') ? 'https:${src!}' : src!}@${quality ?? defaultImgQuality}q.webp'; int? memCacheWidth, memCacheHeight; diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index bcacbbbb..2c35858f 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -323,11 +323,13 @@ class _VideoDetailPageState extends State children: [ GestureDetector( onTap: handlePlay, - child: Image.network( - vdCtr.videoItem['pic'], - width: Get.width, - height: videoHeight, - fit: BoxFit.cover, // 适应方式根据需要调整 + child: Obx( + () => NetworkImgLayer( + src: vdCtr.cover.value, + width: Get.width, + height: videoHeight, + type: 'emote', + ), ), ), buildCustomAppBar(), From 93d8e19a8ca1d74b7ff3eb6c389aa38201954dc0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 5 May 2024 17:00:35 +0800 Subject: [PATCH 174/349] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=E8=AF=A6=E6=83=85=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 2 +- lib/pages/dynamics/widgets/dynamic_panel.dart | 3 +-- lib/pages/dynamics/widgets/video_panel.dart | 13 +++++-------- 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index bd07921b..e83b5547 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -196,7 +196,7 @@ class _DynamicDetailPageState extends State centerTitle: false, titleSpacing: 0, title: StreamBuilder( - stream: titleStreamC.stream.distinct(), + stream: titleStreamC.stream, initialData: false, builder: (context, AsyncSnapshot snapshot) { return AnimatedOpacity( diff --git a/lib/pages/dynamics/widgets/dynamic_panel.dart b/lib/pages/dynamics/widgets/dynamic_panel.dart index d273a1a6..cc5dcbb1 100644 --- a/lib/pages/dynamics/widgets/dynamic_panel.dart +++ b/lib/pages/dynamics/widgets/dynamic_panel.dart @@ -1,14 +1,13 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/pages/dynamics/index.dart'; -import '../../../models/dynamics/result.dart'; import 'action_panel.dart'; import 'author_panel.dart'; import 'content_panel.dart'; import 'forward_panel.dart'; class DynamicPanel extends StatelessWidget { - final DynamicItemModel item; + final dynamic item; final String? source; DynamicPanel({required this.item, this.source, Key? key}) : super(key: key); final DynamicsController _dynamicsController = Get.put(DynamicsController()); diff --git a/lib/pages/dynamics/widgets/video_panel.dart b/lib/pages/dynamics/widgets/video_panel.dart index 32a6e21c..828fb283 100644 --- a/lib/pages/dynamics/widgets/video_panel.dart +++ b/lib/pages/dynamics/widgets/video_panel.dart @@ -80,14 +80,11 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) { double width = box.maxWidth; return Stack( children: [ - Hero( - tag: content.bvid, - child: NetworkImgLayer( - type: floor == 1 ? 'emote' : null, - width: width, - height: width / StyleString.aspectRatio, - src: content.cover, - ), + NetworkImgLayer( + type: floor == 1 ? 'emote' : null, + width: width, + height: width / StyleString.aspectRatio, + src: content.cover, ), if (content.badge != null && type == 'pgc') PBadge( From e65389040f5be4a628928502f6488bd759035532 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 5 May 2024 17:53:54 +0800 Subject: [PATCH 175/349] =?UTF-8?q?feat:=20=E6=90=9C=E7=B4=A2=E8=AE=A1?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 ++ lib/http/search.dart | 24 +++++++++ lib/models/search/all.dart | 9 ++++ lib/pages/search_result/controller.dart | 19 +++++++ lib/pages/search_result/view.dart | 68 +++++++++++++------------ 5 files changed, 91 insertions(+), 32 deletions(-) create mode 100644 lib/models/search/all.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 1735902c..42fc03b8 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -520,4 +520,7 @@ class Api { /// 删除收藏夹 static const String delFavFolder = '/x/v3/fav/folder/del'; + + /// 搜索结果计数 + static const String searchCount = '/x/web-interface/wbi/search/all/v2'; } diff --git a/lib/http/search.dart b/lib/http/search.dart index cf1a1b49..9b493a84 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -1,5 +1,7 @@ import 'dart:convert'; import 'package:hive/hive.dart'; +import 'package:pilipala/models/search/all.dart'; +import 'package:pilipala/utils/wbi_sign.dart'; import '../models/bangumi/info.dart'; import '../models/common/search_type.dart'; import '../models/search/hot.dart'; @@ -179,4 +181,26 @@ class SearchHttp { 'pic': res.data['data'].first['first_frame'], }; } + + static Future> searchCount( + {required String keyword}) async { + Map data = { + 'keyword': keyword, + 'web_location': 333.999, + }; + Map params = await WbiSign().makSign(data); + final dynamic res = await Request().get(Api.searchCount, data: params); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': SearchAllModel.fromJson(res.data['data']), + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': '请求错误 🙅', + }; + } + } } diff --git a/lib/models/search/all.dart b/lib/models/search/all.dart new file mode 100644 index 00000000..796400da --- /dev/null +++ b/lib/models/search/all.dart @@ -0,0 +1,9 @@ +class SearchAllModel { + SearchAllModel({this.topTList}); + + Map? topTList; + + SearchAllModel.fromJson(Map json) { + topTList = json['top_tlist']; + } +} diff --git a/lib/pages/search_result/controller.dart b/lib/pages/search_result/controller.dart index 9914d82b..02a9e63b 100644 --- a/lib/pages/search_result/controller.dart +++ b/lib/pages/search_result/controller.dart @@ -1,8 +1,11 @@ import 'package:get/get.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/common/search_type.dart'; class SearchResultController extends GetxController { String? keyword; int tabIndex = 0; + RxList searchTabs = [].obs; @override void onInit() { @@ -10,5 +13,21 @@ class SearchResultController extends GetxController { if (Get.parameters.keys.isNotEmpty) { keyword = Get.parameters['keyword']; } + searchTabs.value = SearchType.values + .map((type) => {'label': type.label, 'id': type.type}) + .toList(); + querySearchCount(); + } + + Future querySearchCount() async { + var result = await SearchHttp.searchCount(keyword: keyword!); + if (result['status']) { + for (var i in searchTabs) { + final count = result['data'].topTList[i['id']]; + i['count'] = count > 99 ? '99+' : count.toString(); + } + searchTabs.refresh(); + } + return result; } } diff --git a/lib/pages/search_result/view.dart b/lib/pages/search_result/view.dart index ff5bf780..96fdd91d 100644 --- a/lib/pages/search_result/view.dart +++ b/lib/pages/search_result/view.dart @@ -13,7 +13,7 @@ class SearchResultPage extends StatefulWidget { class _SearchResultPageState extends State with TickerProviderStateMixin { - late SearchResultController? _searchResultController; + late SearchResultController _searchResultController; late TabController? _tabController; @override @@ -25,7 +25,7 @@ class _SearchResultPageState extends State _tabController = TabController( vsync: this, length: SearchType.values.length, - initialIndex: _searchResultController!.tabIndex, + initialIndex: _searchResultController.tabIndex, ); } @@ -46,7 +46,7 @@ class _SearchResultPageState extends State child: SizedBox( width: double.infinity, child: Text( - '${_searchResultController!.keyword}', + '${_searchResultController.keyword}', style: Theme.of(context).textTheme.titleMedium, ), ), @@ -64,35 +64,39 @@ class _SearchResultPageState extends State splashColor: Colors.transparent, // 点击时的水波纹颜色设置为透明 highlightColor: Colors.transparent, // 点击时的背景高亮颜色设置为透明 ), - child: TabBar( - controller: _tabController, - tabs: [ - for (var i in SearchType.values) Tab(text: i.label), - ], - isScrollable: true, - indicatorWeight: 0, - indicatorPadding: - const EdgeInsets.symmetric(horizontal: 3, vertical: 8), - indicator: BoxDecoration( - color: Theme.of(context).colorScheme.secondaryContainer, - borderRadius: const BorderRadius.all(Radius.circular(20)), - ), - indicatorSize: TabBarIndicatorSize.tab, - labelColor: Theme.of(context).colorScheme.onSecondaryContainer, - labelStyle: const TextStyle(fontSize: 13), - dividerColor: Colors.transparent, - unselectedLabelColor: Theme.of(context).colorScheme.outline, - tabAlignment: TabAlignment.start, - onTap: (index) { - if (index == _searchResultController!.tabIndex) { - Get.find( - tag: SearchType.values[index].type + - _searchResultController!.keyword!) - .animateToTop(); - } + child: Obx( + () => (TabBar( + controller: _tabController, + tabs: [ + for (var i in _searchResultController.searchTabs) + Tab(text: "${i['label']} ${i['count'] ?? ''}") + ], + isScrollable: true, + indicatorWeight: 0, + indicatorPadding: + const EdgeInsets.symmetric(horizontal: 3, vertical: 8), + indicator: BoxDecoration( + color: Theme.of(context).colorScheme.secondaryContainer, + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + indicatorSize: TabBarIndicatorSize.tab, + labelColor: + Theme.of(context).colorScheme.onSecondaryContainer, + labelStyle: const TextStyle(fontSize: 13), + dividerColor: Colors.transparent, + unselectedLabelColor: Theme.of(context).colorScheme.outline, + tabAlignment: TabAlignment.start, + onTap: (index) { + if (index == _searchResultController.tabIndex) { + Get.find( + tag: SearchType.values[index].type + + _searchResultController.keyword!) + .animateToTop(); + } - _searchResultController!.tabIndex = index; - }, + _searchResultController.tabIndex = index; + }, + )), ), ), ), @@ -102,7 +106,7 @@ class _SearchResultPageState extends State children: [ for (var i in SearchType.values) ...{ SearchPanel( - keyword: _searchResultController!.keyword, + keyword: _searchResultController.keyword, searchType: i, tag: DateTime.now().millisecondsSinceEpoch.toString(), ) From db824681fd6a8d9b53df047315e169c9df5f33df Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 5 May 2024 21:00:12 +0800 Subject: [PATCH 176/349] =?UTF-8?q?fix:=20=E9=80=80=E5=87=BA=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E6=8C=89=E9=92=AE=E6=97=A0=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 6a5f22ec..d359391e 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -334,7 +334,7 @@ class _PLVideoPlayerState extends State color: Colors.white, ), ), - fuc: () => _.triggerFullScreen(), + fuc: () => _.triggerFullScreen(status: !_.isFullScreen.value), ), }; final List list = []; From 0162b9614e278fabd83066f35823d259f28db733 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 6 May 2024 23:14:12 +0800 Subject: [PATCH 177/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index ef4cd15c..b5972ed8 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -12,6 +12,7 @@ import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; @@ -642,23 +643,25 @@ 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; - if (lastPathSegment.startsWith('BV')) { + final String pathSegment = Uri.parse(matchStr).path; + Map matchRes = IdUtils.matchAvorBv(input: pathSegment); + List matchKeys = matchRes.keys.toList(); + if (matchKeys.isNotEmpty) { UrlUtils.matchUrlPush( - lastPathSegment, + matchRes.containsKey('AV') + ? matchRes['AV']! as int + : matchRes['BV'], title, - redirectUrl, + matchStr, ); } else { + final String redirectUrl = + await UrlUtils.parseRedirectUrl(matchStr); + if (redirectUrl == matchStr) { + Clipboard.setData(ClipboardData(text: matchStr)); + SmartDialog.showToast('地址可能有误'); + return; + } Get.toNamed( '/webview', parameters: { From 2fefe325877f12bb99cbf29f0dcf9f334798b8de Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 6 May 2024 23:24:19 +0800 Subject: [PATCH 178/349] =?UTF-8?q?mod:=20=E9=BB=98=E8=AE=A4=E4=B8=8D?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=8D=87=E7=BA=A7=E6=A0=87=E8=AF=86?= 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 b381691a..81d3c3f4 100644 --- a/lib/pages/about/index.dart +++ b/lib/pages/about/index.dart @@ -218,7 +218,7 @@ class AboutController extends GetxController { RxString currentVersion = ''.obs; RxString remoteVersion = ''.obs; late LatestDataModel remoteAppInfo; - RxBool isUpdate = true.obs; + RxBool isUpdate = false.obs; RxBool isLoading = true.obs; late LatestDataModel data; From 4f10e6a869d1d61ea10343a00721d259a30572e7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 7 May 2024 00:01:44 +0800 Subject: [PATCH 179/349] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=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/pages/main/controller.dart | 2 +- lib/pages/main/view.dart | 64 +++++++++++++++++----------------- 2 files changed, 33 insertions(+), 33 deletions(-) diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index a77d9304..2ef2bf7f 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -40,10 +40,10 @@ class MainController extends GetxController { dynamicBadgeType.value = DynamicBadgeMode.values[setting.get( SettingBoxKey.dynamicBadgeMode, defaultValue: DynamicBadgeMode.number.code)]; + setNavBarConfig(); if (dynamicBadgeType.value != DynamicBadgeMode.hidden) { getUnreadDynamic(); } - setNavBarConfig(); } void onBackPressed(BuildContext context) { diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 617fcf83..29573501 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -138,14 +138,14 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { duration: const Duration(milliseconds: 500), offset: Offset(0, snapshot.data ? 0 : 1), child: GlobalData().enableMYBar - ? NavigationBar( - onDestinationSelected: (value) => setIndex(value), - selectedIndex: _mainController.selectedIndex, - destinations: [ - ..._mainController.navigationBars.map((e) { - return NavigationDestination( - icon: Obx( - () => Badge( + ? Obx( + () => NavigationBar( + onDestinationSelected: (value) => setIndex(value), + selectedIndex: _mainController.selectedIndex, + destinations: [ + ..._mainController.navigationBars.map((e) { + return NavigationDestination( + icon: Badge( label: _mainController .dynamicBadgeType.value == DynamicBadgeMode.number @@ -159,25 +159,25 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { e['count'] > 0, child: e['icon'], ), - ), - selectedIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], + selectedIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ), ) - : BottomNavigationBar( - currentIndex: _mainController.selectedIndex, - type: BottomNavigationBarType.fixed, - onTap: (value) => setIndex(value), - iconSize: 16, - selectedFontSize: 12, - unselectedFontSize: 12, - items: [ - ..._mainController.navigationBars.map((e) { - return BottomNavigationBarItem( - icon: Obx( - () => Badge( + : Obx( + () => BottomNavigationBar( + currentIndex: _mainController.selectedIndex, + type: BottomNavigationBarType.fixed, + onTap: (value) => setIndex(value), + iconSize: 16, + selectedFontSize: 12, + unselectedFontSize: 12, + items: [ + ..._mainController.navigationBars.map((e) { + return BottomNavigationBarItem( + icon: Badge( label: _mainController .dynamicBadgeType.value == DynamicBadgeMode.number @@ -191,12 +191,12 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { e['count'] > 0, child: e['icon'], ), - ), - activeIcon: e['selectIcon'], - label: e['label'], - ); - }).toList(), - ], + activeIcon: e['selectIcon'], + label: e['label'], + ); + }).toList(), + ], + ), ), ); }, From 7ff6884342be8d0bd54feed50c8fd8f9c3410717 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 7 May 2024 23:10:27 +0800 Subject: [PATCH 180/349] =?UTF-8?q?fix:=20pathSegments=E8=B6=8A=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/webview/controller.dart | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index f26f4284..40f84ff6 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -52,15 +52,19 @@ class WebviewController extends GetxController { loadProgress.value = progress; }, onPageStarted: (String url) { - final String str = Uri.parse(url).pathSegments[0]; - final Map matchRes = IdUtils.matchAvorBv(input: str); - final List matchKeys = matchRes.keys.toList(); - if (matchKeys.isNotEmpty) { - if (matchKeys.first == 'BV') { - Get.offAndToNamed( - '/searchResult', - parameters: {'keyword': matchRes['BV']}, - ); + final List pathSegments = Uri.parse(url).pathSegments; + if (pathSegments.isNotEmpty && + url != 'https://passport.bilibili.com/h5-app/passport/login') { + final String str = pathSegments[0]; + final Map matchRes = IdUtils.matchAvorBv(input: str); + final List matchKeys = matchRes.keys.toList(); + if (matchKeys.isNotEmpty) { + if (matchKeys.first == 'BV') { + Get.offAndToNamed( + '/searchResult', + parameters: {'keyword': matchRes['BV']}, + ); + } } } }, From c2a176ad2fd5d20e4ad4aed1f68102b5425b45c2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 7 May 2024 23:53:31 +0800 Subject: [PATCH 181/349] fix: issues #718 --- lib/services/audio_handler.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index b0ca8cd7..bf98298b 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -26,7 +26,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { static final List _item = []; Box setting = GStrorage.setting; bool enableBackgroundPlay = false; - PlPlayerController player = PlPlayerController.getInstance(videoType: 'none'); + PlPlayerController player = PlPlayerController.getInstance(); VideoPlayerServiceHandler() { revalidateSetting(); From 250131d2b0925fef686eb4634657bd80eab085bb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 8 May 2024 22:42:18 +0800 Subject: [PATCH 182/349] =?UTF-8?q?fix:=20=E5=88=86p=E5=AD=97=E5=B9=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/controller.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 2ced79f0..9ed04870 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -450,6 +450,7 @@ class VideoIntroController extends GetxController { videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); + videoDetailCtr.getSubtitle(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 From 6380c138e8f0e2c85f6ae73b8b24deccd6d78b33 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 8 May 2024 23:19:02 +0800 Subject: [PATCH 183/349] =?UTF-8?q?typo:=20=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/interceptor.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart index a5359283..10b5aee5 100644 --- a/lib/http/interceptor.dart +++ b/lib/http/interceptor.dart @@ -46,7 +46,7 @@ class ApiInterceptor extends Interceptor { // 处理网络请求错误 // handler.next(err); String url = err.requestOptions.uri.toString(); - if (!url.contains('heartBeat')) { + if (!url.contains('heartbeat')) { SmartDialog.showToast( await dioError(err), displayType: SmartToastType.onlyRefresh, From 5807999296c0806309e3dd809c068764147ee9be Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 8 May 2024 23:19:29 +0800 Subject: [PATCH 184/349] =?UTF-8?q?mod:=20scheme=20BVAV=E5=8C=B9=E9=85=8D?= =?UTF-8?q?=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index fff78b39..8502dc7f 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -4,7 +4,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/utils/route_push.dart'; import '../http/search.dart'; -import '../models/common/search_type.dart'; import 'id_utils.dart'; import 'url_utils.dart'; import 'utils.dart'; @@ -140,11 +139,13 @@ class PiliSchame { print('bilibili.com path: $path'); final String lastPathSegment = path!.split('/').last; if (path.startsWith('/video')) { - if (lastPathSegment.contains('BV')) { - _videoPush(null, lastPathSegment); - } - if (lastPathSegment.contains('av')) { - _videoPush(Utils.matchNum(lastPathSegment)[0], null); + Map matchRes = IdUtils.matchAvorBv(input: path); + if (matchRes.containsKey('AV')) { + _videoPush(matchRes['AV']! as int, null); + } else if (matchRes.containsKey('BV')) { + _videoPush(null, matchRes['BV'] as String); + } else { + SmartDialog.showToast('投稿匹配失败'); } } if (path.startsWith('/bangumi')) { From 37654c5eab465672eba646855431eba229ed7f2e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 9 May 2024 00:00:59 +0800 Subject: [PATCH 185/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index b5972ed8..4ff13065 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -657,11 +657,11 @@ InlineSpan buildContent( } else { final String redirectUrl = await UrlUtils.parseRedirectUrl(matchStr); - if (redirectUrl == matchStr) { - Clipboard.setData(ClipboardData(text: matchStr)); - SmartDialog.showToast('地址可能有误'); - return; - } + // if (redirectUrl == matchStr) { + // Clipboard.setData(ClipboardData(text: matchStr)); + // SmartDialog.showToast('地址可能有误'); + // return; + // } Get.toNamed( '/webview', parameters: { From b8e3764345aaf66e2cbad0c73f8d5d2ce2e1f8d0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 10 May 2024 23:45:51 +0800 Subject: [PATCH 186/349] fix: login Box has already been closed --- lib/pages/webview/controller.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index 40f84ff6..bdacc652 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -110,6 +110,9 @@ class WebviewController extends GetxController { SmartDialog.showToast('登录成功'); try { Box userInfoCache = GStrorage.userInfo; + if (!userInfoCache.isOpen) { + userInfoCache = await Hive.openBox('userInfo'); + } await userInfoCache.put('userInfoCache', result['data']); final HomeController homeCtr = Get.find(); From 14b7ac438f25d97711a70301eb5ded8ad455da5a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 11 May 2024 23:50:41 +0800 Subject: [PATCH 187/349] =?UTF-8?q?fix:=20=E7=9B=B4=E6=92=AD=E5=85=A8?= =?UTF-8?q?=E5=B1=8F=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/widgets/bottom_control.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index e5a9d6c9..4dd7c538 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -153,7 +153,8 @@ class _BottomControlState extends State { size: 20, color: Colors.white, ), - fuc: () => widget.controller!.triggerFullScreen(), + fuc: () => widget.controller!.triggerFullScreen( + status: !(widget.controller!.isFullScreen.value)), ), ], ), From 79bde1b7ff03158a36d18fd8885bc3fa080fd3b2 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 13 May 2024 23:16:25 +0800 Subject: [PATCH 188/349] =?UTF-8?q?mod:=20=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E9=95=BF=E6=8C=89=E5=9C=86=E8=A7=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/history/widgets/item.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index ba5f6b5c..00ebd511 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -198,7 +198,8 @@ class HistoryItem extends StatelessWidget { duration: const Duration(milliseconds: 200), child: Container( decoration: BoxDecoration( - borderRadius: BorderRadius.circular(12), + borderRadius: BorderRadius.circular( + StyleString.imgRadius.x), color: Colors.black.withOpacity( ctr!.enableMultiple.value && videoItem.checked From 68d35a7ed8830c4b352389dd59c4aa60d335ac34 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 13 May 2024 23:37:22 +0800 Subject: [PATCH 189/349] =?UTF-8?q?feat:=20=E6=90=9C=E7=B4=A2=E5=88=86?= =?UTF-8?q?=E5=8C=BAUI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 +- lib/http/search.dart | 2 + lib/pages/search/widgets/search_text.dart | 7 +- lib/pages/search_panel/controller.dart | 14 +- .../search_panel/widgets/video_panel.dart | 125 +++++++++++++++++- 5 files changed, 142 insertions(+), 8 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 42fc03b8..77c168d0 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -189,7 +189,7 @@ class Api { 'https://s.search.bilibili.com/main/suggest'; // 分类搜索 - static const String searchByType = '/x/web-interface/search/type'; + static const String searchByType = '/x/web-interface/wbi/search/type'; // 记录视频播放进度 // https://github.com/SocialSisterYi/bilibili-API-collect/blob/master/docs/video/report.md diff --git a/lib/http/search.dart b/lib/http/search.dart index 9b493a84..70980547 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -75,6 +75,7 @@ class SearchHttp { required page, String? order, int? duration, + int? tid, }) async { var reqData = { 'search_type': searchType.type, @@ -84,6 +85,7 @@ class SearchHttp { 'page': page, if (order != null) 'order': order, if (duration != null) 'duration': duration, + if (tid != null) 'tid': tid, }; var res = await Request().get(Api.searchByType, data: reqData); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 039a851b..5e91a4cd 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -5,12 +5,14 @@ class SearchText extends StatelessWidget { final Function? onSelect; final int? searchTextIdx; final Function? onLongSelect; + final bool isSelect; const SearchText({ super.key, this.searchText, this.onSelect, this.searchTextIdx, this.onLongSelect, + this.isSelect = false, }); @override @@ -34,7 +36,10 @@ class SearchText extends StatelessWidget { child: Text( searchText!, style: TextStyle( - color: Theme.of(context).colorScheme.onSurfaceVariant), + color: isSelect + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.onSurfaceVariant, + ), ), ), ), diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 85952e90..56d83601 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -16,14 +16,18 @@ class SearchPanelController extends GetxController { RxString order = ''.obs; // 视频时长筛选 仅用于搜索视频 RxInt duration = 0.obs; + // 视频分区筛选 仅用于搜索视频 + RxInt tid = (-1).obs; Future onSearch({type = 'init'}) async { var result = await SearchHttp.searchByType( - searchType: searchType!, - keyword: keyword!, - page: page.value, - order: searchType!.type != 'video' ? null : order.value, - duration: searchType!.type != 'video' ? null : duration.value); + searchType: searchType!, + keyword: keyword!, + page: page.value, + order: searchType!.type != 'video' ? null : order.value, + duration: searchType!.type != 'video' ? null : duration.value, + tid: searchType!.type != 'video' ? null : tid.value, + ); if (result['status']) { if (type == 'onRefresh') { resultList.value = result['data'].list; diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index c24a007c..06ee5d4b 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -3,6 +3,7 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/models/common/search_type.dart'; +import 'package:pilipala/pages/search/widgets/search_text.dart'; import 'package:pilipala/pages/search_panel/index.dart'; class SearchVideoPanel extends StatelessWidget { @@ -94,7 +95,7 @@ class SearchVideoPanel extends StatelessWidget { style: ButtonStyle( padding: MaterialStateProperty.all(EdgeInsets.zero), ), - onPressed: () => controller.onShowFilterDialog(ctr), + onPressed: () => controller.onShowFilterSheet(ctr), icon: Icon( Icons.filter_list_outlined, size: 18, @@ -165,7 +166,33 @@ class VideoPanelController extends GetxController { {'label': '30-60分钟', 'value': 3}, {'label': '60分钟+', 'value': 4}, ]; + List> partFiltersList = [ + {'label': '全部', 'value': -1}, + {'label': '动画', 'value': 1}, + {'label': '番剧', 'value': 13}, + {'label': '国创', 'value': 167}, + {'label': '音乐', 'value': 3}, + {'label': '舞蹈', 'value': 129}, + {'label': '游戏', 'value': 4}, + {'label': '知识', 'value': 36}, + {'label': '科技', 'value': 188}, + {'label': '运动', 'value': 234}, + {'label': '汽车', 'value': 223}, + {'label': '生活', 'value': 160}, + {'label': '美食', 'value': 211}, + {'label': '动物', 'value': 217}, + {'label': '鬼畜', 'value': 119}, + {'label': '时尚', 'value': 155}, + {'label': '资讯', 'value': 202}, + {'label': '娱乐', 'value': 5}, + {'label': '影视', 'value': 181}, + {'label': '记录', 'value': 177}, + {'label': '电影', 'value': 23}, + {'label': '电视', 'value': 11}, + ]; + RxInt currentTimeFilterval = 0.obs; + RxInt currentPartFilterval = (-1).obs; @override void onInit() { @@ -219,4 +246,100 @@ class VideoPanelController extends GetxController { }, ); } + + onShowFilterSheet(searchPanelCtr) { + showModalBottomSheet( + context: Get.context!, + builder: (context) { + return StatefulBuilder( + builder: (context, StateSetter setState) { + return Container( + color: Theme.of(Get.context!).colorScheme.surface, + padding: const EdgeInsets.only(top: 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ListTile( + title: Text('内容时长'), + ), + Padding( + padding: const EdgeInsets.only( + left: 14, + right: 14, + bottom: 14, + ), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in timeFiltersList) + Obx( + () => SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: + currentTimeFilterval.value == i['value'], + onSelect: (value) async { + currentTimeFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast("「${i['label']}」的筛选结果"); + SearchPanelController ctr = + Get.find( + tag: 'video${searchPanelCtr.keyword!}'); + ctr.duration.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ), + ) + ], + ), + ), + const ListTile( + title: Text('内容分区'), + ), + Padding( + padding: const EdgeInsets.only(left: 14, right: 14), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in partFiltersList) + SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: currentPartFilterval.value == i['value'], + onSelect: (value) async { + currentPartFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast("「${i['label']}」的筛选结果"); + SearchPanelController ctr = + Get.find( + tag: 'video${searchPanelCtr.keyword!}'); + ctr.tid.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ) + ], + ), + ) + ], + ), + ); + }, + ); + }, + ); + } } From 31e8ee8d34023b6d541088836432eba556123853 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 13 May 2024 23:55:17 +0800 Subject: [PATCH 190/349] =?UTF-8?q?fix:=20=E7=95=AA=E5=89=A7=E6=97=A0?= =?UTF-8?q?=E8=AF=84=E5=88=86=E5=AF=BC=E8=87=B4=E7=9A=84=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/introduction/view.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 6876da79..95d4d898 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -194,7 +194,8 @@ class _BangumiInfoState extends State { src: widget.bangumiDetail!.cover!, ), PBadge( - text: '评分 ${widget.bangumiDetail!.rating!['score']!}', + text: + '评分 ${widget.bangumiDetail?.rating?['score']! ?? '暂无'}', top: null, right: 6, bottom: 6, From 44f2a168ce79b62c315f5706152240c3396a1562 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 15 May 2024 23:55:04 +0800 Subject: [PATCH 191/349] =?UTF-8?q?opt:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E5=8D=95=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/controller.dart | 3 +-- lib/pages/video/detail/controller.dart | 2 +- lib/plugin/pl_player/controller.dart | 19 +++++++++---------- lib/plugin/pl_player/view.dart | 10 +++++----- lib/services/audio_handler.dart | 2 +- lib/services/audio_session.dart | 4 ++-- lib/services/shutdown_timer_service.dart | 4 ++-- 7 files changed, 21 insertions(+), 23 deletions(-) diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 5c2a9800..4e67fa2c 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -17,8 +17,7 @@ class LiveRoomController extends GetxController { double volume = 0.0; // 静音状态 RxBool volumeOff = false.obs; - PlPlayerController plPlayerController = - PlPlayerController.getInstance(videoType: 'live'); + PlPlayerController plPlayerController = PlPlayerController(videoType: 'live'); Rx roomInfoH5 = RoomInfoH5Model().obs; late bool enableCDN; late int currentQn; diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index f7f9e848..722af317 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -74,7 +74,7 @@ class VideoDetailController extends GetxController final scaffoldKey = GlobalKey(); RxString bgCover = ''.obs; RxString cover = ''.obs; - PlPlayerController plPlayerController = PlPlayerController.getInstance(); + PlPlayerController plPlayerController = PlPlayerController(); late VideoItem firstVideo; late AudioItem firstAudio; diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 93d67703..03c2efbe 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -123,6 +123,7 @@ class PlPlayerController { PreferredSizeWidget? bottomControl; Widget? danmuWidget; late RxList subtitles; + String videoType = 'archive'; /// 数据加载监听 Stream get onDataStatusChanged => dataStatus.status.stream; @@ -220,7 +221,7 @@ class PlPlayerController { Rx get playerCount => _playerCount; /// - Rx get videoType => _videoType; + // Rx get videoType => _videoType; /// 弹幕开关 Rx isOpenDanmu = false.obs; @@ -274,8 +275,7 @@ class PlPlayerController { } // 添加一个私有构造函数 - PlPlayerController._() { - _videoType = videoType; + PlPlayerController._internal(this.videoType) { isOpenDanmu.value = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); blockTypes = @@ -330,11 +330,11 @@ class PlPlayerController { } // 获取实例 传参 - static PlPlayerController getInstance({ + factory PlPlayerController({ String videoType = 'archive', }) { // 如果实例尚未创建,则创建一个新实例 - _instance ??= PlPlayerController._(); + _instance ??= PlPlayerController._internal(videoType); if (videoType != 'none') { _instance!._playerCount.value += 1; _videoType.value = videoType; @@ -443,7 +443,7 @@ class PlPlayerController { configuration: PlayerConfiguration( // 默认缓存 5M 大小 bufferSize: - videoType.value == 'live' ? 32 * 1024 * 1024 : 5 * 1024 * 1024, + videoType == 'live' ? 32 * 1024 * 1024 : 5 * 1024 * 1024, ), ); @@ -541,7 +541,7 @@ class PlPlayerController { } /// 设置倍速 - if (videoType.value == 'live') { + if (videoType == 'live') { await setPlaybackSpeed(1.0); } else { if (_playbackSpeed.value != 1.0) { @@ -933,7 +933,7 @@ class PlPlayerController { /// 设置长按倍速状态 live模式下禁用 void setDoubleSpeedStatus(bool val) { - if (videoType.value == 'live') { + if (videoType == 'live') { return; } if (controlsLock.value) { @@ -1015,7 +1015,7 @@ class PlPlayerController { if (!_enableHeart) { return false; } - if (videoType.value == 'live') { + if (videoType == 'live') { return; } // 播放状态变化时,更新 @@ -1114,7 +1114,6 @@ class PlPlayerController { // _buffered.close(); // _showControls.close(); // _controlsLock.close(); - // playerStatus.status.close(); // dataStatus.status.close(); diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index d359391e..cff4611a 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -652,7 +652,7 @@ class _PLVideoPlayerState extends State }, onDoubleTapDown: (TapDownDetails details) { // live模式下禁用 锁定时🔒禁用 - if (_.videoType.value == 'live' || _.controlsLock.value) { + if (_.videoType == 'live' || _.controlsLock.value) { return; } final double totalWidth = MediaQuery.sizeOf(context).width; @@ -679,7 +679,7 @@ class _PLVideoPlayerState extends State /// 水平位置 快进 live模式下禁用 onHorizontalDragUpdate: (DragUpdateDetails details) { // live模式下禁用 锁定时🔒禁用 - if (_.videoType.value == 'live' || _.controlsLock.value) { + if (_.videoType == 'live' || _.controlsLock.value) { return; } // final double tapPosition = details.localPosition.dx; @@ -695,7 +695,7 @@ class _PLVideoPlayerState extends State _.onChangedSliderStart(); }, onHorizontalDragEnd: (DragEndDetails details) { - if (_.videoType.value == 'live' || _.controlsLock.value) { + if (_.videoType == 'live' || _.controlsLock.value) { return; } _.onChangedSliderEnd(); @@ -826,7 +826,7 @@ class _PLVideoPlayerState extends State return const SizedBox(); } - if (_.videoType.value == 'live') { + if (_.videoType == 'live') { return const SizedBox(); } if (value > max || max <= 0) { @@ -879,7 +879,7 @@ class _PLVideoPlayerState extends State // 锁 Obx( () => Visibility( - visible: _.videoType.value != 'live' && _.isFullScreen.value, + visible: _.videoType != 'live' && _.isFullScreen.value, child: Align( alignment: Alignment.centerLeft, child: FractionalTranslation( diff --git a/lib/services/audio_handler.dart b/lib/services/audio_handler.dart index bf98298b..853c58d0 100644 --- a/lib/services/audio_handler.dart +++ b/lib/services/audio_handler.dart @@ -26,7 +26,7 @@ class VideoPlayerServiceHandler extends BaseAudioHandler with SeekHandler { static final List _item = []; Box setting = GStrorage.setting; bool enableBackgroundPlay = false; - PlPlayerController player = PlPlayerController.getInstance(); + PlPlayerController player = PlPlayerController(); VideoPlayerServiceHandler() { revalidateSetting(); diff --git a/lib/services/audio_session.dart b/lib/services/audio_session.dart index 53b497ae..d1d2a466 100644 --- a/lib/services/audio_session.dart +++ b/lib/services/audio_session.dart @@ -18,7 +18,7 @@ class AudioSessionHandler { session.configure(const AudioSessionConfiguration.music()); session.interruptionEventStream.listen((event) { - final player = PlPlayerController.getInstance(videoType: 'none'); + final player = PlPlayerController(videoType: 'none'); if (event.begin) { if (!player.playerStatus.playing) return; switch (event.type) { @@ -51,7 +51,7 @@ class AudioSessionHandler { // 耳机拔出暂停 session.becomingNoisyEventStream.listen((_) { - final player = PlPlayerController.getInstance(videoType: 'none'); + final player = PlPlayerController(videoType: 'none'); if (player.playerStatus.playing) { player.pause(); } diff --git a/lib/services/shutdown_timer_service.dart b/lib/services/shutdown_timer_service.dart index 156b63c8..06993f62 100644 --- a/lib/services/shutdown_timer_service.dart +++ b/lib/services/shutdown_timer_service.dart @@ -89,7 +89,7 @@ class ShutdownTimerService { return; } PlPlayerController plPlayerController = - PlPlayerController.getInstance(videoType: 'none'); + PlPlayerController(videoType: 'none'); if (!exitApp && !waitForPlayingCompleted) { if (!plPlayerController.playerStatus.playing) { //仅提示用户 @@ -124,7 +124,7 @@ class ShutdownTimerService { } else { //暂停播放 PlPlayerController plPlayerController = - PlPlayerController.getInstance(videoType: 'none'); + PlPlayerController(videoType: 'none'); if (plPlayerController.playerStatus.playing) { plPlayerController.pause(); waitForPlayingCompleted = true; From f23a21f6dce39cffc0fb2879a321d3f3b514ba05 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 16 May 2024 23:04:47 +0800 Subject: [PATCH 192/349] =?UTF-8?q?feat:=20=E5=8A=A8=E6=80=81=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/dynamics/result.dart | 3 + lib/pages/dynamics/widgets/forward_panel.dart | 55 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/lib/models/dynamics/result.dart b/lib/models/dynamics/result.dart index bc7105d1..64a6e5b1 100644 --- a/lib/models/dynamics/result.dart +++ b/lib/models/dynamics/result.dart @@ -415,6 +415,7 @@ class DynamicMajorModel { this.type, this.courses, this.common, + this.music, }); DynamicArchiveModel? archive; @@ -431,6 +432,7 @@ class DynamicMajorModel { String? type; Map? courses; Map? common; + Map? music; DynamicMajorModel.fromJson(Map json) { archive = json['archive'] != null @@ -455,6 +457,7 @@ class DynamicMajorModel { type = json['type']; courses = json['courses'] ?? {}; common = json['common'] ?? {}; + music = json['music'] ?? {}; } } diff --git a/lib/pages/dynamics/widgets/forward_panel.dart b/lib/pages/dynamics/widgets/forward_panel.dart index 76c7b24e..f8b90a81 100644 --- a/lib/pages/dynamics/widgets/forward_panel.dart +++ b/lib/pages/dynamics/widgets/forward_panel.dart @@ -238,6 +238,61 @@ Widget forWard(item, context, ctr, source, {floor = 1}) { ), ), ); + case 'DYNAMIC_TYPE_MUSIC': + final Map music = item.modules.moduleDynamic.major.music; + return Padding( + padding: const EdgeInsets.only(top: 8), + child: InkWell( + onTap: () { + Get.toNamed('/webview', parameters: { + 'url': "https:${music['jump_url']}", + 'type': 'url', + 'pageTitle': music['title'] + }); + }, + child: Container( + width: double.infinity, + padding: + const EdgeInsets.only(left: 12, top: 10, right: 12, bottom: 10), + color: Theme.of(context).dividerColor.withOpacity(0.08), + child: Row( + children: [ + NetworkImgLayer( + width: 45, + height: 45, + src: music['cover'], + ), + const SizedBox(width: 10), + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + music['title'], + style: TextStyle( + color: Theme.of(context).colorScheme.primary, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 2), + Text( + music['label'], + style: TextStyle( + color: Theme.of(context).colorScheme.outline, + fontSize: + Theme.of(context).textTheme.labelMedium!.fontSize, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ], + ) + ], + ), + // TextButton(onPressed: () {}, child: Text('123')) + ), + ), + ); default: return const SizedBox( width: double.infinity, From eae39c480c9f99887695f1755fd6acb36f5720bc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 18 May 2024 19:29:57 +0800 Subject: [PATCH 193/349] =?UTF-8?q?mod:=20playInit=20autoplay=20=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 4 ++-- lib/pages/video/detail/view.dart | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 722af317..38c62d7e 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -233,7 +233,7 @@ class VideoDetailController extends GetxController audio, seekToTime, duration, - bool autoplay = true, + bool? autoplay, }) async { /// 设置/恢复 屏幕亮度 if (brightness != null) { @@ -266,7 +266,7 @@ class VideoDetailController extends GetxController cid: cid.value, enableHeart: enableHeart, isFirstTime: isFirstTime, - autoplay: autoplay, + autoplay: autoplay ?? autoPlay.value, ); /// 开启自动全屏时,在player初始化完成后立即传入headerControl diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 2c35858f..6f751e52 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -176,7 +176,7 @@ class _VideoDetailPageState extends State /// 未开启自动播放时触发播放 Future handlePlay() async { - await vdCtr.playerInit(); + await vdCtr.playerInit(autoplay: true); plPlayerController = vdCtr.plPlayerController; plPlayerController!.addStatusLister(playerListener); vdCtr.autoPlay.value = true; @@ -266,7 +266,7 @@ class _VideoDetailPageState extends State } vdCtr.isFirstTime = false; final bool autoplay = autoPlayEnable; - vdCtr.playerInit(autoplay: autoplay); + vdCtr.playerInit(); /// 未开启自动播放时,未播放跳转下一页返回/播放后跳转下一页返回 vdCtr.autoPlay.value = !vdCtr.isShowCover.value; From cb7cd0a2f0658ca84d48b213760aee80decff555 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 19 May 2024 19:57:13 +0800 Subject: [PATCH 194/349] feat: videoDetail menu edit --- assets/images/coin.png | Bin 0 -> 7159 bytes lib/models/common/action_type.dart | 94 ++++++++++++++++ lib/pages/setting/pages/action_menu_set.dart | 100 ++++++++++++++++++ lib/pages/setting/style_setting.dart | 5 + lib/pages/video/detail/introduction/view.dart | 28 ++--- .../introduction/widgets/action_item.dart | 54 ++++++---- lib/router/app_pages.dart | 4 + lib/utils/storage.dart | 3 +- 8 files changed, 249 insertions(+), 39 deletions(-) create mode 100644 assets/images/coin.png create mode 100644 lib/models/common/action_type.dart create mode 100644 lib/pages/setting/pages/action_menu_set.dart diff --git a/assets/images/coin.png b/assets/images/coin.png new file mode 100644 index 0000000000000000000000000000000000000000..bc2952a7a5cf519a331b83c0f268eef88769d471 GIT binary patch literal 7159 zcmai3XEa<vI?2oo<*AX$uJ|9 zbE)c;5wTPqLBNLDXyI`F_$sdFW^)=nesPzJh7-d7Tk-$I#4N)}`MRL7DXk^Ce63JJ z$@|x~Dt)%t|F(sSB|<)Jwbk{iJW~8(_(!4PBksrHCfo7KN9jac37>Zv1N(QM&AyBK zr6Q{qIyv=nqi|5paxTn8tu9Ps`T@6=^q%B)@V@A&uz#M=&%bz?S5^*F<32*K3>Lq3 zVXCYAgH*bFr5u6k<-WPOe*5v6)S&0*WP^jaVU`j396F&bw*nD7yp@`KZ zf_Bfei3C%9{_H8dcljj8=IFOlR=nC(1>f%J2`k5Joj9#YKV0weokY&;Ba0H6&ZavJ z-`kJUtqiVj-R64NcxCU+tIcgOuD!4d!s@}{U5PNqxU%EJs~0&lomZ&`ck18=J=A); zb?~oe*?p-K*30XWEGdh}g@sRLjkpq!)vYGro@xZx9t*R_g`PET$k5@)GQTZ9J^Ip3 zhi=jp_$_R~_P|jrsiCnAy?(ms>v?kAYr%H%_Zc&~C#(w6&{!%z0lH5u2k{X5QrdV? z>xSUJRZ<+M1Tb_)0bVO6@f26cL}69`JOaG|*3rf&If+G(*Y-T_=f3;wHgy@fWS)E& zHluGHXednEwVc-1VNG-0^Lk9BHQQqGkTFM?w(8#C9bi$K>jVvL?rb^II?Ez=s94`P z|DpD2@$y-3KQj|!lYV#<|FExhgWWbZY_vAw`%md~ed)g9;CYj2=Oa~3!(Ri{A-X8v zpx$XkRN%?|H7(bPeTDT$U2Rn5Pt08M4%Dr=hUddQ3bO}vgt~_!e--cCO+GX`r6@mF z4)W?sy!%7r`bKCnc77>HZbRWXU4Qf!i{94oamm_O@#VKk*x?6i_IbSE{(qqL@pX>6g6tMuMySM63bXjKKQW`zugF43{dpMj=NYW(uw%8RJ+=#W zI?~pKSmdm9yVR-Bt?)~To7;!c|EzB$#BF(@wND79z8upagtno+y&?wL1Om$oC)Mkz?W>meIH|OtG$Eu09UW5x;^+{3Pb=)_j+O07=RCt<= zvDJ3?+-3010YS=Mw`{sCT}H(Vj8IsLVh$DT(sSUnyxOSFSSV_e;wcryI^ye~*LfKWB*0r%2$vcM19va@SGLQVv5rKjb7b-dKIVwZtba{LV?Nauw z4~NDSM}5AC{0d;OV4IsBZW5tZ;O)rno#Li7l~M2ob00O-f|9b_@h@czYKS^W*^iiF z8Esz>@pwWp6&sLaJH&b$=P@Q_@`O+0$9gxKdvw2I16+{7=P&FjN*)+bsfk0h^`^>Y$s z1Jl{Rze6gqdDR;=?7fNt1SYa`L%)B^8jU_a>8lF|`!Pxs@J@1uy!=`;zD(PFYkhQm zhqhbq(~@455`m}W;==ln7oTFf1hALu97-*24?!-b8%C-e2;|NQ_$GFB(7W9kU59@N z)6IT0UUMUwXIY{111C|1&)!n|fcE;n)ld*K>*r#LNCb zNDed2a>}ny*$^chgGqo-gJcIKPu7@TPaB?3sSNzuW`>pPiJa;N)ml43RU@x~Hp$PD<*cS3_SGnAH+P{(iOgzWiuelm zy;)VB^g6EwzBe=8r-$}bJ#h>aWW=$aC%E=(R+|mAJJx?}+2nS&yHE|}r}<_O$y7J4+{c!?hJv;GL$Qx-qLBx5v2?txK2h=+ z#IJ9zS)3%_ZJg0of3vwC)xJ?KRVM)R#<{Bf{4r!98A36E&b~=zN8rh9apQ~mfI4Q& zl9EWHY{TE+aQnRZ(ZYtnlilpbM`D~A4WP5JcJ8<{ngMj24hFJ2N!k0C=cAl0K_eTZ zt8|s*nLV>nHyCjHRFUN@+2|^Xsh8`ja9RhsWa#$(OkM0^OMr3Y;K>6dp&Ht75<@Zu z!i%bN`?*JQ>S830vo~#l&QomI(bRIh+cRbQtH7p$vyzgKBnH%0<3Uf&ad9=e!JCdq z3GoodNF`M%Vg$Llzj0}f4?8ZoS{E893BBNrRYj*bm2z1af$&Yz8ZC2_;QFcc`ApgfE@m_6@j3NSowGS>vcBjyf(feK zguOKg%X=Epv;?eJ+yLx6jm>F-Z|6@5{qjzVj^P>QWv%w?sg#7s6!o6mOL9(b0!`1eQFzt7 z{wCnv&gTR~k6phrGQZ$|4P~nvHPiM!DZ~n_A z4>-!;5yStQTKT3*2Kz~0@txrO*UwltY>P7MX&8gB&2Mv=Hi}hpSgS2nMz!YE`f5Ef z{To&zQPW?zohc;Mu)k6;!g$Kb=dAW1yDb~x4;J!$>T4B-g1L<;8z3Gtz~?ZVwo}`` z=ZR*?Qcv>#U{5ZNoB%Q-6uz2$(=haqpperZP%$u8q0qYzlW+dzeMb5Y7yMbAqHDPV z(B&ZfhXSTXKyp*q95nLy;jE|on>S!zy+fOTNawa$7Y>dMfN84C=(Sq_a%JpNs?7K` zirCMB2yi#NhlHr9!=s0!G-)VgFvhdPgvzLtSD?9(#7 znZYH~qFt1M0SiOU-+rLWm4ie#d<9N|E^=bIWoiab4f_7G_PYrz?5(ra+nNWJai!Kc z3aAA~oIkx_?jG~w{0cA(e4zp1eZV@bu|{J=jG4VOfemZCpr|~y(d)%EkFFp$ZlJ*z zoLsvDr!_gZTSohkQ^p57p>ok9&S$YJ&5y|noJ*tq%AeBFvt6sx#r{cN`tjxMZ*B}V z{_5IGl{Fr$+g(bV$EEZyw^fc$uNf-4KfC9~T(opPy!jgNsMDM6eRCdt$MBo+Lwm(6 ziUmXZY%ch<kP$X5Z#1YG)4EL>;v@HohmWj$TvEO`G}z} zYVkwBeo&MYQ2CQ&A=sgbW-$xE#U&$-Z@>PagwI9ia<@<66W-Ac(9Vwddl_R5h!CBc zG3K{kL(FVG?M8#RxCzhOZWWJJ04VgU`hks8Ysy(&VTKk<4&!z~2Y_BQSX~)E1!xDw zjZhS5iqhhNJa9`oI76`NxlhZBOVLv^26!eH;Xb15DkKN2{*^+x6nt@)5w3)i&RHwt zQboiRTkG>>88hLm&*R^zDW)<%LjjT^#IZmUj4A#$wo7v>s@Dt)x+uDf{hLTspnLvV6%+(ehY}w; zmDXjAH9@v_To}8w#qmpTPoIYf&1}4u>wMgSLK%m!^H6Hvqp>?#&;ipCmB69~I= zP@-4DjsyVY4qWP|N}u$C6Pa zyr3P!P383j*$5`MBdZ8j*h!B!{YKGMTkJI?Nr;+j0dNJVw(v2(QWOtGChlmb^a6lg zC*Vy`187H|qcuIw&&>zKib3?2@B4d=I3f|tE=TS#Lb6{FoXnnV8(!p+{|LB5$;Az{ z2A+ikYYI^lb@B4v0??(6Z)6L!Nz#a(@Wm~_d`BeR`P(5|EYG6-v%-`dpfFoZx|R)= zhuTCF&VuQe4f!WSUSJRO1L?LT@x~wn`^b_8B1pf-g^J8Y=6W3qP?Ix+MF#y5GYXVs zzKgYC?AM?$J5&V5U}?^{KJMlrn%Mf;XlVM-kfb*+_j1mf#LdW(1kI9glLva(V}Ras zF{)ERm`uhjH$m8G=je1&Wr!^4y>g`>nKHEF2GZ3)d-^%Q6Cjra<`%`BR@x6yDmBy{ zHxkQrZYkWy{uxyWN-Ck&iKNU~( zWp)o*2#?mOWTYj1%yvQZY>#qXKaek|QheO(^CysR0;xjTK}l3c){!iQu@FM}trG|C zqY5YrCR2^jqeeTmTMjzX|*d>wip!(kJ z!p}ZGWEgbn^dT6EXIQ{dF&V4f$)GFc1>o6F*#N?qI-mq9NVB>2W%IJvD((_Ov>zC; zu{ehOwMC3t{exWrr>uB6A3XckT-Ko<(By@ar-Nz~l#rN2z7VMueFfhr{wL{>qc8VT za4)%7;@3;z(18rs?VL0QEcpD3zelnAO%41*Jxptp02w{oQt!yIhddsb>ZGuGBcG;& zCe_KaJv4fb1KcqwV3SW|970g^gl~qV3WF7Ipei19>cs%90ZzXU1|?=Vpp*nBPwXgP8yQTOq*RrTP3P>%daQQbea)PLeks}Xrr+6LA*Ka(u5c}R5qmD52lqO8pTp!r6U29u7)!;n}G=UUpKi0-g@d0#GkcZcR&@3mbP6tml39i)NU^5%gvTlEWZ)^j?U!1zv=LD z!cwU^j(5y)=1!4??g7o-RxJ@awi=BZ4I`dZMR1?u?4Yfu((sKL{p)@z`uM8nN^5c^K%u4`D8*BuHq@?_UtvzAyTb`C1qB-=ha`Nx9t3_oxpo6^h1 zLM2H4wb#om6&YhU*B0U$ZCn{SJPNu9LdmLBv_B-54aox;Io4ZZr9HA#Llq594)q}s z+cQB$`Ze5Z{@TNP4~)m;&mmcs2b5Y(vE&1i%W@XGUhgHH_fpNnD!&_Ng!Rvttj~Ri zNGcPl#OH#pRBRk(9sH(aeY|t8Fn{1(j_%`RG1ESs!f+A&cf!ONSwps)>!a zVZ*@Vw=oUde@rIMA9m=!>^7h6(R}C;9$^*&KBeOG^$(0(%Gjin-dU9vwE021SmV3A z3pQF;IZ#Oc!*hWuKZ1EpRWFD{=m*1t#}vz7#veSv>qt)Ccay~l3?j}M_W^Sb> zanl})m_^+3yBa@tnNLoh!B$qRt9@J-uDM^O)Yw_e5fK!-#Hhf<4~$C1tYDZ+VR*j2 z{AXn_JknafKU@GSfFq;bO$J00#NCOj=YfX{vw@|#%eclfp=l9mVYiTjtw?C3fz@>` z64MaIIvWz}K}O^ZXYc3Z0QPjEN28^=OFvS~3bHRx_2$Opi5)U+-2V1_n7e#)<_Bm& zPx&(X!U8W>knt#XH`GULXw_N{>k=#HTX{r7$qnCfh zx#>2(>CSNv;(8i+@0HuB6L)}((vj_K;ACWkoW_Gkca-~j4<3I0x4&E;JFSuX-cPN1 zPTh(Ela&}+8UF9VvIY8T5A@WKUU-)00i*sUX0gY_>-?{$-IJLMSGW$I2ivT{Oio1U z9K+mo%PCIl%UPt$X72CNc{zD~9%J>anzm~xLt4ovwS|KGIL&hCdKI7CUGl6pHbbD{R_;~c|h%se}} zrD8#cvGbkY-XL9k?27}j;imls*~h=d@@E$;iY=ulsFvNxE(CR+#E=Z4$~mA#@or`07naUm?|czbR&_QxkoW|i^+tLG?WF2;EYGN+Xu zq{gQryB(ta#Js$)LG(M@QD^CaMY$*DJ?*4ZBwR+z1Jj!7e9TGb-_M0z8I+;Gv^lR? z)8teeQF2KBc46zCd*)gEhIaC%ql<(yDSIs+^?_Cn;Kf;qd{u&}$2A5?Bs==_6Q>lD z{wGM17^~LvGEx5({$?LdGm?b%>LS}?NmC1#2Asc6Xn(J@63(7#_mS#17$zM&d#6xK zRZYf(wPmrB);c{Isu~g(iD{%>353^R*Ho(LUkQ;?-yx zwd~$Igjqu3uA7dwQG^J?w=jk!&+A2roChWuZy(m~Q(KKkC8DPWUb}`eKP5Kkiu(Ac ztUt)act5wB=+d~|3OuN7HtL$%QB0$rT6|^H@07(_lJNDBf7^$r+dNc$%~nDcwuQAx zsqVpgo+C)J1?jgwX)jscx&ElNw3f?%S@~`1<8Xo56>eiau0M1B+ZQO?|Gpn1&hT(C l{=<}~mvg`V-P`>?i}iJ^lzFK-(e&S({%sST25l$Qe*pGzPR;-T literal 0 HcmV?d00001 diff --git a/lib/models/common/action_type.dart b/lib/models/common/action_type.dart new file mode 100644 index 00000000..2284df6c --- /dev/null +++ b/lib/models/common/action_type.dart @@ -0,0 +1,94 @@ +// 操作类型的枚举值:点赞 不喜欢 收藏 投币 稍后再看 下载封面 后台播放 听视频 分享 下载视频 +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +enum ActionType { + like, + coin, + collect, + watchLater, + share, + dislike, + downloadCover, + copyLink, + threeAction, + // backgroundPlay, + // listenVideo, + // downloadVideo, +} + +extension ActionTypeExtension on ActionType { + String get value => [ + 'like', + 'coin', + 'collect', + 'watchLater', + 'share', + 'dislike', + 'downloadCover', + 'copyLink', + // 'backgroundPlay', + // 'listenVideo', + // 'downloadVideo', + ][index]; + String get label => [ + '点赞视频', + '投币', + '收藏视频', + '稍后再看', + '视频分享', + '不喜欢', + '下载封面', + '复制链接', + // '后台播放', + // '听视频', + // '下载视频', + ][index]; +} + +List actionMenuConfig = [ + { + 'icon': const Icon(Icons.thumb_up_alt_outlined), + 'label': '点赞视频', + 'value': ActionType.like, + }, + { + 'icon': Image.asset( + 'assets/images/coin.png', + width: 26, + color: IconTheme.of(Get.context!).color!.withOpacity(0.65), + ), + 'label': '投币', + 'value': ActionType.coin, + }, + { + 'icon': const Icon(Icons.star_border), + 'label': '收藏视频', + 'value': ActionType.collect, + }, + { + 'icon': const Icon(Icons.watch_later_outlined), + 'label': '稍后再看', + 'value': ActionType.watchLater, + }, + { + 'icon': const Icon(Icons.share), + 'label': '视频分享', + 'value': ActionType.share, + }, + { + 'icon': const Icon(Icons.thumb_down_alt_outlined), + 'label': '不喜欢', + 'value': ActionType.dislike, + }, + { + 'icon': const Icon(Icons.image_outlined), + 'label': '下载封面', + 'value': ActionType.downloadCover, + }, + { + 'icon': const Icon(Icons.link_outlined), + 'label': '复制链接', + 'value': ActionType.copyLink, + }, +]; diff --git a/lib/pages/setting/pages/action_menu_set.dart b/lib/pages/setting/pages/action_menu_set.dart new file mode 100644 index 00000000..f7d92e17 --- /dev/null +++ b/lib/pages/setting/pages/action_menu_set.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/models/common/action_type.dart'; +import '../../../utils/storage.dart'; + +class ActionMenuSetPage extends StatefulWidget { + const ActionMenuSetPage({super.key}); + + @override + State createState() => _ActionMenuSetPageState(); +} + +class _ActionMenuSetPageState extends State { + Box settingStorage = GStrorage.setting; + late List actionTypeSort; + late List allLabels; + + @override + void initState() { + super.initState(); + actionTypeSort = settingStorage.get(SettingBoxKey.actionTypeSort, + defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); + allLabels = actionMenuConfig; + allLabels.sort((a, b) { + int indexA = actionTypeSort.indexOf((a['value'] as ActionType).value); + int indexB = actionTypeSort.indexOf((b['value'] as ActionType).value); + if (indexA == -1) indexA = actionTypeSort.length; + if (indexB == -1) indexB = actionTypeSort.length; + return indexA.compareTo(indexB); + }); + } + + void saveEdit() { + List sortedTabbar = allLabels + .where((i) => actionTypeSort.contains((i['value'] as ActionType).value)) + .map((i) => (i['value'] as ActionType).value) + .toList(); + settingStorage.put(SettingBoxKey.actionTypeSort, sortedTabbar); + SmartDialog.showToast('保存成功,下次启动时生效'); + } + + void onReorder(int oldIndex, int newIndex) { + setState(() { + if (newIndex > oldIndex) { + newIndex -= 1; + } + final tabsItem = allLabels.removeAt(oldIndex); + allLabels.insert(newIndex, tabsItem); + }); + } + + @override + Widget build(BuildContext context) { + final listTiles = [ + for (int i = 0; i < allLabels.length; i++) ...[ + CheckboxListTile( + key: Key((allLabels[i]['value'] as ActionType).value), + value: actionTypeSort + .contains((allLabels[i]['value'] as ActionType).value), + onChanged: (bool? newValue) { + String actionTypeId = (allLabels[i]['value'] as ActionType).value; + if (!newValue!) { + actionTypeSort.remove(actionTypeId); + } else { + actionTypeSort.add(actionTypeId); + } + setState(() {}); + }, + title: Row( + children: [ + allLabels[i]['icon'], + const SizedBox(width: 8), + Text(allLabels[i]['label']), + ], + ), + secondary: const Icon(Icons.drag_indicator_rounded), + ) + ] + ]; + + return Scaffold( + appBar: AppBar( + title: const Text('视频操作菜单'), + actions: [ + TextButton(onPressed: () => saveEdit(), child: const Text('保存')), + const SizedBox(width: 12) + ], + ), + body: ReorderableListView( + onReorder: onReorder, + physics: const NeverScrollableScrollPhysics(), + footer: SizedBox( + height: MediaQuery.of(context).padding.bottom + 30, + ), + children: listTiles, + ), + ); + } +} diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 364eabf0..20fdada0 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -289,6 +289,11 @@ class _StyleSettingState extends State { onTap: () => Get.toNamed('/navbarSetting'), title: Text('底部导航栏设置', style: titleStyle), ), + ListTile( + dense: false, + onTap: () => Get.toNamed('/actionMenuSet'), + title: Text('操作菜单设置', style: titleStyle), + ), if (Platform.isAndroid) ListTile( dense: false, diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9103c130..dab81f18 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,7 +1,6 @@ import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; @@ -539,26 +538,21 @@ class _VideoInfoState extends State with TickerProviderStateMixin { builder: (BuildContext context, BoxConstraints constraints) { return Container( margin: const EdgeInsets.only(top: 6, bottom: 4), - height: constraints.maxWidth / 5 * 0.8, - child: GridView.count( - physics: const NeverScrollableScrollPhysics(), - primary: false, - padding: EdgeInsets.zero, - crossAxisCount: 5, - childAspectRatio: 1.25, - children: [ + height: constraints.maxWidth / 5, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.thumbsUp), - selectIcon: const Icon(FontAwesomeIcons.solidThumbsUp), + icon: const Icon(Icons.thumb_up_alt_outlined), + selectIcon: const Icon(Icons.thumb_up), onTap: handleState(videoIntroController.actionLikeVideo), selectStatus: videoIntroController.hasLike.value, text: widget.videoDetail!.stat!.like!.toString()), ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.b), - selectIcon: const Icon(FontAwesomeIcons.b), + icon: Image.asset('assets/images/coin.png', width: 30), onTap: handleState(videoIntroController.actionCoinVideo), selectStatus: videoIntroController.hasCoin.value, text: widget.videoDetail!.stat!.coin!.toString(), @@ -566,8 +560,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), Obx( () => ActionItem( - icon: const Icon(FontAwesomeIcons.star), - selectIcon: const Icon(FontAwesomeIcons.solidStar), + icon: const Icon(Icons.star_border), + selectIcon: const Icon(Icons.star), onTap: () => showFavBottomSheet(), onLongPress: () => showFavBottomSheet(type: 'longPress'), selectStatus: videoIntroController.hasFav.value, @@ -575,7 +569,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), ActionItem( - icon: const Icon(FontAwesomeIcons.clock), + icon: const Icon(Icons.watch_later_outlined), onTap: () async { final res = await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); @@ -585,7 +579,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { text: '稍后看', ), ActionItem( - icon: const Icon(FontAwesomeIcons.shareFromSquare), + icon: const Icon(Icons.share), onTap: () => videoIntroController.actionShareVideo(), selectStatus: false, text: '分享', diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 022d9223..2a89afe7 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -1,9 +1,10 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/utils/feed_back.dart'; class ActionItem extends StatelessWidget { - final Icon? icon; + final dynamic icon; final Icon? selectIcon; final Function? onTap; final Function? onLongPress; @@ -31,26 +32,37 @@ class ActionItem extends StatelessWidget { if (onLongPress != null) {onLongPress!()} }, borderRadius: StyleString.mdRadius, - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 4), - selectStatus - ? Icon(selectIcon!.icon!, - size: 18, color: Theme.of(context).colorScheme.primary) - : Icon(icon!.icon!, - size: 18, color: Theme.of(context).colorScheme.outline), - const SizedBox(height: 6), - Text( - text ?? '', - style: TextStyle( - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, - ), - ) - ], + child: SizedBox( + width: (Get.size.width - 24) / 5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + icon is Icon + ? Icon( + selectStatus ? selectIcon!.icon ?? icon!.icon : icon!.icon, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ) + : Image.asset( + 'assets/images/coin.png', + width: 25, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + const SizedBox(height: 6), + Text( + text ?? '', + style: TextStyle( + color: + selectStatus ? Theme.of(context).colorScheme.primary : null, + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + ), + ) + ], + ), ), ); } diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 7fda1bd8..2ca333f8 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -35,6 +35,7 @@ import '../pages/search/index.dart'; import '../pages/search_result/index.dart'; import '../pages/setting/extra_setting.dart'; import '../pages/setting/index.dart'; +import '../pages/setting/pages/action_menu_set.dart'; import '../pages/setting/pages/color_select.dart'; import '../pages/setting/pages/display_mode.dart'; import '../pages/setting/pages/font_size_select.dart'; @@ -174,6 +175,9 @@ class Routes { // navigation bar CustomGetPage( name: '/navbarSetting', page: () => const NavigationBarSetPage()), + // 操作菜单 + CustomGetPage( + name: '/actionMenuSet', page: () => const ActionMenuSetPage()), ]; } diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 29cf1846..4a163446 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -149,7 +149,8 @@ class SettingBoxKey { tabbarSort = 'tabbarSort', // 首页tabbar dynamicBadgeMode = 'dynamicBadgeMode', enableGradientBg = 'enableGradientBg', - navBarSort = 'navBarSort'; + navBarSort = 'navBarSort', + actionTypeSort = 'actionTypeSort'; } class LocalCacheKey { From ed28e98bbae2d38971ae2ae16c15871e2f8c5c84 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 20 May 2024 23:45:38 +0800 Subject: [PATCH 195/349] =?UTF-8?q?opt:=20=E6=8E=A8=E8=8D=90=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=8B=89=E9=BB=91=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/video_card_v.dart | 37 ++++++++++++++++++++++------ lib/http/video.dart | 10 ++++++-- lib/pages/rcmd/controller.dart | 7 ++++++ lib/pages/rcmd/view.dart | 1 + 4 files changed, 46 insertions(+), 9 deletions(-) diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 7b56152f..14476cdf 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -20,11 +20,13 @@ import 'network_img_layer.dart'; class VideoCardV extends StatelessWidget { final dynamic videoItem; final int crossAxisCount; + final Function? blockUserCb; const VideoCardV({ Key? key, required this.videoItem, required this.crossAxisCount, + this.blockUserCb, }) : super(key: key); bool isStringNumeric(String str) { @@ -157,7 +159,11 @@ class VideoCardV extends StatelessWidget { ); }), ), - VideoContent(videoItem: videoItem, crossAxisCount: crossAxisCount) + VideoContent( + videoItem: videoItem, + crossAxisCount: crossAxisCount, + blockUserCb: blockUserCb, + ) ], ), ); @@ -167,9 +173,14 @@ class VideoCardV extends StatelessWidget { class VideoContent extends StatelessWidget { final dynamic videoItem; final int crossAxisCount; - const VideoContent( - {Key? key, required this.videoItem, required this.crossAxisCount}) - : super(key: key); + final Function? blockUserCb; + + const VideoContent({ + Key? key, + required this.videoItem, + required this.crossAxisCount, + this.blockUserCb, + }) : super(key: key); Widget _buildBadge(String text, String type, [double fs = 12]) { return PBadge( @@ -241,7 +252,10 @@ class VideoContent extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel(videoItem: videoItem); + return MorePanel( + videoItem: videoItem, + blockUserCb: blockUserCb, + ); }, ); }, @@ -297,11 +311,17 @@ class VideoStat extends StatelessWidget { class MorePanel extends StatelessWidget { final dynamic videoItem; - const MorePanel({super.key, required this.videoItem}); + final Function? blockUserCb; + const MorePanel({ + super.key, + required this.videoItem, + this.blockUserCb, + }); Future menuActionHandler(String type) async { switch (type) { case 'block': + Get.back(); blockUser(); break; case 'watchLater': @@ -338,7 +358,10 @@ class MorePanel extends StatelessWidget { reSrc: 11, ); SmartDialog.dismiss(); - SmartDialog.showToast(res['msg'] ?? '成功'); + if (res['status']) { + blockUserCb?.call(videoItem.owner.mid); + } + SmartDialog.showToast(res['msg']); }, child: const Text('确认'), ) diff --git a/lib/http/video.dart b/lib/http/video.dart index bf5921d6..7c1d9ba6 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -387,9 +387,15 @@ class VideoHttp { 'csrf': await Request.getCsrf(), }); if (res.data['code'] == 0) { - return {'status': true, 'data': res.data['data']}; + if (act == 5) { + List blackMidsList = + setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); + blackMidsList.add(mid); + setting.put(SettingBoxKey.blackMidsList, blackMidsList); + } + return {'status': true, 'data': res.data['data'], 'msg': '成功'}; } else { - return {'status': false, 'data': []}; + return {'status': false, 'data': [], 'msg': res.data['message']}; } } diff --git a/lib/pages/rcmd/controller.dart b/lib/pages/rcmd/controller.dart index 28ff055b..2d606b12 100644 --- a/lib/pages/rcmd/controller.dart +++ b/lib/pages/rcmd/controller.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/video.dart'; @@ -106,4 +107,10 @@ class RcmdController extends GetxController { duration: const Duration(milliseconds: 500), curve: Curves.easeInOut); } } + + void blockUserCb(mid) { + videoList.removeWhere((e) => e.owner.mid == mid); + videoList.refresh(); + SmartDialog.showToast('已移除相关视频'); + } } diff --git a/lib/pages/rcmd/view.dart b/lib/pages/rcmd/view.dart index 67567870..29a8d469 100644 --- a/lib/pages/rcmd/view.dart +++ b/lib/pages/rcmd/view.dart @@ -146,6 +146,7 @@ class _RcmdPageState extends State ? VideoCardV( videoItem: videoList[index], crossAxisCount: crossAxisCount, + blockUserCb: (mid) => ctr.blockUserCb(mid), ) : const VideoCardVSkeleton(); }, From 2dfd2d4dc16d42ffbf42d6ed9c112066c8177e2e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 23 May 2024 00:32:59 +0800 Subject: [PATCH 196/349] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E6=A0=8F=E5=AE=89=E5=85=A8=E5=8C=BA=E5=9F=9F=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 46 +++++++++---------- lib/plugin/pl_player/widgets/app_bar_ani.dart | 4 ++ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index cff4611a..ff0888be 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -768,37 +768,33 @@ class _PLVideoPlayerState extends State ), // 头部、底部控制条 - SafeArea( - top: false, - bottom: false, - child: Obx( - () => Column( - children: [ - if (widget.headerControl != null || _.headerControl != null) - ClipRect( - child: AppBarAni( - controller: animationController, - visible: !_.controlsLock.value && _.showControls.value, - position: 'top', - child: widget.headerControl ?? _.headerControl!, - ), - ), - const Spacer(), + Obx( + () => Column( + children: [ + if (widget.headerControl != null || _.headerControl != null) ClipRect( child: AppBarAni( controller: animationController, visible: !_.controlsLock.value && _.showControls.value, - position: 'bottom', - child: widget.bottomControl ?? - BottomControl( - controller: widget.controller, - triggerFullScreen: _.triggerFullScreen, - buildBottomControl: buildBottomControl(), - ), + position: 'top', + child: widget.headerControl ?? _.headerControl!, ), ), - ], - ), + const Spacer(), + ClipRect( + child: AppBarAni( + controller: animationController, + visible: !_.controlsLock.value && _.showControls.value, + position: 'bottom', + child: widget.bottomControl ?? + BottomControl( + controller: widget.controller, + triggerFullScreen: _.triggerFullScreen, + buildBottomControl: buildBottomControl(), + ), + ), + ), + ], ), ), diff --git a/lib/plugin/pl_player/widgets/app_bar_ani.dart b/lib/plugin/pl_player/widgets/app_bar_ani.dart index 53eaad16..a1db5807 100644 --- a/lib/plugin/pl_player/widgets/app_bar_ani.dart +++ b/lib/plugin/pl_player/widgets/app_bar_ani.dart @@ -29,6 +29,10 @@ class AppBarAni extends StatelessWidget implements PreferredSizeWidget { curve: Curves.linear, )), child: Container( + padding: EdgeInsets.only( + left: MediaQuery.of(context).padding.left, + right: MediaQuery.of(context).padding.right, + ), decoration: BoxDecoration( gradient: position! == 'top' ? const LinearGradient( From c387cb83e7809c36456c85a82ccb1fa930d2a19f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 24 May 2024 23:54:25 +0800 Subject: [PATCH 197/349] =?UTF-8?q?mod:=20=E6=94=B6=E8=97=8F=E5=A4=B9?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/media/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 6541680a..cc413e59 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -105,7 +105,7 @@ class _MediaPageState extends State color: Theme.of(context).dividerColor.withOpacity(0.1), ), ListTile( - onTap: () {}, + onTap: () => Get.toNamed('/fav'), leading: null, dense: true, title: Padding( From 732beb38a71a20cb2980d3c40d473b4b64b68fd1 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 25 May 2024 15:31:00 +0800 Subject: [PATCH 198/349] =?UTF-8?q?opt:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 41 ++++++------------- lib/utils/app_scheme.dart | 24 +++++++++-- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 4ff13065..55c16f7d 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,3 +1,4 @@ +import 'package:appscheme/appscheme.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -11,6 +12,7 @@ import 'package:pilipala/models/video/reply/item.dart'; import 'package:pilipala/pages/preview/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; +import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; @@ -643,34 +645,17 @@ InlineSpan buildContent( '', ); } else { - final String pathSegment = Uri.parse(matchStr).path; - Map matchRes = IdUtils.matchAvorBv(input: pathSegment); - List matchKeys = matchRes.keys.toList(); - if (matchKeys.isNotEmpty) { - UrlUtils.matchUrlPush( - matchRes.containsKey('AV') - ? matchRes['AV']! as int - : matchRes['BV'], - title, - matchStr, - ); - } else { - final String redirectUrl = - await UrlUtils.parseRedirectUrl(matchStr); - // if (redirectUrl == matchStr) { - // Clipboard.setData(ClipboardData(text: matchStr)); - // SmartDialog.showToast('地址可能有误'); - // return; - // } - Get.toNamed( - '/webview', - parameters: { - 'url': redirectUrl, - 'type': 'url', - 'pageTitle': title - }, - ); - } + Uri uri = Uri.parse(matchStr); + SchemeEntity scheme = SchemeEntity( + scheme: uri.scheme, + host: uri.host, + port: uri.port, + path: uri.path, + query: uri.queryParameters, + source: '', + dataString: matchStr, + ); + PiliSchame.fullPathPush(scheme); } } else { if (appUrlSchema.startsWith('bilibili://search')) { diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 8502dc7f..a83b7809 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -95,7 +95,7 @@ class PiliSchame { } } if (scheme == 'https') { - _fullPathPush(value); + fullPathPush(value); } } @@ -126,7 +126,7 @@ class PiliSchame { } } - static Future _fullPathPush(SchemeEntity value) async { + static Future fullPathPush(SchemeEntity value) async { // https://m.bilibili.com/bangumi/play/ss39708 // https | m.bilibili.com | /bangumi/play/ss39708 // final String scheme = value.scheme!; @@ -135,8 +135,6 @@ class PiliSchame { Map? query = value.query; RegExp regExp = RegExp(r'^((www\.)|(m\.))?bilibili\.com$'); if (regExp.hasMatch(host)) { - print('bilibili.com host: $host'); - print('bilibili.com path: $path'); final String lastPathSegment = path!.split('/').last; if (path.startsWith('/video')) { Map matchRes = IdUtils.matchAvorBv(input: path); @@ -236,6 +234,24 @@ class PiliSchame { print('个人空间'); Get.toNamed('/member?mid=$area', arguments: {'face': ''}); break; + default: + final Map map = + IdUtils.matchAvorBv(input: area.split('?').first); + if (map.containsKey('AV')) { + _videoPush(map['AV']! as int, null); + } else if (map.containsKey('BV')) { + _videoPush(null, map['BV'] as String); + } else { + Get.toNamed( + '/webview', + parameters: { + 'url': value.dataString ?? "", + 'type': 'url', + 'pageTitle': '' + }, + ); + } + break; } } } From 95685c4fdf04e8e0c725c7257755b641d7ca169f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 25 May 2024 16:16:23 +0800 Subject: [PATCH 199/349] =?UTF-8?q?mod:=20=E5=8E=86=E5=8F=B2=E8=AE=B0?= =?UTF-8?q?=E5=BD=95=E6=90=9C=E7=B4=A2=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/history/widgets/item.dart | 2 +- lib/pages/history_search/controller.dart | 12 +- lib/pages/history_search/view.dart | 150 ++++++++--------------- 3 files changed, 59 insertions(+), 105 deletions(-) diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 00ebd511..baebfedb 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -244,7 +244,7 @@ class HistoryItem extends StatelessWidget { ), ), ), - videoItem.progress != 0 + videoItem.progress != 0 && videoItem.duration != 0 ? Positioned( left: 3, right: 3, diff --git a/lib/pages/history_search/controller.dart b/lib/pages/history_search/controller.dart index 90ac7a02..a6c79e6a 100644 --- a/lib/pages/history_search/controller.dart +++ b/lib/pages/history_search/controller.dart @@ -10,9 +10,8 @@ class HistorySearchController extends GetxController { final FocusNode searchFocusNode = FocusNode(); RxString searchKeyWord = ''.obs; String hintText = '搜索'; - RxString loadingStatus = 'init'.obs; + RxBool loadingStatus = false.obs; RxString loadingText = '加载中...'.obs; - bool hasRequest = false; late int mid; RxString uname = ''.obs; int pn = 1; @@ -36,8 +35,7 @@ class HistorySearchController extends GetxController { // 提交搜索内容 void submit() { - loadingStatus.value = 'loading'; - if (hasRequest) { + if (!loadingStatus.value) { pn = 1; searchHistories(); } @@ -48,6 +46,7 @@ class HistorySearchController extends GetxController { if (type == 'onLoad' && loadingText.value == '没有更多了') { return; } + loadingStatus.value = true; var res = await UserHttp.searchHistory( pn: pn, keyword: controller.value.text, @@ -63,9 +62,8 @@ class HistorySearchController extends GetxController { loadingText.value = '没有更多了'; } pn += 1; - hasRequest = true; } - loadingStatus.value = 'finish'; + loadingStatus.value = false; return res; } @@ -86,6 +84,6 @@ class HistorySearchController extends GetxController { historyList.removeWhere((e) => e.kid == kid); SmartDialog.showToast(res['msg']); } - loadingStatus.value = 'finish'; + // loadingStatus.value = fasle; } } diff --git a/lib/pages/history_search/view.dart b/lib/pages/history_search/view.dart index 5bde691d..f5bcae64 100644 --- a/lib/pages/history_search/view.dart +++ b/lib/pages/history_search/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/skeleton/video_card_h.dart'; -import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/pages/history/widgets/item.dart'; @@ -16,20 +15,19 @@ class HistorySearchPage extends StatefulWidget { } class _HistorySearchPageState extends State { - final HistorySearchController _historySearchCtr = - Get.put(HistorySearchController()); + final HistorySearchController _hisCtr = Get.put(HistorySearchController()); late ScrollController scrollController; @override void initState() { super.initState(); - scrollController = _historySearchCtr.scrollController; + scrollController = _hisCtr.scrollController; scrollController.addListener( () { if (scrollController.position.pixels >= scrollController.position.maxScrollExtent - 300) { EasyThrottle.throttle('history', const Duration(seconds: 1), () { - _historySearchCtr.onLoad(); + _hisCtr.onLoad(); }); } }, @@ -50,19 +48,19 @@ class _HistorySearchPageState extends State { titleSpacing: 0, actions: [ IconButton( - onPressed: () => _historySearchCtr.submit(), + onPressed: () => _hisCtr.submit(), icon: const Icon(Icons.search_outlined, size: 22)), const SizedBox(width: 10) ], title: Obx( () => TextField( autofocus: true, - focusNode: _historySearchCtr.searchFocusNode, - controller: _historySearchCtr.controller.value, + focusNode: _hisCtr.searchFocusNode, + controller: _hisCtr.controller.value, textInputAction: TextInputAction.search, - onChanged: (value) => _historySearchCtr.onChange(value), + onChanged: (value) => _hisCtr.onChange(value), decoration: InputDecoration( - hintText: _historySearchCtr.hintText, + hintText: _hisCtr.hintText, border: InputBorder.none, suffixIcon: IconButton( icon: Icon( @@ -70,103 +68,61 @@ class _HistorySearchPageState extends State { size: 22, color: Theme.of(context).colorScheme.outline, ), - onPressed: () => _historySearchCtr.onClear(), + onPressed: () => _hisCtr.onClear(), ), ), - onSubmitted: (String value) => _historySearchCtr.submit(), + onSubmitted: (String value) => _hisCtr.submit(), ), ), ), body: Obx( - () => Column( - children: _historySearchCtr.loadingStatus.value == 'init' - ? [const SizedBox()] - : [ - Expanded( - child: FutureBuilder( - future: _historySearchCtr.searchHistories(), - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { - return Obx( - () => _historySearchCtr.historyList.isNotEmpty - ? ListView.builder( - controller: scrollController, - itemCount: - _historySearchCtr.historyList.length + - 1, - itemBuilder: (context, index) { - if (index == - _historySearchCtr - .historyList.length) { - return Container( - height: MediaQuery.of(context) - .padding - .bottom + - 60, - padding: EdgeInsets.only( - bottom: MediaQuery.of(context) - .padding - .bottom), - child: Center( - child: Obx( - () => Text( - _historySearchCtr - .loadingText.value, - style: TextStyle( - color: Theme.of(context) - .colorScheme - .outline, - fontSize: 13), - ), - ), - ), - ); - } else { - return HistoryItem( - videoItem: _historySearchCtr - .historyList[index], - ctr: _historySearchCtr, - onChoose: null, - onUpdateMultiple: () => null, - ); - } - }, - ) - : _historySearchCtr.loadingStatus.value == - 'loading' - ? const SizedBox(child: Text('加载中...')) - : const CustomScrollView( - slivers: [ - NoData(), - ], - ), - ); - } else { - return CustomScrollView( - slivers: [ - HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ) - ], - ); - } + () { + return _hisCtr.loadingStatus.value && _hisCtr.historyList.isEmpty + ? ListView.builder( + itemCount: 10, + itemBuilder: (context, index) { + return const VideoCardHSkeleton(); + }, + ) + : _hisCtr.historyList.isNotEmpty + ? ListView.builder( + controller: scrollController, + itemCount: _hisCtr.historyList.length + 1, + itemBuilder: (context, index) { + if (index == _hisCtr.historyList.length) { + return Container( + height: MediaQuery.of(context).padding.bottom + 60, + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom), + child: Center( + child: Obx( + () => Text( + _hisCtr.loadingText.value, + style: TextStyle( + color: + Theme.of(context).colorScheme.outline, + fontSize: 13, + ), + ), + ), + ), + ); } else { - // 骨架屏 - return ListView.builder( - itemCount: 10, - itemBuilder: (context, index) { - return const VideoCardHSkeleton(); - }, + return HistoryItem( + videoItem: _hisCtr.historyList[index], + ctr: _hisCtr, + onChoose: null, + onUpdateMultiple: () => null, ); } }, - ), - ), - ], - ), + ) + : const CustomScrollView( + slivers: [ + NoData(), + ], + ); + }, ), ); } From 1cefdb68b7692833acbe561865a8793af21294c0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 25 May 2024 16:38:16 +0800 Subject: [PATCH 200/349] =?UTF-8?q?opt:=20=E9=AA=A8=E6=9E=B6=E5=B1=8F?= =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav/view.dart | 8 +++++++- lib/pages/subscription/view.dart | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index b8d37f50..4f48213e 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/fav/index.dart'; import 'package:pilipala/pages/fav/widgets/item.dart'; @@ -93,7 +94,12 @@ class _FavPageState extends State { } } else { // 骨架屏 - return const Text('请求中'); + return ListView.builder( + itemBuilder: (context, index) { + return const VideoCardHSkeleton(); + }, + itemCount: 10, + ); } }, ), diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index 5e6e4664..bcc03cc3 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/video_card_h.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/utils/route_push.dart'; import 'controller.dart'; @@ -87,7 +88,12 @@ class _SubPageState extends State { } } else { // 骨架屏 - return const Text('请求中'); + return ListView.builder( + itemBuilder: (context, index) { + return const VideoCardHSkeleton(); + }, + itemCount: 10, + ); } }, ), From d6e24096e0197e389122f463eaf31f075306272b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 26 May 2024 15:48:28 +0800 Subject: [PATCH 201/349] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E9=A1=B5Hero=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 167 +++++++++++++++---------------- 1 file changed, 78 insertions(+), 89 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 6f751e52..bf156c71 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -525,11 +525,10 @@ class _VideoDetailPageState extends State Scaffold( resizeToAvoidBottomInset: false, key: vdCtr.scaffoldKey, - backgroundColor: Colors.black, appBar: PreferredSize( preferredSize: const Size.fromHeight(0), child: AppBar( - backgroundColor: Colors.transparent, + backgroundColor: Colors.black, elevation: 0, ), ), @@ -559,8 +558,7 @@ class _VideoDetailPageState extends State } return SliverAppBar( automaticallyImplyLeading: false, - // 假装使用一个非空变量,避免Obx检测不到而罢工 - pinned: vdCtr.autoPlay.value, + pinned: true, elevation: 0, scrolledUnderElevation: 0, forceElevated: innerBoxIsScrolled, @@ -568,47 +566,42 @@ class _VideoDetailPageState extends State backgroundColor: Colors.black, flexibleSpace: FlexibleSpaceBar( background: PopScope( - canPop: plPlayerController?.isFullScreen.value != - true, - onPopInvoked: (bool didPop) { - if (plPlayerController?.isFullScreen.value == - true) { - plPlayerController! - .triggerFullScreen(status: false); - } - if (MediaQuery.of(context).orientation == - Orientation.landscape) { - verticalScreen(); - } - }, - child: LayoutBuilder( - builder: (BuildContext context, - BoxConstraints boxConstraints) { - return Stack( - children: [ - if (isShowing) - Padding( - padding: EdgeInsets.only(top: 0), - child: videoPlayerPanel, - ), + canPop: + plPlayerController?.isFullScreen.value != true, + onPopInvoked: (bool didPop) { + if (plPlayerController?.isFullScreen.value == + true) { + plPlayerController! + .triggerFullScreen(status: false); + } + if (MediaQuery.of(context).orientation == + Orientation.landscape) { + verticalScreen(); + } + }, + child: Hero( + tag: heroTag, + child: Stack( + children: [ + if (isShowing) videoPlayerPanel, - /// 关闭自动播放时 手动播放 - Obx( - () => Visibility( - visible: !vdCtr.autoPlay.value && - vdCtr.isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: handlePlayPanel(), - ), - ), + /// 关闭自动播放时 手动播放 + Obx( + () => Visibility( + visible: !vdCtr.autoPlay.value && + vdCtr.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: handlePlayPanel(), ), - ], - ); - }, - )), + ), + ), + ], + ), + ), + ), ), ); }, @@ -627,55 +620,51 @@ class _VideoDetailPageState extends State : pinnedHeaderHeight; }, onlyOneScrollInBody: true, - body: ColoredBox( - key: Key(heroTag), - color: Theme.of(context).colorScheme.background, - child: Column( - children: [ - tabbarBuild(), - Expanded( - child: TabBarView( - controller: vdCtr.tabCtr, - children: [ - Builder( - builder: (BuildContext context) { - return CustomScrollView( - key: const PageStorageKey('简介'), - slivers: [ - if (vdCtr.videoType == SearchType.video) ...[ - VideoIntroPanel(bvid: vdCtr.bvid), - ] else if (vdCtr.videoType == - SearchType.media_bangumi) ...[ - Obx(() => BangumiIntroPanel( - cid: vdCtr.cid.value)), - ], - SliverToBoxAdapter( - child: Divider( - indent: 12, - endIndent: 12, - color: Theme.of(context) - .dividerColor - .withOpacity(0.06), - ), - ), - if (vdCtr.videoType == SearchType.video && - vdCtr.enableRelatedVideo) - const RelatedVideoPanel(), + body: Column( + children: [ + tabbarBuild(), + Expanded( + child: TabBarView( + controller: vdCtr.tabCtr, + children: [ + Builder( + builder: (BuildContext context) { + return CustomScrollView( + key: const PageStorageKey('简介'), + slivers: [ + if (vdCtr.videoType == SearchType.video) ...[ + VideoIntroPanel(bvid: vdCtr.bvid), + ] else if (vdCtr.videoType == + SearchType.media_bangumi) ...[ + Obx(() => + BangumiIntroPanel(cid: vdCtr.cid.value)), ], - ); - }, + SliverToBoxAdapter( + child: Divider( + indent: 12, + endIndent: 12, + color: Theme.of(context) + .dividerColor + .withOpacity(0.06), + ), + ), + if (vdCtr.videoType == SearchType.video && + vdCtr.enableRelatedVideo) + const RelatedVideoPanel(), + ], + ); + }, + ), + Obx( + () => VideoReplyPanel( + bvid: vdCtr.bvid, + oid: vdCtr.oid.value, ), - Obx( - () => VideoReplyPanel( - bvid: vdCtr.bvid, - oid: vdCtr.oid.value, - ), - ) - ], - ), + ) + ], ), - ], - ), + ), + ], ), ), ), From 5c0edf873b8c1e959041e563c0598bac8c8afa0f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 26 May 2024 16:26:39 +0800 Subject: [PATCH 202/349] =?UTF-8?q?fix:=20=E5=A4=96=E9=93=BE=E8=B7=B3?= =?UTF-8?q?=E8=BD=AC=20pic=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/url_utils.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/url_utils.dart b/lib/utils/url_utils.dart index 99886e09..573f9f9d 100644 --- a/lib/utils/url_utils.dart +++ b/lib/utils/url_utils.dart @@ -44,7 +44,7 @@ class UrlUtils { final String bv = matchRes['BV']; final Map res = await SearchHttp.ab2cWithPic(bvid: bv); final int cid = res['cid']; - final String pic = res['pic']; + final String? pic = res['pic']; final String heroTag = Utils.makeHeroTag(bv); await Get.toNamed( '/video?bvid=$bv&cid=$cid', From 63d5752d3e797f53b1db7c327952bc94ab9ca06d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 26 May 2024 17:30:42 +0800 Subject: [PATCH 203/349] =?UTF-8?q?fix:=20=E8=BF=9E=E7=BB=AD=E5=BF=AB?= =?UTF-8?q?=E8=BF=9B=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 4 ++-- lib/plugin/pl_player/widgets/backward_seek.dart | 7 +++++++ lib/plugin/pl_player/widgets/forward_seek.dart | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index ff0888be..df68da2f 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -935,7 +935,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekBackwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 500), + duration: const Duration(milliseconds: 200), builder: (BuildContext context, double value, Widget? child) => Opacity( @@ -978,7 +978,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekForwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 500), + duration: const Duration(milliseconds: 200), builder: (BuildContext context, double value, Widget? child) => Opacity( diff --git a/lib/plugin/pl_player/widgets/backward_seek.dart b/lib/plugin/pl_player/widgets/backward_seek.dart index 35de0ae6..8fddf80a 100644 --- a/lib/plugin/pl_player/widgets/backward_seek.dart +++ b/lib/plugin/pl_player/widgets/backward_seek.dart @@ -20,6 +20,13 @@ class BackwardSeekIndicatorState extends State { Timer? timer; + @override + void setState(VoidCallback fn) { + if (mounted) { + super.setState(fn); + } + } + @override void initState() { super.initState(); diff --git a/lib/plugin/pl_player/widgets/forward_seek.dart b/lib/plugin/pl_player/widgets/forward_seek.dart index 43ddd322..7e3886ce 100644 --- a/lib/plugin/pl_player/widgets/forward_seek.dart +++ b/lib/plugin/pl_player/widgets/forward_seek.dart @@ -20,6 +20,13 @@ class ForwardSeekIndicatorState extends State { Timer? timer; + @override + void setState(VoidCallback fn) { + if (mounted) { + super.setState(fn); + } + } + @override void initState() { super.initState(); From 4aa47f4a7bd31cd3085f9200b692884685bb9b41 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 26 May 2024 23:18:37 +0800 Subject: [PATCH 204/349] =?UTF-8?q?fix:=20pip=E8=BF=9B=E5=85=A5=20?= =?UTF-8?q?=E8=AF=84=E8=AE=BA=E5=8C=BA=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/reply/controller.dart | 2 -- lib/pages/video/detail/reply/view.dart | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/reply/controller.dart b/lib/pages/video/detail/reply/controller.dart index b718c840..c1929434 100644 --- a/lib/pages/video/detail/reply/controller.dart +++ b/lib/pages/video/detail/reply/controller.dart @@ -1,5 +1,4 @@ import 'package:easy_debounce/easy_throttle.dart'; -import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/reply.dart'; @@ -15,7 +14,6 @@ class VideoReplyController extends GetxController { this.rpid, this.replyLevel, ); - final ScrollController scrollController = ScrollController(); // 视频aid 请求时使用的oid int? aid; // 层级 2为楼中楼 diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 4c533647..653fe7e0 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -67,13 +67,12 @@ class _VideoReplyPanelState extends State vsync: this, duration: const Duration(milliseconds: 300)); _futureBuilderFuture = _videoReplyController.queryReplyList(); - + scrollController = ScrollController(); fabAnimationCtr.forward(); scrollListener(); } void scrollListener() { - scrollController = _videoReplyController.scrollController; scrollController.addListener( () { if (scrollController.position.pixels >= @@ -185,7 +184,8 @@ class _VideoReplyPanelState extends State builder: (BuildContext context, snapshot) { if (snapshot.connectionState == ConnectionState.done) { var data = snapshot.data; - if (data['status']) { + if (_videoReplyController.replyList.isNotEmpty || + (data && data['status'])) { // 请求成功 return Obx( () => _videoReplyController.isLoadingMore && From 1c6047a3268ab1d01a4a0059912c2b5604aeb267 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 27 May 2024 23:54:33 +0800 Subject: [PATCH 205/349] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 470e9a35..228d17bb 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ QQ频道: https://pd.qq.com/s/365esodk3 - [x] 音质选择(视视频而定) - [x] 解码格式选择(视视频而定) - [x] 弹幕 - - [ ] 字幕 + - [x] 字幕 - [x] 记忆播放 - [x] 视频比例:高度/宽度适应、填充、包含等 From 6b4d24f59566ee12142408b9720809adc23cc86b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 28 May 2024 22:58:51 +0800 Subject: [PATCH 206/349] =?UTF-8?q?opt:=20=E5=8A=A8=E6=80=81=E9=A1=B5up?= =?UTF-8?q?=E4=B8=BB=E7=82=B9=E5=87=BB?= 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, 30 insertions(+), 24 deletions(-) diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index fd0ae642..ee522cbb 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -1,3 +1,4 @@ +import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -30,6 +31,31 @@ class _UpPanelState extends State { liveList = widget.upData.liveList!; } + void onClickUp(data, i) { + currentMid = data.mid; + Get.find().mid.value = data.mid; + Get.find().upInfo.value = data; + Get.find().onSelectUp(data.mid); + int liveLen = liveList.length; + int upLen = upList.length; + double itemWidth = contentWidth + itemPadding.horizontal; + double screenWidth = MediaQuery.sizeOf(context).width; + double moveDistance = 0.0; + if (itemWidth * (upList.length + liveList.length) <= screenWidth) { + } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) { + moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2; + } else { + moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth; + } + data.hasUpdate = false; + scrollController.animateTo( + moveDistance, + duration: const Duration(milliseconds: 200), + curve: Curves.linear, + ); + setState(() {}); + } + @override Widget build(BuildContext context) { listFormat(); @@ -120,30 +146,10 @@ class _UpPanelState extends State { onTap: () { feedBack(); if (data.type == 'up') { - currentMid = data.mid; - Get.find().mid.value = data.mid; - Get.find().upInfo.value = data; - Get.find().onSelectUp(data.mid); - int liveLen = liveList.length; - int upLen = upList.length; - double itemWidth = contentWidth + itemPadding.horizontal; - double screenWidth = MediaQuery.sizeOf(context).width; - double moveDistance = 0.0; - if (itemWidth * (upList.length + liveList.length) <= screenWidth) { - } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) { - moveDistance = - (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2; - } else { - moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth; - } - data.hasUpdate = false; - scrollController.animateTo( - moveDistance, - duration: const Duration(milliseconds: 500), - curve: Curves.easeInOut, - ); - - setState(() {}); + EasyThrottle.throttle('follow', const Duration(milliseconds: 300), + () { + onClickUp(data, i); + }); } else if (data.type == 'live') { LiveItemModel liveItem = LiveItemModel.fromJson({ 'title': data.title, From eef9e3ae9eb3713a14b55fd04f0880132d623beb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 30 May 2024 23:58:42 +0800 Subject: [PATCH 207/349] opt: loading Ani --- assets/loading.json | 1 + assets/trail_loading.json | 1 + lib/pages/video/detail/introduction/view.dart | 12 ++++++------ lib/plugin/pl_player/view.dart | 7 ++++--- pubspec.lock | 8 ++++++++ pubspec.yaml | 8 +++++--- 6 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 assets/loading.json create mode 100644 assets/trail_loading.json diff --git a/assets/loading.json b/assets/loading.json new file mode 100644 index 00000000..38bccbed --- /dev/null +++ b/assets/loading.json @@ -0,0 +1 @@ +{"v":"5.7.11","fr":60,"ip":0,"op":81,"w":1920,"h":1080,"nm":"Loading Dots","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Dot4","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":25,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":39,"s":[100]},{"t":55,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":25,"s":[1142,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":39,"s":[1142,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":55,"s":[1142,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":25,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":39,"s":[75,75,100]},{"t":55,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Dot3","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":17,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":31,"s":[100]},{"t":47,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":17,"s":[1022,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":31,"s":[1022,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":47,"s":[1022,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":17,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":31,"s":[75,75,100]},{"t":47,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Dot2","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":9,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":23,"s":[100]},{"t":39,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":9,"s":[902,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":23,"s":[902,500,0],"to":[0,0,0],"ti":[0,0,0]},{"t":39,"s":[902,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":9,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":23,"s":[75,75,100]},{"t":39,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Dot1","sr":1,"ks":{"o":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0,"s":[25]},{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":14,"s":[100]},{"t":30,"s":[25]}],"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":1,"k":[{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":0,"s":[782,540,0],"to":[0,-6.667,0],"ti":[0,0,0]},{"i":{"x":0.667,"y":1},"o":{"x":0.333,"y":0},"t":14,"s":[782,500,0],"to":[0,0,0],"ti":[0,-6.667,0]},{"t":30,"s":[782,540,0]}],"ix":2,"l":2},"a":{"a":0,"k":[-284,92,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[50,50,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":14,"s":[75,75,100]},{"t":30,"s":[50,50,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[120,120],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"fl","c":{"a":0,"k":[0.0039,0.6157,0.5686,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[-284,92],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":360,"st":0,"bm":0}],"markers":[]} \ No newline at end of file diff --git a/assets/trail_loading.json b/assets/trail_loading.json new file mode 100644 index 00000000..9fb39ea6 --- /dev/null +++ b/assets/trail_loading.json @@ -0,0 +1 @@ +{"v":"4.6.8","fr":60,"ip":0,"op":106,"w":500,"h":500,"nm":"Comp 1","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":2,"ty":4,"nm":"Shape Layer 5","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":20,"s":[0],"e":[360]},{"t":110}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[10,10]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":20,"op":620,"st":20,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"Shape Layer 4","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":15,"s":[0],"e":[360]},{"t":105}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[20,20]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":15,"op":615,"st":15,"bm":0,"sr":1},{"ddd":0,"ind":4,"ty":4,"nm":"Shape Layer 3","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":10,"s":[0],"e":[360]},{"t":100}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[30,30]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":10,"op":610,"st":10,"bm":0,"sr":1},{"ddd":0,"ind":5,"ty":4,"nm":"Shape Layer 2","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":5,"s":[0],"e":[360]},{"t":95}]},"p":{"a":0,"k":[251,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[40,40]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":5,"op":605,"st":5,"bm":0,"sr":1},{"ddd":0,"ind":6,"ty":4,"nm":"Shape Layer 1","ks":{"o":{"a":0,"k":100},"r":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"n":["0p667_1_0p333_0"],"t":0,"s":[0],"e":[360]},{"t":90}]},"p":{"a":0,"k":[250,250,0]},"a":{"a":0,"k":[0,0,0]},"s":{"a":0,"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":1,"k":[{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0"],"t":0,"s":[50,50],"e":[40,40]},{"i":{"x":[0.667,0.667],"y":[1,1]},"o":{"x":[0.333,0.333],"y":[0,0]},"n":["0p667_1_0p333_0","0p667_1_0p333_0"],"t":84,"s":[40,40],"e":[50,50]},{"t":100}]},"p":{"a":0,"k":[0,-100]},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse"},{"ty":"st","c":{"a":0,"k":[0,0,0,1]},"o":{"a":0,"k":100},"w":{"a":0,"k":0},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke"},{"ty":"fl","c":{"a":0,"k":[0,0.7294118,1,1]},"o":{"a":0,"k":100},"r":1,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill"},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"ix":1,"mn":"ADBE Vector Group"}],"ip":0,"op":600,"st":0,"bm":0,"sr":1}]} \ No newline at end of file diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 9103c130..06a026aa 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -5,6 +5,7 @@ import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; +import 'package:lottie/lottie.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/pages/video/detail/index.dart'; @@ -97,11 +98,14 @@ class _VideoIntroPanelState extends State ); } } else { - return const SliverToBoxAdapter( + return SliverToBoxAdapter( child: SizedBox( height: 100, child: Center( - child: CircularProgressIndicator(), + child: Lottie.asset( + 'assets/loading.json', + width: 200, + ), ), ), ); @@ -595,8 +599,4 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); }); } - - // Widget StaffPanel(BuildContext context, videoIntroController) { - // return - // } } diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index df68da2f..6508b624 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -7,6 +7,7 @@ import 'package:flutter_volume_controller/flutter_volume_controller.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:lottie/lottie.dart'; import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:pilipala/models/common/gesture_mode.dart'; @@ -909,9 +910,9 @@ class _PLVideoPlayerState extends State colors: [Colors.black26, Colors.transparent], ), ), - child: Image.asset( - 'assets/images/loading.gif', - height: 25, + child: Lottie.asset( + 'assets/loading.json', + width: 200, ), ), ); diff --git a/pubspec.lock b/pubspec.lock index 8f8a5cae..7ff7ef0d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -846,6 +846,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" + lottie: + dependency: "direct main" + description: + name: lottie + sha256: "6a24ade5d3d918c306bb1c21a6b9a04aab0489d51a2582522eea820b4093b62b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.2" mailer: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index a269cd6f..10a0f065 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -146,6 +146,7 @@ dependencies: expandable: ^5.0.1 # 投屏 dlna_dart: ^0.0.8 + lottie: ^3.1.2 dev_dependencies: flutter_test: @@ -166,15 +167,15 @@ dev_dependencies: build_runner: ^2.4.8 dependency_overrides: - media_kit: + media_kit: git: url: https://github.com/media-kit/media-kit path: media_kit - media_kit_video: + media_kit_video: git: url: https://github.com/media-kit/media-kit path: media_kit_video - media_kit_libs_video: + media_kit_libs_video: git: url: https://github.com/media-kit/media-kit path: libs/universal/media_kit_libs_video @@ -205,6 +206,7 @@ flutter: # To add assets to your application, add an assets section, like this: assets: + - assets/ - assets/images/ - assets/images/lv/ - assets/images/logo/ From e1572c0962b43cf89fb32b02aeb6ceabdb63960f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 31 May 2024 23:37:27 +0800 Subject: [PATCH 208/349] =?UTF-8?q?fix:=20=E9=94=81=E5=AE=9A=E7=8A=B6?= =?UTF-8?q?=E6=80=81=E4=B8=8B=E9=81=BF=E5=85=8D=E6=89=8B=E5=8A=BF=E9=80=80?= =?UTF-8?q?=E5=87=BA=E5=85=A8=E5=B1=8F=20issues=20#753?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index df68da2f..a695e752 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -733,14 +733,18 @@ class _PLVideoPlayerState extends State const double threshold = 7.0; // 滑动阈值 final bool flag = fullScreenGestureMode != FullScreenGestureMode.values.last; - if (dy > _distance.value && dy > threshold) { + if (dy > _distance.value && + dy > threshold && + !_.controlsLock.value) { if (_.isFullScreen.value ^ flag) { lastFullScreenToggleTime = DateTime.now(); // 下滑退出全屏 await widget.controller.triggerFullScreen(status: flag); } _distance.value = 0.0; - } else if (dy < _distance.value && dy < -threshold) { + } else if (dy < _distance.value && + dy < -threshold && + !_.controlsLock.value) { if (!_.isFullScreen.value ^ flag) { lastFullScreenToggleTime = DateTime.now(); // 上滑进入全屏 From 2358ba42e1867b1aa52c7278e10e2fe811455a33 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 1 Jun 2024 00:21:04 +0800 Subject: [PATCH 209/349] opt: pip issues #752 --- lib/pages/video/detail/view.dart | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index bf156c71..fa531684 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -99,7 +99,6 @@ class _VideoDetailPageState extends State fullScreenStatusListener(); if (Platform.isAndroid) { floating = vdCtr.floating!; - autoEnterPip(); } WidgetsBinding.instance.addObserver(this); lifecycleListener(); @@ -128,8 +127,9 @@ class _VideoDetailPageState extends State } // 播放器状态监听 - void playerListener(PlayerStatus? status) async { - playerStatus.value = status!; + void playerListener(PlayerStatus status) async { + playerStatus.value = status; + autoEnterPip(status: status); if (status == PlayerStatus.completed) { // 结束播放退出全屏 if (autoExitFullcreen) { @@ -181,6 +181,7 @@ class _VideoDetailPageState extends State plPlayerController!.addStatusLister(playerListener); vdCtr.autoPlay.value = true; vdCtr.isShowCover.value = false; + autoEnterPip(status: PlayerStatus.playing); } void fullScreenStatusListener() { @@ -287,10 +288,12 @@ class _VideoDetailPageState extends State .subscribe(this, ModalRoute.of(context)! as PageRoute); } - void autoEnterPip() { + void autoEnterPip({PlayerStatus? status}) { final String routePath = Get.currentRoute; if (autoPiP && routePath.startsWith('/video')) { - floating.toggleAutoPip(autoEnter: autoPiP); + floating.toggleAutoPip( + autoEnter: autoPiP && status == PlayerStatus.playing, + ); } } @@ -314,6 +317,15 @@ class _VideoDetailPageState extends State case 'show' || 'restart': plPlayerController?.danmakuController?.clear(); break; + case 'pause': + vdCtr.hiddenReplyReplyPanel(); + if (vdCtr.videoType == SearchType.video) { + videoIntroController.hiddenEpisodeBottomSheet(); + } + if (vdCtr.videoType == SearchType.media_bangumi) { + bangumiIntroController.hiddenEpisodeBottomSheet(); + } + break; } } From bde44af9a0afd283c247c6fac7bcd6eb740eb730 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 1 Jun 2024 22:43:27 +0800 Subject: [PATCH 210/349] opt: main.daat code --- lib/main.dart | 314 ++++++++++++++++++++++++++++---------------------- 1 file changed, 177 insertions(+), 137 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 066bb599..1a2ce989 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,7 +23,7 @@ import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; -import 'package:media_kit/media_kit.dart'; // Provides [Player], [Media], [Playlist] etc. +import 'package:media_kit/media_kit.dart'; import 'package:pilipala/utils/recommend_filter.dart'; import 'package:catcher_2/catcher_2.dart'; import './services/loggeer.dart'; @@ -31,59 +31,42 @@ import './services/loggeer.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); MediaKit.ensureInitialized(); - SystemChrome.setPreferredOrientations( - [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]) - .then((_) async { - await GStrorage.init(); - await setupServiceLocator(); - clearLogs(); - Request(); - await Request.setCookie(); - RecommendFilter(); + await SystemChrome.setPreferredOrientations( + [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); + await GStrorage.init(); + await setupServiceLocator(); + clearLogs(); + Request(); + await Request.setCookie(); - // 异常捕获 logo记录 - final Catcher2Options debugConfig = Catcher2Options( - SilentReportMode(), - [ - FileHandler(await getLogsPath()), - ConsoleHandler( - enableDeviceParameters: false, - enableApplicationParameters: false, - ) - ], - ); + // 异常捕获 logo记录 + final Catcher2Options releaseConfig = Catcher2Options( + SilentReportMode(), + [FileHandler(await getLogsPath())], + ); - final Catcher2Options releaseConfig = Catcher2Options( - SilentReportMode(), - [FileHandler(await getLogsPath())], - ); + Catcher2( + releaseConfig: releaseConfig, + runAppFunction: () { + runApp(const MyApp()); + }, + ); - Catcher2( - debugConfig: debugConfig, - releaseConfig: releaseConfig, - runAppFunction: () { - runApp(const MyApp()); - }, - ); - - // 小白条、导航栏沉浸 - if (Platform.isAndroid) { - final androidInfo = await DeviceInfoPlugin().androidInfo; - if (androidInfo.version.sdkInt >= 29) { - SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - } - SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( - systemNavigationBarColor: Colors.transparent, - systemNavigationBarDividerColor: Colors.transparent, - statusBarColor: Colors.transparent, - )); + // 小白条、导航栏沉浸 + if (Platform.isAndroid) { + final androidInfo = await DeviceInfoPlugin().androidInfo; + if (androidInfo.version.sdkInt >= 29) { + SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); } + SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle( + systemNavigationBarColor: Colors.transparent, + systemNavigationBarDividerColor: Colors.transparent, + statusBarColor: Colors.transparent, + )); + } - Data.init(); - GlobalData(); - PiliSchame.init(); - DisableBatteryOpt(); - }); + PiliSchame.init(); + DisableBatteryOpt(); } class MyApp extends StatelessWidget { @@ -124,6 +107,39 @@ class MyApp extends StatelessWidget { } catch (_) {} } + if (Platform.isAndroid) { + return AndroidApp( + brandColor: brandColor, + isDynamicColor: isDynamicColor, + currentThemeValue: currentThemeValue, + textScale: textScale, + ); + } else { + return OtherApp( + brandColor: brandColor, + currentThemeValue: currentThemeValue, + textScale: textScale, + ); + } + } +} + +class AndroidApp extends StatelessWidget { + const AndroidApp({ + super.key, + required this.brandColor, + required this.isDynamicColor, + required this.currentThemeValue, + required this.textScale, + }); + + final Color brandColor; + final bool isDynamicColor; + final ThemeType currentThemeValue; + final double textScale; + + @override + Widget build(BuildContext context) { return DynamicColorBuilder( builder: ((ColorScheme? lightDynamic, ColorScheme? darkDynamic) { ColorScheme? lightColorScheme; @@ -143,96 +159,120 @@ class MyApp extends StatelessWidget { brightness: Brightness.dark, ); } - - // ThemeData themeData = ThemeData( - // colorScheme: currentThemeValue == ThemeType.dark - // ? darkColorScheme - // : lightColorScheme, - // ); - - // // 小白条、导航栏沉浸 - // if (Platform.isAndroid) { - // List versionParts = Platform.version.split('.'); - // int androidVersion = int.parse(versionParts[0]); - // if (androidVersion >= 29) { - // SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge); - // } - // SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - // systemNavigationBarColor: GlobalData().enableMYBar - // ? const Color(0x00010000) - // : themeData.canvasColor, - // systemNavigationBarDividerColor: GlobalData().enableMYBar - // ? const Color(0x00010000) - // : themeData.canvasColor, - // systemNavigationBarIconBrightness: - // currentThemeValue == ThemeType.dark - // ? Brightness.light - // : Brightness.dark, - // statusBarColor: Colors.transparent, - // )); - // } - - // 图片缓存 - // PaintingBinding.instance.imageCache.maximumSizeBytes = 1000 << 20; - return GetMaterialApp( - title: 'PiliPala', - theme: ThemeData( - colorScheme: currentThemeValue == ThemeType.dark - ? darkColorScheme - : lightColorScheme, - snackBarTheme: SnackBarThemeData( - actionTextColor: lightColorScheme.primary, - backgroundColor: lightColorScheme.secondaryContainer, - closeIconColor: lightColorScheme.secondary, - contentTextStyle: TextStyle(color: lightColorScheme.secondary), - elevation: 20, - ), - pageTransitionsTheme: const PageTransitionsTheme( - builders: { - TargetPlatform.android: ZoomPageTransitionsBuilder( - allowEnterRouteSnapshotting: false, - ), - }, - ), - ), - darkTheme: ThemeData( - colorScheme: currentThemeValue == ThemeType.light - ? lightColorScheme - : darkColorScheme, - snackBarTheme: SnackBarThemeData( - actionTextColor: darkColorScheme.primary, - backgroundColor: darkColorScheme.secondaryContainer, - closeIconColor: darkColorScheme.secondary, - contentTextStyle: TextStyle(color: darkColorScheme.secondary), - elevation: 20, - ), - ), - localizationsDelegates: const [ - GlobalCupertinoLocalizations.delegate, - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ], - locale: const Locale("zh", "CN"), - supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")], - fallbackLocale: const Locale("zh", "CN"), - getPages: Routes.getPages, - home: const MainApp(), - builder: (BuildContext context, Widget? child) { - return FlutterSmartDialog( - toastBuilder: (String msg) => CustomToast(msg: msg), - child: MediaQuery( - data: MediaQuery.of(context) - .copyWith(textScaler: TextScaler.linear(textScale)), - child: child!, - ), - ); - }, - navigatorObservers: [ - VideoDetailPage.routeObserver, - SearchPage.routeObserver, - ], + return BuildMainApp( + lightColorScheme: lightColorScheme, + darkColorScheme: darkColorScheme, + currentThemeValue: currentThemeValue, + textScale: textScale, ); }), ); } } + +class OtherApp extends StatelessWidget { + const OtherApp({ + super.key, + required this.brandColor, + required this.currentThemeValue, + required this.textScale, + }); + + final Color brandColor; + final ThemeType currentThemeValue; + final double textScale; + + @override + Widget build(BuildContext context) { + return BuildMainApp( + lightColorScheme: ColorScheme.fromSeed( + seedColor: brandColor, + brightness: Brightness.light, + ), + darkColorScheme: ColorScheme.fromSeed( + seedColor: brandColor, + brightness: Brightness.dark, + ), + currentThemeValue: currentThemeValue, + textScale: textScale, + ); + } +} + +class BuildMainApp extends StatelessWidget { + const BuildMainApp({ + super.key, + required this.lightColorScheme, + required this.darkColorScheme, + required this.currentThemeValue, + required this.textScale, + }); + + final ColorScheme lightColorScheme; + final ColorScheme darkColorScheme; + final ThemeType currentThemeValue; + final double textScale; + + @override + Widget build(BuildContext context) { + final SnackBarThemeData snackBarTheme = SnackBarThemeData( + actionTextColor: lightColorScheme.primary, + backgroundColor: lightColorScheme.secondaryContainer, + closeIconColor: lightColorScheme.secondary, + contentTextStyle: TextStyle(color: lightColorScheme.secondary), + elevation: 20, + ); + + return GetMaterialApp( + title: 'PiliPala', + theme: ThemeData( + colorScheme: currentThemeValue == ThemeType.dark + ? darkColorScheme + : lightColorScheme, + snackBarTheme: snackBarTheme, + pageTransitionsTheme: const PageTransitionsTheme( + builders: { + TargetPlatform.android: ZoomPageTransitionsBuilder( + allowEnterRouteSnapshotting: false, + ), + }, + ), + ), + darkTheme: ThemeData( + colorScheme: currentThemeValue == ThemeType.light + ? lightColorScheme + : darkColorScheme, + snackBarTheme: snackBarTheme, + ), + localizationsDelegates: const [ + GlobalCupertinoLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ], + locale: const Locale("zh", "CN"), + supportedLocales: const [Locale("zh", "CN"), Locale("en", "US")], + fallbackLocale: const Locale("zh", "CN"), + getPages: Routes.getPages, + home: const MainApp(), + builder: (BuildContext context, Widget? child) { + return FlutterSmartDialog( + toastBuilder: (String msg) => CustomToast(msg: msg), + child: MediaQuery( + data: MediaQuery.of(context) + .copyWith(textScaler: TextScaler.linear(textScale)), + child: child!, + ), + ); + }, + navigatorObservers: [ + VideoDetailPage.routeObserver, + SearchPage.routeObserver, + ], + onInit: () { + RecommendFilter(); + Data.init(); + GlobalData(); + }, + ); + } +} From 8f26884164a5a114529b02efc1875c5387e32bfc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 2 Jun 2024 01:04:51 +0800 Subject: [PATCH 211/349] =?UTF-8?q?upgrade:=20=E4=BE=9D=E8=B5=96=E5=8D=87?= =?UTF-8?q?=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- android/build.gradle | 2 +- ios/Flutter/AppFrameworkInfo.plist | 2 +- ios/Podfile.lock | 14 +- ios/Runner.xcodeproj/project.pbxproj | 2 +- .../xcshareddata/xcschemes/Runner.xcscheme | 2 +- lib/pages/bangumi/view.dart | 7 +- .../pl_player/widgets/bottom_control.dart | 3 +- pubspec.lock | 160 ++++++++++-------- pubspec.yaml | 32 ++-- 9 files changed, 116 insertions(+), 108 deletions(-) diff --git a/android/build.gradle b/android/build.gradle index 713d7f6e..674e96f4 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,5 +1,5 @@ buildscript { - ext.kotlin_version = '1.7.10' + ext.kotlin_version = '1.9.0' repositories { google() mavenCentral() diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 9625e105..7c569640 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2c1a635b..04fe6670 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -10,7 +10,6 @@ PODS: - connectivity_plus (0.0.1): - Flutter - FlutterMacOS - - ReachabilitySwift - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) @@ -41,7 +40,6 @@ PODS: - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter - - ReachabilitySwift (5.0.0) - saver_gallery (0.0.1): - Flutter - screen_brightness_ios (0.1.0): @@ -101,7 +99,6 @@ SPEC REPOS: trunk: - FMDB - GT3Captcha-iOS - - ReachabilitySwift - Toast EXTERNAL SOURCES: @@ -167,9 +164,9 @@ SPEC CHECKSUMS: audio_service: f509d65da41b9521a61f1c404dd58651f265a567 audio_session: 4f3e461722055d21515cf3261b64c973c062f345 auto_orientation: 102ed811a5938d52c86520ddd7ecd3a126b5d39d - connectivity_plus: e2dad488011aeb593e219360e804c43cc1af5770 + connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db device_info_plus: c6fb39579d0f423935b0c9ce7ee2f44b71b9fce6 - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 flutter_mailer: 2ef5a67087bc8c6c4cefd04a178bf1ae2c94cd83 flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529 fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265 @@ -182,7 +179,6 @@ SPEC CHECKSUMS: package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 - ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 share_plus: c3fef564749587fc939ef86ffb283ceac0baf9f5 @@ -190,11 +186,11 @@ SPEC CHECKSUMS: status_bar_control: 7c84146799e6a076315cc1550f78ef53aae3e446 system_proxy: bec1a5c5af67dd3e3ebf43979400a8756c04cc44 Toast: ec33c32b8688982cecc6348adeae667c1b9938da - url_launcher_ios: bf5ce03e0e2088bad9cc378ea97fa0ed5b49673b + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 - wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 + wakelock_plus: 78ec7c5b202cab7761af8e2b2b3d0671be6c4ae1 webview_cookie_manager: eaf920722b493bd0f7611b5484771ca53fed03f7 - webview_flutter_wkwebview: 4f3e50f7273d31e5500066ed267e3ae4309c5ae4 + webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36 PODFILE CHECKSUM: 637cd290bed23275b5f5ffcc7eb1e73d0a5fb2be diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index bac856d2..55565d40 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -156,7 +156,7 @@ 97C146E61CF9000F007C117D /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 97C146ED1CF9000F007C117D = { diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index a6b826db..5e31d3d3 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ ), ); } else { - return nil; + return const SizedBox(); } } else { - return nil; + return const SizedBox(); } }, ), @@ -216,7 +215,7 @@ class _BangumiPageState extends State (BuildContext context, int index) { return bangumiList!.isNotEmpty ? BangumiCardV(bangumiItem: bangumiList[index]) - : nil; + : const SizedBox(); }, childCount: bangumiList!.isNotEmpty ? bangumiList!.length : 10, ), diff --git a/lib/plugin/pl_player/widgets/bottom_control.dart b/lib/plugin/pl_player/widgets/bottom_control.dart index 35e7792a..b3ff37db 100644 --- a/lib/plugin/pl_player/widgets/bottom_control.dart +++ b/lib/plugin/pl_player/widgets/bottom_control.dart @@ -1,7 +1,6 @@ import 'package:audio_video_progress_bar/audio_video_progress_bar.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; -import 'package:nil/nil.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/utils/feed_back.dart'; @@ -36,7 +35,7 @@ class BottomControl extends StatelessWidget implements PreferredSizeWidget { final int max = _.durationSeconds.value; final int buffer = _.bufferedSeconds.value; if (value > max || max <= 0) { - return nil; + return const SizedBox(); } return Padding( padding: const EdgeInsets.only(left: 7, right: 7, bottom: 6), diff --git a/pubspec.lock b/pubspec.lock index 8f8a5cae..de434f2e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,14 +17,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.2.0" - animations: - dependency: "direct main" - description: - name: animations - sha256: d3d6dcfb218225bbe68e87ccf6378bbb2e32a94900722c5f81611dad089911cb - url: "https://pub.flutter-io.cn" - source: hosted - version: "2.0.11" appscheme: dependency: "direct main" description: @@ -101,10 +93,10 @@ packages: dependency: "direct main" description: name: audio_video_progress_bar - sha256: ccc7d7b83d2a16c52d4a7fb332faabd1baa053fb0e4c16815aefd3945ab33b81 + sha256: "552b1f73c56c4c88407999e0a8507176f60c56de3e6d63bc20a0eab48467d4c9" url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.2" + version: "2.0.3" auto_orientation: dependency: "direct main" description: @@ -213,10 +205,10 @@ packages: dependency: "direct main" description: name: catcher_2 - sha256: "9cf33d2befd10058374e5fc6177577fdd938d73d9c06810de81cf91311a7ce98" + sha256: "2c2c6f8cf8c817730cd1dbb010d55292396930e7a3d42c04c3039e3fd411a2f8" url: "https://pub.flutter-io.cn" source: hosted - version: "1.2.3" + version: "1.2.6" characters: dependency: transitive description: @@ -269,10 +261,10 @@ packages: dependency: "direct main" description: name: connectivity_plus - sha256: e9feae83b1849f61bad9f6f33ee00646e3410d54ce0821e02f262f9901dad3c9 + sha256: db7a4e143dc72cc3cb2044ef9b052a7ebfe729513e6a82943bc3526f784365b8 url: "https://pub.flutter-io.cn" source: hosted - version: "6.0.1" + version: "6.0.3" connectivity_plus_platform_interface: dependency: transitive description: @@ -301,10 +293,10 @@ packages: dependency: transitive description: name: cross_file - sha256: fedaadfa3a6996f75211d835aaeb8fede285dae94262485698afd832371b9a5e + sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.3+8" + version: "0.3.4+1" crypto: dependency: "direct main" description: @@ -333,10 +325,10 @@ packages: dependency: "direct main" description: name: custom_sliding_segmented_control - sha256: "05b73fa48d57218bfdf806bad68a859812b216cd81fe81c6cbefde89f39eb257" + sha256: "53c3e931c3ae1f696085d1ec70ac8e934da836595a9b7d9b88fdd0fcbf2a5574" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.1" + version: "1.8.3" dart_style: dependency: transitive description: @@ -461,10 +453,10 @@ packages: dependency: transitive description: name: extended_image_library - sha256: "9b55fc5ebc65fad984de66b8f177a1bef2a84d79203c9c213f75ff83c2c29edd" + sha256: c9caee8fe9b6547bd41c960c4f2d1ef8e34321804de6a1777f1d614a24247ad6 url: "https://pub.flutter-io.cn" source: hosted - version: "4.0.1" + version: "4.0.4" extended_list: dependency: transitive description: @@ -621,10 +613,10 @@ packages: dependency: "direct main" description: name: flutter_volume_controller - sha256: "0f10cc759499cb6c3e152a8f6ff8e5ce385b99db7e1f586d1a29d8e6c11f4082" + sha256: fa4c36dfe7ef7f423704f34ab8e64e00b4a30a90aa6e56f251e9dba649efcd7f url: "https://pub.flutter-io.cn" source: hosted - version: "1.3.1" + version: "1.3.2" flutter_web_plugins: dependency: transitive description: flutter @@ -722,10 +714,10 @@ packages: dependency: transitive description: name: http - sha256: d4872660c46d929f6b8a9ef4e7a7eff7e49bbf0c4ec3f385ee32df5119175139 + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.2" + version: "1.2.1" http2: dependency: transitive description: @@ -770,10 +762,10 @@ packages: dependency: transitive description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.flutter-io.cn" source: hosted - version: "0.18.1" + version: "0.19.0" io: dependency: transitive description: @@ -798,6 +790,30 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.8.1" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + url: "https://pub.flutter-io.cn" + source: hosted + version: "10.0.4" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.3" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" lints: dependency: transitive description: @@ -834,10 +850,10 @@ packages: dependency: "direct main" description: name: logger - sha256: "6bbb9d6f7056729537a4309bda2e74e18e5d9f14302489cc1e93f33b3fe32cac" + sha256: af05cc8714f356fd1f3888fb6741cbe9fbe25cdb6eedbab80e1a6db21047d4a4 url: "https://pub.flutter-io.cn" source: hosted - version: "2.0.2+1" + version: "2.3.0" logging: dependency: transitive description: @@ -850,26 +866,26 @@ packages: dependency: transitive description: name: mailer - sha256: "57f6dd1496699999a7bfd0aa6be0645384f477f4823e16d4321c40a434346382" + sha256: d25d89555c1031abacb448f07b801d7c01b4c21d4558e944b12b64394c84a3cb url: "https://pub.flutter-io.cn" source: hosted - version: "6.0.1" + version: "6.1.0" matcher: dependency: transitive description: name: matcher - sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.flutter-io.cn" source: hosted - version: "0.12.16" + version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" url: "https://pub.flutter-io.cn" source: hosted - version: "0.5.0" + version: "0.8.0" media_kit: dependency: "direct main" description: @@ -949,10 +965,10 @@ packages: dependency: transitive description: name: meta - sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.flutter-io.cn" source: hosted - version: "1.10.0" + version: "1.12.0" mime: dependency: transitive description: @@ -961,14 +977,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.0.4" - nil: - dependency: "direct main" - description: - name: nil - sha256: ef05770c48942876d843bf6a4822d35e5da0ff893a61f1d5ad96d15c4a659136 - url: "https://pub.flutter-io.cn" - source: hosted - version: "1.1.1" nm: dependency: transitive description: @@ -1022,10 +1030,10 @@ packages: dependency: "direct main" description: name: path - sha256: "8829d8a55c13fc0e37127c29fedf290c102f4e40ae94ada574091fe0ff96c917" + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.flutter-io.cn" source: hosted - version: "1.8.3" + version: "1.9.0" path_parsing: dependency: transitive description: @@ -1435,10 +1443,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.flutter-io.cn" source: hosted - version: "0.6.1" + version: "0.7.0" timing: dependency: transitive description: @@ -1467,10 +1475,10 @@ packages: dependency: "direct main" description: name: universal_platform - sha256: d315be0f6641898b280ffa34e2ddb14f3d12b1a37882557869646e0cc363d0cc + sha256: "64e16458a0ea9b99260ceb5467a214c1f298d647c659af1bff6d3bf82536b1ec" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.0+1" + version: "1.1.0" uri_parser: dependency: transitive description: @@ -1483,10 +1491,10 @@ packages: dependency: "direct main" description: name: url_launcher - sha256: e9aa5ea75c84cf46b3db4eea212523591211c3cf2e13099ee4ec147f54201c86 + sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" url: "https://pub.flutter-io.cn" source: hosted - version: "6.2.2" + version: "6.2.6" url_launcher_android: dependency: transitive description: @@ -1499,10 +1507,10 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: bba3373219b7abb6b5e0d071b0fe66dfbe005d07517a68e38d4fc3638f35c6d3 + sha256: "7068716403343f6ba4969b4173cbf3b84fc768042124bc2c011e5d782b24fe89" url: "https://pub.flutter-io.cn" source: hosted - version: "6.2.1" + version: "6.3.0" url_launcher_linux: dependency: transitive description: @@ -1531,10 +1539,10 @@ packages: dependency: transitive description: name: url_launcher_web - sha256: "7286aec002c8feecc338cc33269e96b73955ab227456e9fb2a91f7fab8a358e9" + sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" url: "https://pub.flutter-io.cn" source: hosted - version: "2.2.2" + version: "2.3.1" url_launcher_windows: dependency: transitive description: @@ -1591,6 +1599,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "0.4.0+2" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + url: "https://pub.flutter-io.cn" + source: hosted + version: "14.2.1" volume_controller: dependency: transitive description: @@ -1603,18 +1619,18 @@ packages: dependency: "direct main" description: name: wakelock_plus - sha256: f268ca2116db22e57577fb99d52515a24bdc1d570f12ac18bb762361d43b043d + sha256: "104d94837bb28c735894dcd592877e990149c380e6358b00c04398ca1426eed4" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.4" + version: "1.2.1" wakelock_plus_platform_interface: dependency: transitive description: name: wakelock_plus_platform_interface - sha256: "40fabed5da06caff0796dc638e1f07ee395fb18801fbff3255a2372db2d80385" + sha256: "582f2f7aecc7376332d961a0dd1efa9378ce117657e0ade55d9ff72699a55e82" url: "https://pub.flutter-io.cn" source: hosted - version: "1.1.0" + version: "1.2.0" watcher: dependency: transitive description: @@ -1635,10 +1651,10 @@ packages: dependency: transitive description: name: web - sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" url: "https://pub.flutter-io.cn" source: hosted - version: "0.3.0" + version: "0.5.1" web_socket_channel: dependency: transitive description: @@ -1659,34 +1675,34 @@ packages: dependency: "direct main" description: name: webview_flutter - sha256: d81b68e88cc353e546afb93fb38958e3717282c5ac6e5d3be4a4aef9fc3c1413 + sha256: "6869c8786d179f929144b4a1f86e09ac0eddfe475984951ea6c634774c16b522" url: "https://pub.flutter-io.cn" source: hosted - version: "4.5.0" + version: "4.8.0" webview_flutter_android: dependency: transitive description: name: webview_flutter_android - sha256: e313dcdf45d4c95bcb8960351ef2389b7f0687b90bc92483f7f7983ae5758456 + sha256: f42447ca49523f11d8f70abea55ea211b3cafe172dd7a0e7ac007bb35dd356dc url: "https://pub.flutter-io.cn" source: hosted - version: "3.13.0" + version: "3.16.4" webview_flutter_platform_interface: dependency: transitive description: name: webview_flutter_platform_interface - sha256: "68e86162aa8fc646ae859e1585995c096c95fc2476881fa0c4a8d10f56013a5a" + sha256: d937581d6e558908d7ae3dc1989c4f87b786891ab47bb9df7de548a151779d8d url: "https://pub.flutter-io.cn" source: hosted - version: "2.8.0" + version: "2.10.0" webview_flutter_wkwebview: dependency: transitive description: name: webview_flutter_wkwebview - sha256: "4d062ad505390ecef1c4bfb6001cd857a51e00912cc9dfb66edb1886a9ebd80c" + sha256: "7affdf9d680c015b11587181171d3cad8093e449db1f7d9f0f08f4f33d24f9a0" url: "https://pub.flutter-io.cn" source: hosted - version: "3.10.2" + version: "3.13.1" win32: dependency: transitive description: @@ -1728,5 +1744,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0 <4.0.0" - flutter: ">=3.16.0" + dart: ">=3.4.0 <4.0.0" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index a269cd6f..b9390dd9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -44,7 +44,7 @@ dependencies: dio: ^5.4.1 cookie_jar: ^4.0.8 dio_cookie_manager: ^3.1.1 - connectivity_plus: ^6.0.1 + connectivity_plus: ^6.0.3 dio_http2_adapter: ^2.3.1+1 # 图片 @@ -66,7 +66,7 @@ dependencies: # cookie 管理 webview_cookie_manager: ^2.0.6 # 浏览器 - webview_flutter: ^4.5.0 + webview_flutter: ^4.8.0 # 解决sliver滑动不同步 extended_nested_scroll_view: ^6.2.1 # 上拉加载 @@ -79,7 +79,7 @@ dependencies: flutter_smart_dialog: ^4.9.4 # 下滑关闭 dismissible_page: ^1.0.2 - custom_sliding_segmented_control: ^1.7.5 + custom_sliding_segmented_control: ^1.8.3 # 加密 crypto: ^3.0.3 encrypt: ^5.0.3 @@ -94,19 +94,18 @@ dependencies: audio_session: ^0.1.18 # 音量、亮度、屏幕控制 - flutter_volume_controller: ^1.3.1 + flutter_volume_controller: ^1.3.2 screen_brightness: ^0.2.2+1 - wakelock_plus: ^1.1.1 - universal_platform: ^1.0.0+1 + wakelock_plus: ^1.1.6 + universal_platform: ^1.1.0 # 进度条 - audio_video_progress_bar: ^2.0.2 + audio_video_progress_bar: ^2.0.3 auto_orientation: ^2.3.1 protobuf: ^3.0.0 - animations: ^2.0.11 # 获取appx信息 - package_info_plus: ^4.1.0 - url_launcher: ^6.1.14 + package_info_plus: ^4.2.0 + url_launcher: ^6.2.6 flutter_svg: ^2.0.10+1 # 防抖节流 easy_debounce: ^2.0.3 @@ -136,10 +135,9 @@ dependencies: gt3_flutter_plugin: ^0.0.8 uuid: ^3.0.7 scrollable_positioned_list: ^0.3.8 - nil: ^1.1.1 - catcher_2: ^1.2.3 - logger: ^2.0.2+1 - path: 1.8.3 + catcher_2: ^1.2.6 + logger: ^2.3.0 + path: ^1.9.0 # 电池优化 disable_battery_optimization: ^1.1.1 # 展开/收起 @@ -166,15 +164,15 @@ dev_dependencies: build_runner: ^2.4.8 dependency_overrides: - media_kit: + media_kit: git: url: https://github.com/media-kit/media-kit path: media_kit - media_kit_video: + media_kit_video: git: url: https://github.com/media-kit/media-kit path: media_kit_video - media_kit_libs_video: + media_kit_libs_video: git: url: https://github.com/media-kit/media-kit path: libs/universal/media_kit_libs_video From 3b1ad133db0a9e8bdec5d47ec44e030dc4ee8367 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 2 Jun 2024 22:32:00 +0800 Subject: [PATCH 212/349] mod: logs replace# --- lib/pages/setting/pages/logs.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pages/setting/pages/logs.dart b/lib/pages/setting/pages/logs.dart index 0958edb8..f497aee5 100644 --- a/lib/pages/setting/pages/logs.dart +++ b/lib/pages/setting/pages/logs.dart @@ -41,7 +41,8 @@ class _LogsPageState extends State { .replaceAll('DEVICE INFO', '设备信息') .replaceAll('APP INFO', '应用信息') .replaceAll('ERROR', '错误信息') - .replaceAll('STACK TRACE', '错误堆栈'); + .replaceAll('STACK TRACE', '错误堆栈') + .replaceAll('#', 'Line'); }).toList(); List> result = []; for (String i in contentList) { @@ -50,7 +51,7 @@ class _LogsPageState extends State { .split("\n") .map((l) { if (l.startsWith("Crash occurred on")) { - date = DateTime.parse( + date = DateTime.tryParse( l.split("Crash occurred on")[1].trim().split('.')[0], ); return ""; From e0e1c6b08af706f79856074ba4b3ea5a53806fc4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 4 Jun 2024 23:59:23 +0800 Subject: [PATCH 213/349] upgrade: color --- lib/common/pages_bottom_sheet.dart | 2 +- lib/common/skeleton/skeleton.dart | 4 ++-- lib/common/widgets/stat/danmu.dart | 2 +- lib/common/widgets/stat/view.dart | 2 +- .../introduction/widgets/intro_detail.dart | 4 ++-- lib/pages/dynamics/view.dart | 5 ++--- lib/pages/dynamics/widgets/additional_panel.dart | 2 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- lib/pages/dynamics/widgets/up_panel.dart | 4 ++-- lib/pages/search/widgets/search_text.dart | 5 ++++- lib/pages/setting/pages/font_size_select.dart | 2 +- .../detail/introduction/widgets/fav_panel.dart | 2 +- .../detail/introduction/widgets/group_panel.dart | 2 +- .../detail/introduction/widgets/menu_row.dart | 4 ++-- lib/pages/video/detail/reply_new/view.dart | 2 +- lib/pages/video/detail/reply_reply/view.dart | 2 +- lib/pages/video/detail/widgets/ai_detail.dart | 2 +- lib/pages/video/detail/widgets/app_bar.dart | 2 +- .../video/detail/widgets/header_control.dart | 16 ++++++++-------- lib/utils/image_save.dart | 2 +- 20 files changed, 35 insertions(+), 33 deletions(-) diff --git a/lib/common/pages_bottom_sheet.dart b/lib/common/pages_bottom_sheet.dart index 2a56eb41..49e9b4d8 100644 --- a/lib/common/pages_bottom_sheet.dart +++ b/lib/common/pages_bottom_sheet.dart @@ -124,7 +124,7 @@ class EpisodeBottomSheet { }); return Container( height: sheetHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ buildTitle(), diff --git a/lib/common/skeleton/skeleton.dart b/lib/common/skeleton/skeleton.dart index 34e87f55..b17a55fc 100644 --- a/lib/common/skeleton/skeleton.dart +++ b/lib/common/skeleton/skeleton.dart @@ -13,8 +13,8 @@ class Skeleton extends StatelessWidget { var shimmerGradient = LinearGradient( colors: [ Colors.transparent, - Theme.of(context).colorScheme.background.withAlpha(10), - Theme.of(context).colorScheme.background.withAlpha(10), + Theme.of(context).colorScheme.surface.withAlpha(10), + Theme.of(context).colorScheme.surface.withAlpha(10), Colors.transparent, ], stops: const [ diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart index c1c439db..511839a0 100644 --- a/lib/common/widgets/stat/danmu.dart +++ b/lib/common/widgets/stat/danmu.dart @@ -14,7 +14,7 @@ class StatDanMu extends StatelessWidget { Map colorObject = { 'white': Colors.white, 'gray': Theme.of(context).colorScheme.outline, - 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), + 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), }; Color color = colorObject[theme]!; return Row( diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart index 2665e2d4..5359c979 100644 --- a/lib/common/widgets/stat/view.dart +++ b/lib/common/widgets/stat/view.dart @@ -14,7 +14,7 @@ class StatView extends StatelessWidget { Map colorObject = { 'white': Colors.white, 'gray': Theme.of(context).colorScheme.outline, - 'black': Theme.of(context).colorScheme.onBackground.withOpacity(0.8), + 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), }; Color color = colorObject[theme]!; return Row( diff --git a/lib/pages/bangumi/introduction/widgets/intro_detail.dart b/lib/pages/bangumi/introduction/widgets/intro_detail.dart index c5bbd566..07684a86 100644 --- a/lib/pages/bangumi/introduction/widgets/intro_detail.dart +++ b/lib/pages/bangumi/introduction/widgets/intro_detail.dart @@ -20,10 +20,10 @@ class IntroDetail extends StatelessWidget { sheetHeight = localCache.get('sheetHeight'); TextStyle smallTitle = TextStyle( fontSize: 12, - color: Theme.of(context).colorScheme.onBackground, + color: Theme.of(context).colorScheme.onSurface, ); return Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(left: 14, right: 14), height: sheetHeight, child: Column( diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index da15239d..258ad531 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -162,13 +162,12 @@ class _DynamicsPageState extends State decoration: BoxDecoration( color: Theme.of(context) .colorScheme - .surfaceVariant + .surfaceContainerHighest .withOpacity(0.7), borderRadius: BorderRadius.circular(20), ), thumbDecoration: BoxDecoration( - color: - Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(20), ), duration: const Duration(milliseconds: 300), diff --git a/lib/pages/dynamics/widgets/additional_panel.dart b/lib/pages/dynamics/widgets/additional_panel.dart index fa11f217..50e1b6d3 100644 --- a/lib/pages/dynamics/widgets/additional_panel.dart +++ b/lib/pages/dynamics/widgets/additional_panel.dart @@ -19,7 +19,7 @@ Widget addWidget(item, context, type, {floor = 1}) { }; Color bgColor = floor == 1 ? Theme.of(context).dividerColor.withOpacity(0.08) - : Theme.of(context).colorScheme.background; + : Theme.of(context).colorScheme.surface; switch (type) { case 'ADDITIONAL_TYPE_UGC': // 转发的投稿 diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 0d3baecd..8acdc26a 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -52,7 +52,7 @@ class AuthorPanel extends StatelessWidget { color: item.modules.moduleAuthor!.vip != null && item.modules.moduleAuthor!.vip['status'] > 0 ? const Color.fromARGB(255, 251, 100, 163) - : Theme.of(context).colorScheme.onBackground, + : Theme.of(context).colorScheme.onSurface, fontSize: Theme.of(context).textTheme.titleSmall!.fontSize, ), ), diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index ee522cbb..f8c973a0 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -69,7 +69,7 @@ class _UpPanelState extends State { crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(left: 16, right: 16), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, @@ -95,7 +95,7 @@ class _UpPanelState extends State { ), Container( height: 90, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Row( children: [ Flexible( diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index 039a851b..1b96d412 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -16,7 +16,10 @@ class SearchText extends StatelessWidget { @override Widget build(BuildContext context) { return Material( - color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), + color: Theme.of(context) + .colorScheme + .surfaceContainerHighest + .withOpacity(0.5), borderRadius: BorderRadius.circular(6), child: Padding( padding: EdgeInsets.zero, diff --git a/lib/pages/setting/pages/font_size_select.dart b/lib/pages/setting/pages/font_size_select.dart index 4985c83f..f5ca6be3 100644 --- a/lib/pages/setting/pages/font_size_select.dart +++ b/lib/pages/setting/pages/font_size_select.dart @@ -66,7 +66,7 @@ class _FontSizeSelectPageState extends State { .colorScheme .primary .withOpacity(0.3))), - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), child: Row( children: [ diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index 517caeaa..5ef78967 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -29,7 +29,7 @@ class _FavPanelState extends State { Widget build(BuildContext context) { return Container( height: sheetHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ AppBar( diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/pages/video/detail/introduction/widgets/group_panel.dart index 64ff913d..dcdaf9c5 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/group_panel.dart @@ -57,7 +57,7 @@ class _GroupPanelState extends State { Widget build(BuildContext context) { return Container( height: sheetHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ AppBar( diff --git a/lib/pages/video/detail/introduction/widgets/menu_row.dart b/lib/pages/video/detail/introduction/widgets/menu_row.dart index c175aff1..a26c86f0 100644 --- a/lib/pages/video/detail/introduction/widgets/menu_row.dart +++ b/lib/pages/video/detail/introduction/widgets/menu_row.dart @@ -12,7 +12,7 @@ class MenuRow extends StatelessWidget { Widget build(BuildContext context) { return Container( width: double.infinity, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(top: 9, bottom: 9, left: 12), child: SingleChildScrollView( scrollDirection: Axis.horizontal, @@ -84,7 +84,7 @@ class MenuRow extends StatelessWidget { style: TextStyle( fontSize: 13, color: selectStatus - ? Theme.of(context).colorScheme.onBackground + ? Theme.of(context).colorScheme.onSurface : Theme.of(context).colorScheme.outline), ), ), diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 3da15f64..d7355d81 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -170,7 +170,7 @@ class _VideoReplyNewDialogState extends State topLeft: Radius.circular(12), topRight: Radius.circular(12), ), - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, ), child: Column( mainAxisSize: MainAxisSize.min, diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 3fe84c71..6dda9512 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -78,7 +78,7 @@ class _VideoReplyReplyPanelState extends State { Widget build(BuildContext context) { return Container( height: widget.source == 'videoDetail' ? widget.sheetHeight : null, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, child: Column( children: [ if (widget.source == 'videoDetail') diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index 882a9a8b..197d6124 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -23,7 +23,7 @@ class AiDetail extends StatelessWidget { Widget build(BuildContext context) { sheetHeight = localCache.get('sheetHeight'); return Container( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: const EdgeInsets.only(left: 14, right: 14), height: sheetHeight, child: Column( diff --git a/lib/pages/video/detail/widgets/app_bar.dart b/lib/pages/video/detail/widgets/app_bar.dart index efc0b593..b16623ad 100644 --- a/lib/pages/video/detail/widgets/app_bar.dart +++ b/lib/pages/video/detail/widgets/app_bar.dart @@ -29,7 +29,7 @@ class ScrollAppBar extends StatelessWidget { opacity: scrollDistance / (videoHeight - kToolbarHeight), child: Container( height: statusBarHeight + kToolbarHeight, - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, padding: EdgeInsets.only(top: statusBarHeight), child: AppBar( primary: false, diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 544d93e0..3ff74b44 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -93,7 +93,7 @@ class _HeaderControlState extends State { height: 460, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -317,7 +317,7 @@ class _HeaderControlState extends State { height: 500, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -377,7 +377,7 @@ class _HeaderControlState extends State { inactiveThumbColor: Theme.of(context).colorScheme.primaryContainer, inactiveTrackColor: - Theme.of(context).colorScheme.background, + Theme.of(context).colorScheme.surface, splashRadius: 10.0, // boolean variable value value: shutdownTimerService.waitForPlayingCompleted, @@ -570,7 +570,7 @@ class _HeaderControlState extends State { height: 310, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -660,7 +660,7 @@ class _HeaderControlState extends State { height: 250, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -734,7 +734,7 @@ class _HeaderControlState extends State { height: 250, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -828,7 +828,7 @@ class _HeaderControlState extends State { height: 580, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), @@ -1084,7 +1084,7 @@ class _HeaderControlState extends State { height: 250, clipBehavior: Clip.hardEdge, decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: const BorderRadius.all(Radius.circular(12)), ), margin: const EdgeInsets.all(12), diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0cd6915c..0b77b7cc 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -12,7 +12,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { builder: (context) => Container( margin: const EdgeInsets.symmetric(horizontal: StyleString.safeSpace), decoration: BoxDecoration( - color: Theme.of(context).colorScheme.background, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(10.0), ), child: Column( From 7fc4f3fd4145acacdf116a82ff04ce8aa68002f3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 6 Jun 2024 00:28:46 +0800 Subject: [PATCH 214/349] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5app=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E5=85=B3=E6=B3=A8=E7=8A=B6=E6=80=81=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/home/rcmd/result.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 78747d1a..0098fe95 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -69,9 +69,10 @@ class RecVideoItemAppModel { : null; // 由于app端api并不会直接返回与owner的关注状态 // 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效 + RegExp regex = RegExp(r'已关注|新关注'); isFollowed = rcmdReason != null && rcmdReason!.content != null && - rcmdReason!.content!.contains('关注') + regex.hasMatch(rcmdReason!.content!) ? 1 : 0; // 如果是,就无需再显示推荐原因,交由view统一处理即可 From f8897f74bfae9b2d86d8d30625f3816ab9797462 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 7 Jun 2024 23:48:48 +0800 Subject: [PATCH 215/349] =?UTF-8?q?feat:=20web=E7=AB=AF=E9=AA=8C=E8=AF=81?= =?UTF-8?q?=E7=A0=81=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 +- lib/http/login.dart | 99 ++++++++++++++++++++++--------- lib/pages/login/controller.dart | 80 ++++++++++++++++++++++--- lib/pages/login/view.dart | 37 +++++++----- lib/pages/mine/controller.dart | 5 +- lib/pages/webview/controller.dart | 52 +--------------- lib/pages/webview/view.dart | 4 +- lib/utils/login.dart | 58 ++++++++++++++++++ 8 files changed, 231 insertions(+), 108 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 42fc03b8..8a72479b 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -400,10 +400,12 @@ class Api { '${HttpString.passBaseUrl}/x/passport-login/captcha?source=main_web'; // web端短信验证码 - static const String smsCode = + static const String webSmsCode = '${HttpString.passBaseUrl}/x/passport-login/web/sms/send'; // web端验证码登录 + static const String webSmsLogin = + "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; // web端密码登录 diff --git a/lib/http/login.dart b/lib/http/login.dart index ff3fee23..13236623 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:crypto/crypto.dart'; import 'package:dio/dio.dart'; import 'package:encrypt/encrypt.dart'; +import 'package:pilipala/http/constants.dart'; import 'package:uuid/uuid.dart'; import '../models/login/index.dart'; import '../utils/login.dart'; @@ -21,32 +22,32 @@ class LoginHttp { } } - static Future sendSmsCode({ - int? cid, - required int tel, - required String token, - required String challenge, - required String validate, - required String seccode, - }) async { - var res = await Request().post( - Api.appSmsCode, - data: { - 'cid': cid, - 'tel': tel, - "source": "main_web", - 'token': token, - 'challenge': challenge, - 'validate': validate, - 'seccode': seccode, - }, - options: Options( - contentType: Headers.formUrlEncodedContentType, - // headers: {'user-agent': ApiConstants.userAgent} - ), - ); - print(res); - } + // static Future sendSmsCode({ + // int? cid, + // required int tel, + // required String token, + // required String challenge, + // required String validate, + // required String seccode, + // }) async { + // var res = await Request().post( + // Api.appSmsCode, + // data: { + // 'cid': cid, + // 'tel': tel, + // "source": "main_web", + // 'token': token, + // 'challenge': challenge, + // 'validate': validate, + // 'seccode': seccode, + // }, + // options: Options( + // contentType: Headers.formUrlEncodedContentType, + // // headers: {'user-agent': ApiConstants.userAgent} + // ), + // ); + // print(res); + // } // web端验证码 static Future sendWebSmsCode({ @@ -60,6 +61,7 @@ class LoginHttp { Map data = { 'cid': cid, 'tel': tel, + "source": "main_web", 'token': token, 'challenge': challenge, 'validate': validate, @@ -67,17 +69,56 @@ class LoginHttp { }; FormData formData = FormData.fromMap({...data}); var res = await Request().post( - Api.smsCode, + Api.webSmsCode, data: formData, options: Options( contentType: Headers.formUrlEncodedContentType, ), ); - print(res); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } } // web端验证码登录 - static Future loginInByWebSmsCode() async {} + static Future loginInByWebSmsCode({ + int? cid, + required int tel, + required int code, + required String captchaKey, + }) async { + // webSmsLogin + Map data = { + "cid": cid, + "tel": tel, + "code": code, + "source": "main_mini", + "keep": 0, + "captcha_key": captchaKey, + "go_url": HttpString.baseUrl + }; + FormData formData = FormData.fromMap({...data}); + var res = await Request().post( + Api.webSmsLogin, + data: formData, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } // web端密码登录 static Future liginInByWebPwd() async {} diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index c002fdf9..e47653e2 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -1,11 +1,16 @@ +import 'dart:async'; +import 'dart:developer'; import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:get/get_rx/get_rx.dart'; import 'package:pilipala/http/login.dart'; import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart'; import 'package:pilipala/models/login/index.dart'; +import 'package:pilipala/pages/webview/index.dart'; +import 'package:pilipala/utils/login.dart'; class LoginPageController extends GetxController { final GlobalKey mobFormKey = GlobalKey(); @@ -26,9 +31,19 @@ class LoginPageController extends GetxController { final Gt3FlutterPlugin captcha = Gt3FlutterPlugin(); + // 倒计时60s + RxInt seconds = 60.obs; + late Timer timer; + RxBool smsCodeSendStatus = false.obs; + // 默认密码登录 RxInt loginType = 0.obs; + late String captchaLey; + + late int tel; + late int webSmsCode; + // 监听pageView切换 void onPageChange(int index) { currentIndex.value = index; @@ -43,6 +58,7 @@ class LoginPageController extends GetxController { curve: Curves.easeInOut, ); passwordTextFieldNode.requestFocus(); + (mobFormKey.currentState as FormState).save(); } } @@ -86,18 +102,32 @@ class LoginPageController extends GetxController { } } - // 验证码登录 - void loginInByCode() { - if ((msgCodeFormKey.currentState as FormState).validate()) {} + // web端验证码登录 + void loginInByCode() async { + if ((msgCodeFormKey.currentState as FormState).validate()) { + (msgCodeFormKey.currentState as FormState).save(); + var res = await LoginHttp.loginInByWebSmsCode( + cid: 86, + tel: tel, + code: webSmsCode, + captchaKey: captchaLey, + ); + if (res['status']) { + log(res.toString()); + LoginUtils.confirmLogin('', null); + } else { + SmartDialog.showToast(res['msg']); + } + } } - // app端验证码 - void getMsgCode() async { + // 获取app端验证码 + void getAppMsgCode() async { getCaptcha((data) async { CaptchaDataModel captchaData = data; var res = await LoginHttp.sendAppSmsCode( cid: 86, - tel: 13734077064, + tel: tel, token: captchaData.token!, challenge: captchaData.geetest!.challenge!, validate: captchaData.validate!, @@ -121,7 +151,7 @@ class LoginPageController extends GetxController { captcha.addEventHandler(onShow: (Map message) async { SmartDialog.dismiss(); }, onClose: (Map message) async { - SmartDialog.showToast('关闭验证'); + SmartDialog.showToast('取消验证'); }, onResult: (Map message) async { debugPrint("Captcha result: $message"); String code = message["code"]; @@ -201,4 +231,40 @@ class LoginPageController extends GetxController { captcha.startCaptcha(registerData); } else {} } + + // 获取web端验证码 + void getWebMsgCode() async { + getCaptcha((data) async { + CaptchaDataModel captchaData = data; + var res = await LoginHttp.sendWebSmsCode( + cid: 86, + tel: tel, + token: captchaData.token!, + challenge: captchaData.geetest!.challenge!, + validate: captchaData.validate!, + seccode: captchaData.seccode!, + ); + if (res['status']) { + captchaLey = res['data']['captcha_key']; + SmartDialog.showToast('验证码已发送'); + // 倒计时60s + smsCodeSendStatus.value = true; + startTimer(); + } else { + SmartDialog.showToast(res['msg']); + } + }); + } + + void startTimer() { + timer = Timer.periodic(const Duration(seconds: 1), (timer) { + if (seconds.value > 0) { + seconds.value--; + } else { + seconds.value = 60; + smsCodeSendStatus.value = false; + timer.cancel(); + } + }); + } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 6521e9d9..ae0a60b9 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -93,9 +93,7 @@ class _LoginPageState extends State { validator: (v) { return v!.trim().isNotEmpty ? null : "手机号码不能为空"; }, - onSaved: (val) { - print(val); - }, + onSaved: (val) => _loginPageCtr.tel = int.parse(val!), onEditingComplete: () { _loginPageCtr.nextStep(); }, @@ -308,21 +306,28 @@ class _LoginPageState extends State { ? null : "验证码不能为空"; }, - onSaved: (val) { - print(val); - }, + onSaved: (val) => _loginPageCtr.webSmsCode = + int.parse(val!), ), - Positioned( - right: 8, - top: 4, - child: Center( - child: TextButton( - onPressed: () => - _loginPageCtr.getMsgCode(), - child: const Text('获取验证码'), + Obx(() { + return Positioned( + right: 8, + top: 0, + child: Center( + child: TextButton( + onPressed: _loginPageCtr + .smsCodeSendStatus.value + ? null + : () => + _loginPageCtr.getWebMsgCode(), + child: _loginPageCtr + .smsCodeSendStatus.value + ? Text( + '重新获取(${_loginPageCtr.seconds.value}s)') + : const Text('获取验证码')), ), - ), - ), + ); + }) ], ), ), diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index a61bb820..153a7162 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -6,7 +6,6 @@ import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/theme_type.dart'; import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/models/user/stat.dart'; -import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; class MineController extends GetxController { @@ -34,8 +33,8 @@ class MineController extends GetxController { onLogin() async { if (!userLogin.value) { - RoutePush.loginPush(); - // Get.toNamed('/loginPage'); + // RoutePush.loginPush(); + Get.toNamed('/loginPage'); } else { int mid = userInfo.value.mid!; String face = userInfo.value.face!; diff --git a/lib/pages/webview/controller.dart b/lib/pages/webview/controller.dart index bdacc652..e0ff113c 100644 --- a/lib/pages/webview/controller.dart +++ b/lib/pages/webview/controller.dart @@ -76,7 +76,7 @@ class WebviewController extends GetxController { (url.startsWith( 'https://passport.bilibili.com/web/sso/exchange_cookie') || url.startsWith('https://m.bilibili.com/'))) { - confirmLogin(url); + LoginUtils.confirmLogin(url, controller); } }, onWebResourceError: (WebResourceError error) {}, @@ -97,54 +97,4 @@ class WebviewController extends GetxController { ) ..loadRequest(Uri.parse(url)); } - - confirmLogin(url) async { - var content = ''; - if (url != null) { - content = '${content + url}; \n'; - } - try { - await SetCookie.onSet(); - final result = await UserHttp.userInfo(); - if (result['status'] && result['data'].isLogin) { - SmartDialog.showToast('登录成功'); - try { - Box userInfoCache = GStrorage.userInfo; - if (!userInfoCache.isOpen) { - userInfoCache = await Hive.openBox('userInfo'); - } - await userInfoCache.put('userInfoCache', result['data']); - - final HomeController homeCtr = Get.find(); - homeCtr.updateLoginStatus(true); - homeCtr.userFace.value = result['data'].face; - final MediaController mediaCtr = Get.find(); - mediaCtr.mid = result['data'].mid; - await LoginUtils.refreshLoginStatus(true); - } catch (err) { - SmartDialog.show(builder: (BuildContext context) { - return AlertDialog( - title: const Text('登录遇到问题'), - content: Text(err.toString()), - actions: [ - TextButton( - onPressed: () => controller.reload(), - child: const Text('确认'), - ) - ], - ); - }); - } - Get.back(); - } else { - // 获取用户信息失败 - SmartDialog.showToast(result['msg']); - Clipboard.setData(ClipboardData(text: result['msg'])); - } - } catch (e) { - SmartDialog.showNotify(msg: e.toString(), notifyType: NotifyType.warning); - content = content + e.toString(); - Clipboard.setData(ClipboardData(text: content)); - } - } } diff --git a/lib/pages/webview/view.dart b/lib/pages/webview/view.dart index 8edd2189..cba40ad1 100644 --- a/lib/pages/webview/view.dart +++ b/lib/pages/webview/view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/utils/login.dart'; import 'package:url_launcher/url_launcher.dart'; import 'controller.dart'; import 'package:webview_flutter/webview_flutter.dart'; @@ -43,7 +44,8 @@ class _WebviewPageState extends State { Obx( () => _webviewController.type.value == 'login' ? TextButton( - onPressed: () => _webviewController.confirmLogin(null), + onPressed: () => + LoginUtils.confirmLogin(null, _webviewController), child: const Text('刷新登录状态'), ) : const SizedBox(), diff --git a/lib/utils/login.dart b/lib/utils/login.dart index 59c53027..2687a8c2 100644 --- a/lib/utils/login.dart +++ b/lib/utils/login.dart @@ -2,12 +2,18 @@ import 'dart:convert'; import 'dart:math'; import 'package:crypto/crypto.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; +import 'package:pilipala/http/user.dart'; import 'package:pilipala/pages/dynamics/index.dart'; import 'package:pilipala/pages/home/index.dart'; import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/mine/index.dart'; +import 'package:pilipala/utils/cookie.dart'; +import 'package:pilipala/utils/storage.dart'; import 'package:uuid/uuid.dart'; class LoginUtils { @@ -57,4 +63,56 @@ class LoginUtils { String uuid = getUUID() + getUUID(); return 'XY${uuid.substring(0, 35).toUpperCase()}'; } + + static confirmLogin(url, controller) async { + var content = ''; + if (url != null) { + content = '${content + url}; \n'; + } + try { + await SetCookie.onSet(); + final result = await UserHttp.userInfo(); + if (result['status'] && result['data'].isLogin) { + SmartDialog.showToast('登录成功'); + try { + Box userInfoCache = GStrorage.userInfo; + if (!userInfoCache.isOpen) { + userInfoCache = await Hive.openBox('userInfo'); + } + await userInfoCache.put('userInfoCache', result['data']); + + final HomeController homeCtr = Get.find(); + homeCtr.updateLoginStatus(true); + homeCtr.userFace.value = result['data'].face; + final MediaController mediaCtr = Get.find(); + mediaCtr.mid = result['data'].mid; + await LoginUtils.refreshLoginStatus(true); + } catch (err) { + SmartDialog.show(builder: (BuildContext context) { + return AlertDialog( + title: const Text('登录遇到问题'), + content: Text(err.toString()), + actions: [ + TextButton( + onPressed: controller != null + ? () => controller.reload() + : SmartDialog.dismiss, + child: const Text('确认'), + ) + ], + ); + }); + } + Get.back(); + } else { + // 获取用户信息失败 + SmartDialog.showToast(result['msg']); + Clipboard.setData(ClipboardData(text: result['msg'])); + } + } catch (e) { + SmartDialog.showNotify(msg: e.toString(), notifyType: NotifyType.warning); + content = content + e.toString(); + Clipboard.setData(ClipboardData(text: content)); + } + } } From 9888aba62e93b87c75832c543697302930c588e5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 00:26:08 +0800 Subject: [PATCH 216/349] =?UTF-8?q?feat:=20web=E7=AB=AF=E5=AF=86=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 ++ lib/http/login.dart | 38 ++++++++++++++++++++++++++ lib/pages/login/controller.dart | 47 +++++++++++++++++++++++++++------ lib/pages/login/view.dart | 2 +- 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 8a72479b..e0b92c02 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -408,6 +408,8 @@ class Api { "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; // web端密码登录 + static const String loginInByWebPwd = + "${HttpString.passBaseUrl}/x/passport-login/web/login"; // app端短信验证码 static const String appSmsCode = diff --git a/lib/http/login.dart b/lib/http/login.dart index 13236623..a97360c7 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -214,4 +214,42 @@ class LoginHttp { ); print(res); } + + // web端密码登录 + static Future loginInByWebPwd({ + required int username, + required String password, + required String token, + required String challenge, + required String validate, + required String seccode, + }) async { + Map data = { + 'username': username, + 'password': password, + 'keep': 0, + 'token': token, + 'challenge': challenge, + 'validate': validate, + 'seccode': seccode, + 'source': 'main-fe-header', + "go_url": HttpString.baseUrl + }; + FormData formData = FormData.fromMap({...data}); + var res = await Request().post( + Api.loginInByWebPwd, + data: formData, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index e47653e2..354076f9 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -1,15 +1,13 @@ import 'dart:async'; -import 'dart:developer'; import 'dart:io'; +import 'package:encrypt/encrypt.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; -import 'package:get/get_rx/get_rx.dart'; import 'package:pilipala/http/login.dart'; import 'package:gt3_flutter_plugin/gt3_flutter_plugin.dart'; import 'package:pilipala/models/login/index.dart'; -import 'package:pilipala/pages/webview/index.dart'; import 'package:pilipala/utils/login.dart'; class LoginPageController extends GetxController { @@ -39,7 +37,7 @@ class LoginPageController extends GetxController { // 默认密码登录 RxInt loginType = 0.obs; - late String captchaLey; + late String captchaKey; late int tel; late int webSmsCode; @@ -102,6 +100,39 @@ class LoginPageController extends GetxController { } } + // web端密码登录 + void loginInByWebPassword() async { + if ((passwordFormKey.currentState as FormState).validate()) { + getCaptcha((data) async { + CaptchaDataModel captchaData = data; + var webKeyRes = await LoginHttp.getWebKey(); + if (webKeyRes['status']) { + String rhash = webKeyRes['data']['hash']; + String key = webKeyRes['data']['key']; + dynamic publicKey = RSAKeyParser().parse(key); + String passwordEncryptyed = Encrypter(RSA(publicKey: publicKey)) + .encrypt(rhash + passwordTextController.text) + .base64; + var res = await LoginHttp.loginInByWebPwd( + username: tel, + password: passwordEncryptyed, + token: captchaData.token!, + challenge: captchaData.geetest!.challenge!, + validate: captchaData.validate!, + seccode: captchaData.seccode!, + ); + if (res['status']) { + await LoginUtils.confirmLogin('', null); + } else { + SmartDialog.showToast(res['msg']); + } + } else { + SmartDialog.showToast(webKeyRes['msg']); + } + }); + } + } + // web端验证码登录 void loginInByCode() async { if ((msgCodeFormKey.currentState as FormState).validate()) { @@ -110,11 +141,10 @@ class LoginPageController extends GetxController { cid: 86, tel: tel, code: webSmsCode, - captchaKey: captchaLey, + captchaKey: captchaKey, ); if (res['status']) { - log(res.toString()); - LoginUtils.confirmLogin('', null); + await LoginUtils.confirmLogin('', null); } else { SmartDialog.showToast(res['msg']); } @@ -245,7 +275,7 @@ class LoginPageController extends GetxController { seccode: captchaData.seccode!, ); if (res['status']) { - captchaLey = res['data']['captcha_key']; + captchaKey = res['data']['captcha_key']; SmartDialog.showToast('验证码已发送'); // 倒计时60s smsCodeSendStatus.value = true; @@ -256,6 +286,7 @@ class LoginPageController extends GetxController { }); } + // 验证码倒计时 void startTimer() { timer = Timer.periodic(const Duration(seconds: 1), (timer) { if (seconds.value > 0) { diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index ae0a60b9..2f406706 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -234,7 +234,7 @@ class _LoginPageState extends State { .primary, // 设置按钮背景色 ), onPressed: () => - _loginPageCtr.loginInByAppPassword(), + _loginPageCtr.loginInByWebPassword(), child: const Text('确认登录'), ) ], From 4fe8366c2fe6013f8f9f941ebc6b33437709975e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 00:42:45 +0800 Subject: [PATCH 217/349] upgrade: gt3 plugin --- pubspec.lock | 20 ++++++++++---------- pubspec.yaml | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index fdcd517c..406fbca4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -674,10 +674,10 @@ packages: dependency: "direct main" description: name: gt3_flutter_plugin - sha256: f12bff2bfbcf27467833f8d564dcc24ee2f1b3254a7c7cf5eb2c4590baf11cc1 + sha256: "08f35692e937770ad6b3e2017eb8ef81839a82b8a63f5acf3abab14b688fc36c" url: "https://pub.flutter-io.cn" source: hosted - version: "0.0.8" + version: "0.1.0" hive: dependency: "direct main" description: @@ -981,10 +981,10 @@ packages: dependency: transitive description: name: mime - sha256: e4ff8e8564c03f255408decd16e7899da1733852a9110a58fe6d1b817684a63e + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" url: "https://pub.flutter-io.cn" source: hosted - version: "1.0.4" + version: "1.0.5" nm: dependency: transitive description: @@ -1054,10 +1054,10 @@ packages: dependency: "direct main" description: name: path_provider - sha256: a1aa8aaa2542a6bc57e381f132af822420216c80d4781f7aa085ca3229208aaa + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.flutter-io.cn" source: hosted - version: "2.1.1" + version: "2.1.3" path_provider_android: dependency: transitive description: @@ -1070,10 +1070,10 @@ packages: dependency: transitive description: name: path_provider_foundation - sha256: "19314d595120f82aca0ba62787d58dde2cc6b5df7d2f0daf72489e38d1b57f2d" + sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.flutter-io.cn" source: hosted - version: "2.3.1" + version: "2.4.0" path_provider_linux: dependency: transitive description: @@ -1238,10 +1238,10 @@ packages: dependency: "direct main" description: name: saver_gallery - sha256: "2657953427ebe5a3b2d08157d41587c01923ccce3f1a616d55082be7470f8530" + sha256: "0f740608072053a0da3b19cc5812a87e36f5c3c0b959d2475c4eb3d697f4a782" url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.1" + version: "3.0.3" screen_brightness: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index f758d308..f02c2ead 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -132,7 +132,7 @@ dependencies: # html渲染 flutter_html: ^3.0.0-beta.2 # 极验 - gt3_flutter_plugin: ^0.0.8 + gt3_flutter_plugin: ^0.1.0 uuid: ^3.0.7 scrollable_positioned_list: ^0.3.8 catcher_2: ^1.2.6 From 3a94281310ecf86ccdd0a5d21853b9ed38dceff6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 03:14:37 +0800 Subject: [PATCH 218/349] =?UTF-8?q?feat:=20web=E7=AB=AF=E6=89=AB=E7=A0=81?= =?UTF-8?q?=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 12 +++- lib/http/login.dart | 27 +++++++ lib/pages/login/controller.dart | 35 +++++++++ lib/pages/login/view.dart | 121 ++++++++++++++++++++++++++------ pubspec.lock | 16 +++++ pubspec.yaml | 2 + 6 files changed, 190 insertions(+), 23 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index e0b92c02..b0fc0556 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -405,11 +405,19 @@ class Api { // web端验证码登录 static const String webSmsLogin = - "${HttpString.passBaseUrl}/x/passport-login/web/login/sms"; + '${HttpString.passBaseUrl}/x/passport-login/web/login/sms'; // web端密码登录 static const String loginInByWebPwd = - "${HttpString.passBaseUrl}/x/passport-login/web/login"; + '${HttpString.passBaseUrl}/x/passport-login/web/login'; + + // web端二维码 + static const String qrCodeApi = + '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/generate'; + + // 扫码登录 + static const String loginInByQrcode = + '${HttpString.passBaseUrl}/x/passport-login/web/qrcode/poll'; // app端短信验证码 static const String appSmsCode = diff --git a/lib/http/login.dart b/lib/http/login.dart index a97360c7..2437b72a 100644 --- a/lib/http/login.dart +++ b/lib/http/login.dart @@ -252,4 +252,31 @@ class LoginHttp { return {'status': false, 'data': [], 'msg': res.data['message']}; } } + + // web端登录二维码 + static Future getWebQrcode() async { + var res = await Request().get(Api.qrCodeApi); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } + + // web端二维码轮询登录状态 + static Future queryWebQrcodeStatus(String qrcodeKey) async { + var res = await Request() + .get(Api.loginInByQrcode, data: {'qrcode_key': qrcodeKey}); + if (res.data['data']['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index 354076f9..b5ddba16 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -42,6 +42,10 @@ class LoginPageController extends GetxController { late int tel; late int webSmsCode; + RxInt validSeconds = 180.obs; + late Timer validTimer; + late String qrcodeKey; + // 监听pageView切换 void onPageChange(int index) { currentIndex.value = index; @@ -298,4 +302,35 @@ class LoginPageController extends GetxController { } }); } + + // 获取登录二维码 + Future getWebQrcode() async { + var res = await LoginHttp.getWebQrcode(); + validSeconds.value = 180; + if (res['status']) { + qrcodeKey = res['data']['qrcode_key']; + validTimer = Timer.periodic(const Duration(seconds: 1), (validTimer) { + if (validSeconds.value > 0) { + validSeconds.value--; + queryWebQrcodeStatus(); + } else { + getWebQrcode(); + validTimer.cancel(); + } + }); + return res; + } else { + SmartDialog.showToast(res['msg']); + } + } + + // 轮询二维码登录状态 + Future queryWebQrcodeStatus() async { + var res = await LoginHttp.queryWebQrcodeStatus(qrcodeKey); + if (res['status']) { + await LoginUtils.confirmLogin('', null); + validTimer.cancel(); + Get.back(); + } + } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 2f406706..cd91ea26 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:qr_flutter/qr_flutter.dart'; import 'controller.dart'; @@ -37,6 +38,105 @@ class _LoginPageState extends State { icon: const Icon(Icons.arrow_back), ), ), + actions: [ + IconButton( + tooltip: '浏览器打开', + onPressed: () { + Get.offNamed( + '/webview', + parameters: { + 'url': 'https://passport.bilibili.com/h5-app/passport/login', + 'type': 'login', + 'pageTitle': '登录bilibili', + }, + ); + }, + icon: const Icon(Icons.language), + ), + IconButton( + tooltip: '二维码登录', + onPressed: () { + showDialog( + context: context, + builder: (context) { + return StatefulBuilder( + builder: (context, StateSetter setState) { + return AlertDialog( + title: Row( + children: [ + const Text('扫码登录'), + IconButton( + onPressed: () { + setState(() {}); + }, + icon: const Icon(Icons.refresh), + ), + ], + ), + contentPadding: const EdgeInsets.fromLTRB(0, 0, 0, 4), + content: AspectRatio( + aspectRatio: 1, + child: Container( + width: 200, + padding: const EdgeInsets.all(12), + child: FutureBuilder( + future: _loginPageCtr.getWebQrcode(), + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + Map data = snapshot.data as Map; + return QrImageView( + data: data['data']['url'], + backgroundColor: Colors.transparent, + ); + } else { + return const Center( + child: SizedBox( + width: 40, + height: 40, + child: CircularProgressIndicator(), + ), + ); + } + }, + ), + ), + ), + actions: [ + TextButton( + onPressed: () {}, + child: Obx(() { + return Text( + '有效期: ${_loginPageCtr.validSeconds.value}s', + style: Theme.of(context).textTheme.titleMedium, + ); + }), + ), + TextButton( + onPressed: () {}, + child: Text( + '检查登录状态', + style: TextStyle( + fontSize: Theme.of(context) + .textTheme + .titleMedium! + .fontSize, + ), + ), + ) + ], + ); + }); + }, + ); + }, + icon: const Icon(Icons.qr_code), + ), + const SizedBox(width: 22), + ], ), body: PageView( physics: const NeverScrollableScrollPhysics(), @@ -99,27 +199,6 @@ class _LoginPageState extends State { }, ), ), - GestureDetector( - onTap: () { - Get.offNamed( - '/webview', - parameters: { - 'url': - 'https://passport.bilibili.com/h5-app/passport/login', - 'type': 'login', - 'pageTitle': '登录bilibili', - }, - ); - }, - child: Padding( - padding: const EdgeInsets.only(left: 2), - child: Text( - '使用网页端登录', - style: TextStyle( - color: Theme.of(context).colorScheme.primary), - ), - ), - ), const Spacer(), Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, diff --git a/pubspec.lock b/pubspec.lock index 406fbca4..5c6f2b43 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1218,6 +1218,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "3.1.0" + qr: + dependency: transitive + description: + name: qr + sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.1" + qr_flutter: + dependency: "direct main" + description: + name: qr_flutter + sha256: "5095f0fc6e3f71d08adef8feccc8cea4f12eec18a2e31c2e8d82cb6019f4b097" + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.1.0" rxdart: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index f02c2ead..66187abf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -145,6 +145,8 @@ dependencies: # 投屏 dlna_dart: ^0.0.8 lottie: ^3.1.2 + # 二维码 + qr_flutter: ^4.1.0 dev_dependencies: flutter_test: From aee52b02470f91db723b24a9ce2b9a6dbc573c10 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 04:09:00 +0800 Subject: [PATCH 219/349] =?UTF-8?q?opt:=20=E6=B6=88=E6=81=AF=E9=A1=B5?= =?UTF-8?q?=E9=9D=A2=E5=A2=9E=E5=8A=A0=E9=AA=A8=E6=9E=B6=E5=B1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 270 +++++++++++++++++++----------------- 1 file changed, 142 insertions(+), 128 deletions(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index fa7ad60b..fa95463b 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/skeleton.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; @@ -102,134 +103,83 @@ class _WhisperPageState extends State { }, child: SingleChildScrollView( controller: _scrollController, - child: Column( - children: [ - FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - RxList sessionList = _whisperController.sessionList; - return Obx( - () => sessionList.isEmpty - ? const SizedBox() - : ListView.separated( - itemCount: sessionList.length, - shrinkWrap: true, - physics: - const NeverScrollableScrollPhysics(), - itemBuilder: (_, int i) { - return ListTile( - onTap: () { - sessionList[i].unreadCount = 0; - sessionList.refresh(); - Get.toNamed( - '/whisperDetail', - parameters: { - 'talkerId': sessionList[i] - .talkerId - .toString(), - 'name': sessionList[i] - .accountInfo - .name, - 'face': sessionList[i] - .accountInfo - .face, - 'mid': sessionList[i] - .accountInfo - .mid - .toString(), - }, - ); - }, - leading: Badge( - isLabelVisible: - sessionList[i].unreadCount > 0, - label: Text(sessionList[i] - .unreadCount - .toString()), - alignment: Alignment.topRight, - child: NetworkImgLayer( - width: 45, - height: 45, - type: 'avatar', - src: sessionList[i] - .accountInfo - .face, - ), - ), - title: Text( - sessionList[i].accountInfo.name), - subtitle: Text( - sessionList[i].lastMsg.content != - null && - sessionList[i] - .lastMsg - .content != - '' - ? (sessionList[i] - .lastMsg - .content['text'] ?? - sessionList[i] - .lastMsg - .content['content'] ?? - sessionList[i] - .lastMsg - .content['title'] ?? - sessionList[i] - .lastMsg - .content[ - 'reply_content'] ?? - '不支持的消息类型') - : '不支持的消息类型', - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: Theme.of(context) - .textTheme - .labelMedium! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline)), - trailing: Text( - Utils.dateFormat(sessionList[i] - .lastMsg - .timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith( - color: Theme.of(context) - .colorScheme - .outline), - ), - ); - }, - separatorBuilder: - (BuildContext context, int index) { - return Divider( - indent: 72, - endIndent: 20, - height: 6, - color: Colors.grey.withOpacity(0.1), - ); - }, - ), - ); - } else { - // 请求错误 - return Center( - child: Text(data?['msg'] ?? '请求异常'), - ); - } - } else { - // 骨架屏 - return const SizedBox(); - } - }, - ) - ], + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + RxList sessionList = _whisperController.sessionList; + return Obx( + () => sessionList.isEmpty + ? const SizedBox() + : ListView.separated( + itemCount: sessionList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return SessionItem( + sessionItem: sessionList[i], + changeFucCall: () => + sessionList.refresh(), + ); + }, + separatorBuilder: + (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return Center( + child: Text(data?['msg'] ?? '请求异常'), + ); + } + } else { + // 骨架屏 + return ListView.builder( + itemCount: 15, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, int i) { + return Skeleton( + child: ListTile( + leading: Container( + width: 45, + height: 45, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .onInverseSurface, + borderRadius: BorderRadius.circular(25), + ), + ), + title: Container( + width: 100, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + subtitle: Container( + width: 80, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + ), + ); + }, + ); + } + }, ), ), ), @@ -239,3 +189,67 @@ class _WhisperPageState extends State { ); } } + +class SessionItem extends StatelessWidget { + final dynamic sessionItem; + final Function changeFucCall; + + const SessionItem({ + super.key, + required this.sessionItem, + required this.changeFucCall, + }); + + @override + Widget build(BuildContext context) { + final content = sessionItem.lastMsg.content; + return ListTile( + onTap: () { + sessionItem.unreadCount = 0; + changeFucCall.call(); + Get.toNamed( + '/whisperDetail', + parameters: { + 'talkerId': sessionItem.talkerId.toString(), + 'name': sessionItem.accountInfo.name, + 'face': sessionItem.accountInfo.face, + 'mid': sessionItem.accountInfo.mid.toString(), + }, + ); + }, + leading: Badge( + isLabelVisible: sessionItem.unreadCount > 0, + label: Text(sessionItem.unreadCount.toString()), + alignment: Alignment.topRight, + child: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: sessionItem.accountInfo.face, + ), + ), + title: Text(sessionItem.accountInfo.name), + subtitle: Text( + content != null && content != '' + ? (content['text'] ?? + content['content'] ?? + content['title'] ?? + content['reply_content'] ?? + '不支持的消息类型') + : '不支持的消息类型', + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: Theme.of(context) + .textTheme + .labelMedium! + .copyWith(color: Theme.of(context).colorScheme.outline)), + trailing: Text( + Utils.dateFormat(sessionItem.lastMsg.timestamp), + style: Theme.of(context) + .textTheme + .labelSmall! + .copyWith(color: Theme.of(context).colorScheme.outline), + ), + ); + } +} From f4b7f77e440cbd8d51558f74312a4e4439fc1f8c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 13:29:36 +0800 Subject: [PATCH 220/349] upgrade: new version syntax --- lib/common/widgets/http_error.dart | 2 +- lib/common/widgets/video_card_h.dart | 2 +- lib/pages/bangumi/introduction/view.dart | 8 ++++---- lib/pages/bangumi/widgets/bangumi_panel.dart | 2 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- lib/pages/fav_detail/widget/fav_video_card.dart | 2 +- lib/pages/history/widgets/item.dart | 5 ++--- lib/pages/home/view.dart | 8 ++++---- lib/pages/live_room/widgets/bottom_control.dart | 2 +- lib/pages/login/view.dart | 6 ++---- lib/pages/media/view.dart | 4 ++-- lib/pages/member/view.dart | 5 ++--- lib/pages/search_panel/widgets/video_panel.dart | 2 +- lib/pages/setting/extra_setting.dart | 6 +++--- lib/pages/setting/style_setting.dart | 6 +++--- lib/pages/setting/widgets/switch_item.dart | 6 +++--- .../video/detail/introduction/widgets/page_panel.dart | 2 +- lib/pages/video/detail/reply/widgets/reply_item.dart | 2 +- lib/pages/video/detail/reply_new/toolbar_icon_button.dart | 4 ++-- lib/pages/video/detail/reply_new/view.dart | 2 +- lib/pages/video/detail/view.dart | 2 +- lib/pages/video/detail/widgets/ai_detail.dart | 2 +- lib/pages/video/detail/widgets/header_control.dart | 8 ++++---- lib/pages/whisper_detail/view.dart | 6 +++--- lib/plugin/pl_player/view.dart | 6 +++--- lib/plugin/pl_player/widgets/common_btn.dart | 2 +- lib/plugin/pl_player/widgets/play_pause_btn.dart | 2 +- lib/utils/image_save.dart | 2 +- 28 files changed, 52 insertions(+), 56 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index cbc6659b..b130faae 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -34,7 +34,7 @@ class HttpError extends StatelessWidget { fn!(); }, style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith((states) { + backgroundColor: WidgetStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.primary.withAlpha(20); }), ), diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 1265477f..c674b223 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -305,7 +305,7 @@ class VideoContent extends StatelessWidget { if (source == 'later') ...[ IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => onPressedFn?.call(), icon: Icon( diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 95d4d898..bb85be1a 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -232,11 +232,11 @@ class _BangumiInfoState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all( - EdgeInsets.zero), + padding: + WidgetStateProperty.all(EdgeInsets.zero), backgroundColor: - MaterialStateProperty.resolveWith( - (Set states) { + WidgetStateProperty.resolveWith( + (Set states) { return t.colorScheme.primaryContainer .withOpacity(0.7); }), diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 3df7ce25..345a47b6 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -139,7 +139,7 @@ class _BangumiPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.bangumiIntroController?.bottomSheetController = diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index 8acdc26a..e66e2a91 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -82,7 +82,7 @@ class AuthorPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { showModalBottomSheet( diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 79e5c073..72d7b4a0 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -217,7 +217,7 @@ class VideoContent extends StatelessWidget { bottom: -4, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { showDialog( diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index baebfedb..8e71df6f 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -217,11 +217,10 @@ class HistoryItem extends StatelessWidget { curve: Curves.easeInOut, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( EdgeInsets.zero), backgroundColor: - MaterialStateProperty - .resolveWith( + WidgetStateProperty.resolveWith( (states) { return Colors.white .withOpacity(0.8); diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index a25389bd..0c45a262 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -278,8 +278,8 @@ class DefaultUser extends StatelessWidget { height: 38, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith((states) { + padding: WidgetStateProperty.all(EdgeInsets.zero), + backgroundColor: WidgetStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.onInverseSurface; }), ), @@ -371,8 +371,8 @@ class CustomChip extends StatelessWidget { ), backgroundColor: secondaryContainer, selectedColor: secondaryContainer, - color: MaterialStateProperty.resolveWith( - (Set states) => secondaryContainer.withAlpha(200)), + color: WidgetStateProperty.resolveWith( + (Set states) => secondaryContainer.withAlpha(200)), padding: const EdgeInsets.fromLTRB(7, 1, 7, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 4dd7c538..6abb1260 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -124,7 +124,7 @@ class _BottomControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index cd91ea26..ecc53af2 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -253,8 +253,7 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - MaterialStateProperty.resolveWith( - (states) { + WidgetStateProperty.resolveWith((states) { return Theme.of(context) .colorScheme .primary @@ -344,8 +343,7 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - MaterialStateProperty.resolveWith( - (states) { + WidgetStateProperty.resolveWith((states) { return Theme.of(context) .colorScheme .primary diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index cc413e59..f6a033e5 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -178,10 +178,10 @@ class _MediaPageState extends State child: Center( child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all( + padding: WidgetStateProperty.all( EdgeInsets.zero), backgroundColor: - MaterialStateProperty.resolveWith( + WidgetStateProperty.resolveWith( (states) { return Theme.of(context) .colorScheme diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index b6648647..bb0d92be 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -90,9 +90,8 @@ class _MemberPageState extends State () => Text( _memberController.memberInfo.value.name ?? '', style: TextStyle( - color: Theme.of(context) - .colorScheme - .onBackground, + color: + Theme.of(context).colorScheme.onSurface, fontSize: 14), ), ), diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index c24a007c..3207a8ea 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -92,7 +92,7 @@ class SearchVideoPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => controller.onShowFilterDialog(ctr), icon: Icon( diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index aaaa8b84..885a831c 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -240,10 +240,10 @@ class _ExtraSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: MaterialStateProperty.resolveWith( - (Set states) { + thumbIcon: WidgetStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == MaterialState.selected) { + states.first == WidgetState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 364eabf0..e07bef66 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -69,10 +69,10 @@ class _StyleSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: MaterialStateProperty.resolveWith( - (Set states) { + thumbIcon: WidgetStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == MaterialState.selected) { + states.first == WidgetState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart index d0c2bbf2..36c4433e 100644 --- a/lib/pages/setting/widgets/switch_item.dart +++ b/lib/pages/setting/widgets/switch_item.dart @@ -70,9 +70,9 @@ class _SetSwitchItemState extends State { alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大 scale: 0.8, child: Switch( - thumbIcon: MaterialStateProperty.resolveWith( - (Set states) { - if (states.isNotEmpty && states.first == MaterialState.selected) { + thumbIcon: + WidgetStateProperty.resolveWith((Set states) { + if (states.isNotEmpty && states.first == WidgetState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index 266f5566..c8111847 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -106,7 +106,7 @@ class _PagesPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.videoIntroCtr.bottomSheetController = diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 55c16f7d..08e4d405 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -97,7 +97,7 @@ class ReplyItem extends StatelessWidget { child: Container( decoration: BoxDecoration( borderRadius: BorderRadius.circular(7), - color: colorScheme.background, + color: colorScheme.surface, ), child: Icon( Icons.offline_bolt, diff --git a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart index c4390796..bf810ce2 100644 --- a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart +++ b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart @@ -27,8 +27,8 @@ class ToolbarIconButton extends StatelessWidget { ? Theme.of(context).colorScheme.onSecondaryContainer : Theme.of(context).colorScheme.outline, style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith((states) { + padding: WidgetStateProperty.all(EdgeInsets.zero), + backgroundColor: WidgetStateProperty.resolveWith((states) { return selected ? Theme.of(context).colorScheme.secondaryContainer : null; diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index d7355d81..a136f623 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -259,7 +259,7 @@ class _VideoReplyNewDialogState extends State size: 22), label: const Text('转发到动态'), style: ButtonStyle( - foregroundColor: MaterialStateProperty.all( + foregroundColor: WidgetStateProperty.all( isForward.value ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index fa531684..a0f5d6bc 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -412,7 +412,7 @@ class _VideoDetailPageState extends State height: 32, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => vdCtr.showShootDanmakuSheet(), child: diff --git a/lib/pages/video/detail/widgets/ai_detail.dart b/lib/pages/video/detail/widgets/ai_detail.dart index 197d6124..37d51106 100644 --- a/lib/pages/video/detail/widgets/ai_detail.dart +++ b/lib/pages/video/detail/widgets/ai_detail.dart @@ -108,7 +108,7 @@ class AiDetail extends StatelessWidget { fontSize: 13, color: Theme.of(context) .colorScheme - .onBackground, + .onSurface, height: 1.5, ), children: [ diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 3ff74b44..d839a1eb 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1248,7 +1248,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => showShootDanmakuSheet(), child: const Text( @@ -1263,7 +1263,7 @@ class _HeaderControlState extends State { child: Obx( () => IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { _.isOpenDanmu.value = !_.isOpenDanmu.value; @@ -1286,7 +1286,7 @@ class _HeaderControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; @@ -1330,7 +1330,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => showSetSpeedSheet(), child: Text( diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 1701be33..042afca1 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -95,9 +95,9 @@ class _WhisperDetailPageState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) { + padding: WidgetStateProperty.all(EdgeInsets.zero), + backgroundColor: WidgetStateProperty.resolveWith( + (Set states) { return Theme.of(context) .colorScheme .primaryContainer diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 34140be8..0e405884 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -279,7 +279,7 @@ class _PLVideoPlayerState extends State widget.showEposideCb?.call(); }, style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), child: const Text( '选集', @@ -294,7 +294,7 @@ class _PLVideoPlayerState extends State child: TextButton( onPressed: () => _.toggleVideoFit(), style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), child: Obx( () => Text( @@ -311,7 +311,7 @@ class _PLVideoPlayerState extends State height: 34, child: TextButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () {}, child: Obx( diff --git a/lib/plugin/pl_player/widgets/common_btn.dart b/lib/plugin/pl_player/widgets/common_btn.dart index 5f33311c..bf9467a8 100644 --- a/lib/plugin/pl_player/widgets/common_btn.dart +++ b/lib/plugin/pl_player/widgets/common_btn.dart @@ -17,7 +17,7 @@ class ComBtn extends StatelessWidget { height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () { fuc!(); diff --git a/lib/plugin/pl_player/widgets/play_pause_btn.dart b/lib/plugin/pl_player/widgets/play_pause_btn.dart index 6cbe31f0..7547a1cb 100644 --- a/lib/plugin/pl_player/widgets/play_pause_btn.dart +++ b/lib/plugin/pl_player/widgets/play_pause_btn.dart @@ -68,7 +68,7 @@ class PlayOrPauseButtonState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: player.playOrPause, color: Colors.white, diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0b77b7cc..0727ad68 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -39,7 +39,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { const BorderRadius.all(Radius.circular(20))), child: IconButton( style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), + padding: WidgetStateProperty.all(EdgeInsets.zero), ), onPressed: () => closeFn!(), icon: const Icon( From 897551bf237c65ec364507aa1c3d1cff865c6851 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 15:35:45 +0800 Subject: [PATCH 221/349] =?UTF-8?q?fix:=20=E7=A7=81=E4=BF=A1=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E7=B1=BB=E5=9E=8B=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whisper_detail/widget/chat_item.dart | 203 +++++++++--------- 1 file changed, 101 insertions(+), 102 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index ad11e4c3..0d37e8b3 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -259,115 +259,114 @@ class ChatItem extends StatelessWidget { ); case MsgType.auto_reply_push: return Container( - constraints: const BoxConstraints( - maxWidth: 300.0, // 设置最大宽度为200.0 + constraints: const BoxConstraints( + maxWidth: 300.0, // 设置最大宽度为200.0 + ), + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .secondaryContainer + .withOpacity(0.4), + borderRadius: const BorderRadius.all( + Radius.circular(16), ), - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .secondaryContainer - .withOpacity(0.4), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), - bottomLeft: Radius.circular(6), - bottomRight: Radius.circular(16), - ), - ), - margin: const EdgeInsets.all(12), - padding: const EdgeInsets.all(12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - content['main_title'], - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), - fontWeight: FontWeight.bold, - ), + ), + margin: const EdgeInsets.all(12), + padding: const EdgeInsets.all(12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + content['main_title'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, ), - for (var i in content['sub_cards']) ...[ - const SizedBox(height: 6), - GestureDetector( - onTap: () async { - RegExp bvRegex = RegExp(r'BV[0-9A-Za-z]{10}', - caseSensitive: false); - Iterable matches = - bvRegex.allMatches(i['jump_url']); - if (matches.isNotEmpty) { - Match match = matches.first; - String bvid = match.group(0)!; - try { - SmartDialog.showLoading(); - final int cid = await SearchHttp.ab2c(bvid: bvid); - final String heroTag = Utils.makeHeroTag(bvid); - SmartDialog.dismiss().then( - (e) => Get.toNamed( - '/video?bvid=$bvid&cid=$cid', - arguments: { - 'pic': i['cover_url'], - 'heroTag': heroTag, - }), - ); - } catch (err) { - SmartDialog.dismiss(); - SmartDialog.showToast(err.toString()); - } - } else { - SmartDialog.showToast('未匹配到 BV 号'); - Get.toNamed('/webview', - arguments: {'url': i['jump_url']}); - } - }, - child: Row( + ), + for (var i in content['sub_cards']) ...[ + const SizedBox(height: 6), + GestureDetector( + onTap: () async { + RegExp bvRegex = + RegExp(r'BV[0-9A-Za-z]{10}', caseSensitive: false); + Iterable matches = + bvRegex.allMatches(i['jump_url']); + if (matches.isNotEmpty) { + Match match = matches.first; + String bvid = match.group(0)!; + try { + SmartDialog.showLoading(); + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + SmartDialog.dismiss().then( + (e) => Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': i['cover_url'], + 'heroTag': heroTag, + }), + ); + } catch (err) { + SmartDialog.dismiss(); + SmartDialog.showToast(err.toString()); + } + } else { + SmartDialog.showToast('未匹配到 BV 号'); + Get.toNamed('/webview', + arguments: {'url': i['jump_url']}); + } + }, + child: Row( + children: [ + NetworkImgLayer( + width: 130, + height: 130 * 9 / 16, + src: i['cover_url'], + ), + const SizedBox(width: 6), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - NetworkImgLayer( - width: 130, - height: 130 * 9 / 16, - src: i['cover_url'], + Text( + i['field1'], + maxLines: 2, + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context), + fontWeight: FontWeight.bold, + ), + ), + Text( + i['field2'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context).withOpacity(0.6), + fontSize: 12, + ), + ), + Text( + i['field3'], + style: TextStyle( + letterSpacing: 0.6, + height: 1.5, + color: textColor(context).withOpacity(0.6), + fontSize: 12, + ), ), - const SizedBox(width: 6), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - i['field1'], - maxLines: 2, - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context), - fontWeight: FontWeight.bold, - ), - ), - Text( - i['field2'], - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context).withOpacity(0.6), - fontSize: 12, - ), - ), - Text( - Utils.timeFormat(int.parse(i['field3'])), - style: TextStyle( - letterSpacing: 0.6, - height: 1.5, - color: textColor(context).withOpacity(0.6), - fontSize: 12, - ), - ), - ], - )), ], )), - ], + ], + ), + ), ], - )); + ], + ), + ); default: return Text( content != null && content != '' From a03d159a86b338c6ca6e7cc145cb83b86e603870 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 17:36:55 +0800 Subject: [PATCH 222/349] =?UTF-8?q?mod:=20=E5=86=85=E5=AE=B9=E5=88=86?= =?UTF-8?q?=E5=8C=BA=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/search.dart | 4 ++-- lib/pages/search/widgets/search_text.dart | 10 ++++++---- lib/pages/search_panel/controller.dart | 6 +++--- lib/pages/search_panel/widgets/video_panel.dart | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/lib/http/search.dart b/lib/http/search.dart index 70980547..075defc7 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -75,7 +75,7 @@ class SearchHttp { required page, String? order, int? duration, - int? tid, + int? tids, }) async { var reqData = { 'search_type': searchType.type, @@ -85,7 +85,7 @@ class SearchHttp { 'page': page, if (order != null) 'order': order, if (duration != null) 'duration': duration, - if (tid != null) 'tid': tid, + if (tids != null && tids != -1) 'tids': tids, }; var res = await Request().get(Api.searchByType, data: reqData); if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index fbd1cfc6..d3ffafea 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -18,10 +18,12 @@ class SearchText extends StatelessWidget { @override Widget build(BuildContext context) { return Material( - color: Theme.of(context) - .colorScheme - .surfaceContainerHighest - .withOpacity(0.5), + color: isSelect + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context) + .colorScheme + .surfaceContainerHighest + .withOpacity(0.5), borderRadius: BorderRadius.circular(6), child: Padding( padding: EdgeInsets.zero, diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 56d83601..35113198 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -16,8 +16,8 @@ class SearchPanelController extends GetxController { RxString order = ''.obs; // 视频时长筛选 仅用于搜索视频 RxInt duration = 0.obs; - // 视频分区筛选 仅用于搜索视频 - RxInt tid = (-1).obs; + // 视频分区筛选 仅用于搜索视频 -1时不传 + RxInt tids = (-1).obs; Future onSearch({type = 'init'}) async { var result = await SearchHttp.searchByType( @@ -26,7 +26,7 @@ class SearchPanelController extends GetxController { page: page.value, order: searchType!.type != 'video' ? null : order.value, duration: searchType!.type != 'video' ? null : duration.value, - tid: searchType!.type != 'video' ? null : tid.value, + tids: searchType!.type != 'video' ? null : tids.value, ); if (result['status']) { if (type == 'onRefresh') { diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index 06ee5d4b..15745bde 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -323,7 +323,7 @@ class VideoPanelController extends GetxController { SearchPanelController ctr = Get.find( tag: 'video${searchPanelCtr.keyword!}'); - ctr.tid.value = i['value']; + ctr.tids.value = i['value']; Get.back(); SmartDialog.showLoading(msg: '获取中'); await ctr.onRefresh(); From b37232931bc0c838b37af1cd2d6620afb0ca293b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 17:45:27 +0800 Subject: [PATCH 223/349] =?UTF-8?q?mod:=20=E9=BB=98=E8=AE=A4=E4=B8=8D?= =?UTF-8?q?=E8=B0=83=E7=94=A8=E7=94=B5=E6=B1=A0=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 1a2ce989..05c0476c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -17,7 +17,6 @@ import 'package:pilipala/pages/search/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/router/app_pages.dart'; import 'package:pilipala/pages/main/view.dart'; -import 'package:pilipala/services/disable_battery_opt.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; @@ -66,7 +65,6 @@ void main() async { } PiliSchame.init(); - DisableBatteryOpt(); } class MyApp extends StatelessWidget { From be3bffa33cee1d142d78bb7917c288ef9502ed37 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 8 Jun 2024 17:54:40 +0800 Subject: [PATCH 224/349] =?UTF-8?q?opt:=20=E6=88=91=E7=9A=84=E8=AE=A2?= =?UTF-8?q?=E9=98=85=E9=A1=B5=E9=9D=A2=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/subscription/view.dart | 31 +++++++++++++++--------- lib/pages/subscription/widgets/item.dart | 19 +++++++-------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index bcc03cc3..e1d1820d 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -53,18 +53,25 @@ class _SubPageState extends State { if (snapshot.connectionState == ConnectionState.done) { Map? data = snapshot.data; if (data != null && data['status']) { - return Obx( - () => ListView.builder( - controller: scrollController, - itemCount: _subController.subFolderData.value.list!.length, - itemBuilder: (context, index) { - return SubItem( - subFolderItem: - _subController.subFolderData.value.list![index], - cancelSub: _subController.cancelSub); - }, - ), - ); + if (_subController.subFolderData.value.list!.isNotEmpty) { + return Obx( + () => ListView.builder( + controller: scrollController, + itemCount: _subController.subFolderData.value.list!.length, + itemBuilder: (context, index) { + return SubItem( + subFolderItem: + _subController.subFolderData.value.list![index], + cancelSub: _subController.cancelSub); + }, + ), + ); + } else { + return const CustomScrollView( + physics: NeverScrollableScrollPhysics(), + slivers: [HttpError(errMsg: '', btnText: '没有数据', fn: null)], + ); + } } else { return CustomScrollView( physics: const NeverScrollableScrollPhysics(), diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index 0d424611..6c1cc2b9 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -114,16 +114,15 @@ class VideoContent extends StatelessWidget { Row( mainAxisAlignment: MainAxisAlignment.end, children: [ - SizedBox( - height: 35, - width: 35, - child: IconButton( - onPressed: () => cancelSub?.call(subFolderItem), - style: TextButton.styleFrom( - foregroundColor: Theme.of(context).colorScheme.outline, - padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), - ), - icon: const Icon(Icons.delete_outline, size: 18), + IconButton( + style: ButtonStyle( + padding: WidgetStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => cancelSub?.call(subFolderItem), + icon: Icon( + Icons.clear_outlined, + color: Theme.of(context).colorScheme.outline, + size: 18, ), ) ], From 006ba8bc59f1898f77d7c95c4a8013a861455514 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 9 Jun 2024 14:51:59 +0800 Subject: [PATCH 225/349] =?UTF-8?q?feat:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Podfile.lock | 6 +- lib/models/common/action_type.dart | 1 - lib/pages/setting/pages/action_menu_set.dart | 10 +- lib/pages/setting/style_setting.dart | 10 +- .../video/detail/introduction/controller.dart | 55 ++-- lib/pages/video/detail/introduction/view.dart | 234 ++++++++++++++---- .../introduction/widgets/action_item.dart | 39 +-- lib/utils/global_data.dart | 3 +- 8 files changed, 246 insertions(+), 112 deletions(-) diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 04fe6670..a400600f 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -23,7 +23,7 @@ PODS: - FMDB (2.7.5): - FMDB/standard (= 2.7.5) - FMDB/standard (2.7.5) - - gt3_flutter_plugin (0.0.8): + - gt3_flutter_plugin (0.0.9): - Flutter - GT3Captcha-iOS - GT3Captcha-iOS (0.15.8.3) @@ -171,13 +171,13 @@ SPEC CHECKSUMS: flutter_volume_controller: e4d5832f08008180f76e30faf671ffd5a425e529 fluttertoast: 31b00dabfa7fb7bacd9e7dbee580d7a2ff4bf265 FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a - gt3_flutter_plugin: bfa1f26e9a09dc00401514be5ed437f964cabf23 + gt3_flutter_plugin: 5bd2c08d3c19cbb6ee3b08f4358439e54c8ab2ee GT3Captcha-iOS: 5e3b1077834d8a9d6f4d64a447a30af3e14affe6 media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 - path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 saver_gallery: 2b4e584106fde2407ab51560f3851564963e6b78 screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 diff --git a/lib/models/common/action_type.dart b/lib/models/common/action_type.dart index 2284df6c..b888be17 100644 --- a/lib/models/common/action_type.dart +++ b/lib/models/common/action_type.dart @@ -11,7 +11,6 @@ enum ActionType { dislike, downloadCover, copyLink, - threeAction, // backgroundPlay, // listenVideo, // downloadVideo, diff --git a/lib/pages/setting/pages/action_menu_set.dart b/lib/pages/setting/pages/action_menu_set.dart index f7d92e17..7a4fd9ba 100644 --- a/lib/pages/setting/pages/action_menu_set.dart +++ b/lib/pages/setting/pages/action_menu_set.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/action_type.dart'; +import 'package:pilipala/utils/global_data.dart'; import '../../../utils/storage.dart'; class ActionMenuSetPage extends StatefulWidget { @@ -12,14 +13,14 @@ class ActionMenuSetPage extends StatefulWidget { } class _ActionMenuSetPageState extends State { - Box settingStorage = GStrorage.setting; + Box setting = GStrorage.setting; late List actionTypeSort; late List allLabels; @override void initState() { super.initState(); - actionTypeSort = settingStorage.get(SettingBoxKey.actionTypeSort, + actionTypeSort = setting.get(SettingBoxKey.actionTypeSort, defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); allLabels = actionMenuConfig; allLabels.sort((a, b) { @@ -36,8 +37,9 @@ class _ActionMenuSetPageState extends State { .where((i) => actionTypeSort.contains((i['value'] as ActionType).value)) .map((i) => (i['value'] as ActionType).value) .toList(); - settingStorage.put(SettingBoxKey.actionTypeSort, sortedTabbar); - SmartDialog.showToast('保存成功,下次启动时生效'); + setting.put(SettingBoxKey.actionTypeSort, sortedTabbar); + GlobalData().actionTypeSort = sortedTabbar; + SmartDialog.showToast('操作成功'); } void onReorder(int oldIndex, int newIndex) { diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 20fdada0..5fca0c86 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -289,11 +289,11 @@ class _StyleSettingState extends State { onTap: () => Get.toNamed('/navbarSetting'), title: Text('底部导航栏设置', style: titleStyle), ), - ListTile( - dense: false, - onTap: () => Get.toNamed('/actionMenuSet'), - title: Text('操作菜单设置', style: titleStyle), - ), + // ListTile( + // dense: false, + // onTap: () => Get.toNamed('/actionMenuSet'), + // title: Text('操作菜单设置', style: titleStyle), + // ), if (Platform.isAndroid) ListTile( dense: false, diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 9ed04870..9c542f21 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -38,6 +38,8 @@ class VideoIntroController extends GetxController { RxBool hasCoin = false.obs; // 是否收藏 RxBool hasFav = false.obs; + // 是否不喜欢 + RxBool hasDisLike = false.obs; Box userInfoCache = GStrorage.userInfo; bool userLogin = false; Rx favFolderData = FavFolderData().obs; @@ -153,36 +155,16 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('🙏 UP已经收到了~'); return false; } - SmartDialog.show( - useSystem: true, - animationType: SmartAnimationType.centerFade_otherSlide, - builder: (BuildContext context) { - return AlertDialog( - title: const Text('提示'), - content: const Text('一键三连 给UP送温暖'), - actions: [ - TextButton( - onPressed: () => SmartDialog.dismiss(), - child: const Text('点错了')), - TextButton( - onPressed: () async { - var result = await VideoHttp.oneThree(bvid: bvid); - if (result['status']) { - hasLike.value = result["data"]["like"]; - hasCoin.value = result["data"]["coin"]; - hasFav.value = result["data"]["fav"]; - SmartDialog.showToast('三连成功 🎉'); - } else { - SmartDialog.showToast(result['msg']); - } - SmartDialog.dismiss(); - }, - child: const Text('确认'), - ) - ], - ); - }, - ); + var result = await VideoHttp.oneThree(bvid: bvid); + print('🤣🦴:${result["data"]}'); + if (result['status']) { + hasLike.value = result["data"]["like"]; + hasCoin.value = result["data"]["coin"]; + hasFav.value = result["data"]["fav"]; + SmartDialog.showToast('三连成功'); + } else { + SmartDialog.showToast(result['msg']); + } } // (取消)点赞 @@ -193,9 +175,8 @@ class VideoIntroController extends GetxController { } var result = await VideoHttp.likeVideo(bvid: bvid, type: !hasLike.value); if (result['status']) { - // hasLike.value = result["data"] == 1 ? true : false; if (!hasLike.value) { - SmartDialog.showToast('点赞成功 👍'); + SmartDialog.showToast('点赞成功'); hasLike.value = true; videoDetail.value.stat!.like = videoDetail.value.stat!.like! + 1; } else if (hasLike.value) { @@ -215,6 +196,10 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('账号未登录'); return; } + if (hasCoin.value) { + SmartDialog.showToast('已投过币了'); + return; + } showDialog( context: Get.context!, builder: (context) { @@ -236,7 +221,7 @@ class VideoIntroController extends GetxController { var res = await VideoHttp.coinVideo( bvid: bvid, multiply: _tempThemeValue); if (res['status']) { - SmartDialog.showToast('投币成功 👏'); + SmartDialog.showToast('投币成功'); hasCoin.value = true; videoDetail.value.stat!.coin = videoDetail.value.stat!.coin! + _tempThemeValue; @@ -269,7 +254,7 @@ class VideoIntroController extends GetxController { if (result['status']) { // 重新获取收藏状态 await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); + SmartDialog.showToast('操作成功'); } else { SmartDialog.showToast(result['msg']); } @@ -299,7 +284,7 @@ class VideoIntroController extends GetxController { Get.back(); // 重新获取收藏状态 await queryHasFavVideo(); - SmartDialog.showToast('✅ 操作成功'); + SmartDialog.showToast('操作成功'); } else { SmartDialog.showToast(result['msg']); } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index ceec8fa4..91287c59 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,6 @@ +import 'dart:ffi'; + +import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -15,6 +18,7 @@ import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/introduction/controller.dart'; import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; import '../../../../http/user.dart'; @@ -146,13 +150,18 @@ class _VideoInfoState extends State with TickerProviderStateMixin { RxBool isExpand = false.obs; late ExpandableController _expandableCtr; - void Function()? handleState(Future Function() action) { + // 一键三连动画 + late AnimationController _controller; + late Animation _scaleTransition; + final RxDouble _progress = 0.0.obs; + + void Function()? handleState(Future Function() action) { return isProcessing ? null : () async { - setState(() => isProcessing = true); - await action(); - setState(() => isProcessing = false); + isProcessing = true; + await action.call(); + isProcessing = false; }; } @@ -169,6 +178,25 @@ class _VideoInfoState extends State with TickerProviderStateMixin { follower = Utils.numFormat(videoIntroController.userStat['follower']); enableAi = setting.get(SettingBoxKey.enableAi, defaultValue: true); _expandableCtr = ExpandableController(initialExpanded: false); + + /// 一键三连动画 + _controller = AnimationController( + duration: const Duration(milliseconds: 1500), + reverseDuration: const Duration(milliseconds: 300), + vsync: this, + ); + _scaleTransition = Tween(begin: 0.5, end: 1.5).animate(_controller) + ..addListener(() async { + _progress.value = _scaleTransition.value - 0.5; + if (_progress.value == 1) { + if (_controller.status == AnimationStatus.completed) { + await videoIntroController.actionOneThree(); + } + _progress.value = 0; + _scaleTransition.removeListener(() {}); + _controller.stop(); + } + }); } // 收藏 @@ -249,6 +277,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { @override void dispose() { _expandableCtr.dispose(); + _controller.dispose(); + _scaleTransition.removeListener(() {}); super.dispose(); } @@ -538,6 +568,157 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } Widget actionGrid(BuildContext context, videoIntroController) { + final actionTypeSort = GlobalData().actionTypeSort; + + Widget progressWidget(progress) { + return SizedBox( + width: 68, + height: 68, + child: CircularProgressIndicator( + value: progress.value, + strokeWidth: 4, + ), + ); + } + + Map menuListWidgets = { + 'like': Obx( + () { + bool likeStatus = videoIntroController.hasLike.value; + ColorScheme colorScheme = Theme.of(context).colorScheme; + return Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + InkWell( + onTapDown: (details) { + feedBack(); + _controller.forward(); + }, + onTapUp: (TapUpDetails details) { + if (_progress.value == 0) { + feedBack(); + EasyThrottle.throttle( + 'my-throttler', const Duration(milliseconds: 200), () { + videoIntroController.actionLikeVideo(); + }); + } + _controller.reverse(); + }, + borderRadius: StyleString.mdRadius, + child: SizedBox( + width: (Get.size.width - 24) / 5, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const SizedBox(height: 4), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: + (Widget child, Animation animation) { + return ScaleTransition( + scale: animation, child: child); + }, + child: Icon( + key: ValueKey(likeStatus), + likeStatus + ? Icons.thumb_up + : Icons.thumb_up_alt_outlined, + color: likeStatus + ? colorScheme.primary + : colorScheme.outline, + ), + ), + const SizedBox(height: 6), + Text( + widget.videoDetail!.stat!.like!.toString(), + style: TextStyle( + color: likeStatus ? colorScheme.primary : null, + fontSize: + Theme.of(context).textTheme.labelSmall!.fontSize, + ), + ) + ], + ), + ), + ), + ], + ); + }, + ), + 'coin': Obx( + () => Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + ActionItem( + icon: Image.asset('assets/images/coin.png', width: 30), + onTap: handleState(videoIntroController.actionCoinVideo), + selectStatus: videoIntroController.hasCoin.value, + text: widget.videoDetail!.stat!.coin!.toString(), + ), + ], + ), + ), + 'collect': Obx( + () => Stack( + alignment: Alignment.center, + children: [ + progressWidget(_progress), + ActionItem( + icon: const Icon(Icons.star_border), + selectIcon: const Icon(Icons.star), + onTap: () => showFavBottomSheet(), + onLongPress: () => showFavBottomSheet(type: 'longPress'), + selectStatus: videoIntroController.hasFav.value, + text: widget.videoDetail!.stat!.favorite!.toString(), + ), + ], + ), + ), + 'watchLater': ActionItem( + icon: const Icon(Icons.watch_later_outlined), + onTap: () async { + final res = + await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); + SmartDialog.showToast(res['msg']); + }, + selectStatus: false, + text: '稍后看', + ), + 'share': ActionItem( + icon: const Icon(Icons.share), + onTap: () => videoIntroController.actionShareVideo(), + selectStatus: false, + text: '分享', + ), + 'dislike': Obx( + () => ActionItem( + icon: const Icon(Icons.thumb_down_alt_outlined), + selectIcon: const Icon(Icons.thumb_down), + onTap: () {}, + selectStatus: videoIntroController.hasDisLike.value, + text: '不喜欢', + ), + ), + 'downloadCover': ActionItem( + icon: const Icon(Icons.image_outlined), + onTap: () {}, + selectStatus: false, + text: '下载封面', + ), + 'copyLink': ActionItem( + icon: const Icon(Icons.link_outlined), + onTap: () {}, + selectStatus: false, + text: '复制链接', + ), + }; + final List list = []; + for (var i = 0; i < actionTypeSort.length; i++) { + list.add(menuListWidgets[actionTypeSort[i]]!); + } + return LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return Container( @@ -545,50 +726,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { height: constraints.maxWidth / 5, child: ListView( scrollDirection: Axis.horizontal, - children: [ - Obx( - () => ActionItem( - icon: const Icon(Icons.thumb_up_alt_outlined), - selectIcon: const Icon(Icons.thumb_up), - onTap: handleState(videoIntroController.actionLikeVideo), - selectStatus: videoIntroController.hasLike.value, - text: widget.videoDetail!.stat!.like!.toString()), - ), - Obx( - () => ActionItem( - icon: Image.asset('assets/images/coin.png', width: 30), - onTap: handleState(videoIntroController.actionCoinVideo), - selectStatus: videoIntroController.hasCoin.value, - text: widget.videoDetail!.stat!.coin!.toString(), - ), - ), - Obx( - () => ActionItem( - icon: const Icon(Icons.star_border), - selectIcon: const Icon(Icons.star), - onTap: () => showFavBottomSheet(), - onLongPress: () => showFavBottomSheet(type: 'longPress'), - selectStatus: videoIntroController.hasFav.value, - text: widget.videoDetail!.stat!.favorite!.toString(), - ), - ), - ActionItem( - icon: const Icon(Icons.watch_later_outlined), - onTap: () async { - final res = - await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); - SmartDialog.showToast(res['msg']); - }, - selectStatus: false, - text: '稍后看', - ), - ActionItem( - icon: const Icon(Icons.share), - onTap: () => videoIntroController.actionShareVideo(), - selectStatus: false, - text: '分享', - ), - ], + children: list, ), ); }); diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 2a89afe7..af409dfc 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -38,20 +38,29 @@ class ActionItem extends StatelessWidget { mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 4), - icon is Icon - ? Icon( - selectStatus ? selectIcon!.icon ?? icon!.icon : icon!.icon, - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ) - : Image.asset( - 'assets/images/coin.png', - width: 25, - color: selectStatus - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.outline, - ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 300), + transitionBuilder: (Widget child, Animation animation) { + return ScaleTransition(scale: animation, child: child); + }, + child: icon is Icon + ? Icon( + selectStatus + ? selectIcon!.icon ?? icon!.icon + : icon!.icon, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ) + : Image.asset( + key: ValueKey(selectStatus), + 'assets/images/coin.png', + width: 25, + color: selectStatus + ? Theme.of(context).colorScheme.primary + : Theme.of(context).colorScheme.outline, + ), + ), const SizedBox(height: 6), Text( text ?? '', @@ -60,7 +69,7 @@ class ActionItem extends StatelessWidget { selectStatus ? Theme.of(context).colorScheme.primary : null, fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, ), - ) + ), ], ), ), diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart index 29791210..97bff5a5 100644 --- a/lib/utils/global_data.dart +++ b/lib/utils/global_data.dart @@ -11,7 +11,8 @@ class GlobalData { bool enablePlayerControlAnimation = true; final bool enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); - + List actionTypeSort = setting.get(SettingBoxKey.actionTypeSort, + defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); // 私有构造函数 GlobalData._(); From d94ddca9056f1f00f8b2a7412a3cdcf86e07edfa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 00:58:04 +0800 Subject: [PATCH 226/349] =?UTF-8?q?opt:=20=E4=B8=89=E8=BF=9E=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=9D=A1=E6=A0=B7=E5=BC=8F&=E7=99=BB=E5=BD=95?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 91287c59..22f2dfe2 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,5 +1,3 @@ -import 'dart:ffi'; - import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; @@ -576,7 +574,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { height: 68, child: CircularProgressIndicator( value: progress.value, - strokeWidth: 4, + strokeWidth: 2, ), ); } @@ -593,6 +591,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { InkWell( onTapDown: (details) { feedBack(); + if (videoIntroController.userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } _controller.forward(); }, onTapUp: (TapUpDetails details) { From 61c29d30829a59d189225abfc1eae830fc0d1ff4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 01:09:28 +0800 Subject: [PATCH 227/349] =?UTF-8?q?fix:=20=E8=B7=B3=E8=BD=AC=E7=99=BB?= =?UTF-8?q?=E5=BD=95=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/login/controller.dart | 6 +++--- lib/pages/login/view.dart | 30 +++++++++++++----------------- lib/pages/mine/controller.dart | 3 +-- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/lib/pages/login/controller.dart b/lib/pages/login/controller.dart index b5ddba16..fbb06e2f 100644 --- a/lib/pages/login/controller.dart +++ b/lib/pages/login/controller.dart @@ -31,7 +31,7 @@ class LoginPageController extends GetxController { // 倒计时60s RxInt seconds = 60.obs; - late Timer timer; + Timer? timer; RxBool smsCodeSendStatus = false.obs; // 默认密码登录 @@ -43,7 +43,7 @@ class LoginPageController extends GetxController { late int webSmsCode; RxInt validSeconds = 180.obs; - late Timer validTimer; + Timer? validTimer; late String qrcodeKey; // 监听pageView切换 @@ -329,7 +329,7 @@ class LoginPageController extends GetxController { var res = await LoginHttp.queryWebQrcodeStatus(qrcodeKey); if (res['status']) { await LoginUtils.confirmLogin('', null); - validTimer.cancel(); + validTimer?.cancel(); Get.back(); } } diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index cd91ea26..85a8adf0 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -15,8 +15,10 @@ class _LoginPageState extends State { final LoginPageController _loginPageCtr = Get.put(LoginPageController()); @override - void initState() { - super.initState(); + void dispose() { + _loginPageCtr.validTimer?.cancel(); + _loginPageCtr.timer?.cancel(); + super.dispose(); } @override @@ -51,7 +53,7 @@ class _LoginPageState extends State { }, ); }, - icon: const Icon(Icons.language), + icon: const Icon(Icons.language, size: 20), ), IconButton( tooltip: '二维码登录', @@ -90,7 +92,7 @@ class _LoginPageState extends State { Map data = snapshot.data as Map; return QrImageView( data: data['data']['url'], - backgroundColor: Colors.transparent, + backgroundColor: Colors.white, ); } else { return const Center( @@ -131,9 +133,11 @@ class _LoginPageState extends State { ); }); }, - ); + ).then((value) { + _loginPageCtr.validTimer!.cancel(); + }); }, - icon: const Icon(Icons.qr_code), + icon: const Icon(Icons.qr_code, size: 20), ), const SizedBox(width: 22), ], @@ -164,17 +168,9 @@ class _LoginPageState extends State { fontSize: 34, fontWeight: FontWeight.w500), ), - Row( - children: [ - Text( - '请使用您的 BiliBili 账号登录。', - style: Theme.of(context).textTheme.titleSmall!, - ), - GestureDetector( - onTap: () {}, - child: const Icon(Icons.info_outline, size: 16), - ) - ], + Text( + '请使用您的 BiliBili 账号登录。', + style: Theme.of(context).textTheme.titleSmall!, ), Container( margin: const EdgeInsets.only(top: 38, bottom: 15), diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 153a7162..75c50d82 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -33,8 +33,7 @@ class MineController extends GetxController { onLogin() async { if (!userLogin.value) { - // RoutePush.loginPush(); - Get.toNamed('/loginPage'); + Get.toNamed('/loginPage', preventDuplicates: false); } else { int mid = userInfo.value.mid!; String face = userInfo.value.face!; From 51be62cd26f06364d9a8ef923ed13b874ce6c88d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 12:50:15 +0800 Subject: [PATCH 228/349] =?UTF-8?q?opt:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 22f2dfe2..3b89d1de 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -185,7 +185,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ); _scaleTransition = Tween(begin: 0.5, end: 1.5).animate(_controller) ..addListener(() async { - _progress.value = _scaleTransition.value - 0.5; + _progress.value = + double.parse((_scaleTransition.value - 0.5).toStringAsFixed(3)); if (_progress.value == 1) { if (_controller.status == AnimationStatus.completed) { await videoIntroController.actionOneThree(); @@ -570,8 +571,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget progressWidget(progress) { return SizedBox( - width: 68, - height: 68, + width: 33, + height: 33, child: CircularProgressIndicator( value: progress.value, strokeWidth: 2, @@ -585,16 +586,15 @@ class _VideoInfoState extends State with TickerProviderStateMixin { bool likeStatus = videoIntroController.hasLike.value; ColorScheme colorScheme = Theme.of(context).colorScheme; return Stack( - alignment: Alignment.center, children: [ - progressWidget(_progress), + Positioned(child: progressWidget(_progress), top: 15, left: 24), InkWell( onTapDown: (details) { feedBack(); - if (videoIntroController.userInfo == null) { - SmartDialog.showToast('账号未登录'); - return; - } + // if (videoIntroController.userInfo == null) { + // SmartDialog.showToast('账号未登录'); + // return; + // } _controller.forward(); }, onTapUp: (TapUpDetails details) { @@ -650,9 +650,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), 'coin': Obx( () => Stack( - alignment: Alignment.center, children: [ - progressWidget(_progress), + Positioned(child: progressWidget(_progress), top: 15, left: 24), ActionItem( icon: Image.asset('assets/images/coin.png', width: 30), onTap: handleState(videoIntroController.actionCoinVideo), @@ -664,9 +663,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), 'collect': Obx( () => Stack( - alignment: Alignment.center, children: [ - progressWidget(_progress), + Positioned(child: progressWidget(_progress), top: 15, left: 24), ActionItem( icon: const Icon(Icons.star_border), selectIcon: const Icon(Icons.star), From 128c9201bda0efae89cc627bdd841a5313e921dd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 13:36:23 +0800 Subject: [PATCH 229/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E6=A1=86?= =?UTF-8?q?=E5=AE=89=E5=85=A8=E8=B7=9D=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_new/view.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index a136f623..d55e2c1c 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -214,7 +214,15 @@ class _VideoReplyNewDialogState extends State ), Container( height: 52, - padding: const EdgeInsets.only(left: 12, right: 12), + padding: const EdgeInsets.only( + left: 12, + right: 12, + ), + margin: EdgeInsets.only( + bottom: toolbarType == 'input' && keyboardHeight == 0.0 + ? MediaQuery.of(context).padding.bottom + : 0, + ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ From e47816d0c8f748411779bde717de2e2d72c51250 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 10 Jun 2024 14:18:59 +0800 Subject: [PATCH 230/349] =?UTF-8?q?opt:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 33 ++++++++++++++----- .../introduction/widgets/action_item.dart | 2 +- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 3b89d1de..93cc26c9 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -571,8 +571,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { Widget progressWidget(progress) { return SizedBox( - width: 33, - height: 33, + width: const IconThemeData.fallback().size! + 5, + height: const IconThemeData.fallback().size! + 5, child: CircularProgressIndicator( value: progress.value, strokeWidth: 2, @@ -587,14 +587,19 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ColorScheme colorScheme = Theme.of(context).colorScheme; return Stack( children: [ - Positioned(child: progressWidget(_progress), top: 15, left: 24), + Positioned( + top: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size!), + left: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size! + 5) / 2, + child: progressWidget(_progress)), InkWell( onTapDown: (details) { feedBack(); - // if (videoIntroController.userInfo == null) { - // SmartDialog.showToast('账号未登录'); - // return; - // } + if (videoIntroController.userInfo == null) { + SmartDialog.showToast('账号未登录'); + return; + } _controller.forward(); }, onTapUp: (TapUpDetails details) { @@ -651,7 +656,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { 'coin': Obx( () => Stack( children: [ - Positioned(child: progressWidget(_progress), top: 15, left: 24), + Positioned( + top: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size!), + left: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size! + 5) / 2, + child: progressWidget(_progress)), ActionItem( icon: Image.asset('assets/images/coin.png', width: 30), onTap: handleState(videoIntroController.actionCoinVideo), @@ -664,7 +674,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { 'collect': Obx( () => Stack( children: [ - Positioned(child: progressWidget(_progress), top: 15, left: 24), + Positioned( + top: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size!), + left: ((Get.size.width - 24) / 5) / 2 - + (const IconThemeData.fallback().size! + 5) / 2, + child: progressWidget(_progress)), ActionItem( icon: const Icon(Icons.star_border), selectIcon: const Icon(Icons.star), diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index af409dfc..3288b2fa 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -55,7 +55,7 @@ class ActionItem extends StatelessWidget { : Image.asset( key: ValueKey(selectStatus), 'assets/images/coin.png', - width: 25, + width: const IconThemeData.fallback().size, color: selectStatus ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, From beb5945135209ce70a20f48b37f18745b1200298 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 11 Jun 2024 00:06:49 +0800 Subject: [PATCH 231/349] =?UTF-8?q?fix:=20=E7=BD=91=E7=BB=9C=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E6=97=B6=E7=99=BB=E5=BD=95=E7=8A=B6=E6=80=81=E9=87=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/mine/controller.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/pages/mine/controller.dart b/lib/pages/mine/controller.dart index 75c50d82..e682a7e5 100644 --- a/lib/pages/mine/controller.dart +++ b/lib/pages/mine/controller.dart @@ -57,8 +57,6 @@ class MineController extends GetxController { } else { resetUserInfo(); } - } else { - resetUserInfo(); } await queryUserStatOwner(); return res; From 7af1ebd3d5fd63d4dab2b5ec8dc0356dd02046d7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 11 Jun 2024 22:59:07 +0800 Subject: [PATCH 232/349] =?UTF-8?q?fix:=20=E5=BC=B9=E5=B9=95=E6=98=BE?= =?UTF-8?q?=E7=A4=BA&=E5=BC=B9=E5=B9=95=E8=AE=BE=E7=BD=AE=E7=BC=93?= =?UTF-8?q?=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 41 +++++++++++-------- .../video/detail/widgets/header_control.dart | 4 +- lib/plugin/pl_player/controller.dart | 17 +++++--- 3 files changed, 37 insertions(+), 25 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a0f5d6bc..a99ee936 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -591,27 +591,32 @@ class _VideoDetailPageState extends State verticalScreen(); } }, - child: Hero( - tag: heroTag, - child: Stack( - children: [ - if (isShowing) videoPlayerPanel, + child: LayoutBuilder( + builder: (BuildContext context, + BoxConstraints constraints) { + return Hero( + tag: heroTag, + child: Stack( + children: [ + if (isShowing) videoPlayerPanel, - /// 关闭自动播放时 手动播放 - Obx( - () => Visibility( - visible: !vdCtr.autoPlay.value && - vdCtr.isShowCover.value, - child: Positioned( - top: 0, - left: 0, - right: 0, - child: handlePlayPanel(), + /// 关闭自动播放时 手动播放 + Obx( + () => Visibility( + visible: !vdCtr.autoPlay.value && + vdCtr.isShowCover.value, + child: Positioned( + top: 0, + left: 0, + right: 0, + child: handlePlayPanel(), + ), + ), ), - ), + ], ), - ], - ), + ); + }, ), ), ), diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index d839a1eb..b597a060 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1069,7 +1069,9 @@ class _HeaderControlState extends State { ); }); }, - ); + ).then((value) { + widget.controller!.cacheDanmakuOption(); + }); } /// 播放顺序 diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 03c2efbe..2865a117 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -1089,6 +1089,16 @@ class PlPlayerController { videoStorage.put(VideoBoxKey.playRepeat, type.value); } + /// 缓存本次弹幕选项 + cacheDanmakuOption() { + localCache.put(LocalCacheKey.danmakuBlockType, blockTypes); + localCache.put(LocalCacheKey.danmakuShowArea, showArea); + localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); + localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); + localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal); + localCache.put(LocalCacheKey.strokeWidth, strokeWidth); + } + Future dispose({String type = 'single'}) async { // 每次减1,最后销毁 if (type == 'single' && playerCount.value > 1) { @@ -1118,12 +1128,7 @@ class PlPlayerController { // dataStatus.status.close(); /// 缓存本次弹幕选项 - localCache.put(LocalCacheKey.danmakuBlockType, blockTypes); - localCache.put(LocalCacheKey.danmakuShowArea, showArea); - localCache.put(LocalCacheKey.danmakuOpacity, opacityVal); - localCache.put(LocalCacheKey.danmakuFontScale, fontSizeVal); - localCache.put(LocalCacheKey.danmakuDuration, danmakuDurationVal); - localCache.put(LocalCacheKey.strokeWidth, strokeWidth); + cacheDanmakuOption(); if (_videoPlayerController != null) { var pp = _videoPlayerController!.platform as NativePlayer; await pp.setProperty('audio-files', ''); From 5c3d3cc957e037a699a7f8a19a32c375227d35f5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 12 Jun 2024 23:37:29 +0800 Subject: [PATCH 233/349] undo: flutter version 3.22.1 -> 3.19.1 --- lib/common/widgets/http_error.dart | 2 +- lib/common/widgets/video_card_h.dart | 2 +- lib/pages/bangumi/introduction/view.dart | 8 ++--- lib/pages/bangumi/widgets/bangumi_panel.dart | 2 +- lib/pages/dynamics/view.dart | 2 +- lib/pages/dynamics/widgets/author_panel.dart | 2 +- .../fav_detail/widget/fav_video_card.dart | 2 +- lib/pages/history/widgets/item.dart | 5 +-- lib/pages/home/view.dart | 8 ++--- .../live_room/widgets/bottom_control.dart | 2 +- lib/pages/login/view.dart | 6 ++-- lib/pages/media/view.dart | 4 +-- lib/pages/search/widgets/search_text.dart | 5 +-- .../search_panel/widgets/video_panel.dart | 2 +- lib/pages/setting/extra_setting.dart | 6 ++-- lib/pages/setting/style_setting.dart | 6 ++-- lib/pages/setting/widgets/switch_item.dart | 6 ++-- lib/pages/subscription/widgets/item.dart | 2 +- .../introduction/widgets/page_panel.dart | 2 +- .../detail/reply_new/toolbar_icon_button.dart | 4 +-- lib/pages/video/detail/reply_new/view.dart | 2 +- lib/pages/video/detail/view.dart | 2 +- .../video/detail/widgets/header_control.dart | 8 ++--- lib/pages/whisper_detail/view.dart | 6 ++-- lib/plugin/pl_player/view.dart | 6 ++-- lib/plugin/pl_player/widgets/common_btn.dart | 2 +- .../pl_player/widgets/play_pause_btn.dart | 2 +- lib/utils/image_save.dart | 2 +- pubspec.lock | 36 +++++++++---------- 29 files changed, 72 insertions(+), 72 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index b130faae..cbc6659b 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -34,7 +34,7 @@ class HttpError extends StatelessWidget { fn!(); }, style: ButtonStyle( - backgroundColor: WidgetStateProperty.resolveWith((states) { + backgroundColor: MaterialStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.primary.withAlpha(20); }), ), diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index c674b223..1265477f 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -305,7 +305,7 @@ class VideoContent extends StatelessWidget { if (source == 'later') ...[ IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => onPressedFn?.call(), icon: Icon( diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index bb85be1a..95d4d898 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -232,11 +232,11 @@ class _BangumiInfoState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: - WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all( + EdgeInsets.zero), backgroundColor: - WidgetStateProperty.resolveWith( - (Set states) { + MaterialStateProperty.resolveWith( + (Set states) { return t.colorScheme.primaryContainer .withOpacity(0.7); }), diff --git a/lib/pages/bangumi/widgets/bangumi_panel.dart b/lib/pages/bangumi/widgets/bangumi_panel.dart index 345a47b6..3df7ce25 100644 --- a/lib/pages/bangumi/widgets/bangumi_panel.dart +++ b/lib/pages/bangumi/widgets/bangumi_panel.dart @@ -139,7 +139,7 @@ class _BangumiPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.bangumiIntroController?.bottomSheetController = diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 258ad531..1775798e 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -162,7 +162,7 @@ class _DynamicsPageState extends State decoration: BoxDecoration( color: Theme.of(context) .colorScheme - .surfaceContainerHighest + .surfaceVariant .withOpacity(0.7), borderRadius: BorderRadius.circular(20), ), diff --git a/lib/pages/dynamics/widgets/author_panel.dart b/lib/pages/dynamics/widgets/author_panel.dart index e66e2a91..8acdc26a 100644 --- a/lib/pages/dynamics/widgets/author_panel.dart +++ b/lib/pages/dynamics/widgets/author_panel.dart @@ -82,7 +82,7 @@ class AuthorPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { showModalBottomSheet( diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 72d7b4a0..79e5c073 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -217,7 +217,7 @@ class VideoContent extends StatelessWidget { bottom: -4, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { showDialog( diff --git a/lib/pages/history/widgets/item.dart b/lib/pages/history/widgets/item.dart index 8e71df6f..baebfedb 100644 --- a/lib/pages/history/widgets/item.dart +++ b/lib/pages/history/widgets/item.dart @@ -217,10 +217,11 @@ class HistoryItem extends StatelessWidget { curve: Curves.easeInOut, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all( + padding: MaterialStateProperty.all( EdgeInsets.zero), backgroundColor: - WidgetStateProperty.resolveWith( + MaterialStateProperty + .resolveWith( (states) { return Colors.white .withOpacity(0.8); diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 0c45a262..a25389bd 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -278,8 +278,8 @@ class DefaultUser extends StatelessWidget { height: 38, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - backgroundColor: WidgetStateProperty.resolveWith((states) { + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith((states) { return Theme.of(context).colorScheme.onInverseSurface; }), ), @@ -371,8 +371,8 @@ class CustomChip extends StatelessWidget { ), backgroundColor: secondaryContainer, selectedColor: secondaryContainer, - color: WidgetStateProperty.resolveWith( - (Set states) => secondaryContainer.withAlpha(200)), + color: MaterialStateProperty.resolveWith( + (Set states) => secondaryContainer.withAlpha(200)), padding: const EdgeInsets.fromLTRB(7, 1, 7, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), diff --git a/lib/pages/live_room/widgets/bottom_control.dart b/lib/pages/live_room/widgets/bottom_control.dart index 6abb1260..4dd7c538 100644 --- a/lib/pages/live_room/widgets/bottom_control.dart +++ b/lib/pages/live_room/widgets/bottom_control.dart @@ -124,7 +124,7 @@ class _BottomControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; diff --git a/lib/pages/login/view.dart b/lib/pages/login/view.dart index 04d5a1a8..85a8adf0 100644 --- a/lib/pages/login/view.dart +++ b/lib/pages/login/view.dart @@ -249,7 +249,8 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - WidgetStateProperty.resolveWith((states) { + MaterialStateProperty.resolveWith( + (states) { return Theme.of(context) .colorScheme .primary @@ -339,7 +340,8 @@ class _LoginPageState extends State { IconButton( style: ButtonStyle( backgroundColor: - WidgetStateProperty.resolveWith((states) { + MaterialStateProperty.resolveWith( + (states) { return Theme.of(context) .colorScheme .primary diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index f6a033e5..cc413e59 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -178,10 +178,10 @@ class _MediaPageState extends State child: Center( child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all( + padding: MaterialStateProperty.all( EdgeInsets.zero), backgroundColor: - WidgetStateProperty.resolveWith( + MaterialStateProperty.resolveWith( (states) { return Theme.of(context) .colorScheme diff --git a/lib/pages/search/widgets/search_text.dart b/lib/pages/search/widgets/search_text.dart index d3ffafea..bcc8c17e 100644 --- a/lib/pages/search/widgets/search_text.dart +++ b/lib/pages/search/widgets/search_text.dart @@ -20,10 +20,7 @@ class SearchText extends StatelessWidget { return Material( color: isSelect ? Theme.of(context).colorScheme.primaryContainer - : Theme.of(context) - .colorScheme - .surfaceContainerHighest - .withOpacity(0.5), + : Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.5), borderRadius: BorderRadius.circular(6), child: Padding( padding: EdgeInsets.zero, diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index bc9b48c9..15745bde 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -93,7 +93,7 @@ class SearchVideoPanel extends StatelessWidget { height: 32, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => controller.onShowFilterSheet(ctr), icon: Icon( diff --git a/lib/pages/setting/extra_setting.dart b/lib/pages/setting/extra_setting.dart index 885a831c..aaaa8b84 100644 --- a/lib/pages/setting/extra_setting.dart +++ b/lib/pages/setting/extra_setting.dart @@ -240,10 +240,10 @@ class _ExtraSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: WidgetStateProperty.resolveWith( - (Set states) { + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == WidgetState.selected) { + states.first == MaterialState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index b6ee5041..5fca0c86 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -69,10 +69,10 @@ class _StyleSettingState extends State { alignment: Alignment.centerRight, scale: 0.8, child: Switch( - thumbIcon: WidgetStateProperty.resolveWith( - (Set states) { + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { if (states.isNotEmpty && - states.first == WidgetState.selected) { + states.first == MaterialState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/setting/widgets/switch_item.dart b/lib/pages/setting/widgets/switch_item.dart index 36c4433e..d0c2bbf2 100644 --- a/lib/pages/setting/widgets/switch_item.dart +++ b/lib/pages/setting/widgets/switch_item.dart @@ -70,9 +70,9 @@ class _SetSwitchItemState extends State { alignment: Alignment.centerRight, // 缩放Switch的大小后保持右侧对齐, 避免右侧空隙过大 scale: 0.8, child: Switch( - thumbIcon: - WidgetStateProperty.resolveWith((Set states) { - if (states.isNotEmpty && states.first == WidgetState.selected) { + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { + if (states.isNotEmpty && states.first == MaterialState.selected) { return const Icon(Icons.done); } return null; // All other states will use the default thumbIcon. diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index 6c1cc2b9..b244d3c7 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -116,7 +116,7 @@ class VideoContent extends StatelessWidget { children: [ IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => cancelSub?.call(subFolderItem), icon: Icon( diff --git a/lib/pages/video/detail/introduction/widgets/page_panel.dart b/lib/pages/video/detail/introduction/widgets/page_panel.dart index c8111847..266f5566 100644 --- a/lib/pages/video/detail/introduction/widgets/page_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/page_panel.dart @@ -106,7 +106,7 @@ class _PagesPanelState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { widget.videoIntroCtr.bottomSheetController = diff --git a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart index bf810ce2..c4390796 100644 --- a/lib/pages/video/detail/reply_new/toolbar_icon_button.dart +++ b/lib/pages/video/detail/reply_new/toolbar_icon_button.dart @@ -27,8 +27,8 @@ class ToolbarIconButton extends StatelessWidget { ? Theme.of(context).colorScheme.onSecondaryContainer : Theme.of(context).colorScheme.outline, style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - backgroundColor: WidgetStateProperty.resolveWith((states) { + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith((states) { return selected ? Theme.of(context).colorScheme.secondaryContainer : null; diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index d55e2c1c..40267559 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -267,7 +267,7 @@ class _VideoReplyNewDialogState extends State size: 22), label: const Text('转发到动态'), style: ButtonStyle( - foregroundColor: WidgetStateProperty.all( + foregroundColor: MaterialStateProperty.all( isForward.value ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a99ee936..22271c2b 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -412,7 +412,7 @@ class _VideoDetailPageState extends State height: 32, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => vdCtr.showShootDanmakuSheet(), child: diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index b597a060..f51edff1 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1250,7 +1250,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => showShootDanmakuSheet(), child: const Text( @@ -1265,7 +1265,7 @@ class _HeaderControlState extends State { child: Obx( () => IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { _.isOpenDanmu.value = !_.isOpenDanmu.value; @@ -1288,7 +1288,7 @@ class _HeaderControlState extends State { height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () async { bool canUsePiP = false; @@ -1332,7 +1332,7 @@ class _HeaderControlState extends State { height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => showSetSpeedSheet(), child: Text( diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 042afca1..1701be33 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -95,9 +95,9 @@ class _WhisperDetailPageState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), - backgroundColor: WidgetStateProperty.resolveWith( - (Set states) { + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { return Theme.of(context) .colorScheme .primaryContainer diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 0e405884..34140be8 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -279,7 +279,7 @@ class _PLVideoPlayerState extends State widget.showEposideCb?.call(); }, style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), child: const Text( '选集', @@ -294,7 +294,7 @@ class _PLVideoPlayerState extends State child: TextButton( onPressed: () => _.toggleVideoFit(), style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), child: Obx( () => Text( @@ -311,7 +311,7 @@ class _PLVideoPlayerState extends State height: 34, child: TextButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () {}, child: Obx( diff --git a/lib/plugin/pl_player/widgets/common_btn.dart b/lib/plugin/pl_player/widgets/common_btn.dart index bf9467a8..5f33311c 100644 --- a/lib/plugin/pl_player/widgets/common_btn.dart +++ b/lib/plugin/pl_player/widgets/common_btn.dart @@ -17,7 +17,7 @@ class ComBtn extends StatelessWidget { height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () { fuc!(); diff --git a/lib/plugin/pl_player/widgets/play_pause_btn.dart b/lib/plugin/pl_player/widgets/play_pause_btn.dart index 7547a1cb..6cbe31f0 100644 --- a/lib/plugin/pl_player/widgets/play_pause_btn.dart +++ b/lib/plugin/pl_player/widgets/play_pause_btn.dart @@ -68,7 +68,7 @@ class PlayOrPauseButtonState extends State height: 34, child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: player.playOrPause, color: Colors.white, diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0727ad68..0b77b7cc 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -39,7 +39,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { const BorderRadius.all(Radius.circular(20))), child: IconButton( style: ButtonStyle( - padding: WidgetStateProperty.all(EdgeInsets.zero), + padding: MaterialStateProperty.all(EdgeInsets.zero), ), onPressed: () => closeFn!(), icon: const Icon( diff --git a/pubspec.lock b/pubspec.lock index 5c6f2b43..c73ce395 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -762,10 +762,10 @@ packages: dependency: transitive description: name: intl - sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" url: "https://pub.flutter-io.cn" source: hosted - version: "0.19.0" + version: "0.18.1" io: dependency: transitive description: @@ -794,26 +794,26 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" url: "https://pub.flutter-io.cn" source: hosted - version: "10.0.4" + version: "10.0.0" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.3" + version: "2.0.1" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 url: "https://pub.flutter-io.cn" source: hosted - version: "3.0.1" + version: "2.0.1" lints: dependency: transitive description: @@ -973,10 +973,10 @@ packages: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 url: "https://pub.flutter-io.cn" source: hosted - version: "1.12.0" + version: "1.11.0" mime: dependency: transitive description: @@ -1467,10 +1467,10 @@ packages: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.flutter-io.cn" source: hosted - version: "0.7.0" + version: "0.6.1" timing: dependency: transitive description: @@ -1627,10 +1627,10 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 url: "https://pub.flutter-io.cn" source: hosted - version: "14.2.1" + version: "13.0.0" volume_controller: dependency: transitive description: @@ -1707,10 +1707,10 @@ packages: dependency: transitive description: name: webview_flutter_android - sha256: f42447ca49523f11d8f70abea55ea211b3cafe172dd7a0e7ac007bb35dd356dc + sha256: "0d21cfc3bfdd2e30ab2ebeced66512b91134b39e72e97b43db2d47dda1c4e53a" url: "https://pub.flutter-io.cn" source: hosted - version: "3.16.4" + version: "3.16.3" webview_flutter_platform_interface: dependency: transitive description: @@ -1768,5 +1768,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.4.0 <4.0.0" - flutter: ">=3.22.0" + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" From 5afb50cac990d41ea5f69001fa637e3a59dee38f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 13 Jun 2024 22:55:54 +0800 Subject: [PATCH 234/349] =?UTF-8?q?fix:=20=E5=BF=AB=E8=BF=9B=E5=BF=AB?= =?UTF-8?q?=E9=80=80=E8=A7=A6=E5=8F=91=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 4 ++-- lib/plugin/pl_player/widgets/backward_seek.dart | 4 ++-- lib/plugin/pl_player/widgets/forward_seek.dart | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index 34140be8..fa0dc444 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -940,7 +940,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekBackwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 200), + duration: const Duration(milliseconds: 150), builder: (BuildContext context, double value, Widget? child) => Opacity( @@ -983,7 +983,7 @@ class _PLVideoPlayerState extends State begin: 0.0, end: _hideSeekForwardButton.value ? 0.0 : 1.0, ), - duration: const Duration(milliseconds: 200), + duration: const Duration(milliseconds: 150), builder: (BuildContext context, double value, Widget? child) => Opacity( diff --git a/lib/plugin/pl_player/widgets/backward_seek.dart b/lib/plugin/pl_player/widgets/backward_seek.dart index 8fddf80a..8289d77c 100644 --- a/lib/plugin/pl_player/widgets/backward_seek.dart +++ b/lib/plugin/pl_player/widgets/backward_seek.dart @@ -30,14 +30,14 @@ class BackwardSeekIndicatorState extends State { @override void initState() { super.initState(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); } void increment() { timer?.cancel(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); widget.onChanged.call(value); diff --git a/lib/plugin/pl_player/widgets/forward_seek.dart b/lib/plugin/pl_player/widgets/forward_seek.dart index 7e3886ce..3f68fe0d 100644 --- a/lib/plugin/pl_player/widgets/forward_seek.dart +++ b/lib/plugin/pl_player/widgets/forward_seek.dart @@ -30,14 +30,14 @@ class ForwardSeekIndicatorState extends State { @override void initState() { super.initState(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); } void increment() { timer?.cancel(); - timer = Timer(const Duration(milliseconds: 400), () { + timer = Timer(const Duration(milliseconds: 200), () { widget.onSubmitted.call(value); }); widget.onChanged.call(value); From 28737543174976d65a0a91e560c21bbc870367f7 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 13 Jun 2024 23:13:47 +0800 Subject: [PATCH 235/349] =?UTF-8?q?feat:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E5=85=A8=E5=B1=8F=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 7 +++++++ lib/plugin/pl_player/view.dart | 7 ++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 22271c2b..ef067d1b 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -514,6 +514,13 @@ class _VideoDetailPageState extends State showEposideCb: () => vdCtr.videoType == SearchType.video ? videoIntroController.showEposideHandler() : bangumiIntroController.showEposideHandler(), + fullScreenCb: (bool status) { + if (status) { + videoHeight.value = Get.size.height; + } else { + videoHeight.value = defaultVideoHeight; + } + }, ), ); }, diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index fa0dc444..f3e0946b 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -39,6 +39,7 @@ class PLVideoPlayer extends StatefulWidget { this.customWidget, this.customWidgets, this.showEposideCb, + this.fullScreenCb, super.key, }); @@ -52,6 +53,7 @@ class PLVideoPlayer extends StatefulWidget { final Widget? customWidget; final List? customWidgets; final Function? showEposideCb; + final Function? fullScreenCb; @override State createState() => _PLVideoPlayerState(); @@ -335,7 +337,10 @@ class _PLVideoPlayerState extends State color: Colors.white, ), ), - fuc: () => _.triggerFullScreen(status: !_.isFullScreen.value), + fuc: () { + _.triggerFullScreen(status: !_.isFullScreen.value); + widget.fullScreenCb?.call(!_.isFullScreen.value); + }, ), }; final List list = []; From 5e63f0da7b93eb3e2d2b27ed0ba8b52d24518f50 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 15 Jun 2024 16:03:45 +0800 Subject: [PATCH 236/349] =?UTF-8?q?feat:=20=E7=A7=81=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/msg.dart | 66 ++-- lib/pages/member/widgets/profile.dart | 12 +- lib/pages/whisper/controller.dart | 9 + lib/pages/whisper/view.dart | 23 +- lib/pages/whisper_detail/controller.dart | 37 +- lib/pages/whisper_detail/view.dart | 342 ++++++++++-------- .../whisper_detail/widget/chat_item.dart | 162 +++++---- 7 files changed, 373 insertions(+), 278 deletions(-) diff --git a/lib/http/msg.dart b/lib/http/msg.dart index d1d31958..7ac30bc1 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,4 +1,6 @@ +import 'dart:convert'; import 'dart:math'; +import 'package:dio/dio.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; import '../utils/wbi_sign.dart'; @@ -134,56 +136,42 @@ class MsgHttp { // 发送私信 static Future sendMsg({ - int? senderUid, - int? receiverId, + required int senderUid, + required int receiverId, int? receiverType, int? msgType, dynamic content, }) async { String csrf = await Request.getCsrf(); - Map params = await WbiSign().makSign({ - 'msg[sender_uid]': senderUid, - 'msg[receiver_id]': receiverId, - 'msg[receiver_type]': receiverType ?? 1, - 'msg[msg_type]': msgType ?? 1, - 'msg[msg_status]': 0, - 'msg[dev_id]': getDevId(), - 'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000, - 'msg[new_face_version]': 0, - 'msg[content]': content, - 'from_firework': 0, - 'build': 0, - 'mobi_app': 'web', - 'csrf_token': csrf, - 'csrf': csrf, - }); - var res = - await Request().post(Api.sendMsg, queryParameters: { - ...params, - 'csrf_token': csrf, - 'csrf': csrf, - }, data: { - 'w_sender_uid': params['msg[sender_uid]'], - 'w_receiver_id': params['msg[receiver_id]'], - 'w_dev_id': params['msg[dev_id]'], - 'w_rid': params['w_rid'], - 'wts': params['wts'], - 'csrf_token': csrf, - 'csrf': csrf, - }); + var res = await Request().post( + Api.sendMsg, + data: { + 'msg[sender_uid]': senderUid, + 'msg[receiver_id]': receiverId, + 'msg[receiver_type]': 1, + 'msg[msg_type]': 1, + 'msg[msg_status]': 0, + 'msg[content]': jsonEncode(content), + 'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000, + 'msg[new_face_version]': 0, + 'msg[dev_id]': getDevId(), + 'from_firework': 0, + 'build': 0, + 'mobi_app': 'web', + 'csrf_token': csrf, + 'csrf': csrf, + }, + options: Options( + contentType: Headers.formUrlEncodedContentType, + ), + ); if (res.data['code'] == 0) { return { 'status': true, 'data': res.data['data'], }; } else { - return { - 'status': false, - 'date': [], - 'msg': "message: ${res.data['message']}," - " msg: ${res.data['msg']}," - " code: ${res.data['code']}", - }; + return {'status': false, 'date': [], 'msg': res.data['message']}; } } diff --git a/lib/pages/member/widgets/profile.dart b/lib/pages/member/widgets/profile.dart index a708a35e..8c6385db 100644 --- a/lib/pages/member/widgets/profile.dart +++ b/lib/pages/member/widgets/profile.dart @@ -208,7 +208,17 @@ class ProfilePanel extends StatelessWidget { const SizedBox(width: 8), Expanded( child: TextButton( - onPressed: () {}, + onPressed: () { + Get.toNamed( + '/whisperDetail', + parameters: { + 'name': memberInfo.name!, + 'face': memberInfo.face!, + 'mid': memberInfo.mid.toString(), + 'heroTag': ctr.heroTag!, + }, + ); + }, style: TextButton.styleFrom( backgroundColor: Theme.of(context) .colorScheme diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index c82cab5b..b7c52d2e 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -62,4 +62,13 @@ class WhisperController extends GetxController { Future onRefresh() async { querySessionList('onRefresh'); } + + void refreshLastMsg(int talkerId, String content) { + final SessionList currentItem = + sessionList.where((p0) => p0.talkerId == talkerId).first; + currentItem.lastMsg!.content['content'] = content; + sessionList.removeWhere((p0) => p0.talkerId == talkerId); + sessionList.insert(0, currentItem); + sessionList.refresh(); + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index fa95463b..6d2f281b 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -202,6 +202,7 @@ class SessionItem extends StatelessWidget { @override Widget build(BuildContext context) { + final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo.mid); final content = sessionItem.lastMsg.content; return ListTile( onTap: () { @@ -214,6 +215,7 @@ class SessionItem extends StatelessWidget { 'name': sessionItem.accountInfo.name, 'face': sessionItem.accountInfo.face, 'mid': sessionItem.accountInfo.mid.toString(), + 'heroTag': heroTag, }, ); }, @@ -221,11 +223,14 @@ class SessionItem extends StatelessWidget { isLabelVisible: sessionItem.unreadCount > 0, label: Text(sessionItem.unreadCount.toString()), alignment: Alignment.topRight, - child: NetworkImgLayer( - width: 45, - height: 45, - type: 'avatar', - src: sessionItem.accountInfo.face, + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + width: 45, + height: 45, + type: 'avatar', + src: sessionItem.accountInfo.face, + ), ), ), title: Text(sessionItem.accountInfo.name), @@ -245,10 +250,10 @@ class SessionItem extends StatelessWidget { .copyWith(color: Theme.of(context).colorScheme.outline)), trailing: Text( Utils.dateFormat(sessionItem.lastMsg.timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith(color: Theme.of(context).colorScheme.outline), + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelSmall!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), ), ); } diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 6e950f81..3c7e0837 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -1,30 +1,39 @@ +import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/session.dart'; +import 'package:pilipala/pages/whisper/index.dart'; import '../../utils/feed_back.dart'; import '../../utils/storage.dart'; class WhisperDetailController extends GetxController { - late int talkerId; + int? talkerId; late String name; late String face; late String mid; + late String heroTag; RxList messageList = [].obs; //表情转换图片规则 - List? eInfos; + RxList eInfos = [].obs; final TextEditingController replyContentController = TextEditingController(); Box userInfoCache = GStrorage.userInfo; + List emoteList = []; @override void onInit() { super.onInit(); - talkerId = int.parse(Get.parameters['talkerId']!); + if (Get.parameters.containsKey('talkerId')) { + talkerId = int.parse(Get.parameters['talkerId']!); + } else { + talkerId = int.parse(Get.parameters['mid']!); + } name = Get.parameters['name']!; face = Get.parameters['face']!; mid = Get.parameters['mid']!; + heroTag = Get.parameters['heroTag']!; } Future querySessionMsg() async { @@ -34,7 +43,7 @@ class WhisperDetailController extends GetxController { if (messageList.isNotEmpty) { ackSessionMsg(); if (res['data'].eInfos != null) { - eInfos = res['data'].eInfos; + eInfos.value = res['data'].eInfos; } } } else { @@ -73,7 +82,25 @@ class WhisperDetailController extends GetxController { msgType: 1, ); if (result['status']) { - SmartDialog.showToast('发送成功'); + String content = jsonDecode(result['data']['msg_content'])['content']; + messageList.insert( + 0, + MessageItem( + msgSeqno: result['data']['msg_key'], + senderUid: userInfo.mid, + receiverId: int.parse(mid), + content: {'content': content}, + msgType: 1, + timestamp: DateTime.now().millisecondsSinceEpoch, + ), + ); + eInfos.addAll(emoteList); + replyContentController.clear(); + try { + late final WhisperController whisperController = + Get.find(); + whisperController.refreshLastMsg(talkerId!, message); + } catch (_) {} } else { SmartDialog.showToast(result['msg']); } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 1701be33..7c5762d9 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -1,9 +1,12 @@ import 'dart:async'; +import 'dart:math'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/video/reply/emote.dart'; import 'package:pilipala/pages/emote/index.dart'; +import 'package:pilipala/pages/video/detail/reply_new/toolbar_icon_button.dart'; import 'package:pilipala/pages/whisper_detail/controller.dart'; import 'package:pilipala/utils/feed_back.dart'; import '../../utils/storage.dart'; @@ -24,9 +27,9 @@ class _WhisperDetailPageState extends State late TextEditingController _replyContentController; final FocusNode replyContentFocusNode = FocusNode(); final _debouncer = Debouncer(milliseconds: 200); // 设置延迟时间 - late double emoteHeight = 0.0; + late double emoteHeight = 230.0; double keyboardHeight = 0.0; // 键盘高度 - String toolbarType = 'input'; + RxString toolbarType = ''.obs; Box userInfoCache = GStrorage.userInfo; @override @@ -41,9 +44,7 @@ class _WhisperDetailPageState extends State _focuslistener() { replyContentFocusNode.addListener(() { if (replyContentFocusNode.hasFocus) { - setState(() { - toolbarType = 'input'; - }); + toolbarType.value = 'input'; } }); } @@ -52,7 +53,7 @@ class _WhisperDetailPageState extends State void didChangeMetrics() { super.didChangeMetrics(); final String routePath = Get.currentRoute; - if (mounted && routePath.startsWith('/whisper_detail')) { + if (mounted && routePath.startsWith('/whisperDetail')) { WidgetsBinding.instance.addPostFrameCallback((_) { // 键盘高度 final viewInsets = EdgeInsets.fromViewPadding( @@ -61,8 +62,11 @@ class _WhisperDetailPageState extends State if (mounted) { if (keyboardHeight == 0) { setState(() { - emoteHeight = keyboardHeight = + keyboardHeight = keyboardHeight == 0.0 ? viewInsets.bottom : keyboardHeight; + if (keyboardHeight != 0) { + emoteHeight = keyboardHeight; + } }); } } @@ -79,6 +83,23 @@ class _WhisperDetailPageState extends State super.dispose(); } + void onChooseEmote(PackageItem package, Emote emote) { + _whisperDetailController.emoteList.add( + {'text': emote.text, 'url': emote.url}, + ); + final int cursorPosition = + max(_replyContentController.selection.baseOffset, 0); + final String currentText = _replyContentController.text; + final String newText = currentText.substring(0, cursorPosition) + + emote.text! + + currentText.substring(cursorPosition); + _replyContentController.value = TextEditingValue( + text: newText, + selection: + TextSelection.collapsed(offset: cursorPosition + emote.text!.length), + ); + } + @override Widget build(BuildContext context) { return Scaffold( @@ -88,30 +109,20 @@ class _WhisperDetailPageState extends State width: double.infinity, height: 50, child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ SizedBox( width: 34, height: 34, child: IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - backgroundColor: MaterialStateProperty.resolveWith( - (Set states) { - return Theme.of(context) - .colorScheme - .primaryContainer - .withOpacity(0.6); - }), - ), onPressed: () => Get.back(), icon: Icon( - Icons.arrow_back_outlined, + Icons.arrow_back_ios, size: 18, color: Theme.of(context).colorScheme.onPrimaryContainer, ), ), ), + const SizedBox(width: 10), GestureDetector( onTap: () { feedBack(); @@ -125,11 +136,14 @@ class _WhisperDetailPageState extends State }, child: Row( children: [ - NetworkImgLayer( - width: 34, - height: 34, - type: 'avatar', - src: _whisperDetailController.face, + Hero( + tag: _whisperDetailController.heroTag, + child: NetworkImgLayer( + width: 34, + height: 34, + type: 'avatar', + src: _whisperDetailController.face, + ), ), const SizedBox(width: 6), Text( @@ -143,155 +157,169 @@ class _WhisperDetailPageState extends State ], ), ), - ), - body: GestureDetector( - onTap: () { - FocusScope.of(context).unfocus(); - setState(() { - keyboardHeight = 0; - }); - }, - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - final Map data = snapshot.data as Map; - if (data['status']) { - List messageList = _whisperDetailController.messageList; - return Obx( - () => messageList.isEmpty - ? const SizedBox() - : ListView.builder( - itemCount: messageList.length, - shrinkWrap: true, - reverse: true, - itemBuilder: (_, int i) { - if (i == 0) { - return Column( - children: [ - ChatItem( - item: messageList[i], - e_infos: _whisperDetailController.eInfos), - const SizedBox(height: 12), - ], - ); - } else { - return ChatItem( - item: messageList[i], - e_infos: _whisperDetailController.eInfos); - } - }, - ), - ); - } else { - // 请求错误 - return const SizedBox(); - } - } else { - // 骨架屏 - return const SizedBox(); - } - }, - ), - ), - // resizeToAvoidBottomInset: true, - bottomNavigationBar: Container( - width: double.infinity, - height: MediaQuery.of(context).padding.bottom + 70 + keyboardHeight, - padding: EdgeInsets.only( - left: 8, - right: 12, - top: 12, - bottom: MediaQuery.of(context).padding.bottom, - ), - decoration: BoxDecoration( - border: Border( - top: BorderSide( - width: 4, - color: Theme.of(context).colorScheme.primary.withOpacity(0.1), + actions: [ + IconButton( + onPressed: () {}, + icon: const Icon( + Icons.more_vert_outlined, + size: 20, ), ), - ), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // IconButton( - // onPressed: () {}, - // icon: Icon( - // Icons.add_circle_outline, - // color: Theme.of(context).colorScheme.outline, - // ), - // ), - IconButton( - onPressed: () { - // if (toolbarType == 'input') { - // setState(() { - // toolbarType = 'emote'; - // }); - // } - // FocusScope.of(context).unfocus(); - }, - icon: Icon( - Icons.emoji_emotions_outlined, - color: Theme.of(context).colorScheme.outline, + const SizedBox(width: 14) + ], + ), + body: Column( + children: [ + Expanded( + child: GestureDetector( + onTap: () { + FocusScope.of(context).unfocus(); + toolbarType.value = ''; + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + final Map data = snapshot.data as Map; + if (data['status']) { + List messageList = _whisperDetailController.messageList; + return Obx( + () => messageList.isEmpty + ? const SizedBox() + : Align( + alignment: Alignment.topCenter, + child: ListView.builder( + itemCount: messageList.length, + shrinkWrap: true, + reverse: true, + itemBuilder: (_, int i) { + if (i == 0) { + return Column( + children: [ + ChatItem( + item: messageList[i], + e_infos: _whisperDetailController + .eInfos), + const SizedBox(height: 20), + ], + ); + } else { + return ChatItem( + item: messageList[i], + e_infos: + _whisperDetailController.eInfos); + } + }, + ), + ), + ); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + // 骨架屏 + return const SizedBox(); + } + }, + ), + ), + ), + Obx( + () => Container( + padding: EdgeInsets.fromLTRB( + 8, + 12, + 12, + toolbarType.value == '' + ? MediaQuery.of(context).padding.bottom + 6 + : 6, + ), + decoration: BoxDecoration( + border: Border( + top: BorderSide( + width: 1, + color: Colors.grey.withOpacity(0.15), ), ), - Expanded( - child: Container( - height: 45, - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .primary - .withOpacity(0.08), - borderRadius: BorderRadius.circular(40.0), - ), - child: TextField( - readOnly: true, - style: Theme.of(context).textTheme.titleMedium, - controller: _replyContentController, - autofocus: false, - focusNode: replyContentFocusNode, - decoration: const InputDecoration( - border: InputBorder.none, // 移除默认边框 - hintText: '开发中 ...', // 提示文本 - contentPadding: EdgeInsets.symmetric( - horizontal: 16.0, vertical: 12.0), // 内边距 + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ToolbarIconButton( + onPressed: () { + if (toolbarType.value == '') { + toolbarType.value = 'emote'; + } else if (toolbarType.value == 'input') { + FocusScope.of(context).unfocus(); + toolbarType.value = 'emote'; + } else if (toolbarType.value == 'emote') { + FocusScope.of(context).requestFocus(); + } + }, + icon: const Icon(Icons.emoji_emotions_outlined, size: 22), + toolbarType: toolbarType.value, + selected: false, + ), + const SizedBox(width: 4), + Expanded( + child: Container( + height: 45, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .outline + .withOpacity(0.05), + borderRadius: BorderRadius.circular(40.0), + ), + child: TextField( + style: Theme.of(context).textTheme.titleMedium, + controller: _replyContentController, + autofocus: false, + focusNode: replyContentFocusNode, + decoration: const InputDecoration( + border: InputBorder.none, // 移除默认边框 + hintText: '文明发言 ~', // 提示文本 + contentPadding: EdgeInsets.symmetric( + horizontal: 16.0, vertical: 12.0), // 内边距 + ), ), ), ), - ), - IconButton( - // onPressed: _whisperDetailController.sendMsg, - onPressed: null, - icon: Icon( - Icons.send, - color: Theme.of(context).colorScheme.outline, + IconButton( + onPressed: _whisperDetailController.sendMsg, + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.outline, + ), ), - ), - // const SizedBox(width: 16), - ], + ], + ), ), - AnimatedSize( - curve: Curves.easeInOut, - duration: const Duration(milliseconds: 300), + ), + Obx( + () => AnimatedSize( + curve: Curves.linear, + duration: const Duration(milliseconds: 200), child: SizedBox( width: double.infinity, - height: toolbarType == 'input' ? keyboardHeight : emoteHeight, + height: toolbarType.value == 'input' + ? keyboardHeight + : toolbarType.value == 'emote' + ? emoteHeight + : 0, child: EmotePanel( - onChoose: (package, emote) => {}, + onChoose: (package, emote) => onChooseEmote(package, emote), ), ), ), - ], - ), + ), + ], ), + resizeToAvoidBottomInset: false, ); } } diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 0d37e8b3..c0d87221 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -1,7 +1,6 @@ // ignore_for_file: must_be_immutable import 'dart:convert'; - import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -69,9 +68,13 @@ class ChatItem extends StatelessWidget { Color textColor(BuildContext context) { return isOwner ? Theme.of(context).colorScheme.onPrimary - : Theme.of(context).colorScheme.onSecondaryContainer; + : Theme.of(context).colorScheme.onBackground; } + const double safeDistanceval = 6; + const double borderRadiusVal = 12; + const double paddingVal = 10; + Widget richTextMessage(BuildContext context) { var text = content['content']; if (e_infos != null) { @@ -386,73 +389,98 @@ class ChatItem extends StatelessWidget { ? messageContent(context) : isRevoke ? const SizedBox() - : Row( - children: [ - if (!isOwner) const SizedBox(width: 12), - if (isOwner) const Spacer(), - Container( - constraints: const BoxConstraints( - maxWidth: 300.0, // 设置最大宽度为200.0 - ), - decoration: BoxDecoration( - color: isOwner - ? Theme.of(context).colorScheme.primary - : Theme.of(context).colorScheme.secondaryContainer, - borderRadius: BorderRadius.only( - topLeft: const Radius.circular(16), - topRight: const Radius.circular(16), - bottomLeft: Radius.circular(isOwner ? 16 : 6), - bottomRight: Radius.circular(isOwner ? 6 : 16), + : Padding( + padding: const EdgeInsets.only(top: 12), + child: Row( + mainAxisAlignment: !isOwner + ? MainAxisAlignment.start + : MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const SizedBox(width: safeDistanceval), + if (isOwner) + Text( + Utils.dateFormat(item.timestamp), + style: Theme.of(context).textTheme.labelSmall!.copyWith( + color: Theme.of(context).colorScheme.outline), ), + Container( + constraints: const BoxConstraints( + maxWidth: 300.0, // 设置最大宽度为200.0 + ), + decoration: BoxDecoration( + color: isOwner + ? Theme.of(context) + .colorScheme + .primary + .withAlpha(180) + : Theme.of(context) + .colorScheme + .outlineVariant + .withOpacity(0.6) + .withAlpha(125), + borderRadius: BorderRadius.only( + topLeft: const Radius.circular(borderRadiusVal), + topRight: const Radius.circular(borderRadiusVal), + bottomLeft: + Radius.circular(isOwner ? borderRadiusVal : 2), + bottomRight: + Radius.circular(isOwner ? 2 : borderRadiusVal), + ), + ), + margin: const EdgeInsets.only( + left: 8, + right: 8, + ), + padding: const EdgeInsets.all(paddingVal), + child: messageContent(context), + // child: Column( + // crossAxisAlignment: isOwner + // ? CrossAxisAlignment.end + // : CrossAxisAlignment.start, + // children: [ + // messageContent(context), + // SizedBox(height: isPic ? 7 : 2), + // Row( + // mainAxisSize: MainAxisSize.min, + // children: [ + // Text( + // Utils.dateFormat(item.timestamp), + // style: Theme.of(context) + // .textTheme + // .labelSmall! + // .copyWith( + // color: isOwner + // ? Theme.of(context) + // .colorScheme + // .onPrimary + // .withOpacity(0.8) + // : Theme.of(context) + // .colorScheme + // .onSecondaryContainer + // .withOpacity(0.8)), + // ), + // item.msgStatus == 1 + // ? Text( + // ' 已撤回', + // style: + // Theme.of(context).textTheme.labelSmall!, + // ) + // : const SizedBox() + // ], + // ) + // ], + // ), ), - margin: const EdgeInsets.only(top: 12), - padding: EdgeInsets.only( - top: 8, - bottom: 6, - left: isPic ? 8 : 12, - right: isPic ? 8 : 12, - ), - child: Column( - crossAxisAlignment: isOwner - ? CrossAxisAlignment.end - : CrossAxisAlignment.start, - children: [ - messageContent(context), - SizedBox(height: isPic ? 7 : 2), - Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - Utils.dateFormat(item.timestamp), - style: Theme.of(context) - .textTheme - .labelSmall! - .copyWith( - color: isOwner - ? Theme.of(context) - .colorScheme - .onPrimary - .withOpacity(0.8) - : Theme.of(context) - .colorScheme - .onSecondaryContainer - .withOpacity(0.8)), - ), - item.msgStatus == 1 - ? Text( - ' 已撤回', - style: - Theme.of(context).textTheme.labelSmall!, - ) - : const SizedBox() - ], - ) - ], - ), - ), - if (!isOwner) const Spacer(), - if (isOwner) const SizedBox(width: 12), - ], + if (!isOwner) + Text( + Utils.dateFormat(item.timestamp), + style: Theme.of(context).textTheme.labelSmall!.copyWith( + color: Theme.of(context).colorScheme.outline), + ), + const SizedBox(width: safeDistanceval), + ], + ), ); } } From 46cef5e55b41adc0c9448255afc79051c3c44622 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 15 Jun 2024 17:03:52 +0800 Subject: [PATCH 237/349] =?UTF-8?q?feat:=20=E4=BC=9A=E8=AF=9D=E7=A7=BB?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 ++ lib/http/msg.dart | 31 +++++++++++---- lib/pages/whisper/controller.dart | 6 +++ lib/pages/whisper/view.dart | 18 +++++---- lib/pages/whisper_detail/controller.dart | 38 +++++++++++++++++++ lib/pages/whisper_detail/view.dart | 16 ++++---- .../whisper_detail/widget/chat_item.dart | 15 +++++++- 7 files changed, 105 insertions(+), 23 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index e519d91c..aaa804fa 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -535,4 +535,8 @@ class Api { /// 搜索结果计数 static const String searchCount = '/x/web-interface/wbi/search/all/v2'; + + /// 关闭会话 + static const String removeSession = + '${HttpString.tUrl}/session_svr/v1/session_svr/remove_session'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 7ac30bc1..9cdc9160 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -124,13 +124,7 @@ class MsgHttp { 'data': res.data['data'], }; } else { - return { - 'status': false, - 'date': [], - 'msg': "message: ${res.data['message']}," - " msg: ${res.data['msg']}," - " code: ${res.data['code']}", - }; + return {'status': false, 'date': [], 'msg': res.data['message']}; } } @@ -208,4 +202,27 @@ class MsgHttp { } return s.join(); } + + static Future removeSession({ + int? talkerId, + }) async { + String csrf = await Request.getCsrf(); + Map params = await WbiSign().makSign({ + 'talker_id': talkerId, + 'session_type': 1, + 'build': 0, + 'mobi_app': 'web', + 'csrf_token': csrf, + 'csrf': csrf + }); + var res = await Request().get(Api.removeSession, data: params); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index b7c52d2e..195b238b 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -71,4 +71,10 @@ class WhisperController extends GetxController { sessionList.insert(0, currentItem); sessionList.refresh(); } + + // 移除会话 + void removeSessionMsg(int talkerId) { + sessionList.removeWhere((p0) => p0.talkerId == talkerId); + sessionList.refresh(); + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 6d2f281b..bbe17048 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -204,6 +204,8 @@ class SessionItem extends StatelessWidget { Widget build(BuildContext context) { final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo.mid); final content = sessionItem.lastMsg.content; + final msgStatus = sessionItem.lastMsg.msgStatus; + return ListTile( onTap: () { sessionItem.unreadCount = 0; @@ -235,13 +237,15 @@ class SessionItem extends StatelessWidget { ), title: Text(sessionItem.accountInfo.name), subtitle: Text( - content != null && content != '' - ? (content['text'] ?? - content['content'] ?? - content['title'] ?? - content['reply_content'] ?? - '不支持的消息类型') - : '不支持的消息类型', + msgStatus == 1 + ? '你撤回了一条消息' + : content != null && content != '' + ? (content['text'] ?? + content['content'] ?? + content['title'] ?? + content['reply_content'] ?? + '不支持的消息类型') + : '不支持的消息类型', maxLines: 1, overflow: TextOverflow.ellipsis, style: Theme.of(context) diff --git a/lib/pages/whisper_detail/controller.dart b/lib/pages/whisper_detail/controller.dart index 3c7e0837..32e0ceb0 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -105,4 +106,41 @@ class WhisperDetailController extends GetxController { SmartDialog.showToast(result['msg']); } } + + void removeSession(context) { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + clipBehavior: Clip.hardEdge, + title: const Text('提示'), + content: const Text('确认清空会话内容并移除会话?'), + actions: [ + TextButton( + onPressed: Get.back, + child: Text( + '取消', + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + ), + TextButton( + onPressed: () async { + var res = await MsgHttp.removeSession(talkerId: talkerId); + if (res['status']) { + SmartDialog.showToast('操作成功'); + try { + late final WhisperController whisperController = + Get.find(); + whisperController.removeSessionMsg(talkerId!); + Get.back(); + } catch (_) {} + } + }, + child: const Text('确认'), + ), + ], + ); + }, + ); + } } diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 7c5762d9..912b5dc5 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -145,7 +145,7 @@ class _WhisperDetailPageState extends State src: _whisperDetailController.face, ), ), - const SizedBox(width: 6), + const SizedBox(width: 10), Text( _whisperDetailController.name, style: Theme.of(context).textTheme.titleMedium, @@ -158,12 +158,14 @@ class _WhisperDetailPageState extends State ), ), actions: [ - IconButton( - onPressed: () {}, - icon: const Icon( - Icons.more_vert_outlined, - size: 20, - ), + PopupMenuButton( + icon: const Icon(Icons.more_vert_outlined, size: 20), + itemBuilder: (BuildContext context) => [ + PopupMenuItem( + onTap: () => _whisperDetailController.removeSession(context), + child: const Text('关闭会话'), + ) + ], ), const SizedBox(width: 14) ], diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index c0d87221..f64cf223 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -389,8 +389,19 @@ class ChatItem extends StatelessWidget { ? messageContent(context) : isRevoke ? const SizedBox() - : Padding( - padding: const EdgeInsets.only(top: 12), + : Container( + padding: const EdgeInsets.only(top: 6, bottom: 6), + decoration: BoxDecoration( + border: Border( + left: item.msgStatus == 1 && !isOwner + ? BorderSide( + width: 4, color: Theme.of(context).dividerColor) + : BorderSide.none, + right: item.msgStatus == 1 && isOwner + ? BorderSide( + width: 4, color: Theme.of(context).primaryColor) + : BorderSide.none, + )), child: Row( mainAxisAlignment: !isOwner ? MainAxisAlignment.start From bfdd996b0833f13ad7b4de6b1ee11850ba8e4fbd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 15 Jun 2024 20:34:28 +0800 Subject: [PATCH 238/349] =?UTF-8?q?feat:=20=E6=9C=AA=E8=AF=BB=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E8=AE=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 12 ++ lib/pages/whisper/controller.dart | 45 +++++ lib/pages/whisper/view.dart | 269 +++++++++++++++--------------- 4 files changed, 194 insertions(+), 135 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index aaa804fa..198d6174 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -539,4 +539,7 @@ class Api { /// 关闭会话 static const String removeSession = '${HttpString.tUrl}/session_svr/v1/session_svr/remove_session'; + + /// 消息未读数 + static const String unread = '${HttpString.tUrl}/x/im/web/msgfeed/unread'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 9cdc9160..9a6e878b 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -225,4 +225,16 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + static Future unread() async { + var res = await Request().get(Api.unread); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index 195b238b..e00c990e 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/account.dart'; @@ -7,6 +8,38 @@ class WhisperController extends GetxController { RxList sessionList = [].obs; RxList accountList = [].obs; bool isLoading = false; + RxList noticesList = [ + { + 'icon': Icons.message_outlined, + 'title': '回复我的', + 'path': '', + 'count': 0, + }, + { + 'icon': Icons.alternate_email, + 'title': '@ 我的', + 'path': '', + 'count': 0, + }, + { + 'icon': Icons.thumb_up_outlined, + 'title': '收到的赞', + 'path': '', + 'count': 0, + }, + { + 'icon': Icons.notifications_none_outlined, + 'title': '系统通知', + 'path': '', + 'count': 0, + } + ].obs; + + @override + void onInit() { + unread(); + super.onInit(); + } Future querySessionList(String? type) async { if (isLoading) return; @@ -77,4 +110,16 @@ class WhisperController extends GetxController { sessionList.removeWhere((p0) => p0.talkerId == talkerId); sessionList.refresh(); } + + // 消息未读数 + void unread() async { + var res = await MsgHttp.unread(); + if (res['status']) { + noticesList[0]['count'] = res['data']['reply']; + noticesList[1]['count'] = res['data']['at']; + noticesList[2]['count'] = res['data']['like']; + noticesList[3]['count'] = res['data']['sys_msg']; + noticesList.refresh(); + } + } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index bbe17048..8070498e 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -1,6 +1,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/skeleton.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/utils.dart'; @@ -44,147 +45,145 @@ class _WhisperPageState extends State { appBar: AppBar( title: const Text('消息'), ), - body: Column( - children: [ - // LayoutBuilder( - // builder: (BuildContext context, BoxConstraints constraints) { - // // 在这里根据父级容器的约束条件构建小部件树 - // return Padding( - // padding: const EdgeInsets.only(left: 20, right: 20), - // child: SizedBox( - // height: constraints.maxWidth / 5, - // child: GridView.count( - // primary: false, - // crossAxisCount: 4, - // padding: const EdgeInsets.all(0), - // childAspectRatio: 1.25, - // children: [ - // Column( - // crossAxisAlignment: CrossAxisAlignment.center, - // mainAxisAlignment: MainAxisAlignment.center, - // children: [ - // SizedBox( - // width: 36, - // height: 36, - // child: IconButton( - // style: ButtonStyle( - // padding: - // MaterialStateProperty.all(EdgeInsets.zero), - // backgroundColor: - // MaterialStateProperty.resolveWith((states) { - // return Theme.of(context) - // .colorScheme - // .primary - // .withOpacity(0.1); - // }), - // ), - // onPressed: () {}, - // icon: Icon( - // Icons.message_outlined, - // size: 18, - // color: Theme.of(context).colorScheme.primary, - // ), - // ), - // ), - // const SizedBox(height: 6), - // const Text('回复我的', style: TextStyle(fontSize: 13)) - // ], - // ), - // ], - // ), - // ), - // ); - // }, - // ), - Expanded( - child: RefreshIndicator( - onRefresh: () async { - await _whisperController.onRefresh(); - }, - child: SingleChildScrollView( - controller: _scrollController, - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - RxList sessionList = _whisperController.sessionList; - return Obx( - () => sessionList.isEmpty - ? const SizedBox() - : ListView.separated( - itemCount: sessionList.length, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (_, int i) { - return SessionItem( - sessionItem: sessionList[i], - changeFucCall: () => - sessionList.refresh(), - ); - }, - separatorBuilder: - (BuildContext context, int index) { - return Divider( - indent: 72, - endIndent: 20, - height: 6, - color: Colors.grey.withOpacity(0.1), - ); - }, + body: RefreshIndicator( + onRefresh: () async { + _whisperController.unread(); + await _whisperController.onRefresh(); + }, + child: SingleChildScrollView( + controller: _scrollController, + child: Column( + children: [ + LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + // 在这里根据父级容器的约束条件构建小部件树 + return Padding( + padding: const EdgeInsets.only(left: 20, right: 20), + child: SizedBox( + height: constraints.maxWidth / 4, + child: Obx( + () => GridView.count( + primary: false, + crossAxisCount: 4, + padding: const EdgeInsets.all(0), + children: [ + ..._whisperController.noticesList.map((element) { + return InkWell( + onTap: () => {}, + onLongPress: () {}, + borderRadius: StyleString.mdRadius, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Badge( + isLabelVisible: element['count'] > 0, + label: Text(element['count'] > 99 + ? '99+' + : element['count'].toString()), + child: Padding( + padding: const EdgeInsets.all(10), + child: Icon( + element['icon'], + size: 21, + color: Theme.of(context) + .colorScheme + .primary, + ), + ), + ), + const SizedBox(height: 4), + Text(element['title']) + ], ), - ); - } else { - // 请求错误 - return Center( - child: Text(data?['msg'] ?? '请求异常'), - ); - } + ); + }).toList(), + ], + ), + ), + ), + ); + }, + ), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + RxList sessionList = _whisperController.sessionList; + return Obx( + () => sessionList.isEmpty + ? const SizedBox() + : ListView.separated( + itemCount: sessionList.length, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (_, int i) { + return SessionItem( + sessionItem: sessionList[i], + changeFucCall: () => sessionList.refresh(), + ); + }, + separatorBuilder: + (BuildContext context, int index) { + return Divider( + indent: 72, + endIndent: 20, + height: 6, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); } else { - // 骨架屏 - return ListView.builder( - itemCount: 15, - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - itemBuilder: (context, int i) { - return Skeleton( - child: ListTile( - leading: Container( - width: 45, - height: 45, - decoration: BoxDecoration( - color: Theme.of(context) - .colorScheme - .onInverseSurface, - borderRadius: BorderRadius.circular(25), - ), - ), - title: Container( - width: 100, - height: 14, - color: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - subtitle: Container( - width: 80, - height: 14, - color: Theme.of(context) - .colorScheme - .onInverseSurface, - ), - ), - ); - }, + // 请求错误 + return Center( + child: Text(data?['msg'] ?? '请求异常'), ); } - }, - ), + } else { + // 骨架屏 + return ListView.builder( + itemCount: 15, + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemBuilder: (context, int i) { + return Skeleton( + child: ListTile( + leading: Container( + width: 45, + height: 45, + decoration: BoxDecoration( + color: Theme.of(context) + .colorScheme + .onInverseSurface, + borderRadius: BorderRadius.circular(25), + ), + ), + title: Container( + width: 100, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + subtitle: Container( + width: 80, + height: 14, + color: Theme.of(context) + .colorScheme + .onInverseSurface, + ), + ), + ); + }, + ); + } + }, ), - ), + ], ), - ], + ), ), ); } From 569f7c23fd2e64072fe905dbd0d098b1ebc66ec9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 13:58:43 +0800 Subject: [PATCH 239/349] =?UTF-8?q?feat:=20=E5=9B=9E=E5=A4=8D=E6=88=91?= =?UTF-8?q?=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 26 +++ lib/models/msg/reply.dart | 168 ++++++++++++++ lib/pages/message/reply/controller.dart | 25 +++ lib/pages/message/reply/index.dart | 4 + lib/pages/message/reply/view.dart | 285 ++++++++++++++++++++++++ lib/pages/whisper/controller.dart | 2 +- lib/pages/whisper/view.dart | 2 +- lib/router/app_pages.dart | 3 + 9 files changed, 516 insertions(+), 2 deletions(-) create mode 100644 lib/models/msg/reply.dart create mode 100644 lib/pages/message/reply/controller.dart create mode 100644 lib/pages/message/reply/index.dart create mode 100644 lib/pages/message/reply/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 198d6174..4db5994d 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -542,4 +542,7 @@ class Api { /// 消息未读数 static const String unread = '${HttpString.tUrl}/x/im/web/msgfeed/unread'; + + /// 回复我的 + static const String messageReplyAPi = '/x/msgfeed/reply'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 9a6e878b..20905386 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:math'; import 'package:dio/dio.dart'; +import 'package:pilipala/models/msg/reply.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; import '../utils/wbi_sign.dart'; @@ -237,4 +238,29 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + // 回复我的 + static Future messageReply({ + int? id, + int? replyTime, + }) async { + var params = { + if (id != null) 'id': id, + if (replyTime != null) 'reply_time': replyTime, + }; + var res = await Request().get(Api.messageReplyAPi, data: params); + if (res.data['code'] == 0) { + try { + return { + 'status': true, + 'data': MessageReplyModel.fromJson(res.data['data']), + }; + } catch (err) { + print(err); + return {'status': false, 'date': [], 'msg': err.toString()}; + } + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/models/msg/reply.dart b/lib/models/msg/reply.dart new file mode 100644 index 00000000..be617088 --- /dev/null +++ b/lib/models/msg/reply.dart @@ -0,0 +1,168 @@ +class MessageReplyModel { + MessageReplyModel({ + this.cursor, + this.items, + }); + + Cursor? cursor; + List? items; + + MessageReplyModel.fromJson(Map json) { + cursor = Cursor.fromJson(json['cursor']); + items = json["items"] != null + ? json["items"].map((e) { + return MessageReplyItem.fromJson(e); + }).toList() + : []; + } +} + +class Cursor { + Cursor({ + this.id, + this.isEnd, + this.time, + }); + + int? id; + bool? isEnd; + int? time; + + Cursor.fromJson(Map json) { + id = json['id']; + isEnd = json['is_end']; + time = json['time']; + } +} + +class MessageReplyItem { + MessageReplyItem({ + this.count, + this.id, + this.isMulti, + this.item, + this.replyTime, + this.user, + }); + + int? count; + int? id; + int? isMulti; + ReplyContentItem? item; + int? replyTime; + ReplyUser? user; + + MessageReplyItem.fromJson(Map json) { + count = json['count']; + id = json['id']; + isMulti = json['is_multi']; + item = ReplyContentItem.fromJson(json["item"]); + replyTime = json['reply_time']; + user = ReplyUser.fromJson(json['user']); + } +} + +class ReplyContentItem { + ReplyContentItem({ + this.subjectId, + this.rootId, + this.sourceId, + this.targetId, + this.type, + this.businessId, + this.business, + this.title, + this.desc, + this.image, + this.uri, + this.nativeUri, + this.detailTitle, + this.rootReplyContent, + this.sourceContent, + this.targetReplyContent, + this.atDetails, + this.topicDetails, + this.hideReplyButton, + this.hideLikeButton, + this.likeState, + this.danmu, + this.message, + }); + + int? subjectId; + int? rootId; + int? sourceId; + int? targetId; + String? type; + int? businessId; + String? business; + String? title; + String? desc; + String? image; + String? uri; + String? nativeUri; + String? detailTitle; + String? rootReplyContent; + String? sourceContent; + String? targetReplyContent; + List? atDetails; + List? topicDetails; + bool? hideReplyButton; + bool? hideLikeButton; + int? likeState; + String? danmu; + String? message; + + ReplyContentItem.fromJson(Map json) { + subjectId = json['subject_id']; + rootId = json['root_id']; + sourceId = json['source_id']; + targetId = json['target_id']; + type = json['type']; + businessId = json['business_id']; + business = json['business']; + title = json['title']; + desc = json['desc']; + image = json['image']; + uri = json['uri']; + nativeUri = json['native_uri']; + detailTitle = json['detail_title']; + rootReplyContent = json['root_reply_content']; + sourceContent = json['source_content']; + targetReplyContent = json['target_reply_content']; + atDetails = json['at_details']; + topicDetails = json['topic_details']; + hideReplyButton = json['hide_reply_button']; + hideLikeButton = json['hide_like_button']; + likeState = json['like_state']; + danmu = json['danmu']; + message = json['message']; + } +} + +class ReplyUser { + ReplyUser({ + this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow, + }); + + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + ReplyUser.fromJson(Map json) { + mid = json['mid']; + fans = json['fans']; + nickname = json['nickname']; + avatar = json['avatar']; + midLink = json['mid_link']; + follow = json['follow']; + } +} diff --git a/lib/pages/message/reply/controller.dart b/lib/pages/message/reply/controller.dart new file mode 100644 index 00000000..d62662df --- /dev/null +++ b/lib/pages/message/reply/controller.dart @@ -0,0 +1,25 @@ +import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import 'package:pilipala/models/msg/reply.dart'; + +class MessageReplyController extends GetxController { + Cursor? cursor; + RxList replyItems = [].obs; + + Future queryMessageReply({String type = 'init'}) async { + if (cursor != null && cursor!.isEnd == true) { + return {}; + } + var params = { + if (type == 'onLoad') 'id': cursor!.id, + if (type == 'onLoad') 'replyTime': cursor!.time, + }; + var res = await MsgHttp.messageReply( + id: params['id'], replyTime: params['replyTime']); + if (res['status']) { + cursor = res['data'].cursor; + replyItems.addAll(res['data'].items); + } + return res; + } +} diff --git a/lib/pages/message/reply/index.dart b/lib/pages/message/reply/index.dart new file mode 100644 index 00000000..969d03dd --- /dev/null +++ b/lib/pages/message/reply/index.dart @@ -0,0 +1,4 @@ +library message_reply; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/reply/view.dart b/lib/pages/message/reply/view.dart new file mode 100644 index 00000000..c4c02f6c --- /dev/null +++ b/lib/pages/message/reply/view.dart @@ -0,0 +1,285 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/gestures.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/msg/reply.dart'; +import 'package:pilipala/utils/utils.dart'; + +import 'controller.dart'; + +class MessageReplyPage extends StatefulWidget { + const MessageReplyPage({super.key}); + + @override + State createState() => _MessageReplyPageState(); +} + +class _MessageReplyPageState extends State { + final MessageReplyController _messageReplyCtr = + Get.put(MessageReplyController()); + late Future _futureBuilderFuture; + final ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _messageReplyCtr.queryMessageReply(); + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle('follow', const Duration(seconds: 1), () { + _messageReplyCtr.queryMessageReply(type: 'onLoad'); + }); + } + }, + ); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('回复我的'), + ), + body: RefreshIndicator( + onRefresh: () async { + await _messageReplyCtr.queryMessageReply(type: 'init'); + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + final replyItems = _messageReplyCtr.replyItems; + return Obx( + () => ListView.separated( + controller: scrollController, + itemBuilder: (context, index) => + ReplyItem(item: replyItems[index]), + itemCount: replyItems.length, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 66, + endIndent: 14, + height: 1, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return CustomScrollView( + slivers: [ + HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageReplyCtr.queryMessageReply(); + }); + }, + ) + ], + ); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ); + } +} + +class ReplyItem extends StatelessWidget { + final MessageReplyItem item; + + const ReplyItem({super.key, required this.item}); + + @override + Widget build(BuildContext context) { + final String heroTag = Utils.makeHeroTag(item.user!.mid); + final String bvid = item.item!.uri!.split('/').last; + // 页码 + final String page = + item.item!.nativeUri!.split('page=').last.split('&').first; + // 根评论id + final String commentRootId = + item.item!.nativeUri!.split('comment_root_id=').last.split('&').first; + // 二级评论id + final String commentSecondaryId = + item.item!.nativeUri!.split('comment_secondary_id=').last; + + return InkWell( + onTap: () async { + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': '', + 'heroTag': heroTag, + }, + ); + }, + child: Padding( + padding: const EdgeInsets.all(14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + onTap: () { + Get.toNamed('/member?mid=${item.user!.mid}', + arguments: {'face': item.user!.avatar, 'heroTag': heroTag}); + }, + child: Hero( + tag: heroTag, + child: NetworkImgLayer( + width: 42, + height: 42, + type: 'avatar', + src: item.user!.avatar, + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich(TextSpan(children: [ + TextSpan(text: item.user!.nickname!), + const TextSpan(text: ' '), + if (item.item!.type! == 'video') + TextSpan( + text: '对我的视频发表了评论', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + if (item.item!.type! == 'reply') + TextSpan( + text: '回复了我的评论', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ])), + const SizedBox(height: 6), + Text.rich( + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle(letterSpacing: 0.3), + buildContent(context, item.item)), + if (item.item!.targetReplyContent != '') ...[ + const SizedBox(height: 2), + Text( + item.item!.targetReplyContent!, + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ], + const SizedBox(height: 4), + Row( + children: [ + Text( + Utils.dateFormat(item.replyTime!, formatType: 'detail'), + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + const SizedBox(width: 16), + Text( + '回复', + style: TextStyle( + color: Theme.of(context).colorScheme.outline), + ), + ], + ) + ], + ), + ), + // Spacer(), + const SizedBox(width: 25), + if (item.item!.type! == 'reply') + Container( + width: 60, + height: 80, + padding: const EdgeInsets.all(4), + child: Text( + item.item!.rootReplyContent!, + maxLines: 4, + style: const TextStyle( + fontSize: 12, + letterSpacing: 0.3, + ), + overflow: TextOverflow.ellipsis, + ), + ), + if (item.item!.type! == 'video') + NetworkImgLayer( + width: 60, + height: 60, + src: item.item!.image, + ), + ], + ), + ), + ); + } +} + +InlineSpan buildContent(BuildContext context, item) { + List? atDetails = item!.atDetails; + final List spanChilds = []; + if (atDetails!.isNotEmpty) { + final String patternStr = + atDetails.map((e) => '@${e['nickname']}').toList().join('|'); + final RegExp regExp = RegExp(patternStr); + item.sourceContent!.splitMapJoin( + regExp, + onMatch: (Match match) { + spanChilds.add( + TextSpan( + text: match.group(0), + recognizer: TapGestureRecognizer() + ..onTap = () { + var currentUser = atDetails + .where((e) => e['nickname'] == match.group(0)!.substring(1)) + .first; + Get.toNamed('/member?mid=${currentUser['mid']}', arguments: { + 'face': currentUser['avatar'], + }); + }, + style: TextStyle(color: Theme.of(context).colorScheme.primary), + ), + ); + return ''; + }, + onNonMatch: (String nonMatch) { + spanChilds.add( + TextSpan(text: nonMatch), + ); + return ''; + }, + ); + } else { + spanChilds.add( + TextSpan(text: item.sourceContent), + ); + } + + return TextSpan(children: spanChilds); +} diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index e00c990e..f3cc47d6 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -12,7 +12,7 @@ class WhisperController extends GetxController { { 'icon': Icons.message_outlined, 'title': '回复我的', - 'path': '', + 'path': '/messageReply', 'count': 0, }, { diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 8070498e..fccdd844 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -69,7 +69,7 @@ class _WhisperPageState extends State { children: [ ..._whisperController.noticesList.map((element) { return InkWell( - onTap: () => {}, + onTap: () => Get.toNamed(element['path']), onLongPress: () {}, borderRadius: StyleString.mdRadius, child: Column( diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 2ca333f8..bb036c6e 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/pages/follow_search/view.dart'; +import 'package:pilipala/pages/message/reply/index.dart'; import 'package:pilipala/pages/setting/pages/logs.dart'; import '../pages/about/index.dart'; @@ -178,6 +179,8 @@ class Routes { // 操作菜单 CustomGetPage( name: '/actionMenuSet', page: () => const ActionMenuSetPage()), + // 回复我的 + CustomGetPage(name: '/messageReply', page: () => const MessageReplyPage()), ]; } From 779f7e76662fbadc562bff5f13c3b5c0f4858386 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 17:53:46 +0800 Subject: [PATCH 240/349] =?UTF-8?q?feat:=20=E6=94=B6=E5=88=B0=E7=9A=84?= =?UTF-8?q?=E8=B5=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/msg.dart | 26 +- lib/models/msg/like.dart | 183 +++++++++++++ lib/pages/message/at/controller.dart | 3 + lib/pages/message/at/index.dart | 4 + lib/pages/message/at/view.dart | 19 ++ lib/pages/message/like/controller.dart | 30 +++ lib/pages/message/like/index.dart | 4 + lib/pages/message/like/view.dart | 319 +++++++++++++++++++++++ lib/pages/message/reply/view.dart | 27 +- lib/pages/message/system/controller.dart | 3 + lib/pages/message/system/index.dart | 4 + lib/pages/message/system/view.dart | 19 ++ lib/pages/whisper/controller.dart | 6 +- lib/router/app_pages.dart | 10 + 15 files changed, 636 insertions(+), 24 deletions(-) create mode 100644 lib/models/msg/like.dart create mode 100644 lib/pages/message/at/controller.dart create mode 100644 lib/pages/message/at/index.dart create mode 100644 lib/pages/message/at/view.dart create mode 100644 lib/pages/message/like/controller.dart create mode 100644 lib/pages/message/like/index.dart create mode 100644 lib/pages/message/like/view.dart create mode 100644 lib/pages/message/system/controller.dart create mode 100644 lib/pages/message/system/index.dart create mode 100644 lib/pages/message/system/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 4db5994d..f20b8bcf 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -545,4 +545,7 @@ class Api { /// 回复我的 static const String messageReplyAPi = '/x/msgfeed/reply'; + + /// 收到的赞 + static const String messageLikeAPi = '/x/msgfeed/like'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 20905386..7c168230 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:math'; import 'package:dio/dio.dart'; +import 'package:pilipala/models/msg/like.dart'; import 'package:pilipala/models/msg/reply.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; @@ -256,7 +257,30 @@ class MsgHttp { 'data': MessageReplyModel.fromJson(res.data['data']), }; } catch (err) { - print(err); + return {'status': false, 'date': [], 'msg': err.toString()}; + } + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } + + // 收到的赞 + static Future messageLike({ + int? id, + int? likeTime, + }) async { + var params = { + if (id != null) 'id': id, + if (likeTime != null) 'like_time': likeTime, + }; + var res = await Request().get(Api.messageLikeAPi, data: params); + if (res.data['code'] == 0) { + try { + return { + 'status': true, + 'data': MessageLikeModel.fromJson(res.data['data']), + }; + } catch (err) { return {'status': false, 'date': [], 'msg': err.toString()}; } } else { diff --git a/lib/models/msg/like.dart b/lib/models/msg/like.dart new file mode 100644 index 00000000..b279131b --- /dev/null +++ b/lib/models/msg/like.dart @@ -0,0 +1,183 @@ +class MessageLikeModel { + MessageLikeModel({ + this.latest, + this.total, + }); + + Latest? latest; + Total? total; + + factory MessageLikeModel.fromJson(Map json) => + MessageLikeModel( + latest: json["latest"] == null ? null : Latest.fromJson(json["latest"]), + total: json["total"] == null ? null : Total.fromJson(json["total"]), + ); +} + +class Latest { + Latest({ + this.items, + this.lastViewAt, + }); + + List? items; + int? lastViewAt; + + factory Latest.fromJson(Map json) => Latest( + items: json["items"], + lastViewAt: json["last_view_at"], + ); +} + +class Total { + Total({ + this.cursor, + this.items, + }); + + Cursor? cursor; + List? items; + + factory Total.fromJson(Map json) => Total( + cursor: Cursor.fromJson(json['cursor']), + items: json["items"] == null + ? [] + : json["items"].map((e) { + return MessageLikeItem.fromJson(e); + }).toList(), + ); +} + +class Cursor { + Cursor({ + this.id, + this.isEnd, + this.time, + }); + + int? id; + bool? isEnd; + int? time; + + factory Cursor.fromJson(Map json) => Cursor( + id: json['id'], + isEnd: json['is_end'], + time: json['time'], + ); +} + +class MessageLikeItem { + MessageLikeItem({ + this.id, + this.users, + this.item, + this.counts, + this.likeTime, + this.noticeState, + this.isExpand = false, + }); + + int? id; + List? users; + MessageLikeItemItem? item; + int? counts; + int? likeTime; + int? noticeState; + bool isExpand; + + factory MessageLikeItem.fromJson(Map json) => + MessageLikeItem( + id: json["id"], + users: json["users"] == null + ? [] + : json["users"].map((e) { + return MessageLikeUser.fromJson(e); + }).toList(), + item: json["item"] == null + ? null + : MessageLikeItemItem.fromJson(json["item"]), + counts: json["counts"], + likeTime: json["like_time"], + noticeState: json["notice_state"], + ); +} + +class MessageLikeUser { + MessageLikeUser({ + this.mid, + this.fans, + this.nickname, + this.avatar, + this.midLink, + this.follow, + }); + + int? mid; + int? fans; + String? nickname; + String? avatar; + String? midLink; + bool? follow; + + factory MessageLikeUser.fromJson(Map json) => + MessageLikeUser( + mid: json["mid"], + fans: json["fans"], + nickname: json["nickname"], + avatar: json["avatar"], + midLink: json["mid_link"], + follow: json["follow"], + ); +} + +class MessageLikeItemItem { + MessageLikeItemItem({ + this.itemId, + this.pid, + this.type, + this.business, + this.businessId, + this.replyBusinessId, + this.likeBusinessId, + this.title, + this.desc, + this.image, + this.uri, + this.detailName, + this.nativeUri, + this.ctime, + }); + + int? itemId; + int? pid; + String? type; + String? business; + int? businessId; + int? replyBusinessId; + int? likeBusinessId; + String? title; + String? desc; + String? image; + String? uri; + String? detailName; + String? nativeUri; + int? ctime; + + factory MessageLikeItemItem.fromJson(Map json) => + MessageLikeItemItem( + itemId: json["item_id"], + pid: json["pid"], + type: json["type"], + business: json["business"], + businessId: json["business_id"], + replyBusinessId: json["reply_business_id"], + likeBusinessId: json["like_business_id"], + title: json["title"], + desc: json["desc"], + image: json["image"], + uri: json["uri"], + detailName: json["detail_name"], + nativeUri: json["native_uri"], + ctime: json["ctime"], + ); +} diff --git a/lib/pages/message/at/controller.dart b/lib/pages/message/at/controller.dart new file mode 100644 index 00000000..af08987f --- /dev/null +++ b/lib/pages/message/at/controller.dart @@ -0,0 +1,3 @@ +import 'package:get/get.dart'; + +class MessageAtController extends GetxController {} diff --git a/lib/pages/message/at/index.dart b/lib/pages/message/at/index.dart new file mode 100644 index 00000000..b2c573e6 --- /dev/null +++ b/lib/pages/message/at/index.dart @@ -0,0 +1,4 @@ +library message_at; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/at/view.dart b/lib/pages/message/at/view.dart new file mode 100644 index 00000000..9c48ec99 --- /dev/null +++ b/lib/pages/message/at/view.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class MessageAtPage extends StatefulWidget { + const MessageAtPage({super.key}); + + @override + State createState() => _MessageAtPageState(); +} + +class _MessageAtPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('@我的'), + ), + ); + } +} diff --git a/lib/pages/message/like/controller.dart b/lib/pages/message/like/controller.dart new file mode 100644 index 00000000..9b09f89a --- /dev/null +++ b/lib/pages/message/like/controller.dart @@ -0,0 +1,30 @@ +import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import 'package:pilipala/models/msg/like.dart'; + +class MessageLikeController extends GetxController { + Cursor? cursor; + RxList likeItems = [].obs; + + Future queryMessageLike({String type = 'init'}) async { + if (cursor != null && cursor!.isEnd == true) { + return {}; + } + var params = { + if (type == 'onLoad') 'id': cursor!.id, + if (type == 'onLoad') 'likeTime': cursor!.time, + }; + var res = await MsgHttp.messageLike( + id: params['id'], likeTime: params['likeTime']); + if (res['status']) { + cursor = res['data'].total.cursor; + likeItems.addAll(res['data'].total.items); + } + return res; + } + + Future expandedUsersAvatar(i) async { + likeItems[i].isExpand = !likeItems[i].isExpand; + likeItems.refresh(); + } +} diff --git a/lib/pages/message/like/index.dart b/lib/pages/message/like/index.dart new file mode 100644 index 00000000..4f9c143a --- /dev/null +++ b/lib/pages/message/like/index.dart @@ -0,0 +1,4 @@ +library message_like; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart new file mode 100644 index 00000000..e677fb25 --- /dev/null +++ b/lib/pages/message/like/view.dart @@ -0,0 +1,319 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/msg/like.dart'; +import 'package:pilipala/utils/utils.dart'; + +import 'controller.dart'; + +class MessageLikePage extends StatefulWidget { + const MessageLikePage({super.key}); + + @override + State createState() => _MessageLikePageState(); +} + +class _MessageLikePageState extends State { + final MessageLikeController _messageLikeCtr = + Get.put(MessageLikeController()); + late Future _futureBuilderFuture; + final ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _messageLikeCtr.queryMessageLike(); + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle('follow', const Duration(seconds: 1), () { + _messageLikeCtr.queryMessageLike(type: 'onLoad'); + }); + } + }, + ); + } + + @override + void dispose() { + scrollController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('收到的赞'), + ), + body: RefreshIndicator( + onRefresh: () async { + await _messageLikeCtr.queryMessageLike(type: 'init'); + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + final likeItems = _messageLikeCtr.likeItems; + return Obx( + () => ListView.separated( + controller: scrollController, + itemBuilder: (context, index) => LikeItem( + item: likeItems[index], + index: index, + messageLikeCtr: _messageLikeCtr, + ), + itemCount: likeItems.length, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 66, + endIndent: 14, + height: 1, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return CustomScrollView( + slivers: [ + HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageLikeCtr.queryMessageLike(); + }); + }, + ) + ], + ); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ); + } +} + +class LikeItem extends StatelessWidget { + final MessageLikeItem item; + final int index; + final MessageLikeController messageLikeCtr; + + const LikeItem( + {super.key, + required this.item, + required this.index, + required this.messageLikeCtr}); + + @override + Widget build(BuildContext context) { + Color outline = Theme.of(context).colorScheme.outline; + final nickNameList = item.users!.map((e) => e.nickname).take(2).toList(); + int usersLen = item.users!.length > 3 ? 3 : item.users!.length; + final String bvid = item.item!.uri!.split('/').last; + // 页码 + final String page = + item.item!.nativeUri!.split('page=').last.split('&').first; + // 根评论id + final String commentRootId = + item.item!.nativeUri!.split('comment_root_id=').last.split('&').first; + // 二级评论id + final String commentSecondaryId = + item.item!.nativeUri!.split('comment_secondary_id=').last; + + return InkWell( + onTap: () async { + try { + final int cid = await SearchHttp.ab2c(bvid: bvid); + final String heroTag = Utils.makeHeroTag(bvid); + Get.toNamed( + '/video?bvid=$bvid&cid=$cid', + arguments: { + 'pic': '', + 'heroTag': heroTag, + }, + ); + } catch (_) { + SmartDialog.showToast('视频可能失效了'); + } + }, + child: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(14), + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + GestureDetector( + behavior: HitTestBehavior.translucent, + onTap: () { + if (usersLen == 1) { + final String heroTag = + Utils.makeHeroTag(item.users!.first.mid); + Get.toNamed('/member?mid=${item.users!.first.mid}', + arguments: { + 'face': item.users!.first.avatar, + 'heroTag': heroTag + }); + } else { + messageLikeCtr.expandedUsersAvatar(index); + } + }, + // 多个头像层叠 + child: SizedBox( + width: 50, + height: 50, + child: Stack( + children: [ + for (var i = 0; i < usersLen; i++) + Positioned( + top: i % 2 * (50 / (usersLen >= 2 ? 2 : 1)), + left: i / 2 * (50 / (usersLen >= 2 ? 2 : 1)), + child: NetworkImgLayer( + width: 50 / (usersLen >= 2 ? 2 : 1), + height: 50 / (usersLen >= 2 ? 2 : 1), + type: 'avatar', + src: item.users![i].avatar, + ), + ), + ], + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text.rich(TextSpan(children: [ + TextSpan(text: nickNameList.join('、')), + const TextSpan(text: ' '), + if (item.users!.length > 1) + TextSpan( + text: '等总计${item.users!.length}人', + style: TextStyle(color: outline), + ), + TextSpan( + text: '赞了我的评论', + style: TextStyle(color: outline), + ), + ])), + const SizedBox(height: 4), + Text( + Utils.dateFormat(item.likeTime!, formatType: 'detail'), + style: TextStyle(color: outline), + ), + ], + ), + ), + const SizedBox(width: 25), + if (item.item!.type! == 'reply') + Container( + width: 60, + height: 60, + padding: const EdgeInsets.all(4), + child: Text( + item.item!.title!, + maxLines: 4, + style: const TextStyle(fontSize: 12, letterSpacing: 0.3), + overflow: TextOverflow.ellipsis, + ), + ), + if (item.item!.type! == 'video') + NetworkImgLayer( + width: 60, + height: 60, + src: item.item!.image, + ), + ], + ), + ), + Positioned( + top: 0, + right: 0, + bottom: 0, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + width: item.isExpand ? Get.size.width - 74 : 0, + color: Theme.of(context).colorScheme.secondaryContainer, + child: ListView.builder( + itemCount: item.users!.length, + scrollDirection: Axis.horizontal, + itemBuilder: (BuildContext context, int i) { + return Padding( + padding: EdgeInsets.fromLTRB(i == 0 ? 12 : 4, 8, 4, 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + GestureDetector( + onTap: () { + final String heroTag = + Utils.makeHeroTag(item.users![i].mid); + Get.toNamed( + '/member?mid=${item.users![i].mid}', + arguments: { + 'face': item.users![i].avatar, + 'heroTag': heroTag + }, + ); + }, + child: NetworkImgLayer( + width: 42, + height: 42, + type: 'avatar', + src: item.users![i].avatar, + ), + ), + const SizedBox(height: 6), + SizedBox( + width: 68, + child: Text( + textAlign: TextAlign.center, + item.users![i].nickname!, + maxLines: 1, + overflow: TextOverflow.clip, + style: TextStyle(color: outline), + ), + ), + ], + ), + ); + }, + ), + ), + ), + Positioned( + top: 0, + left: 0, + bottom: 0, + child: GestureDetector( + onTap: () { + messageLikeCtr.expandedUsersAvatar(index); + }, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + width: item.isExpand ? 74 : 0, + color: Colors.black.withOpacity(0.3), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/pages/message/reply/view.dart b/lib/pages/message/reply/view.dart index c4c02f6c..881f8650 100644 --- a/lib/pages/message/reply/view.dart +++ b/lib/pages/message/reply/view.dart @@ -113,6 +113,7 @@ class ReplyItem extends StatelessWidget { @override Widget build(BuildContext context) { + Color outline = Theme.of(context).colorScheme.outline; final String heroTag = Utils.makeHeroTag(item.user!.mid); final String bvid = item.item!.uri!.split('/').last; // 页码 @@ -167,15 +168,11 @@ class ReplyItem extends StatelessWidget { const TextSpan(text: ' '), if (item.item!.type! == 'video') TextSpan( - text: '对我的视频发表了评论', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), + text: '对我的视频发表了评论', style: TextStyle(color: outline)), if (item.item!.type! == 'reply') TextSpan( text: '回复了我的评论', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: outline), ), ])), const SizedBox(height: 6), @@ -188,8 +185,7 @@ class ReplyItem extends StatelessWidget { const SizedBox(height: 2), Text( item.item!.targetReplyContent!, - style: TextStyle( - color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: outline), ), ], const SizedBox(height: 4), @@ -197,21 +193,15 @@ class ReplyItem extends StatelessWidget { children: [ Text( Utils.dateFormat(item.replyTime!, formatType: 'detail'), - style: TextStyle( - color: Theme.of(context).colorScheme.outline), + style: TextStyle(color: outline), ), const SizedBox(width: 16), - Text( - '回复', - style: TextStyle( - color: Theme.of(context).colorScheme.outline), - ), + Text('回复', style: TextStyle(color: outline)), ], ) ], ), ), - // Spacer(), const SizedBox(width: 25), if (item.item!.type! == 'reply') Container( @@ -221,10 +211,7 @@ class ReplyItem extends StatelessWidget { child: Text( item.item!.rootReplyContent!, maxLines: 4, - style: const TextStyle( - fontSize: 12, - letterSpacing: 0.3, - ), + style: const TextStyle(fontSize: 12, letterSpacing: 0.3), overflow: TextOverflow.ellipsis, ), ), diff --git a/lib/pages/message/system/controller.dart b/lib/pages/message/system/controller.dart new file mode 100644 index 00000000..ad28af56 --- /dev/null +++ b/lib/pages/message/system/controller.dart @@ -0,0 +1,3 @@ +import 'package:get/get.dart'; + +class MessageSystemController extends GetxController {} diff --git a/lib/pages/message/system/index.dart b/lib/pages/message/system/index.dart new file mode 100644 index 00000000..70b9fc6d --- /dev/null +++ b/lib/pages/message/system/index.dart @@ -0,0 +1,4 @@ +library message_system; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/message/system/view.dart b/lib/pages/message/system/view.dart new file mode 100644 index 00000000..da0f1219 --- /dev/null +++ b/lib/pages/message/system/view.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; + +class MessageSystemPage extends StatefulWidget { + const MessageSystemPage({super.key}); + + @override + State createState() => _MessageSystemPageState(); +} + +class _MessageSystemPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('系统通知'), + ), + ); + } +} diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index f3cc47d6..2614bf5a 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -18,19 +18,19 @@ class WhisperController extends GetxController { { 'icon': Icons.alternate_email, 'title': '@ 我的', - 'path': '', + 'path': '/messageAt', 'count': 0, }, { 'icon': Icons.thumb_up_outlined, 'title': '收到的赞', - 'path': '', + 'path': '/messageLike', 'count': 0, }, { 'icon': Icons.notifications_none_outlined, 'title': '系统通知', - 'path': '', + 'path': '/messageSystem', 'count': 0, } ].obs; diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index bb036c6e..a6b48f0d 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -4,7 +4,10 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/pages/follow_search/view.dart'; +import 'package:pilipala/pages/message/at/index.dart'; +import 'package:pilipala/pages/message/like/index.dart'; import 'package:pilipala/pages/message/reply/index.dart'; +import 'package:pilipala/pages/message/system/index.dart'; import 'package:pilipala/pages/setting/pages/logs.dart'; import '../pages/about/index.dart'; @@ -181,6 +184,13 @@ class Routes { name: '/actionMenuSet', page: () => const ActionMenuSetPage()), // 回复我的 CustomGetPage(name: '/messageReply', page: () => const MessageReplyPage()), + // @我的 + CustomGetPage(name: '/messageAt', page: () => const MessageAtPage()), + // 收到的赞 + CustomGetPage(name: '/messageLike', page: () => const MessageLikePage()), + // 系统通知 + CustomGetPage( + name: '/messageSystem', page: () => const MessageSystemPage()), ]; } From a2210f17f64b65008481d05503f8c8219d4c2dfa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 18:24:31 +0800 Subject: [PATCH 241/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E9=93=BE=E6=8E=A5=E9=87=8D=E5=A4=8D=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index a83b7809..023e52be 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -196,9 +196,7 @@ class PiliSchame { parameters: {'url': redirectUrl, 'type': 'url', 'pageTitle': ''}, ); } - } - - if (path != null) { + } else if (path != null) { final String area = path.split('/').last; switch (area) { case 'bangumi': From 4591593bb56a36539700383404823355410244c5 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 18:34:32 +0800 Subject: [PATCH 242/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E8=A1=A8?= =?UTF-8?q?=E6=83=85=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply_new/view.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pages/video/detail/reply_new/view.dart b/lib/pages/video/detail/reply_new/view.dart index 40267559..0f04455f 100644 --- a/lib/pages/video/detail/reply_new/view.dart +++ b/lib/pages/video/detail/reply_new/view.dart @@ -117,6 +117,7 @@ class _VideoReplyNewDialogState extends State final String newText = currentText.substring(0, cursorPosition) + emote.text! + currentText.substring(cursorPosition); + message.value = newText; _replyContentController.value = TextEditingValue( text: newText, selection: From 2503d5cbb4833ad19d63eb71919b097d10052026 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 19:09:05 +0800 Subject: [PATCH 243/349] =?UTF-8?q?opt:=20=E6=90=9C=E7=B4=A2=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E4=B8=BA=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/http_error.dart | 38 +++++++++------ lib/http/search.dart | 16 +++++-- lib/models/search/result.dart | 10 ++-- lib/pages/search_panel/controller.dart | 4 +- .../search_panel/widgets/video_panel.dart | 47 ++++++++++++------- 5 files changed, 72 insertions(+), 43 deletions(-) diff --git a/lib/common/widgets/http_error.dart b/lib/common/widgets/http_error.dart index cbc6659b..0381319e 100644 --- a/lib/common/widgets/http_error.dart +++ b/lib/common/widgets/http_error.dart @@ -2,12 +2,18 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; class HttpError extends StatelessWidget { - const HttpError( - {required this.errMsg, required this.fn, this.btnText, super.key}); + const HttpError({ + required this.errMsg, + required this.fn, + this.btnText, + this.isShowBtn = true, + super.key, + }); final String? errMsg; final Function()? fn; final String? btnText; + final bool isShowBtn; @override Widget build(BuildContext context) { @@ -29,20 +35,22 @@ class HttpError extends StatelessWidget { style: Theme.of(context).textTheme.titleSmall, ), const SizedBox(height: 20), - FilledButton.tonal( - onPressed: () { - fn!(); - }, - style: ButtonStyle( - backgroundColor: MaterialStateProperty.resolveWith((states) { - return Theme.of(context).colorScheme.primary.withAlpha(20); - }), + if (isShowBtn) + FilledButton.tonal( + onPressed: () { + fn!(); + }, + style: ButtonStyle( + backgroundColor: MaterialStateProperty.resolveWith((states) { + return Theme.of(context).colorScheme.primary.withAlpha(20); + }), + ), + child: Text( + btnText ?? '点击重试', + style: + TextStyle(color: Theme.of(context).colorScheme.primary), + ), ), - child: Text( - btnText ?? '点击重试', - style: TextStyle(color: Theme.of(context).colorScheme.primary), - ), - ), ], ), ), diff --git a/lib/http/search.dart b/lib/http/search.dart index 075defc7..403e6a37 100644 --- a/lib/http/search.dart +++ b/lib/http/search.dart @@ -88,7 +88,11 @@ class SearchHttp { if (tids != null && tids != -1) 'tids': tids, }; var res = await Request().get(Api.searchByType, data: reqData); - if (res.data['code'] == 0 && res.data['data']['numPages'] > 0) { + if (res.data['code'] == 0) { + if (res.data['data']['numPages'] == 0) { + // 我想返回数据,使得可以通过data.list 取值,结果为[] + return {'status': true, 'data': Data()}; + } Object data; try { switch (searchType) { @@ -125,9 +129,7 @@ class SearchHttp { return { 'status': false, 'data': [], - 'msg': res.data['data'] != null && res.data['data']['numPages'] == 0 - ? '没有相关数据' - : res.data['message'], + 'msg': res.data['message'], }; } } @@ -206,3 +208,9 @@ class SearchHttp { } } } + +class Data { + List list; + + Data({this.list = const []}); +} diff --git a/lib/models/search/result.dart b/lib/models/search/result.dart index 81917b72..b903c873 100644 --- a/lib/models/search/result.dart +++ b/lib/models/search/result.dart @@ -5,10 +5,12 @@ class SearchVideoModel { SearchVideoModel({this.list}); List? list; SearchVideoModel.fromJson(Map json) { - list = json['result'] - .where((e) => e['available'] == true) - .map((e) => SearchVideoItemModel.fromJson(e)) - .toList(); + list = json['result'] == null + ? [] + : json['result'] + .where((e) => e['available'] == true) + .map((e) => SearchVideoItemModel.fromJson(e)) + .toList(); } } diff --git a/lib/pages/search_panel/controller.dart b/lib/pages/search_panel/controller.dart index 35113198..dc0b2bac 100644 --- a/lib/pages/search_panel/controller.dart +++ b/lib/pages/search_panel/controller.dart @@ -30,9 +30,9 @@ class SearchPanelController extends GetxController { ); if (result['status']) { if (type == 'onRefresh') { - resultList.value = result['data'].list; + resultList.value = result['data'].list ?? []; } else { - resultList.addAll(result['data'].list); + resultList.addAll(result['data'].list ?? []); } page.value++; onPushDetail(keyword, resultList); diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index 15745bde..f43e2eec 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/pages/search/widgets/search_text.dart'; @@ -25,25 +26,35 @@ class SearchVideoPanel extends StatelessWidget { children: [ Padding( padding: const EdgeInsets.only(top: 36), - child: ListView.builder( - controller: ctr!.scrollController, - addAutomaticKeepAlives: false, - addRepaintBoundaries: false, - itemCount: list!.length, - itemBuilder: (context, index) { - var i = list![index]; - return Padding( - padding: index == 0 - ? const EdgeInsets.only(top: 2) - : EdgeInsets.zero, - child: VideoCardH( - videoItem: i, - showPubdate: true, - source: 'search', + child: list!.isNotEmpty + ? ListView.builder( + controller: ctr!.scrollController, + addAutomaticKeepAlives: false, + addRepaintBoundaries: false, + itemCount: list!.length, + itemBuilder: (context, index) { + var i = list![index]; + return Padding( + padding: index == 0 + ? const EdgeInsets.only(top: 2) + : EdgeInsets.zero, + child: VideoCardH( + videoItem: i, + showPubdate: true, + source: 'search', + ), + ); + }, + ) + : CustomScrollView( + slivers: [ + HttpError( + errMsg: '没有数据', + isShowBtn: false, + fn: () => {}, + ) + ], ), - ); - }, - ), ), // 分类筛选 Container( From 75525595c6258a9561d1b88845babb626aaf07ad Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 16 Jun 2024 20:59:55 +0800 Subject: [PATCH 244/349] =?UTF-8?q?opt:=20=E9=A6=96=E9=A1=B5tab=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/view.dart | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index a25389bd..e485fe41 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -357,25 +357,29 @@ class CustomChip extends StatelessWidget { Widget build(BuildContext context) { final ColorScheme colorTheme = Theme.of(context).colorScheme; final Color secondaryContainer = colorTheme.secondaryContainer; + final Color onPrimary = colorTheme.onPrimary; + final Color primary = colorTheme.primary; final TextStyle chipTextStyle = selected - ? const TextStyle(fontWeight: FontWeight.bold, fontSize: 13) - : const TextStyle(fontSize: 13); - final ColorScheme colorScheme = Theme.of(context).colorScheme; + ? TextStyle(fontSize: 13, color: onPrimary) + : TextStyle(fontSize: 13, color: colorTheme.onSecondaryContainer); const VisualDensity visualDensity = VisualDensity(horizontal: -4.0, vertical: -2.0); return InputChip( - side: BorderSide( - color: selected - ? colorScheme.onSecondaryContainer.withOpacity(0.2) - : Colors.transparent, - ), + side: BorderSide.none, backgroundColor: secondaryContainer, - selectedColor: secondaryContainer, - color: MaterialStateProperty.resolveWith( - (Set states) => secondaryContainer.withAlpha(200)), - padding: const EdgeInsets.fromLTRB(7, 1, 7, 1), + color: MaterialStateProperty.resolveWith((states) { + if (states.contains(MaterialState.selected) || + states.contains(MaterialState.hovered)) { + return primary; + } + return colorTheme.secondaryContainer; + }), + padding: const EdgeInsets.fromLTRB(6, 1, 6, 1), label: Text(label, style: chipTextStyle), onPressed: () => onTap(), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(6), + ), selected: selected, showCheckmark: false, visualDensity: visualDensity, From b61d2305a93cbd12d910f83e7f7ed263fe938b47 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 17 Jun 2024 23:51:01 +0800 Subject: [PATCH 245/349] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E7=B1=BB?= =?UTF-8?q?=E5=88=AB=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/subtitle_type.dart | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/models/common/subtitle_type.dart b/lib/models/common/subtitle_type.dart index 54b52e8e..3db43dd2 100644 --- a/lib/models/common/subtitle_type.dart +++ b/lib/models/common/subtitle_type.dart @@ -9,6 +9,8 @@ enum SubtitleType { zhHans, // 英文(美国) enUS, + // 中文繁体 + zhTW, } extension SubtitleTypeExtension on SubtitleType { @@ -24,6 +26,8 @@ extension SubtitleTypeExtension on SubtitleType { return '中文(简体)'; case SubtitleType.enUS: return '英文(美国)'; + case SubtitleType.zhTW: + return '中文(繁体)'; } } } @@ -41,6 +45,8 @@ extension SubtitleIdExtension on SubtitleType { return 'zh-Hans'; case SubtitleType.enUS: return 'en-US'; + case SubtitleType.zhTW: + return 'zh-TW'; } } } @@ -58,6 +64,8 @@ extension SubtitleCodeExtension on SubtitleType { return 4; case SubtitleType.enUS: return 5; + case SubtitleType.zhTW: + return 6; } } } From 35d2d92480be22636e80b091ba381f237df9ff0b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 18 Jun 2024 23:35:38 +0800 Subject: [PATCH 246/349] =?UTF-8?q?fix:=20=E5=88=86=E9=9B=86=E5=88=87?= =?UTF-8?q?=E6=8D=A2=E5=AD=97=E5=B9=95=E6=9C=AA=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/bangumi/introduction/controller.dart | 2 ++ lib/pages/video/detail/introduction/controller.dart | 1 + 2 files changed, 3 insertions(+) diff --git a/lib/pages/bangumi/introduction/controller.dart b/lib/pages/bangumi/introduction/controller.dart index 65cd5dd8..208a85e4 100644 --- a/lib/pages/bangumi/introduction/controller.dart +++ b/lib/pages/bangumi/introduction/controller.dart @@ -225,6 +225,8 @@ class BangumiIntroController extends GetxController { videoDetailCtr.oid.value = aid; videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); + videoDetailCtr.getSubtitle(); + videoDetailCtr.setSubtitleContent(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 9c542f21..1a1b1b74 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -436,6 +436,7 @@ class VideoIntroController extends GetxController { videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); videoDetailCtr.getSubtitle(); + videoDetailCtr.setSubtitleContent(); // 重新请求评论 try { /// 未渲染回复组件时可能异常 From 54c38d8683acee6e6c81c75afaf0e4ec7573bfd6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 19 Jun 2024 23:17:36 +0800 Subject: [PATCH 247/349] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E7=B1=BB?= =?UTF-8?q?=E5=88=AB=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/subtitle_type.dart | 24 ++++++ .../video/detail/widgets/header_control.dart | 75 ++++++++++--------- 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/lib/models/common/subtitle_type.dart b/lib/models/common/subtitle_type.dart index 3db43dd2..ac3ee3e0 100644 --- a/lib/models/common/subtitle_type.dart +++ b/lib/models/common/subtitle_type.dart @@ -11,6 +11,12 @@ enum SubtitleType { enUS, // 中文繁体 zhTW, + // + en, + // + pt, + // + es, } extension SubtitleTypeExtension on SubtitleType { @@ -28,6 +34,12 @@ extension SubtitleTypeExtension on SubtitleType { return '英文(美国)'; case SubtitleType.zhTW: return '中文(繁体)'; + case SubtitleType.en: + return '英文'; + case SubtitleType.pt: + return '葡萄牙语'; + case SubtitleType.es: + return '西班牙语'; } } } @@ -47,6 +59,12 @@ extension SubtitleIdExtension on SubtitleType { return 'en-US'; case SubtitleType.zhTW: return 'zh-TW'; + case SubtitleType.en: + return 'en'; + case SubtitleType.pt: + return 'pt'; + case SubtitleType.es: + return 'es'; } } } @@ -66,6 +84,12 @@ extension SubtitleCodeExtension on SubtitleType { return 5; case SubtitleType.zhTW: return 6; + case SubtitleType.en: + return 7; + case SubtitleType.pt: + return 8; + case SubtitleType.es: + return 9; } } } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index f51edff1..5072377a 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -433,42 +433,47 @@ class _HeaderControlState extends State { return AlertDialog( title: const Text('选择字幕'), contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 18), - content: StatefulBuilder(builder: (context, StateSetter setState) { - return len == 0 - ? const SizedBox( - height: 60, - child: Center( - child: Text('没有字幕'), - ), - ) - : Column( - mainAxisSize: MainAxisSize.min, - children: [ - RadioListTile( - value: -1, - title: const Text('关闭字幕'), - groupValue: tempThemeValue, - onChanged: (value) { - tempThemeValue = value!; - widget.controller?.toggleSubtitle(value); - Get.back(); - }, + content: StatefulBuilder( + builder: (context, StateSetter setState) { + return len == 0 + ? const SizedBox( + height: 60, + child: Center( + child: Text('没有字幕'), ), - ...widget.videoDetailCtr!.subtitles - .map((e) => RadioListTile( - value: e.code, - title: Text(e.title), - groupValue: tempThemeValue, - onChanged: (value) { - tempThemeValue = value!; - widget.controller?.toggleSubtitle(value); - Get.back(); - }, - )) - .toList(), - ], - ); - }), + ) + : SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + RadioListTile( + value: -1, + title: const Text('关闭字幕'), + groupValue: tempThemeValue, + onChanged: (value) { + tempThemeValue = value!; + widget.controller?.toggleSubtitle(value); + Get.back(); + }, + ), + ...widget.videoDetailCtr!.subtitles + .map((e) => RadioListTile( + value: e.code, + title: Text(e.title), + groupValue: tempThemeValue, + onChanged: (value) { + tempThemeValue = value!; + widget.controller + ?.toggleSubtitle(value); + Get.back(); + }, + )) + .toList(), + ], + ), + ); + }, + ), ); }); } From 56c063538964c5119c10572b8e606118a87ea31b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 19 Jun 2024 23:39:19 +0800 Subject: [PATCH 248/349] =?UTF-8?q?mod:=20scheme=20=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index a83b7809..02c0084f 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -19,6 +19,7 @@ class PiliSchame { /// 完整链接进入 b23.无效 appScheme.getLatestScheme().then((SchemeEntity? value) { + print('getLatestScheme value: $value'); if (value != null) { _routePush(value); } @@ -26,6 +27,8 @@ class PiliSchame { /// 注册从外部打开的Scheme监听信息 # appScheme.registerSchemeListener().listen((SchemeEntity? event) { + print('registerSchemeListener event: $event'); + if (event != null) { _routePush(event); } @@ -92,6 +95,12 @@ class PiliSchame { 'id': 'cv$id', 'dynamicType': 'read' }); + } else if (host == 'pgc') { + if (path.contains('ep')) { + final String lastPathSegment = path.split('/').last; + RoutePush.bangumiPush( + null, int.parse(lastPathSegment.split('?').first)); + } } } if (scheme == 'https') { From 4de15ad6b3282b6b2e2354509e92bc6648d93dbe Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 20 Jun 2024 00:06:45 +0800 Subject: [PATCH 249/349] =?UTF-8?q?opt:=20=20=5FroutePush=20=E9=80=BB?= =?UTF-8?q?=E8=BE=91=E5=88=86=E6=94=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/app_scheme.dart | 138 +++++++++++++++++++++----------------- 1 file changed, 76 insertions(+), 62 deletions(-) diff --git a/lib/utils/app_scheme.dart b/lib/utils/app_scheme.dart index 39a36cad..17d20bcd 100644 --- a/lib/utils/app_scheme.dart +++ b/lib/utils/app_scheme.dart @@ -1,5 +1,6 @@ import 'package:appscheme/appscheme.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/utils/route_push.dart'; @@ -19,7 +20,6 @@ class PiliSchame { /// 完整链接进入 b23.无效 appScheme.getLatestScheme().then((SchemeEntity? value) { - print('getLatestScheme value: $value'); if (value != null) { _routePush(value); } @@ -27,8 +27,6 @@ class PiliSchame { /// 注册从外部打开的Scheme监听信息 # appScheme.registerSchemeListener().listen((SchemeEntity? event) { - print('registerSchemeListener event: $event'); - if (event != null) { _routePush(event); } @@ -41,66 +39,82 @@ class PiliSchame { final String host = value.host; final String path = value.path; if (scheme == 'bilibili') { - if (host == 'root') { - Navigator.popUntil( - Get.context!, (Route route) => route.isFirst); - } else if (host == 'space') { - final String mid = path.split('/').last; - Get.toNamed( - '/member?mid=$mid', - arguments: {'face': null}, - ); - } else if (host == 'video') { - String pathQuery = path.split('/').last; - final numericRegex = RegExp(r'^[0-9]+$'); - if (numericRegex.hasMatch(pathQuery)) { - pathQuery = 'AV$pathQuery'; - } - Map map = IdUtils.matchAvorBv(input: pathQuery); - if (map.containsKey('AV')) { - _videoPush(map['AV'], null); - } else if (map.containsKey('BV')) { - _videoPush(null, map['BV']); - } else { - SmartDialog.showToast('投稿匹配失败'); - } - } else if (host == 'live') { - final String roomId = path.split('/').last; - Get.toNamed('/liveRoom?roomid=$roomId', - arguments: {'liveItem': null, 'heroTag': roomId}); - } else if (host == 'bangumi') { - if (path.startsWith('/season')) { - final String seasonId = path.split('/').last; - RoutePush.bangumiPush(int.parse(seasonId), null); - } - } else if (host == 'opus') { - if (path.startsWith('/detail')) { - var opusId = path.split('/').last; - Get.toNamed( - '/webview', - parameters: { - 'url': 'https://www.bilibili.com/opus/$opusId', - 'type': 'url', - 'pageTitle': '', - }, + switch (host) { + case 'root': + Navigator.popUntil( + Get.context!, (Route route) => route.isFirst); + break; + case 'space': + final String mid = path.split('/').last; + Get.toNamed( + '/member?mid=$mid', + arguments: {'face': null}, ); - } - } else if (host == 'search') { - Get.toNamed('/searchResult', parameters: {'keyword': ''}); - } else if (host == 'article') { - final String id = path.split('/').last.split('?').first; - Get.toNamed('/htmlRender', parameters: { - 'url': 'https://www.bilibili.com/read/cv$id', - 'title': 'cv$id', - 'id': 'cv$id', - 'dynamicType': 'read' - }); - } else if (host == 'pgc') { - if (path.contains('ep')) { - final String lastPathSegment = path.split('/').last; - RoutePush.bangumiPush( - null, int.parse(lastPathSegment.split('?').first)); - } + break; + case 'video': + String pathQuery = path.split('/').last; + final numericRegex = RegExp(r'^[0-9]+$'); + if (numericRegex.hasMatch(pathQuery)) { + pathQuery = 'AV$pathQuery'; + } + Map map = IdUtils.matchAvorBv(input: pathQuery); + if (map.containsKey('AV')) { + _videoPush(map['AV'], null); + } else if (map.containsKey('BV')) { + _videoPush(null, map['BV']); + } else { + SmartDialog.showToast('投稿匹配失败'); + } + break; + case 'live': + final String roomId = path.split('/').last; + Get.toNamed( + '/liveRoom?roomid=$roomId', + arguments: {'liveItem': null, 'heroTag': roomId}, + ); + break; + case 'bangumi': + if (path.startsWith('/season')) { + final String seasonId = path.split('/').last; + RoutePush.bangumiPush(int.parse(seasonId), null); + } + break; + case 'opus': + if (path.startsWith('/detail')) { + var opusId = path.split('/').last; + Get.toNamed( + '/webview', + parameters: { + 'url': 'https://www.bilibili.com/opus/$opusId', + 'type': 'url', + 'pageTitle': '', + }, + ); + } + break; + case 'search': + Get.toNamed('/searchResult', parameters: {'keyword': ''}); + break; + case 'article': + final String id = path.split('/').last.split('?').first; + Get.toNamed('/htmlRender', parameters: { + 'url': 'https://www.bilibili.com/read/cv$id', + 'title': 'cv$id', + 'id': 'cv$id', + 'dynamicType': 'read' + }); + break; + case 'pgc': + if (path.contains('ep')) { + final String lastPathSegment = path.split('/').last; + RoutePush.bangumiPush( + null, int.parse(lastPathSegment.split('?').first)); + } + break; + default: + SmartDialog.showToast('未匹配地址,请联系开发者'); + Clipboard.setData(ClipboardData(text: value.toJson().toString())); + break; } } if (scheme == 'https') { From 1db1d8f598ac4b7236f2abe8e9fc7eab3074602c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 20 Jun 2024 23:38:49 +0800 Subject: [PATCH 250/349] =?UTF-8?q?feat:=20=E7=82=B9=E5=87=BBtab=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E9=A1=B6=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 12 ++++++++++++ lib/pages/video/detail/reply/view.dart | 3 +++ lib/pages/video/detail/view.dart | 2 ++ 3 files changed, 17 insertions(+) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 38c62d7e..ea85a5b9 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -109,6 +109,7 @@ class VideoDetailController extends GetxController ].obs; RxDouble sheetHeight = 0.0.obs; RxString archiveSourceType = 'dash'.obs; + ScrollController? replyScrillController; @override void onInit() { @@ -551,4 +552,15 @@ class VideoDetailController extends GetxController cover.value = videoItem['pic'] = pic; } } + + void onControllerCreated(ScrollController controller) { + replyScrillController = controller; + } + + void onTapTabbar(int index) { + if (index == 1 && tabCtr.index == 1) { + replyScrillController?.animateTo(0, + duration: const Duration(milliseconds: 300), curve: Curves.ease); + } + } } diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 653fe7e0..be1bd331 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -19,12 +19,14 @@ class VideoReplyPanel extends StatefulWidget { final int? oid; final int rpid; final String? replyLevel; + final Function(ScrollController)? onControllerCreated; const VideoReplyPanel({ this.bvid, this.oid, this.rpid = 0, this.replyLevel, + this.onControllerCreated, super.key, }); @@ -68,6 +70,7 @@ class _VideoReplyPanelState extends State _futureBuilderFuture = _videoReplyController.queryReplyList(); scrollController = ScrollController(); + widget.onControllerCreated?.call(scrollController); fabAnimationCtr.forward(); scrollListener(); } diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 22271c2b..ea29bf78 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -387,6 +387,7 @@ class _VideoDetailPageState extends State dividerColor: Colors.transparent, tabs: vdCtr.tabs.map((String name) => Tab(text: name)).toList(), + onTap: (index) => vdCtr.onTapTabbar(index), ), ), ), @@ -676,6 +677,7 @@ class _VideoDetailPageState extends State () => VideoReplyPanel( bvid: vdCtr.bvid, oid: vdCtr.oid.value, + onControllerCreated: vdCtr.onControllerCreated, ), ) ], From c5247b27c7b478bd15912d18521f1f13448a078b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 23 Jun 2024 23:12:04 +0800 Subject: [PATCH 251/349] =?UTF-8?q?mod:=20=E6=B6=88=E6=81=AF=E8=AE=A1?= =?UTF-8?q?=E6=95=B0=E6=B8=85=E9=9B=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index fccdd844..e31e942e 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -69,7 +69,14 @@ class _WhisperPageState extends State { children: [ ..._whisperController.noticesList.map((element) { return InkWell( - onTap: () => Get.toNamed(element['path']), + onTap: () { + Get.toNamed(element['path']); + + if (element['count'] > 0) { + element['count'] = 0; + } + _whisperController.noticesList.refresh(); + }, onLongPress: () {}, borderRadius: StyleString.mdRadius, child: Column( From 4fa9bdc3804c4ca3f4bd2e33973b42d7969c532a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 23 Jun 2024 23:23:05 +0800 Subject: [PATCH 252/349] =?UTF-8?q?mod:=20chatItem=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whisper_detail/widget/chat_item.dart | 90 ++++++++----------- 1 file changed, 39 insertions(+), 51 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index f64cf223..77e38073 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -1,4 +1,5 @@ // ignore_for_file: must_be_immutable +// ignore_for_file: constant_identifier_names import 'dart:convert'; import 'package:flutter/material.dart'; @@ -8,7 +9,6 @@ import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/storage.dart'; - import '../../../http/search.dart'; enum MsgType { @@ -409,12 +409,6 @@ class ChatItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(width: safeDistanceval), - if (isOwner) - Text( - Utils.dateFormat(item.timestamp), - style: Theme.of(context).textTheme.labelSmall!.copyWith( - color: Theme.of(context).colorScheme.outline), - ), Container( constraints: const BoxConstraints( maxWidth: 300.0, // 设置最大宽度为200.0 @@ -444,51 +438,45 @@ class ChatItem extends StatelessWidget { right: 8, ), padding: const EdgeInsets.all(paddingVal), - child: messageContent(context), - // child: Column( - // crossAxisAlignment: isOwner - // ? CrossAxisAlignment.end - // : CrossAxisAlignment.start, - // children: [ - // messageContent(context), - // SizedBox(height: isPic ? 7 : 2), - // Row( - // mainAxisSize: MainAxisSize.min, - // children: [ - // Text( - // Utils.dateFormat(item.timestamp), - // style: Theme.of(context) - // .textTheme - // .labelSmall! - // .copyWith( - // color: isOwner - // ? Theme.of(context) - // .colorScheme - // .onPrimary - // .withOpacity(0.8) - // : Theme.of(context) - // .colorScheme - // .onSecondaryContainer - // .withOpacity(0.8)), - // ), - // item.msgStatus == 1 - // ? Text( - // ' 已撤回', - // style: - // Theme.of(context).textTheme.labelSmall!, - // ) - // : const SizedBox() - // ], - // ) - // ], - // ), - ), - if (!isOwner) - Text( - Utils.dateFormat(item.timestamp), - style: Theme.of(context).textTheme.labelSmall!.copyWith( - color: Theme.of(context).colorScheme.outline), + child: Column( + crossAxisAlignment: isOwner + ? CrossAxisAlignment.end + : CrossAxisAlignment.start, + children: [ + messageContent(context), + SizedBox(height: isPic ? 7 : 4), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + Utils.dateFormat(item.timestamp), + style: Theme.of(context) + .textTheme + .labelSmall! + .copyWith( + color: isOwner + ? Theme.of(context) + .colorScheme + .onPrimary + .withOpacity(0.8) + : Theme.of(context) + .colorScheme + .onSecondaryContainer + .withOpacity(0.8)), + ), + item.msgStatus == 1 + ? Text( + ' 已撤回', + style: Theme.of(context) + .textTheme + .labelSmall!, + ) + : const SizedBox() + ], + ) + ], ), + ), const SizedBox(width: safeDistanceval), ], ), From abfb7a14391513474d30a7f19875b008c04b0931 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 24 Jun 2024 23:04:32 +0800 Subject: [PATCH 253/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA=20/?= =?UTF-8?q?=3F=20=E9=93=BE=E6=8E=A5=E8=B7=B3=E8=BD=AC=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/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 08e4d405..02d004e8 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -645,7 +645,7 @@ InlineSpan buildContent( '', ); } else { - Uri uri = Uri.parse(matchStr); + Uri uri = Uri.parse(matchStr.replaceAll('/?', '?')); SchemeEntity scheme = SchemeEntity( scheme: uri.scheme, host: uri.host, From cc32224daf5c2304999d1dfe0952b6b41395c924 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 24 Jun 2024 23:43:37 +0800 Subject: [PATCH 254/349] =?UTF-8?q?feat:=20=E9=9F=B3=E9=A2=91=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E6=96=B9=E5=BC=8F=E8=AE=BE=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/video/play/ao_output.dart | 6 ++++++ lib/pages/setting/play_setting.dart | 29 ++++++++++++++++++++++++++++ lib/plugin/pl_player/controller.dart | 9 ++++++++- lib/utils/storage.dart | 2 ++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 lib/models/video/play/ao_output.dart diff --git a/lib/models/video/play/ao_output.dart b/lib/models/video/play/ao_output.dart new file mode 100644 index 00000000..170a78c5 --- /dev/null +++ b/lib/models/video/play/ao_output.dart @@ -0,0 +1,6 @@ +final List aoOutputList = [ + {'title': 'audiotrack,opensles', 'value': '0'}, + {'title': 'opensles,audiotrack', 'value': '1'}, + {'title': 'audiotrack', 'value': '2'}, + {'title': 'opensles', 'value': '3'}, +]; diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 4a8495e5..0f7dcdc3 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/models/video/play/ao_output.dart'; 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'; @@ -28,6 +29,7 @@ class _PlaySettingState extends State { late dynamic defaultDecode; late int defaultFullScreenMode; late int defaultBtmProgressBehavior; + late String defaultAoOutput; @override void initState() { @@ -44,6 +46,8 @@ class _PlaySettingState extends State { defaultValue: FullScreenMode.values.first.code); defaultBtmProgressBehavior = setting.get(SettingBoxKey.btmProgressBehavior, defaultValue: BtmProgresBehavior.values.first.code); + defaultAoOutput = + setting.get(SettingBoxKey.defaultAoOutput, defaultValue: '0'); } @override @@ -263,6 +267,31 @@ class _PlaySettingState extends State { } }, ), + ListTile( + dense: false, + title: Text('音频输出方式', style: titleStyle), + subtitle: Text( + '当前输出方式 ${aoOutputList.firstWhere((element) => element['value'] == defaultAoOutput)['title']}', + style: subTitleStyle, + ), + onTap: () async { + String? result = await showDialog( + context: context, + builder: (context) { + return SelectDialog( + title: '音频输出方式', + value: defaultAoOutput, + values: aoOutputList, + ); + }, + ); + if (result != null) { + defaultAoOutput = result; + setting.put(SettingBoxKey.defaultAoOutput, result); + setState(() {}); + } + }, + ), ListTile( dense: false, title: Text('默认全屏方式', style: titleStyle), diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index 2865a117..a614d75d 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -13,6 +13,7 @@ import 'package:media_kit/media_kit.dart'; import 'package:media_kit_video/media_kit_video.dart'; import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/video.dart'; +import 'package:pilipala/models/video/play/ao_output.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/services/service_locator.dart'; @@ -453,7 +454,13 @@ class PlPlayerController { // 音量不一致 if (Platform.isAndroid) { await pp.setProperty("volume-max", "100"); - await pp.setProperty("ao", "audiotrack,opensles"); + String defaultAoOutput = + setting.get(SettingBoxKey.defaultAoOutput, defaultValue: '0'); + await pp.setProperty( + "ao", + aoOutputList + .where((e) => e['value'] == defaultAoOutput) + .first['title']); } await player.setAudioTrack( diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index 4a163446..bf9074e3 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -102,6 +102,8 @@ class SettingBoxKey { autoPiP = 'autoPiP', enableAutoLongPressSpeed = 'enableAutoLongPressSpeed', enablePlayerControlAnimation = 'enablePlayerControlAnimation', + // 默认音频输出方式 + defaultAoOutput = 'defaultAoOutput', // youtube 双击快进快退 enableQuickDouble = 'enableQuickDouble', From 7aa02be25197df858b32c5bfe7eb3fbc8f8dd1f8 Mon Sep 17 00:00:00 2001 From: guozhibin Date: Tue, 25 Jun 2024 23:51:32 +0800 Subject: [PATCH 255/349] mod --- assets/images/coin.png | Bin 7159 -> 1701 bytes lib/pages/message/reply/view.dart | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/images/coin.png b/assets/images/coin.png index bc2952a7a5cf519a331b83c0f268eef88769d471..afca87b286cdc1f677aef1cf5c80f5d4fc4d980e 100644 GIT binary patch literal 1701 zcmV;W23q-vP)JA z04>JA0Xnmvc>n+gS4l)cRCt{2n|p}WMHI(BdpC6}ZBf&-4K=OYinQEHi?X*;TU1Cz z_9C*9!U&_VEdMF!K}3J_pe1^1DN$`pN*ku;J0GpRU-Zz3-nwmB`;G(W!ms1}?(cVZ z=H9)pc(i}+b-Z+ zU_G!1xC595RPrCi!N5FV1JDk1_pr4BtALwbOBbg*G|sW7VkHKppUL!otY^EddVf z4a3XSn3f#Mi9@>q80j(3XkaezUXI214Y-sdVjtkS7-c$umw=iAEci)4Lym=b92h`> zF*q1LcoHZ`E`y--YgNOHdM)y)o+#FFkUDFo^c3&HITz83v_h$29`PWe9~9& z)efPJfs8*3_!`&_oEK=Pu5jpkMNb8eQU@0m_EErX|7Aq(_e!OGAveIMM(15>OWt_h z2olnBIN`i_i{TIb0?9rS_|xbvZ-~4w%|SSXqK3j4bA`#{>5zc(Z*&}m>1hXN^vxa} zygI-Z6=yd?iInTLC=0$Bm>FQMa@9F1e{))gJKYQvWsmh#Xt3i*^s=WS`|0X;=h;38Kx>y6nQtWx7^My=x-BPrD)EBDxK2H0!ZDuCaV%c(oO_ay zC$^2tHQ-h=V3Vf;$2**b%x;p5rm548I;u9a{SU$q_5pQfiZLgcF%m8I!2u>PE5|_oZ4aF7a6&%FXtX9ev4p{r zuov)mWL$fIYjKmY?Lt?lI~@@gAIP2igV|AO<9Ayl<37V54;kC;b#;2tbh<9U#4C-= z+ft4@LW8Z#0!5iFOGAw}nog$#nD`bWQ|Rv*6d%W>Io>-=C*kY95Xbtnk=w4vYqGv9 z3m(W+rx(4Be|2%^W^-PJ!G*vjIU>28nj74%r04PwQJk27NvoOnU+AxjWIMD4R%C7- z>9TJE1xTzzwgJ?lE=q^QjVEp^pFqZiYGvZQAQLo(PncGLddUWD6G`BP7WO!;D>W1H{_3!Avd% z4nC-JoM&NWaS&Skjv2GiQ-@hbo)lZeVxH+Nu{s}2anRtk`r5NN6nNXg2Tu3Y!7ixy zx}4>0QOeq-fkhuIav0U2i(98*v(+)AsNOSqI>CGZvF)&=L5)TUi4*vsQny`qo=w7O z`+Ls4(8!eJX(DPJU&}WrF(gGbS+p$=zyK$j>r~qLX$EFYNUB@Urf(i!1;I$*3&$i|ls)zq zsx`RbB2mGsMMuXh!cNV+6th$|qnWFg7~>1}>yj!5Ns3-I@El|L#{G6{ zv-yCewpBAtC53WjbZdI>E3h=JL(AN1OYvuOu6o_Of%iRsL)f9u=lMzHZKaGV-5K|5 vP(+T3@c-z-$lt%{Lq-xQ_W&0Z@=E&$KxEtUmHWF=00000NkvXXu0mjfN2xS6 literal 7159 zcmai3XEa<vI?2oo<*AX$uJ|9 zbE)c;5wTPqLBNLDXyI`F_$sdFW^)=nesPzJh7-d7Tk-$I#4N)}`MRL7DXk^Ce63JJ z$@|x~Dt)%t|F(sSB|<)Jwbk{iJW~8(_(!4PBksrHCfo7KN9jac37>Zv1N(QM&AyBK zr6Q{qIyv=nqi|5paxTn8tu9Ps`T@6=^q%B)@V@A&uz#M=&%bz?S5^*F<32*K3>Lq3 zVXCYAgH*bFr5u6k<-WPOe*5v6)S&0*WP^jaVU`j396F&bw*nD7yp@`KZ zf_Bfei3C%9{_H8dcljj8=IFOlR=nC(1>f%J2`k5Joj9#YKV0weokY&;Ba0H6&ZavJ z-`kJUtqiVj-R64NcxCU+tIcgOuD!4d!s@}{U5PNqxU%EJs~0&lomZ&`ck18=J=A); zb?~oe*?p-K*30XWEGdh}g@sRLjkpq!)vYGro@xZx9t*R_g`PET$k5@)GQTZ9J^Ip3 zhi=jp_$_R~_P|jrsiCnAy?(ms>v?kAYr%H%_Zc&~C#(w6&{!%z0lH5u2k{X5QrdV? z>xSUJRZ<+M1Tb_)0bVO6@f26cL}69`JOaG|*3rf&If+G(*Y-T_=f3;wHgy@fWS)E& zHluGHXednEwVc-1VNG-0^Lk9BHQQqGkTFM?w(8#C9bi$K>jVvL?rb^II?Ez=s94`P z|DpD2@$y-3KQj|!lYV#<|FExhgWWbZY_vAw`%md~ed)g9;CYj2=Oa~3!(Ri{A-X8v zpx$XkRN%?|H7(bPeTDT$U2Rn5Pt08M4%Dr=hUddQ3bO}vgt~_!e--cCO+GX`r6@mF z4)W?sy!%7r`bKCnc77>HZbRWXU4Qf!i{94oamm_O@#VKk*x?6i_IbSE{(qqL@pX>6g6tMuMySM63bXjKKQW`zugF43{dpMj=NYW(uw%8RJ+=#W zI?~pKSmdm9yVR-Bt?)~To7;!c|EzB$#BF(@wND79z8upagtno+y&?wL1Om$oC)Mkz?W>meIH|OtG$Eu09UW5x;^+{3Pb=)_j+O07=RCt<= zvDJ3?+-3010YS=Mw`{sCT}H(Vj8IsLVh$DT(sSUnyxOSFSSV_e;wcryI^ye~*LfKWB*0r%2$vcM19va@SGLQVv5rKjb7b-dKIVwZtba{LV?Nauw z4~NDSM}5AC{0d;OV4IsBZW5tZ;O)rno#Li7l~M2ob00O-f|9b_@h@czYKS^W*^iiF z8Esz>@pwWp6&sLaJH&b$=P@Q_@`O+0$9gxKdvw2I16+{7=P&FjN*)+bsfk0h^`^>Y$s z1Jl{Rze6gqdDR;=?7fNt1SYa`L%)B^8jU_a>8lF|`!Pxs@J@1uy!=`;zD(PFYkhQm zhqhbq(~@455`m}W;==ln7oTFf1hALu97-*24?!-b8%C-e2;|NQ_$GFB(7W9kU59@N z)6IT0UUMUwXIY{111C|1&)!n|fcE;n)ld*K>*r#LNCb zNDed2a>}ny*$^chgGqo-gJcIKPu7@TPaB?3sSNzuW`>pPiJa;N)ml43RU@x~Hp$PD<*cS3_SGnAH+P{(iOgzWiuelm zy;)VB^g6EwzBe=8r-$}bJ#h>aWW=$aC%E=(R+|mAJJx?}+2nS&yHE|}r}<_O$y7J4+{c!?hJv;GL$Qx-qLBx5v2?txK2h=+ z#IJ9zS)3%_ZJg0of3vwC)xJ?KRVM)R#<{Bf{4r!98A36E&b~=zN8rh9apQ~mfI4Q& zl9EWHY{TE+aQnRZ(ZYtnlilpbM`D~A4WP5JcJ8<{ngMj24hFJ2N!k0C=cAl0K_eTZ zt8|s*nLV>nHyCjHRFUN@+2|^Xsh8`ja9RhsWa#$(OkM0^OMr3Y;K>6dp&Ht75<@Zu z!i%bN`?*JQ>S830vo~#l&QomI(bRIh+cRbQtH7p$vyzgKBnH%0<3Uf&ad9=e!JCdq z3GoodNF`M%Vg$Llzj0}f4?8ZoS{E893BBNrRYj*bm2z1af$&Yz8ZC2_;QFcc`ApgfE@m_6@j3NSowGS>vcBjyf(feK zguOKg%X=Epv;?eJ+yLx6jm>F-Z|6@5{qjzVj^P>QWv%w?sg#7s6!o6mOL9(b0!`1eQFzt7 z{wCnv&gTR~k6phrGQZ$|4P~nvHPiM!DZ~n_A z4>-!;5yStQTKT3*2Kz~0@txrO*UwltY>P7MX&8gB&2Mv=Hi}hpSgS2nMz!YE`f5Ef z{To&zQPW?zohc;Mu)k6;!g$Kb=dAW1yDb~x4;J!$>T4B-g1L<;8z3Gtz~?ZVwo}`` z=ZR*?Qcv>#U{5ZNoB%Q-6uz2$(=haqpperZP%$u8q0qYzlW+dzeMb5Y7yMbAqHDPV z(B&ZfhXSTXKyp*q95nLy;jE|on>S!zy+fOTNawa$7Y>dMfN84C=(Sq_a%JpNs?7K` zirCMB2yi#NhlHr9!=s0!G-)VgFvhdPgvzLtSD?9(#7 znZYH~qFt1M0SiOU-+rLWm4ie#d<9N|E^=bIWoiab4f_7G_PYrz?5(ra+nNWJai!Kc z3aAA~oIkx_?jG~w{0cA(e4zp1eZV@bu|{J=jG4VOfemZCpr|~y(d)%EkFFp$ZlJ*z zoLsvDr!_gZTSohkQ^p57p>ok9&S$YJ&5y|noJ*tq%AeBFvt6sx#r{cN`tjxMZ*B}V z{_5IGl{Fr$+g(bV$EEZyw^fc$uNf-4KfC9~T(opPy!jgNsMDM6eRCdt$MBo+Lwm(6 ziUmXZY%ch<kP$X5Z#1YG)4EL>;v@HohmWj$TvEO`G}z} zYVkwBeo&MYQ2CQ&A=sgbW-$xE#U&$-Z@>PagwI9ia<@<66W-Ac(9Vwddl_R5h!CBc zG3K{kL(FVG?M8#RxCzhOZWWJJ04VgU`hks8Ysy(&VTKk<4&!z~2Y_BQSX~)E1!xDw zjZhS5iqhhNJa9`oI76`NxlhZBOVLv^26!eH;Xb15DkKN2{*^+x6nt@)5w3)i&RHwt zQboiRTkG>>88hLm&*R^zDW)<%LjjT^#IZmUj4A#$wo7v>s@Dt)x+uDf{hLTspnLvV6%+(ehY}w; zmDXjAH9@v_To}8w#qmpTPoIYf&1}4u>wMgSLK%m!^H6Hvqp>?#&;ipCmB69~I= zP@-4DjsyVY4qWP|N}u$C6Pa zyr3P!P383j*$5`MBdZ8j*h!B!{YKGMTkJI?Nr;+j0dNJVw(v2(QWOtGChlmb^a6lg zC*Vy`187H|qcuIw&&>zKib3?2@B4d=I3f|tE=TS#Lb6{FoXnnV8(!p+{|LB5$;Az{ z2A+ikYYI^lb@B4v0??(6Z)6L!Nz#a(@Wm~_d`BeR`P(5|EYG6-v%-`dpfFoZx|R)= zhuTCF&VuQe4f!WSUSJRO1L?LT@x~wn`^b_8B1pf-g^J8Y=6W3qP?Ix+MF#y5GYXVs zzKgYC?AM?$J5&V5U}?^{KJMlrn%Mf;XlVM-kfb*+_j1mf#LdW(1kI9glLva(V}Ras zF{)ERm`uhjH$m8G=je1&Wr!^4y>g`>nKHEF2GZ3)d-^%Q6Cjra<`%`BR@x6yDmBy{ zHxkQrZYkWy{uxyWN-Ck&iKNU~( zWp)o*2#?mOWTYj1%yvQZY>#qXKaek|QheO(^CysR0;xjTK}l3c){!iQu@FM}trG|C zqY5YrCR2^jqeeTmTMjzX|*d>wip!(kJ z!p}ZGWEgbn^dT6EXIQ{dF&V4f$)GFc1>o6F*#N?qI-mq9NVB>2W%IJvD((_Ov>zC; zu{ehOwMC3t{exWrr>uB6A3XckT-Ko<(By@ar-Nz~l#rN2z7VMueFfhr{wL{>qc8VT za4)%7;@3;z(18rs?VL0QEcpD3zelnAO%41*Jxptp02w{oQt!yIhddsb>ZGuGBcG;& zCe_KaJv4fb1KcqwV3SW|970g^gl~qV3WF7Ipei19>cs%90ZzXU1|?=Vpp*nBPwXgP8yQTOq*RrTP3P>%daQQbea)PLeks}Xrr+6LA*Ka(u5c}R5qmD52lqO8pTp!r6U29u7)!;n}G=UUpKi0-g@d0#GkcZcR&@3mbP6tml39i)NU^5%gvTlEWZ)^j?U!1zv=LD z!cwU^j(5y)=1!4??g7o-RxJ@awi=BZ4I`dZMR1?u?4Yfu((sKL{p)@z`uM8nN^5c^K%u4`D8*BuHq@?_UtvzAyTb`C1qB-=ha`Nx9t3_oxpo6^h1 zLM2H4wb#om6&YhU*B0U$ZCn{SJPNu9LdmLBv_B-54aox;Io4ZZr9HA#Llq594)q}s z+cQB$`Ze5Z{@TNP4~)m;&mmcs2b5Y(vE&1i%W@XGUhgHH_fpNnD!&_Ng!Rvttj~Ri zNGcPl#OH#pRBRk(9sH(aeY|t8Fn{1(j_%`RG1ESs!f+A&cf!ONSwps)>!a zVZ*@Vw=oUde@rIMA9m=!>^7h6(R}C;9$^*&KBeOG^$(0(%Gjin-dU9vwE021SmV3A z3pQF;IZ#Oc!*hWuKZ1EpRWFD{=m*1t#}vz7#veSv>qt)Ccay~l3?j}M_W^Sb> zanl})m_^+3yBa@tnNLoh!B$qRt9@J-uDM^O)Yw_e5fK!-#Hhf<4~$C1tYDZ+VR*j2 z{AXn_JknafKU@GSfFq;bO$J00#NCOj=YfX{vw@|#%eclfp=l9mVYiTjtw?C3fz@>` z64MaIIvWz}K}O^ZXYc3Z0QPjEN28^=OFvS~3bHRx_2$Opi5)U+-2V1_n7e#)<_Bm& zPx&(X!U8W>knt#XH`GULXw_N{>k=#HTX{r7$qnCfh zx#>2(>CSNv;(8i+@0HuB6L)}((vj_K;ACWkoW_Gkca-~j4<3I0x4&E;JFSuX-cPN1 zPTh(Ela&}+8UF9VvIY8T5A@WKUU-)00i*sUX0gY_>-?{$-IJLMSGW$I2ivT{Oio1U z9K+mo%PCIl%UPt$X72CNc{zD~9%J>anzm~xLt4ovwS|KGIL&hCdKI7CUGl6pHbbD{R_;~c|h%se}} zrD8#cvGbkY-XL9k?27}j;imls*~h=d@@E$;iY=ulsFvNxE(CR+#E=Z4$~mA#@or`07naUm?|czbR&_QxkoW|i^+tLG?WF2;EYGN+Xu zq{gQryB(ta#Js$)LG(M@QD^CaMY$*DJ?*4ZBwR+z1Jj!7e9TGb-_M0z8I+;Gv^lR? z)8teeQF2KBc46zCd*)gEhIaC%ql<(yDSIs+^?_Cn;Kf;qd{u&}$2A5?Bs==_6Q>lD z{wGM17^~LvGEx5({$?LdGm?b%>LS}?NmC1#2Asc6Xn(J@63(7#_mS#17$zM&d#6xK zRZYf(wPmrB);c{Isu~g(iD{%>353^R*Ho(LUkQ;?-yx zwd~$Igjqu3uA7dwQG^J?w=jk!&+A2roChWuZy(m~Q(KKkC8DPWUb}`eKP5Kkiu(Ac ztUt)act5wB=+d~|3OuN7HtL$%QB0$rT6|^H@07(_lJNDBf7^$r+dNc$%~nDcwuQAx zsqVpgo+C)J1?jgwX)jscx&ElNw3f?%S@~`1<8Xo56>eiau0M1B+ZQO?|Gpn1&hT(C l{=<}~mvg`V-P`>?i}iJ^lzFK-(e&S({%sST25l$Qe*pGzPR;-T diff --git a/lib/pages/message/reply/view.dart b/lib/pages/message/reply/view.dart index 881f8650..9776c7f7 100644 --- a/lib/pages/message/reply/view.dart +++ b/lib/pages/message/reply/view.dart @@ -196,7 +196,7 @@ class ReplyItem extends StatelessWidget { style: TextStyle(color: outline), ), const SizedBox(width: 16), - Text('回复', style: TextStyle(color: outline)), + // Text('回复', style: TextStyle(color: outline)), ], ) ], From 0031b229021429318174eed48303312837c89499 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 26 Jun 2024 22:18:49 +0800 Subject: [PATCH 256/349] =?UTF-8?q?mod:=20=E6=9C=AA=E5=BC=80=E5=8F=91?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E5=85=B3=E9=97=AD=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/controller.dart | 2 +- lib/pages/whisper/view.dart | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/pages/whisper/controller.dart b/lib/pages/whisper/controller.dart index 2614bf5a..749a3482 100644 --- a/lib/pages/whisper/controller.dart +++ b/lib/pages/whisper/controller.dart @@ -17,7 +17,7 @@ class WhisperController extends GetxController { }, { 'icon': Icons.alternate_email, - 'title': '@ 我的', + 'title': '@我的', 'path': '/messageAt', 'count': 0, }, diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index e31e942e..9436e2be 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -1,5 +1,6 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/skeleton/skeleton.dart'; @@ -70,6 +71,11 @@ class _WhisperPageState extends State { ..._whisperController.noticesList.map((element) { return InkWell( onTap: () { + if (['/messageAt', '/messageSystem'] + .contains(element['path'])) { + SmartDialog.showToast('功能开发中'); + return; + } Get.toNamed(element['path']); if (element['count'] > 0) { From dbfc31a1dff498d11d4e76f8fc3ec1f5f88ea728 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 26 Jun 2024 22:29:11 +0800 Subject: [PATCH 257/349] =?UTF-8?q?v1.0.24=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.24.0626.md | 23 +++++++++++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 change_log/1.0.24.0626.md diff --git a/change_log/1.0.24.0626.md b/change_log/1.0.24.0626.md new file mode 100644 index 00000000..d9a8892f --- /dev/null +++ b/change_log/1.0.24.0626.md @@ -0,0 +1,23 @@ +## 1.0.24 + +### 功能 ++ 私信功能 ++ 回复我的、收到的赞查看 ++ 新的登录方式 ++ 全屏选集 ++ 一键三连 ++ 按分区搜索 + +### 优化 ++ 页面跳转动画 ++ 评论区跳转 + +### 修复 ++ 音画不同步问题 ++ 分集字幕未同步 ++ 多语言字幕 ++ 弹幕设置未生效 ++ + + +问题反馈、功能建议请查看「关于」页面。 diff --git a/pubspec.yaml b/pubspec.yaml index 66187abf..09169473 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.23+1023 +version: 1.0.24+1024 environment: sdk: ">=3.0.0 <4.0.0" From 7301673783dfde6c6ef6114a77ff57cc4db8e782 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 28 Jun 2024 23:24:42 +0800 Subject: [PATCH 258/349] =?UTF-8?q?mod:=20=E8=A7=86=E9=A2=91=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E5=8C=BA=E5=9B=BE=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 21 +++++++++++-------- .../introduction/widgets/action_item.dart | 1 + 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 93cc26c9..1e8d97f1 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -2,6 +2,7 @@ import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; @@ -629,11 +630,12 @@ class _VideoInfoState extends State with TickerProviderStateMixin { child: Icon( key: ValueKey(likeStatus), likeStatus - ? Icons.thumb_up - : Icons.thumb_up_alt_outlined, + ? FontAwesomeIcons.solidThumbsUp + : FontAwesomeIcons.thumbsUp, color: likeStatus ? colorScheme.primary : colorScheme.outline, + size: 21, ), ), const SizedBox(height: 6), @@ -663,7 +665,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { (const IconThemeData.fallback().size! + 5) / 2, child: progressWidget(_progress)), ActionItem( - icon: Image.asset('assets/images/coin.png', width: 30), + icon: const Icon(FontAwesomeIcons.b), + selectIcon: const Icon(FontAwesomeIcons.b), onTap: handleState(videoIntroController.actionCoinVideo), selectStatus: videoIntroController.hasCoin.value, text: widget.videoDetail!.stat!.coin!.toString(), @@ -681,8 +684,8 @@ class _VideoInfoState extends State with TickerProviderStateMixin { (const IconThemeData.fallback().size! + 5) / 2, child: progressWidget(_progress)), ActionItem( - icon: const Icon(Icons.star_border), - selectIcon: const Icon(Icons.star), + icon: const Icon(FontAwesomeIcons.star), + selectIcon: const Icon(FontAwesomeIcons.solidStar), onTap: () => showFavBottomSheet(), onLongPress: () => showFavBottomSheet(type: 'longPress'), selectStatus: videoIntroController.hasFav.value, @@ -692,7 +695,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { ), ), 'watchLater': ActionItem( - icon: const Icon(Icons.watch_later_outlined), + icon: const Icon(FontAwesomeIcons.clock), onTap: () async { final res = await UserHttp.toViewLater(bvid: widget.videoDetail!.bvid); @@ -702,15 +705,15 @@ class _VideoInfoState extends State with TickerProviderStateMixin { text: '稍后看', ), 'share': ActionItem( - icon: const Icon(Icons.share), + icon: const Icon(FontAwesomeIcons.shareFromSquare), onTap: () => videoIntroController.actionShareVideo(), selectStatus: false, text: '分享', ), 'dislike': Obx( () => ActionItem( - icon: const Icon(Icons.thumb_down_alt_outlined), - selectIcon: const Icon(Icons.thumb_down), + icon: const Icon(FontAwesomeIcons.thumbsDown), + selectIcon: const Icon(FontAwesomeIcons.solidThumbsDown), onTap: () {}, selectStatus: videoIntroController.hasDisLike.value, text: '不喜欢', diff --git a/lib/pages/video/detail/introduction/widgets/action_item.dart b/lib/pages/video/detail/introduction/widgets/action_item.dart index 3288b2fa..fd0b9fef 100644 --- a/lib/pages/video/detail/introduction/widgets/action_item.dart +++ b/lib/pages/video/detail/introduction/widgets/action_item.dart @@ -51,6 +51,7 @@ class ActionItem extends StatelessWidget { color: selectStatus ? Theme.of(context).colorScheme.primary : Theme.of(context).colorScheme.outline, + size: 20, ) : Image.asset( key: ValueKey(selectStatus), From 6a28ccbf64f1f56382209ddfb84bf41ed8e49b52 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 28 Jun 2024 23:44:48 +0800 Subject: [PATCH 259/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 02d004e8..ebb266cd 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -700,9 +700,10 @@ InlineSpan buildContent( ); // 只显示一次 matchedStrs.add(matchStr); - } else if (content - .topicsMeta[matchStr.substring(1, matchStr.length - 1)] != - null) { + } else if (content.topicsMeta.keys.isNotEmpty && + matchStr.length > 1 && + content.topicsMeta[matchStr.substring(1, matchStr.length - 1)] != + null) { spanChilds.add( TextSpan( text: matchStr, From a63a9a28d7df50d38a359373fc9dcd2a680a4e3f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 29 Jun 2024 13:53:43 +0800 Subject: [PATCH 260/349] =?UTF-8?q?feat:=20=E6=9C=80=E8=BF=91=E7=82=B9?= =?UTF-8?q?=E8=B5=9E=E7=9A=84=E8=A7=86=E9=A2=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/member.dart | 5 +- lib/models/member/like.dart | 210 ++++++++++++++++++++++++ lib/pages/member/controller.dart | 10 ++ lib/pages/member/view.dart | 62 +++++-- lib/pages/member/widgets/conis.dart | 8 +- lib/pages/member/widgets/like.dart | 31 ++++ lib/pages/member_like/widgets/item.dart | 96 +++++++++++ 7 files changed, 400 insertions(+), 22 deletions(-) create mode 100644 lib/models/member/like.dart create mode 100644 lib/pages/member/widgets/like.dart create mode 100644 lib/pages/member_like/widgets/item.dart diff --git a/lib/http/member.dart b/lib/http/member.dart index 1af0f9a4..20a2c728 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -1,5 +1,6 @@ import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/models/member/like.dart'; import '../common/constants.dart'; import '../models/dynamics/result.dart'; import '../models/follow/result.dart'; @@ -328,7 +329,9 @@ class MemberHttp { if (res.data['code'] == 0) { return { 'status': true, - 'data': MemberSeasonsDataModel.fromJson(res.data['data']['items_lists']) + 'data': res.data['data']['list'] + .map((e) => MemberLikeDataModel.fromJson(e)) + .toList(), }; } else { return { diff --git a/lib/models/member/like.dart b/lib/models/member/like.dart new file mode 100644 index 00000000..df71e2ec --- /dev/null +++ b/lib/models/member/like.dart @@ -0,0 +1,210 @@ +class MemberLikeDataModel { + MemberLikeDataModel({ + this.aid, + this.videos, + this.tid, + this.tname, + this.pic, + this.title, + this.pubdate, + this.ctime, + this.desc, + this.state, + this.duration, + this.redirectUrl, + this.rights, + this.owner, + this.stat, + this.dimension, + this.cover43, + this.bvid, + this.interVideo, + this.resourceType, + this.subtitle, + this.enableVt, + }); + + final int? aid; + final int? videos; + final int? tid; + final String? tname; + final String? pic; + final String? title; + final int? pubdate; + final int? ctime; + final String? desc; + final int? state; + final int? duration; + final String? redirectUrl; + final Rights? rights; + final Owner? owner; + final Stat? stat; + final Dimension? dimension; + final String? cover43; + final String? bvid; + final bool? interVideo; + final String? resourceType; + final String? subtitle; + final int? enableVt; + + factory MemberLikeDataModel.fromJson(Map json) => + MemberLikeDataModel( + aid: json["aid"], + videos: json["videos"], + tid: json["tid"], + tname: json["tname"], + pic: json["pic"], + title: json["title"], + pubdate: json["pubdate"], + ctime: json["ctime"], + desc: json["desc"], + state: json["state"], + duration: json["duration"], + redirectUrl: json["redirect_url"], + rights: Rights.fromJson(json["rights"]), + owner: Owner.fromJson(json["owner"]), + stat: Stat.fromJson(json["stat"]), + dimension: Dimension.fromJson(json["dimension"]), + cover43: json["cover43"], + bvid: json["bvid"], + interVideo: json["inter_video"], + resourceType: json["resource_type"], + subtitle: json["subtitle"], + enableVt: json["enable_vt"], + ); +} + +class Dimension { + Dimension({ + required this.width, + required this.height, + required this.rotate, + }); + + final int width; + final int height; + final int rotate; + + factory Dimension.fromJson(Map json) => Dimension( + width: json["width"], + height: json["height"], + rotate: json["rotate"], + ); +} + +class Owner { + Owner({ + required this.mid, + required this.name, + required this.face, + }); + + final int mid; + final String name; + final String face; + + factory Owner.fromJson(Map json) => Owner( + mid: json["mid"], + name: json["name"], + face: json["face"], + ); +} + +class Rights { + Rights({ + required this.bp, + required this.elec, + required this.download, + required this.movie, + required this.pay, + required this.hd5, + required this.noReprint, + required this.autoplay, + required this.ugcPay, + required this.isCooperation, + required this.ugcPayPreview, + required this.noBackground, + required this.arcPay, + required this.payFreeWatch, + }); + + final int bp; + final int elec; + final int download; + final int movie; + final int pay; + final int hd5; + final int noReprint; + final int autoplay; + final int ugcPay; + final int isCooperation; + final int ugcPayPreview; + final int noBackground; + final int arcPay; + final int payFreeWatch; + + factory Rights.fromJson(Map json) => Rights( + bp: json["bp"], + elec: json["elec"], + download: json["download"], + movie: json["movie"], + pay: json["pay"], + hd5: json["hd5"], + noReprint: json["no_reprint"], + autoplay: json["autoplay"], + ugcPay: json["ugc_pay"], + isCooperation: json["is_cooperation"], + ugcPayPreview: json["ugc_pay_preview"], + noBackground: json["no_background"], + arcPay: json["arc_pay"], + payFreeWatch: json["pay_free_watch"], + ); +} + +class Stat { + Stat({ + required this.aid, + required this.view, + required this.danmaku, + required this.reply, + required this.favorite, + required this.coin, + required this.share, + required this.nowRank, + required this.hisRank, + required this.like, + required this.dislike, + required this.vt, + required this.vv, + }); + + final int aid; + final int view; + final int danmaku; + final int reply; + final int favorite; + final int coin; + final int share; + final int nowRank; + final int hisRank; + final int like; + final int dislike; + final int vt; + final int vv; + + factory Stat.fromJson(Map json) => Stat( + aid: json["aid"], + view: json["view"], + danmaku: json["danmaku"], + reply: json["reply"], + favorite: json["favorite"], + coin: json["coin"], + share: json["share"], + nowRank: json["now_rank"], + hisRank: json["his_rank"], + like: json["like"], + dislike: json["dislike"], + vt: json["vt"], + vv: json["vv"], + ); +} diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 0aa7166f..7db046d4 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -8,6 +8,7 @@ import 'package:pilipala/http/video.dart'; import 'package:pilipala/models/member/archive.dart'; import 'package:pilipala/models/member/coin.dart'; import 'package:pilipala/models/member/info.dart'; +import 'package:pilipala/models/member/like.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:share_plus/share_plus.dart'; @@ -25,6 +26,7 @@ class MemberController extends GetxController { RxInt attribute = (-1).obs; RxString attributeText = '关注'.obs; RxList recentCoinsList = [].obs; + RxList recentLikeList = [].obs; @override void onInit() { @@ -208,6 +210,14 @@ class MemberController extends GetxController { return res; } + // 请求点赞视频 + Future getRecentLikeVideo() async { + if (userInfo == null) return; + var res = await MemberHttp.getRecentLikeVideo(mid: mid); + recentLikeList.value = res['data']; + return res; + } + // 跳转查看动态 void pushDynamicsPage() => Get.toNamed('/memberDynamics?mid=$mid'); diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index bb0d92be..9c0da652 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -9,6 +9,7 @@ import 'package:pilipala/pages/member/index.dart'; import 'package:pilipala/utils/utils.dart'; import 'widgets/conis.dart'; +import 'widgets/like.dart'; import 'widgets/profile.dart'; import 'widgets/seasons.dart'; @@ -26,6 +27,7 @@ class _MemberPageState extends State late Future _futureBuilderFuture; late Future _memberSeasonsFuture; late Future _memberCoinsFuture; + late Future _memberLikeFuture; final ScrollController _extendNestCtr = ScrollController(); final StreamController appbarStream = StreamController(); late int mid; @@ -39,6 +41,7 @@ class _MemberPageState extends State _futureBuilderFuture = _memberController.getInfo(); _memberSeasonsFuture = _memberController.getMemberSeasons(); _memberCoinsFuture = _memberController.getRecentCoinVideo(); + _memberLikeFuture = _memberController.getRecentLikeVideo(); _extendNestCtr.addListener( () { final double offset = _extendNestCtr.position.pixels; @@ -162,6 +165,7 @@ class _MemberPageState extends State trailing: const Icon(Icons.arrow_forward_outlined, size: 19), ), + const Divider(height: 1, thickness: 0.1), /// 视频 ListTile( @@ -170,12 +174,10 @@ class _MemberPageState extends State trailing: const Icon(Icons.arrow_forward_outlined, size: 19), ), + const Divider(height: 1, thickness: 0.1), /// 专栏 - ListTile( - onTap: () {}, - title: const Text('Ta的专栏'), - ), + const ListTile(title: Text('Ta的专栏')), MediaQuery.removePadding( removeTop: true, removeBottom: true, @@ -218,12 +220,7 @@ class _MemberPageState extends State /// 最近投币 Obx( () => _memberController.recentCoinsList.isNotEmpty - ? ListTile( - onTap: () {}, - title: const Text('最近投币的视频'), - // trailing: const Icon(Icons.arrow_forward_outlined, - // size: 19), - ) + ? const ListTile(title: Text('最近投币的视频')) : const SizedBox(), ), MediaQuery.removePadding( @@ -257,13 +254,44 @@ class _MemberPageState extends State ), ), ), - // 最近点赞 - // ListTile( - // onTap: () {}, - // title: const Text('最近点赞的视频'), - // trailing: - // const Icon(Icons.arrow_forward_outlined, size: 19), - // ), + + /// 最近点赞 + Obx( + () => _memberController.recentLikeList.isNotEmpty + ? const ListTile(title: Text('最近点赞的视频')) + : const SizedBox(), + ), + MediaQuery.removePadding( + removeTop: true, + removeBottom: true, + context: context, + child: Padding( + padding: const EdgeInsets.only( + left: StyleString.safeSpace, + right: StyleString.safeSpace, + ), + child: FutureBuilder( + future: _memberLikeFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + return MemberLikePanel(data: data['data']); + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ), ], ), ), diff --git a/lib/pages/member/widgets/conis.dart b/lib/pages/member/widgets/conis.dart index 57a8a583..bdff2120 100644 --- a/lib/pages/member/widgets/conis.dart +++ b/lib/pages/member/widgets/conis.dart @@ -4,8 +4,8 @@ import 'package:pilipala/models/member/coin.dart'; import 'package:pilipala/pages/member_coin/widgets/item.dart'; class MemberCoinsPanel extends StatelessWidget { - final List? data; - const MemberCoinsPanel({super.key, this.data}); + final List data; + const MemberCoinsPanel({super.key, required this.data}); @override Widget build(BuildContext context) { @@ -20,9 +20,9 @@ class MemberCoinsPanel extends StatelessWidget { ), physics: const NeverScrollableScrollPhysics(), shrinkWrap: true, - itemCount: data!.length, + itemCount: data.length, itemBuilder: (context, i) { - return MemberCoinsItem(coinItem: data![i]); + return MemberCoinsItem(coinItem: data[i]); }, ); }, diff --git a/lib/pages/member/widgets/like.dart b/lib/pages/member/widgets/like.dart new file mode 100644 index 00000000..6342c274 --- /dev/null +++ b/lib/pages/member/widgets/like.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/models/member/like.dart'; +import 'package:pilipala/pages/member_like/widgets/item.dart'; + +class MemberLikePanel extends StatelessWidget { + final List data; + const MemberLikePanel({super.key, required this.data}); + + @override + Widget build(BuildContext context) { + return LayoutBuilder( + builder: (context, boxConstraints) { + return GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, // Use a fixed count for GridView + crossAxisSpacing: StyleString.safeSpace, + mainAxisSpacing: StyleString.safeSpace, + childAspectRatio: 0.94, + ), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: data.length, + itemBuilder: (context, i) { + return MemberLikeItem(likeItem: data[i]); + }, + ); + }, + ); + } +} diff --git a/lib/pages/member_like/widgets/item.dart b/lib/pages/member_like/widgets/item.dart new file mode 100644 index 00000000..57798bb7 --- /dev/null +++ b/lib/pages/member_like/widgets/item.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/constants.dart'; +import 'package:pilipala/common/widgets/badge.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/common/widgets/stat/view.dart'; +import 'package:pilipala/http/search.dart'; +import 'package:pilipala/models/member/like.dart'; +import 'package:pilipala/utils/utils.dart'; + +class MemberLikeItem extends StatelessWidget { + final MemberLikeDataModel likeItem; + + const MemberLikeItem({ + Key? key, + required this.likeItem, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + String heroTag = Utils.makeHeroTag(likeItem.aid); + return Card( + elevation: 0, + clipBehavior: Clip.hardEdge, + margin: EdgeInsets.zero, + child: InkWell( + onTap: () async { + int cid = + await SearchHttp.ab2c(aid: likeItem.aid, bvid: likeItem.bvid); + Get.toNamed('/video?bvid=${likeItem.bvid}&cid=$cid', + arguments: {'videoItem': likeItem, 'heroTag': heroTag}); + }, + child: Column( + children: [ + AspectRatio( + aspectRatio: StyleString.aspectRatio, + child: LayoutBuilder(builder: (context, boxConstraints) { + double maxWidth = boxConstraints.maxWidth; + double maxHeight = boxConstraints.maxHeight; + return Stack( + children: [ + NetworkImgLayer( + src: likeItem.pic, + width: maxWidth, + height: maxHeight, + ), + if (likeItem.duration != null) + PBadge( + bottom: 6, + right: 6, + type: 'gray', + text: Utils.timeFormat(likeItem.duration), + ) + ], + ); + }), + ), + Padding( + padding: const EdgeInsets.fromLTRB(5, 6, 0, 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + likeItem.title!, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + const SizedBox(height: 4), + Row( + children: [ + StatView( + view: likeItem.stat!.view, + theme: 'gray', + ), + const Spacer(), + Text( + Utils.CustomStamp_str( + timestamp: likeItem.pubdate, date: 'MM-DD'), + style: TextStyle( + fontSize: 11, + color: Theme.of(context).colorScheme.outline, + ), + ), + const SizedBox(width: 6) + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} From ea7ae15384660071505332282154a6d8f2037ed9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 29 Jun 2024 14:29:43 +0800 Subject: [PATCH 261/349] =?UTF-8?q?mod:=20=E5=90=88=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/controller.dart | 7 +++- lib/pages/member/view.dart | 52 +++++++++++++-------------- lib/pages/member/widgets/seasons.dart | 44 +++++++++++++---------- lib/pages/member_seasons/view.dart | 3 +- 4 files changed, 58 insertions(+), 48 deletions(-) diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index 7db046d4..cc928a8d 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -192,12 +192,17 @@ class MemberController extends GetxController { Share.share('${memberInfo.value.name} - https://space.bilibili.com/$mid'); } - // 请求专栏 + // 请求合集 Future getMemberSeasons() async { if (userInfo == null) return; var res = await MemberHttp.getMemberSeasons(mid, 1, 10); if (!res['status']) { SmartDialog.showToast("用户专栏请求异常:${res['msg']}"); + } else { + // 只取前四个专栏 + res['data'].seasonsList.map((e) { + e.archives = e.archives!.sublist(0, 4); + }).toList(); } return res; } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 9c0da652..f62ffacc 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -178,39 +178,37 @@ class _MemberPageState extends State /// 专栏 const ListTile(title: Text('Ta的专栏')), + const Divider(height: 1, thickness: 0.1), + + /// 合集 + const ListTile(title: Text('Ta的合集')), MediaQuery.removePadding( removeTop: true, removeBottom: true, context: context, - child: Padding( - padding: const EdgeInsets.only( - left: StyleString.safeSpace, - right: StyleString.safeSpace, - ), - child: FutureBuilder( - future: _memberSeasonsFuture, - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.done) { - if (snapshot.data == null) { - return const SizedBox(); - } - if (snapshot.data['status']) { - Map data = snapshot.data as Map; - if (data['data'].seasonsList.isEmpty) { - return commenWidget('用户没有设置专栏'); - } else { - return MemberSeasonsPanel(data: data['data']); - } - } else { - // 请求错误 - return const SizedBox(); - } - } else { + child: FutureBuilder( + future: _memberSeasonsFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + if (snapshot.data == null) { return const SizedBox(); } - }, - ), + if (snapshot.data['status']) { + Map data = snapshot.data as Map; + if (data['data'].seasonsList.isEmpty) { + return commenWidget('用户没有设置合集'); + } else { + return MemberSeasonsPanel(data: data['data']); + } + } else { + // 请求错误 + return const SizedBox(); + } + } else { + return const SizedBox(); + } + }, ), ), diff --git a/lib/pages/member/widgets/seasons.dart b/lib/pages/member/widgets/seasons.dart index 125c978f..1367d6bd 100644 --- a/lib/pages/member/widgets/seasons.dart +++ b/lib/pages/member/widgets/seasons.dart @@ -25,7 +25,7 @@ class MemberSeasonsPanel extends StatelessWidget { children: [ ListTile( onTap: () => Get.toNamed( - '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}'), + '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}&seasonName=${item.meta!.name}'), title: Text( item.meta!.name!, maxLines: 1, @@ -44,24 +44,30 @@ class MemberSeasonsPanel extends StatelessWidget { ), ), const SizedBox(height: 10), - LayoutBuilder( - builder: (context, boxConstraints) { - return GridView.builder( - gridDelegate: - const SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: 2, // Use a fixed count for GridView - crossAxisSpacing: StyleString.safeSpace, - mainAxisSpacing: StyleString.safeSpace, - childAspectRatio: 0.94, - ), - physics: const NeverScrollableScrollPhysics(), - shrinkWrap: true, - itemCount: item.archives!.length, - itemBuilder: (context, i) { - return MemberSeasonsItem(seasonItem: item.archives![i]); - }, - ); - }, + Padding( + padding: const EdgeInsets.only( + left: StyleString.safeSpace, + right: StyleString.safeSpace, + ), + child: LayoutBuilder( + builder: (context, boxConstraints) { + return GridView.builder( + gridDelegate: + const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, // Use a fixed count for GridView + crossAxisSpacing: StyleString.safeSpace, + mainAxisSpacing: StyleString.safeSpace, + childAspectRatio: 0.94, + ), + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: item.archives!.length, + itemBuilder: (context, i) { + return MemberSeasonsItem(seasonItem: item.archives![i]); + }, + ); + }, + ), ), ], ), diff --git a/lib/pages/member_seasons/view.dart b/lib/pages/member_seasons/view.dart index 06944f10..556e2ec5 100644 --- a/lib/pages/member_seasons/view.dart +++ b/lib/pages/member_seasons/view.dart @@ -43,7 +43,8 @@ class _MemberSeasonsPageState extends State { appBar: AppBar( titleSpacing: 0, centerTitle: false, - title: Text('他的专栏', style: Theme.of(context).textTheme.titleMedium), + title: Text(Get.parameters['seasonName']!, + style: Theme.of(context).textTheme.titleMedium), ), body: Padding( padding: const EdgeInsets.only( From eaae622f95b582bfd1f511e0ce1f70ecd08514ba Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 29 Jun 2024 17:12:06 +0800 Subject: [PATCH 262/349] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/common/subtitle_type.dart | 95 ------------------- lib/models/video/subTitile/result.dart | 8 -- lib/pages/video/detail/controller.dart | 9 -- .../video/detail/introduction/controller.dart | 3 +- .../video/detail/widgets/header_control.dart | 9 +- lib/plugin/pl_player/controller.dart | 27 +----- 6 files changed, 11 insertions(+), 140 deletions(-) delete mode 100644 lib/models/common/subtitle_type.dart diff --git a/lib/models/common/subtitle_type.dart b/lib/models/common/subtitle_type.dart deleted file mode 100644 index ac3ee3e0..00000000 --- a/lib/models/common/subtitle_type.dart +++ /dev/null @@ -1,95 +0,0 @@ -enum SubtitleType { - // 中文(中国) - zhCN, - // 中文(自动翻译) - aizh, - // 英语(自动生成) - aien, - // 中文(简体) - zhHans, - // 英文(美国) - enUS, - // 中文繁体 - zhTW, - // - en, - // - pt, - // - es, -} - -extension SubtitleTypeExtension on SubtitleType { - String get description { - switch (this) { - case SubtitleType.zhCN: - return '中文(中国)'; - case SubtitleType.aizh: - return '中文(自动翻译)'; - case SubtitleType.aien: - return '英语(自动生成)'; - case SubtitleType.zhHans: - return '中文(简体)'; - case SubtitleType.enUS: - return '英文(美国)'; - case SubtitleType.zhTW: - return '中文(繁体)'; - case SubtitleType.en: - return '英文'; - case SubtitleType.pt: - return '葡萄牙语'; - case SubtitleType.es: - return '西班牙语'; - } - } -} - -extension SubtitleIdExtension on SubtitleType { - String get id { - switch (this) { - case SubtitleType.zhCN: - return 'zh-CN'; - case SubtitleType.aizh: - return 'ai-zh'; - case SubtitleType.aien: - return 'ai-en'; - case SubtitleType.zhHans: - return 'zh-Hans'; - case SubtitleType.enUS: - return 'en-US'; - case SubtitleType.zhTW: - return 'zh-TW'; - case SubtitleType.en: - return 'en'; - case SubtitleType.pt: - return 'pt'; - case SubtitleType.es: - return 'es'; - } - } -} - -extension SubtitleCodeExtension on SubtitleType { - int get code { - switch (this) { - case SubtitleType.zhCN: - return 1; - case SubtitleType.aizh: - return 2; - case SubtitleType.aien: - return 3; - case SubtitleType.zhHans: - return 4; - case SubtitleType.enUS: - return 5; - case SubtitleType.zhTW: - return 6; - case SubtitleType.en: - return 7; - case SubtitleType.pt: - return 8; - case SubtitleType.es: - return 9; - } - } -} diff --git a/lib/models/video/subTitile/result.dart b/lib/models/video/subTitile/result.dart index d3e32e55..e137439f 100644 --- a/lib/models/video/subTitile/result.dart +++ b/lib/models/video/subTitile/result.dart @@ -1,6 +1,3 @@ -import 'package:get/get.dart'; -import '../../common/subtitle_type.dart'; - class SubTitlteModel { SubTitlteModel({ this.aid, @@ -78,11 +75,6 @@ class SubTitlteItemModel { aiType: json["ai_type"], aiStatus: json["ai_status"], title: json["lan_doc"], - code: SubtitleType.values - .firstWhereOrNull( - (element) => element.id.toString() == json["lan"]) - ?.index ?? - -1, content: '', body: [], ); diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index ea85a5b9..901c4014 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -446,15 +446,6 @@ class VideoDetailController extends GetxController } } - // 获取字幕内容 - // Future getSubtitleContent(String url) async { - // var res = await Request().get('https:$url'); - // subtitleContents.value = res.data['body'].map((e) { - // return SubTitileContentModel.fromJson(e); - // }).toList(); - // setSubtitleContent(); - // } - setSubtitleContent() { plPlayerController.subtitleContent.value = ''; plPlayerController.subtitles.value = subtitles; diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 1a1b1b74..50aac4cd 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -435,7 +435,8 @@ class VideoIntroController extends GetxController { videoDetailCtr.danmakuCid.value = cid; videoDetailCtr.cover.value = cover; videoDetailCtr.queryVideoUrl(); - videoDetailCtr.getSubtitle(); + videoDetailCtr.clearSubtitleContent(); + await videoDetailCtr.getSubtitle(); videoDetailCtr.setSubtitleContent(); // 重新请求评论 try { diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 5072377a..45a9684a 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -426,7 +426,12 @@ class _HeaderControlState extends State { /// 选择字幕 void showSubtitleDialog() async { int tempThemeValue = widget.controller!.subTitleCode.value; - int len = widget.videoDetailCtr!.subtitles.length; + final List subtitles = widget.videoDetailCtr!.subtitles; + int len = subtitles.length; + if (subtitles.firstWhereOrNull((element) => element.id == tempThemeValue) == + null) { + tempThemeValue = -1; + } showDialog( context: context, builder: (BuildContext context) { @@ -458,7 +463,7 @@ class _HeaderControlState extends State { ), ...widget.videoDetailCtr!.subtitles .map((e) => RadioListTile( - value: e.code, + value: e.id, title: Text(e.title), groupValue: tempThemeValue, onChanged: (value) { diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index a614d75d..a5de33fd 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -123,7 +123,7 @@ class PlPlayerController { PreferredSizeWidget? headerControl; PreferredSizeWidget? bottomControl; Widget? danmuWidget; - late RxList subtitles; + RxList subtitles = [].obs; String videoType = 'archive'; /// 数据加载监听 @@ -642,10 +642,6 @@ class PlPlayerController { const Duration(seconds: 1), () => videoPlayerServiceHandler.onPositionChange(event)); }), - - // onSubTitleOpenChanged.listen((bool event) { - // toggleSubtitle(event ? subTitleCode.value : -1); - // }) ], ); } @@ -1049,25 +1045,6 @@ class PlPlayerController { void toggleSubtitle(int code) { _subTitleOpen.value = code != -1; _subTitleCode.value = code; - // if (code == -1) { - // // 关闭字幕 - // _subTitleOpen.value = false; - // _subTitleCode.value = code; - // _videoPlayerController?.setSubtitleTrack(SubtitleTrack.no()); - // return; - // } - // final SubTitlteItemModel? subtitle = subtitles?.firstWhereOrNull( - // (element) => element.code == code, - // ); - // _subTitleOpen.value = true; - // _subTitleCode.value = code; - // _videoPlayerController?.setSubtitleTrack( - // SubtitleTrack.data( - // subtitle!.content!, - // title: subtitle.title, - // language: subtitle.lan, - // ), - // ); } void querySubtitleContent(double progress) { @@ -1079,7 +1056,7 @@ class PlPlayerController { return; } final SubTitlteItemModel? subtitle = subtitles.firstWhereOrNull( - (element) => element.code == subTitleCode.value, + (element) => element.id == subTitleCode.value, ); if (subtitle != null && subtitle.body!.isNotEmpty) { for (var content in subtitle.body!) { From de86dba39dee578a77c54c938397552cc962f56d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 29 Jun 2024 18:47:04 +0800 Subject: [PATCH 263/349] =?UTF-8?q?feat:=20=E6=B8=AF=E6=BE=B3=E5=8F=B0?= =?UTF-8?q?=E6=A8=A1=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/constants.dart | 1 + lib/http/init.dart | 40 ++++++++++++++++++++--------- lib/pages/setting/play_setting.dart | 9 +++++++ lib/utils/storage.dart | 2 ++ 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/lib/http/constants.dart b/lib/http/constants.dart index 3d749ee8..cf6e00f2 100644 --- a/lib/http/constants.dart +++ b/lib/http/constants.dart @@ -5,6 +5,7 @@ class HttpString { static const String appBaseUrl = 'https://app.bilibili.com'; static const String liveBaseUrl = 'https://api.live.bilibili.com'; static const String passBaseUrl = 'https://passport.bilibili.com'; + static const String bangumiBaseUrl = 'https://bili.meark.me'; static const List validateStatusCodes = [ 302, 304, diff --git a/lib/http/init.dart b/lib/http/init.dart index a0b36369..cb9d6f39 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -27,11 +27,13 @@ class Request { late bool enableSystemProxy; late String systemProxyHost; late String systemProxyPort; - static final RegExp spmPrefixExp = RegExp(r''); + static final RegExp spmPrefixExp = + RegExp(r''); /// 设置cookie static setCookie() async { Box userInfoCache = GStrorage.userInfo; + Box setting = GStrorage.setting; final String cookiePath = await Utils.getCookiePath(); final PersistCookieJar cookieJar = PersistCookieJar( ignoreExpires: true, @@ -54,7 +56,11 @@ class Request { } } setOptionsHeaders(userInfo, userInfo != null && userInfo.mid != null); - + String baseUrlType = 'default'; + if (setting.get(SettingBoxKey.enableGATMode, defaultValue: false)) { + baseUrlType = 'bangumi'; + } + setBaseUrl(type: baseUrlType); try { await buvidActivate(); } catch (e) { @@ -95,11 +101,10 @@ class Request { String spmPrefix = spmPrefixExp.firstMatch(html.data)!.group(1)!; Random rand = Random(); String rand_png_end = base64.encode( - List.generate(32, (_) => rand.nextInt(256)) + - List.filled(4, 0) + - [73, 69, 78, 68] + - List.generate(4, (_) => rand.nextInt(256)) - ); + List.generate(32, (_) => rand.nextInt(256)) + + List.filled(4, 0) + + [73, 69, 78, 68] + + List.generate(4, (_) => rand.nextInt(256))); String jsonData = json.encode({ '3064': 1, @@ -110,11 +115,9 @@ class Request { }, }); - await Request().post( - Api.activateBuvidApi, - data: {'payload': jsonData}, - options: Options(contentType: 'application/json') - ); + await Request().post(Api.activateBuvidApi, + data: {'payload': jsonData}, + options: Options(contentType: 'application/json')); } /* @@ -294,4 +297,17 @@ class Request { } return headerUa; } + + static setBaseUrl({String type = 'default'}) { + switch (type) { + case 'default': + dio.options.baseUrl = HttpString.apiBaseUrl; + break; + case 'bangumi': + dio.options.baseUrl = HttpString.bangumiBaseUrl; + break; + default: + dio.options.baseUrl = HttpString.apiBaseUrl; + } + } } diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index 0f7dcdc3..cb8a3996 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/http/init.dart'; import 'package:pilipala/models/video/play/ao_output.dart'; import 'package:pilipala/models/video/play/quality.dart'; import 'package:pilipala/pages/setting/widgets/select_dialog.dart'; @@ -163,6 +164,14 @@ class _PlaySettingState extends State { callFn: (bool val) { GlobalData().enablePlayerControlAnimation = val; }), + SetSwitchItem( + title: '港澳台模式', + setKey: SettingBoxKey.enableGATMode, + defaultVal: false, + callFn: (bool val) { + Request.setBaseUrl(type: val ? 'bangumi' : 'default'); + }, + ), ListTile( dense: false, title: Text('默认视频画质', style: titleStyle), diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index bf9074e3..dca5a158 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -104,6 +104,8 @@ class SettingBoxKey { enablePlayerControlAnimation = 'enablePlayerControlAnimation', // 默认音频输出方式 defaultAoOutput = 'defaultAoOutput', + // 港澳台模式 + enableGATMode = 'enableGATMode', // youtube 双击快进快退 enableQuickDouble = 'enableQuickDouble', From 9f1b74b7e23f2b09c0c506dfbb5dd26a8217b506 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 30 Jun 2024 00:28:26 +0800 Subject: [PATCH 264/349] mod --- lib/pages/video/detail/reply/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index be1bd331..4fe69481 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -188,7 +188,7 @@ class _VideoReplyPanelState extends State if (snapshot.connectionState == ConnectionState.done) { var data = snapshot.data; if (_videoReplyController.replyList.isNotEmpty || - (data && data['status'])) { + (data != null && data['status'])) { // 请求成功 return Obx( () => _videoReplyController.isLoadingMore && From a19129c59615c5eda9750ffd0d881528d1054d66 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 30 Jun 2024 13:02:50 +0800 Subject: [PATCH 265/349] =?UTF-8?q?opt:=20=E5=AD=97=E5=B9=95=E6=8C=89?= =?UTF-8?q?=E9=92=AE=E6=98=BE=E9=9A=90=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/controller.dart | 46 +++++++++++-------- .../video/detail/widgets/header_control.dart | 17 ++++--- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 901c4014..c5e37be1 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -141,13 +141,7 @@ class VideoDetailController extends GetxController if (Platform.isAndroid) { floating = Floating(); } - headerControl = HeaderControl( - controller: plPlayerController, - videoDetailCtr: this, - floating: floating, - bvid: bvid, - videoType: videoType, - ); + // CDN优化 enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); // 预设的画质 @@ -158,7 +152,18 @@ class VideoDetailController extends GetxController defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.hiRes.code); oid.value = IdUtils.bv2av(Get.parameters['bvid']!); - getSubtitle(); + getSubtitle().then( + (subtitles) { + headerControl = HeaderControl( + controller: plPlayerController, + videoDetailCtr: this, + floating: floating, + bvid: bvid, + videoType: videoType, + showSubtitleBtn: subtitles.isNotEmpty, + ); + }, + ); } showReplyReplyPanel(oid, fRpid, firstFloor) { @@ -432,17 +437,22 @@ class VideoDetailController extends GetxController if (result['status']) { if (result['data'].subtitles.isNotEmpty) { subtitles = result['data'].subtitles; - if (subtitles.isNotEmpty) { - for (var i in subtitles) { - final Map res = await VideoHttp.getSubtitleContent( - i.subtitleUrl, - ); - i.content = res['content']; - i.body = res['body']; - } - } + getDanmaku(subtitles); + } + return subtitles; + } + } + + // 获取弹幕 + Future getDanmaku(List subtitles) async { + if (subtitles.isNotEmpty) { + for (var i in subtitles) { + final Map res = await VideoHttp.getSubtitleContent( + i.subtitleUrl, + ); + i.content = res['content']; + i.body = res['body']; } - return result['data']; } } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index 45a9684a..f3b9549a 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -30,6 +30,7 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { this.floating, this.bvid, this.videoType, + this.showSubtitleBtn, super.key, }); final PlPlayerController? controller; @@ -37,6 +38,7 @@ class HeaderControl extends StatefulWidget implements PreferredSizeWidget { final Floating? floating; final String? bvid; final SearchType? videoType; + final bool? showSubtitleBtn; @override State createState() => _HeaderControlState(); @@ -1327,14 +1329,15 @@ class _HeaderControlState extends State { ], /// 字幕 - ComBtn( - icon: const Icon( - Icons.closed_caption_off, - size: 22, - color: Colors.white, + if (widget.showSubtitleBtn!) + ComBtn( + icon: const Icon( + Icons.closed_caption_off, + size: 22, + color: Colors.white, + ), + fuc: () => showSubtitleDialog(), ), - fuc: () => showSubtitleDialog(), - ), SizedBox(width: buttonSpace), Obx( () => SizedBox( From c7ba9dc97bd9adb0042ded14858638ede7050ae9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 30 Jun 2024 22:01:12 +0800 Subject: [PATCH 266/349] =?UTF-8?q?feat:=20=E7=B3=BB=E7=BB=9F=E6=B6=88?= =?UTF-8?q?=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 + lib/http/constants.dart | 1 + lib/http/msg.dart | 27 +++++- lib/models/msg/system.dart | 77 +++++++++++++++++ lib/pages/message/system/controller.dart | 14 ++- lib/pages/message/system/view.dart | 104 +++++++++++++++++++++++ lib/pages/whisper/view.dart | 2 +- 7 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 lib/models/msg/system.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index f20b8bcf..46bbb6ac 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -548,4 +548,8 @@ class Api { /// 收到的赞 static const String messageLikeAPi = '/x/msgfeed/like'; + + /// 系统通知 + static const String messageSystemAPi = + '${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify'; } diff --git a/lib/http/constants.dart b/lib/http/constants.dart index 3d749ee8..cad413ef 100644 --- a/lib/http/constants.dart +++ b/lib/http/constants.dart @@ -5,6 +5,7 @@ class HttpString { static const String appBaseUrl = 'https://app.bilibili.com'; static const String liveBaseUrl = 'https://api.live.bilibili.com'; static const String passBaseUrl = 'https://passport.bilibili.com'; + static const String messageBaseUrl = 'https://message.bilibili.com'; static const List validateStatusCodes = [ 302, 304, diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 7c168230..86789fd1 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -3,6 +3,7 @@ import 'dart:math'; import 'package:dio/dio.dart'; import 'package:pilipala/models/msg/like.dart'; import 'package:pilipala/models/msg/reply.dart'; +import 'package:pilipala/models/msg/system.dart'; import '../models/msg/account.dart'; import '../models/msg/session.dart'; import '../utils/wbi_sign.dart'; @@ -149,7 +150,7 @@ class MsgHttp { 'msg[msg_status]': 0, 'msg[content]': jsonEncode(content), 'msg[timestamp]': DateTime.now().millisecondsSinceEpoch ~/ 1000, - 'msg[new_face_version]': 0, + 'msg[new_face_version]': 1, 'msg[dev_id]': getDevId(), 'from_firework': 0, 'build': 0, @@ -287,4 +288,28 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + static Future messageSystem() async { + var res = await Request().get(Api.messageSystemAPi, data: { + 'csrf': await Request.getCsrf(), + 'page_size': 20, + 'build': 0, + 'mobi_app': 'web', + }); + if (res.data['code'] == 0) { + try { + print(res.data['data']['system_notify_list']); + return { + 'status': true, + 'data': res.data['data']['system_notify_list'] + .map((e) => MessageSystemModel.fromJson(e)) + .toList(), + }; + } catch (err) { + return {'status': false, 'date': [], 'msg': err.toString()}; + } + } else { + return {'status': false, 'date': [], 'msg': res.data['message']}; + } + } } diff --git a/lib/models/msg/system.dart b/lib/models/msg/system.dart new file mode 100644 index 00000000..20427707 --- /dev/null +++ b/lib/models/msg/system.dart @@ -0,0 +1,77 @@ +import 'dart:convert'; + +class MessageSystemModel { + int? id; + int? cursor; + int? type; + String? title; + Map? content; + Source? source; + String? timeAt; + int? cardType; + String? cardBrief; + String? cardMsgBrief; + String? cardCover; + String? cardStoryTitle; + String? cardLink; + String? mc; + int? isStation; + int? isSend; + int? notifyCursor; + + MessageSystemModel({ + this.id, + this.cursor, + this.type, + this.title, + this.content, + this.source, + this.timeAt, + this.cardType, + this.cardBrief, + this.cardMsgBrief, + this.cardCover, + this.cardStoryTitle, + this.cardLink, + this.mc, + this.isStation, + this.isSend, + this.notifyCursor, + }); + + factory MessageSystemModel.fromJson(Map jsons) => + MessageSystemModel( + id: jsons["id"], + cursor: jsons["cursor"], + type: jsons["type"], + title: jsons["title"], + content: json.decode(jsons["content"]), + source: Source.fromJson(jsons["source"]), + timeAt: jsons["time_at"], + cardType: jsons["card_type"], + cardBrief: jsons["card_brief"], + cardMsgBrief: jsons["card_msg_brief"], + cardCover: jsons["card_cover"], + cardStoryTitle: jsons["card_story_title"], + cardLink: jsons["card_link"], + mc: jsons["mc"], + isStation: jsons["is_station"], + isSend: jsons["is_send"], + notifyCursor: jsons["notify_cursor"], + ); +} + +class Source { + String? name; + String? logo; + + Source({ + this.name, + this.logo, + }); + + factory Source.fromJson(Map json) => Source( + name: json["name"], + logo: json["logo"], + ); +} diff --git a/lib/pages/message/system/controller.dart b/lib/pages/message/system/controller.dart index ad28af56..bf31f6bc 100644 --- a/lib/pages/message/system/controller.dart +++ b/lib/pages/message/system/controller.dart @@ -1,3 +1,15 @@ import 'package:get/get.dart'; +import 'package:pilipala/http/msg.dart'; +import 'package:pilipala/models/msg/system.dart'; -class MessageSystemController extends GetxController {} +class MessageSystemController extends GetxController { + RxList systemItems = [].obs; + + Future queryMessageSystem({String type = 'init'}) async { + var res = await MsgHttp.messageSystem(); + if (res['status']) { + systemItems.addAll(res['data']); + } + return res; + } +} diff --git a/lib/pages/message/system/view.dart b/lib/pages/message/system/view.dart index da0f1219..f7b94e5a 100644 --- a/lib/pages/message/system/view.dart +++ b/lib/pages/message/system/view.dart @@ -1,4 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/models/msg/system.dart'; +import 'controller.dart'; class MessageSystemPage extends StatefulWidget { const MessageSystemPage({super.key}); @@ -8,12 +12,112 @@ class MessageSystemPage extends StatefulWidget { } class _MessageSystemPageState extends State { + final MessageSystemController _messageSystemCtr = + Get.put(MessageSystemController()); + late Future _futureBuilderFuture; + final ScrollController scrollController = ScrollController(); + + @override + void initState() { + super.initState(); + _futureBuilderFuture = _messageSystemCtr.queryMessageSystem(); + } + @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('系统通知'), ), + body: RefreshIndicator( + onRefresh: () async { + await _messageSystemCtr.queryMessageSystem(); + }, + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SizedBox(); + } + if (snapshot.data['status']) { + final systemItems = _messageSystemCtr.systemItems; + print(systemItems.length); + return Obx( + () => ListView.separated( + controller: scrollController, + itemBuilder: (context, index) => SystemItem( + item: systemItems[index], + index: index, + messageSystemCtr: _messageSystemCtr, + ), + itemCount: systemItems.length, + separatorBuilder: (BuildContext context, int index) { + return Divider( + indent: 14, + endIndent: 14, + height: 1, + color: Colors.grey.withOpacity(0.1), + ); + }, + ), + ); + } else { + // 请求错误 + return CustomScrollView( + slivers: [ + HttpError( + errMsg: snapshot.data['msg'], + fn: () { + setState(() { + _futureBuilderFuture = + _messageSystemCtr.queryMessageSystem(); + }); + }, + ) + ], + ); + } + } else { + return const SizedBox(); + } + }, + ), + ), + ); + } +} + +class SystemItem extends StatelessWidget { + final MessageSystemModel item; + final int index; + final MessageSystemController messageSystemCtr; + + const SystemItem( + {super.key, + required this.item, + required this.index, + required this.messageSystemCtr}); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.fromLTRB(14, 14, 14, 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text(item.title!, + style: + const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)), + const SizedBox(height: 4), + Text( + item.timeAt!, + style: TextStyle(color: Theme.of(context).colorScheme.outline), + ), + const SizedBox(height: 6), + Text(item.content!['web']), + ], + ), ); } } diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 9436e2be..1814c274 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -71,7 +71,7 @@ class _WhisperPageState extends State { ..._whisperController.noticesList.map((element) { return InkWell( onTap: () { - if (['/messageAt', '/messageSystem'] + if (['/messageAt'] .contains(element['path'])) { SmartDialog.showToast('功能开发中'); return; From 0d96327f344b99424c54be2e395c71ef9debeb08 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 1 Jul 2024 23:11:11 +0800 Subject: [PATCH 267/349] =?UTF-8?q?fix:=20=E5=AD=97=E5=B9=95=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=E8=B6=8A=E7=95=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/controller.dart | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 11e097e1..52c423d7 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -17,7 +17,11 @@ class PlDanmakuController { int segCount = (videoDuration / segmentLength).ceil(); requestedSeg = List.generate(segCount, (index) => false); } - queryDanmaku(calcSegment(progress)); + try { + queryDanmaku(calcSegment(progress)); + } catch (e) { + print(e); + } } void dispose() { @@ -31,16 +35,18 @@ class PlDanmakuController { void queryDanmaku(int segmentIndex) async { assert(requestedSeg[segmentIndex] == false); - requestedSeg[segmentIndex] = true; - final DmSegMobileReply result = await DanmakaHttp.queryDanmaku( - cid: cid, segmentIndex: segmentIndex + 1); - if (result.elems.isNotEmpty) { - for (var element in result.elems) { - int pos = element.progress ~/ 100; //每0.1秒存储一次 - if (dmSegMap[pos] == null) { - dmSegMap[pos] = []; + if (requestedSeg.length > segmentIndex) { + requestedSeg[segmentIndex] = true; + final DmSegMobileReply result = await DanmakaHttp.queryDanmaku( + cid: cid, segmentIndex: segmentIndex + 1); + if (result.elems.isNotEmpty) { + for (var element in result.elems) { + int pos = element.progress ~/ 100; //每0.1秒存储一次 + if (dmSegMap[pos] == null) { + dmSegMap[pos] = []; + } + dmSegMap[pos]!.add(element); } - dmSegMap[pos]!.add(element); } } } From 739e1d9ada6d7fddd0dedf9a2a6def33c45bdb53 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 5 Jul 2024 22:54:21 +0800 Subject: [PATCH 268/349] =?UTF-8?q?opt:=20AppBar=E6=B8=90=E5=8F=98?= =?UTF-8?q?=E8=83=8C=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/view.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index e485fe41..74072b2f 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -78,16 +78,16 @@ class _HomePageState extends State Theme.of(context) .colorScheme .primary - .withOpacity(0.9), + .withOpacity(0.4), Theme.of(context) .colorScheme - .primary + .surface .withOpacity(0.5), Theme.of(context).colorScheme.surface ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - stops: const [0, 0.0034, 0.34]), + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: const [0.1, 0.3, 0.5]), ), ), ), From b3f1400c044e6505e62936df8fc2b021b1e39e54 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 00:07:02 +0800 Subject: [PATCH 269/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E5=8C=BA?= =?UTF-8?q?=E8=B7=B3=E8=BD=AC=E7=AC=94=E8=AE=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index ebb266cd..d8f696d4 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -644,6 +644,15 @@ InlineSpan buildContent( title, '', ); + } else if (RegExp(r'^cv\d+$').hasMatch(matchStr)) { + Get.toNamed( + '/webview', + parameters: { + 'url': 'https://www.bilibili.com/read/$matchStr', + 'type': 'url', + 'pageTitle': title + }, + ); } else { Uri uri = Uri.parse(matchStr.replaceAll('/?', '?')); SchemeEntity scheme = SchemeEntity( From 7ae637ed6acd7454723dc4e45821efa8ba5e95a6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 15:37:50 +0800 Subject: [PATCH 270/349] =?UTF-8?q?feat:=20=E6=9F=A5=E7=9C=8Bup=E4=B8=BB?= =?UTF-8?q?=E6=94=B6=E8=97=8F=E8=AE=A2=E9=98=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav/controller.dart | 8 +++- lib/pages/fav/view.dart | 22 ++++++--- lib/pages/fav/widgets/item.dart | 5 +- lib/pages/fav_detail/controller.dart | 2 + lib/pages/fav_detail/view.dart | 1 + .../fav_detail/widget/fav_video_card.dart | 7 ++- lib/pages/fav_search/view.dart | 1 + lib/pages/member/controller.dart | 9 +++- lib/pages/member/view.dart | 46 +++++++++++++------ lib/pages/subscription/controller.dart | 8 +++- lib/pages/subscription/view.dart | 9 ++-- lib/pages/subscription/widgets/item.dart | 45 +++++++++++------- 12 files changed, 115 insertions(+), 48 deletions(-) diff --git a/lib/pages/fav/controller.dart b/lib/pages/fav/controller.dart index 8fcbf971..5be46bf0 100644 --- a/lib/pages/fav/controller.dart +++ b/lib/pages/fav/controller.dart @@ -16,10 +16,16 @@ class FavController extends GetxController { int currentPage = 1; int pageSize = 60; RxBool hasMore = true.obs; + late int mid; + late int ownerMid; + RxBool isOwner = false.obs; @override void onInit() { + mid = int.parse(Get.parameters['mid'] ?? '-1'); userInfo = userInfoCache.get('userInfoCache'); + ownerMid = userInfo != null ? userInfo!.mid! : -1; + isOwner.value = mid == -1 || mid == ownerMid; super.onInit(); } @@ -33,7 +39,7 @@ class FavController extends GetxController { var res = await UserHttp.userfavFolder( pn: currentPage, ps: pageSize, - mid: userInfo!.mid!, + mid: isOwner.value ? ownerMid : mid, ); if (res['status']) { if (type == 'init') { diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index 4f48213e..7010ba0d 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -42,17 +42,25 @@ class _FavPageState extends State { appBar: AppBar( centerTitle: false, titleSpacing: 0, - title: Text( - '我的收藏', - style: Theme.of(context).textTheme.titleMedium, - ), + title: Obx(() => Text( + '${_favController.isOwner.value ? '我' : 'Ta'}的收藏', + style: Theme.of(context).textTheme.titleMedium, + )), actions: [ + Obx(() => !_favController.isOwner.value + ? IconButton( + onPressed: () => + Get.toNamed('/subscription?mid=${_favController.mid}'), + icon: const Icon(Icons.subscriptions_outlined, size: 21), + tooltip: 'Ta的订阅', + ) + : const SizedBox.shrink()), IconButton( onPressed: () => Get.toNamed( '/favSearch?searchType=1&mediaId=${_favController.favFolderData.value.list!.first.id}'), icon: const Icon(Icons.search_outlined), ), - const SizedBox(width: 6), + const SizedBox(width: 14), ], ), body: FutureBuilder( @@ -67,7 +75,9 @@ class _FavPageState extends State { itemCount: _favController.favFolderList.length, itemBuilder: (context, index) { return FavItem( - favFolderItem: _favController.favFolderList[index]); + favFolderItem: _favController.favFolderList[index], + isOwner: _favController.isOwner.value, + ); }, ), ); diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/widgets/item.dart index 3c44ec9d..9d453fb5 100644 --- a/lib/pages/fav/widgets/item.dart +++ b/lib/pages/fav/widgets/item.dart @@ -7,7 +7,9 @@ import 'package:pilipala/utils/utils.dart'; class FavItem extends StatelessWidget { // ignore: prefer_typing_uninitialized_variables final favFolderItem; - const FavItem({super.key, required this.favFolderItem}); + final bool isOwner; + const FavItem( + {super.key, required this.favFolderItem, required this.isOwner}); @override Widget build(BuildContext context) { @@ -20,6 +22,7 @@ class FavItem extends StatelessWidget { parameters: { 'heroTag': heroTag, 'mediaId': favFolderItem.id.toString(), + 'isOwner': isOwner ? '1' : '0', }, ); }, diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 7af398e8..3f87c226 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -19,6 +19,7 @@ class FavDetailController extends GetxController { RxList favList = [].obs; RxString loadingText = '加载中...'.obs; RxInt mediaCount = 0.obs; + late String isOwner; @override void onInit() { @@ -26,6 +27,7 @@ class FavDetailController extends GetxController { if (Get.parameters.keys.isNotEmpty) { mediaId = int.parse(Get.parameters['mediaId']!); heroTag = Get.parameters['heroTag']!; + isOwner = Get.parameters['isOwner']!; } super.onInit(); } diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index 1bf5cb6f..cb9d7e7b 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -212,6 +212,7 @@ class _FavDetailPageState extends State { SliverChildBuilderDelegate((context, index) { return FavVideoCardH( videoItem: favList[index], + isOwner: _favDetailController.isOwner, callFn: () => _favDetailController .onCancelFav(favList[index].id), ); diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 79e5c073..9779c549 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -18,12 +18,14 @@ class FavVideoCardH extends StatelessWidget { final dynamic videoItem; final Function? callFn; final int? searchType; + final String isOwner; const FavVideoCardH({ Key? key, required this.videoItem, this.callFn, this.searchType, + required this.isOwner, }) : super(key: key); @override @@ -123,6 +125,7 @@ class FavVideoCardH extends StatelessWidget { videoItem: videoItem, callFn: callFn, searchType: searchType, + isOwner: isOwner, ) ], ), @@ -140,11 +143,13 @@ class VideoContent extends StatelessWidget { final dynamic videoItem; final Function? callFn; final int? searchType; + final String isOwner; const VideoContent({ super.key, required this.videoItem, this.callFn, this.searchType, + required this.isOwner, }); @override @@ -211,7 +216,7 @@ class VideoContent extends StatelessWidget { ), ], ), - searchType != 1 + searchType != 1 && isOwner == '1' ? Positioned( right: 0, bottom: -4, diff --git a/lib/pages/fav_search/view.dart b/lib/pages/fav_search/view.dart index 9b2ab15d..2654ccb1 100644 --- a/lib/pages/fav_search/view.dart +++ b/lib/pages/fav_search/view.dart @@ -100,6 +100,7 @@ class _FavSearchPageState extends State { return FavVideoCardH( videoItem: _favSearchCtr.favList[index], searchType: searchType, + isOwner: '0', callFn: () => searchType != 1 ? _favSearchCtr .onCancelFav(_favSearchCtr.favList[index].id!) diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index cc928a8d..ada869b5 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -27,6 +27,7 @@ class MemberController extends GetxController { RxString attributeText = '关注'.obs; RxList recentCoinsList = [].obs; RxList recentLikeList = [].obs; + RxBool isOwner = false.obs; @override void onInit() { @@ -34,6 +35,7 @@ class MemberController extends GetxController { mid = int.parse(Get.parameters['mid']!); userInfo = userInfoCache.get('userInfoCache'); ownerMid = userInfo != null ? userInfo.mid : -1; + isOwner.value = mid == ownerMid; face.value = Get.arguments['face'] ?? ''; heroTag = Get.arguments['heroTag'] ?? ''; relationSearch(); @@ -197,11 +199,12 @@ class MemberController extends GetxController { if (userInfo == null) return; var res = await MemberHttp.getMemberSeasons(mid, 1, 10); if (!res['status']) { - SmartDialog.showToast("用户专栏请求异常:${res['msg']}"); + SmartDialog.showToast("用户合集请求异常:${res['msg']}"); } else { // 只取前四个专栏 res['data'].seasonsList.map((e) { - e.archives = e.archives!.sublist(0, 4); + e.archives = + e.archives!.length > 4 ? e.archives!.sublist(0, 4) : e.archives!; }).toList(); } return res; @@ -235,4 +238,6 @@ class MemberController extends GetxController { void pushRecentCoinsPage() async { if (recentCoinsList.isNotEmpty) {} } + + void pushfavPage() => Get.toNamed('/fav?mid=$mid'); } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index f62ffacc..c721d638 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -159,29 +159,47 @@ class _MemberPageState extends State profileWidget(), /// 动态链接 - ListTile( - onTap: _memberController.pushDynamicsPage, - title: const Text('Ta的动态'), - trailing: - const Icon(Icons.arrow_forward_outlined, size: 19), + Obx( + () => ListTile( + onTap: _memberController.pushDynamicsPage, + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的动态'), + trailing: + const Icon(Icons.arrow_forward_outlined, size: 19), + ), ), const Divider(height: 1, thickness: 0.1), /// 视频 - ListTile( - onTap: _memberController.pushArchivesPage, - title: const Text('Ta的投稿'), - trailing: - const Icon(Icons.arrow_forward_outlined, size: 19), - ), + Obx(() => ListTile( + onTap: _memberController.pushArchivesPage, + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的投稿'), + trailing: const Icon(Icons.arrow_forward_outlined, + size: 19), + )), + const Divider(height: 1, thickness: 0.1), + + /// 他的收藏夹 + Obx(() => ListTile( + onTap: _memberController.pushfavPage, + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的收藏'), + trailing: const Icon(Icons.arrow_forward_outlined, + size: 19), + )), const Divider(height: 1, thickness: 0.1), /// 专栏 - const ListTile(title: Text('Ta的专栏')), + Obx(() => ListTile( + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'))), const Divider(height: 1, thickness: 0.1), /// 合集 - const ListTile(title: Text('Ta的合集')), + Obx(() => ListTile( + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的合集'))), MediaQuery.removePadding( removeTop: true, removeBottom: true, @@ -212,8 +230,6 @@ class _MemberPageState extends State ), ), - /// 收藏 - /// 追番 /// 最近投币 Obx( diff --git a/lib/pages/subscription/controller.dart b/lib/pages/subscription/controller.dart index d8a76d44..b59a42f0 100644 --- a/lib/pages/subscription/controller.dart +++ b/lib/pages/subscription/controller.dart @@ -16,11 +16,17 @@ class SubController extends GetxController { int currentPage = 1; int pageSize = 20; RxBool hasMore = true.obs; + late int mid; + late int ownerMid; + RxBool isOwner = false.obs; @override void onInit() { super.onInit(); + mid = int.parse(Get.parameters['mid'] ?? '-1'); userInfo = userInfoCache.get('userInfoCache'); + ownerMid = userInfo != null ? userInfo!.mid! : -1; + isOwner.value = mid == -1 || mid == ownerMid; } Future querySubFolder({type = 'init'}) async { @@ -30,7 +36,7 @@ class SubController extends GetxController { var res = await UserHttp.userSubFolder( pn: currentPage, ps: pageSize, - mid: userInfo!.mid!, + mid: isOwner.value ? ownerMid : mid, ); if (res['status']) { if (type == 'init') { diff --git a/lib/pages/subscription/view.dart b/lib/pages/subscription/view.dart index e1d1820d..cb9993b0 100644 --- a/lib/pages/subscription/view.dart +++ b/lib/pages/subscription/view.dart @@ -42,10 +42,10 @@ class _SubPageState extends State { appBar: AppBar( centerTitle: false, titleSpacing: 0, - title: Text( - '我的订阅', - style: Theme.of(context).textTheme.titleMedium, - ), + title: Obx(() => Text( + '${_subController.isOwner.value ? '我' : 'Ta'}的订阅', + style: Theme.of(context).textTheme.titleMedium, + )), ), body: FutureBuilder( future: _futureBuilderFuture, @@ -62,6 +62,7 @@ class _SubPageState extends State { return SubItem( subFolderItem: _subController.subFolderData.value.list![index], + isOwner: _subController.isOwner.value, cancelSub: _subController.cancelSub); }, ), diff --git a/lib/pages/subscription/widgets/item.dart b/lib/pages/subscription/widgets/item.dart index b244d3c7..0389b4a6 100644 --- a/lib/pages/subscription/widgets/item.dart +++ b/lib/pages/subscription/widgets/item.dart @@ -8,10 +8,12 @@ import '../../../models/user/sub_folder.dart'; class SubItem extends StatelessWidget { final SubFolderItemData subFolderItem; + final bool isOwner; final Function(SubFolderItemData) cancelSub; const SubItem({ super.key, required this.subFolderItem, + required this.isOwner, required this.cancelSub, }); @@ -59,6 +61,7 @@ class SubItem extends StatelessWidget { ), VideoContent( subFolderItem: subFolderItem, + isOwner: isOwner, cancelSub: cancelSub, ) ], @@ -73,8 +76,14 @@ class SubItem extends StatelessWidget { class VideoContent extends StatelessWidget { final SubFolderItemData subFolderItem; + final bool isOwner; final Function(SubFolderItemData)? cancelSub; - const VideoContent({super.key, required this.subFolderItem, this.cancelSub}); + const VideoContent({ + super.key, + required this.subFolderItem, + required this.isOwner, + this.cancelSub, + }); @override Widget build(BuildContext context) { @@ -111,22 +120,24 @@ class VideoContent extends StatelessWidget { ), ), const Spacer(), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - IconButton( - style: ButtonStyle( - padding: MaterialStateProperty.all(EdgeInsets.zero), - ), - onPressed: () => cancelSub?.call(subFolderItem), - icon: Icon( - Icons.clear_outlined, - color: Theme.of(context).colorScheme.outline, - size: 18, - ), - ) - ], - ) + isOwner + ? Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => cancelSub?.call(subFolderItem), + icon: Icon( + Icons.clear_outlined, + color: Theme.of(context).colorScheme.outline, + size: 18, + ), + ) + ], + ) + : const SizedBox() ], ), ), From ba430eaa2eb1038eaf5155293e5046beb636e7c4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 15:56:25 +0800 Subject: [PATCH 271/349] =?UTF-8?q?mod:=20up=E4=B8=BB=E5=90=88=E9=9B=86?= =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/models/member/seasons.dart | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/models/member/seasons.dart b/lib/models/member/seasons.dart index 70230367..88b93c78 100644 --- a/lib/models/member/seasons.dart +++ b/lib/models/member/seasons.dart @@ -9,11 +9,18 @@ class MemberSeasonsDataModel { MemberSeasonsDataModel.fromJson(Map json) { page = json['page']; - seasonsList = json['seasons_list'] != null + var tempList1 = json['seasons_list'] != null ? json['seasons_list'] .map((e) => MemberSeasonsList.fromJson(e)) .toList() : []; + var tempList2 = json['series_list'] != null + ? json['series_list'] + .map((e) => MemberSeasonsList.fromJson(e)) + .toList() + : []; + + seasonsList = [...tempList1, ...tempList2]; } } From a23a18f3bc8c995d5043fec9fa66c50d6c567c51 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 19:31:28 +0800 Subject: [PATCH 272/349] =?UTF-8?q?fix:=20=E5=AD=97=E5=B9=95=E5=AF=BC?= =?UTF-8?q?=E8=87=B4headControl=E5=88=9D=E5=A7=8B=E5=8C=96=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/video.dart | 2 +- lib/pages/video/detail/controller.dart | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/http/video.dart b/lib/http/video.dart index 7c1d9ba6..834a0102 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -492,7 +492,7 @@ class VideoHttp { return {'status': false, 'data': [], 'msg': res.data['msg']}; } } catch (err) { - print(err); + return {'status': false, 'data': [], 'msg': res.data['msg']}; } } diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index c5e37be1..dbdd9fce 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -440,6 +440,8 @@ class VideoDetailController extends GetxController getDanmaku(subtitles); } return subtitles; + } else { + return []; } } From dac0898729862fc8ae25d31f4ecf0fdf939015ed Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 6 Jul 2024 23:43:50 +0800 Subject: [PATCH 273/349] =?UTF-8?q?opt:=20AppBar=E6=B8=90=E5=8F=98?= =?UTF-8?q?=E8=83=8C=E6=99=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/view.dart | 7 +- lib/pages/home/view.dart | 149 ++++++++++++------------------- lib/pages/main/controller.dart | 3 + lib/pages/main/view.dart | 49 ++++++++-- lib/pages/media/view.dart | 13 ++- lib/pages/rank/controller.dart | 3 - lib/pages/rank/view.dart | 124 +++++++++---------------- lib/pages/video/detail/view.dart | 1 + 8 files changed, 162 insertions(+), 187 deletions(-) diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 1775798e..495eb770 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/skeleton/dynamic_card.dart'; @@ -77,10 +78,14 @@ class _DynamicsPageState extends State Widget build(BuildContext context) { super.build(context); return Scaffold( + backgroundColor: Colors.transparent, appBar: AppBar( elevation: 0, scrolledUnderElevation: 0, - titleSpacing: 0, + backgroundColor: Colors.transparent, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, title: SizedBox( height: 34, child: Stack( diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 74072b2f..0163723b 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -46,104 +46,66 @@ class _HomePageState extends State @override Widget build(BuildContext context) { super.build(context); - Brightness currentBrightness = MediaQuery.of(context).platformBrightness; - // 设置状态栏图标的亮度 - if (_homeController.enableGradientBg) { - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - statusBarIconBrightness: currentBrightness == Brightness.light - ? Brightness.dark - : Brightness.light, - )); - } return Scaffold( extendBody: true, extendBodyBehindAppBar: true, - appBar: _homeController.enableGradientBg - ? null - : AppBar(toolbarHeight: 0, elevation: 0), - body: Stack( + backgroundColor: Colors.transparent, + appBar: AppBar( + toolbarHeight: 0, + elevation: 0, + backgroundColor: Colors.transparent, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, + ), + body: Column( children: [ - // gradient background - if (_homeController.enableGradientBg) ...[ - Align( - alignment: Alignment.topLeft, - child: Opacity( - opacity: 0.6, - child: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Theme.of(context) - .colorScheme - .primary - .withOpacity(0.4), - Theme.of(context) - .colorScheme - .surface - .withOpacity(0.5), - Theme.of(context).colorScheme.surface - ], - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - stops: const [0.1, 0.3, 0.5]), + CustomAppBar( + stream: _homeController.hideSearchBar + ? stream + : StreamController.broadcast().stream, + ctr: _homeController, + callback: showUserBottomSheet, + ), + if (_homeController.tabs.length > 1) ...[ + if (_homeController.enableGradientBg) ...[ + const CustomTabs(), + ] else ...[ + Container( + width: double.infinity, + height: 42, + padding: const EdgeInsets.only(top: 4), + child: Align( + alignment: Alignment.center, + child: TabBar( + controller: _homeController.tabController, + tabs: [ + for (var i in _homeController.tabs) Tab(text: i['label']) + ], + isScrollable: true, + dividerColor: Colors.transparent, + enableFeedback: true, + splashBorderRadius: BorderRadius.circular(10), + tabAlignment: TabAlignment.center, + onTap: (value) { + feedBack(); + if (_homeController.initialIndex.value == value) { + _homeController.tabsCtrList[value]().animateToTop(); + } + _homeController.initialIndex.value = value; + }, ), ), ), - ), - ], - Column( - children: [ - CustomAppBar( - stream: _homeController.hideSearchBar - ? stream - : StreamController.broadcast().stream, - ctr: _homeController, - callback: showUserBottomSheet, - ), - if (_homeController.tabs.length > 1) ...[ - if (_homeController.enableGradientBg) ...[ - const CustomTabs(), - ] else ...[ - const SizedBox(height: 4), - SizedBox( - width: double.infinity, - height: 42, - child: Align( - alignment: Alignment.center, - child: TabBar( - controller: _homeController.tabController, - tabs: [ - for (var i in _homeController.tabs) - Tab(text: i['label']) - ], - isScrollable: true, - dividerColor: Colors.transparent, - enableFeedback: true, - splashBorderRadius: BorderRadius.circular(10), - tabAlignment: TabAlignment.center, - onTap: (value) { - feedBack(); - if (_homeController.initialIndex.value == value) { - _homeController.tabsCtrList[value]().animateToTop(); - } - _homeController.initialIndex.value = value; - }, - ), - ), - ), - ], - ] else ...[ - const SizedBox(height: 6), - ], - Expanded( - child: TabBarView( - controller: _homeController.tabController, - children: _homeController.tabsPageList, - ), - ), ], + ] else ...[ + const SizedBox(height: 6), + ], + Expanded( + child: TabBarView( + controller: _homeController.tabController, + children: _homeController.tabsPageList, + ), ), ], ), @@ -280,7 +242,10 @@ class DefaultUser extends StatelessWidget { style: ButtonStyle( padding: MaterialStateProperty.all(EdgeInsets.zero), backgroundColor: MaterialStateProperty.resolveWith((states) { - return Theme.of(context).colorScheme.onInverseSurface; + return Theme.of(context) + .colorScheme + .onSecondaryContainer + .withOpacity(0.05); }), ), onPressed: () => callback?.call(), @@ -317,7 +282,7 @@ class _CustomTabsState extends State { Widget build(BuildContext context) { return Container( height: 44, - margin: const EdgeInsets.only(top: 4), + margin: const EdgeInsets.only(top: 8), child: Obx( () => ListView.separated( padding: const EdgeInsets.symmetric(horizontal: 14.0), diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 2ef2bf7f..849e16d5 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -26,6 +26,7 @@ class MainController extends GetxController { Box userInfoCache = GStrorage.userInfo; RxBool userLogin = false.obs; late Rx dynamicBadgeType = DynamicBadgeMode.number.obs; + late bool enableGradientBg; @override void onInit() { @@ -44,6 +45,8 @@ class MainController extends GetxController { if (dynamicBadgeType.value != DynamicBadgeMode.hidden) { getUnreadDynamic(); } + enableGradientBg = + setting.get(SettingBoxKey.enableGradientBg, defaultValue: true); } void onBackPressed(BuildContext context) { diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 29573501..20a12d35 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -117,14 +117,47 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { }, child: Scaffold( extendBody: true, - body: PageView( - physics: const NeverScrollableScrollPhysics(), - controller: _mainController.pageController, - onPageChanged: (index) { - _mainController.selectedIndex = index; - setState(() {}); - }, - children: _mainController.pages, + body: Stack( + children: [ + if (_mainController.enableGradientBg) + Align( + alignment: Alignment.topLeft, + child: Opacity( + opacity: 0.6, + child: Container( + width: MediaQuery.of(context).size.width, + // height: MediaQuery.of(context).size.height, + height: MediaQuery.of(context).padding.top + 400, + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + Theme.of(context) + .colorScheme + .primary + .withOpacity(0.6), + Theme.of(context) + .colorScheme + .surface + .withOpacity(0.3), + Theme.of(context).colorScheme.surface + ], + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + stops: const [0.1, 0.8, 1]), + ), + ), + ), + ), + PageView( + physics: const NeverScrollableScrollPhysics(), + controller: _mainController.pageController, + onPageChanged: (index) { + _mainController.selectedIndex = index; + setState(() {}); + }, + children: _mainController.pages, + ), + ], ), bottomNavigationBar: _mainController.navigationBars.length > 1 ? StreamBuilder( diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index cc413e59..3694fa68 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/user/fav_folder.dart'; import 'package:pilipala/pages/media/index.dart'; -import 'package:pilipala/utils/main_stream.dart'; import 'package:pilipala/utils/utils.dart'; class MediaPage extends StatefulWidget { @@ -46,7 +46,16 @@ class _MediaPageState extends State super.build(context); Color primary = Theme.of(context).colorScheme.primary; return Scaffold( - appBar: AppBar(toolbarHeight: 30), + backgroundColor: Colors.transparent, + appBar: AppBar( + elevation: 0, + scrolledUnderElevation: 0, + toolbarHeight: 30, + backgroundColor: Colors.transparent, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, + ), body: SingleChildScrollView( controller: mediaController.scrollController, child: Column( diff --git a/lib/pages/rank/controller.dart b/lib/pages/rank/controller.dart index 64395ec7..bb34b13f 100644 --- a/lib/pages/rank/controller.dart +++ b/lib/pages/rank/controller.dart @@ -17,13 +17,10 @@ class RankController extends GetxController with GetTickerProviderStateMixin { Box setting = GStrorage.setting; late final StreamController searchBarStream = StreamController.broadcast(); - late bool enableGradientBg; @override void onInit() { super.onInit(); - enableGradientBg = - setting.get(SettingBoxKey.enableGradientBg, defaultValue: true); // 进行tabs配置 setTabConfig(); } diff --git a/lib/pages/rank/view.dart b/lib/pages/rank/view.dart index 4efa2b4e..218e60cf 100644 --- a/lib/pages/rank/view.dart +++ b/lib/pages/rank/view.dart @@ -31,94 +31,56 @@ class _RankPageState extends State @override Widget build(BuildContext context) { super.build(context); - Brightness currentBrightness = MediaQuery.of(context).platformBrightness; - // 设置状态栏图标的亮度 - if (_rankController.enableGradientBg) { - SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle( - statusBarIconBrightness: currentBrightness == Brightness.light - ? Brightness.dark - : Brightness.light, - )); - } return Scaffold( extendBody: true, - extendBodyBehindAppBar: false, - appBar: _rankController.enableGradientBg - ? null - : AppBar(toolbarHeight: 0, elevation: 0), - body: Stack( + extendBodyBehindAppBar: true, + backgroundColor: Colors.transparent, + appBar: AppBar( + toolbarHeight: 0, + elevation: 0, + backgroundColor: Colors.transparent, + systemOverlayStyle: Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, + ), + body: Column( children: [ - // gradient background - if (_rankController.enableGradientBg) ...[ - Align( - alignment: Alignment.topLeft, - child: Opacity( - opacity: 0.6, - child: Container( - width: MediaQuery.of(context).size.width, - height: MediaQuery.of(context).size.height, - decoration: BoxDecoration( - gradient: LinearGradient( - colors: [ - Theme.of(context) - .colorScheme - .primary - .withOpacity(0.9), - Theme.of(context) - .colorScheme - .primary - .withOpacity(0.5), - Theme.of(context).colorScheme.surface - ], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - stops: const [0, 0.0034, 0.34]), - ), + const CustomAppBar(), + if (_rankController.tabs.length > 1) ...[ + const SizedBox(height: 4), + SizedBox( + width: double.infinity, + height: 42, + child: Align( + alignment: Alignment.center, + child: TabBar( + controller: _rankController.tabController, + tabs: [ + for (var i in _rankController.tabs) Tab(text: i['label']) + ], + isScrollable: true, + dividerColor: Colors.transparent, + enableFeedback: true, + splashBorderRadius: BorderRadius.circular(10), + tabAlignment: TabAlignment.center, + onTap: (value) { + feedBack(); + if (_rankController.initialIndex.value == value) { + _rankController.tabsCtrList[value].animateToTop(); + } + _rankController.initialIndex.value = value; + }, ), ), ), + ] else ...[ + const SizedBox(height: 6), ], - Column( - children: [ - const CustomAppBar(), - if (_rankController.tabs.length > 1) ...[ - const SizedBox(height: 4), - SizedBox( - width: double.infinity, - height: 42, - child: Align( - alignment: Alignment.center, - child: TabBar( - controller: _rankController.tabController, - tabs: [ - for (var i in _rankController.tabs) - Tab(text: i['label']) - ], - isScrollable: true, - dividerColor: Colors.transparent, - enableFeedback: true, - splashBorderRadius: BorderRadius.circular(10), - tabAlignment: TabAlignment.center, - onTap: (value) { - feedBack(); - if (_rankController.initialIndex.value == value) { - _rankController.tabsCtrList[value].animateToTop(); - } - _rankController.initialIndex.value = value; - }, - ), - ), - ), - ] else ...[ - const SizedBox(height: 6), - ], - Expanded( - child: TabBarView( - controller: _rankController.tabController, - children: _rankController.tabsPageList, - ), - ), - ], + Expanded( + child: TabBarView( + controller: _rankController.tabController, + children: _rankController.tabsPageList, + ), ), ], ), diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 02a0bbe1..beb9dca1 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -4,6 +4,7 @@ import 'dart:ui'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_svg/svg.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; From c248d33109c053ecef5bdb895e6a3ea2c9d857af Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 7 Jul 2024 11:01:09 +0800 Subject: [PATCH 274/349] mod --- lib/pages/dynamics/view.dart | 5 ----- lib/pages/main/view.dart | 13 +++++++------ lib/pages/media/view.dart | 11 +---------- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 495eb770..0fc16dcf 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -78,14 +78,9 @@ class _DynamicsPageState extends State Widget build(BuildContext context) { super.build(context); return Scaffold( - backgroundColor: Colors.transparent, appBar: AppBar( elevation: 0, scrolledUnderElevation: 0, - backgroundColor: Colors.transparent, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, title: SizedBox( height: 34, child: Stack( diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 20a12d35..00eafa0d 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -123,27 +123,28 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { Align( alignment: Alignment.topLeft, child: Opacity( - opacity: 0.6, + opacity: Theme.of(context).brightness == Brightness.dark + ? 0.3 + : 0.6, child: Container( width: MediaQuery.of(context).size.width, - // height: MediaQuery.of(context).size.height, - height: MediaQuery.of(context).padding.top + 400, + height: MediaQuery.of(context).size.height, decoration: BoxDecoration( gradient: LinearGradient( colors: [ Theme.of(context) .colorScheme .primary - .withOpacity(0.6), + .withOpacity(0.7), + Theme.of(context).colorScheme.surface, Theme.of(context) .colorScheme .surface .withOpacity(0.3), - Theme.of(context).colorScheme.surface ], begin: Alignment.topCenter, end: Alignment.bottomCenter, - stops: const [0.1, 0.8, 1]), + stops: const [0.1, 0.3, 5]), ), ), ), diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 3694fa68..eb6d139c 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -46,16 +46,7 @@ class _MediaPageState extends State super.build(context); Color primary = Theme.of(context).colorScheme.primary; return Scaffold( - backgroundColor: Colors.transparent, - appBar: AppBar( - elevation: 0, - scrolledUnderElevation: 0, - toolbarHeight: 30, - backgroundColor: Colors.transparent, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, - ), + appBar: AppBar(toolbarHeight: 30), body: SingleChildScrollView( controller: mediaController.scrollController, child: Column( From aab51443717b4fb7bdf3b2284ee3bb7ecbbc3270 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 7 Jul 2024 22:27:28 +0800 Subject: [PATCH 275/349] =?UTF-8?q?fix:=20=E5=AD=97=E5=B9=95req=E5=AF=BC?= =?UTF-8?q?=E8=87=B4headerControl=E5=88=9D=E5=A7=8B=E5=8C=96=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/video/detail/controller.dart | 21 +++++++------------ .../video/detail/widgets/header_control.dart | 2 +- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index dbdd9fce..662f45f2 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -152,17 +152,13 @@ class VideoDetailController extends GetxController defaultAudioQa = setting.get(SettingBoxKey.defaultAudioQa, defaultValue: AudioQuality.hiRes.code); oid.value = IdUtils.bv2av(Get.parameters['bvid']!); - getSubtitle().then( - (subtitles) { - headerControl = HeaderControl( - controller: plPlayerController, - videoDetailCtr: this, - floating: floating, - bvid: bvid, - videoType: videoType, - showSubtitleBtn: subtitles.isNotEmpty, - ); - }, + getSubtitle(); + headerControl = HeaderControl( + controller: plPlayerController, + videoDetailCtr: this, + floating: floating, + bvid: bvid, + videoType: videoType, ); } @@ -439,9 +435,6 @@ class VideoDetailController extends GetxController subtitles = result['data'].subtitles; getDanmaku(subtitles); } - return subtitles; - } else { - return []; } } diff --git a/lib/pages/video/detail/widgets/header_control.dart b/lib/pages/video/detail/widgets/header_control.dart index f3b9549a..b2bed21c 100644 --- a/lib/pages/video/detail/widgets/header_control.dart +++ b/lib/pages/video/detail/widgets/header_control.dart @@ -1329,7 +1329,7 @@ class _HeaderControlState extends State { ], /// 字幕 - if (widget.showSubtitleBtn!) + if (widget.showSubtitleBtn ?? true) ComBtn( icon: const Icon( Icons.closed_caption_off, From 6f69212952d840a409cf153ff6a3f05c3a4b639f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 8 Jul 2024 23:44:39 +0800 Subject: [PATCH 276/349] =?UTF-8?q?opt:=20=E8=A7=86=E9=A2=91=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E5=BC=82=E5=B8=B8=E6=8D=95=E8=8E=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 116 ++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 42 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index beb9dca1..a9353546 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -1,16 +1,15 @@ import 'dart:async'; import 'dart:io'; -import 'dart:ui'; import 'package:extended_nested_scroll_view/extended_nested_scroll_view.dart'; import 'package:floating/floating.dart'; -import 'package:flutter/services.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:flutter_svg/svg.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'; +import 'package:lottie/lottie.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/search_type.dart'; @@ -29,6 +28,7 @@ import 'package:status_bar_control/status_bar_control.dart'; import '../../../plugin/pl_player/models/bottom_control_type.dart'; import '../../../services/shutdown_timer_service.dart'; import 'widgets/app_bar.dart'; +import 'widgets/header_control.dart'; class VideoDetailPage extends StatefulWidget { const VideoDetailPage({Key? key}) : super(key: key); @@ -494,45 +494,77 @@ class _VideoDetailPageState extends State exitFullScreen(); } + Widget buildLoadingWidget() { + return Center(child: Lottie.asset('assets/loading.json', width: 200)); + } + + Widget buildVideoPlayerWidget(AsyncSnapshot snapshot) { + return Obx(() => !vdCtr.autoPlay.value + ? const SizedBox() + : PLVideoPlayer( + controller: plPlayerController!, + headerControl: vdCtr.headerControl, + danmuWidget: PlDanmaku( + key: Key(vdCtr.danmakuCid.value.toString()), + cid: vdCtr.danmakuCid.value, + playerController: plPlayerController!, + ), + bottomList: vdCtr.bottomList, + showEposideCb: () => vdCtr.videoType == SearchType.video + ? videoIntroController.showEposideHandler() + : bangumiIntroController.showEposideHandler(), + fullScreenCb: (bool status) { + videoHeight.value = + status ? Get.size.height : defaultVideoHeight; + }, + )); + } + + Widget buildErrorWidget(dynamic error) { + return Obx( + () => SizedBox( + height: videoHeight.value, + width: Get.size.width, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + const Text('加载失败', style: TextStyle(color: Colors.white)), + Text('$error', style: const TextStyle(color: Colors.white)), + const SizedBox(height: 10), + IconButton.filled( + onPressed: () { + setState(() { + _futureBuilderFuture = vdCtr.queryVideoUrl(); + }); + }, + icon: const Icon(Icons.refresh), + ) + ], + ), + ), + ); + } + /// 播放器面板 - Widget videoPlayerPanel = FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.hasData && snapshot.data['status']) { - return Obx( - () { - return !vdCtr.autoPlay.value - ? const SizedBox() - : Obx( - () => PLVideoPlayer( - controller: plPlayerController!, - headerControl: vdCtr.headerControl, - danmuWidget: PlDanmaku( - key: Key(vdCtr.danmakuCid.value.toString()), - cid: vdCtr.danmakuCid.value, - playerController: plPlayerController!, - ), - bottomList: vdCtr.bottomList, - showEposideCb: () => vdCtr.videoType == SearchType.video - ? videoIntroController.showEposideHandler() - : bangumiIntroController.showEposideHandler(), - fullScreenCb: (bool status) { - if (status) { - videoHeight.value = Get.size.height; - } else { - videoHeight.value = defaultVideoHeight; - } - }, - ), - ); - }, - ); - } else { - // 加载失败异常处理 - return const SizedBox(); - } - }, - ); + Widget buildVideoPlayerPanel() { + return FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.waiting) { + return buildLoadingWidget(); + } else if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasData && snapshot.data['status']) { + return buildVideoPlayerWidget(snapshot); + } else { + return buildErrorWidget(snapshot.error); + } + } else { + return buildErrorWidget('未知错误'); + } + }, + ); + } Widget childWhenDisabled = SafeArea( top: MediaQuery.of(context).orientation == Orientation.portrait && @@ -607,7 +639,7 @@ class _VideoDetailPageState extends State tag: heroTag, child: Stack( children: [ - if (isShowing) videoPlayerPanel, + if (isShowing) buildVideoPlayerPanel(), /// 关闭自动播放时 手动播放 Obx( @@ -717,7 +749,7 @@ class _VideoDetailPageState extends State if (Platform.isAndroid) { return PiPSwitcher( childWhenDisabled: childWhenDisabled, - childWhenEnabled: videoPlayerPanel, + childWhenEnabled: buildVideoPlayerPanel(), floating: floating, ); } else { From 189abbc747b2887a94d8fb57b45eb0a59e62344e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 10 Jul 2024 23:58:55 +0800 Subject: [PATCH 277/349] =?UTF-8?q?fix:=20=E5=AF=BC=E8=88=AA=E6=A0=8F?= =?UTF-8?q?=E8=83=8C=E6=99=AF=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/home/view.dart | 14 +++++++++++--- lib/pages/rank/view.dart | 14 +++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib/pages/home/view.dart b/lib/pages/home/view.dart index 0163723b..187ed6c3 100644 --- a/lib/pages/home/view.dart +++ b/lib/pages/home/view.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -54,9 +55,16 @@ class _HomePageState extends State toolbarHeight: 0, elevation: 0, backgroundColor: Colors.transparent, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, + systemOverlayStyle: Platform.isAndroid + ? SystemUiOverlayStyle( + statusBarIconBrightness: + Theme.of(context).brightness == Brightness.dark + ? Brightness.light + : Brightness.dark, + ) + : Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, ), body: Column( children: [ diff --git a/lib/pages/rank/view.dart b/lib/pages/rank/view.dart index 218e60cf..cf7789a2 100644 --- a/lib/pages/rank/view.dart +++ b/lib/pages/rank/view.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -39,9 +40,16 @@ class _RankPageState extends State toolbarHeight: 0, elevation: 0, backgroundColor: Colors.transparent, - systemOverlayStyle: Theme.of(context).brightness == Brightness.dark - ? SystemUiOverlayStyle.light - : SystemUiOverlayStyle.dark, + systemOverlayStyle: Platform.isAndroid + ? SystemUiOverlayStyle( + statusBarIconBrightness: + Theme.of(context).brightness == Brightness.dark + ? Brightness.light + : Brightness.dark, + ) + : Theme.of(context).brightness == Brightness.dark + ? SystemUiOverlayStyle.light + : SystemUiOverlayStyle.dark, ), body: Column( children: [ From cc2595fd7b9246e97691569184bf03ed16e84850 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 13 Jul 2024 16:47:44 +0800 Subject: [PATCH 278/349] =?UTF-8?q?opt:=20=E5=9B=BE=E7=89=87=E9=A2=84?= =?UTF-8?q?=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../widgets/pull_to_refresh_header.dart | 130 ------ lib/pages/dynamics/widgets/content_panel.dart | 92 ++-- lib/pages/dynamics/widgets/pic_panel.dart | 68 ++- lib/pages/main/controller.dart | 1 + lib/pages/preview/controller.dart | 50 --- lib/pages/preview/index.dart | 4 - lib/pages/preview/view.dart | 290 ------------- .../detail/reply/widgets/reply_item.dart | 137 +++--- lib/pages/video/detail/view.dart | 11 + lib/plugin/pl_gallery/custom_dismissible.dart | 156 +++++++ lib/plugin/pl_gallery/hero_dialog_route.dart | 63 +++ lib/plugin/pl_gallery/index.dart | 6 + .../interactive_viewer_boundary.dart | 117 +++++ .../pl_gallery/interactiveviewer_gallery.dart | 399 ++++++++++++++++++ lib/router/app_pages.dart | 8 - pubspec.lock | 24 -- pubspec.yaml | 1 - 17 files changed, 958 insertions(+), 599 deletions(-) delete mode 100644 lib/common/widgets/pull_to_refresh_header.dart delete mode 100644 lib/pages/preview/controller.dart delete mode 100644 lib/pages/preview/index.dart delete mode 100644 lib/pages/preview/view.dart create mode 100644 lib/plugin/pl_gallery/custom_dismissible.dart create mode 100644 lib/plugin/pl_gallery/hero_dialog_route.dart create mode 100644 lib/plugin/pl_gallery/index.dart create mode 100644 lib/plugin/pl_gallery/interactive_viewer_boundary.dart create mode 100644 lib/plugin/pl_gallery/interactiveviewer_gallery.dart diff --git a/lib/common/widgets/pull_to_refresh_header.dart b/lib/common/widgets/pull_to_refresh_header.dart deleted file mode 100644 index 46db5138..00000000 --- a/lib/common/widgets/pull_to_refresh_header.dart +++ /dev/null @@ -1,130 +0,0 @@ -// ignore_for_file: depend_on_referenced_packages - -import 'dart:math'; -import 'dart:ui' as ui show Image; - -import 'package:extended_image/extended_image.dart'; -import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; -import 'package:pull_to_refresh_notification/pull_to_refresh_notification.dart'; - -double get maxDragOffset => 100; -double hideHeight = maxDragOffset / 2.3; -double refreshHeight = maxDragOffset / 1.5; - -class PullToRefreshHeader extends StatelessWidget { - const PullToRefreshHeader( - this.info, - this.lastRefreshTime, { - this.color, - super.key, - }); - - final PullToRefreshScrollNotificationInfo? info; - final DateTime? lastRefreshTime; - final Color? color; - - @override - Widget build(BuildContext context) { - final PullToRefreshScrollNotificationInfo? infos = info; - if (infos == null) { - return const SizedBox(); - } - String text = ''; - if (infos.mode == PullToRefreshIndicatorMode.armed) { - text = 'Release to refresh'; - } else if (infos.mode == PullToRefreshIndicatorMode.refresh || - infos.mode == PullToRefreshIndicatorMode.snap) { - text = 'Loading...'; - } else if (infos.mode == PullToRefreshIndicatorMode.done) { - text = 'Refresh completed.'; - } else if (infos.mode == PullToRefreshIndicatorMode.drag) { - text = 'Pull to refresh'; - } else if (infos.mode == PullToRefreshIndicatorMode.canceled) { - text = 'Cancel refresh'; - } - - final TextStyle ts = const TextStyle( - color: Colors.grey, - ).copyWith(fontSize: 14); - - final double dragOffset = info?.dragOffset ?? 0.0; - - final DateTime time = lastRefreshTime ?? DateTime.now(); - final double top = -hideHeight + dragOffset; - return Container( - height: dragOffset, - color: color ?? Colors.transparent, - // padding: EdgeInsets.only(top: dragOffset / 3), - // padding: EdgeInsets.only(bottom: 5.0), - child: Stack( - children: [ - Positioned( - left: 0.0, - right: 0.0, - top: top, - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded( - child: Container( - alignment: Alignment.centerRight, - margin: const EdgeInsets.only(right: 12.0), - child: RefreshImage(top, null), - ), - ), - Column( - children: [ - Text(text, style: ts), - Text( - 'Last updated:${DateFormat('yyyy-MM-dd hh:mm').format(time)}', - style: ts.copyWith(fontSize: 14), - ) - ], - ), - const Spacer(), - ], - ), - ) - ], - ), - ); - } -} - -class RefreshImage extends StatelessWidget { - const RefreshImage(this.top, Key? key) : super(key: key); - - final double top; - - @override - Widget build(BuildContext context) { - const double imageSize = 30; - return ExtendedImage.asset( - 'assets/flutterCandies_grey.png', - width: imageSize, - height: imageSize, - afterPaintImage: (Canvas canvas, Rect rect, ui.Image image, Paint paint) { - final double imageHeight = image.height.toDouble(); - final double imageWidth = image.width.toDouble(); - final Size size = rect.size; - final double y = - (1 - min(top / (refreshHeight - hideHeight), 1)) * imageHeight; - - canvas.drawImageRect( - image, - Rect.fromLTWH(0.0, y, imageWidth, imageHeight - y), - Rect.fromLTWH(rect.left, rect.top + y / imageHeight * size.height, - size.width, (imageHeight - y) / imageHeight * size.height), - Paint() - ..colorFilter = - const ColorFilter.mode(Color(0xFFea5504), BlendMode.srcIn) - ..isAntiAlias = false - ..filterQuality = FilterQuality.low, - ); - - //canvas.restore(); - }, - ); - } -} diff --git a/lib/pages/dynamics/widgets/content_panel.dart b/lib/pages/dynamics/widgets/content_panel.dart index e1beaeb2..28451dde 100644 --- a/lib/pages/dynamics/widgets/content_panel.dart +++ b/lib/pages/dynamics/widgets/content_panel.dart @@ -1,11 +1,11 @@ // 内容 +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/dynamics/result.dart'; -import 'package:pilipala/pages/preview/index.dart'; - +import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'rich_node_panel.dart'; // ignore: must_be_immutable @@ -59,17 +59,15 @@ class _ContentState extends State { (pictureItem.height != null && pictureItem.width != null ? pictureItem.height! / pictureItem.width! : 1); - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview(initialPage: 0, imgList: picList); - }, - ); + return Hero( + tag: pictureItem.url!, + placeholderBuilder: + (BuildContext context, Size heroSize, Widget child) { + return child; }, - child: Container( + child: GestureDetector( + onTap: () => onPreviewImg(picList, 1, context), + child: Container( padding: const EdgeInsets.only(top: 4), constraints: BoxConstraints(maxHeight: maxHeight), width: box.maxWidth / 2, @@ -91,7 +89,9 @@ class _ContentState extends State { ) : const SizedBox(), ], - )), + ), + ), + ), ); }, ), @@ -102,26 +102,23 @@ class _ContentState extends State { List list = []; for (var i = 0; i < len; i++) { picList.add(pics[i].url!); + } + for (var i = 0; i < len; i++) { list.add( LayoutBuilder( builder: (context, BoxConstraints box) { double maxWidth = box.maxWidth.truncateToDouble(); - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview(initialPage: i, imgList: picList); - }, - ); - }, - child: NetworkImgLayer( - src: pics[i].url, - width: maxWidth, - height: maxWidth, - origAspectRatio: - pics[i].width!.toInt() / pics[i].height!.toInt(), + return Hero( + tag: picList[i], + child: GestureDetector( + onTap: () => onPreviewImg(picList, i, context), + child: NetworkImgLayer( + src: pics[i].url, + width: maxWidth, + height: maxWidth, + origAspectRatio: + pics[i].width!.toInt() / pics[i].height!.toInt(), + ), ), ); }, @@ -163,6 +160,43 @@ class _ContentState extends State { ); } + void onPreviewImg(picList, initIndex, context) { + Navigator.of(context).push( + HeroDialogRoute( + builder: (BuildContext context) => InteractiveviewerGallery( + sources: picList, + initIndex: initIndex, + itemBuilder: ( + BuildContext context, + int index, + bool isFocus, + bool enablePageView, + ) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (enablePageView) { + Navigator.of(context).pop(); + } + }, + child: Center( + child: Hero( + tag: picList[index], + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 0), + imageUrl: picList[index], + fit: BoxFit.contain, + ), + ), + ), + ); + }, + onPageChanged: (int pageIndex) {}, + ), + ), + ); + } + @override Widget build(BuildContext context) { TextStyle authorStyle = diff --git a/lib/pages/dynamics/widgets/pic_panel.dart b/lib/pages/dynamics/widgets/pic_panel.dart index 4e94e6fd..783fe89b 100644 --- a/lib/pages/dynamics/widgets/pic_panel.dart +++ b/lib/pages/dynamics/widgets/pic_panel.dart @@ -1,9 +1,47 @@ +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/constants.dart'; import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; -import 'package:pilipala/pages/preview/index.dart'; +import 'package:pilipala/plugin/pl_gallery/index.dart'; + +void onPreviewImg(currentUrl, picList, initIndex, context) { + Navigator.of(context).push( + HeroDialogRoute( + builder: (BuildContext context) => InteractiveviewerGallery( + sources: picList, + initIndex: initIndex, + itemBuilder: ( + BuildContext context, + int index, + bool isFocus, + bool enablePageView, + ) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (enablePageView) { + Navigator.of(context).pop(); + } + }, + child: Center( + child: Hero( + tag: picList[index], + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 0), + imageUrl: picList[index], + fit: BoxFit.contain, + ), + ), + ), + ); + }, + onPageChanged: (int pageIndex) {}, + ), + ), + ); +} Widget picWidget(item, context) { String type = item.modules.moduleDynamic.major.type; @@ -21,25 +59,25 @@ Widget picWidget(item, context) { List list = []; for (var i = 0; i < len; i++) { picList.add(pictures[i].src ?? pictures[i].url); + } + for (var i = 0; i < len; i++) { list.add( LayoutBuilder( builder: (context, BoxConstraints box) { - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview(initialPage: i, imgList: picList); - }, - ); + return Hero( + tag: picList[i], + placeholderBuilder: + (BuildContext context, Size heroSize, Widget child) { + return child; }, - child: NetworkImgLayer( - src: pictures[i].src ?? pictures[i].url, - width: box.maxWidth, - height: box.maxWidth, + child: GestureDetector( + onTap: () => onPreviewImg(picList[i], picList, i, context), + child: NetworkImgLayer( + src: pictures[i].src ?? pictures[i].url, + width: box.maxWidth, + height: box.maxWidth, + ), ), - // ), ); }, ), diff --git a/lib/pages/main/controller.dart b/lib/pages/main/controller.dart index 849e16d5..ad2a7781 100644 --- a/lib/pages/main/controller.dart +++ b/lib/pages/main/controller.dart @@ -27,6 +27,7 @@ class MainController extends GetxController { RxBool userLogin = false.obs; late Rx dynamicBadgeType = DynamicBadgeMode.number.obs; late bool enableGradientBg; + bool imgPreviewStatus = false; @override void onInit() { diff --git a/lib/pages/preview/controller.dart b/lib/pages/preview/controller.dart deleted file mode 100644 index bb06b275..00000000 --- a/lib/pages/preview/controller.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'dart:io'; - -import 'package:device_info_plus/device_info_plus.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get.dart'; -import 'package:dio/dio.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:share_plus/share_plus.dart'; - -class PreviewController extends GetxController { - DeviceInfoPlugin deviceInfo = DeviceInfoPlugin(); - RxInt initialPage = 0.obs; - RxInt currentPage = 1.obs; - RxList imgList = [].obs; - bool storage = true; - bool videos = true; - bool photos = true; - String currentImgUrl = ''; - - requestPermission() async { - Map statuses = await [ - Permission.storage, - // Permission.photos - ].request(); - - statuses[Permission.storage].toString(); - // final photosInfo = statuses[Permission.photos].toString(); - } - - // 图片分享 - void onShareImg() async { - SmartDialog.showLoading(); - var response = await Dio().get(imgList[initialPage.value], - options: Options(responseType: ResponseType.bytes)); - final temp = await getTemporaryDirectory(); - SmartDialog.dismiss(); - String imgName = - "plpl_pic_${DateTime.now().toString().split('-').join()}.jpg"; - var path = '${temp.path}/$imgName'; - File(path).writeAsBytesSync(response.data); - Share.shareXFiles([XFile(path)], subject: imgList[initialPage.value]); - } - - void onChange(int index) { - initialPage.value = index; - currentPage.value = index + 1; - currentImgUrl = imgList[index]; - } -} diff --git a/lib/pages/preview/index.dart b/lib/pages/preview/index.dart deleted file mode 100644 index 9fb82e8d..00000000 --- a/lib/pages/preview/index.dart +++ /dev/null @@ -1,4 +0,0 @@ -library preview; - -export './controller.dart'; -export './view.dart'; diff --git a/lib/pages/preview/view.dart b/lib/pages/preview/view.dart deleted file mode 100644 index 13868d37..00000000 --- a/lib/pages/preview/view.dart +++ /dev/null @@ -1,290 +0,0 @@ -// ignore_for_file: library_private_types_in_public_api - -import 'dart:io'; - -import 'package:dismissible_page/dismissible_page.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:get/get.dart'; -import 'package:flutter/material.dart'; -import 'package:extended_image/extended_image.dart'; -import 'package:pilipala/utils/download.dart'; -import 'controller.dart'; -import 'package:status_bar_control/status_bar_control.dart'; - -typedef DoubleClickAnimationListener = void Function(); - -class ImagePreview extends StatefulWidget { - final int? initialPage; - final List? imgList; - const ImagePreview({ - Key? key, - this.initialPage, - this.imgList, - }) : super(key: key); - - @override - _ImagePreviewState createState() => _ImagePreviewState(); -} - -class _ImagePreviewState extends State - with TickerProviderStateMixin { - final PreviewController _previewController = Get.put(PreviewController()); - // late AnimationController animationController; - late AnimationController _doubleClickAnimationController; - Animation? _doubleClickAnimation; - late DoubleClickAnimationListener _doubleClickAnimationListener; - List doubleTapScales = [1.0, 2.0]; - bool _dismissDisabled = false; - - @override - void initState() { - super.initState(); - - _previewController.initialPage.value = widget.initialPage!; - _previewController.currentPage.value = widget.initialPage! + 1; - _previewController.imgList.value = widget.imgList!; - _previewController.currentImgUrl = widget.imgList![widget.initialPage!]; - // animationController = AnimationController( - // vsync: this, duration: const Duration(milliseconds: 400)); - setStatusBar(); - _doubleClickAnimationController = AnimationController( - duration: const Duration(milliseconds: 250), vsync: this); - } - - onOpenMenu() { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - clipBehavior: Clip.hardEdge, - contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 12), - content: Column( - mainAxisSize: MainAxisSize.min, - children: [ - ListTile( - onTap: () { - _previewController.onShareImg(); - Get.back(); - }, - dense: true, - title: const Text('分享', style: TextStyle(fontSize: 14)), - ), - ListTile( - onTap: () { - Clipboard.setData( - ClipboardData(text: _previewController.currentImgUrl)) - .then((value) { - Get.back(); - SmartDialog.showToast('已复制到粘贴板'); - }).catchError((err) { - SmartDialog.showNotify( - msg: err.toString(), - notifyType: NotifyType.error, - ); - }); - }, - dense: true, - title: const Text('复制链接', style: TextStyle(fontSize: 14)), - ), - ListTile( - onTap: () { - Get.back(); - DownloadUtils.downloadImg(_previewController.currentImgUrl); - }, - dense: true, - title: const Text('保存到手机', style: TextStyle(fontSize: 14)), - ), - ], - ), - ); - }, - ); - } - - // 隐藏状态栏,避免遮挡图片内容 - setStatusBar() async { - if (Platform.isIOS || Platform.isAndroid) { - await StatusBarControl.setHidden(true, - animation: StatusBarAnimation.SLIDE); - } - } - - @override - void dispose() { - // animationController.dispose(); - try { - StatusBarControl.setHidden(false, animation: StatusBarAnimation.SLIDE); - } catch (_) {} - _doubleClickAnimationController.dispose(); - clearGestureDetailsCache(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: Colors.black, - primary: false, - extendBody: true, - appBar: AppBar( - primary: false, - toolbarHeight: 0, - backgroundColor: Colors.black, - systemOverlayStyle: SystemUiOverlayStyle.dark, - ), - body: Stack( - children: [ - 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( - left: 0, - right: 0, - bottom: 0, - child: Container( - 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), - ), - ], - )), - ), - ], - ), - ); - } -} diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index d8f696d4..c4c26e6b 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,4 +1,5 @@ import 'package:appscheme/appscheme.dart'; +import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -9,9 +10,10 @@ import 'package:pilipala/common/widgets/badge.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/common/reply_type.dart'; import 'package:pilipala/models/video/reply/item.dart'; -import 'package:pilipala/pages/preview/index.dart'; +import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; +import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; @@ -540,6 +542,53 @@ InlineSpan buildContent( ); } + void onPreviewImg(picList, initIndex) { + final MainController mainController = Get.find(); + mainController.imgPreviewStatus = true; + Navigator.of(context).push( + HeroDialogRoute( + builder: (BuildContext context) => InteractiveviewerGallery( + sources: picList, + initIndex: initIndex, + itemBuilder: ( + BuildContext context, + int index, + bool isFocus, + bool enablePageView, + ) { + return GestureDetector( + behavior: HitTestBehavior.opaque, + onTap: () { + if (enablePageView) { + Navigator.of(context).pop(); + final MainController mainController = + Get.find(); + mainController.imgPreviewStatus = false; + } + }, + child: Center( + child: Hero( + tag: picList[index], + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 0), + imageUrl: picList[index], + fit: BoxFit.contain, + ), + ), + ), + ); + }, + onPageChanged: (int pageIndex) {}, + onDismissed: (int value) { + print('onDismissed'); + final MainController mainController = Get.find(); + mainController.imgPreviewStatus = false; + }, + ), + ), + ); + } + // 分割文本并处理每个部分 content.message.splitMapJoin( pattern, @@ -831,38 +880,33 @@ InlineSpan buildContent( .truncateToDouble(); } catch (_) {} - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (BuildContext context) { - return ImagePreview(initialPage: 0, imgList: picList); - }, - ); - }, - child: Container( - padding: const EdgeInsets.only(top: 4), - constraints: BoxConstraints(maxHeight: maxHeight), - width: box.maxWidth / 2, - height: height, - child: Stack( - children: [ - Positioned.fill( - child: NetworkImgLayer( - src: pictureItem['img_src'], - width: box.maxWidth / 2, - height: height, + return Hero( + tag: picList[0], + child: GestureDetector( + onTap: () => onPreviewImg(picList, 0), + child: Container( + padding: const EdgeInsets.only(top: 4), + constraints: BoxConstraints(maxHeight: maxHeight), + width: box.maxWidth / 2, + height: height, + child: Stack( + children: [ + Positioned.fill( + child: NetworkImgLayer( + src: picList[0], + width: box.maxWidth / 2, + height: height, + ), ), - ), - height > Get.size.height * 0.9 - ? const PBadge( - text: '长图', - right: 8, - bottom: 8, - ) - : const SizedBox(), - ], + height > Get.size.height * 0.9 + ? const PBadge( + text: '长图', + right: 8, + bottom: 8, + ) + : const SizedBox(), + ], + ), ), ), ); @@ -874,25 +918,22 @@ InlineSpan buildContent( List list = []; for (var i = 0; i < len; i++) { picList.add(content.pictures[i]['img_src']); + } + for (var i = 0; i < len; i++) { list.add( LayoutBuilder( builder: (context, BoxConstraints box) { - return GestureDetector( - onTap: () { - showDialog( - useSafeArea: false, - context: context, - builder: (context) { - return ImagePreview(initialPage: i, imgList: picList); - }, - ); - }, - child: NetworkImgLayer( - src: content.pictures[i]['img_src'], - width: box.maxWidth, - height: box.maxWidth, - origAspectRatio: content.pictures[i]['img_width'] / - content.pictures[i]['img_height']), + return Hero( + tag: picList[i], + child: GestureDetector( + onTap: () => onPreviewImg(picList, i), + child: NetworkImgLayer( + src: picList[i], + width: box.maxWidth, + height: box.maxWidth, + origAspectRatio: content.pictures[i]['img_width'] / + content.pictures[i]['img_height']), + ), ); }, ), diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index a9353546..331986dd 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -15,6 +15,7 @@ import 'package:pilipala/http/user.dart'; import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/pages/bangumi/introduction/index.dart'; import 'package:pilipala/pages/danmaku/view.dart'; +import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/video/detail/reply/index.dart'; import 'package:pilipala/pages/video/detail/controller.dart'; import 'package:pilipala/pages/video/detail/introduction/index.dart'; @@ -240,6 +241,11 @@ class _VideoDetailPageState extends State @override // 离开当前页面时 void didPushNext() async { + final MainController mainController = Get.find(); + if (mainController.imgPreviewStatus) { + return; + } + /// 开启 if (setting.get(SettingBoxKey.enableAutoBrightness, defaultValue: false) as bool) { @@ -259,6 +265,11 @@ class _VideoDetailPageState extends State @override // 返回当前页面时 void didPopNext() async { + final MainController mainController = Get.find(); + if (mainController.imgPreviewStatus) { + return; + } + if (plPlayerController != null && plPlayerController!.videoPlayerController != null) { setState(() { diff --git a/lib/plugin/pl_gallery/custom_dismissible.dart b/lib/plugin/pl_gallery/custom_dismissible.dart new file mode 100644 index 00000000..05761cac --- /dev/null +++ b/lib/plugin/pl_gallery/custom_dismissible.dart @@ -0,0 +1,156 @@ +import 'package:flutter/material.dart'; + +/// A widget used to dismiss its [child]. +/// +/// Similar to [Dismissible] with some adjustments. +class CustomDismissible extends StatefulWidget { + const CustomDismissible({ + required this.child, + this.onDismissed, + this.dismissThreshold = 0.2, + this.enabled = true, + Key? key, + }) : super(key: key); + + final Widget child; + final double dismissThreshold; + final VoidCallback? onDismissed; + final bool enabled; + + @override + State createState() => _CustomDismissibleState(); +} + +class _CustomDismissibleState extends State + with SingleTickerProviderStateMixin { + late AnimationController _animateController; + late Animation _moveAnimation; + late Animation _scaleAnimation; + late Animation _opacityAnimation; + + double _dragExtent = 0; + bool _dragUnderway = false; + + bool get _isActive => _dragUnderway || _animateController.isAnimating; + + @override + void initState() { + super.initState(); + + _animateController = AnimationController( + duration: const Duration(milliseconds: 300), + vsync: this, + ); + + _updateMoveAnimation(); + } + + @override + void dispose() { + _animateController.dispose(); + + super.dispose(); + } + + void _updateMoveAnimation() { + final double end = _dragExtent.sign; + + _moveAnimation = _animateController.drive( + Tween( + begin: Offset.zero, + end: Offset(0, end), + ), + ); + + _scaleAnimation = _animateController.drive(Tween( + begin: 1, + end: 0.5, + )); + + _opacityAnimation = DecorationTween( + begin: const BoxDecoration(color: Color(0xFF000000)), + end: const BoxDecoration(color: Color(0x00000000)), + ).animate(_animateController); + } + + void _handleDragStart(DragStartDetails details) { + _dragUnderway = true; + + if (_animateController.isAnimating) { + _dragExtent = + _animateController.value * context.size!.height * _dragExtent.sign; + _animateController.stop(); + } else { + _dragExtent = 0.0; + _animateController.value = 0.0; + } + setState(_updateMoveAnimation); + } + + void _handleDragUpdate(DragUpdateDetails details) { + if (!_isActive || _animateController.isAnimating) { + return; + } + + final double delta = details.primaryDelta!; + final double oldDragExtent = _dragExtent; + + if (_dragExtent + delta < 0) { + _dragExtent += delta; + } else if (_dragExtent + delta > 0) { + _dragExtent += delta; + } + + if (oldDragExtent.sign != _dragExtent.sign) { + setState(_updateMoveAnimation); + } + + if (!_animateController.isAnimating) { + _animateController.value = _dragExtent.abs() / context.size!.height; + } + } + + void _handleDragEnd(DragEndDetails details) { + if (!_isActive || _animateController.isAnimating) { + return; + } + + _dragUnderway = false; + + if (_animateController.isCompleted) { + return; + } + + if (!_animateController.isDismissed) { + // if the dragged value exceeded the dismissThreshold, call onDismissed + // else animate back to initial position. + if (_animateController.value > widget.dismissThreshold) { + widget.onDismissed?.call(); + } else { + _animateController.reverse(); + } + } + } + + @override + Widget build(BuildContext context) { + final Widget content = DecoratedBoxTransition( + decoration: _opacityAnimation, + child: SlideTransition( + position: _moveAnimation, + child: ScaleTransition( + scale: _scaleAnimation, + child: widget.child, + ), + ), + ); + + return GestureDetector( + behavior: HitTestBehavior.translucent, + onVerticalDragStart: widget.enabled ? _handleDragStart : null, + onVerticalDragUpdate: widget.enabled ? _handleDragUpdate : null, + onVerticalDragEnd: widget.enabled ? _handleDragEnd : null, + child: content, + ); + } +} diff --git a/lib/plugin/pl_gallery/hero_dialog_route.dart b/lib/plugin/pl_gallery/hero_dialog_route.dart new file mode 100644 index 00000000..d9f129d2 --- /dev/null +++ b/lib/plugin/pl_gallery/hero_dialog_route.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +/// A [PageRoute] with a semi transparent background. +/// +/// Similar to calling [showDialog] except it can be used with a [Navigator] to +/// show a [Hero] animation. +class HeroDialogRoute extends PageRoute { + HeroDialogRoute({ + required this.builder, + this.onBackgroundTap, + }) : super(); + + final WidgetBuilder builder; + + /// Called when the background is tapped. + final VoidCallback? onBackgroundTap; + + @override + bool get opaque => false; + + @override + bool get barrierDismissible => true; + + @override + String? get barrierLabel => null; + + @override + Duration get transitionDuration => const Duration(milliseconds: 300); + + @override + bool get maintainState => true; + + @override + Color? get barrierColor => null; + + @override + Widget buildTransitions( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child, + ) { + return FadeTransition( + opacity: CurvedAnimation(parent: animation, curve: Curves.easeOut), + child: child, + ); + } + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + final Widget child = builder(context); + final Widget result = Semantics( + scopesRoute: true, + explicitChildNodes: true, + child: child, + ); + return result; + } +} diff --git a/lib/plugin/pl_gallery/index.dart b/lib/plugin/pl_gallery/index.dart new file mode 100644 index 00000000..5a6f04e1 --- /dev/null +++ b/lib/plugin/pl_gallery/index.dart @@ -0,0 +1,6 @@ +library pl_gallery; + +export './hero_dialog_route.dart'; +export './custom_dismissible.dart'; +export './interactiveviewer_gallery.dart'; +export './interactive_viewer_boundary.dart'; diff --git a/lib/plugin/pl_gallery/interactive_viewer_boundary.dart b/lib/plugin/pl_gallery/interactive_viewer_boundary.dart new file mode 100644 index 00000000..929b7a6d --- /dev/null +++ b/lib/plugin/pl_gallery/interactive_viewer_boundary.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; + +/// A callback for the [InteractiveViewerBoundary] that is called when the scale +/// changed. +typedef ScaleChanged = void Function(double scale); + +/// Builds an [InteractiveViewer] and provides callbacks that are called when a +/// horizontal boundary has been hit. +/// +/// The callbacks are called when an interaction ends by listening to the +/// [InteractiveViewer.onInteractionEnd] callback. +class InteractiveViewerBoundary extends StatefulWidget { + const InteractiveViewerBoundary({ + required this.child, + required this.boundaryWidth, + this.controller, + this.onScaleChanged, + this.onLeftBoundaryHit, + this.onRightBoundaryHit, + this.onNoBoundaryHit, + this.maxScale, + this.minScale, + Key? key, + }) : super(key: key); + + final Widget child; + + /// The max width this widget can have. + /// + /// If the [InteractiveViewer] can take up the entire screen width, this + /// should be set to `MediaQuery.of(context).size.width`. + final double boundaryWidth; + + /// The [TransformationController] for the [InteractiveViewer]. + final TransformationController? controller; + + /// Called when the scale changed after an interaction ended. + final ScaleChanged? onScaleChanged; + + /// Called when the left boundary has been hit after an interaction ended. + final VoidCallback? onLeftBoundaryHit; + + /// Called when the right boundary has been hit after an interaction ended. + final VoidCallback? onRightBoundaryHit; + + /// Called when no boundary has been hit after an interaction ended. + final VoidCallback? onNoBoundaryHit; + + final double? maxScale; + + final double? minScale; + + @override + InteractiveViewerBoundaryState createState() => + InteractiveViewerBoundaryState(); +} + +class InteractiveViewerBoundaryState extends State { + TransformationController? _controller; + + double? _scale; + + @override + void initState() { + super.initState(); + + _controller = widget.controller ?? TransformationController(); + } + + @override + void dispose() { + _controller!.dispose(); + + super.dispose(); + } + + void _updateBoundaryDetection() { + final double scale = _controller!.value.row0[0]; + + if (_scale != scale) { + // the scale changed + _scale = scale; + widget.onScaleChanged?.call(scale); + } + + if (scale <= 1.01) { + // cant hit any boundaries when the child is not scaled + return; + } + + final double xOffset = _controller!.value.row0[3]; + final double boundaryWidth = widget.boundaryWidth; + final double boundaryEnd = boundaryWidth * scale; + final double xPos = boundaryEnd + xOffset; + + if (boundaryEnd.round() == xPos.round()) { + // left boundary hit + widget.onLeftBoundaryHit?.call(); + } else if (boundaryWidth.round() == xPos.round()) { + // right boundary hit + widget.onRightBoundaryHit?.call(); + } else { + widget.onNoBoundaryHit?.call(); + } + } + + @override + Widget build(BuildContext context) { + return InteractiveViewer( + maxScale: widget.maxScale!, + minScale: widget.minScale!, + transformationController: _controller, + onInteractionEnd: (_) => _updateBoundaryDetection(), + child: widget.child, + ); + } +} diff --git a/lib/plugin/pl_gallery/interactiveviewer_gallery.dart b/lib/plugin/pl_gallery/interactiveviewer_gallery.dart new file mode 100644 index 00000000..03ff4642 --- /dev/null +++ b/lib/plugin/pl_gallery/interactiveviewer_gallery.dart @@ -0,0 +1,399 @@ +library interactiveviewer_gallery; + +import 'dart:io'; + +import 'package:dio/dio.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:pilipala/utils/download.dart'; +import 'package:share_plus/share_plus.dart'; +import 'package:status_bar_control/status_bar_control.dart'; +import 'custom_dismissible.dart'; +import 'interactive_viewer_boundary.dart'; + +/// Builds a carousel controlled by a [PageView] for the tweet media sources. +/// +/// Used for showing a full screen view of the [TweetMedia] sources. +/// +/// The sources can be panned and zoomed interactively using an +/// [InteractiveViewer]. +/// An [InteractiveViewerBoundary] is used to detect when the boundary of the +/// source is hit after zooming in to disable or enable the swiping gesture of +/// the [PageView]. +/// +typedef IndexedFocusedWidgetBuilder = Widget Function( + BuildContext context, int index, bool isFocus, bool enablePageView); + +typedef IndexedTagStringBuilder = String Function(int index); + +class InteractiveviewerGallery extends StatefulWidget { + const InteractiveviewerGallery({ + required this.sources, + required this.initIndex, + required this.itemBuilder, + this.maxScale = 4.5, + this.minScale = 1.0, + this.onPageChanged, + this.onDismissed, + Key? key, + }) : super(key: key); + + /// The sources to show. + final List sources; + + /// The index of the first source in [sources] to show. + final int initIndex; + + /// The item content + final IndexedFocusedWidgetBuilder itemBuilder; + + final double maxScale; + + final double minScale; + + final ValueChanged? onPageChanged; + + final ValueChanged? onDismissed; + + @override + State createState() => + _InteractiveviewerGalleryState(); +} + +class _InteractiveviewerGalleryState extends State + with SingleTickerProviderStateMixin { + PageController? _pageController; + TransformationController? _transformationController; + + /// The controller to animate the transformation value of the + /// [InteractiveViewer] when it should reset. + late AnimationController _animationController; + Animation? _animation; + + /// `true` when an source is zoomed in and not at the at a horizontal boundary + /// to disable the [PageView]. + bool _enablePageView = true; + + /// `true` when an source is zoomed in to disable the [CustomDismissible]. + bool _enableDismiss = true; + + late Offset _doubleTapLocalPosition; + + int? currentIndex; + + @override + void initState() { + super.initState(); + + _pageController = PageController(initialPage: widget.initIndex); + + _transformationController = TransformationController(); + + _animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + ) + ..addListener(() { + _transformationController!.value = + _animation?.value ?? Matrix4.identity(); + }) + ..addStatusListener((AnimationStatus status) { + if (status == AnimationStatus.completed && !_enableDismiss) { + setState(() { + _enableDismiss = true; + }); + } + }); + + currentIndex = widget.initIndex; + setStatusBar(); + } + + setStatusBar() async { + if (Platform.isIOS || Platform.isAndroid) { + await StatusBarControl.setHidden(true, + animation: StatusBarAnimation.FADE); + } + } + + @override + void dispose() { + _pageController!.dispose(); + _animationController.dispose(); + try { + StatusBarControl.setHidden(false, animation: StatusBarAnimation.FADE); + } catch (_) {} + super.dispose(); + } + + /// When the source gets scaled up, the swipe up / down to dismiss gets + /// disabled. + /// + /// When the scale resets, the dismiss and the page view swiping gets enabled. + void _onScaleChanged(double scale) { + final bool initialScale = scale <= widget.minScale; + + if (initialScale) { + if (!_enableDismiss) { + setState(() { + _enableDismiss = true; + }); + } + + if (!_enablePageView) { + setState(() { + _enablePageView = true; + }); + } + } else { + if (_enableDismiss) { + setState(() { + _enableDismiss = false; + }); + } + + if (_enablePageView) { + setState(() { + _enablePageView = false; + }); + } + } + } + + /// When the left boundary has been hit after scaling up the source, the page + /// view swiping gets enabled if it has a page to swipe to. + void _onLeftBoundaryHit() { + if (!_enablePageView && _pageController!.page!.floor() > 0) { + setState(() { + _enablePageView = true; + }); + } + } + + /// When the right boundary has been hit after scaling up the source, the page + /// view swiping gets enabled if it has a page to swipe to. + void _onRightBoundaryHit() { + if (!_enablePageView && + _pageController!.page!.floor() < widget.sources.length - 1) { + setState(() { + _enablePageView = true; + }); + } + } + + /// When the source has been scaled up and no horizontal boundary has been hit, + /// the page view swiping gets disabled. + void _onNoBoundaryHit() { + if (_enablePageView) { + setState(() { + _enablePageView = false; + }); + } + } + + /// When the page view changed its page, the source will animate back into the + /// original scale if it was scaled up. + /// + /// Additionally the swipe up / down to dismiss gets enabled. + void _onPageChanged(int page) { + setState(() { + currentIndex = page; + }); + widget.onPageChanged?.call(page); + if (_transformationController!.value != Matrix4.identity()) { + // animate the reset for the transformation of the interactive viewer + + _animation = Matrix4Tween( + begin: _transformationController!.value, + end: Matrix4.identity(), + ).animate( + CurveTween(curve: Curves.easeOut).animate(_animationController), + ); + + _animationController.forward(from: 0); + } + } + + @override + Widget build(BuildContext context) { + return InteractiveViewerBoundary( + controller: _transformationController, + boundaryWidth: MediaQuery.of(context).size.width, + onScaleChanged: _onScaleChanged, + onLeftBoundaryHit: _onLeftBoundaryHit, + onRightBoundaryHit: _onRightBoundaryHit, + onNoBoundaryHit: _onNoBoundaryHit, + maxScale: widget.maxScale, + minScale: widget.minScale, + child: Stack(children: [ + CustomDismissible( + onDismissed: () { + Navigator.of(context).pop(); + widget.onDismissed?.call(_pageController!.page!.floor()); + }, + enabled: _enableDismiss, + child: PageView.builder( + onPageChanged: _onPageChanged, + controller: _pageController, + physics: + _enablePageView ? null : const NeverScrollableScrollPhysics(), + itemCount: widget.sources.length, + itemBuilder: (BuildContext context, int index) { + return GestureDetector( + onDoubleTapDown: (TapDownDetails details) { + _doubleTapLocalPosition = details.localPosition; + }, + onDoubleTap: onDoubleTap, + child: widget.itemBuilder( + context, + index, + index == currentIndex, + _enablePageView, + ), + ); + }, + ), + ), + Positioned( + bottom: 0, + left: 0, + right: 0, + child: Container( + padding: EdgeInsets.fromLTRB( + 12, 8, 20, MediaQuery.of(context).padding.bottom + 8), + decoration: _enablePageView + ? BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black.withOpacity(0.3) + ], + ), + ) + : null, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + IconButton( + icon: const Icon(Icons.close, color: Colors.white), + onPressed: () { + Navigator.of(context).pop(); + widget.onDismissed?.call(_pageController!.page!.floor()); + }, + ), + widget.sources.length > 1 + ? Text( + "${currentIndex! + 1}/${widget.sources.length}", + style: const TextStyle(color: Colors.white), + ) + : const SizedBox(), + PopupMenuButton( + itemBuilder: (context) { + return [ + PopupMenuItem( + value: 0, + onTap: () => onShareImg(widget.sources[currentIndex!]), + child: const Text("分享图片"), + ), + PopupMenuItem( + value: 1, + onTap: () { + Clipboard.setData(ClipboardData( + text: + widget.sources[currentIndex!].toString())) + .then((value) { + SmartDialog.showToast('已复制到粘贴板'); + }).catchError((err) { + SmartDialog.showNotify( + msg: err.toString(), + notifyType: NotifyType.error, + ); + }); + }, + child: const Text("复制图片"), + ), + PopupMenuItem( + value: 2, + onTap: () { + DownloadUtils.downloadImg( + widget.sources[currentIndex!]); + }, + child: const Text("保存图片"), + ), + ]; + }, + child: const Icon(Icons.more_horiz, color: Colors.white), + ), + ], + ), + ), + ), + ]), + ); + } + + // 图片分享 + void onShareImg(String imgUrl) async { + SmartDialog.showLoading(); + var response = await Dio() + .get(imgUrl, options: Options(responseType: ResponseType.bytes)); + final temp = await getTemporaryDirectory(); + SmartDialog.dismiss(); + String imgName = + "plpl_pic_${DateTime.now().toString().split('-').join()}.jpg"; + var path = '${temp.path}/$imgName'; + File(path).writeAsBytesSync(response.data); + Share.shareXFiles([XFile(path)], subject: imgUrl); + } + + onDoubleTap() { + Matrix4 matrix = _transformationController!.value.clone(); + double currentScale = matrix.row0.x; + + double targetScale = widget.minScale; + + if (currentScale <= widget.minScale) { + targetScale = widget.maxScale * 0.7; + } + + double offSetX = targetScale == 1.0 + ? 0.0 + : -_doubleTapLocalPosition.dx * (targetScale - 1); + double offSetY = targetScale == 1.0 + ? 0.0 + : -_doubleTapLocalPosition.dy * (targetScale - 1); + + matrix = Matrix4.fromList([ + targetScale, + matrix.row1.x, + matrix.row2.x, + matrix.row3.x, + matrix.row0.y, + targetScale, + matrix.row2.y, + matrix.row3.y, + matrix.row0.z, + matrix.row1.z, + targetScale, + matrix.row3.z, + offSetX, + offSetY, + matrix.row2.w, + matrix.row3.w + ]); + + _animation = Matrix4Tween( + begin: _transformationController!.value, + end: matrix, + ).animate( + CurveTween(curve: Curves.easeOut).animate(_animationController), + ); + _animationController + .forward(from: 0) + .whenComplete(() => _onScaleChanged(targetScale)); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index a6b48f0d..7840c126 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -70,14 +70,6 @@ class Routes { CustomGetPage(name: '/hot', page: () => const HotPage()), // 视频详情 CustomGetPage(name: '/video', page: () => const VideoDetailPage()), - // 图片预览 - // GetPage( - // name: '/preview', - // page: () => const ImagePreview(), - // transition: Transition.fade, - // transitionDuration: const Duration(milliseconds: 300), - // showCupertinoParallax: false, - // ), // CustomGetPage(name: '/webview', page: () => const WebviewPage()), // 设置 diff --git a/pubspec.lock b/pubspec.lock index c73ce395..a46127f9 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -441,22 +441,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "5.0.1" - extended_image: - dependency: "direct main" - description: - name: extended_image - sha256: d7f091d068fcac7246c4b22a84b8dac59a62e04d29a5c172710c696e67a22f94 - url: "https://pub.flutter-io.cn" - source: hosted - version: "8.2.0" - extended_image_library: - dependency: transitive - description: - name: extended_image_library - sha256: c9caee8fe9b6547bd41c960c4f2d1ef8e34321804de6a1777f1d614a24247ad6 - url: "https://pub.flutter-io.cn" - source: hosted - version: "4.0.4" extended_list: dependency: transitive description: @@ -726,14 +710,6 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" - http_client_helper: - dependency: transitive - description: - name: http_client_helper - sha256: "8a9127650734da86b5c73760de2b404494c968a3fd55602045ffec789dac3cb1" - url: "https://pub.flutter-io.cn" - source: hosted - version: "3.0.0" http_multi_server: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 09169473..b5d45714 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,7 +49,6 @@ dependencies: # 图片 cached_network_image: ^3.3.0 - extended_image: ^8.2.0 saver_gallery: ^3.0.1 # 存储 From b489a45481915b37452b2d9f7bce7e4f5d0f44ee Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Jul 2024 00:41:59 +0800 Subject: [PATCH 279/349] =?UTF-8?q?fix:=20=E6=94=B6=E8=97=8F=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E3=80=81=E6=94=B6=E5=88=B0=E7=9A=84=E8=B5=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/media/view.dart | 8 +++++--- lib/pages/message/like/view.dart | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/pages/media/view.dart b/lib/pages/media/view.dart index 3694fa68..420d7c44 100644 --- a/lib/pages/media/view.dart +++ b/lib/pages/media/view.dart @@ -245,9 +245,11 @@ class FavFolderItem extends StatelessWidget { return Container( margin: EdgeInsets.only(left: index == 0 ? 20 : 0, right: 14), child: GestureDetector( - onTap: () => Get.toNamed('/favDetail', - arguments: item, - parameters: {'mediaId': item!.id.toString(), 'heroTag': heroTag}), + onTap: () => Get.toNamed('/favDetail', arguments: item, parameters: { + 'mediaId': item!.id.toString(), + 'heroTag': heroTag, + 'isOwner': '1', + }), child: Column( mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart index e677fb25..80b20117 100644 --- a/lib/pages/message/like/view.dart +++ b/lib/pages/message/like/view.dart @@ -209,7 +209,7 @@ class LikeItem extends StatelessWidget { style: TextStyle(color: outline), ), TextSpan( - text: '赞了我的评论', + text: '赞了我的${item.item!.business}', style: TextStyle(color: outline), ), ])), From 04bdf5c2934316b8ff2dce20a0a418700e9ba7c0 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 14 Jul 2024 21:47:37 +0800 Subject: [PATCH 280/349] =?UTF-8?q?feat:=20=E8=BD=AE=E6=92=AD=E6=9F=A5?= =?UTF-8?q?=E7=9C=8Bup=E5=8A=A8=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/controller.dart | 10 +- lib/pages/dynamics/up_dynamic/controller.dart | 46 +++++ lib/pages/dynamics/up_dynamic/index.dart | 4 + .../dynamics/up_dynamic/route_panel.dart | 151 +++++++++++++++ lib/pages/dynamics/up_dynamic/view.dart | 178 ++++++++++++++++++ lib/pages/dynamics/view.dart | 19 +- lib/pages/dynamics/widgets/up_panel.dart | 49 ++--- lib/plugin/pl_popup/index.dart | 43 +++++ 8 files changed, 472 insertions(+), 28 deletions(-) create mode 100644 lib/pages/dynamics/up_dynamic/controller.dart create mode 100644 lib/pages/dynamics/up_dynamic/index.dart create mode 100644 lib/pages/dynamics/up_dynamic/route_panel.dart create mode 100644 lib/pages/dynamics/up_dynamic/view.dart create mode 100644 lib/plugin/pl_popup/index.dart diff --git a/lib/pages/dynamics/controller.dart b/lib/pages/dynamics/controller.dart index 9bed3685..1cf9db9f 100644 --- a/lib/pages/dynamics/controller.dart +++ b/lib/pages/dynamics/controller.dart @@ -6,9 +6,7 @@ import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/dynamics.dart'; import 'package:pilipala/http/search.dart'; -import 'package:pilipala/models/bangumi/info.dart'; import 'package:pilipala/models/common/dynamics_type.dart'; -import 'package:pilipala/models/common/search_type.dart'; import 'package:pilipala/models/dynamics/result.dart'; import 'package:pilipala/models/dynamics/up.dart'; import 'package:pilipala/models/live/item.dart'; @@ -16,7 +14,6 @@ import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/storage.dart'; -import 'package:pilipala/utils/utils.dart'; class DynamicsController extends GetxController { int page = 1; @@ -282,4 +279,11 @@ class DynamicsController extends GetxController { dynamicsList.value = []; queryFollowDynamic(); } + + // 点击up主 + void onTapUp(data) { + mid.value = data.mid; + upInfo.value = data; + onSelectUp(data.mid); + } } diff --git a/lib/pages/dynamics/up_dynamic/controller.dart b/lib/pages/dynamics/up_dynamic/controller.dart new file mode 100644 index 00000000..a30a0148 --- /dev/null +++ b/lib/pages/dynamics/up_dynamic/controller.dart @@ -0,0 +1,46 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/dynamics.dart'; +import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/models/dynamics/up.dart'; + +class UpDynamicsController extends GetxController { + UpDynamicsController(this.upInfo); + UpItem upInfo; + RxList dynamicsList = [].obs; + RxBool isLoadingDynamic = false.obs; + String? offset = ''; + int page = 1; + + Future queryFollowDynamic({type = 'init'}) async { + if (type == 'init') { + dynamicsList.clear(); + } + // 下拉刷新数据渲染时会触发onLoad + if (type == 'onLoad' && page == 1) { + return; + } + isLoadingDynamic.value = true; + var res = await DynamicsHttp.followDynamic( + page: type == 'init' ? 1 : page, + type: 'all', + offset: offset, + mid: upInfo.mid, + ); + isLoadingDynamic.value = false; + if (res['status']) { + if (type == 'onLoad' && res['data'].items.isEmpty) { + SmartDialog.showToast('没有更多了'); + return; + } + if (type == 'init') { + dynamicsList.value = res['data'].items; + } else { + dynamicsList.addAll(res['data'].items); + } + offset = res['data'].offset; + page++; + } + return res; + } +} diff --git a/lib/pages/dynamics/up_dynamic/index.dart b/lib/pages/dynamics/up_dynamic/index.dart new file mode 100644 index 00000000..2de4c7e4 --- /dev/null +++ b/lib/pages/dynamics/up_dynamic/index.dart @@ -0,0 +1,4 @@ +library up_dynamics; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/dynamics/up_dynamic/route_panel.dart b/lib/pages/dynamics/up_dynamic/route_panel.dart new file mode 100644 index 00000000..40c725a8 --- /dev/null +++ b/lib/pages/dynamics/up_dynamic/route_panel.dart @@ -0,0 +1,151 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:get/get.dart'; +import 'package:flutter/material.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/dynamics/up.dart'; +import 'package:pilipala/utils/feed_back.dart'; +import '../controller.dart'; +import 'index.dart'; + +class OverlayPanel extends StatefulWidget { + const OverlayPanel({super.key, required this.ctr, required this.upInfo}); + + final DynamicsController ctr; + final UpItem upInfo; + + @override + State createState() => _OverlayPanelState(); +} + +class _OverlayPanelState extends State + with SingleTickerProviderStateMixin { + static const itemPadding = EdgeInsets.symmetric(horizontal: 6, vertical: 0); + final PageController pageController = PageController(); + late double contentWidth = 50; + late List upList; + late RxInt currentMid = (-1).obs; + TabController? _tabController; + + @override + void initState() { + super.initState(); + upList = widget.ctr.upData.value.upList! + .map((element) => element) + .toList(); + upList.removeAt(0); + _tabController = TabController(length: upList.length, vsync: this); + + currentMid.value = widget.upInfo.mid!; + + pageController.addListener(() { + int index = pageController.page!.round(); + int mid = upList[index].mid!; + if (mid != currentMid.value) { + currentMid.value = mid; + _tabController?.animateTo(index, + duration: Duration.zero, curve: Curves.linear); + onClickUp(upList[index], index, type: 'pageChange'); + } + }); + + WidgetsBinding.instance.addPostFrameCallback((timeStamp) { + int index = + upList.indexWhere((element) => element.mid == widget.upInfo.mid); + pageController.jumpToPage(index); + onClickUp(widget.upInfo, index); + _tabController?.animateTo(index, + duration: Duration.zero, curve: Curves.linear); + onClickUp(upList[index], index, type: 'pageChange'); + }); + } + + void onClickUp(data, i, {type = 'click'}) { + if (type == 'click') { + pageController.jumpToPage(i); + } + } + + @override + Widget build(BuildContext context) { + return Container( + width: Get.width, + height: Get.height, + clipBehavior: Clip.antiAlias, + margin: EdgeInsets.fromLTRB( + 0, + MediaQuery.of(context).padding.top + 4, + 0, + MediaQuery.of(context).padding.bottom + 4, + ), + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular(20), + ), + child: Column( + children: [ + SizedBox( + height: 50, + child: TabBar( + controller: _tabController, + dividerColor: Colors.transparent, + automaticIndicatorColorAdjustment: false, + tabAlignment: TabAlignment.start, + padding: const EdgeInsets.only(left: 12, right: 12), + indicatorPadding: EdgeInsets.zero, + indicatorSize: TabBarIndicatorSize.label, + indicator: const BoxDecoration(), + labelPadding: itemPadding, + indicatorWeight: 1, + isScrollable: true, + tabs: upList.map((e) => Tab(child: upItemBuild(e))).toList(), + onTap: (index) { + feedBack(); + EasyThrottle.throttle( + 'follow', const Duration(milliseconds: 200), () { + onClickUp(upList[index], index); + }); + }, + ), + ), + Expanded( + child: PageView.builder( + itemCount: upList.length, + controller: pageController, + itemBuilder: (BuildContext context, int index) { + return Container( + clipBehavior: Clip.antiAlias, + margin: const EdgeInsets.fromLTRB(10, 12, 10, 0), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: BorderRadius.circular(20), + ), + child: UpDyanmicsPage(upInfo: upList[index], ctr: widget.ctr), + ); + }, + ), + ), + ], + ), + ); + } + + Widget upItemBuild(data) { + return Obx( + () => AnimatedOpacity( + opacity: currentMid == data.mid ? 1 : 0.3, + duration: const Duration(milliseconds: 200), + curve: Curves.easeInOut, + child: AnimatedScale( + duration: const Duration(milliseconds: 200), + scale: currentMid == data.mid ? 1 : 0.9, + child: NetworkImgLayer( + width: contentWidth, + height: contentWidth, + src: data.face, + type: 'avatar', + ), + ), + ), + ); + } +} diff --git a/lib/pages/dynamics/up_dynamic/view.dart b/lib/pages/dynamics/up_dynamic/view.dart new file mode 100644 index 00000000..4b3fb0c7 --- /dev/null +++ b/lib/pages/dynamics/up_dynamic/view.dart @@ -0,0 +1,178 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/dynamic_card.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/no_data.dart'; +import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/models/dynamics/up.dart'; +import 'package:pilipala/pages/dynamics/up_dynamic/index.dart'; + +import '../index.dart'; +import '../widgets/dynamic_panel.dart'; + +class UpDyanmicsPage extends StatefulWidget { + final UpItem upInfo; + final DynamicsController ctr; + + const UpDyanmicsPage({ + required this.upInfo, + required this.ctr, + Key? key, + }) : super(key: key); + + @override + State createState() => _UpDyanmicsPageState(); +} + +class _UpDyanmicsPageState extends State + with AutomaticKeepAliveClientMixin { + late UpDynamicsController _upDynamicsController; + final ScrollController scrollController = ScrollController(); + late Future _futureBuilderFuture; + + @override + bool get wantKeepAlive => true; + + @override + void initState() { + super.initState(); + _upDynamicsController = Get.put(UpDynamicsController(widget.upInfo), + tag: widget.upInfo.mid.toString()); + _futureBuilderFuture = _upDynamicsController.queryFollowDynamic(); + + scrollController.addListener( + () async { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle( + 'queryFollowDynamic', const Duration(seconds: 1), () { + _upDynamicsController.queryFollowDynamic(type: 'onLoad'); + }); + } + }, + ); + } + + @override + Widget build(BuildContext context) { + super.build(context); + return CustomScrollView( + controller: scrollController, + physics: const AlwaysScrollableScrollPhysics(), + slivers: [ + SliverPersistentHeader( + pinned: true, + floating: true, + delegate: _MySliverPersistentHeaderDelegate( + child: Container( + height: 50, + padding: const EdgeInsets.fromLTRB(20, 4, 4, 4), + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + border: Border( + bottom: BorderSide( + color: Theme.of(context).colorScheme.onSurface, + width: 0.1, + ), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + widget.upInfo.uname!, + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ), + IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const Icon(Icons.close), + ) + ], + ), + ), + ), + ), + FutureBuilder( + future: _futureBuilderFuture, + builder: (context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data == null) { + return const SliverToBoxAdapter(child: SizedBox()); + } + Map? data = snapshot.data; + if (data != null && data['status']) { + List list = + _upDynamicsController.dynamicsList; + return Obx( + () { + if (list.isEmpty) { + if (_upDynamicsController.isLoadingDynamic.value) { + return skeleton(); + } else { + return const NoData(); + } + } else { + return SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return DynamicPanel(item: list[index]); + }, + childCount: list.length, + ), + ); + } + }, + ); + } else { + return HttpError( + errMsg: data?['msg'] ?? '请求异常', + btnText: data?['code'] == -101 ? '去登录' : null, + fn: () {}, + ); + } + } else { + // 骨架屏 + return skeleton(); + } + }, + ), + ], + ); + } + + Widget skeleton() { + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const DynamicCardSkeleton(); + }, childCount: 5), + ); + } +} + +class _MySliverPersistentHeaderDelegate extends SliverPersistentHeaderDelegate { + _MySliverPersistentHeaderDelegate({required this.child}); + final double _minExtent = 50; + final double _maxExtent = 50; + final Widget child; + + @override + Widget build( + BuildContext context, double shrinkOffset, bool overlapsContent) { + return child; + } + + @override + double get maxExtent => _maxExtent; + + @override + double get minExtent => _minExtent; + + @override + bool shouldRebuild(covariant _MySliverPersistentHeaderDelegate oldDelegate) { + return true; + } +} diff --git a/lib/pages/dynamics/view.dart b/lib/pages/dynamics/view.dart index 0fc16dcf..38411613 100644 --- a/lib/pages/dynamics/view.dart +++ b/lib/pages/dynamics/view.dart @@ -3,13 +3,13 @@ import 'dart:async'; import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/skeleton/dynamic_card.dart'; import 'package:pilipala/common/widgets/http_error.dart'; import 'package:pilipala/common/widgets/no_data.dart'; import 'package:pilipala/models/dynamics/result.dart'; +import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/main_stream.dart'; import 'package:pilipala/utils/route_push.dart'; @@ -18,6 +18,7 @@ import 'package:pilipala/utils/storage.dart'; import '../mine/controller.dart'; import 'controller.dart'; import 'widgets/dynamic_panel.dart'; +import 'up_dynamic/route_panel.dart'; import 'widgets/up_panel.dart'; class DynamicsPage extends StatefulWidget { @@ -202,7 +203,21 @@ class _DynamicsPageState extends State } Map data = snapshot.data; if (data['status']) { - return Obx(() => UpPanel(_dynamicsController.upData.value)); + return Obx( + () => UpPanel( + upData: _dynamicsController.upData.value, + onClickUpCb: (data) { + // _dynamicsController.onTapUp(data); + Navigator.push( + context, + PlPopupRoute( + child: OverlayPanel( + ctr: _dynamicsController, upInfo: data), + ), + ); + }, + ), + ); } else { return const SliverToBoxAdapter( child: SizedBox(height: 80), diff --git a/lib/pages/dynamics/widgets/up_panel.dart b/lib/pages/dynamics/widgets/up_panel.dart index f8c973a0..3a0edcf6 100644 --- a/lib/pages/dynamics/widgets/up_panel.dart +++ b/lib/pages/dynamics/widgets/up_panel.dart @@ -4,13 +4,18 @@ import 'package:get/get.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/utils.dart'; class UpPanel extends StatefulWidget { final FollowUpModel upData; - const UpPanel(this.upData, {Key? key}) : super(key: key); + final Function? onClickUpCb; + + const UpPanel({ + super.key, + required this.upData, + this.onClickUpCb, + }); @override State createState() => _UpPanelState(); @@ -33,27 +38,25 @@ class _UpPanelState extends State { void onClickUp(data, i) { currentMid = data.mid; - Get.find().mid.value = data.mid; - Get.find().upInfo.value = data; - Get.find().onSelectUp(data.mid); - int liveLen = liveList.length; - int upLen = upList.length; - double itemWidth = contentWidth + itemPadding.horizontal; - double screenWidth = MediaQuery.sizeOf(context).width; - double moveDistance = 0.0; - if (itemWidth * (upList.length + liveList.length) <= screenWidth) { - } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) { - moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2; - } else { - moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth; - } - data.hasUpdate = false; - scrollController.animateTo( - moveDistance, - duration: const Duration(milliseconds: 200), - curve: Curves.linear, - ); - setState(() {}); + widget.onClickUpCb?.call(data); + // int liveLen = liveList.length; + // int upLen = upList.length; + // double itemWidth = contentWidth + itemPadding.horizontal; + // double screenWidth = MediaQuery.sizeOf(context).width; + // double moveDistance = 0.0; + // if (itemWidth * (upList.length + liveList.length) <= screenWidth) { + // } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) { + // moveDistance = (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2; + // } else { + // moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth; + // } + // data.hasUpdate = false; + // scrollController.animateTo( + // moveDistance, + // duration: const Duration(milliseconds: 200), + // curve: Curves.linear, + // ); + // setState(() {}); } @override diff --git a/lib/plugin/pl_popup/index.dart b/lib/plugin/pl_popup/index.dart new file mode 100644 index 00000000..678eb049 --- /dev/null +++ b/lib/plugin/pl_popup/index.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +class PlPopupRoute extends PopupRoute { + PlPopupRoute({ + this.backgroudColor, + this.alignment = Alignment.center, + required this.child, + this.onClick, + }); + + /// backgroudColor + final Color? backgroudColor; + + /// child'alignment, default value: [Alignment.center] + final Alignment alignment; + + /// child + final Widget child; + + /// backgroudView action + final Function? onClick; + + @override + Duration get transitionDuration => const Duration(milliseconds: 300); + + @override + bool get barrierDismissible => false; + + @override + Color get barrierColor => Colors.black54; + + @override + String? get barrierLabel => null; + + @override + Widget buildPage( + BuildContext context, + Animation animation, + Animation secondaryAnimation, + ) { + return child; + } +} From 9f676f8ef9ca13e6f6d93f01e9b2554e6ef3657f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 17 Jul 2024 23:37:55 +0800 Subject: [PATCH 281/349] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E6=95=B0=E6=8D=AE=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/badge.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common/widgets/badge.dart b/lib/common/widgets/badge.dart index a8f2fc67..1e518f39 100644 --- a/lib/common/widgets/badge.dart +++ b/lib/common/widgets/badge.dart @@ -66,7 +66,7 @@ class PBadge extends StatelessWidget { border: Border.all(color: borderColor), ), child: Text( - text!, + text ?? '', style: TextStyle(fontSize: fs ?? fontSize, color: color), ), ); From 9c00d3c4d36c6ccd07d084095dc44eab4de2057d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 18 Jul 2024 00:52:17 +0800 Subject: [PATCH 282/349] =?UTF-8?q?feat:=20up=E6=8A=95=E7=A8=BF=E6=98=BE?= =?UTF-8?q?=E7=A4=BA=E5=85=85=E7=94=B5=E4=B8=93=E5=B1=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/member.dart | 7 ++++ lib/pages/member_archive/controller.dart | 9 +++-- lib/pages/member_archive/view.dart | 46 +++++++++++++++++++----- 3 files changed, 51 insertions(+), 11 deletions(-) diff --git a/lib/http/member.dart b/lib/http/member.dart index 20a2c728..0fb010b0 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -96,7 +96,14 @@ class MemberHttp { 'dm_img_str': dmImgStr.substring(0, dmImgStr.length - 2), 'dm_cover_img_str': dmCoverImgStr.substring(0, dmCoverImgStr.length - 2), 'dm_img_inter': '{"ds":[],"wh":[0,0,0],"of":[0,0,0]}', + ...order == 'charge' + ? { + 'order': 'pubdate', + 'special_type': 'charging', + } + : {} }); + var res = await Request().get( Api.memberArchive, data: params, diff --git a/lib/pages/member_archive/controller.dart b/lib/pages/member_archive/controller.dart index 4c41de4c..667d16c5 100644 --- a/lib/pages/member_archive/controller.dart +++ b/lib/pages/member_archive/controller.dart @@ -9,12 +9,14 @@ class MemberArchiveController extends GetxController { int pn = 1; int count = 0; RxMap currentOrder = {}.obs; - List> orderList = [ + RxList> orderList = [ {'type': 'pubdate', 'label': '最新发布'}, {'type': 'click', 'label': '最多播放'}, {'type': 'stow', 'label': '最多收藏'}, - ]; + {'type': 'charge', 'label': '充电专属'}, + ].obs; RxList archivesList = [].obs; + RxBool isLoading = false.obs; @override void onInit() { @@ -27,6 +29,8 @@ class MemberArchiveController extends GetxController { Future getMemberArchive(type) async { if (type == 'init') { pn = 1; + archivesList.clear(); + isLoading.value = true; } var res = await MemberHttp.memberArchive( mid: mid, @@ -43,6 +47,7 @@ class MemberArchiveController extends GetxController { count = res['data'].page['count']; pn += 1; } + isLoading.value = false; return res; } diff --git a/lib/pages/member_archive/view.dart b/lib/pages/member_archive/view.dart index f38ca0cb..898aa915 100644 --- a/lib/pages/member_archive/view.dart +++ b/lib/pages/member_archive/view.dart @@ -1,6 +1,8 @@ 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/no_data.dart'; import 'package:pilipala/common/widgets/video_card_h.dart'; import 'package:pilipala/utils/utils.dart'; import '../../common/widgets/http_error.dart'; @@ -47,14 +49,29 @@ class _MemberArchivePageState extends State { appBar: AppBar( titleSpacing: 0, centerTitle: false, - title: Text('他的投稿', style: Theme.of(context).textTheme.titleMedium), + title: Obx( + () => Text( + '他的投稿 - ${_memberArchivesController.currentOrder['label']}', + style: Theme.of(context).textTheme.titleMedium), + ), actions: [ - Obx( - () => TextButton.icon( - icon: const Icon(Icons.sort, size: 20), - onPressed: _memberArchivesController.toggleSort, - label: Text(_memberArchivesController.currentOrder['label']!), - ), + // Obx( + PopupMenuButton( + icon: const Icon(Icons.more_vert), + onSelected: (value) { + // 这里处理选择逻辑 + _memberArchivesController.currentOrder.value = value; + _memberArchivesController.getMemberArchive('init'); + }, + itemBuilder: (BuildContext context) => + _memberArchivesController.orderList.map( + (e) { + return PopupMenuItem( + value: e, + child: Text(e['label']!), + ); + }, + ).toList(), ), const SizedBox(width: 6), ], @@ -85,7 +102,14 @@ class _MemberArchivePageState extends State { childCount: list.length, ), ) - : const SliverToBoxAdapter(), + : _memberArchivesController.isLoading.value + ? SliverList( + delegate: SliverChildBuilderDelegate( + (context, index) { + return const VideoCardHSkeleton(); + }, childCount: 10), + ) + : const NoData(), ); } else { return HttpError( @@ -100,7 +124,11 @@ class _MemberArchivePageState extends State { ); } } else { - return const SliverToBoxAdapter(); + return SliverList( + delegate: SliverChildBuilderDelegate((context, index) { + return const VideoCardHSkeleton(); + }, childCount: 10), + ); } }, ), From 75859fc9cf687a3c86a1ba9060d741f8e56bed14 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 18 Jul 2024 01:11:26 +0800 Subject: [PATCH 283/349] =?UTF-8?q?mod:=20=E5=8F=96=E6=B6=88=E6=8A=95?= =?UTF-8?q?=E5=B8=81=E9=99=90=E5=88=B6&=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/introduction/controller.dart | 58 +++++++++---------- 1 file changed, 26 insertions(+), 32 deletions(-) diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 50aac4cd..cb9ad3c1 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/http/constants.dart'; @@ -196,45 +197,38 @@ class VideoIntroController extends GetxController { SmartDialog.showToast('账号未登录'); return; } - if (hasCoin.value) { - SmartDialog.showToast('已投过币了'); - return; - } showDialog( context: Get.context!, builder: (context) { return AlertDialog( title: const Text('选择投币个数'), contentPadding: const EdgeInsets.fromLTRB(0, 12, 0, 24), - content: StatefulBuilder(builder: (context, StateSetter setState) { - return Column( - mainAxisSize: MainAxisSize.min, - children: [1, 2] - .map( - (e) => RadioListTile( - value: e, - title: Text('$e枚'), - groupValue: _tempThemeValue, - onChanged: (value) async { - _tempThemeValue = value!; - setState(() {}); - var res = await VideoHttp.coinVideo( - bvid: bvid, multiply: _tempThemeValue); - if (res['status']) { - SmartDialog.showToast('投币成功'); - hasCoin.value = true; - videoDetail.value.stat!.coin = - videoDetail.value.stat!.coin! + _tempThemeValue; - } else { - SmartDialog.showToast(res['msg']); - } - Get.back(); - }, + content: Column( + mainAxisSize: MainAxisSize.min, + children: [1, 2] + .map( + (e) => ListTile( + title: Padding( + padding: const EdgeInsets.only(left: 20), + child: Text('$e 枚'), ), - ) - .toList(), - ); - }), + onTap: () async { + var res = + await VideoHttp.coinVideo(bvid: bvid, multiply: e); + if (res['status']) { + SmartDialog.showToast('投币成功'); + hasCoin.value = true; + videoDetail.value.stat!.coin = + videoDetail.value.stat!.coin! + e; + } else { + SmartDialog.showToast(res['msg']); + } + Get.back(); + }, + ), + ) + .toList(), + ), ); }); } From 38b6b6b4e09cf8e2db9b969f8e525b9abd43ae92 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Jul 2024 18:50:36 +0800 Subject: [PATCH 284/349] =?UTF-8?q?fix:=20=E6=B6=88=E6=81=AFmid=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 1814c274..1f69fa64 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -214,7 +214,7 @@ class SessionItem extends StatelessWidget { @override Widget build(BuildContext context) { - final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo.mid); + final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo?.mid ?? 0); final content = sessionItem.lastMsg.content; final msgStatus = sessionItem.lastMsg.msgStatus; From bc3ce33f78329d4cf0985d57ab8fa3a1a80b1753 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 21 Jul 2024 22:40:43 +0800 Subject: [PATCH 285/349] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E4=BA=8C?= =?UTF-8?q?=E6=A5=BC=E8=B7=B3=E8=BD=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 6 +- lib/pages/video/detail/controller.dart | 4 +- lib/pages/video/detail/reply/view.dart | 10 +- .../detail/reply/widgets/reply_item.dart | 21 ++- .../video/detail/reply_reply/controller.dart | 13 +- lib/pages/video/detail/reply_reply/view.dart | 176 ++++++++++-------- 6 files changed, 139 insertions(+), 91 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index e83b5547..6b3d969d 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -106,7 +106,7 @@ class _DynamicDetailPageState extends State } // 查看二级评论 - void replyReply(replyItem) { + void replyReply(replyItem, currentReply) { int oid = replyItem.oid; int rpid = replyItem.rpid!; Get.to( @@ -324,8 +324,8 @@ class _DynamicDetailPageState extends State replyItem: replyList[index], showReplyRow: true, replyLevel: '1', - replyReply: (replyItem) => - replyReply(replyItem), + replyReply: (replyItem, currentReply) => + replyReply(replyItem, currentReply), replyType: ReplyType.values[replyType], addReply: (replyItem) { replyList[index] diff --git a/lib/pages/video/detail/controller.dart b/lib/pages/video/detail/controller.dart index 662f45f2..2c81140d 100644 --- a/lib/pages/video/detail/controller.dart +++ b/lib/pages/video/detail/controller.dart @@ -162,7 +162,7 @@ class VideoDetailController extends GetxController ); } - showReplyReplyPanel(oid, fRpid, firstFloor) { + showReplyReplyPanel(oid, fRpid, firstFloor, currentReply, loadMore) { replyReplyBottomSheetCtr = scaffoldKey.currentState?.showBottomSheet((BuildContext context) { return VideoReplyReplyPanel( @@ -175,6 +175,8 @@ class VideoDetailController extends GetxController replyType: ReplyType.video, source: 'videoDetail', sheetHeight: sheetHeight.value, + currentReply: currentReply, + loadMore: loadMore, ); }); replyReplyBottomSheetCtr?.closed.then((value) { diff --git a/lib/pages/video/detail/reply/view.dart b/lib/pages/video/detail/reply/view.dart index 4fe69481..f91ef625 100644 --- a/lib/pages/video/detail/reply/view.dart +++ b/lib/pages/video/detail/reply/view.dart @@ -112,7 +112,7 @@ class _VideoReplyPanelState extends State } // 展示二级回复 - void replyReply(replyItem) { + void replyReply(replyItem, currentReply, loadMore) { final VideoDetailController videoDetailCtr = Get.find(tag: heroTag); if (replyItem != null) { @@ -120,7 +120,7 @@ class _VideoReplyPanelState extends State videoDetailCtr.fRpid = replyItem.rpid!; videoDetailCtr.firstFloor = replyItem; videoDetailCtr.showReplyReplyPanel( - replyItem.oid, replyItem.rpid!, replyItem); + replyItem.oid, replyItem.rpid!, replyItem, currentReply, loadMore); } } @@ -232,8 +232,10 @@ class _VideoReplyPanelState extends State .replyList[index], showReplyRow: true, replyLevel: replyLevel, - replyReply: (replyItem) => - replyReply(replyItem), + replyReply: (replyItem, currentReply, + loadMore) => + replyReply(replyItem, currentReply, + loadMore), replyType: ReplyType.video, ); } diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index c4c26e6b..bba5ddbb 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -49,7 +49,7 @@ class ReplyItem extends StatelessWidget { onTap: () { feedBack(); if (replyReply != null) { - replyReply!(replyItem); + replyReply!(replyItem, null, replyItem!.replies!.isNotEmpty); } }, onLongPress: () { @@ -362,9 +362,13 @@ class ReplyItemRow extends StatelessWidget { for (int i = 0; i < replies!.length; i++) ...[ InkWell( // 一楼点击评论展开评论详情 - // onTap: () { - // replyReply?.call(replyItem); - // }, + onTap: () { + replyReply?.call( + replyItem, + replies![i], + replyItem!.replies!.isNotEmpty, + ); + }, onLongPress: () { feedBack(); showModalBottomSheet( @@ -535,9 +539,12 @@ InlineSpan buildContent( spanChilds.add( TextSpan( text: str, - recognizer: TapGestureRecognizer() - ..onTap = () => - replyReply?.call(replyItem.root == 0 ? replyItem : fReplyItem), + // recognizer: TapGestureRecognizer() + // ..onTap = () => replyReply?.call( + // replyItem.root == 0 ? replyItem : fReplyItem, + // replyItem, + // fReplyItem!.replies!.isNotEmpty, + // ), ), ); } diff --git a/lib/pages/video/detail/reply_reply/controller.dart b/lib/pages/video/detail/reply_reply/controller.dart index e94aaea5..3d5644e8 100644 --- a/lib/pages/video/detail/reply_reply/controller.dart +++ b/lib/pages/video/detail/reply_reply/controller.dart @@ -26,7 +26,7 @@ class VideoReplyReplyController extends GetxController { currentPage = 0; } - Future queryReplyList({type = 'init'}) async { + Future queryReplyList({type = 'init', currentReply}) async { if (type == 'init') { currentPage = 0; } @@ -63,6 +63,17 @@ class VideoReplyReplyController extends GetxController { // res['data'].replies.addAll(replyList); } } + if (replyList.isNotEmpty && currentReply != null) { + int indexToRemove = + replyList.indexWhere((item) => currentReply.rpid == item.rpid); + // 如果找到了指定ID的项,则移除 + if (indexToRemove != -1) { + replyList.removeAt(indexToRemove); + } + if (currentPage == 1 && type == 'init') { + replyList.insert(0, currentReply); + } + } isLoadingMore = false; return res; } diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 6dda9512..439f5d1d 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -20,6 +20,8 @@ class VideoReplyReplyPanel extends StatefulWidget { this.source, this.replyType, this.sheetHeight, + this.currentReply, + this.loadMore, super.key, }); final int? oid; @@ -29,6 +31,8 @@ class VideoReplyReplyPanel extends StatefulWidget { final String? source; final ReplyType? replyType; final double? sheetHeight; + final dynamic currentReply; + final bool? loadMore; @override State createState() => _VideoReplyReplyPanelState(); @@ -63,7 +67,9 @@ class _VideoReplyReplyPanelState extends State { }, ); - _futureBuilderFuture = _videoReplyReplyController.queryReplyList(); + _futureBuilderFuture = _videoReplyReplyController.queryReplyList( + currentReply: widget.currentReply, + ); } void replyReply(replyItem) {} @@ -107,7 +113,9 @@ class _VideoReplyReplyPanelState extends State { onRefresh: () async { setState(() {}); _videoReplyReplyController.currentPage = 0; - return await _videoReplyReplyController.queryReplyList(); + return await _videoReplyReplyController.queryReplyList( + currentReply: widget.currentReply, + ); }, child: CustomScrollView( controller: _videoReplyReplyController.scrollController, @@ -134,84 +142,102 @@ class _VideoReplyReplyPanelState extends State { ), ), ], - FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map? data = snapshot.data; - if (data != null && data['status']) { - // 请求成功 - return Obx( - () => SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - if (index == - _videoReplyReplyController - .replyList.length) { - return Container( - padding: EdgeInsets.only( - bottom: MediaQuery.of(context) - .padding - .bottom), - height: MediaQuery.of(context) - .padding - .bottom + - 100, - child: Center( - child: Obx( - () => Text( + widget.loadMore != null && widget.loadMore! + ? FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, snapshot) { + if (snapshot.connectionState == + ConnectionState.done) { + Map? data = snapshot.data; + if (data != null && data['status']) { + // 请求成功 + return Obx( + () => SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + if (index == _videoReplyReplyController - .noMore.value, - style: TextStyle( - fontSize: 12, - color: Theme.of(context) - .colorScheme - .outline, + .replyList.length) { + return Container( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context) + .padding + .bottom), + height: MediaQuery.of(context) + .padding + .bottom + + 100, + child: Center( + child: Obx( + () => Text( + _videoReplyReplyController + .noMore.value, + style: TextStyle( + fontSize: 12, + color: Theme.of(context) + .colorScheme + .outline, + ), + ), + ), ), - ), - ), - ), - ); - } else { - return ReplyItem( - replyItem: _videoReplyReplyController - .replyList[index], - replyLevel: '2', - showReplyRow: false, - addReply: (replyItem) { - _videoReplyReplyController.replyList - .add(replyItem); + ); + } else { + return ReplyItem( + replyItem: + _videoReplyReplyController + .replyList[index], + replyLevel: '2', + showReplyRow: false, + addReply: (replyItem) { + _videoReplyReplyController + .replyList + .add(replyItem); + }, + replyType: widget.replyType, + replyReply: (replyItem) => + replyReply(replyItem), + ); + } }, - replyType: widget.replyType, - replyReply: (replyItem) => - replyReply(replyItem), - ); - } - }, - childCount: _videoReplyReplyController - .replyList.length + - 1, + childCount: _videoReplyReplyController + .replyList.length + + 1, + ), + ), + ); + } else { + // 请求错误 + return HttpError( + errMsg: data?['msg'] ?? '请求错误', + fn: () => setState(() {}), + ); + } + } else { + // 骨架屏 + return SliverList( + delegate: SliverChildBuilderDelegate( + (BuildContext context, int index) { + return const VideoReplySkeleton(); + }, childCount: 8), + ); + } + }, + ) + : SliverToBoxAdapter( + child: SizedBox( + height: 200, + child: Center( + child: Text( + '还没有评论', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.outline, + ), ), ), - ); - } else { - // 请求错误 - return HttpError( - errMsg: data?['msg'] ?? '请求错误', - fn: () => setState(() {}), - ); - } - } else { - // 骨架屏 - return SliverList( - delegate: SliverChildBuilderDelegate( - (BuildContext context, int index) { - return const VideoReplySkeleton(); - }, childCount: 8), - ); - } - }, - ) + ), + ) ], ), ), From bc36493e60eb254c9e19e76a07f8aec4e8e37259 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 23 Jul 2024 23:25:06 +0800 Subject: [PATCH 286/349] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5=E6=8E=A8?= =?UTF-8?q?=E8=8D=90=E6=95=B0=E6=8D=AE=E6=A0=BC=E5=BC=8F=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_v.dart | 5 +- lib/http/video.dart | 72 ++++++------ lib/models/home/rcmd/result.dart | 12 +- lib/models/model_owner.dart | 9 +- lib/models/model_owner.g.dart | 47 -------- lib/models/model_rec_video_item.dart | 50 +------- lib/models/model_rec_video_item.g.dart | 154 ------------------------- lib/models/search/hot.dart | 11 -- lib/models/search/hot.g.dart | 84 -------------- lib/models/user/stat.dart | 8 -- lib/models/user/stat.g.dart | 47 -------- lib/utils/storage.dart | 5 - 12 files changed, 45 insertions(+), 459 deletions(-) delete mode 100644 lib/models/model_owner.g.dart delete mode 100644 lib/models/model_rec_video_item.g.dart delete mode 100644 lib/models/search/hot.g.dart delete mode 100644 lib/models/user/stat.g.dart diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index 14476cdf..d8e1bb2c 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -215,9 +215,8 @@ class VideoContent extends StatelessWidget { children: [ if (videoItem.goto == 'bangumi') _buildBadge(videoItem.bangumiBadge, 'line', 9), - if (videoItem.rcmdReason?.content != null && - videoItem.rcmdReason.content != '') - _buildBadge(videoItem.rcmdReason.content, 'color'), + if (videoItem.rcmdReason != null) + _buildBadge(videoItem.rcmdReason, 'color'), if (videoItem.goto == 'picture') _buildBadge('动态', 'line', 9), if (videoItem.isFollowed == 1) _buildBadge('已关注', 'color'), Expanded( diff --git a/lib/http/video.dart b/lib/http/video.dart index 834a0102..160f5db2 100644 --- a/lib/http/video.dart +++ b/lib/http/video.dart @@ -70,47 +70,43 @@ class VideoHttp { // 添加额外的loginState变量模拟未登录状态 static Future rcmdVideoListApp( {bool loginStatus = true, required int freshIdx}) async { - try { - var res = await Request().get( - Api.recommendListApp, - data: { - 'idx': freshIdx, - 'flush': '5', - 'column': '4', - 'device': 'pad', - 'device_type': 0, - 'device_name': 'vivo', - 'pull': freshIdx == 0 ? 'true' : 'false', - 'appkey': Constants.appKey, - 'access_key': loginStatus - ? (localCache.get(LocalCacheKey.accessKey, - defaultValue: {})['value'] ?? - '') - : '' - }, - ); - if (res.data['code'] == 0) { - List list = []; - List blackMidsList = - setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); - for (var i in res.data['data']['items']) { - // 屏蔽推广和拉黑用户 - if (i['card_goto'] != 'ad_av' && - (!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) && - (i['args'] != null && - !blackMidsList.contains(i['args']['up_mid']))) { - RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i); - if (!RecommendFilter.filter(videoItem)) { - list.add(videoItem); - } + var res = await Request().get( + Api.recommendListApp, + data: { + 'idx': freshIdx, + 'flush': '5', + 'column': '4', + 'device': 'pad', + 'device_type': 0, + 'device_name': 'vivo', + 'pull': freshIdx == 0 ? 'true' : 'false', + 'appkey': Constants.appKey, + 'access_key': loginStatus + ? (localCache + .get(LocalCacheKey.accessKey, defaultValue: {})['value'] ?? + '') + : '' + }, + ); + if (res.data['code'] == 0) { + List list = []; + List blackMidsList = + setting.get(SettingBoxKey.blackMidsList, defaultValue: [-1]); + for (var i in res.data['data']['items']) { + // 屏蔽推广和拉黑用户 + if (i['card_goto'] != 'ad_av' && + (!enableRcmdDynamic ? i['card_goto'] != 'picture' : true) && + (i['args'] != null && + !blackMidsList.contains(i['args']['up_mid']))) { + RecVideoItemAppModel videoItem = RecVideoItemAppModel.fromJson(i); + if (!RecommendFilter.filter(videoItem)) { + list.add(videoItem); } } - return {'status': true, 'data': list}; - } else { - return {'status': false, 'data': [], 'msg': res.data['message']}; } - } catch (err) { - return {'status': false, 'data': [], 'msg': err.toString()}; + return {'status': true, 'data': list}; + } else { + return {'status': false, 'data': [], 'msg': res.data['message']}; } } diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 0098fe95..98beda59 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -34,7 +34,7 @@ class RecVideoItemAppModel { String? title; int? isFollowed; RcmdOwner? owner; - RcmdReason? rcmdReason; + String? rcmdReason; String? goto; int? param; String? uri; @@ -64,17 +64,11 @@ class RecVideoItemAppModel { //duration = json['cover_right_text']; title = json['title']; owner = RcmdOwner.fromJson(json); - rcmdReason = json['rcmd_reason_style'] != null - ? RcmdReason.fromJson(json['rcmd_reason_style']) - : null; + rcmdReason = json['bottom_rcmd_reason'] ?? json['top_rcmd_reason']; // 由于app端api并不会直接返回与owner的关注状态 // 所以借用推荐原因是否为“已关注”、“新关注”等判别关注状态,从而与web端接口等效 RegExp regex = RegExp(r'已关注|新关注'); - isFollowed = rcmdReason != null && - rcmdReason!.content != null && - regex.hasMatch(rcmdReason!.content!) - ? 1 - : 0; + isFollowed = regex.hasMatch(rcmdReason ?? '') ? 1 : 0; // 如果是,就无需再显示推荐原因,交由view统一处理即可 if (isFollowed == 1) { rcmdReason = null; diff --git a/lib/models/model_owner.dart b/lib/models/model_owner.dart index 70396cbb..6ef425eb 100644 --- a/lib/models/model_owner.dart +++ b/lib/models/model_owner.dart @@ -1,19 +1,12 @@ -import 'package:hive/hive.dart'; - -part 'model_owner.g.dart'; - -@HiveType(typeId: 3) class Owner { Owner({ this.mid, this.name, this.face, }); - @HiveField(0) + int? mid; - @HiveField(1) String? name; - @HiveField(2) String? face; Owner.fromJson(Map json) { diff --git a/lib/models/model_owner.g.dart b/lib/models/model_owner.g.dart deleted file mode 100644 index de452713..00000000 --- a/lib/models/model_owner.g.dart +++ /dev/null @@ -1,47 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'model_owner.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class OwnerAdapter extends TypeAdapter { - @override - final int typeId = 3; - - @override - Owner read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return Owner( - mid: fields[0] as int?, - name: fields[1] as String?, - face: fields[2] as String?, - ); - } - - @override - void write(BinaryWriter writer, Owner obj) { - writer - ..writeByte(3) - ..writeByte(0) - ..write(obj.mid) - ..writeByte(1) - ..write(obj.name) - ..writeByte(2) - ..write(obj.face); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is OwnerAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/models/model_rec_video_item.dart b/lib/models/model_rec_video_item.dart index 1503f192..b82510db 100644 --- a/lib/models/model_rec_video_item.dart +++ b/lib/models/model_rec_video_item.dart @@ -1,9 +1,5 @@ import './model_owner.dart'; -import 'package:hive/hive.dart'; -part 'model_rec_video_item.g.dart'; - -@HiveType(typeId: 0) class RecVideoItemModel { RecVideoItemModel({ this.id, @@ -21,32 +17,19 @@ class RecVideoItemModel { this.rcmdReason, }); - @HiveField(0) int? id = -1; - @HiveField(1) String? bvid = ''; - @HiveField(2) int? cid = -1; - @HiveField(3) String? goto = ''; - @HiveField(4) String? uri = ''; - @HiveField(5) String? pic = ''; - @HiveField(6) String? title = ''; - @HiveField(7) int? duration = -1; - @HiveField(8) int? pubdate = -1; - @HiveField(9) Owner? owner; - @HiveField(10) Stat? stat; - @HiveField(11) int? isFollowed; - @HiveField(12) - RcmdReason? rcmdReason; + String? rcmdReason; RecVideoItemModel.fromJson(Map json) { id = json["id"]; @@ -61,26 +44,20 @@ class RecVideoItemModel { owner = Owner.fromJson(json["owner"]); stat = Stat.fromJson(json["stat"]); isFollowed = json["is_followed"] ?? 0; - rcmdReason = json["rcmd_reason"] != null - ? RcmdReason.fromJson(json["rcmd_reason"]) - : RcmdReason(content: ''); + rcmdReason = json["rcmd_reason"]?['content']; } } -@HiveType(typeId: 1) class Stat { Stat({ this.view, this.like, this.danmu, }); - @HiveField(0) - int? view; - @HiveField(1) - int? like; - @HiveField(2) - int? danmu; + int? view; + int? like; + int? danmu; Stat.fromJson(Map json) { // 无需在model中转换以保留原始数据,在view层处理即可 view = json["view"]; @@ -88,20 +65,3 @@ class Stat { danmu = json['danmaku']; } } - -@HiveType(typeId: 2) -class RcmdReason { - RcmdReason({ - this.reasonType, - this.content, - }); - @HiveField(0) - int? reasonType; - @HiveField(1) - String? content = ''; - - RcmdReason.fromJson(Map json) { - reasonType = json["reason_type"]; - content = json["content"] ?? ''; - } -} diff --git a/lib/models/model_rec_video_item.g.dart b/lib/models/model_rec_video_item.g.dart deleted file mode 100644 index dc614354..00000000 --- a/lib/models/model_rec_video_item.g.dart +++ /dev/null @@ -1,154 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'model_rec_video_item.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class RecVideoItemModelAdapter extends TypeAdapter { - @override - final int typeId = 0; - - @override - RecVideoItemModel read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return RecVideoItemModel( - id: fields[0] as int?, - bvid: fields[1] as String?, - cid: fields[2] as int?, - goto: fields[3] as String?, - uri: fields[4] as String?, - pic: fields[5] as String?, - title: fields[6] as String?, - duration: fields[7] as int?, - pubdate: fields[8] as int?, - owner: fields[9] as Owner?, - stat: fields[10] as Stat?, - isFollowed: fields[11] as int?, - rcmdReason: fields[12] as RcmdReason?, - ); - } - - @override - void write(BinaryWriter writer, RecVideoItemModel obj) { - writer - ..writeByte(13) - ..writeByte(0) - ..write(obj.id) - ..writeByte(1) - ..write(obj.bvid) - ..writeByte(2) - ..write(obj.cid) - ..writeByte(3) - ..write(obj.goto) - ..writeByte(4) - ..write(obj.uri) - ..writeByte(5) - ..write(obj.pic) - ..writeByte(6) - ..write(obj.title) - ..writeByte(7) - ..write(obj.duration) - ..writeByte(8) - ..write(obj.pubdate) - ..writeByte(9) - ..write(obj.owner) - ..writeByte(10) - ..write(obj.stat) - ..writeByte(11) - ..write(obj.isFollowed) - ..writeByte(12) - ..write(obj.rcmdReason); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RecVideoItemModelAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class StatAdapter extends TypeAdapter { - @override - final int typeId = 1; - - @override - Stat read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return Stat( - view: fields[0] as int?, - like: fields[1] as int?, - danmu: fields[2] as int?, - ); - } - - @override - void write(BinaryWriter writer, Stat obj) { - writer - ..writeByte(3) - ..writeByte(0) - ..write(obj.view) - ..writeByte(1) - ..write(obj.like) - ..writeByte(2) - ..write(obj.danmu); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is StatAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class RcmdReasonAdapter extends TypeAdapter { - @override - final int typeId = 2; - - @override - RcmdReason read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return RcmdReason( - reasonType: fields[0] as int?, - content: fields[1] as String?, - ); - } - - @override - void write(BinaryWriter writer, RcmdReason obj) { - writer - ..writeByte(2) - ..writeByte(0) - ..write(obj.reasonType) - ..writeByte(1) - ..write(obj.content); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is RcmdReasonAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/models/search/hot.dart b/lib/models/search/hot.dart index ce09b2ea..59d7e749 100644 --- a/lib/models/search/hot.dart +++ b/lib/models/search/hot.dart @@ -1,14 +1,8 @@ -import 'package:hive/hive.dart'; - -part 'hot.g.dart'; - -@HiveType(typeId: 6) class HotSearchModel { HotSearchModel({ this.list, }); - @HiveField(0) List? list; HotSearchModel.fromJson(Map json) { @@ -18,7 +12,6 @@ class HotSearchModel { } } -@HiveType(typeId: 7) class HotSearchItem { HotSearchItem({ this.keyword, @@ -27,14 +20,10 @@ class HotSearchItem { this.icon, }); - @HiveField(0) String? keyword; - @HiveField(1) String? showName; // 4/5热 11话题 8普通 7直播 - @HiveField(2) int? wordType; - @HiveField(3) String? icon; HotSearchItem.fromJson(Map json) { diff --git a/lib/models/search/hot.g.dart b/lib/models/search/hot.g.dart deleted file mode 100644 index a06dd475..00000000 --- a/lib/models/search/hot.g.dart +++ /dev/null @@ -1,84 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'hot.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class HotSearchModelAdapter extends TypeAdapter { - @override - final int typeId = 6; - - @override - HotSearchModel read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return HotSearchModel( - list: (fields[0] as List?)?.cast(), - ); - } - - @override - void write(BinaryWriter writer, HotSearchModel obj) { - writer - ..writeByte(1) - ..writeByte(0) - ..write(obj.list); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is HotSearchModelAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} - -class HotSearchItemAdapter extends TypeAdapter { - @override - final int typeId = 7; - - @override - HotSearchItem read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return HotSearchItem( - keyword: fields[0] as String?, - showName: fields[1] as String?, - wordType: fields[2] as int?, - icon: fields[3] as String?, - ); - } - - @override - void write(BinaryWriter writer, HotSearchItem obj) { - writer - ..writeByte(4) - ..writeByte(0) - ..write(obj.keyword) - ..writeByte(1) - ..write(obj.showName) - ..writeByte(2) - ..write(obj.wordType) - ..writeByte(3) - ..write(obj.icon); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is HotSearchItemAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/models/user/stat.dart b/lib/models/user/stat.dart index 3b09acb1..0b56a499 100644 --- a/lib/models/user/stat.dart +++ b/lib/models/user/stat.dart @@ -1,8 +1,3 @@ -import 'package:hive/hive.dart'; - -part 'stat.g.dart'; - -@HiveType(typeId: 1) class UserStat { UserStat({ this.following, @@ -10,11 +5,8 @@ class UserStat { this.dynamicCount, }); - @HiveField(0) int? following; - @HiveField(1) int? follower; - @HiveField(2) int? dynamicCount; UserStat.fromJson(Map json) { diff --git a/lib/models/user/stat.g.dart b/lib/models/user/stat.g.dart deleted file mode 100644 index 290fe026..00000000 --- a/lib/models/user/stat.g.dart +++ /dev/null @@ -1,47 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'stat.dart'; - -// ************************************************************************** -// TypeAdapterGenerator -// ************************************************************************** - -class UserStatAdapter extends TypeAdapter { - @override - final int typeId = 1; - - @override - UserStat read(BinaryReader reader) { - final numOfFields = reader.readByte(); - final fields = { - for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(), - }; - return UserStat( - following: fields[0] as int?, - follower: fields[1] as int?, - dynamicCount: fields[2] as int?, - ); - } - - @override - void write(BinaryWriter writer, UserStat obj) { - writer - ..writeByte(3) - ..writeByte(0) - ..write(obj.following) - ..writeByte(1) - ..write(obj.follower) - ..writeByte(2) - ..write(obj.dynamicCount); - } - - @override - int get hashCode => typeId.hashCode; - - @override - bool operator ==(Object other) => - identical(this, other) || - other is UserStatAdapter && - runtimeType == other.runtimeType && - typeId == other.typeId; -} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index dca5a158..a132c5da 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -1,8 +1,6 @@ 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 '../models/common/gesture_mode.dart'; import 'global_data.dart'; @@ -54,11 +52,8 @@ class GStrorage { } static void regAdapter() { - Hive.registerAdapter(OwnerAdapter()); Hive.registerAdapter(UserInfoDataAdapter()); Hive.registerAdapter(LevelInfoAdapter()); - Hive.registerAdapter(HotSearchModelAdapter()); - Hive.registerAdapter(HotSearchItemAdapter()); } static Future close() async { From 5b78810ba43cd795b2bff953b08f41e0b4e680d3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 23 Jul 2024 23:29:53 +0800 Subject: [PATCH 287/349] =?UTF-8?q?fix:=20=E6=B6=88=E6=81=AFmid=20null?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 1f69fa64..7082619f 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -228,7 +228,7 @@ class SessionItem extends StatelessWidget { 'talkerId': sessionItem.talkerId.toString(), 'name': sessionItem.accountInfo.name, 'face': sessionItem.accountInfo.face, - 'mid': sessionItem.accountInfo.mid.toString(), + 'mid': (sessionItem.accountInfo?.mid ?? 0).toString(), 'heroTag': heroTag, }, ); From a0bc0314bf51c1da040e4a6f0326ab96737fbd28 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 25 Jul 2024 00:08:18 +0800 Subject: [PATCH 288/349] =?UTF-8?q?fix:=20=E8=AF=84=E8=AE=BA=E8=AF=A6?= =?UTF-8?q?=E6=83=85=E6=9F=A5=E7=9C=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/reply/widgets/reply_item.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index bba5ddbb..5b7b0659 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -16,7 +16,6 @@ import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; @@ -436,7 +435,8 @@ class ReplyItemRow extends StatelessWidget { if (extraRow == 1) InkWell( // 一楼点击【共xx条回复】展开评论详情 - onTap: () => replyReply!(replyItem), + onTap: () => replyReply?.call(replyItem, null, true), + onLongPress: () => {}, child: Container( width: double.infinity, padding: const EdgeInsets.fromLTRB(8, 5, 8, 8), From 80d0b15ac88d4b9bdcb576d45519fc296c036b2a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 26 Jul 2024 22:47:14 +0800 Subject: [PATCH 289/349] =?UTF-8?q?fix:=20=E5=8A=A8=E6=80=81=E8=AF=84?= =?UTF-8?q?=E8=AE=BA=E6=9F=A5=E7=9C=8B=E8=AF=A6=E6=83=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/detail/view.dart | 9 ++++++--- lib/pages/video/detail/reply_reply/view.dart | 6 +++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/pages/dynamics/detail/view.dart b/lib/pages/dynamics/detail/view.dart index 6b3d969d..56af68fc 100644 --- a/lib/pages/dynamics/detail/view.dart +++ b/lib/pages/dynamics/detail/view.dart @@ -106,7 +106,7 @@ class _DynamicDetailPageState extends State } // 查看二级评论 - void replyReply(replyItem, currentReply) { + void replyReply(replyItem, currentReply, loadMore) { int oid = replyItem.oid; int rpid = replyItem.rpid!; Get.to( @@ -125,6 +125,7 @@ class _DynamicDetailPageState extends State source: 'dynamic', replyType: ReplyType.values[replyType], firstFloor: replyItem, + loadMore: loadMore, ), ), ); @@ -324,8 +325,10 @@ class _DynamicDetailPageState extends State replyItem: replyList[index], showReplyRow: true, replyLevel: '1', - replyReply: (replyItem, currentReply) => - replyReply(replyItem, currentReply), + replyReply: + (replyItem, currentReply, loadMore) => + replyReply(replyItem, + currentReply, loadMore), replyType: ReplyType.values[replyType], addReply: (replyItem) { replyList[index] diff --git a/lib/pages/video/detail/reply_reply/view.dart b/lib/pages/video/detail/reply_reply/view.dart index 439f5d1d..06a40cd6 100644 --- a/lib/pages/video/detail/reply_reply/view.dart +++ b/lib/pages/video/detail/reply_reply/view.dart @@ -21,7 +21,7 @@ class VideoReplyReplyPanel extends StatefulWidget { this.replyType, this.sheetHeight, this.currentReply, - this.loadMore, + this.loadMore = true, super.key, }); final int? oid; @@ -32,7 +32,7 @@ class VideoReplyReplyPanel extends StatefulWidget { final ReplyType? replyType; final double? sheetHeight; final dynamic currentReply; - final bool? loadMore; + final bool loadMore; @override State createState() => _VideoReplyReplyPanelState(); @@ -142,7 +142,7 @@ class _VideoReplyReplyPanelState extends State { ), ), ], - widget.loadMore != null && widget.loadMore! + widget.loadMore ? FutureBuilder( future: _futureBuilderFuture, builder: (BuildContext context, snapshot) { From 5e6f082ade33223e30fc522ec6fb78f428a713a3 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jul 2024 11:00:19 +0800 Subject: [PATCH 290/349] =?UTF-8?q?feat:=20=E8=AF=84=E8=AE=BA=E4=BF=9D?= =?UTF-8?q?=E5=AD=98=E4=B8=BA=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/reply/widgets/reply_item.dart | 28 +++- .../detail/reply/widgets/reply_save.dart | 140 ++++++++++++++++++ 2 files changed, 166 insertions(+), 2 deletions(-) create mode 100644 lib/pages/video/detail/reply/widgets/reply_save.dart diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 5b7b0659..9fc8b11e 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -14,11 +14,13 @@ import 'package:pilipala/pages/main/index.dart'; import 'package:pilipala/pages/video/detail/index.dart'; import 'package:pilipala/pages/video/detail/reply_new/index.dart'; import 'package:pilipala/plugin/pl_gallery/index.dart'; +import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; +import 'reply_save.dart'; import 'zan.dart'; Box setting = GStrorage.setting; @@ -58,7 +60,10 @@ class ReplyItem extends StatelessWidget { useRootNavigator: true, isScrollControlled: true, builder: (context) { - return MorePanel(item: replyItem); + return MorePanel( + item: replyItem, + mainFloor: true, + ); }, ); }, @@ -1004,7 +1009,12 @@ InlineSpan buildContent( class MorePanel extends StatelessWidget { final dynamic item; - const MorePanel({super.key, required this.item}); + final bool mainFloor; + const MorePanel({ + super.key, + required this.item, + this.mainFloor = false, + }); Future menuActionHandler(String type) async { String message = item.content.message ?? item.content; @@ -1026,6 +1036,13 @@ class MorePanel extends StatelessWidget { }, ); break; + case 'save': + Get.back(); + Navigator.push( + Get.context!, + PlPopupRoute(child: ReplySave(replyItem: item)), + ); + break; // case 'block': // SmartDialog.showToast('加入黑名单'); // break; @@ -1076,6 +1093,13 @@ class MorePanel extends StatelessWidget { leading: const Icon(Icons.copy_outlined, size: 19), title: Text('自由复制', style: textTheme.titleSmall), ), + if (mainFloor) + ListTile( + onTap: () async => await menuActionHandler('save'), + minLeadingWidth: 0, + leading: const Icon(Icons.save_alt_rounded, size: 19), + title: Text('本地保存', style: textTheme.titleSmall), + ), // ListTile( // onTap: () async => await menuActionHandler('block'), // minLeadingWidth: 0, diff --git a/lib/pages/video/detail/reply/widgets/reply_save.dart b/lib/pages/video/detail/reply/widgets/reply_save.dart new file mode 100644 index 00000000..c083806f --- /dev/null +++ b/lib/pages/video/detail/reply/widgets/reply_save.dart @@ -0,0 +1,140 @@ +import 'dart:math'; +import 'dart:typed_data'; +import 'dart:ui'; +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/models/video/reply/item.dart'; +import 'package:pilipala/pages/video/detail/reply/widgets/reply_item.dart'; +import 'package:saver_gallery/saver_gallery.dart'; + +class ReplySave extends StatefulWidget { + final ReplyItemModel? replyItem; + const ReplySave({required this.replyItem, super.key}); + + @override + State createState() => _ReplySaveState(); +} + +class _ReplySaveState extends State { + final _boundaryKey = GlobalKey(); + + void _generatePicWidget() async { + SmartDialog.showLoading(msg: '保存中'); + try { + RenderRepaintBoundary boundary = _boundaryKey.currentContext! + .findRenderObject() as RenderRepaintBoundary; + var image = await boundary.toImage(pixelRatio: 3); + ByteData? byteData = await image.toByteData(format: ImageByteFormat.png); + Uint8List pngBytes = byteData!.buffer.asUint8List(); + String picName = + "plpl_reply_${DateTime.now().toString().replaceAll(RegExp(r'[- :]'), '').split('.').first}"; + final result = await SaverGallery.saveImage( + Uint8List.fromList(pngBytes), + name: '$picName.png', + androidRelativePath: "Pictures/PiliPala", + androidExistNotSave: false, + ); + if (result.isSuccess) { + SmartDialog.showToast('保存成功'); + } + } catch (err) { + print(err); + } finally { + SmartDialog.dismiss(); + } + } + + List _createWidgets(int count, Widget Function() builder) { + return List.generate(count, (_) => Expanded(child: builder())); + } + + List _createColumnWidgets() { + return _createWidgets(3, () => Row(children: _createRowWidgets())); + } + + List _createRowWidgets() { + return _createWidgets( + 4, + () => Center( + child: Transform.rotate( + angle: pi / 10, + child: const Text( + 'PiliPala', + style: TextStyle( + color: Color(0x08000000), + fontSize: 18, + fontWeight: FontWeight.bold, + decoration: TextDecoration.none, + ), + ), + ), + ), + ); + } + + @override + Widget build(BuildContext context) { + return Container( + width: Get.width, + height: Get.height, + margin: EdgeInsets.fromLTRB( + 0, + MediaQuery.of(context).padding.top + 4, + 0, + MediaQuery.of(context).padding.bottom + 4, + ), + color: Colors.transparent, + child: Column( + children: [ + Expanded( + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + ), + child: Center( + child: SingleChildScrollView( + child: RepaintBoundary( + key: _boundaryKey, + child: IntrinsicHeight( + child: Stack( + children: [ + ReplyItem( + replyItem: widget.replyItem, + showReplyRow: false, + ), + Positioned.fill( + child: Column( + children: _createColumnWidgets(), + ), + ), + ], + ), + ), + ), + ), + ), + ), + ), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FilledButton( + onPressed: () => Get.back(), + child: const Text('取消'), + ), + const SizedBox(width: 40), + FilledButton( + onPressed: _generatePicWidget, + child: const Text('保存'), + ), + ], + ), + ], + ), + ); + } +} From a2cbcca9091ce10dd041bb99fe6aba2f303df1eb Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jul 2024 11:02:42 +0800 Subject: [PATCH 291/349] =?UTF-8?q?fix:=20=E9=A6=96=E9=A1=B5=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/models/home/rcmd/result.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/models/home/rcmd/result.dart b/lib/models/home/rcmd/result.dart index 98beda59..88657b33 100644 --- a/lib/models/home/rcmd/result.dart +++ b/lib/models/home/rcmd/result.dart @@ -62,7 +62,7 @@ class RecVideoItemAppModel { duration = json['player_args'] != null ? json['player_args']['duration'] : -1; //duration = json['cover_right_text']; - title = json['title']; + title = json['title'] ?? '获取标题失败'; owner = RcmdOwner.fromJson(json); rcmdReason = json['bottom_rcmd_reason'] ?? json['top_rcmd_reason']; // 由于app端api并不会直接返回与owner的关注状态 @@ -74,7 +74,7 @@ class RecVideoItemAppModel { rcmdReason = null; } goto = json['goto']; - param = int.parse(json['param']); + param = int.parse(json['param'] ?? '-1'); uri = json['uri']; talkBack = json['talk_back']; From 8e0fbb2a54391dd84d536ab6ef6d78f8bfbf850f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 27 Jul 2024 22:08:12 +0800 Subject: [PATCH 292/349] =?UTF-8?q?opt:=20=E9=9D=9Epip=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E8=87=B3=E5=90=8E=E5=8F=B0=E4=B8=8D=E5=85=B3?= =?UTF-8?q?=E9=97=ADBottomSheet?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/view.dart | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 331986dd..0388e962 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -330,12 +330,14 @@ class _VideoDetailPageState extends State plPlayerController?.danmakuController?.clear(); break; case 'pause': - vdCtr.hiddenReplyReplyPanel(); - if (vdCtr.videoType == SearchType.video) { - videoIntroController.hiddenEpisodeBottomSheet(); - } - if (vdCtr.videoType == SearchType.media_bangumi) { - bangumiIntroController.hiddenEpisodeBottomSheet(); + if (autoPiP) { + vdCtr.hiddenReplyReplyPanel(); + if (vdCtr.videoType == SearchType.video) { + videoIntroController.hiddenEpisodeBottomSheet(); + } + if (vdCtr.videoType == SearchType.media_bangumi) { + bangumiIntroController.hiddenEpisodeBottomSheet(); + } } break; } From 66edac428b99b1ff45860f9c6ed5b2ae66e51925 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 4 Aug 2024 00:35:42 +0800 Subject: [PATCH 293/349] =?UTF-8?q?mod:=20=E8=A1=A5=E5=85=85=E5=90=88?= =?UTF-8?q?=E9=9B=86=E7=B1=BB=E5=9E=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 2 ++ lib/http/member.dart | 36 ++++++++++++++++++++ lib/models/member/seasons.dart | 13 ++++++++ lib/pages/member/widgets/seasons.dart | 23 +++++++++++-- lib/pages/member_seasons/controller.dart | 42 +++++++++++++++++++++--- lib/pages/member_seasons/view.dart | 7 ++-- 6 files changed, 114 insertions(+), 9 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 46bbb6ac..31e5a38b 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -487,6 +487,8 @@ class Api { static const getSeasonDetailApi = '/x/polymer/web-space/seasons_archives_list'; + static const getSeriesDetailApi = '/x/series/archives'; + /// 获取未读动态数 static const getUnreadDynamic = '/x/web-interface/dynamic/entrance'; diff --git a/lib/http/member.dart b/lib/http/member.dart index 0fb010b0..e87aa42e 100644 --- a/lib/http/member.dart +++ b/lib/http/member.dart @@ -520,4 +520,40 @@ class MemberHttp { }; } } + + static Future getSeriesDetail({ + required int mid, + required int currentMid, + required int seriesId, + required int pn, + }) async { + var res = await Request().get( + Api.getSeriesDetailApi, + data: { + 'mid': mid, + 'series_id': seriesId, + 'only_normal': true, + 'sort': 'desc', + 'pn': pn, + 'ps': 30, + 'current_mid': currentMid, + }, + ); + if (res.data['code'] == 0) { + try { + return { + 'status': true, + 'data': MemberSeasonsDataModel.fromJson(res.data['data']) + }; + } catch (err) { + print(err); + } + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/models/member/seasons.dart b/lib/models/member/seasons.dart index 88b93c78..275466a6 100644 --- a/lib/models/member/seasons.dart +++ b/lib/models/member/seasons.dart @@ -2,10 +2,12 @@ class MemberSeasonsDataModel { MemberSeasonsDataModel({ this.page, this.seasonsList, + this.seriesList, }); Map? page; List? seasonsList; + List? seriesList; MemberSeasonsDataModel.fromJson(Map json) { page = json['page']; @@ -19,6 +21,11 @@ class MemberSeasonsDataModel { .map((e) => MemberSeasonsList.fromJson(e)) .toList() : []; + seriesList = json['archives'] != null + ? json['archives'] + .map((e) => MemberArchiveItem.fromJson(e)) + .toList() + : []; seasonsList = [...tempList1, ...tempList2]; } @@ -93,6 +100,8 @@ class MamberMeta { this.ptime, this.seasonId, this.total, + this.seriesId, + this.category, }); String? cover; @@ -102,6 +111,8 @@ class MamberMeta { int? ptime; int? seasonId; int? total; + int? seriesId; + int? category; MamberMeta.fromJson(Map json) { cover = json['cover']; @@ -111,5 +122,7 @@ class MamberMeta { ptime = json['ptime']; seasonId = json['season_id']; total = json['total']; + seriesId = json['series_id']; + category = json['category']; } } diff --git a/lib/pages/member/widgets/seasons.dart b/lib/pages/member/widgets/seasons.dart index 1367d6bd..1749ff45 100644 --- a/lib/pages/member/widgets/seasons.dart +++ b/lib/pages/member/widgets/seasons.dart @@ -24,8 +24,27 @@ class MemberSeasonsPanel extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ ListTile( - onTap: () => Get.toNamed( - '/memberSeasons?mid=${item.meta!.mid}&seasonId=${item.meta!.seasonId}&seasonName=${item.meta!.name}'), + onTap: () { + final int category = item.meta!.category!; + Map parameters = {}; + if (category == 0) { + parameters = { + 'category': '0', + 'mid': item.meta!.mid.toString(), + 'seasonId': item.meta!.seasonId.toString(), + 'seasonName': item.meta!.name!, + }; + } + if (category == 1) { + parameters = { + 'category': '1', + 'mid': item.meta!.mid.toString(), + 'seriesId': item.meta!.seriesId.toString(), + 'seasonName': item.meta!.name!, + }; + } + Get.toNamed('/memberSeasons', parameters: parameters); + }, title: Text( item.meta!.name!, maxLines: 1, diff --git a/lib/pages/member_seasons/controller.dart b/lib/pages/member_seasons/controller.dart index 82ef0af0..58a9035f 100644 --- a/lib/pages/member_seasons/controller.dart +++ b/lib/pages/member_seasons/controller.dart @@ -6,7 +6,9 @@ import 'package:pilipala/models/member/seasons.dart'; class MemberSeasonsController extends GetxController { final ScrollController scrollController = ScrollController(); late int mid; - late int seasonId; + int? seasonId; + int? seriesId; + late String category; int pn = 1; int ps = 30; int count = 0; @@ -17,17 +19,23 @@ class MemberSeasonsController extends GetxController { void onInit() { super.onInit(); mid = int.parse(Get.parameters['mid']!); - seasonId = int.parse(Get.parameters['seasonId']!); + category = Get.parameters['category']!; + if (category == '0') { + seasonId = int.parse(Get.parameters['seriesId']!); + } + if (category == '1') { + seriesId = int.parse(Get.parameters['seriesId']!); + } } - // 获取专栏详情 + // 获取专栏详情 0: 专栏 1: 系列 Future getSeasonDetail(type) async { if (type == 'onRefresh') { pn = 1; } var res = await MemberHttp.getSeasonDetail( mid: mid, - seasonId: seasonId, + seasonId: seasonId!, pn: pn, ps: ps, sortReverse: false, @@ -40,8 +48,32 @@ class MemberSeasonsController extends GetxController { return res; } + // 获取系列详情 0: 专栏 1: 系列 + Future getSeriesDetail(type) async { + if (type == 'onRefresh') { + pn = 1; + } + var res = await MemberHttp.getSeriesDetail( + mid: mid, + seriesId: seriesId!, + pn: pn, + currentMid: 17340771, + ); + if (res['status']) { + seasonsList.addAll(res['data'].seriesList); + page = res['data'].page; + pn += 1; + } + return res; + } + // 上拉加载 Future onLoad() async { - getSeasonDetail('onLoad'); + if (category == '0') { + getSeasonDetail('onLoad'); + } + if (category == '1') { + getSeriesDetail('onLoad'); + } } } diff --git a/lib/pages/member_seasons/view.dart b/lib/pages/member_seasons/view.dart index 556e2ec5..b8c0407d 100644 --- a/lib/pages/member_seasons/view.dart +++ b/lib/pages/member_seasons/view.dart @@ -17,12 +17,15 @@ class _MemberSeasonsPageState extends State { Get.put(MemberSeasonsController()); late Future _futureBuilderFuture; late ScrollController scrollController; + late String category; @override void initState() { super.initState(); - _futureBuilderFuture = - _memberSeasonsController.getSeasonDetail('onRefresh'); + category = Get.parameters['category']!; + _futureBuilderFuture = category == '0' + ? _memberSeasonsController.getSeasonDetail('onRefresh') + : _memberSeasonsController.getSeriesDetail('onRefresh'); scrollController = _memberSeasonsController.scrollController; scrollController.addListener( () { From 0bb9a68cc55cb65bbc1a5bf41c683a47f8d9827d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 9 Aug 2024 18:39:16 +0800 Subject: [PATCH 294/349] =?UTF-8?q?mod:=20=E4=BF=9D=E5=AD=98=E5=B0=81?= =?UTF-8?q?=E9=9D=A2=E6=97=B6=E8=A7=86=E9=A2=91=E6=A0=87=E9=A2=98=E5=8F=AF?= =?UTF-8?q?=E9=80=89=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/utils/image_save.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/utils/image_save.dart b/lib/utils/image_save.dart index 0b77b7cc..99513ac8 100644 --- a/lib/utils/image_save.dart +++ b/lib/utils/image_save.dart @@ -57,7 +57,7 @@ Future imageSaveDialog(context, videoItem, closeFn) { child: Row( children: [ Expanded( - child: Text( + child: SelectableText( videoItem.title! as String, style: Theme.of(context).textTheme.titleSmall, ), From 61337338bd42d0e43ffcee264a952b5d52318c6a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 10 Aug 2024 23:11:21 +0800 Subject: [PATCH 295/349] =?UTF-8?q?fix:=20=E5=9B=BE=E7=89=87=E9=A2=84?= =?UTF-8?q?=E8=A7=88Hero=20tag=E9=87=8D=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../video/detail/reply/widgets/reply_item.dart | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 5b7b0659..5f53b398 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -1,3 +1,5 @@ +import 'dart:math'; + import 'package:appscheme/appscheme.dart'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/gestures.dart'; @@ -549,7 +551,7 @@ InlineSpan buildContent( ); } - void onPreviewImg(picList, initIndex) { + void onPreviewImg(picList, initIndex, randomInt) { final MainController mainController = Get.find(); mainController.imgPreviewStatus = true; Navigator.of(context).push( @@ -575,7 +577,7 @@ InlineSpan buildContent( }, child: Center( child: Hero( - tag: picList[index], + tag: picList[index] + randomInt, child: CachedNetworkImage( fadeInDuration: const Duration(milliseconds: 0), imageUrl: picList[index], @@ -886,11 +888,12 @@ InlineSpan buildContent( pictureItem['img_width'])) .truncateToDouble(); } catch (_) {} + String randomInt = Random().nextInt(101).toString(); return Hero( - tag: picList[0], + tag: picList[0] + randomInt, child: GestureDetector( - onTap: () => onPreviewImg(picList, 0), + onTap: () => onPreviewImg(picList, 0, randomInt), child: Container( padding: const EdgeInsets.only(top: 4), constraints: BoxConstraints(maxHeight: maxHeight), @@ -927,13 +930,14 @@ InlineSpan buildContent( picList.add(content.pictures[i]['img_src']); } for (var i = 0; i < len; i++) { + String randomInt = Random().nextInt(101).toString(); list.add( LayoutBuilder( builder: (context, BoxConstraints box) { return Hero( - tag: picList[i], + tag: picList[i] + randomInt, child: GestureDetector( - onTap: () => onPreviewImg(picList, i), + onTap: () => onPreviewImg(picList, i, randomInt), child: NetworkImgLayer( src: picList[i], width: box.maxWidth, From b188675faf2607e9e5d3eb5c60da0ae3863203fa Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 11 Aug 2024 18:43:21 +0800 Subject: [PATCH 296/349] opt --- .../detail/reply/widgets/reply_item.dart | 43 +++++-- .../detail/reply/widgets/reply_save.dart | 106 ++++++++++-------- 2 files changed, 88 insertions(+), 61 deletions(-) diff --git a/lib/pages/video/detail/reply/widgets/reply_item.dart b/lib/pages/video/detail/reply/widgets/reply_item.dart index 9fc8b11e..ae691f97 100644 --- a/lib/pages/video/detail/reply/widgets/reply_item.dart +++ b/lib/pages/video/detail/reply/widgets/reply_item.dart @@ -17,6 +17,7 @@ import 'package:pilipala/plugin/pl_gallery/index.dart'; import 'package:pilipala/plugin/pl_popup/index.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/id_utils.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/url_utils.dart'; import 'package:pilipala/utils/utils.dart'; @@ -33,6 +34,7 @@ class ReplyItem extends StatelessWidget { this.showReplyRow = true, this.replyReply, this.replyType, + this.replySave = false, super.key, }); final ReplyItemModel? replyItem; @@ -41,6 +43,7 @@ class ReplyItem extends StatelessWidget { final bool? showReplyRow; final Function? replyReply; final ReplyType? replyType; + final bool? replySave; @override Widget build(BuildContext context) { @@ -48,12 +51,18 @@ class ReplyItem extends StatelessWidget { child: InkWell( // 点击整个评论区 评论详情/回复 onTap: () { + if (replySave!) { + return; + } feedBack(); if (replyReply != null) { replyReply!(replyItem, null, replyItem!.replies!.isNotEmpty); } }, onLongPress: () { + if (replySave!) { + return; + } feedBack(); showModalBottomSheet( context: context, @@ -236,7 +245,7 @@ class ReplyItem extends StatelessWidget { ), ), // 操作区域 - bottonAction(context, replyItem!.replyControl), + bottonAction(context, replyItem!.replyControl, replySave), // 一楼的评论 if ((replyItem!.replyControl!.isShow! || replyItem!.replies!.isNotEmpty) && @@ -257,7 +266,7 @@ class ReplyItem extends StatelessWidget { } // 感谢、回复、复制 - Widget bottonAction(BuildContext context, replyControl) { + Widget bottonAction(BuildContext context, replyControl, replySave) { ColorScheme colorScheme = Theme.of(context).colorScheme; TextTheme textTheme = Theme.of(context).textTheme; return Row( @@ -290,16 +299,26 @@ class ReplyItem extends StatelessWidget { }); }, child: Row(children: [ - Icon(Icons.reply, - size: 18, color: colorScheme.outline.withOpacity(0.8)), - const SizedBox(width: 3), - Text( - '回复', - style: TextStyle( - fontSize: textTheme.labelMedium!.fontSize, - color: colorScheme.outline, + if (!replySave!) ...[ + Icon(Icons.reply, + size: 18, color: colorScheme.outline.withOpacity(0.8)), + const SizedBox(width: 3), + Text( + '回复', + style: TextStyle( + fontSize: textTheme.labelMedium!.fontSize, + color: colorScheme.outline, + ), + ) + ], + if (replySave!) + Text( + IdUtils.av2bv(replyItem!.oid!), + style: TextStyle( + fontSize: textTheme.labelMedium!.fontSize, + color: colorScheme.outline, + ), ), - ), ]), ), ), @@ -1093,7 +1112,7 @@ class MorePanel extends StatelessWidget { leading: const Icon(Icons.copy_outlined, size: 19), title: Text('自由复制', style: textTheme.titleSmall), ), - if (mainFloor) + if (mainFloor && item.content.pictures.isEmpty) ListTile( onTap: () async => await menuActionHandler('save'), minLeadingWidth: 0, diff --git a/lib/pages/video/detail/reply/widgets/reply_save.dart b/lib/pages/video/detail/reply/widgets/reply_save.dart index c083806f..9eed4923 100644 --- a/lib/pages/video/detail/reply/widgets/reply_save.dart +++ b/lib/pages/video/detail/reply/widgets/reply_save.dart @@ -76,64 +76,72 @@ class _ReplySaveState extends State { @override Widget build(BuildContext context) { - return Container( - width: Get.width, - height: Get.height, - margin: EdgeInsets.fromLTRB( - 0, - MediaQuery.of(context).padding.top + 4, - 0, - MediaQuery.of(context).padding.bottom + 4, - ), - color: Colors.transparent, - child: Column( - children: [ - Expanded( - child: Container( - clipBehavior: Clip.antiAlias, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20), - ), - child: Center( - child: SingleChildScrollView( - child: RepaintBoundary( - key: _boundaryKey, - child: IntrinsicHeight( - child: Stack( - children: [ - ReplyItem( - replyItem: widget.replyItem, - showReplyRow: false, + return SafeArea( + top: false, + bottom: false, + child: BackdropFilter( + filter: ImageFilter.blur(sigmaX: 4.0, sigmaY: 4.0), + child: Container( + width: Get.width, + height: Get.height, + padding: EdgeInsets.fromLTRB( + 0, + MediaQuery.of(context).padding.top + 4, + 0, + MediaQuery.of(context).padding.bottom + 4, + ), + color: Colors.black54, + child: Column( + children: [ + Expanded( + child: Container( + clipBehavior: Clip.antiAlias, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20), + ), + child: Center( + child: SingleChildScrollView( + child: RepaintBoundary( + key: _boundaryKey, + child: IntrinsicHeight( + child: Stack( + children: [ + ReplyItem( + replyItem: widget.replyItem, + showReplyRow: false, + replySave: true, + ), + Positioned.fill( + child: Column( + children: _createColumnWidgets(), + ), + ), + ], ), - Positioned.fill( - child: Column( - children: _createColumnWidgets(), - ), - ), - ], + ), ), ), ), ), ), - ), - ), - const SizedBox(height: 20), - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - FilledButton( - onPressed: () => Get.back(), - child: const Text('取消'), - ), - const SizedBox(width: 40), - FilledButton( - onPressed: _generatePicWidget, - child: const Text('保存'), + const SizedBox(height: 20), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FilledButton( + onPressed: () => Get.back(), + child: const Text('取消'), + ), + const SizedBox(width: 40), + FilledButton( + onPressed: _generatePicWidget, + child: const Text('保存'), + ), + ], ), ], ), - ], + ), ), ); } From ec30235421b13dfba26b3b7a7685d1402eff0709 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 11 Aug 2024 23:16:33 +0800 Subject: [PATCH 297/349] =?UTF-8?q?opt:=20=E4=B8=80=E9=94=AE=E4=B8=89?= =?UTF-8?q?=E8=BF=9E=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 1e8d97f1..760976ae 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -613,6 +613,9 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } _controller.reverse(); }, + onTapCancel: () { + _controller.reverse(); + }, borderRadius: StyleString.mdRadius, child: SizedBox( width: (Get.size.width - 24) / 5, From 1012b5d00907e05616833a4f71f53300913d31ed Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 18 Aug 2024 23:30:51 +0800 Subject: [PATCH 298/349] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 + lib/http/init.dart | 3 + lib/http/live.dart | 19 ++ lib/models/live/message.dart | 101 +++++++++++ lib/pages/live_room/controller.dart | 78 ++++++++ lib/pages/live_room/view.dart | 269 ++++++++++++++++++++++++++-- lib/plugin/pl_socket/index.dart | 107 +++++++++++ lib/utils/binary_writer.dart | 117 ++++++++++++ lib/utils/live.dart | 196 ++++++++++++++++++++ pubspec.lock | 14 +- pubspec.yaml | 2 + 11 files changed, 890 insertions(+), 20 deletions(-) create mode 100644 lib/models/live/message.dart create mode 100644 lib/plugin/pl_socket/index.dart create mode 100644 lib/utils/binary_writer.dart create mode 100644 lib/utils/live.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 31e5a38b..08a20382 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -554,4 +554,8 @@ class Api { /// 系统通知 static const String messageSystemAPi = '${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify'; + + /// 直播间弹幕信息 + static const String getDanmuInfo = + '${HttpString.liveBaseUrl}/xlive/web-room/v1/index/getDanmuInfo'; } diff --git a/lib/http/init.dart b/lib/http/init.dart index cb9d6f39..faa57dd5 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -29,6 +29,7 @@ class Request { late String systemProxyPort; static final RegExp spmPrefixExp = RegExp(r''); + static late String buvid; /// 设置cookie static setCookie() async { @@ -70,6 +71,8 @@ class Request { final String cookieString = cookie .map((Cookie cookie) => '${cookie.name}=${cookie.value}') .join('; '); + + buvid = cookie.firstWhere((e) => e.name == 'buvid3').value; dio.options.headers['cookie'] = cookieString; } diff --git a/lib/http/live.dart b/lib/http/live.dart index e624120e..a405fd58 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -65,4 +65,23 @@ class LiveHttp { }; } } + + // 获取弹幕信息 + static Future liveDanmakuInfo({roomId}) async { + var res = await Request().get(Api.getDanmuInfo, data: { + 'id': roomId, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/models/live/message.dart b/lib/models/live/message.dart new file mode 100644 index 00000000..cd0f4b75 --- /dev/null +++ b/lib/models/live/message.dart @@ -0,0 +1,101 @@ +class LiveMessageModel { + // 消息类型 + final LiveMessageType type; + + // 用户名 + final String userName; + + // 信息 + final String? message; + + // 数据 + final dynamic data; + + final String? face; + final int? uid; + final Map? emots; + + // 颜色 + final LiveMessageColor color; + + LiveMessageModel({ + required this.type, + required this.userName, + required this.message, + required this.color, + this.data, + this.face, + this.uid, + this.emots, + }); +} + +class LiveSuperChatMessage { + final String backgroundBottomColor; + final String backgroundColor; + final DateTime endTime; + final String face; + final String message; + final String price; + final DateTime startTime; + final String userName; + + LiveSuperChatMessage({ + required this.backgroundBottomColor, + required this.backgroundColor, + required this.endTime, + required this.face, + required this.message, + required this.price, + required this.startTime, + required this.userName, + }); +} + +enum LiveMessageType { + // 普通留言 + chat, + // 醒目留言 + superChat, + // + online, + // 加入 + join, + // 关注 + follow, +} + +class LiveMessageColor { + final int r, g, b; + LiveMessageColor(this.r, this.g, this.b); + static LiveMessageColor get white => LiveMessageColor(255, 255, 255); + static LiveMessageColor numberToColor(int intColor) { + var obj = intColor.toRadixString(16); + + LiveMessageColor color = LiveMessageColor.white; + if (obj.length == 4) { + obj = "00$obj"; + } + if (obj.length == 6) { + var R = int.parse(obj.substring(0, 2), radix: 16); + var G = int.parse(obj.substring(2, 4), radix: 16); + var B = int.parse(obj.substring(4, 6), radix: 16); + + color = LiveMessageColor(R, G, B); + } + if (obj.length == 8) { + var R = int.parse(obj.substring(2, 4), radix: 16); + var G = int.parse(obj.substring(4, 6), radix: 16); + var B = int.parse(obj.substring(6, 8), radix: 16); + //var A = int.parse(obj.substring(0, 2), radix: 16); + color = LiveMessageColor(R, G, B); + } + + return color; + } + + @override + String toString() { + return "#${r.toRadixString(16).padLeft(2, '0')}${g.toRadixString(16).padLeft(2, '0')}${b.toRadixString(16).padLeft(2, '0')}"; + } +} diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 4e67fa2c..fa95ce63 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,10 +1,16 @@ +import 'dart:convert'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; +import 'package:hive/hive.dart'; import 'package:pilipala/http/constants.dart'; +import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/live.dart'; +import 'package:pilipala/models/live/message.dart'; import 'package:pilipala/models/live/quality.dart'; import 'package:pilipala/models/live/room_info.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; +import 'package:pilipala/plugin/pl_socket/index.dart'; +import 'package:pilipala/utils/live.dart'; import '../../models/live/room_info_h5.dart'; import '../../utils/storage.dart'; import '../../utils/video_utils.dart'; @@ -24,6 +30,13 @@ class LiveRoomController extends GetxController { int? tempCurrentQn; late List> acceptQnList; RxString currentQnDesc = ''.obs; + Box userInfoCache = GStrorage.userInfo; + int userId = 0; + PlSocket? plSocket; + List danmuHostList = []; + String token = ''; + // 弹幕消息列表 + RxList messageList = [].obs; @override void onInit() { @@ -43,6 +56,11 @@ class LiveRoomController extends GetxController { } // CDN优化 enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); + final userInfo = userInfoCache.get('userInfoCache'); + if (userInfo != null && userInfo.mid != null) { + userId = userInfo.mid; + } + liveDanmakuInfo().then((value) => initSocket()); } playerInit(source) async { @@ -127,4 +145,64 @@ class LiveRoomController extends GetxController { .description; await queryLiveInfo(); } + + Future liveDanmakuInfo() async { + var res = await LiveHttp.liveDanmakuInfo(roomId: roomId); + if (res['status']) { + danmuHostList = (res["data"]["host_list"] as List) + .map((e) => '${e["host"]}:${e['wss_port']}') + .toList(); + token = res["data"]["token"]; + return res; + } + } + + // 建立socket + void initSocket() async { + final wsUrl = danmuHostList.isNotEmpty + ? danmuHostList.first + : "broadcastlv.chat.bilibili.com"; + plSocket = PlSocket( + url: 'wss://$wsUrl/sub', + heartTime: 30, + onReadyCb: () { + joinRoom(); + }, + onMessageCb: (message) { + final List? liveMsg = + LiveUtils.decodeMessage(message); + if (liveMsg != null) { + messageList.addAll(liveMsg + .where((msg) => msg.type == LiveMessageType.chat) + .toList()); + } + }, + onErrorCb: (e) { + print('error: $e'); + }, + ); + await plSocket?.connect(); + } + + void joinRoom() async { + var joinData = LiveUtils.encodeData( + json.encode({ + "uid": userId, + "roomid": roomId, + "protover": 3, + "buvid": Request.buvid, + "platform": "web", + "type": 2, + "key": token, + }), + 7, + ); + plSocket?.sendMessage(joinData); + } + + @override + void onClose() { + plSocket?.onClose(); + super.onClose(); + } } diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 37981b1d..d9b316e9 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -1,9 +1,12 @@ import 'dart:io'; import 'package:floating/floating.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/models/live/message.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'controller.dart'; @@ -16,7 +19,8 @@ class LiveRoomPage extends StatefulWidget { State createState() => _LiveRoomPageState(); } -class _LiveRoomPageState extends State { +class _LiveRoomPageState extends State + with TickerProviderStateMixin { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); PlPlayerController? plPlayerController; late Future? _futureBuilder; @@ -25,6 +29,9 @@ class _LiveRoomPageState extends State { bool isShowCover = true; bool isPlay = true; Floating? floating; + final ScrollController _scrollController = ScrollController(); + late AnimationController fabAnimationCtr; + bool _shouldAutoScroll = true; @override void initState() { @@ -34,6 +41,13 @@ class _LiveRoomPageState extends State { } videoSourceInit(); _futureBuilderFuture = _liveRoomController.queryLiveInfo(); + // 监听滚动事件 + _scrollController.addListener(_onScroll); + fabAnimationCtr = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 300), + value: 0.0, + ); } Future videoSourceInit() async { @@ -41,12 +55,52 @@ class _LiveRoomPageState extends State { plPlayerController = _liveRoomController.plPlayerController; } + void _onScroll() { + // 反向时,展示按钮 + if (_scrollController.position.userScrollDirection == + ScrollDirection.forward) { + _shouldAutoScroll = false; + fabAnimationCtr.forward(); + } else { + _shouldAutoScroll = true; + fabAnimationCtr.reverse(); + } + } + + // 监听messageList的变化,自动滚动到底部 + @override + void didChangeDependencies() { + super.didChangeDependencies(); + _liveRoomController.messageList.listen((_) { + if (_shouldAutoScroll) { + _scrollToBottom(); + } + }); + } + + void _scrollToBottom() { + if (_scrollController.hasClients) { + _scrollController + .animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration(milliseconds: 300), + curve: Curves.easeOut, + ) + .then((value) { + _shouldAutoScroll = true; + // fabAnimationCtr.forward(); + }); + } + } + @override void dispose() { plPlayerController!.dispose(); if (floating != null) { floating!.dispose(); } + _scrollController.dispose(); + fabAnimationCtr.dispose(); super.dispose(); } @@ -80,20 +134,6 @@ class _LiveRoomPageState extends State { backgroundColor: Colors.black, body: Stack( children: [ - Positioned( - left: 0, - right: 0, - bottom: 0, - child: Opacity( - opacity: 0.8, - child: Image.asset( - 'assets/images/live/default_bg.webp', - fit: BoxFit.cover, - // width: Get.width, - // height: Get.height, - ), - ), - ), Obx( () => Positioned( left: 0, @@ -106,7 +146,7 @@ class _LiveRoomPageState extends State { .roomInfoH5.value.roomInfo?.appBackground != null ? Opacity( - opacity: 0.8, + opacity: 0.6, child: NetworkImgLayer( width: Get.width, height: Get.height, @@ -116,7 +156,15 @@ class _LiveRoomPageState extends State { '', ), ) - : const SizedBox(), + : Opacity( + opacity: 0.6, + child: Image.asset( + 'assets/images/live/default_bg.webp', + fit: BoxFit.cover, + // width: Get.width, + // height: Get.height, + ), + ), ), ), Column( @@ -198,8 +246,45 @@ class _LiveRoomPageState extends State { child: videoPlayerPanel, ), ), + const SizedBox(height: 20), + // 显示消息的列表 + buildMessageListUI( + context, + _liveRoomController, + _scrollController, + ), + // 底部安全距离 + SizedBox( + height: MediaQuery.of(context).padding.bottom + 20, + ) ], ), + // 定位 快速滑动到底部 + Positioned( + right: 20, + bottom: MediaQuery.of(context).padding.bottom + 20, + child: SlideTransition( + position: Tween( + begin: const Offset(0, 2), + end: const Offset(0, 0), + ).animate(CurvedAnimation( + parent: fabAnimationCtr, + curve: Curves.easeInOut, + )), + child: ElevatedButton.icon( + onPressed: () { + _scrollToBottom(); + }, + icon: const Icon(Icons.keyboard_arrow_down), // 图标 + label: const Text('新消息'), // 文字 + style: ElevatedButton.styleFrom( + // primary: Colors.blue, // 按钮背景颜色 + // onPrimary: Colors.white, // 按钮文字颜色 + padding: const EdgeInsets.fromLTRB(14, 12, 20, 12), // 按钮内边距 + ), + ), + ), + ), ], ), ); @@ -214,3 +299,153 @@ class _LiveRoomPageState extends State { } } } + +Widget buildMessageListUI( + BuildContext context, + LiveRoomController liveRoomController, + ScrollController scrollController, +) { + return Expanded( + child: Obx( + () => MediaQuery.removePadding( + context: context, + removeTop: true, + removeBottom: true, + child: ShaderMask( + shaderCallback: (Rect bounds) { + return const LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [ + Colors.transparent, + Colors.black, + Colors.black, + ], + stops: [0.0, 0.1, 1.0], + ).createShader(bounds); + }, + blendMode: BlendMode.dstIn, + child: ListView.builder( + controller: scrollController, + itemCount: liveRoomController.messageList.length, + itemBuilder: (context, index) { + final LiveMessageModel liveMsgItem = + liveRoomController.messageList[index]; + return Padding( + padding: EdgeInsets.only( + top: index == 0 ? 40.0 : 4.0, + bottom: 4.0, + left: 20.0, + right: 20.0, + ), + child: Text.rich( + TextSpan( + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '${liveMsgItem.userName}: ', + style: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + print('Text clicked'); + }, + ), + TextSpan( + children: [ + ...buildMessageTextSpan(context, liveMsgItem) + ], + // text: liveMsgItem.message, + ), + ], + ), + ), + ); + }, + ), + ), + ), + ), + ); +} + +List buildMessageTextSpan( + BuildContext context, + LiveMessageModel liveMsgItem, +) { + final List inlineSpanList = []; + + // 是否包含表情包 + if (liveMsgItem.emots == null) { + // 没有表情包的消息 + inlineSpanList.add( + TextSpan( + text: liveMsgItem.message ?? '', + style: const TextStyle( + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 3.0, + color: Colors.black, + ), + Shadow( + offset: Offset(-1.0, -1.0), + blurRadius: 3.0, + color: Colors.black, + ), + ], + ), + ), + ); + } else { + // 有表情包的消息 使用正则匹配 表情包用图片渲染 + final List emotsKeys = liveMsgItem.emots!.keys.toList(); + final RegExp pattern = RegExp(emotsKeys.map(RegExp.escape).join('|')); + + liveMsgItem.message?.splitMapJoin( + pattern, + onMatch: (Match match) { + final emoteItem = liveMsgItem.emots![match.group(0)]; + if (emoteItem != null) { + inlineSpanList.add( + WidgetSpan( + child: NetworkImgLayer( + width: emoteItem['width'].toDouble(), + height: emoteItem['height'].toDouble(), + type: 'emote', + src: emoteItem['url'], + ), + ), + ); + } + return ''; + }, + onNonMatch: (String nonMatch) { + inlineSpanList.add( + TextSpan( + text: nonMatch, + style: const TextStyle( + shadows: [ + Shadow( + offset: Offset(2.0, 2.0), + blurRadius: 3.0, + color: Colors.black, + ), + Shadow( + offset: Offset(-1.0, -1.0), + blurRadius: 3.0, + color: Colors.black, + ), + ], + ), + ), + ); + return nonMatch; + }, + ); + } + + return inlineSpanList; +} diff --git a/lib/plugin/pl_socket/index.dart b/lib/plugin/pl_socket/index.dart new file mode 100644 index 00000000..1ad6af94 --- /dev/null +++ b/lib/plugin/pl_socket/index.dart @@ -0,0 +1,107 @@ +import 'dart:async'; + +import 'package:pilipala/utils/live.dart'; +import 'package:web_socket_channel/io.dart'; +import 'package:web_socket_channel/web_socket_channel.dart'; + +enum SocketStatus { + connected, + failed, + closed, +} + +class PlSocket { + SocketStatus status = SocketStatus.closed; + // 链接 + final String url; + // 心跳时间 + final int heartTime; + // 监听初始化完成 + final Function? onReadyCb; + // 监听关闭 + final Function? onCloseCb; + // 监听异常 + final Function? onErrorCb; + // 监听消息 + final Function? onMessageCb; + // 请求头 + final Map? headers; + + PlSocket({ + required this.url, + required this.heartTime, + this.onReadyCb, + this.onCloseCb, + this.onErrorCb, + this.onMessageCb, + this.headers, + }); + + WebSocketChannel? channel; + StreamSubscription? channelStreamSub; + + // 建立连接 + Future connect() async { + // 连接之前关闭上次连接 + onClose(); + try { + channel = IOWebSocketChannel.connect( + url, + connectTimeout: const Duration(seconds: 15), + headers: null, + ); + await channel?.ready; + onReady(); + } catch (err) { + connect(); + onError(err); + } + } + + // 初始化完成 + void onReady() { + status = SocketStatus.connected; + onReadyCb?.call(); + channelStreamSub = channel?.stream.listen((message) { + onMessageCb?.call(message); + }, onDone: () { + // 流被关闭 + print('结束了'); + }, onError: (err) { + onError(err); + }); + // 每30s发送心跳 + Timer.periodic(Duration(seconds: heartTime), (timer) { + if (status == SocketStatus.connected) { + sendMessage(LiveUtils.encodeData( + "", + 2, + )); + } else { + timer.cancel(); + } + }); + } + + // 连接关闭 + void onClose() { + status = SocketStatus.closed; + onCloseCb?.call(); + channelStreamSub?.cancel(); + channel?.sink.close(); + } + + // 连接异常 + void onError(err) { + onErrorCb?.call(err); + } + + // 接收消息 + void onMessage() {} + + void sendMessage(dynamic message) { + if (status == SocketStatus.connected) { + channel?.sink.add(message); + } + } +} diff --git a/lib/utils/binary_writer.dart b/lib/utils/binary_writer.dart new file mode 100644 index 00000000..929bc573 --- /dev/null +++ b/lib/utils/binary_writer.dart @@ -0,0 +1,117 @@ +import 'dart:typed_data'; + +class BinaryWriter { + List buffer; + int position = 0; + BinaryWriter(this.buffer); + int get length => buffer.length; + + void writeBytes(List list) { + buffer.addAll(list); + position += list.length; + } + + void writeInt(int value, int len, {Endian endian = Endian.big}) { + var bytes = _createByteData(len); + switch (len) { + case 1: + bytes.setUint8(0, value.toUnsigned(8)); + break; + case 2: + bytes.setInt16(0, value, endian); + break; + case 4: + bytes.setInt32(0, value, endian); + break; + case 8: + bytes.setInt64(0, value, endian); + break; + default: + throw ArgumentError('Invalid length for writeInt: $len'); + } + _addBytesToBuffer(bytes, len); + } + + void writeDouble(double value, int len, {Endian endian = Endian.big}) { + var bytes = _createByteData(len); + switch (len) { + case 4: + bytes.setFloat32(0, value, endian); + break; + case 8: + bytes.setFloat64(0, value, endian); + break; + default: + throw ArgumentError('Invalid length for writeDouble: $len'); + } + _addBytesToBuffer(bytes, len); + } + + ByteData _createByteData(int len) { + var b = Uint8List(len).buffer; + return ByteData.view(b); + } + + void _addBytesToBuffer(ByteData bytes, int len) { + buffer.addAll(bytes.buffer.asUint8List()); + position += len; + } +} + +class BinaryReader { + Uint8List buffer; + int position = 0; + BinaryReader(this.buffer); + int get length => buffer.length; + + int read() { + return buffer[position++]; + } + + int readInt(int len, {Endian endian = Endian.big}) { + var bytes = _getBytes(len); + var data = ByteData.view(bytes.buffer); + switch (len) { + case 1: + return data.getUint8(0); + case 2: + return data.getInt16(0, endian); + case 4: + return data.getInt32(0, endian); + case 8: + return data.getInt64(0, endian); + default: + throw ArgumentError('Invalid length for readInt: $len'); + } + } + + int readByte({Endian endian = Endian.big}) => readInt(1, endian: endian); + int readShort({Endian endian = Endian.big}) => readInt(2, endian: endian); + int readInt32({Endian endian = Endian.big}) => readInt(4, endian: endian); + int readLong({Endian endian = Endian.big}) => readInt(8, endian: endian); + + Uint8List readBytes(int len) { + var bytes = _getBytes(len); + return bytes; + } + + double readFloat(int len, {Endian endian = Endian.big}) { + var bytes = _getBytes(len); + var data = ByteData.view(bytes.buffer); + switch (len) { + case 4: + return data.getFloat32(0, endian); + case 8: + return data.getFloat64(0, endian); + default: + throw ArgumentError('Invalid length for readFloat: $len'); + } + } + + Uint8List _getBytes(int len) { + var bytes = + Uint8List.fromList(buffer.getRange(position, position + len).toList()); + position += len; + return bytes; + } +} diff --git a/lib/utils/live.dart b/lib/utils/live.dart new file mode 100644 index 00000000..dd56616e --- /dev/null +++ b/lib/utils/live.dart @@ -0,0 +1,196 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:typed_data'; +import 'package:brotli/brotli.dart'; +import 'package:pilipala/models/live/message.dart'; +import 'package:pilipala/utils/binary_writer.dart'; + +class LiveUtils { + static List encodeData(String msg, int action) { + var data = utf8.encode(msg); + //头部长度固定16 + var length = data.length + 16; + var buffer = Uint8List(length); + + var writer = BinaryWriter([]); + + //数据包长度 + writer.writeInt(buffer.length, 4); + //数据包头部长度,固定16 + writer.writeInt(16, 2); + + //协议版本,0=JSON,1=Int32,2=Buffer + writer.writeInt(0, 2); + + //操作类型 + writer.writeInt(action, 4); + + //数据包头部长度,固定1 + + writer.writeInt(1, 4); + + writer.writeBytes(data); + + return writer.buffer; + } + + static List? decodeMessage(List data) { + try { + //操作类型。3=心跳回应,内容为房间人气值;5=通知,弹幕、广播等全部信息;8=进房回应,空 + int operation = readInt(data, 8, 4); + //内容 + var body = data.skip(16).toList(); + if (operation == 3) { + var online = readInt(body, 0, 4); + final LiveMessageModel liveMsg = LiveMessageModel( + type: LiveMessageType.online, + userName: '', + message: '', + color: LiveMessageColor.white, + data: online, + ); + return [liveMsg]; + } else if (operation == 5) { + //协议版本。0为JSON,可以直接解析;1为房间人气值,Body为4位Int32;2为压缩过Buffer,需要解压再处理 + int protocolVersion = readInt(data, 6, 2); + if (protocolVersion == 2) { + body = zlib.decode(body); + } else if (protocolVersion == 3) { + body = brotli.decode(body); + } + + var text = utf8.decode(body, allowMalformed: true); + + var group = + text.split(RegExp(r"[\x00-\x1f]+", unicode: true, multiLine: true)); + List messages = []; + for (var item + in group.where((x) => x.length > 2 && x.startsWith('{'))) { + if (parseMessage(item) is LiveMessageModel) { + messages.add(parseMessage(item)!); + } + } + return messages; + } + } catch (e) { + print(e); + } + return null; + } + + static LiveMessageModel? parseMessage(String jsonMessage) { + try { + var obj = json.decode(jsonMessage); + var cmd = obj["cmd"].toString(); + if (cmd.contains("DANMU_MSG")) { + if (obj["info"] != null && obj["info"].length != 0) { + var message = obj["info"][1].toString(); + var color = asT(obj["info"][0][3]) ?? 0; + if (obj["info"][2] != null && obj["info"][2].length != 0) { + var extra = obj["info"][0][15]['extra']; + var user = obj["info"][0][15]['user']['base']; + Map extraMap = jsonDecode(extra); + final int userId = obj["info"][2][0]; + final LiveMessageModel liveMsg = LiveMessageModel( + type: LiveMessageType.chat, + userName: user['name'], + message: message, + color: color == 0 + ? LiveMessageColor.white + : LiveMessageColor.numberToColor(color), + face: user['face'], + uid: userId, + emots: extraMap['emots'], + ); + return liveMsg; + } + } + } else if (cmd == "SUPER_CHAT_MESSAGE") { + if (obj["data"] == null) { + return null; + } + final data = obj["data"]; + final userInfo = data["user_info"]; + final String backgroundBottomColor = + data["background_bottom_color"].toString(); + final String backgroundColor = data["background_color"].toString(); + final DateTime endTime = + DateTime.fromMillisecondsSinceEpoch(data["end_time"] * 1000); + final String face = "${userInfo["face"]}@200w.jpg"; + final String message = data["message"].toString(); + final String price = data["price"]; + final DateTime startTime = + DateTime.fromMillisecondsSinceEpoch(data["start_time"] * 1000); + final String userName = userInfo["uname"].toString(); + + final LiveMessageModel liveMsg = LiveMessageModel( + type: LiveMessageType.superChat, + userName: "SUPER_CHAT_MESSAGE", + message: "SUPER_CHAT_MESSAGE", + color: LiveMessageColor.white, + data: { + "backgroundBottomColor": backgroundBottomColor, + "backgroundColor": backgroundColor, + "endTime": endTime, + "face": face, + "message": message, + "price": price, + "startTime": startTime, + "userName": userName, + }, + ); + return liveMsg; + } else if (cmd == 'INTERACT_WORD') { + if (obj["data"] == null) { + return null; + } + final data = obj["data"]; + final String userName = data['uname']; + final int msgType = data['msg_type']; + final LiveMessageModel liveMsg = LiveMessageModel( + type: msgType == 1 ? LiveMessageType.join : LiveMessageType.follow, + userName: userName, + message: msgType == 1 ? '进入直播间' : '关注了主播', + color: LiveMessageColor.white, + ); + return liveMsg; + } + } catch (e) { + print(e); + } + return null; + } + + static T? asT(dynamic value) { + if (value is T) { + return value; + } + return null; + } + + static int readInt(List buffer, int start, int len) { + var data = _getByteData(buffer, start, len); + return _readIntFromByteData(data, len); + } + + static ByteData _getByteData(List buffer, int start, int len) { + var bytes = + Uint8List.fromList(buffer.getRange(start, start + len).toList()); + return ByteData.view(bytes.buffer); + } + + static int _readIntFromByteData(ByteData data, int len) { + switch (len) { + case 1: + return data.getUint8(0); + case 2: + return data.getInt16(0, Endian.big); + case 4: + return data.getInt32(0, Endian.big); + case 8: + return data.getInt64(0, Endian.big); + default: + throw ArgumentError('Invalid length: $len'); + } + } +} diff --git a/pubspec.lock b/pubspec.lock index a46127f9..2dfaa961 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -113,6 +113,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" + brotli: + dependency: "direct main" + description: + name: brotli + sha256: "7f891558ed779aab2bed874f0a36b8123f9ff3f19cf6efbee89e18ed294945ae" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.6.0" build: dependency: transitive description: @@ -1656,13 +1664,13 @@ packages: source: hosted version: "0.5.1" web_socket_channel: - dependency: transitive + dependency: "direct main" description: name: web_socket_channel - sha256: d88238e5eac9a42bb43ca4e721edba3c08c6354d4a53063afaa568516217621b + sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" url: "https://pub.flutter-io.cn" source: hosted - version: "2.4.0" + version: "2.4.5" webview_cookie_manager: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index b5d45714..23c1426c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -146,6 +146,8 @@ dependencies: lottie: ^3.1.2 # 二维码 qr_flutter: ^4.1.0 + web_socket_channel: ^2.4.5 + brotli: ^0.6.0 dev_dependencies: flutter_test: From 869e49bec2369e85a3ade02768888a7620b90924 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 21 Aug 2024 00:09:26 +0800 Subject: [PATCH 299/349] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=952?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/controller.dart | 3 ++- lib/pages/danmaku/view.dart | 32 +++++++++++++++++------------ lib/pages/live_room/controller.dart | 28 ++++++++++++++++++++++--- lib/pages/live_room/view.dart | 15 ++++++++++++-- 4 files changed, 59 insertions(+), 19 deletions(-) diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 52c423d7..ed259b96 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -2,8 +2,9 @@ import 'package:pilipala/http/danmaku.dart'; import 'package:pilipala/models/danmaku/dm.pb.dart'; class PlDanmakuController { - PlDanmakuController(this.cid); + PlDanmakuController(this.cid, this.type); final int cid; + final String type; Map> dmSegMap = {}; // 已请求的段落标记 List requestedSeg = []; diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 109f0206..e669b881 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -12,11 +12,15 @@ import 'package:pilipala/utils/storage.dart'; class PlDanmaku extends StatefulWidget { final int cid; final PlPlayerController playerController; + final String type; + final Function(DanmakuController)? createdController; const PlDanmaku({ super.key, required this.cid, required this.playerController, + this.type = 'video', + this.createdController, }); @override @@ -43,9 +47,9 @@ class _PlDanmakuState extends State { super.initState(); enableShowDanmaku = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); - _plDanmakuController = PlDanmakuController(widget.cid); - if (mounted) { - playerController = widget.playerController; + _plDanmakuController = PlDanmakuController(widget.cid, widget.type); + playerController = widget.playerController; + if (mounted && widget.type == 'video') { if (enableShowDanmaku || playerController.isOpenDanmu.value) { _plDanmakuController.initiate( playerController.duration.value.inMilliseconds, @@ -55,13 +59,15 @@ class _PlDanmakuState extends State { ..addStatusLister(playerListener) ..addPositionListener(videoPositionListen); } - playerController.isOpenDanmu.listen((p0) { - if (p0 && !_plDanmakuController.initiated) { - _plDanmakuController.initiate( - playerController.duration.value.inMilliseconds, - playerController.position.value.inMilliseconds); - } - }); + if (widget.type == 'video') { + playerController.isOpenDanmu.listen((p0) { + if (p0 && !_plDanmakuController.initiated) { + _plDanmakuController.initiate( + playerController.duration.value.inMilliseconds, + playerController.position.value.inMilliseconds); + } + }); + } blockTypes = playerController.blockTypes; showArea = playerController.showArea; opacityVal = playerController.opacityVal; @@ -123,11 +129,12 @@ class _PlDanmakuState extends State { // double initDuration = box.maxWidth / 12; return Obx( () => AnimatedOpacity( - opacity: playerController.isOpenDanmu.value ? 1 : 0, + opacity: playerController.isOpenDanmu.value ? 1 : 1, duration: const Duration(milliseconds: 100), child: DanmakuView( createdController: (DanmakuController e) async { playerController.danmakuController = _controller = e; + widget.createdController?.call(e); }, option: DanmakuOption( fontSize: 15 * fontSizeVal, @@ -136,8 +143,7 @@ class _PlDanmakuState extends State { hideTop: blockTypes.contains(5), hideScroll: blockTypes.contains(2), hideBottom: blockTypes.contains(4), - duration: - danmakuDurationVal / playerController.playbackSpeed, + duration: danmakuDurationVal / playerController.playbackSpeed, strokeWidth: strokeWidth, // initDuration / // (danmakuSpeedVal * widget.playerController.playbackSpeed), diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index fa95ce63..6a3525e3 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,7 +1,9 @@ import 'dart:convert'; +import 'dart:ui'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/live.dart'; @@ -37,6 +39,7 @@ class LiveRoomController extends GetxController { String token = ''; // 弹幕消息列表 RxList messageList = [].obs; + DanmakuController? danmakuController; @override void onInit() { @@ -172,9 +175,28 @@ class LiveRoomController extends GetxController { final List? liveMsg = LiveUtils.decodeMessage(message); if (liveMsg != null) { - messageList.addAll(liveMsg - .where((msg) => msg.type == LiveMessageType.chat) - .toList()); + // 过滤出聊天消息 + var chatMessages = + liveMsg.where((msg) => msg.type == LiveMessageType.chat).toList(); + + // 添加到 messageList + messageList.addAll(chatMessages); + + // 将 chatMessages 转换为 danmakuItems 列表 + List danmakuItems = chatMessages.map((e) { + return DanmakuItem( + e.message ?? '', + color: Color.fromARGB( + 255, + e.color.r, + e.color.g, + e.color.b, + ), + ); + }).toList(); + + // 添加到 danmakuController + danmakuController?.addItems(danmakuItems); } }, onErrorCb: (e) { diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index d9b316e9..d1fc6e07 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/live/message.dart'; +import 'package:pilipala/pages/danmaku/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'controller.dart'; @@ -22,7 +23,7 @@ class LiveRoomPage extends StatefulWidget { class _LiveRoomPageState extends State with TickerProviderStateMixin { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); - PlPlayerController? plPlayerController; + late PlPlayerController plPlayerController; late Future? _futureBuilder; late Future? _futureBuilderFuture; @@ -32,6 +33,7 @@ class _LiveRoomPageState extends State final ScrollController _scrollController = ScrollController(); late AnimationController fabAnimationCtr; bool _shouldAutoScroll = true; + final int roomId = int.parse(Get.parameters['roomid']!); @override void initState() { @@ -110,8 +112,9 @@ class _LiveRoomPageState extends State future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data['status']) { + plPlayerController = _liveRoomController.plPlayerController; return PLVideoPlayer( - controller: plPlayerController!, + controller: plPlayerController, bottomControl: BottomControl( controller: plPlayerController, liveRoomCtr: _liveRoomController, @@ -122,6 +125,14 @@ class _LiveRoomPageState extends State }); }, ), + danmuWidget: PlDanmaku( + cid: roomId, + playerController: plPlayerController, + type: 'live', + createdController: (e) { + _liveRoomController.danmakuController = e; + }, + ), ); } else { return const SizedBox(); From a074a360f2f1370a554d9248aa72909b841b268c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 21 Aug 2024 00:14:45 +0800 Subject: [PATCH 300/349] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=952?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/controller.dart | 3 ++- lib/pages/danmaku/view.dart | 30 +++++++++++++++++------------ lib/pages/live_room/controller.dart | 28 ++++++++++++++++++++++++--- lib/pages/live_room/view.dart | 15 +++++++++++++-- 4 files changed, 58 insertions(+), 18 deletions(-) diff --git a/lib/pages/danmaku/controller.dart b/lib/pages/danmaku/controller.dart index 52c423d7..ed259b96 100644 --- a/lib/pages/danmaku/controller.dart +++ b/lib/pages/danmaku/controller.dart @@ -2,8 +2,9 @@ import 'package:pilipala/http/danmaku.dart'; import 'package:pilipala/models/danmaku/dm.pb.dart'; class PlDanmakuController { - PlDanmakuController(this.cid); + PlDanmakuController(this.cid, this.type); final int cid; + final String type; Map> dmSegMap = {}; // 已请求的段落标记 List requestedSeg = []; diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index 109f0206..3cf1ed8a 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -12,11 +12,15 @@ import 'package:pilipala/utils/storage.dart'; class PlDanmaku extends StatefulWidget { final int cid; final PlPlayerController playerController; + final String type; + final Function(DanmakuController)? createdController; const PlDanmaku({ super.key, required this.cid, required this.playerController, + this.type = 'video', + this.createdController, }); @override @@ -43,9 +47,9 @@ class _PlDanmakuState extends State { super.initState(); enableShowDanmaku = setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); - _plDanmakuController = PlDanmakuController(widget.cid); - if (mounted) { - playerController = widget.playerController; + _plDanmakuController = PlDanmakuController(widget.cid, widget.type); + playerController = widget.playerController; + if (mounted && widget.type == 'video') { if (enableShowDanmaku || playerController.isOpenDanmu.value) { _plDanmakuController.initiate( playerController.duration.value.inMilliseconds, @@ -55,13 +59,15 @@ class _PlDanmakuState extends State { ..addStatusLister(playerListener) ..addPositionListener(videoPositionListen); } - playerController.isOpenDanmu.listen((p0) { - if (p0 && !_plDanmakuController.initiated) { - _plDanmakuController.initiate( - playerController.duration.value.inMilliseconds, - playerController.position.value.inMilliseconds); - } - }); + if (widget.type == 'video') { + playerController.isOpenDanmu.listen((p0) { + if (p0 && !_plDanmakuController.initiated) { + _plDanmakuController.initiate( + playerController.duration.value.inMilliseconds, + playerController.position.value.inMilliseconds); + } + }); + } blockTypes = playerController.blockTypes; showArea = playerController.showArea; opacityVal = playerController.opacityVal; @@ -128,6 +134,7 @@ class _PlDanmakuState extends State { child: DanmakuView( createdController: (DanmakuController e) async { playerController.danmakuController = _controller = e; + widget.createdController?.call(e); }, option: DanmakuOption( fontSize: 15 * fontSizeVal, @@ -136,8 +143,7 @@ class _PlDanmakuState extends State { hideTop: blockTypes.contains(5), hideScroll: blockTypes.contains(2), hideBottom: blockTypes.contains(4), - duration: - danmakuDurationVal / playerController.playbackSpeed, + duration: danmakuDurationVal / playerController.playbackSpeed, strokeWidth: strokeWidth, // initDuration / // (danmakuSpeedVal * widget.playerController.playbackSpeed), diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index fa95ce63..6a3525e3 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,7 +1,9 @@ import 'dart:convert'; +import 'dart:ui'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:ns_danmaku/ns_danmaku.dart'; import 'package:pilipala/http/constants.dart'; import 'package:pilipala/http/init.dart'; import 'package:pilipala/http/live.dart'; @@ -37,6 +39,7 @@ class LiveRoomController extends GetxController { String token = ''; // 弹幕消息列表 RxList messageList = [].obs; + DanmakuController? danmakuController; @override void onInit() { @@ -172,9 +175,28 @@ class LiveRoomController extends GetxController { final List? liveMsg = LiveUtils.decodeMessage(message); if (liveMsg != null) { - messageList.addAll(liveMsg - .where((msg) => msg.type == LiveMessageType.chat) - .toList()); + // 过滤出聊天消息 + var chatMessages = + liveMsg.where((msg) => msg.type == LiveMessageType.chat).toList(); + + // 添加到 messageList + messageList.addAll(chatMessages); + + // 将 chatMessages 转换为 danmakuItems 列表 + List danmakuItems = chatMessages.map((e) { + return DanmakuItem( + e.message ?? '', + color: Color.fromARGB( + 255, + e.color.r, + e.color.g, + e.color.b, + ), + ); + }).toList(); + + // 添加到 danmakuController + danmakuController?.addItems(danmakuItems); } }, onErrorCb: (e) { diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index d9b316e9..d1fc6e07 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -7,6 +7,7 @@ import 'package:flutter/rendering.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; import 'package:pilipala/models/live/message.dart'; +import 'package:pilipala/pages/danmaku/index.dart'; import 'package:pilipala/plugin/pl_player/index.dart'; import 'controller.dart'; @@ -22,7 +23,7 @@ class LiveRoomPage extends StatefulWidget { class _LiveRoomPageState extends State with TickerProviderStateMixin { final LiveRoomController _liveRoomController = Get.put(LiveRoomController()); - PlPlayerController? plPlayerController; + late PlPlayerController plPlayerController; late Future? _futureBuilder; late Future? _futureBuilderFuture; @@ -32,6 +33,7 @@ class _LiveRoomPageState extends State final ScrollController _scrollController = ScrollController(); late AnimationController fabAnimationCtr; bool _shouldAutoScroll = true; + final int roomId = int.parse(Get.parameters['roomid']!); @override void initState() { @@ -110,8 +112,9 @@ class _LiveRoomPageState extends State future: _futureBuilderFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { if (snapshot.hasData && snapshot.data['status']) { + plPlayerController = _liveRoomController.plPlayerController; return PLVideoPlayer( - controller: plPlayerController!, + controller: plPlayerController, bottomControl: BottomControl( controller: plPlayerController, liveRoomCtr: _liveRoomController, @@ -122,6 +125,14 @@ class _LiveRoomPageState extends State }); }, ), + danmuWidget: PlDanmaku( + cid: roomId, + playerController: plPlayerController, + type: 'live', + createdController: (e) { + _liveRoomController.danmakuController = e; + }, + ), ); } else { return const SizedBox(); From 44910b35d97fd29c7ad30fe60f497a50872f662c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 22 Aug 2024 20:24:21 +0800 Subject: [PATCH 301/349] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/live.dart | 33 ++++++ lib/pages/live_room/controller.dart | 21 +++- lib/pages/live_room/view.dart | 163 +++++++++++++++------------- 4 files changed, 145 insertions(+), 75 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 08a20382..65b74d2d 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -558,4 +558,7 @@ class Api { /// 直播间弹幕信息 static const String getDanmuInfo = '${HttpString.liveBaseUrl}/xlive/web-room/v1/index/getDanmuInfo'; + + /// 直播间发送弹幕 + static const String sendLiveMsg = '${HttpString.liveBaseUrl}/msg/send'; } diff --git a/lib/http/live.dart b/lib/http/live.dart index a405fd58..f6fc4ea4 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -84,4 +84,37 @@ class LiveHttp { }; } } + + // 发送弹幕 + static Future sendDanmaku({roomId, msg}) async { + var res = await Request().post(Api.sendLiveMsg, queryParameters: { + 'bubble': 0, + 'msg': msg, + 'color': 16777215, // 颜色 + 'mode': 1, // 模式 + 'room_type': 0, + 'jumpfrom': 71001, // 直播间来源 + 'reply_mid': 0, + 'reply_attr': 0, + 'replay_dmid': '', + 'statistics': {"appId": 100, "platform": 5}, + 'fontsize': 25, // 字体大小 + 'rnd': DateTime.now().millisecondsSinceEpoch ~/ 1000, // 时间戳 + 'roomid': roomId, + 'csrf': await Request.getCsrf(), + 'csrf_token': await Request.getCsrf(), + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 6a3525e3..e8db54fb 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,5 +1,5 @@ import 'dart:convert'; -import 'dart:ui'; +import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; @@ -40,6 +40,8 @@ class LiveRoomController extends GetxController { // 弹幕消息列表 RxList messageList = [].obs; DanmakuController? danmakuController; + // 输入控制器 + TextEditingController inputController = TextEditingController(); @override void onInit() { @@ -222,6 +224,23 @@ class LiveRoomController extends GetxController { plSocket?.sendMessage(joinData); } + // 发送弹幕 + void sendMsg() async { + final msg = inputController.text; + if (msg.isEmpty) { + return; + } + final res = await LiveHttp.sendDanmaku( + roomId: roomId, + msg: msg, + ); + if (res['status']) { + inputController.clear(); + } else { + SmartDialog.showToast(res['msg']); + } + } + @override void onClose() { plSocket?.onClose(); diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index d1fc6e07..92ea8fd8 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -97,7 +97,7 @@ class _LiveRoomPageState extends State @override void dispose() { - plPlayerController!.dispose(); + plPlayerController.dispose(); if (floating != null) { floating!.dispose(); } @@ -238,10 +238,10 @@ class _LiveRoomPageState extends State ), ), PopScope( - canPop: plPlayerController?.isFullScreen.value != true, + canPop: plPlayerController.isFullScreen.value != true, onPopInvoked: (bool didPop) { - if (plPlayerController?.isFullScreen.value == true) { - plPlayerController!.triggerFullScreen(status: false); + if (plPlayerController.isFullScreen.value == true) { + plPlayerController.triggerFullScreen(status: false); } if (MediaQuery.of(context).orientation == Orientation.landscape) { @@ -257,17 +257,53 @@ class _LiveRoomPageState extends State child: videoPlayerPanel, ), ), - const SizedBox(height: 20), // 显示消息的列表 buildMessageListUI( context, _liveRoomController, _scrollController, ), - // 底部安全距离 - SizedBox( - height: MediaQuery.of(context).padding.bottom + 20, - ) + // 弹幕输入框 + Container( + padding: EdgeInsets.only( + left: 14, + right: 14, + top: 4, + bottom: MediaQuery.of(context).padding.bottom + 20), + decoration: BoxDecoration( + color: Colors.black.withOpacity(0.5), + border: Border( + top: BorderSide( + color: Colors.white.withOpacity(0.1), + ), + ), + ), + child: Row( + children: [ + Expanded( + child: TextField( + controller: _liveRoomController.inputController, + style: + const TextStyle(color: Colors.white, fontSize: 13), + decoration: InputDecoration( + hintText: '发送弹幕', + hintStyle: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + border: InputBorder.none, + ), + ), + ), + IconButton( + onPressed: () => _liveRoomController.sendMsg(), + icon: const Icon( + Icons.send, + color: Colors.white, + ), + ), + ], + ), + ), ], ), // 定位 快速滑动到底部 @@ -324,15 +360,15 @@ Widget buildMessageListUI( removeBottom: true, child: ShaderMask( shaderCallback: (Rect bounds) { - return const LinearGradient( + return LinearGradient( begin: Alignment.topCenter, end: Alignment.bottomCenter, colors: [ Colors.transparent, - Colors.black, + Colors.black.withOpacity(0.5), Colors.black, ], - stops: [0.0, 0.1, 1.0], + stops: const [0.01, 0.05, 0.2], ).createShader(bounds); }, blendMode: BlendMode.dstIn, @@ -342,35 +378,46 @@ Widget buildMessageListUI( itemBuilder: (context, index) { final LiveMessageModel liveMsgItem = liveRoomController.messageList[index]; - return Padding( - padding: EdgeInsets.only( - top: index == 0 ? 40.0 : 4.0, - bottom: 4.0, - left: 20.0, - right: 20.0, - ), - child: Text.rich( - TextSpan( - style: const TextStyle(color: Colors.white), - children: [ - TextSpan( - text: '${liveMsgItem.userName}: ', - style: TextStyle( - color: Colors.white.withOpacity(0.6), + return Align( + alignment: Alignment.centerLeft, + child: Container( + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.1), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + margin: EdgeInsets.only( + top: index == 0 ? 20.0 : 0.0, + bottom: 6.0, + left: 14.0, + right: 14.0, + ), + padding: const EdgeInsets.symmetric( + vertical: 3.0, + horizontal: 10.0, + ), + child: Text.rich( + TextSpan( + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '${liveMsgItem.userName}: ', + style: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + print('Text clicked'); + }, ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 处理点击事件 - print('Text clicked'); - }, - ), - TextSpan( - children: [ - ...buildMessageTextSpan(context, liveMsgItem) - ], - // text: liveMsgItem.message, - ), - ], + TextSpan( + children: [ + ...buildMessageTextSpan(context, liveMsgItem) + ], + // text: liveMsgItem.message, + ), + ], + ), ), ), ); @@ -392,23 +439,7 @@ List buildMessageTextSpan( if (liveMsgItem.emots == null) { // 没有表情包的消息 inlineSpanList.add( - TextSpan( - text: liveMsgItem.message ?? '', - style: const TextStyle( - shadows: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 3.0, - color: Colors.black, - ), - Shadow( - offset: Offset(-1.0, -1.0), - blurRadius: 3.0, - color: Colors.black, - ), - ], - ), - ), + TextSpan(text: liveMsgItem.message ?? ''), ); } else { // 有表情包的消息 使用正则匹配 表情包用图片渲染 @@ -435,23 +466,7 @@ List buildMessageTextSpan( }, onNonMatch: (String nonMatch) { inlineSpanList.add( - TextSpan( - text: nonMatch, - style: const TextStyle( - shadows: [ - Shadow( - offset: Offset(2.0, 2.0), - blurRadius: 3.0, - color: Colors.black, - ), - Shadow( - offset: Offset(-1.0, -1.0), - blurRadius: 3.0, - color: Colors.black, - ), - ], - ), - ), + TextSpan(text: nonMatch), ); return nonMatch; }, From 844db213a56d480934aecf8b786ab5028d8e2cbc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 22 Aug 2024 23:57:11 +0800 Subject: [PATCH 302/349] =?UTF-8?q?mod:=20=E8=BE=93=E5=85=A5=E6=A1=86?= =?UTF-8?q?=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/live_room/controller.dart | 27 ++++++++++++++- lib/pages/live_room/view.dart | 52 +++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index e8db54fb..a56a5459 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; @@ -42,6 +43,8 @@ class LiveRoomController extends GetxController { DanmakuController? danmakuController; // 输入控制器 TextEditingController inputController = TextEditingController(); + // 加入直播间提示 + RxMap joinRoomTip = {'userName': '', 'message': ''}.obs; @override void onInit() { @@ -176,7 +179,29 @@ class LiveRoomController extends GetxController { onMessageCb: (message) { final List? liveMsg = LiveUtils.decodeMessage(message); - if (liveMsg != null) { + if (liveMsg != null && liveMsg.isNotEmpty) { + if (liveMsg.first.type == LiveMessageType.online) { + print('当前直播间人气:${liveMsg.first.data}'); + } else if (liveMsg.first.type == LiveMessageType.join || + liveMsg.first.type == LiveMessageType.follow) { + // 每隔一秒依次liveMsg中的每一项赋给activeUserName + int index = 0; + Timer.periodic(const Duration(seconds: 2), (timer) { + if (index < liveMsg.length) { + if (liveMsg[index].type == LiveMessageType.join || + liveMsg[index].type == LiveMessageType.follow) { + joinRoomTip.value = { + 'userName': liveMsg[index].userName, + 'message': liveMsg[index].message!, + }; + } + index++; + } else { + timer.cancel(); + } + }); + return; + } // 过滤出聊天消息 var chatMessages = liveMsg.where((msg) => msg.type == LiveMessageType.chat).toList(); diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 92ea8fd8..3f563ad6 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -179,6 +179,7 @@ class _LiveRoomPageState extends State ), ), Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ AppBar( centerTitle: false, @@ -263,6 +264,49 @@ class _LiveRoomPageState extends State _liveRoomController, _scrollController, ), + // Container( + // padding: const EdgeInsets.only( + // left: 14, right: 14, top: 4, bottom: 4), + // margin: const EdgeInsets.only( + // bottom: 6, + // left: 14, + // ), + // decoration: BoxDecoration( + // color: Colors.grey.withOpacity(0.1), + // borderRadius: const BorderRadius.all(Radius.circular(20)), + // ), + // child: Obx( + // () => AnimatedSwitcher( + // duration: const Duration(milliseconds: 300), + // transitionBuilder: + // (Widget child, Animation animation) { + // return FadeTransition(opacity: animation, child: child); + // }, + // child: Text.rich( + // key: + // ValueKey(_liveRoomController.joinRoomTip['userName']), + // TextSpan( + // style: const TextStyle(color: Colors.white), + // children: [ + // TextSpan( + // text: + // '${_liveRoomController.joinRoomTip['userName']} ', + // style: TextStyle( + // color: Colors.white.withOpacity(0.6), + // ), + // ), + // TextSpan( + // text: + // '${_liveRoomController.joinRoomTip['message']}', + // style: const TextStyle(color: Colors.white), + // ), + // ], + // ), + // ), + // ), + // ), + // ), + const SizedBox(height: 10), // 弹幕输入框 Container( padding: EdgeInsets.only( @@ -271,7 +315,8 @@ class _LiveRoomPageState extends State top: 4, bottom: MediaQuery.of(context).padding.bottom + 20), decoration: BoxDecoration( - color: Colors.black.withOpacity(0.5), + color: Colors.grey.withOpacity(0.1), + borderRadius: const BorderRadius.all(Radius.circular(20)), border: Border( top: BorderSide( color: Colors.white.withOpacity(0.1), @@ -280,6 +325,7 @@ class _LiveRoomPageState extends State ), child: Row( children: [ + const SizedBox(width: 4), Expanded( child: TextField( controller: _liveRoomController.inputController, @@ -309,10 +355,10 @@ class _LiveRoomPageState extends State // 定位 快速滑动到底部 Positioned( right: 20, - bottom: MediaQuery.of(context).padding.bottom + 20, + bottom: MediaQuery.of(context).padding.bottom + 80, child: SlideTransition( position: Tween( - begin: const Offset(0, 2), + begin: const Offset(0, 4), end: const Offset(0, 0), ).animate(CurvedAnimation( parent: fabAnimationCtr, From 61e019f458542e8f25a14d8153044df9e14f361f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 23 Aug 2024 00:20:20 +0800 Subject: [PATCH 303/349] =?UTF-8?q?fix:=20=E5=90=88=E9=9B=86=E7=B1=BB?= =?UTF-8?q?=E5=9E=8B=E8=A1=A5=E5=85=85=EF=BC=88=E7=9B=B4=E6=92=AD=E5=9B=9E?= =?UTF-8?q?=E6=94=BE=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/widgets/seasons.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pages/member/widgets/seasons.dart b/lib/pages/member/widgets/seasons.dart index 1749ff45..615fc44c 100644 --- a/lib/pages/member/widgets/seasons.dart +++ b/lib/pages/member/widgets/seasons.dart @@ -35,7 +35,8 @@ class MemberSeasonsPanel extends StatelessWidget { 'seasonName': item.meta!.name!, }; } - if (category == 1) { + // 2为直播回放 + if (category == 1 || category == 2) { parameters = { 'category': '1', 'mid': item.meta!.mid.toString(), From fc22834e4a66e4d3fd803c3ec0455f13203bfcde Mon Sep 17 00:00:00 2001 From: guozhigq Date: Fri, 23 Aug 2024 23:47:15 +0800 Subject: [PATCH 304/349] =?UTF-8?q?mod:=20=E7=9B=B4=E6=92=AD=E5=BC=B9?= =?UTF-8?q?=E5=B9=95=E5=BC=80=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/danmaku/view.dart | 2 +- lib/pages/live_room/controller.dart | 12 ++- lib/pages/live_room/view.dart | 146 ++++++++++++++++++---------- 3 files changed, 105 insertions(+), 55 deletions(-) diff --git a/lib/pages/danmaku/view.dart b/lib/pages/danmaku/view.dart index e669b881..3cf1ed8a 100644 --- a/lib/pages/danmaku/view.dart +++ b/lib/pages/danmaku/view.dart @@ -129,7 +129,7 @@ class _PlDanmakuState extends State { // double initDuration = box.maxWidth / 12; return Obx( () => AnimatedOpacity( - opacity: playerController.isOpenDanmu.value ? 1 : 1, + opacity: playerController.isOpenDanmu.value ? 1 : 0, duration: const Duration(milliseconds: 100), child: DanmakuView( createdController: (DanmakuController e) async { diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index a56a5459..99025dce 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -45,6 +45,8 @@ class LiveRoomController extends GetxController { TextEditingController inputController = TextEditingController(); // 加入直播间提示 RxMap joinRoomTip = {'userName': '', 'message': ''}.obs; + // 直播间弹幕开关 默认打开 + RxBool danmakuSwitch = true.obs; @override void onInit() { @@ -69,6 +71,9 @@ class LiveRoomController extends GetxController { userId = userInfo.mid; } liveDanmakuInfo().then((value) => initSocket()); + danmakuSwitch.listen((p0) { + plPlayerController.isOpenDanmu.value = p0; + }); } playerInit(source) async { @@ -87,6 +92,7 @@ class LiveRoomController extends GetxController { enableHA: true, autoplay: true, ); + plPlayerController.isOpenDanmu.value = danmakuSwitch.value; } Future queryLiveInfo() async { @@ -185,6 +191,7 @@ class LiveRoomController extends GetxController { } else if (liveMsg.first.type == LiveMessageType.join || liveMsg.first.type == LiveMessageType.follow) { // 每隔一秒依次liveMsg中的每一项赋给activeUserName + int index = 0; Timer.periodic(const Duration(seconds: 2), (timer) { if (index < liveMsg.length) { @@ -200,6 +207,7 @@ class LiveRoomController extends GetxController { timer.cancel(); } }); + return; } // 过滤出聊天消息 @@ -223,7 +231,9 @@ class LiveRoomController extends GetxController { }).toList(); // 添加到 danmakuController - danmakuController?.addItems(danmakuItems); + if (danmakuSwitch.value) { + danmakuController?.addItems(danmakuItems); + } } }, onErrorCb: (e) { diff --git a/lib/pages/live_room/view.dart b/lib/pages/live_room/view.dart index 3f563ad6..abbcce13 100644 --- a/lib/pages/live_room/view.dart +++ b/lib/pages/live_room/view.dart @@ -325,7 +325,33 @@ class _LiveRoomPageState extends State ), child: Row( children: [ - const SizedBox(width: 4), + SizedBox( + width: 34, + height: 34, + child: Obx( + () => IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + backgroundColor: MaterialStateProperty.resolveWith( + (Set states) { + return Colors.grey.withOpacity(0.1); + }), + ), + onPressed: () { + _liveRoomController.danmakuSwitch.value = + !_liveRoomController.danmakuSwitch.value; + }, + icon: Icon( + _liveRoomController.danmakuSwitch.value + ? Icons.subtitles_outlined + : Icons.subtitles_off_outlined, + size: 19, + color: Colors.white, + ), + ), + ), + ), + const SizedBox(width: 8), Expanded( child: TextField( controller: _liveRoomController.inputController, @@ -340,11 +366,19 @@ class _LiveRoomPageState extends State ), ), ), - IconButton( - onPressed: () => _liveRoomController.sendMsg(), - icon: const Icon( - Icons.send, - color: Colors.white, + SizedBox( + width: 34, + height: 34, + child: IconButton( + style: ButtonStyle( + padding: MaterialStateProperty.all(EdgeInsets.zero), + ), + onPressed: () => _liveRoomController.sendMsg(), + icon: const Icon( + Icons.send, + color: Colors.white, + size: 20, + ), ), ), ], @@ -418,56 +452,62 @@ Widget buildMessageListUI( ).createShader(bounds); }, blendMode: BlendMode.dstIn, - child: ListView.builder( - controller: scrollController, - itemCount: liveRoomController.messageList.length, - itemBuilder: (context, index) { - final LiveMessageModel liveMsgItem = - liveRoomController.messageList[index]; - return Align( - alignment: Alignment.centerLeft, - child: Container( - decoration: BoxDecoration( - color: Colors.grey.withOpacity(0.1), - borderRadius: const BorderRadius.all(Radius.circular(20)), - ), - margin: EdgeInsets.only( - top: index == 0 ? 20.0 : 0.0, - bottom: 6.0, - left: 14.0, - right: 14.0, - ), - padding: const EdgeInsets.symmetric( - vertical: 3.0, - horizontal: 10.0, - ), - child: Text.rich( - TextSpan( - style: const TextStyle(color: Colors.white), - children: [ - TextSpan( - text: '${liveMsgItem.userName}: ', - style: TextStyle( - color: Colors.white.withOpacity(0.6), + child: GestureDetector( + onTap: () { + // 键盘失去焦点 + FocusScope.of(context).requestFocus(FocusNode()); + }, + child: ListView.builder( + controller: scrollController, + itemCount: liveRoomController.messageList.length, + itemBuilder: (context, index) { + final LiveMessageModel liveMsgItem = + liveRoomController.messageList[index]; + return Align( + alignment: Alignment.centerLeft, + child: Container( + decoration: BoxDecoration( + color: Colors.grey.withOpacity(0.1), + borderRadius: const BorderRadius.all(Radius.circular(20)), + ), + margin: EdgeInsets.only( + top: index == 0 ? 20.0 : 0.0, + bottom: 6.0, + left: 14.0, + right: 14.0, + ), + padding: const EdgeInsets.symmetric( + vertical: 3.0, + horizontal: 10.0, + ), + child: Text.rich( + TextSpan( + style: const TextStyle(color: Colors.white), + children: [ + TextSpan( + text: '${liveMsgItem.userName}: ', + style: TextStyle( + color: Colors.white.withOpacity(0.6), + ), + recognizer: TapGestureRecognizer() + ..onTap = () { + // 处理点击事件 + print('Text clicked'); + }, ), - recognizer: TapGestureRecognizer() - ..onTap = () { - // 处理点击事件 - print('Text clicked'); - }, - ), - TextSpan( - children: [ - ...buildMessageTextSpan(context, liveMsgItem) - ], - // text: liveMsgItem.message, - ), - ], + TextSpan( + children: [ + ...buildMessageTextSpan(context, liveMsgItem) + ], + // text: liveMsgItem.message, + ), + ], + ), ), ), - ), - ); - }, + ); + }, + ), ), ), ), From 15e1cb5d4763a3b63b4c44e861b1dd79df7e6c6e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Aug 2024 00:07:08 +0800 Subject: [PATCH 305/349] =?UTF-8?q?feat:=20=E7=A7=81=E4=BF=A1=E5=8F=AF?= =?UTF-8?q?=E9=80=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../whisper_detail/widget/chat_item.dart | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 77e38073..94347aff 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -84,7 +84,7 @@ class ChatItem extends StatelessWidget { emojiMap[e['text']] = e['url']; } text.splitMapJoin( - RegExp(r"\[.+?\]"), + RegExp(r"\[[^\[\]]+\]"), onMatch: (Match match) { final String emojiKey = match[0]!; if (emojiMap.containsKey(emojiKey)) { @@ -95,6 +95,17 @@ class ChatItem extends StatelessWidget { src: emojiMap[emojiKey]!, ), )); + } else { + children.add( + TextSpan( + text: emojiKey, + style: TextStyle( + color: textColor(context), + letterSpacing: 0.6, + height: 1.5, + ), + ), + ); } return ''; }, @@ -109,13 +120,13 @@ class ChatItem extends StatelessWidget { return ''; }, ); - return RichText( - text: TextSpan( + return SelectableText.rich( + TextSpan( children: children, ), ); } else { - return Text( + return SelectableText( text, style: TextStyle( letterSpacing: 0.6, @@ -133,7 +144,7 @@ class ChatItem extends StatelessWidget { case MsgType.pic_card: return SystemNotice2(item: item); case MsgType.notify_text: - return Text( + return SelectableText( jsonDecode(content['content']) .map((m) => m['text'] as String) .join("\n"), @@ -530,7 +541,7 @@ class SystemNotice extends StatelessWidget { Divider( color: Theme.of(context).colorScheme.primary.withOpacity(0.05), ), - Text( + SelectableText( content['text'], ) ], From b96f35bae2f2f3e1458a0fdb66e1976fddc02f91 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Aug 2024 00:28:07 +0800 Subject: [PATCH 306/349] =?UTF-8?q?mod:=20=E7=A7=BB=E9=99=A4up=E4=B8=BB?= =?UTF-8?q?=E9=A1=B5=E5=88=86=E5=89=B2=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/view.dart | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index c721d638..7d2e4bed 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -168,7 +168,6 @@ class _MemberPageState extends State const Icon(Icons.arrow_forward_outlined, size: 19), ), ), - const Divider(height: 1, thickness: 0.1), /// 视频 Obx(() => ListTile( @@ -178,7 +177,6 @@ class _MemberPageState extends State trailing: const Icon(Icons.arrow_forward_outlined, size: 19), )), - const Divider(height: 1, thickness: 0.1), /// 他的收藏夹 Obx(() => ListTile( @@ -188,13 +186,11 @@ class _MemberPageState extends State trailing: const Icon(Icons.arrow_forward_outlined, size: 19), )), - const Divider(height: 1, thickness: 0.1), /// 专栏 Obx(() => ListTile( title: Text( '${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'))), - const Divider(height: 1, thickness: 0.1), /// 合集 Obx(() => ListTile( From 8810a74ebfcfa4cc9e52226089f0e6c79a70a399 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 24 Aug 2024 23:33:19 +0800 Subject: [PATCH 307/349] =?UTF-8?q?fix:=20=E6=9C=AA=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E6=97=B6=E8=BF=94=E5=9B=9E=E5=BD=93=E5=89=8D=E9=A1=B5=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/video/detail/view.dart | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/pages/video/detail/view.dart b/lib/pages/video/detail/view.dart index 0388e962..91c5ae00 100644 --- a/lib/pages/video/detail/view.dart +++ b/lib/pages/video/detail/view.dart @@ -63,7 +63,7 @@ class _VideoDetailPageState extends State late bool autoPlayEnable; late bool autoPiP; late Floating floating; - bool isShowing = true; + RxBool isShowing = true.obs; // 生命周期监听 late final AppLifecycleListener _lifecycleListener; late double statusHeight; @@ -183,6 +183,7 @@ class _VideoDetailPageState extends State plPlayerController!.addStatusLister(playerListener); vdCtr.autoPlay.value = true; vdCtr.isShowCover.value = false; + isShowing.value = true; autoEnterPip(status: PlayerStatus.playing); } @@ -258,7 +259,7 @@ class _VideoDetailPageState extends State plPlayerController!.pause(); vdCtr.clearSubtitleContent(); } - setState(() => isShowing = false); + isShowing.value = false; super.didPushNext(); } @@ -272,10 +273,8 @@ class _VideoDetailPageState extends State if (plPlayerController != null && plPlayerController!.videoPlayerController != null) { - setState(() { - vdCtr.setSubtitleContent(); - isShowing = true; - }); + vdCtr.setSubtitleContent(); + isShowing.value = true; } vdCtr.isFirstTime = false; final bool autoplay = autoPlayEnable; @@ -652,7 +651,11 @@ class _VideoDetailPageState extends State tag: heroTag, child: Stack( children: [ - if (isShowing) buildVideoPlayerPanel(), + Obx( + () => isShowing.value + ? buildVideoPlayerPanel() + : const SizedBox(), + ), /// 关闭自动播放时 手动播放 Obx( From 8fd3bfae5f9a88a8ce72670b2499f035e51b4f0e Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 25 Aug 2024 22:07:42 +0800 Subject: [PATCH 308/349] =?UTF-8?q?fix:=20=E7=B3=BB=E7=BB=9F=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E6=A0=87=E8=AE=B0=E5=B7=B2=E8=AF=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 4 ++++ lib/http/msg.dart | 20 +++++++++++++++++++- lib/pages/message/system/controller.dart | 14 +++++++++++++- 3 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 31e5a38b..d9286e47 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -554,4 +554,8 @@ class Api { /// 系统通知 static const String messageSystemAPi = '${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify'; + + /// 系统通知标记已读 + static const String systemMarkRead = + '${HttpString.messageBaseUrl}/x/sys-msg/update_cursor'; } diff --git a/lib/http/msg.dart b/lib/http/msg.dart index 86789fd1..2de9cd49 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -298,7 +298,6 @@ class MsgHttp { }); if (res.data['code'] == 0) { try { - print(res.data['data']['system_notify_list']); return { 'status': true, 'data': res.data['data']['system_notify_list'] @@ -312,4 +311,23 @@ class MsgHttp { return {'status': false, 'date': [], 'msg': res.data['message']}; } } + + // 系统消息标记已读 + static Future systemMarkRead(int cursor) async { + String csrf = await Request.getCsrf(); + var res = await Request().get(Api.systemMarkRead, data: { + 'csrf': csrf, + 'cursor': cursor, + }); + if (res.data['code'] == 0) { + return { + 'status': true, + }; + } else { + return { + 'status': false, + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/message/system/controller.dart b/lib/pages/message/system/controller.dart index bf31f6bc..f63a659a 100644 --- a/lib/pages/message/system/controller.dart +++ b/lib/pages/message/system/controller.dart @@ -8,8 +8,20 @@ class MessageSystemController extends GetxController { Future queryMessageSystem({String type = 'init'}) async { var res = await MsgHttp.messageSystem(); if (res['status']) { - systemItems.addAll(res['data']); + if (type == 'init') { + systemItems.value = res['data']; + } else { + systemItems.addAll(res['data']); + } + if (systemItems.isNotEmpty) { + systemMarkRead(systemItems.first.cursor!); + } } return res; } + + // 标记已读 + void systemMarkRead(int cursor) async { + await MsgHttp.systemMarkRead(cursor); + } } From d85e9446e3d634ad5c1cb3eecec45301a10a73e8 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 26 Aug 2024 00:12:35 +0800 Subject: [PATCH 309/349] opt: fav follow bottomSheet --- lib/pages/follow/widgets/follow_item.dart | 19 +++++-- .../video/detail/introduction/controller.dart | 19 +++++-- lib/pages/video/detail/introduction/view.dart | 49 ++++++++++--------- .../introduction/widgets/fav_panel.dart | 43 +++++++++------- .../introduction/widgets/group_panel.dart | 16 ++++-- pubspec.lock | 16 ++++++ pubspec.yaml | 1 + 7 files changed, 111 insertions(+), 52 deletions(-) diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart index d21a89bc..3d393277 100644 --- a/lib/pages/follow/widgets/follow_item.dart +++ b/lib/pages/follow/widgets/follow_item.dart @@ -1,3 +1,4 @@ +import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:pilipala/common/widgets/network_img_layer.dart'; @@ -47,9 +48,21 @@ class FollowItem extends StatelessWidget { height: 34, child: TextButton( onPressed: () async { - await Get.bottomSheet( - GroupPanel(mid: item.mid!), - isScrollControlled: true, + await showFlexibleBottomSheet( + bottomSheetColor: Colors.transparent, + minHeight: 1, + initHeight: 1, + maxHeight: 1, + context: Get.context!, + builder: (BuildContext context, + ScrollController scrollController, double offset) { + return GroupPanel( + mid: item.mid!, + scrollController: scrollController, + ); + }, + anchors: [1], + isSafeArea: true, ); }, style: TextButton.styleFrom( diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index 50aac4cd..d564aba4 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; @@ -523,9 +524,21 @@ class VideoIntroController extends GetxController { // 设置关注分组 void setFollowGroup() { - Get.bottomSheet( - GroupPanel(mid: videoDetail.value.owner!.mid!), - isScrollControlled: true, + showFlexibleBottomSheet( + bottomSheetColor: Colors.transparent, + minHeight: 0.6, + initHeight: 0.6, + maxHeight: 1, + context: Get.context!, + builder: (BuildContext context, ScrollController scrollController, + double offset) { + return GroupPanel( + mid: videoDetail.value.owner!.mid!, + scrollController: scrollController, + ); + }, + anchors: [0.6, 1], + isSafeArea: true, ); } diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 760976ae..a89b4d28 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -1,3 +1,6 @@ +import 'dart:ffi'; + +import 'package:bottom_sheet/bottom_sheet.dart'; import 'package:easy_debounce/easy_throttle.dart'; import 'package:expandable/expandable.dart'; import 'package:flutter/services.dart'; @@ -215,37 +218,35 @@ class _VideoInfoState extends State with TickerProviderStateMixin { if (!videoIntroController.hasFav.value) { videoIntroController.actionFavVideo(type: 'default'); } else { - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - builder: (BuildContext context) { - return FavPanel(ctr: videoIntroController); - }, - ); + _showFavPanel(); } } else { - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - builder: (BuildContext context) { - return FavPanel(ctr: videoIntroController); - }, - ); + _showFavPanel(); } } else if (type != 'longPress') { - showModalBottomSheet( - context: context, - useRootNavigator: true, - isScrollControlled: true, - builder: (BuildContext context) { - return FavPanel(ctr: videoIntroController); - }, - ); + _showFavPanel(); } } + void _showFavPanel() { + showFlexibleBottomSheet( + bottomSheetColor: Colors.transparent, + minHeight: 0.6, + initHeight: 0.6, + maxHeight: 1, + context: context, + builder: (BuildContext context, ScrollController scrollController, + double offset) { + return FavPanel( + ctr: videoIntroController, + scrollController: scrollController, + ); + }, + anchors: [0.6, 1], + isSafeArea: true, + ); + } + // 视频介绍 showIntroDetail() { feedBack(); diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index 5ef78967..a4e98df7 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -6,8 +6,9 @@ import 'package:pilipala/utils/feed_back.dart'; import 'package:pilipala/utils/storage.dart'; class FavPanel extends StatefulWidget { - const FavPanel({super.key, this.ctr}); + const FavPanel({super.key, this.ctr, this.scrollController}); final dynamic ctr; + final ScrollController? scrollController; @override State createState() => _FavPanelState(); @@ -15,31 +16,39 @@ class FavPanel extends StatefulWidget { class _FavPanelState extends State { final Box localCache = GStrorage.localCache; - late double sheetHeight; late Future _futureBuilderFuture; @override void initState() { super.initState(); - sheetHeight = localCache.get('sheetHeight'); _futureBuilderFuture = widget.ctr!.queryVideoInFolder(); } @override Widget build(BuildContext context) { return Container( - height: sheetHeight, - color: Theme.of(context).colorScheme.surface, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), child: Column( children: [ AppBar( centerTitle: false, elevation: 0, - leading: IconButton( - onPressed: () => Get.back(), - icon: const Icon(Icons.close_outlined)), - title: - Text('添加到收藏夹', style: Theme.of(context).textTheme.titleMedium), + automaticallyImplyLeading: false, + leadingWidth: 0, + title: Text( + '选择收藏夹', + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ), ), Expanded( child: Material( @@ -51,22 +60,22 @@ class _FavPanelState extends State { if (data['status']) { return Obx( () => ListView.builder( + controller: widget.scrollController, itemCount: widget.ctr!.favFolderData.value.list!.length, itemBuilder: (context, index) { + final item = + widget.ctr!.favFolderData.value.list![index]; return ListTile( - onTap: () => widget.ctr!.onChoose( - widget.ctr!.favFolderData.value.list![index] - .favState != - 1, - index), + onTap: () => widget.ctr! + .onChoose(item.favState != 1, index), dense: true, leading: const Icon(Icons.folder_outlined), minLeadingWidth: 0, title: Text(widget.ctr!.favFolderData.value .list![index].title!), subtitle: Text( - '${widget.ctr!.favFolderData.value.list![index].mediaCount}个内容', + '${item.mediaCount}个内容 ', ), trailing: Transform.scale( scale: 0.9, @@ -132,7 +141,7 @@ class _FavPanelState extends State { backgroundColor: Theme.of(context).colorScheme.primary, // 设置按钮背景色 ), - child: const Text('完成'), + child: const Text('确认选择'), ), ], ), diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/pages/video/detail/introduction/widgets/group_panel.dart index dcdaf9c5..4bb0980c 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/group_panel.dart @@ -10,7 +10,8 @@ import 'package:pilipala/utils/storage.dart'; class GroupPanel extends StatefulWidget { final int? mid; - const GroupPanel({super.key, this.mid}); + final ScrollController scrollController; + const GroupPanel({super.key, this.mid, required this.scrollController}); @override State createState() => _GroupPanelState(); @@ -18,7 +19,6 @@ class GroupPanel extends StatefulWidget { class _GroupPanelState extends State { final Box localCache = GStrorage.localCache; - late double sheetHeight; late Future _futureBuilderFuture; late List tagsList; bool showDefault = true; @@ -26,7 +26,6 @@ class _GroupPanelState extends State { @override void initState() { super.initState(); - sheetHeight = localCache.get('sheetHeight'); _futureBuilderFuture = MemberHttp.followUpTags(); } @@ -56,8 +55,14 @@ class _GroupPanelState extends State { @override Widget build(BuildContext context) { return Container( - height: sheetHeight, - color: Theme.of(context).colorScheme.surface, + clipBehavior: Clip.hardEdge, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.surface, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), + ), child: Column( children: [ AppBar( @@ -79,6 +84,7 @@ class _GroupPanelState extends State { if (data['status']) { tagsList = data['data']; return ListView.builder( + controller: widget.scrollController, itemCount: data['data'].length, itemBuilder: (context, index) { return ListTile( diff --git a/pubspec.lock b/pubspec.lock index a46127f9..a42df7bb 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -113,6 +113,22 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" + bottom_inset_observer: + dependency: transitive + description: + name: bottom_inset_observer + sha256: cbfb01e0e07cc4922052701786d5e607765a6f54e1844f41061abf8744519a7d + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.1.0" + bottom_sheet: + dependency: "direct main" + description: + name: bottom_sheet + sha256: efd28f52357d23e1c01eaeb45466b407f1e29318305bd6d10baf814fda18bd7e + url: "https://pub.flutter-io.cn" + source: hosted + version: "4.0.4" build: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index b5d45714..fb4cc2c0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -146,6 +146,7 @@ dependencies: lottie: ^3.1.2 # 二维码 qr_flutter: ^4.1.0 + bottom_sheet: ^4.0.4 dev_dependencies: flutter_test: From f598b6adadf00c0d6f2f7a3d0e088f9faabd952d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 26 Aug 2024 23:14:37 +0800 Subject: [PATCH 310/349] opt: liveRoom getBuvid --- lib/http/init.dart | 27 +++++++++++++++++++++++++-- lib/pages/live_room/controller.dart | 4 +++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/lib/http/init.dart b/lib/http/init.dart index faa57dd5..6a90a87d 100644 --- a/lib/http/init.dart +++ b/lib/http/init.dart @@ -29,7 +29,7 @@ class Request { late String systemProxyPort; static final RegExp spmPrefixExp = RegExp(r''); - static late String buvid; + static String? buvid; /// 设置cookie static setCookie() async { @@ -72,7 +72,6 @@ class Request { .map((Cookie cookie) => '${cookie.name}=${cookie.value}') .join('; '); - buvid = cookie.firstWhere((e) => e.name == 'buvid3').value; dio.options.headers['cookie'] = cookieString; } @@ -87,6 +86,30 @@ class Request { return token; } + static Future getBuvid() async { + if (buvid != null) { + return buvid!; + } + + final List cookies = await cookieManager.cookieJar + .loadForRequest(Uri.parse(HttpString.baseUrl)); + buvid = cookies.firstWhere((cookie) => cookie.name == 'buvid3').value; + if (buvid == null) { + try { + var result = await Request().get( + "${HttpString.apiBaseUrl}/x/frontend/finger/spi", + ); + buvid = result["data"]["b_3"].toString(); + } catch (e) { + // 处理请求错误 + buvid = ''; + print("Error fetching buvid: $e"); + } + } + + return buvid!; + } + static setOptionsHeaders(userInfo, bool status) { if (status) { dio.options.headers['x-bili-mid'] = userInfo.mid.toString(); diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 99025dce..5d4e2b67 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -47,6 +47,7 @@ class LiveRoomController extends GetxController { RxMap joinRoomTip = {'userName': '', 'message': ''}.obs; // 直播间弹幕开关 默认打开 RxBool danmakuSwitch = true.obs; + late String buvid; @override void onInit() { @@ -63,6 +64,7 @@ class LiveRoomController extends GetxController { if (liveItem != null && liveItem.cover != null && liveItem.cover != '') { cover = liveItem.cover; } + Request.getBuvid().then((value) => buvid = value); } // CDN优化 enableCDN = setting.get(SettingBoxKey.enableCDN, defaultValue: true); @@ -249,7 +251,7 @@ class LiveRoomController extends GetxController { "uid": userId, "roomid": roomId, "protover": 3, - "buvid": Request.buvid, + "buvid": buvid, "platform": "web", "type": 2, "key": token, From 2e329553d914d4203e776d27c48194fc32fb2fdd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 26 Aug 2024 23:23:43 +0800 Subject: [PATCH 311/349] =?UTF-8?q?opt:=20=E6=95=B0=E6=8D=AE=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/network_img_layer.dart | 4 +- lib/main.dart | 8 +- lib/pages/main/view.dart | 5 +- lib/pages/setting/pages/action_menu_set.dart | 4 +- lib/pages/setting/pages/play_gesture_set.dart | 6 +- lib/pages/setting/play_setting.dart | 4 +- lib/pages/setting/style_setting.dart | 4 +- lib/pages/video/detail/introduction/view.dart | 4 +- lib/plugin/pl_player/controller.dart | 58 +++------- lib/plugin/pl_player/view.dart | 6 +- lib/utils/global_data.dart | 24 ----- lib/utils/global_data_cache.dart | 100 ++++++++++++++++++ lib/utils/storage.dart | 9 -- 13 files changed, 137 insertions(+), 99 deletions(-) delete mode 100644 lib/utils/global_data.dart create mode 100644 lib/utils/global_data_cache.dart diff --git a/lib/common/widgets/network_img_layer.dart b/lib/common/widgets/network_img_layer.dart index d2772478..0b715a89 100644 --- a/lib/common/widgets/network_img_layer.dart +++ b/lib/common/widgets/network_img_layer.dart @@ -2,7 +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 'package:pilipala/utils/global_data_cache.dart'; import '../../utils/storage.dart'; import '../constants.dart'; @@ -33,7 +33,7 @@ class NetworkImgLayer extends StatelessWidget { @override Widget build(BuildContext context) { - final int defaultImgQuality = GlobalData().imgQuality; + final int defaultImgQuality = GlobalDataCache().imgQuality; if (src == '' || src == null) { return placeholder(context); } diff --git a/lib/main.dart b/lib/main.dart index 05c0476c..78747279 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -20,7 +20,7 @@ import 'package:pilipala/pages/main/view.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/app_scheme.dart'; import 'package:pilipala/utils/data.dart'; -import 'package:pilipala/utils/global_data.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:media_kit/media_kit.dart'; import 'package:pilipala/utils/recommend_filter.dart'; @@ -33,7 +33,6 @@ void main() async { await SystemChrome.setPreferredOrientations( [DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]); await GStrorage.init(); - await setupServiceLocator(); clearLogs(); Request(); await Request.setCookie(); @@ -266,10 +265,11 @@ class BuildMainApp extends StatelessWidget { VideoDetailPage.routeObserver, SearchPage.routeObserver, ], - onInit: () { + onReady: () async { RecommendFilter(); Data.init(); - GlobalData(); + await GlobalDataCache().initialize(); + setupServiceLocator(); }, ); } diff --git a/lib/pages/main/view.dart b/lib/pages/main/view.dart index 00eafa0d..3da667e8 100644 --- a/lib/pages/main/view.dart +++ b/lib/pages/main/view.dart @@ -10,7 +10,6 @@ import 'package:pilipala/pages/media/index.dart'; import 'package:pilipala/pages/rank/index.dart'; import 'package:pilipala/utils/event_bus.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/global_data.dart'; import 'package:pilipala/utils/storage.dart'; import './controller.dart'; @@ -30,6 +29,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { int? _lastSelectTime; //上次点击时间 Box setting = GStrorage.setting; + late bool enableMYBar; @override void initState() { @@ -37,6 +37,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { _lastSelectTime = DateTime.now().millisecondsSinceEpoch; _mainController.pageController = PageController(initialPage: _mainController.selectedIndex); + enableMYBar = setting.get(SettingBoxKey.enableMYBar, defaultValue: true); } void setIndex(int value) async { @@ -171,7 +172,7 @@ class _MainAppState extends State with SingleTickerProviderStateMixin { curve: Curves.easeInOutCubicEmphasized, duration: const Duration(milliseconds: 500), offset: Offset(0, snapshot.data ? 0 : 1), - child: GlobalData().enableMYBar + child: enableMYBar ? Obx( () => NavigationBar( onDestinationSelected: (value) => setIndex(value), diff --git a/lib/pages/setting/pages/action_menu_set.dart b/lib/pages/setting/pages/action_menu_set.dart index 7a4fd9ba..3655df73 100644 --- a/lib/pages/setting/pages/action_menu_set.dart +++ b/lib/pages/setting/pages/action_menu_set.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/models/common/action_type.dart'; -import 'package:pilipala/utils/global_data.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import '../../../utils/storage.dart'; class ActionMenuSetPage extends StatefulWidget { @@ -38,7 +38,7 @@ class _ActionMenuSetPageState extends State { .map((i) => (i['value'] as ActionType).value) .toList(); setting.put(SettingBoxKey.actionTypeSort, sortedTabbar); - GlobalData().actionTypeSort = sortedTabbar; + GlobalDataCache().actionTypeSort = sortedTabbar; SmartDialog.showToast('操作成功'); } diff --git a/lib/pages/setting/pages/play_gesture_set.dart b/lib/pages/setting/pages/play_gesture_set.dart index f688c43c..e671bfb2 100644 --- a/lib/pages/setting/pages/play_gesture_set.dart +++ b/lib/pages/setting/pages/play_gesture_set.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:hive/hive.dart'; -import 'package:pilipala/utils/global_data.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import '../../../models/common/gesture_mode.dart'; import '../../../utils/storage.dart'; @@ -64,11 +64,11 @@ class _PlayGesturePageState extends State { }, ); if (result != null) { - GlobalData().fullScreenGestureMode = FullScreenGestureMode + GlobalDataCache().fullScreenGestureMode = FullScreenGestureMode .values .firstWhere((element) => element.values == result); fullScreenGestureMode = - GlobalData().fullScreenGestureMode.index; + GlobalDataCache().fullScreenGestureMode.index; setting.put( SettingBoxKey.fullScreenGestureMode, fullScreenGestureMode); setState(() {}); diff --git a/lib/pages/setting/play_setting.dart b/lib/pages/setting/play_setting.dart index cb8a3996..a3c75ab5 100644 --- a/lib/pages/setting/play_setting.dart +++ b/lib/pages/setting/play_setting.dart @@ -9,7 +9,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/global_data_cache.dart'; import 'package:pilipala/utils/storage.dart'; import '../../models/live/quality.dart'; @@ -162,7 +162,7 @@ class _PlaySettingState extends State { setKey: SettingBoxKey.enablePlayerControlAnimation, defaultVal: true, callFn: (bool val) { - GlobalData().enablePlayerControlAnimation = val; + GlobalDataCache().enablePlayerControlAnimation = val; }), SetSwitchItem( title: '港澳台模式', diff --git a/lib/pages/setting/style_setting.dart b/lib/pages/setting/style_setting.dart index 5fca0c86..544dd4e8 100644 --- a/lib/pages/setting/style_setting.dart +++ b/lib/pages/setting/style_setting.dart @@ -8,7 +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/global_data_cache.dart'; import 'package:pilipala/utils/storage.dart'; import '../../models/common/dynamic_badge_mode.dart'; @@ -176,7 +176,7 @@ class _StyleSettingState extends State { SettingBoxKey.defaultPicQa, picQuality); Get.back(); settingController.picQuality.value = picQuality; - GlobalData().imgQuality = picQuality; + GlobalDataCache().imgQuality = picQuality; SmartDialog.showToast('设置成功'); }, child: const Text('确定'), diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index a89b4d28..4a8feaf2 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -20,7 +20,7 @@ import 'package:pilipala/models/video_detail_res.dart'; import 'package:pilipala/pages/video/detail/introduction/controller.dart'; import 'package:pilipala/pages/video/detail/widgets/ai_detail.dart'; import 'package:pilipala/utils/feed_back.dart'; -import 'package:pilipala/utils/global_data.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:pilipala/utils/utils.dart'; import '../../../../http/user.dart'; @@ -569,7 +569,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { } Widget actionGrid(BuildContext context, videoIntroController) { - final actionTypeSort = GlobalData().actionTypeSort; + final actionTypeSort = GlobalDataCache().actionTypeSort; Widget progressWidget(progress) { return SizedBox( diff --git a/lib/plugin/pl_player/controller.dart b/lib/plugin/pl_player/controller.dart index a5de33fd..8dff954a 100644 --- a/lib/plugin/pl_player/controller.dart +++ b/lib/plugin/pl_player/controller.dart @@ -18,6 +18,7 @@ import 'package:pilipala/plugin/pl_player/index.dart'; import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/services/service_locator.dart'; import 'package:pilipala/utils/feed_back.dart'; +import 'package:pilipala/utils/global_data_cache.dart'; import 'package:pilipala/utils/storage.dart'; import 'package:screen_brightness/screen_brightness.dart'; import 'package:status_bar_control/status_bar_control.dart'; @@ -277,50 +278,19 @@ class PlPlayerController { // 添加一个私有构造函数 PlPlayerController._internal(this.videoType) { - isOpenDanmu.value = - setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); - blockTypes = - localCache.get(LocalCacheKey.danmakuBlockType, defaultValue: []); - showArea = localCache.get(LocalCacheKey.danmakuShowArea, defaultValue: 0.5); - // 不透明度 - opacityVal = - localCache.get(LocalCacheKey.danmakuOpacity, defaultValue: 1.0); - // 字体大小 - fontSizeVal = - localCache.get(LocalCacheKey.danmakuFontScale, defaultValue: 1.0); - // 弹幕时间 - danmakuDurationVal = - localCache.get(LocalCacheKey.danmakuDuration, defaultValue: 4.0); - // 描边粗细 - strokeWidth = localCache.get(LocalCacheKey.strokeWidth, defaultValue: 1.5); - playRepeat = PlayRepeat.values.toList().firstWhere( - (e) => - e.value == - videoStorage.get(VideoBoxKey.playRepeat, - defaultValue: PlayRepeat.pause.value), - ); - _playbackSpeed.value = - videoStorage.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0); - enableAutoLongPressSpeed = setting - .get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false); - if (!enableAutoLongPressSpeed) { - _longPressSpeed.value = videoStorage - .get(VideoBoxKey.longPressSpeedDefault, defaultValue: 2.0); - } - // 自定义倍速集合 - speedsList = List.from(videoStorage - .get(VideoBoxKey.customSpeedsList, defaultValue: [])); - // 默认倍速 - speedsList = List.from(videoStorage - .get(VideoBoxKey.customSpeedsList, defaultValue: [])); - //playSpeedSystem - final List playSpeedSystem = - videoStorage.get(VideoBoxKey.playSpeedSystem, defaultValue: playSpeed); - - // for (final PlaySpeed i in PlaySpeed.values) { - speedsList.addAll(playSpeedSystem); - // } - + final cache = GlobalDataCache(); + isOpenDanmu.value = cache.isOpenDanmu; + blockTypes = cache.blockTypes; + showArea = cache.showArea; + opacityVal = cache.opacityVal; + fontSizeVal = cache.fontSizeVal; + danmakuDurationVal = cache.danmakuDurationVal; + strokeWidth = cache.strokeWidth; + playRepeat = cache.playRepeat; + _playbackSpeed.value = cache.playbackSpeed; + enableAutoLongPressSpeed = cache.enableAutoLongPressSpeed; + _longPressSpeed.value = cache.longPressSpeed; + speedsList = cache.speedsList; // _playerEventSubs = onPlayerStatusChanged.listen((PlayerStatus status) { // if (status == PlayerStatus.playing) { // WakelockPlus.enable(); diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index f3e0946b..e3e14caf 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -19,7 +19,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 '../../utils/global_data_cache.dart'; import 'models/bottom_control_type.dart'; import 'models/bottom_progress_behavior.dart'; import 'widgets/app_bar_ani.dart'; @@ -87,7 +87,7 @@ class _PLVideoPlayerState extends State late bool enableBackgroundPlay; late double screenWidth; final FullScreenGestureMode fullScreenGestureMode = - GlobalData().fullScreenGestureMode; + GlobalDataCache().fullScreenGestureMode; // 用于记录上一次全屏切换手势触发时间,避免误触 DateTime? lastFullScreenToggleTime; @@ -132,7 +132,7 @@ class _PLVideoPlayerState extends State screenWidth = Get.size.width; animationController = AnimationController( vsync: this, - duration: GlobalData().enablePlayerControlAnimation + duration: GlobalDataCache().enablePlayerControlAnimation ? const Duration(milliseconds: 150) : const Duration(milliseconds: 10), ); diff --git a/lib/utils/global_data.dart b/lib/utils/global_data.dart deleted file mode 100644 index 97bff5a5..00000000 --- a/lib/utils/global_data.dart +++ /dev/null @@ -1,24 +0,0 @@ -import 'package:hive/hive.dart'; -import 'package:pilipala/utils/storage.dart'; -import '../models/common/index.dart'; - -Box setting = GStrorage.setting; - -class GlobalData { - int imgQuality = 10; - FullScreenGestureMode fullScreenGestureMode = - FullScreenGestureMode.values.last; - bool enablePlayerControlAnimation = true; - final bool enableMYBar = - setting.get(SettingBoxKey.enableMYBar, defaultValue: true); - List actionTypeSort = setting.get(SettingBoxKey.actionTypeSort, - defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); - // 私有构造函数 - GlobalData._(); - - // 单例实例 - static final GlobalData _instance = GlobalData._(); - - // 获取全局实例 - factory GlobalData() => _instance; -} diff --git a/lib/utils/global_data_cache.dart b/lib/utils/global_data_cache.dart new file mode 100644 index 00000000..c5884e79 --- /dev/null +++ b/lib/utils/global_data_cache.dart @@ -0,0 +1,100 @@ +import 'package:hive/hive.dart'; +import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; +import 'package:pilipala/plugin/pl_player/models/play_speed.dart'; +import 'package:pilipala/utils/storage.dart'; +import '../models/common/index.dart'; + +Box setting = GStrorage.setting; +Box localCache = GStrorage.localCache; +Box videoStorage = GStrorage.video; + +class GlobalDataCache { + late int imgQuality; + late FullScreenGestureMode fullScreenGestureMode; + late bool enablePlayerControlAnimation; + late List actionTypeSort; + + /// 播放器相关 + // 弹幕开关 + late bool isOpenDanmu; + // 弹幕屏蔽类型 + late List blockTypes; + // 弹幕展示区域 + late double showArea; + // 弹幕透明度 + late double opacityVal; + // 弹幕字体大小 + late double fontSizeVal; + // 弹幕显示时间 + late double danmakuDurationVal; + // 弹幕描边宽度 + late double strokeWidth; + // 播放器循环模式 + late PlayRepeat playRepeat; + // 播放器默认播放速度 + late double playbackSpeed; + // 播放器自动长按速度 + late bool enableAutoLongPressSpeed; + // 播放器长按速度 + late double longPressSpeed; + // 播放器速度列表 + late List speedsList; + + // 私有构造函数 + GlobalDataCache._(); + + // 单例实例 + static final GlobalDataCache _instance = GlobalDataCache._(); + + // 获取全局实例 + factory GlobalDataCache() => _instance; + + // 异步初始化方法 + Future initialize() async { + imgQuality = await setting.get(SettingBoxKey.defaultPicQa, + defaultValue: 10); // 设置全局变量 + fullScreenGestureMode = FullScreenGestureMode.values[setting.get( + SettingBoxKey.fullScreenGestureMode, + defaultValue: FullScreenGestureMode.values.last.index) as int]; + enablePlayerControlAnimation = setting + .get(SettingBoxKey.enablePlayerControlAnimation, defaultValue: true); + actionTypeSort = await setting.get(SettingBoxKey.actionTypeSort, + defaultValue: ['like', 'coin', 'collect', 'watchLater', 'share']); + + isOpenDanmu = + await setting.get(SettingBoxKey.enableShowDanmaku, defaultValue: false); + blockTypes = + await localCache.get(LocalCacheKey.danmakuBlockType, defaultValue: []); + showArea = + await localCache.get(LocalCacheKey.danmakuShowArea, defaultValue: 0.5); + opacityVal = + await localCache.get(LocalCacheKey.danmakuOpacity, defaultValue: 1.0); + fontSizeVal = + await localCache.get(LocalCacheKey.danmakuFontScale, defaultValue: 1.0); + danmakuDurationVal = + await localCache.get(LocalCacheKey.danmakuDuration, defaultValue: 4.0); + strokeWidth = + await localCache.get(LocalCacheKey.strokeWidth, defaultValue: 1.5); + + var defaultPlayRepeat = await videoStorage.get(VideoBoxKey.playRepeat, + defaultValue: PlayRepeat.pause.value); + playRepeat = PlayRepeat.values + .toList() + .firstWhere((e) => e.value == defaultPlayRepeat); + playbackSpeed = + await videoStorage.get(VideoBoxKey.playSpeedDefault, defaultValue: 1.0); + enableAutoLongPressSpeed = await setting + .get(SettingBoxKey.enableAutoLongPressSpeed, defaultValue: false); + if (!enableAutoLongPressSpeed) { + longPressSpeed = await videoStorage.get(VideoBoxKey.longPressSpeedDefault, + defaultValue: 2.0); + } else { + longPressSpeed = 2.0; + } + speedsList = List.from(await videoStorage + .get(VideoBoxKey.customSpeedsList, defaultValue: [])); + final List playSpeedSystem = await videoStorage + .get(VideoBoxKey.playSpeedSystem, defaultValue: playSpeed); + speedsList.addAll(playSpeedSystem); + } +} diff --git a/lib/utils/storage.dart b/lib/utils/storage.dart index a132c5da..d9b9e100 100644 --- a/lib/utils/storage.dart +++ b/lib/utils/storage.dart @@ -2,8 +2,6 @@ import 'dart:io'; import 'package:hive_flutter/hive_flutter.dart'; import 'package:path_provider/path_provider.dart'; import 'package:pilipala/models/user/info.dart'; -import '../models/common/gesture_mode.dart'; -import 'global_data.dart'; class GStrorage { static late final Box userInfo; @@ -42,13 +40,6 @@ 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]; - GlobalData().enablePlayerControlAnimation = setting - .get(SettingBoxKey.enablePlayerControlAnimation, defaultValue: true); } static void regAdapter() { From 478dd39b205e0992d244fa3cc5759c6a54f24143 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 26 Aug 2024 23:48:12 +0800 Subject: [PATCH 312/349] =?UTF-8?q?mod:=20bottomSheet=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/follow/widgets/follow_item.dart | 5 +- .../video/detail/introduction/controller.dart | 5 +- lib/pages/video/detail/introduction/view.dart | 5 +- .../introduction/widgets/fav_panel.dart | 218 +++++++++--------- .../introduction/widgets/group_panel.dart | 183 +++++++-------- 5 files changed, 201 insertions(+), 215 deletions(-) diff --git a/lib/pages/follow/widgets/follow_item.dart b/lib/pages/follow/widgets/follow_item.dart index 3d393277..6ee93a98 100644 --- a/lib/pages/follow/widgets/follow_item.dart +++ b/lib/pages/follow/widgets/follow_item.dart @@ -49,7 +49,10 @@ class FollowItem extends StatelessWidget { child: TextButton( onPressed: () async { await showFlexibleBottomSheet( - bottomSheetColor: Colors.transparent, + bottomSheetBorderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), minHeight: 1, initHeight: 1, maxHeight: 1, diff --git a/lib/pages/video/detail/introduction/controller.dart b/lib/pages/video/detail/introduction/controller.dart index d564aba4..3d22cb87 100644 --- a/lib/pages/video/detail/introduction/controller.dart +++ b/lib/pages/video/detail/introduction/controller.dart @@ -525,7 +525,10 @@ class VideoIntroController extends GetxController { // 设置关注分组 void setFollowGroup() { showFlexibleBottomSheet( - bottomSheetColor: Colors.transparent, + bottomSheetBorderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), minHeight: 0.6, initHeight: 0.6, maxHeight: 1, diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index a89b4d28..1ee0744b 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -230,7 +230,10 @@ class _VideoInfoState extends State with TickerProviderStateMixin { void _showFavPanel() { showFlexibleBottomSheet( - bottomSheetColor: Colors.transparent, + bottomSheetBorderRadius: const BorderRadius.only( + topLeft: Radius.circular(16), + topRight: Radius.circular(16), + ), minHeight: 0.6, initHeight: 0.6, maxHeight: 1, diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index a4e98df7..c8c71e40 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -26,128 +26,116 @@ class _FavPanelState extends State { @override Widget build(BuildContext context) { - return Container( - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), + return Column( + children: [ + AppBar( + centerTitle: false, + elevation: 0, + automaticallyImplyLeading: false, + leadingWidth: 0, + title: Text( + '选择收藏夹', + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), + ), ), - ), - child: Column( - children: [ - AppBar( - centerTitle: false, - elevation: 0, - automaticallyImplyLeading: false, - leadingWidth: 0, - title: Text( - '选择收藏夹', - style: Theme.of(context) - .textTheme - .titleMedium! - .copyWith(fontWeight: FontWeight.bold), - ), - ), - Expanded( - child: Material( - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { - return Obx( - () => ListView.builder( - controller: widget.scrollController, - itemCount: - widget.ctr!.favFolderData.value.list!.length, - itemBuilder: (context, index) { - final item = - widget.ctr!.favFolderData.value.list![index]; - return ListTile( - onTap: () => widget.ctr! - .onChoose(item.favState != 1, index), - dense: true, - leading: const Icon(Icons.folder_outlined), - minLeadingWidth: 0, - title: Text(widget.ctr!.favFolderData.value - .list![index].title!), - subtitle: Text( - '${item.mediaCount}个内容 ', + Expanded( + child: Material( + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (data['status']) { + return Obx( + () => ListView.builder( + controller: widget.scrollController, + itemCount: widget.ctr!.favFolderData.value.list!.length, + itemBuilder: (context, index) { + final item = + widget.ctr!.favFolderData.value.list![index]; + return ListTile( + onTap: () => + widget.ctr!.onChoose(item.favState != 1, index), + dense: true, + leading: const Icon(Icons.folder_outlined), + minLeadingWidth: 0, + title: Text(widget + .ctr!.favFolderData.value.list![index].title!), + subtitle: Text( + '${item.mediaCount}个内容 ', + ), + trailing: Transform.scale( + scale: 0.9, + child: Checkbox( + value: widget.ctr!.favFolderData.value + .list![index].favState == + 1, + onChanged: (bool? checkValue) => + widget.ctr!.onChoose(checkValue!, index), ), - trailing: Transform.scale( - scale: 0.9, - child: Checkbox( - value: widget.ctr!.favFolderData.value - .list![index].favState == - 1, - onChanged: (bool? checkValue) => - widget.ctr!.onChoose(checkValue!, index), - ), - ), - ); - }, - ), - ); - } else { - return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ); - } + ), + ); + }, + ), + ); } else { - // 骨架屏 - return const Text('请求中'); + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ); } - }, + } else { + // 骨架屏 + return const Text('请求中'); + } + }, + ), + ), + ), + Divider( + height: 1, + color: Theme.of(context).disabledColor.withOpacity(0.08), + ), + Padding( + padding: EdgeInsets.only( + left: 20, + right: 20, + top: 12, + bottom: MediaQuery.of(context).padding.bottom + 12, + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => Get.back(), + style: TextButton.styleFrom( + padding: const EdgeInsets.only(left: 30, right: 30), + backgroundColor: + Theme.of(context).colorScheme.onInverseSurface, // 设置按钮背景色 + ), + child: const Text('取消'), ), - ), - ), - Divider( - height: 1, - color: Theme.of(context).disabledColor.withOpacity(0.08), - ), - Padding( - padding: EdgeInsets.only( - left: 20, - right: 20, - top: 12, - bottom: MediaQuery.of(context).padding.bottom + 12, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () => Get.back(), - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 30, right: 30), - backgroundColor: Theme.of(context) - .colorScheme - .onInverseSurface, // 设置按钮背景色 - ), - child: const Text('取消'), + const SizedBox(width: 10), + TextButton( + onPressed: () async { + feedBack(); + await widget.ctr!.actionFavVideo(); + }, + style: TextButton.styleFrom( + padding: const EdgeInsets.only(left: 30, right: 30), + foregroundColor: Theme.of(context).colorScheme.onPrimary, + backgroundColor: + Theme.of(context).colorScheme.primary, // 设置按钮背景色 ), - const SizedBox(width: 10), - TextButton( - onPressed: () async { - feedBack(); - await widget.ctr!.actionFavVideo(); - }, - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 30, right: 30), - foregroundColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: - Theme.of(context).colorScheme.primary, // 设置按钮背景色 - ), - child: const Text('确认选择'), - ), - ], - ), + child: const Text('确认'), + ), + ], ), - ], - ), + ), + ], ); } } diff --git a/lib/pages/video/detail/introduction/widgets/group_panel.dart b/lib/pages/video/detail/introduction/widgets/group_panel.dart index 4bb0980c..c007d52d 100644 --- a/lib/pages/video/detail/introduction/widgets/group_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/group_panel.dart @@ -54,112 +54,101 @@ class _GroupPanelState extends State { @override Widget build(BuildContext context) { - return Container( - clipBehavior: Clip.hardEdge, - decoration: BoxDecoration( - color: Theme.of(context).colorScheme.surface, - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(16), - topRight: Radius.circular(16), + return Column( + children: [ + AppBar( + centerTitle: false, + elevation: 0, + leading: IconButton( + onPressed: () => Get.back(), + icon: const Icon(Icons.close_outlined)), + title: Text('设置关注分组', style: Theme.of(context).textTheme.titleMedium), ), - ), - child: Column( - children: [ - AppBar( - centerTitle: false, - elevation: 0, - leading: IconButton( - onPressed: () => Get.back(), - icon: const Icon(Icons.close_outlined)), - title: - Text('设置关注分组', style: Theme.of(context).textTheme.titleMedium), - ), - Expanded( - child: Material( - child: FutureBuilder( - future: _futureBuilderFuture, - builder: (BuildContext context, AsyncSnapshot snapshot) { - if (snapshot.connectionState == ConnectionState.done) { - Map data = snapshot.data as Map; - if (data['status']) { - tagsList = data['data']; - return ListView.builder( - controller: widget.scrollController, - itemCount: data['data'].length, - itemBuilder: (context, index) { - return ListTile( - onTap: () { - data['data'][index].checked = - !data['data'][index].checked; - showDefault = - !data['data'].any((e) => e.checked == true); - setState(() {}); - }, - dense: true, - leading: const Icon(Icons.group_outlined), - minLeadingWidth: 0, - title: Text(data['data'][index].name), - subtitle: data['data'][index].tip != '' - ? Text(data['data'][index].tip) - : null, - trailing: Transform.scale( - scale: 0.9, - child: Checkbox( - value: data['data'][index].checked, - onChanged: (bool? checkValue) { - data['data'][index].checked = checkValue; - showDefault = !data['data'] - .any((e) => e.checked == true); - setState(() {}); - }, - ), + Expanded( + child: Material( + child: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + Map data = snapshot.data as Map; + if (data['status']) { + tagsList = data['data']; + return ListView.builder( + controller: widget.scrollController, + itemCount: data['data'].length, + itemBuilder: (context, index) { + return ListTile( + onTap: () { + data['data'][index].checked = + !data['data'][index].checked; + showDefault = + !data['data'].any((e) => e.checked == true); + setState(() {}); + }, + dense: true, + leading: const Icon(Icons.group_outlined), + minLeadingWidth: 0, + title: Text(data['data'][index].name), + subtitle: data['data'][index].tip != '' + ? Text(data['data'][index].tip) + : null, + trailing: Transform.scale( + scale: 0.9, + child: Checkbox( + value: data['data'][index].checked, + onChanged: (bool? checkValue) { + data['data'][index].checked = checkValue; + showDefault = + !data['data'].any((e) => e.checked == true); + setState(() {}); + }, ), - ); - }, - ); - } else { - return HttpError( - errMsg: data['msg'], - fn: () => setState(() {}), - ); - } + ), + ); + }, + ); } else { - // 骨架屏 - return const Text('请求中'); + return HttpError( + errMsg: data['msg'], + fn: () => setState(() {}), + ); } - }, - ), + } else { + // 骨架屏 + return const Text('请求中'); + } + }, ), ), - Divider( - height: 1, - color: Theme.of(context).disabledColor.withOpacity(0.08), + ), + Divider( + height: 1, + color: Theme.of(context).disabledColor.withOpacity(0.08), + ), + Padding( + padding: EdgeInsets.only( + left: 20, + right: 20, + top: 12, + bottom: MediaQuery.of(context).padding.bottom + 12, ), - Padding( - padding: EdgeInsets.only( - left: 20, - right: 20, - top: 12, - bottom: MediaQuery.of(context).padding.bottom + 12, - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - TextButton( - onPressed: () => onSave(), - style: TextButton.styleFrom( - padding: const EdgeInsets.only(left: 30, right: 30), - foregroundColor: Theme.of(context).colorScheme.onPrimary, - backgroundColor: - Theme.of(context).colorScheme.primary, // 设置按钮背景色 - ), - child: Text(showDefault ? '保存至默认分组' : '保存'), + child: Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + TextButton( + onPressed: () => onSave(), + style: TextButton.styleFrom( + padding: const EdgeInsets.only(left: 30, right: 30), + foregroundColor: Theme.of(context).colorScheme.onPrimary, + backgroundColor: + Theme.of(context).colorScheme.primary, // 设置按钮背景色 ), - ], - ), + child: Text(showDefault ? '保存至默认分组' : '保存'), + ), + ], ), - ], - ), + ), + ], ); } } From 01cae5e280921c8acc19ed2fc0c1f1747b905650 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 27 Aug 2024 23:46:01 +0800 Subject: [PATCH 313/349] mod: GlobalDataCache init --- lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index 78747279..fe93da22 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -64,6 +64,7 @@ void main() async { } PiliSchame.init(); + await GlobalDataCache().initialize(); } class MyApp extends StatelessWidget { @@ -268,7 +269,6 @@ class BuildMainApp extends StatelessWidget { onReady: () async { RecommendFilter(); Data.init(); - await GlobalDataCache().initialize(); setupServiceLocator(); }, ); From 67aa95c027f1373322402e905dbbeb342f2ae87d Mon Sep 17 00:00:00 2001 From: guozhigq Date: Tue, 27 Aug 2024 23:57:19 +0800 Subject: [PATCH 314/349] =?UTF-8?q?fix:=20=E9=83=A8=E5=88=86=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=BF=BD=E7=95=A5error=20response=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/interceptor.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/http/interceptor.dart b/lib/http/interceptor.dart index 10b5aee5..259a3bf9 100644 --- a/lib/http/interceptor.dart +++ b/lib/http/interceptor.dart @@ -46,7 +46,8 @@ class ApiInterceptor extends Interceptor { // 处理网络请求错误 // handler.next(err); String url = err.requestOptions.uri.toString(); - if (!url.contains('heartbeat')) { + final excludedPatterns = RegExp(r'heartbeat|seg\.so|online/total'); + if (!excludedPatterns.hasMatch(url)) { SmartDialog.showToast( await dioError(err), displayType: SmartToastType.onlyRefresh, From 5c7f477ad8d467f93a056794165ef30e15e4584a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 31 Aug 2024 14:26:03 +0800 Subject: [PATCH 315/349] =?UTF-8?q?mod=EF=BC=9A=E6=94=B6=E8=97=8F=E5=A4=B9?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E5=B1=95=E7=A4=BA(=E5=85=AC=E5=BC=80/?= =?UTF-8?q?=E7=A7=81=E5=AF=86)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/fav/widgets/item.dart | 11 ++++++++++- .../detail/introduction/widgets/fav_panel.dart | 17 ++++++++++------- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/pages/fav/widgets/item.dart b/lib/pages/fav/widgets/item.dart index 9d453fb5..25b92a5c 100644 --- a/lib/pages/fav/widgets/item.dart +++ b/lib/pages/fav/widgets/item.dart @@ -74,7 +74,7 @@ class VideoContent extends StatelessWidget { Widget build(BuildContext context) { return Expanded( child: Padding( - padding: const EdgeInsets.fromLTRB(10, 2, 6, 0), + padding: const EdgeInsets.fromLTRB(10, 2, 6, 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -94,6 +94,15 @@ class VideoContent extends StatelessWidget { color: Theme.of(context).colorScheme.outline, ), ), + const Spacer(), + Text( + [23, 1].contains(favFolderItem.attr) ? '私密' : '公开', + textAlign: TextAlign.start, + style: TextStyle( + fontSize: Theme.of(context).textTheme.labelMedium!.fontSize, + color: Theme.of(context).colorScheme.outline, + ), + ), ], ), ), diff --git a/lib/pages/video/detail/introduction/widgets/fav_panel.dart b/lib/pages/video/detail/introduction/widgets/fav_panel.dart index c8c71e40..2cf7d236 100644 --- a/lib/pages/video/detail/introduction/widgets/fav_panel.dart +++ b/lib/pages/video/detail/introduction/widgets/fav_panel.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/common/widgets/http_error.dart'; @@ -60,19 +61,21 @@ class _FavPanelState extends State { onTap: () => widget.ctr!.onChoose(item.favState != 1, index), dense: true, - leading: const Icon(Icons.folder_outlined), + leading: Icon([23, 1].contains(item.attr) + ? Icons.lock_outline + : Icons.folder_outlined), minLeadingWidth: 0, - title: Text(widget - .ctr!.favFolderData.value.list![index].title!), + title: Text(item.title!), subtitle: Text( - '${item.mediaCount}个内容 ', + '${item.mediaCount}个内容 - ${[ + 23, + 1 + ].contains(item.attr) ? '私密' : '公开'}', ), trailing: Transform.scale( scale: 0.9, child: Checkbox( - value: widget.ctr!.favFolderData.value - .list![index].favState == - 1, + value: item.favState == 1, onChanged: (bool? checkValue) => widget.ctr!.onChoose(checkValue!, index), ), From e4a820268a5068418d3fa39ecaa706ac07c1b7d6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 4 Sep 2024 09:42:08 +0800 Subject: [PATCH 316/349] =?UTF-8?q?fix:=20iOS=E9=9D=99=E9=9F=B3=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E6=97=A0=E5=A3=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner/AppDelegate.swift | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift index 70693e4a..dfa078c3 100644 --- a/ios/Runner/AppDelegate.swift +++ b/ios/Runner/AppDelegate.swift @@ -1,5 +1,6 @@ import UIKit import Flutter +import AVFoundation @UIApplicationMain @objc class AppDelegate: FlutterAppDelegate { @@ -8,6 +9,14 @@ import Flutter didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) + + // 设置音频会话类别,确保在静音模式下播放音频 + do { + try AVAudioSession.sharedInstance().setCategory(.playback, options: [.duckOthers]) + } catch { + print("Failed to set audio session category: \(error)") + } + return super.application(application, didFinishLaunchingWithOptions: launchOptions) } -} +} \ No newline at end of file From c3595a0dcd91430a498ef660d162b001df684382 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 8 Sep 2024 15:53:56 +0800 Subject: [PATCH 317/349] =?UTF-8?q?fix:=20=E6=90=9C=E7=B4=A2=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E7=AD=9B=E9=80=89=E9=9D=A2=E6=9D=BF=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E6=BB=9A=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search_panel/widgets/video_panel.dart | 163 +++++++++--------- 1 file changed, 85 insertions(+), 78 deletions(-) diff --git a/lib/pages/search_panel/widgets/video_panel.dart b/lib/pages/search_panel/widgets/video_panel.dart index f43e2eec..7da6aaaa 100644 --- a/lib/pages/search_panel/widgets/video_panel.dart +++ b/lib/pages/search_panel/widgets/video_panel.dart @@ -261,90 +261,97 @@ class VideoPanelController extends GetxController { onShowFilterSheet(searchPanelCtr) { showModalBottomSheet( context: Get.context!, + isScrollControlled: true, builder: (context) { return StatefulBuilder( builder: (context, StateSetter setState) { - return Container( - color: Theme.of(Get.context!).colorScheme.surface, - padding: const EdgeInsets.only(top: 12), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + return Padding( + padding: EdgeInsets.only( + top: 12, bottom: MediaQuery.of(context).padding.bottom + 20), + child: Wrap( children: [ - const ListTile( - title: Text('内容时长'), - ), - Padding( - padding: const EdgeInsets.only( - left: 14, - right: 14, - bottom: 14, - ), - child: Wrap( - spacing: 10, - runSpacing: 10, - direction: Axis.horizontal, - textDirection: TextDirection.ltr, - children: [ - for (var i in timeFiltersList) - Obx( - () => SearchText( - searchText: i['label'], - searchTextIdx: i['value'], - isSelect: - currentTimeFilterval.value == i['value'], - onSelect: (value) async { - currentTimeFilterval.value = i['value']; - setState(() {}); - SmartDialog.showToast("「${i['label']}」的筛选结果"); - SearchPanelController ctr = - Get.find( + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const ListTile( + title: Text('内容时长'), + ), + Padding( + padding: const EdgeInsets.only( + left: 14, + right: 14, + bottom: 14, + ), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in timeFiltersList) + Obx( + () => SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: + currentTimeFilterval.value == i['value'], + onSelect: (value) async { + currentTimeFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast( + "「${i['label']}」的筛选结果"); + SearchPanelController ctr = Get.find< + SearchPanelController>( tag: 'video${searchPanelCtr.keyword!}'); - ctr.duration.value = i['value']; - Get.back(); - SmartDialog.showLoading(msg: '获取中'); - await ctr.onRefresh(); - SmartDialog.dismiss(); - }, - onLongSelect: (value) => {}, - ), - ) - ], - ), - ), - const ListTile( - title: Text('内容分区'), - ), - Padding( - padding: const EdgeInsets.only(left: 14, right: 14), - child: Wrap( - spacing: 10, - runSpacing: 10, - direction: Axis.horizontal, - textDirection: TextDirection.ltr, - children: [ - for (var i in partFiltersList) - SearchText( - searchText: i['label'], - searchTextIdx: i['value'], - isSelect: currentPartFilterval.value == i['value'], - onSelect: (value) async { - currentPartFilterval.value = i['value']; - setState(() {}); - SmartDialog.showToast("「${i['label']}」的筛选结果"); - SearchPanelController ctr = - Get.find( + ctr.duration.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ), + ) + ], + ), + ), + const ListTile( + title: Text('内容分区'), + ), + Padding( + padding: const EdgeInsets.only(left: 14, right: 14), + child: Wrap( + spacing: 10, + runSpacing: 10, + direction: Axis.horizontal, + textDirection: TextDirection.ltr, + children: [ + for (var i in partFiltersList) + SearchText( + searchText: i['label'], + searchTextIdx: i['value'], + isSelect: + currentPartFilterval.value == i['value'], + onSelect: (value) async { + currentPartFilterval.value = i['value']; + setState(() {}); + SmartDialog.showToast("「${i['label']}」的筛选结果"); + SearchPanelController ctr = Get.find< + SearchPanelController>( tag: 'video${searchPanelCtr.keyword!}'); - ctr.tids.value = i['value']; - Get.back(); - SmartDialog.showLoading(msg: '获取中'); - await ctr.onRefresh(); - SmartDialog.dismiss(); - }, - onLongSelect: (value) => {}, - ) - ], - ), - ) + ctr.tids.value = i['value']; + Get.back(); + SmartDialog.showLoading(msg: '获取中'); + await ctr.onRefresh(); + SmartDialog.dismiss(); + }, + onLongSelect: (value) => {}, + ) + ], + ), + ) + ], + ), ], ), ); From df1866286314de2d890557415dd09a86ceb9dcd6 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 8 Sep 2024 17:46:08 +0800 Subject: [PATCH 318/349] =?UTF-8?q?fix:=20=E7=94=A8=E6=88=B7=E5=90=88?= =?UTF-8?q?=E9=9B=86=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_seasons/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/member_seasons/controller.dart b/lib/pages/member_seasons/controller.dart index 58a9035f..4e7c9762 100644 --- a/lib/pages/member_seasons/controller.dart +++ b/lib/pages/member_seasons/controller.dart @@ -21,7 +21,7 @@ class MemberSeasonsController extends GetxController { mid = int.parse(Get.parameters['mid']!); category = Get.parameters['category']!; if (category == '0') { - seasonId = int.parse(Get.parameters['seriesId']!); + seasonId = int.parse(Get.parameters['seasonId']!); } if (category == '1') { seriesId = int.parse(Get.parameters['seriesId']!); From 1d87e66433fdfc04aab9bd3f9ba1a0eab55da45f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 8 Sep 2024 17:46:23 +0800 Subject: [PATCH 319/349] =?UTF-8?q?fix:=20=E7=BF=BB=E9=A1=B5=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member_archive/controller.dart | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pages/member_archive/controller.dart b/lib/pages/member_archive/controller.dart index 667d16c5..20cf38d3 100644 --- a/lib/pages/member_archive/controller.dart +++ b/lib/pages/member_archive/controller.dart @@ -27,10 +27,13 @@ class MemberArchiveController extends GetxController { // 获取用户投稿 Future getMemberArchive(type) async { + if (isLoading.value) { + return; + } + isLoading.value = true; if (type == 'init') { pn = 1; archivesList.clear(); - isLoading.value = true; } var res = await MemberHttp.memberArchive( mid: mid, From 012b4152337faa9db1d7d6d1ecc48b4fc6dda010 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 9 Sep 2024 00:05:22 +0800 Subject: [PATCH 320/349] =?UTF-8?q?fix:=20=E7=82=B9=E8=B5=9E=E8=AE=A1?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/message/like/view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pages/message/like/view.dart b/lib/pages/message/like/view.dart index 80b20117..8b061505 100644 --- a/lib/pages/message/like/view.dart +++ b/lib/pages/message/like/view.dart @@ -203,9 +203,9 @@ class LikeItem extends StatelessWidget { Text.rich(TextSpan(children: [ TextSpan(text: nickNameList.join('、')), const TextSpan(text: ' '), - if (item.users!.length > 1) + if (item.counts! > 1) TextSpan( - text: '等总计${item.users!.length}人', + text: '等总计${item.counts}人', style: TextStyle(color: outline), ), TextSpan( From 5651466881cbffe0879e69d5da66a28f70bd454f Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 9 Sep 2024 00:12:38 +0800 Subject: [PATCH 321/349] =?UTF-8?q?fix:=20=E6=9A=97=E9=BB=91=E6=A8=A1?= =?UTF-8?q?=E5=BC=8F=E4=B8=8B=E4=B8=AA=E4=BA=BA=E8=AE=A4=E8=AF=81=E6=96=87?= =?UTF-8?q?=E6=9C=AC=E9=A2=9C=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/member/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index 7d2e4bed..be0ddedc 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -406,7 +406,7 @@ class _MemberPageState extends State ? '个人认证:' : '企业认证:', style: TextStyle( - color: Theme.of(context).primaryColor, + color: Theme.of(context).colorScheme.primary, ), children: [ TextSpan( From 10f464aab15885fba15bd9e9f1782a08bebf8344 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 9 Sep 2024 00:47:16 +0800 Subject: [PATCH 322/349] =?UTF-8?q?mod:=20=E5=8A=A8=E6=80=81=E8=A7=86?= =?UTF-8?q?=E9=A2=91=E6=A0=87=E7=AD=BE=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/dynamics/widgets/video_panel.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/dynamics/widgets/video_panel.dart b/lib/pages/dynamics/widgets/video_panel.dart index 828fb283..75f16a3b 100644 --- a/lib/pages/dynamics/widgets/video_panel.dart +++ b/lib/pages/dynamics/widgets/video_panel.dart @@ -86,7 +86,7 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) { height: width / StyleString.aspectRatio, src: content.cover, ), - if (content.badge != null && type == 'pgc') + if (content.badge != null && content.badge['text'] != null) PBadge( text: content.badge['text'], top: 8.0, From ba1704fa2af51adaadef33f841b852e2d78fb5fe Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 11 Sep 2024 00:03:14 +0800 Subject: [PATCH 323/349] =?UTF-8?q?feat:=20=E6=94=B6=E8=97=8F=E5=A4=B9?= =?UTF-8?q?=E6=96=B0=E5=BB=BA/=E7=BC=96=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 10 +++ lib/http/fav.dart | 67 ++++++++++++++++ lib/pages/fav/view.dart | 16 ++++ lib/pages/fav_detail/controller.dart | 13 ++++ lib/pages/fav_detail/view.dart | 5 ++ lib/pages/fav_edit/controller.dart | 77 +++++++++++++++++++ lib/pages/fav_edit/index.dart | 4 + lib/pages/fav_edit/view.dart | 111 +++++++++++++++++++++++++++ lib/router/app_pages.dart | 3 + 9 files changed, 306 insertions(+) create mode 100644 lib/http/fav.dart create mode 100644 lib/pages/fav_edit/controller.dart create mode 100644 lib/pages/fav_edit/index.dart create mode 100644 lib/pages/fav_edit/view.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 46bbb6ac..d34bfa0e 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -552,4 +552,14 @@ class Api { /// 系统通知 static const String messageSystemAPi = '${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify'; + + /// 系统通知标记已读 + static const String systemMarkRead = + '${HttpString.messageBaseUrl}/x/sys-msg/update_cursor'; + + /// 编辑收藏夹 + static const String editFavFolder = '/x/v3/fav/folder/edit'; + + /// 新建收藏夹 + static const String addFavFolder = '/x/v3/fav/folder/add'; } diff --git a/lib/http/fav.dart b/lib/http/fav.dart new file mode 100644 index 00000000..6f49d68a --- /dev/null +++ b/lib/http/fav.dart @@ -0,0 +1,67 @@ +import 'index.dart'; + +class FavHttp { + /// 编辑收藏夹 + static Future editFolder({ + required String title, + required String intro, + required String mediaId, + String? cover, + int? privacy, + }) async { + var res = await Request().post( + Api.editFavFolder, + queryParameters: { + 'title': title, + 'intro': intro, + 'media_id': mediaId, + 'cover': cover ?? '', + 'privacy': privacy ?? 0, + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } + + /// 新建收藏夹 + static Future addFolder({ + required String title, + required String intro, + String? cover, + int? privacy, + }) async { + var res = await Request().post( + Api.addFavFolder, + queryParameters: { + 'title': title, + 'intro': intro, + 'cover': cover ?? '', + 'privacy': privacy ?? 0, + 'csrf': await Request.getCsrf(), + }, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } +} diff --git a/lib/pages/fav/view.dart b/lib/pages/fav/view.dart index 7010ba0d..f6164609 100644 --- a/lib/pages/fav/view.dart +++ b/lib/pages/fav/view.dart @@ -55,6 +55,22 @@ class _FavPageState extends State { tooltip: 'Ta的订阅', ) : const SizedBox.shrink()), + + // 新建收藏夹 + Obx(() => _favController.isOwner.value + ? IconButton( + onPressed: () async { + await Get.toNamed('/favEdit'); + _favController.hasMore.value = true; + _favController.currentPage = 1; + setState(() { + _futureBuilderFuture = _favController.queryFavFolder(); + }); + }, + icon: const Icon(Icons.add_outlined), + tooltip: '新建收藏夹', + ) + : const SizedBox.shrink()), IconButton( onPressed: () => Get.toNamed( '/favSearch?searchType=1&mediaId=${_favController.favFolderData.value.list!.first.id}'), diff --git a/lib/pages/fav_detail/controller.dart b/lib/pages/fav_detail/controller.dart index 3f87c226..e0158587 100644 --- a/lib/pages/fav_detail/controller.dart +++ b/lib/pages/fav_detail/controller.dart @@ -115,4 +115,17 @@ class FavDetailController extends GetxController { }, ); } + + onEditFavFolder() async { + Get.toNamed( + '/favEdit', + arguments: { + 'mediaId': mediaId.toString(), + 'title': item!.title, + 'intro': item!.intro, + 'cover': item!.cover, + 'privacy': item!.attr, + }, + ); + } } diff --git a/lib/pages/fav_detail/view.dart b/lib/pages/fav_detail/view.dart index cb9d7e7b..c1865355 100644 --- a/lib/pages/fav_detail/view.dart +++ b/lib/pages/fav_detail/view.dart @@ -106,6 +106,11 @@ class _FavDetailPageState extends State { position: PopupMenuPosition.under, onSelected: (String type) {}, itemBuilder: (BuildContext context) => >[ + PopupMenuItem( + onTap: () => _favDetailController.onEditFavFolder(), + value: 'edit', + child: const Text('编辑收藏夹'), + ), PopupMenuItem( onTap: () => _favDetailController.onDelFavFolder(), value: 'pause', diff --git a/lib/pages/fav_edit/controller.dart b/lib/pages/fav_edit/controller.dart new file mode 100644 index 00000000..4772caee --- /dev/null +++ b/lib/pages/fav_edit/controller.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/fav.dart'; + +class FavEditController extends GetxController { + final GlobalKey formKey = GlobalKey(); + final TextEditingController titleController = TextEditingController(); + final TextEditingController contentController = TextEditingController(); + + final FocusNode titleTextFieldNode = FocusNode(); + final FocusNode contentTextFieldNode = FocusNode(); + + // 默认新建 + RxString type = 'add'.obs; + + String? mediaId; + String cover = ''; // 封面 + String title = ''; // 名称 + String intro = ''; // 简介 + RxInt privacy = 0.obs; // 是否公开 0公开 1私密 + + @override + void onInit() { + super.onInit(); + var args = Get.arguments; + if (args != null) { + type.value = 'edit'; + mediaId = args['mediaId']; + title = args['title']; + intro = args['intro']; + cover = args['cover']; + privacy.value = args['privacy']; + titleController.text = title; + contentController.text = intro; + } + } + + void onSubmit() async { + // 表单验证 + if ((formKey.currentState as FormState).validate()) { + if (type.value == 'edit') { + await editFolder(); + } else { + await addFolder(); + } + } + } + + Future editFolder() async { + var res = await FavHttp.editFolder( + title: title, + intro: intro, + mediaId: mediaId!, + cover: cover, + ); + if (res['status']) { + SmartDialog.showToast('编辑成功'); + Get.back(); + } else { + SmartDialog.showToast(res['msg']); + } + } + + Future addFolder() async { + var res = await FavHttp.addFolder( + title: title, + intro: intro, + ); + if (res['status']) { + SmartDialog.showToast('新建成功'); + Get.back(); + } else { + SmartDialog.showToast(res['msg']); + } + } +} diff --git a/lib/pages/fav_edit/index.dart b/lib/pages/fav_edit/index.dart new file mode 100644 index 00000000..872df8d4 --- /dev/null +++ b/lib/pages/fav_edit/index.dart @@ -0,0 +1,4 @@ +library fav_edit; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/fav_edit/view.dart b/lib/pages/fav_edit/view.dart new file mode 100644 index 00000000..3ef7cda3 --- /dev/null +++ b/lib/pages/fav_edit/view.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; + +import 'controller.dart'; + +class FavEditPage extends StatefulWidget { + const FavEditPage({super.key}); + + @override + State createState() => _FavEditPageState(); +} + +class _FavEditPageState extends State { + final FavEditController _favEditController = Get.put(FavEditController()); + String title = ''; + String content = ''; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + elevation: 0, + scrolledUnderElevation: 0, + title: Obx( + () => _favEditController.type.value == 'add' + ? Text( + '新建收藏夹', + style: Theme.of(context).textTheme.titleMedium, + ) + : Text( + '编辑收藏夹', + style: Theme.of(context).textTheme.titleMedium, + ), + ), + centerTitle: false, + actions: [ + Obx( + () => _favEditController.privacy.value == 0 + ? IconButton( + onPressed: () { + _favEditController.privacy.value = 1; + }, + icon: const Icon(Icons.lock_open_outlined)) + : IconButton( + onPressed: () { + _favEditController.privacy.value = 0; + }, + icon: Icon( + Icons.lock_outlined, + color: Theme.of(context).colorScheme.error, + )), + ), + TextButton( + onPressed: _favEditController.onSubmit, child: const Text('保存')), + const SizedBox(width: 14), + ], + ), + body: Form( + key: _favEditController.formKey, //设置globalKey,用于后面获取FormState + autovalidateMode: AutovalidateMode.disabled, + child: Column( + children: [ + Container( + padding: const EdgeInsets.fromLTRB(14, 10, 14, 5), + decoration: BoxDecoration( + border: Border( + bottom: BorderSide( + color: Theme.of(context).dividerColor.withOpacity(0.2), + ), + ), + ), + child: TextFormField( + autofocus: true, + controller: _favEditController.titleController, + focusNode: _favEditController.titleTextFieldNode, + decoration: const InputDecoration( + hintText: "收藏夹名称", + enabledBorder: InputBorder.none, + focusedBorder: InputBorder.none, + ), + // 校验标题 + validator: (v) { + return v!.trim().isNotEmpty ? null : "请输入收藏夹名称"; + }, + onChanged: (val) { + _favEditController.title = val; + }, + ), + ), + Expanded( + child: Container( + padding: + const EdgeInsets.symmetric(horizontal: 14, vertical: 5), + child: TextFormField( + controller: _favEditController.contentController, + minLines: 1, + maxLines: 5, + decoration: const InputDecoration( + hintText: '输入收藏夹简介', border: InputBorder.none), + style: Theme.of(context).textTheme.bodyLarge, + onChanged: (val) { + _favEditController.intro = val; + }, + )), + ), + ], + ), + ), + ); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 7840c126..136de91f 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:hive/hive.dart'; +import 'package:pilipala/pages/fav_edit/index.dart'; import 'package:pilipala/pages/follow_search/view.dart'; import 'package:pilipala/pages/message/at/index.dart'; import 'package:pilipala/pages/message/like/index.dart'; @@ -183,6 +184,8 @@ class Routes { // 系统通知 CustomGetPage( name: '/messageSystem', page: () => const MessageSystemPage()), + // 收藏夹编辑 + CustomGetPage(name: '/favEdit', page: () => const FavEditPage()), ]; } From 95a452dea8be36ff969f1d05b4186e2b2feec161 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 11 Sep 2024 20:25:40 +0800 Subject: [PATCH 324/349] =?UTF-8?q?opt:=20stat=E7=BB=84=E4=BB=B6=E4=BC=A0?= =?UTF-8?q?=E5=8F=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/stat/danmu.dart | 33 +++++++++++++++-- lib/common/widgets/stat/view.dart | 37 +++++++++++++++++-- lib/common/widgets/video_card_h.dart | 10 +---- lib/common/widgets/video_card_v.dart | 4 +- lib/pages/bangumi/introduction/view.dart | 2 - .../introduction/widgets/intro_detail.dart | 2 - .../fav_detail/widget/fav_video_card.dart | 8 +--- lib/pages/member_coin/widgets/item.dart | 5 +-- lib/pages/member_like/widgets/item.dart | 5 +-- lib/pages/member_seasons/widgets/item.dart | 5 +-- .../widget/sub_video_card.dart | 8 +--- lib/pages/video/detail/introduction/view.dart | 2 - 12 files changed, 73 insertions(+), 48 deletions(-) diff --git a/lib/common/widgets/stat/danmu.dart b/lib/common/widgets/stat/danmu.dart index 511839a0..9ea05301 100644 --- a/lib/common/widgets/stat/danmu.dart +++ b/lib/common/widgets/stat/danmu.dart @@ -6,7 +6,7 @@ class StatDanMu extends StatelessWidget { final dynamic danmu; final String? size; - const StatDanMu({Key? key, this.theme, this.danmu, this.size}) + const StatDanMu({Key? key, this.theme = 'gray', this.danmu, this.size}) : super(key: key); @override @@ -17,21 +17,46 @@ class StatDanMu extends StatelessWidget { 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), }; Color color = colorObject[theme]!; + return StatIconText( + icon: Icons.subtitles_outlined, + text: Utils.numFormat(danmu!), + color: color, + size: size, + ); + } +} + +class StatIconText extends StatelessWidget { + final IconData icon; + final String text; + final Color color; + final String? size; + + const StatIconText({ + Key? key, + required this.icon, + required this.text, + required this.color, + this.size, + }) : super(key: key); + + @override + Widget build(BuildContext context) { return Row( children: [ Icon( - Icons.subtitles_outlined, + icon, size: 14, color: color, ), const SizedBox(width: 2), Text( - Utils.numFormat(danmu!), + text, style: TextStyle( fontSize: size == 'medium' ? 12 : 11, color: color, ), - ) + ), ], ); } diff --git a/lib/common/widgets/stat/view.dart b/lib/common/widgets/stat/view.dart index 5359c979..85bec816 100644 --- a/lib/common/widgets/stat/view.dart +++ b/lib/common/widgets/stat/view.dart @@ -6,8 +6,12 @@ class StatView extends StatelessWidget { final dynamic view; final String? size; - const StatView({Key? key, this.theme, this.view, this.size}) - : super(key: key); + const StatView({ + Key? key, + this.theme = 'gray', + this.view, + this.size, + }) : super(key: key); @override Widget build(BuildContext context) { @@ -17,16 +21,41 @@ class StatView extends StatelessWidget { 'black': Theme.of(context).colorScheme.onSurface.withOpacity(0.8), }; Color color = colorObject[theme]!; + return StatIconText( + icon: Icons.play_circle_outlined, + text: Utils.numFormat(view!), + color: color, + size: size, + ); + } +} + +class StatIconText extends StatelessWidget { + final IconData icon; + final String text; + final Color color; + final String? size; + + const StatIconText({ + Key? key, + required this.icon, + required this.text, + required this.color, + this.size, + }) : super(key: key); + + @override + Widget build(BuildContext context) { return Row( children: [ Icon( - Icons.play_circle_outlined, + icon, size: 13, color: color, ), const SizedBox(width: 2), Text( - Utils.numFormat(view!), + text, style: TextStyle( fontSize: size == 'medium' ? 12 : 11, color: color, diff --git a/lib/common/widgets/video_card_h.dart b/lib/common/widgets/video_card_h.dart index 1265477f..78c4ba87 100644 --- a/lib/common/widgets/video_card_h.dart +++ b/lib/common/widgets/video_card_h.dart @@ -266,17 +266,11 @@ class VideoContent extends StatelessWidget { Row( children: [ if (showView) ...[ - StatView( - theme: 'gray', - view: videoItem.stat.view as int, - ), + StatView(view: videoItem.stat.view as int), const SizedBox(width: 8), ], if (showDanmaku) - StatDanMu( - theme: 'gray', - danmu: videoItem.stat.danmaku as int, - ), + StatDanMu(danmu: videoItem.stat.danmaku as int), const Spacer(), if (source == 'normal') SizedBox( diff --git a/lib/common/widgets/video_card_v.dart b/lib/common/widgets/video_card_v.dart index d8e1bb2c..7a9ef39c 100644 --- a/lib/common/widgets/video_card_v.dart +++ b/lib/common/widgets/video_card_v.dart @@ -287,9 +287,9 @@ class VideoStat extends StatelessWidget { Widget build(BuildContext context) { return Row( children: [ - StatView(theme: 'gray', view: videoItem.stat.view), + StatView(view: videoItem.stat.view), const SizedBox(width: 8), - StatDanMu(theme: 'gray', danmu: videoItem.stat.danmu), + StatDanMu(danmu: videoItem.stat.danmu), if (videoItem is RecVideoItemModel) ...[ crossAxisCount > 1 ? const Spacer() : const SizedBox(width: 8), RichText( diff --git a/lib/pages/bangumi/introduction/view.dart b/lib/pages/bangumi/introduction/view.dart index 95d4d898..94ee24de 100644 --- a/lib/pages/bangumi/introduction/view.dart +++ b/lib/pages/bangumi/introduction/view.dart @@ -255,13 +255,11 @@ class _BangumiInfoState extends State { Row( children: [ StatView( - theme: 'gray', view: widget.bangumiDetail!.stat!['views'], size: 'medium', ), const SizedBox(width: 6), StatDanMu( - theme: 'gray', danmu: widget.bangumiDetail!.stat!['danmakus'], size: 'medium', ), diff --git a/lib/pages/bangumi/introduction/widgets/intro_detail.dart b/lib/pages/bangumi/introduction/widgets/intro_detail.dart index 07684a86..409474a9 100644 --- a/lib/pages/bangumi/introduction/widgets/intro_detail.dart +++ b/lib/pages/bangumi/introduction/widgets/intro_detail.dart @@ -60,13 +60,11 @@ class IntroDetail extends StatelessWidget { Row( children: [ StatView( - theme: 'gray', view: bangumiDetail!.stat!['views'], size: 'medium', ), const SizedBox(width: 6), StatDanMu( - theme: 'gray', danmu: bangumiDetail!.stat!['danmakus'], size: 'medium', ), diff --git a/lib/pages/fav_detail/widget/fav_video_card.dart b/lib/pages/fav_detail/widget/fav_video_card.dart index 9779c549..ecb4dd4a 100644 --- a/lib/pages/fav_detail/widget/fav_video_card.dart +++ b/lib/pages/fav_detail/widget/fav_video_card.dart @@ -203,13 +203,9 @@ class VideoContent extends StatelessWidget { padding: const EdgeInsets.only(top: 2), child: Row( children: [ - StatView( - theme: 'gray', - view: videoItem.cntInfo['play'], - ), + StatView(view: videoItem.cntInfo['play']), const SizedBox(width: 8), - StatDanMu( - theme: 'gray', danmu: videoItem.cntInfo['danmaku']), + StatDanMu(danmu: videoItem.cntInfo['danmaku']), const Spacer(), ], ), diff --git a/lib/pages/member_coin/widgets/item.dart b/lib/pages/member_coin/widgets/item.dart index de28585c..6d732694 100644 --- a/lib/pages/member_coin/widgets/item.dart +++ b/lib/pages/member_coin/widgets/item.dart @@ -69,10 +69,7 @@ class MemberCoinsItem extends StatelessWidget { const SizedBox(height: 4), Row( children: [ - StatView( - view: coinItem.view, - theme: 'gray', - ), + StatView(view: coinItem.view), const Spacer(), Text( Utils.CustomStamp_str( diff --git a/lib/pages/member_like/widgets/item.dart b/lib/pages/member_like/widgets/item.dart index 57798bb7..3b3c81da 100644 --- a/lib/pages/member_like/widgets/item.dart +++ b/lib/pages/member_like/widgets/item.dart @@ -69,10 +69,7 @@ class MemberLikeItem extends StatelessWidget { const SizedBox(height: 4), Row( children: [ - StatView( - view: likeItem.stat!.view, - theme: 'gray', - ), + StatView(view: likeItem.stat!.view), const Spacer(), Text( Utils.CustomStamp_str( diff --git a/lib/pages/member_seasons/widgets/item.dart b/lib/pages/member_seasons/widgets/item.dart index 85b763b7..8d877773 100644 --- a/lib/pages/member_seasons/widgets/item.dart +++ b/lib/pages/member_seasons/widgets/item.dart @@ -78,10 +78,7 @@ class MemberSeasonsItem extends StatelessWidget { const SizedBox(height: 4), Row( children: [ - StatView( - view: seasonItem.view, - theme: 'gray', - ), + StatView(view: seasonItem.view), const Spacer(), Text( Utils.CustomStamp_str( diff --git a/lib/pages/subscription_detail/widget/sub_video_card.dart b/lib/pages/subscription_detail/widget/sub_video_card.dart index dcdee4ef..268c4a00 100644 --- a/lib/pages/subscription_detail/widget/sub_video_card.dart +++ b/lib/pages/subscription_detail/widget/sub_video_card.dart @@ -154,13 +154,9 @@ class VideoContent extends StatelessWidget { padding: const EdgeInsets.only(top: 2), child: Row( children: [ - StatView( - theme: 'gray', - view: videoItem.cntInfo['play'], - ), + StatView(view: videoItem.cntInfo['play']), const SizedBox(width: 8), - StatDanMu( - theme: 'gray', danmu: videoItem.cntInfo['danmaku']), + StatDanMu(danmu: videoItem.cntInfo['danmaku']), const Spacer(), ], ), diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index a89b4d28..f56b28e0 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -346,13 +346,11 @@ class _VideoInfoState extends State with TickerProviderStateMixin { child: Row( children: [ StatView( - theme: 'gray', view: widget.videoDetail!.stat!.view, size: 'medium', ), const SizedBox(width: 10), StatDanMu( - theme: 'gray', danmu: widget.videoDetail!.stat!.danmaku, size: 'medium', ), From 0bec084becbf1762922fa5e3d4fcc9b4b8435a4c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Thu, 12 Sep 2024 09:40:08 +0800 Subject: [PATCH 325/349] =?UTF-8?q?opt:=20=E6=94=B6=E8=97=8F=E5=A4=B9?= =?UTF-8?q?=E4=B8=BA=E7=A9=BA=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/user.dart | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/http/user.dart b/lib/http/user.dart index 972acfdd..7d0b29a3 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -57,6 +57,8 @@ class UserHttp { if (res.data['data'] != null) { data = FavFolderData.fromJson(res.data['data']); return {'status': true, 'data': data}; + } else { + return {'status': false, 'msg': '收藏夹为空'}; } } else { return { From 8aebf463e5f9a4ddf08325847f270f1bb9bfaf06 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 15 Sep 2024 01:16:30 +0800 Subject: [PATCH 326/349] =?UTF-8?q?opt:=20=E6=92=AD=E6=94=BE=E5=99=A8?= =?UTF-8?q?=E9=9F=B3=E9=87=8F&=E4=BA=AE=E5=BA=A6=E6=8E=A7=E5=88=B6?= =?UTF-8?q?=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/plugin/pl_player/view.dart | 110 +++--------------- lib/plugin/pl_player/widgets/control_bar.dart | 50 ++++++++ 2 files changed, 67 insertions(+), 93 deletions(-) create mode 100644 lib/plugin/pl_player/widgets/control_bar.dart diff --git a/lib/plugin/pl_player/view.dart b/lib/plugin/pl_player/view.dart index f3e0946b..75693445 100644 --- a/lib/plugin/pl_player/view.dart +++ b/lib/plugin/pl_player/view.dart @@ -26,6 +26,7 @@ import 'widgets/app_bar_ani.dart'; import 'widgets/backward_seek.dart'; import 'widgets/bottom_control.dart'; import 'widgets/common_btn.dart'; +import 'widgets/control_bar.dart'; import 'widgets/forward_seek.dart'; import 'widgets/play_pause_btn.dart'; @@ -484,104 +485,27 @@ class _PLVideoPlayerState extends State /// 音量🔊 控制条展示 Obx( - () => Align( - child: AnimatedOpacity( - curve: Curves.easeInOut, - opacity: _volumeIndicator.value ? 1.0 : 0.0, - duration: const Duration(milliseconds: 150), - child: Container( - alignment: Alignment.center, - decoration: BoxDecoration( - color: const Color(0x88000000), - borderRadius: BorderRadius.circular(64.0), - ), - height: 34.0, - width: 70.0, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - height: 34.0, - width: 28.0, - alignment: Alignment.centerRight, - child: Icon( - _volumeValue.value == 0.0 - ? Icons.volume_off - : _volumeValue.value < 0.5 - ? Icons.volume_down - : Icons.volume_up, - color: const Color(0xFFFFFFFF), - size: 20.0, - ), - ), - Expanded( - child: Text( - '${(_volumeValue.value * 100.0).round()}%', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 13.0, - color: Color(0xFFFFFFFF), - ), - ), - ), - const SizedBox(width: 6.0), - ], - ), - ), - ), + () => ControlBar( + visible: _volumeIndicator.value, + icon: _volumeValue.value < 1.0 / 3.0 + ? Icons.volume_mute + : _volumeValue.value < 2.0 / 3.0 + ? Icons.volume_down + : Icons.volume_up, + value: _volumeValue.value, ), ), /// 亮度🌞 控制条展示 Obx( - () => Align( - child: AnimatedOpacity( - curve: Curves.easeInOut, - opacity: _brightnessIndicator.value ? 1.0 : 0.0, - duration: const Duration(milliseconds: 150), - child: Container( - alignment: Alignment.center, - decoration: BoxDecoration( - color: const Color(0x88000000), - borderRadius: BorderRadius.circular(64.0), - ), - height: 34.0, - width: 70.0, - child: Row( - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - height: 30.0, - width: 28.0, - alignment: Alignment.centerRight, - child: Icon( - _brightnessValue.value < 1.0 / 3.0 - ? Icons.brightness_low - : _brightnessValue.value < 2.0 / 3.0 - ? Icons.brightness_medium - : Icons.brightness_high, - color: const Color(0xFFFFFFFF), - size: 18.0, - ), - ), - const SizedBox(width: 2.0), - Expanded( - child: Text( - '${(_brightnessValue.value * 100.0).round()}%', - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 13.0, - color: Color(0xFFFFFFFF), - ), - ), - ), - const SizedBox(width: 6.0), - ], - ), - ), - ), + () => ControlBar( + visible: _brightnessIndicator.value, + icon: _brightnessValue.value < 1.0 / 3.0 + ? Icons.brightness_low + : _brightnessValue.value < 2.0 / 3.0 + ? Icons.brightness_medium + : Icons.brightness_high, + value: _brightnessValue.value, ), ), diff --git a/lib/plugin/pl_player/widgets/control_bar.dart b/lib/plugin/pl_player/widgets/control_bar.dart new file mode 100644 index 00000000..44e2abfa --- /dev/null +++ b/lib/plugin/pl_player/widgets/control_bar.dart @@ -0,0 +1,50 @@ +import 'package:flutter/material.dart'; + +class ControlBar extends StatelessWidget { + final bool visible; + final IconData icon; + final double value; + + const ControlBar({ + Key? key, + required this.visible, + required this.icon, + required this.value, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + Color color = const Color(0xFFFFFFFF); + return Align( + child: AnimatedOpacity( + curve: Curves.easeInOut, + opacity: visible ? 1.0 : 0.0, + duration: const Duration(milliseconds: 150), + child: IntrinsicWidth( + child: Container( + padding: const EdgeInsets.fromLTRB(10, 2, 10, 2), + decoration: BoxDecoration( + color: const Color(0x88000000), + borderRadius: BorderRadius.circular(64.0), + ), + height: 34.0, + child: Row( + children: [ + Icon(icon, color: color, size: 18.0), + const SizedBox(width: 4.0), + Container( + constraints: const BoxConstraints(minWidth: 30.0), + child: Text( + '${(value * 100.0).round()}%', + textAlign: TextAlign.center, + style: TextStyle(fontSize: 13.0, color: color), + ), + ) + ], + ), + ), + ), + ), + ); + } +} From 389ca286a76b50c30b25b0fd2d20e05f6be90529 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 15 Sep 2024 02:03:53 +0800 Subject: [PATCH 327/349] mod: userInfo cache --- lib/utils/global_data_cache.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/utils/global_data_cache.dart b/lib/utils/global_data_cache.dart index c5884e79..9d925a0f 100644 --- a/lib/utils/global_data_cache.dart +++ b/lib/utils/global_data_cache.dart @@ -1,4 +1,5 @@ import 'package:hive/hive.dart'; +import 'package:pilipala/models/user/info.dart'; import 'package:pilipala/plugin/pl_player/models/play_repeat.dart'; import 'package:pilipala/plugin/pl_player/models/play_speed.dart'; import 'package:pilipala/utils/storage.dart'; @@ -7,6 +8,7 @@ import '../models/common/index.dart'; Box setting = GStrorage.setting; Box localCache = GStrorage.localCache; Box videoStorage = GStrorage.video; +Box userInfoCache = GStrorage.userInfo; class GlobalDataCache { late int imgQuality; @@ -39,6 +41,8 @@ class GlobalDataCache { late double longPressSpeed; // 播放器速度列表 late List speedsList; + // 用户信息 + UserInfoData? userInfo; // 私有构造函数 GlobalDataCache._(); @@ -96,5 +100,7 @@ class GlobalDataCache { final List playSpeedSystem = await videoStorage .get(VideoBoxKey.playSpeedSystem, defaultValue: playSpeed); speedsList.addAll(playSpeedSystem); + + userInfo = userInfoCache.get('userInfoCache'); } } From 5f02c17a2d8d31c8999a6bc816f4ad79990fb84b Mon Sep 17 00:00:00 2001 From: guozhigq Date: Wed, 18 Sep 2024 00:29:43 +0800 Subject: [PATCH 328/349] =?UTF-8?q?fix:=20=E5=85=B3=E6=B3=A8up=E4=B8=BB?= =?UTF-8?q?=E6=90=9C=E7=B4=A2ps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/follow_search/controller.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/follow_search/controller.dart b/lib/pages/follow_search/controller.dart index 9fd1590d..7dc47a45 100644 --- a/lib/pages/follow_search/controller.dart +++ b/lib/pages/follow_search/controller.dart @@ -48,7 +48,7 @@ class FollowSearchController extends GetxController { return {'status': true, 'data': [].obs}; } if (type == 'init') { - ps = 1; + pn = 1; } var res = await MemberHttp.getfollowSearch( mid: mid, From cf66d3be4c04eb0810576eae0ead9b14e3d007dc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Sep 2024 15:14:38 +0800 Subject: [PATCH 329/349] =?UTF-8?q?feat:=20=E7=A8=8D=E5=90=8E=E5=86=8D?= =?UTF-8?q?=E7=9C=8B&=E6=94=B6=E8=97=8F=E5=A4=B9=E6=92=AD=E6=94=BE?= =?UTF-8?q?=E5=85=A8=E9=83=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 + lib/http/user.dart | 108 +++++++ lib/models/video/later.dart | 270 ++++++++++++++++++ lib/pages/fav_detail/controller.dart | 22 +- lib/pages/fav_detail/view.dart | 9 + lib/pages/later/controller.dart | 20 +- lib/pages/later/view.dart | 9 + lib/pages/video/detail/controller.dart | 127 +++++++- .../video/detail/introduction/controller.dart | 4 +- lib/pages/video/detail/introduction/view.dart | 17 +- lib/pages/video/detail/view.dart | 80 ++++++ .../detail/widgets/watch_later_list.dart | 229 +++++++++++++++ 12 files changed, 880 insertions(+), 18 deletions(-) create mode 100644 lib/models/video/later.dart create mode 100644 lib/pages/video/detail/widgets/watch_later_list.dart diff --git a/lib/http/api.dart b/lib/http/api.dart index 93226946..f24918b3 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -565,4 +565,7 @@ class Api { /// 直播间发送弹幕 static const String sendLiveMsg = '${HttpString.liveBaseUrl}/msg/send'; + + /// 稍后再看&收藏夹视频列表 + static const String mediaList = '/x/v2/medialist/resource/list'; } diff --git a/lib/http/user.dart b/lib/http/user.dart index 972acfdd..333c4038 100644 --- a/lib/http/user.dart +++ b/lib/http/user.dart @@ -1,4 +1,9 @@ +import 'dart:convert'; +import 'dart:developer'; + import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:html/parser.dart'; +import 'package:pilipala/models/video/later.dart'; import '../common/constants.dart'; import '../models/model_hot_video_item.dart'; import '../models/user/fav_detail.dart'; @@ -428,4 +433,107 @@ class UserHttp { return {'status': false, 'msg': res.data['message']}; } } + + // 稍后再看播放全部 + // static Future toViewPlayAll({required int oid, required String bvid}) async { + // var res = await Request().get( + // Api.watchLaterHtml, + // data: { + // 'oid': oid, + // 'bvid': bvid, + // }, + // ); + // String scriptContent = + // extractScriptContents(parse(res.data).body!.outerHtml)[0]; + // int startIndex = scriptContent.indexOf('{'); + // int endIndex = scriptContent.lastIndexOf('};'); + // String jsonContent = scriptContent.substring(startIndex, endIndex + 1); + // // 解析JSON字符串为Map + // Map jsonData = json.decode(jsonContent); + // // 输出解析后的数据 + // return { + // 'status': true, + // 'data': jsonData['resourceList'] + // .map((e) => MediaVideoItemModel.fromJson(e)) + // .toList() + // }; + // } + + static List extractScriptContents(String htmlContent) { + RegExp scriptRegExp = RegExp(r''); + if (headContent != null) { + final match = regex.firstMatch(headContent); + if (match != null && match.groupCount >= 1) { + final content = match.group(1); + String decodedString = Uri.decodeComponent(content!); + Map map = jsonDecode(decodedString); + return {'status': true, 'data': map['access_id']}; + } else { + return {'status': false, 'data': '请检查登录状态'}; + } + } + return {'status': false, 'data': '请检查登录状态'}; + } + + // 获取用户专栏 + static Future getMemberArticle({ + required int mid, + required int pn, + required String wWebid, + String? offset, + }) async { + Map params = await WbiSign().makSign({ + 'host_mid': mid, + 'page': pn, + 'offset': offset, + 'web_location': 333.999, + 'w_webid': wWebid, + }); + var res = await Request().get(Api.opusList, data: { + 'host_mid': mid, + 'page': pn, + 'offset': offset, + 'web_location': 333.999, + 'w_webid': wWebid, + 'w_rid': params['w_rid'], + 'wts': params['wts'], + }); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': MemberArticleDataModel.fromJson(res.data['data']) + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'] ?? '请求异常', + }; + } + } } diff --git a/lib/models/member/article.dart b/lib/models/member/article.dart new file mode 100644 index 00000000..8489385e --- /dev/null +++ b/lib/models/member/article.dart @@ -0,0 +1,46 @@ +class MemberArticleDataModel { + MemberArticleDataModel({ + this.hasMore, + this.items, + this.offset, + this.updateNum, + }); + + bool? hasMore; + List? items; + String? offset; + int? updateNum; + + MemberArticleDataModel.fromJson(Map json) { + hasMore = json['has_more']; + items = json['items'] + .map((e) => MemberArticleItemModel.fromJson(e)) + .toList(); + offset = json['offset']; + updateNum = json['update_num']; + } +} + +class MemberArticleItemModel { + MemberArticleItemModel({ + this.content, + this.cover, + this.jumpUrl, + this.opusId, + this.stat, + }); + + String? content; + Map? cover; + String? jumpUrl; + String? opusId; + Map? stat; + + MemberArticleItemModel.fromJson(Map json) { + content = json['content']; + cover = json['cover']; + jumpUrl = json['jump_url']; + opusId = json['opus_id']; + stat = json['stat']; + } +} diff --git a/lib/pages/member/controller.dart b/lib/pages/member/controller.dart index ada869b5..3b7f24a4 100644 --- a/lib/pages/member/controller.dart +++ b/lib/pages/member/controller.dart @@ -240,4 +240,6 @@ class MemberController extends GetxController { } void pushfavPage() => Get.toNamed('/fav?mid=$mid'); + // 跳转图文专栏 + void pushArticlePage() => Get.toNamed('/memberArticle?mid=$mid'); } diff --git a/lib/pages/member/view.dart b/lib/pages/member/view.dart index be0ddedc..2939628c 100644 --- a/lib/pages/member/view.dart +++ b/lib/pages/member/view.dart @@ -170,32 +170,44 @@ class _MemberPageState extends State ), /// 视频 - Obx(() => ListTile( - onTap: _memberController.pushArchivesPage, - title: Text( - '${_memberController.isOwner.value ? '我' : 'Ta'}的投稿'), - trailing: const Icon(Icons.arrow_forward_outlined, - size: 19), - )), + Obx( + () => ListTile( + onTap: _memberController.pushArchivesPage, + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的投稿'), + trailing: + const Icon(Icons.arrow_forward_outlined, size: 19), + ), + ), /// 他的收藏夹 - Obx(() => ListTile( - onTap: _memberController.pushfavPage, - title: Text( - '${_memberController.isOwner.value ? '我' : 'Ta'}的收藏'), - trailing: const Icon(Icons.arrow_forward_outlined, - size: 19), - )), + Obx( + () => ListTile( + onTap: _memberController.pushfavPage, + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的收藏'), + trailing: + const Icon(Icons.arrow_forward_outlined, size: 19), + ), + ), /// 专栏 - Obx(() => ListTile( + Obx( + () => ListTile( + onTap: _memberController.pushArticlePage, title: Text( - '${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'))), + '${_memberController.isOwner.value ? '我' : 'Ta'}的专栏'), + trailing: + const Icon(Icons.arrow_forward_outlined, size: 19), + ), + ), /// 合集 - Obx(() => ListTile( - title: Text( - '${_memberController.isOwner.value ? '我' : 'Ta'}的合集'))), + Obx( + () => ListTile( + title: Text( + '${_memberController.isOwner.value ? '我' : 'Ta'}的合集')), + ), MediaQuery.removePadding( removeTop: true, removeBottom: true, diff --git a/lib/pages/member_article/controller.dart b/lib/pages/member_article/controller.dart new file mode 100644 index 00000000..cffce2fe --- /dev/null +++ b/lib/pages/member_article/controller.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/http/member.dart'; +import 'package:pilipala/models/member/article.dart'; + +class MemberArticleController extends GetxController { + final ScrollController scrollController = ScrollController(); + late int mid; + int pn = 1; + String? offset; + bool hasMore = true; + String? wWebid; + RxBool isLoading = false.obs; + RxList articleList = [].obs; + + @override + void onInit() { + super.onInit(); + mid = int.parse(Get.parameters['mid']!); + } + + // 获取wWebid + Future getWWebid() async { + var res = await MemberHttp.getWWebid(mid: mid); + if (res['status']) { + wWebid = res['data']; + } else { + wWebid = '-1'; + SmartDialog.showToast(res['msg']); + } + } + + Future getMemberArticle(type) async { + if (isLoading.value) { + return; + } + isLoading.value = true; + if (wWebid == null) { + await getWWebid(); + } + if (type == 'init') { + pn = 1; + articleList.clear(); + } + var res = await MemberHttp.getMemberArticle( + mid: mid, + pn: pn, + offset: offset, + wWebid: wWebid!, + ); + if (res['status']) { + offset = res['data'].offset; + hasMore = res['data'].hasMore!; + if (type == 'init') { + articleList.value = res['data'].items; + } + if (type == 'onLoad') { + articleList.addAll(res['data'].items); + } + pn += 1; + } else { + SmartDialog.showToast(res['msg']); + } + isLoading.value = false; + return res; + } +} diff --git a/lib/pages/member_article/index.dart b/lib/pages/member_article/index.dart new file mode 100644 index 00000000..bfc2f344 --- /dev/null +++ b/lib/pages/member_article/index.dart @@ -0,0 +1,4 @@ +library member_article; + +export './controller.dart'; +export './view.dart'; diff --git a/lib/pages/member_article/view.dart b/lib/pages/member_article/view.dart new file mode 100644 index 00000000..e23e208d --- /dev/null +++ b/lib/pages/member_article/view.dart @@ -0,0 +1,176 @@ +import 'package:easy_debounce/easy_throttle.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:pilipala/common/skeleton/skeleton.dart'; +import 'package:pilipala/common/widgets/http_error.dart'; +import 'package:pilipala/common/widgets/network_img_layer.dart'; +import 'package:pilipala/common/widgets/no_data.dart'; +import 'package:pilipala/utils/utils.dart'; + +import 'controller.dart'; + +class MemberArticlePage extends StatefulWidget { + const MemberArticlePage({super.key}); + + @override + State createState() => _MemberArticlePageState(); +} + +class _MemberArticlePageState extends State { + late MemberArticleController _memberArticleController; + late Future _futureBuilderFuture; + late ScrollController scrollController; + late int mid; + + @override + void initState() { + super.initState(); + mid = int.parse(Get.parameters['mid']!); + final String heroTag = Utils.makeHeroTag(mid); + _memberArticleController = Get.put(MemberArticleController(), tag: heroTag); + _futureBuilderFuture = _memberArticleController.getMemberArticle('init'); + scrollController = _memberArticleController.scrollController; + + scrollController.addListener(_scrollListener); + } + + void _scrollListener() { + if (scrollController.position.pixels >= + scrollController.position.maxScrollExtent - 200) { + EasyThrottle.throttle( + 'member_archives', const Duration(milliseconds: 500), () { + _memberArticleController.getMemberArticle('onLoad'); + }); + } + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + titleSpacing: 0, + centerTitle: false, + title: const Text('Ta的图文', style: TextStyle(fontSize: 16)), + ), + body: FutureBuilder( + future: _futureBuilderFuture, + builder: (BuildContext context, snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.data != null) { + return _buildContent(snapshot.data as Map); + } else { + return _buildError(snapshot.data['msg']); + } + } else { + return ListView.builder( + itemCount: 10, + itemBuilder: (BuildContext context, int index) { + return _buildSkeleton(); + }, + ); + } + }, + ), + ); + } + + Widget _buildContent(Map data) { + RxList list = _memberArticleController.articleList; + if (data['status']) { + return Obx( + () => list.isNotEmpty + ? ListView.separated( + controller: scrollController, + itemCount: list.length, + separatorBuilder: (BuildContext context, int index) { + return Divider( + height: 10, + color: Theme.of(context).dividerColor.withOpacity(0.15), + ); + }, + itemBuilder: (BuildContext context, int index) { + return _buildListItem(list[index]); + }, + ) + : const CustomScrollView( + physics: NeverScrollableScrollPhysics(), + slivers: [ + NoData(), + ], + ), + ); + } else { + return _buildError(data['msg']); + } + } + + Widget _buildListItem(dynamic item) { + return ListTile( + onTap: () { + Get.toNamed('/opus', parameters: { + 'title': item.content, + 'id': item.opusId, + 'articleType': 'opus', + }); + }, + leading: NetworkImgLayer( + width: 50, + height: 50, + type: 'emote', + src: item.cover['url'], + ), + title: Text( + item.content, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ), + subtitle: Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + '${item.stat["like"]}人点赞', + style: TextStyle( + fontSize: 12, + color: Theme.of(context).colorScheme.outline, + ), + ), + ), + ); + } + + Widget _buildError(String errMsg) { + return CustomScrollView( + physics: const NeverScrollableScrollPhysics(), + slivers: [ + SliverToBoxAdapter( + child: HttpError( + errMsg: errMsg, + fn: () {}, + ), + ), + ], + ); + } + + Widget _buildSkeleton() { + return Skeleton( + child: ListTile( + leading: Container( + width: 50, + height: 50, + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.onInverseSurface, + borderRadius: BorderRadius.circular(4), + ), + ), + title: Container( + height: 16, + color: Theme.of(context).colorScheme.onInverseSurface, + ), + subtitle: Container( + height: 11, + color: Theme.of(context).colorScheme.onInverseSurface, + ), + ), + ); + } +} diff --git a/lib/router/app_pages.dart b/lib/router/app_pages.dart index 136de91f..a679fd79 100644 --- a/lib/router/app_pages.dart +++ b/lib/router/app_pages.dart @@ -5,6 +5,7 @@ import 'package:get/get.dart'; import 'package:hive/hive.dart'; import 'package:pilipala/pages/fav_edit/index.dart'; import 'package:pilipala/pages/follow_search/view.dart'; +import 'package:pilipala/pages/member_article/index.dart'; import 'package:pilipala/pages/message/at/index.dart'; import 'package:pilipala/pages/message/like/index.dart'; import 'package:pilipala/pages/message/reply/index.dart'; @@ -186,6 +187,9 @@ class Routes { name: '/messageSystem', page: () => const MessageSystemPage()), // 收藏夹编辑 CustomGetPage(name: '/favEdit', page: () => const FavEditPage()), + // 用户专栏 + CustomGetPage( + name: '/memberArticle', page: () => const MemberArticlePage()), ]; } From aa6b5af05df17894541511be4dbfd6dc3b4a72b9 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 28 Sep 2024 17:24:23 +0800 Subject: [PATCH 348/349] =?UTF-8?q?fix:=20=E9=87=8D=E5=AE=9A=E5=90=91cv?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/read.dart | 25 +++++++++++++++++++++++++ lib/pages/opus/controller.dart | 11 ++++++++++- lib/pages/opus/view.dart | 3 +++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/lib/http/read.dart b/lib/http/read.dart index 4fff4547..cc522505 100644 --- a/lib/http/read.dart +++ b/lib/http/read.dart @@ -20,6 +20,29 @@ class ReadHttp { var res = await Request().get('https://www.bilibili.com/opus/$id', extra: { 'ua': 'pc', }); + String? headContent = parse(res.data).head?.outerHtml; + var document = parse(headContent); + var linkTags = document.getElementsByTagName('link'); + bool isCv = false; + String cvId = ''; + for (var linkTag in linkTags) { + var attributes = linkTag.attributes; + if (attributes.containsKey('rel') && + attributes['rel'] == 'canonical' && + attributes.containsKey('data-vue-meta') && + attributes['data-vue-meta'] == 'true') { + final String cvHref = linkTag.attributes['href']!; + RegExp regex = RegExp(r'cv(\d+)'); + RegExpMatch? match = regex.firstMatch(cvHref); + if (match != null) { + cvId = match.group(1)!; + } else { + print('No match found.'); + } + isCv = true; + break; + } + } String scriptContent = extractScriptContents(parse(res.data).body!.outerHtml)[0]; int startIndex = scriptContent.indexOf('{'); @@ -30,6 +53,8 @@ class ReadHttp { return { 'status': true, 'data': OpusDataModel.fromJson(jsonData), + 'isCv': isCv, + 'cvId': cvId, }; } } diff --git a/lib/pages/opus/controller.dart b/lib/pages/opus/controller.dart index f5c35770..86dd67a5 100644 --- a/lib/pages/opus/controller.dart +++ b/lib/pages/opus/controller.dart @@ -31,7 +31,16 @@ class OpusController extends GetxController { Future fetchOpusData() async { var res = await ReadHttp.parseArticleOpus(id: id); if (res['status']) { - opusData.value = res['data']; + List keys = res.keys.toList(); + if (keys.contains('isCv') && res['isCv']) { + Get.offNamed('/read', parameters: { + 'id': res['cvId'], + 'title': title.value, + 'articleType': 'cv', + }); + } else { + opusData.value = res['data']; + } } return res; } diff --git a/lib/pages/opus/view.dart b/lib/pages/opus/view.dart index a36b4813..8535230f 100644 --- a/lib/pages/opus/view.dart +++ b/lib/pages/opus/view.dart @@ -107,6 +107,9 @@ class _OpusPageState extends State { } Widget _buildContent(OpusDataModel opusData) { + if (opusData.detail == null) { + return const SizedBox(); + } final modules = opusData.detail!.modules!; late ModuleContent moduleContent; // 获取所有的图片链接 From 26a57336750d08a48813650f202d6fdd180e6b4a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 28 Sep 2024 18:22:42 +0800 Subject: [PATCH 349/349] =?UTF-8?q?feat:=20cv=E4=B8=93=E6=A0=8F=E8=AE=B0?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/http/api.dart | 3 +++ lib/http/read.dart | 39 ++++++++++++++++++++++++++++++++-- lib/pages/read/controller.dart | 5 +++++ 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 42819d7d..ff49b314 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -578,4 +578,7 @@ class Api { /// 稍后再看&收藏夹视频列表 static const String mediaList = '/x/v2/medialist/resource/list'; + + /// + static const String getViewInfo = '/x/article/viewinfo'; } diff --git a/lib/http/read.dart b/lib/http/read.dart index 22ca3503..558985b4 100644 --- a/lib/http/read.dart +++ b/lib/http/read.dart @@ -1,9 +1,9 @@ import 'dart:convert'; -import 'dart:developer'; import 'package:html/parser.dart'; import 'package:pilipala/models/read/opus.dart'; import 'package:pilipala/models/read/read.dart'; -import 'init.dart'; +import 'package:pilipala/utils/wbi_sign.dart'; +import 'index.dart'; class ReadHttp { static List extractScriptContents(String htmlContent) { @@ -53,4 +53,39 @@ class ReadHttp { 'data': ReadDataModel.fromJson(jsonData), }; } + + // + static Future getViewInfo({required String id}) async { + Map params = await WbiSign().makSign({ + 'id': id, + 'mobi_app': 'pc', + 'from': 'web', + 'gaia_source': 'main_web', + 'web_location': 333.976, + }); + var res = await Request().get( + Api.getViewInfo, + data: { + 'id': id, + 'mobi_app': 'pc', + 'from': 'web', + 'gaia_source': 'main_web', + 'web_location': 333.976, + 'w_rid': params['w_rid'], + 'wts': params['wts'], + }, + ); + if (res.data['code'] == 0) { + return { + 'status': true, + 'data': res.data['data'], + }; + } else { + return { + 'status': false, + 'data': [], + 'msg': res.data['message'], + }; + } + } } diff --git a/lib/pages/read/controller.dart b/lib/pages/read/controller.dart index d2e942fc..a0e4ef8e 100644 --- a/lib/pages/read/controller.dart +++ b/lib/pages/read/controller.dart @@ -23,6 +23,7 @@ class ReadPageController extends GetxController { id = Get.parameters['id']!; articleType = Get.parameters['articleType']!; scrollController.addListener(_scrollListener); + fetchViewInfo(); } Future fetchCvData() async { @@ -80,6 +81,10 @@ class ReadPageController extends GetxController { ); } + void fetchViewInfo() { + ReadHttp.getViewInfo(id: id); + } + @override void onClose() { scrollController.removeListener(_scrollListener);