From 2e329553d914d4203e776d27c48194fc32fb2fdd Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 26 Aug 2024 23:23:43 +0800 Subject: [PATCH 01/35] =?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 02/35] =?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 03/35] 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 5c7f477ad8d467f93a056794165ef30e15e4584a Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 31 Aug 2024 14:26:03 +0800 Subject: [PATCH 04/35] =?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 10f464aab15885fba15bd9e9f1782a08bebf8344 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Mon, 9 Sep 2024 00:47:16 +0800 Subject: [PATCH 05/35] =?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 06/35] =?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 07/35] =?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 08/35] =?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 09/35] =?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 10/35] 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 cf66d3be4c04eb0810576eae0ead9b14e3d007dc Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 21 Sep 2024 15:14:38 +0800 Subject: [PATCH 11/35] =?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 29/35] =?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 30/35] =?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); From b34fc2ff1f9b796c1e399335c4f71b7d307d263c Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sat, 28 Sep 2024 23:16:00 +0800 Subject: [PATCH 31/35] =?UTF-8?q?mod:=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/msg.dart | 23 +++++++++++++ lib/models/msg/system.dart | 15 +++++++-- lib/pages/message/system/controller.dart | 43 +++++++++++++++++++----- lib/pages/message/system/view.dart | 7 ++-- 5 files changed, 78 insertions(+), 14 deletions(-) diff --git a/lib/http/api.dart b/lib/http/api.dart index 6c5374dd..588bd058 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -555,6 +555,10 @@ class Api { static const String messageSystemAPi = '${HttpString.messageBaseUrl}/x/sys-msg/query_unified_notify'; + /// 系统通知 个人 + static const String userMessageSystemAPi = + '${HttpString.messageBaseUrl}/x/sys-msg/query_user_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 2de9cd49..869b5a28 100644 --- a/lib/http/msg.dart +++ b/lib/http/msg.dart @@ -330,4 +330,27 @@ class MsgHttp { }; } } + + static Future messageSystemAccount() async { + var res = await Request().get(Api.userMessageSystemAPi, data: { + 'csrf': await Request.getCsrf(), + 'page_size': 20, + 'build': 0, + 'mobi_app': 'web', + }); + if (res.data['code'] == 0) { + try { + 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 index 20427707..12b1ac77 100644 --- a/lib/models/msg/system.dart +++ b/lib/models/msg/system.dart @@ -5,7 +5,7 @@ class MessageSystemModel { int? cursor; int? type; String? title; - Map? content; + dynamic content; Source? source; String? timeAt; int? cardType; @@ -45,7 +45,9 @@ class MessageSystemModel { cursor: jsons["cursor"], type: jsons["type"], title: jsons["title"], - content: json.decode(jsons["content"]), + content: isValidJson(jsons["content"]) + ? json.decode(jsons["content"]) + : jsons["content"], source: Source.fromJson(jsons["source"]), timeAt: jsons["time_at"], cardType: jsons["card_type"], @@ -75,3 +77,12 @@ class Source { logo: json["logo"], ); } + +bool isValidJson(String str) { + try { + json.decode(str); + } catch (e) { + return false; + } + return true; +} diff --git a/lib/pages/message/system/controller.dart b/lib/pages/message/system/controller.dart index f63a659a..c4731120 100644 --- a/lib/pages/message/system/controller.dart +++ b/lib/pages/message/system/controller.dart @@ -1,3 +1,4 @@ +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:get/get.dart'; import 'package:pilipala/http/msg.dart'; import 'package:pilipala/models/msg/system.dart'; @@ -5,18 +6,44 @@ import 'package:pilipala/models/msg/system.dart'; class MessageSystemController extends GetxController { RxList systemItems = [].obs; - Future queryMessageSystem({String type = 'init'}) async { - var res = await MsgHttp.messageSystem(); - if (res['status']) { - if (type == 'init') { - systemItems.value = res['data']; - } else { - systemItems.addAll(res['data']); - } + Future queryAndProcessMessages({String type = 'init'}) async { + // 并行调用两个接口 + var results = await Future.wait([ + queryMessageSystem(type: type), + queryMessageSystemAccount(type: type), + ]); + + // 对返回的数据进行处理 + var systemRes = results[0]; + var accountRes = results[1]; + + if (systemRes['status'] || accountRes['status']) { + // 处理返回的数据 + List combinedData = [ + ...systemRes['data'], + ...accountRes['data'] + ]; + combinedData.sort((a, b) => b.cursor!.compareTo(a.cursor!)); + systemItems.addAll(combinedData); + systemItems.refresh(); if (systemItems.isNotEmpty) { systemMarkRead(systemItems.first.cursor!); } + } else { + SmartDialog.showToast(systemRes['msg'] ?? accountRes['msg']); } + return systemRes; + } + + // 获取系统消息 + Future queryMessageSystem({String type = 'init'}) async { + var res = await MsgHttp.messageSystem(); + return res; + } + + // 获取系统消息 个人 + Future queryMessageSystemAccount({String type = 'init'}) async { + var res = await MsgHttp.messageSystemAccount(); return res; } diff --git a/lib/pages/message/system/view.dart b/lib/pages/message/system/view.dart index f7b94e5a..ee10b639 100644 --- a/lib/pages/message/system/view.dart +++ b/lib/pages/message/system/view.dart @@ -20,7 +20,7 @@ class _MessageSystemPageState extends State { @override void initState() { super.initState(); - _futureBuilderFuture = _messageSystemCtr.queryMessageSystem(); + _futureBuilderFuture = _messageSystemCtr.queryAndProcessMessages(); } @override @@ -31,7 +31,7 @@ class _MessageSystemPageState extends State { ), body: RefreshIndicator( onRefresh: () async { - await _messageSystemCtr.queryMessageSystem(); + await _messageSystemCtr.queryAndProcessMessages(); }, child: FutureBuilder( future: _futureBuilderFuture, @@ -42,7 +42,6 @@ class _MessageSystemPageState extends State { } if (snapshot.data['status']) { final systemItems = _messageSystemCtr.systemItems; - print(systemItems.length); return Obx( () => ListView.separated( controller: scrollController, @@ -115,7 +114,7 @@ class SystemItem extends StatelessWidget { style: TextStyle(color: Theme.of(context).colorScheme.outline), ), const SizedBox(height: 6), - Text(item.content!['web']), + Text(item.content is String ? item.content : item.content!['web']), ], ), ); From ec5cc6ff91d42a89759a1d6b06568c7ee96ebaa4 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Sep 2024 01:01:45 +0800 Subject: [PATCH 32/35] =?UTF-8?q?feat:=20=E7=9B=B4=E6=92=AD=E9=97=B4?= =?UTF-8?q?=E5=8E=86=E5=8F=B2=E8=AE=B0=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/live.dart | 11 +++++++++++ lib/pages/live_room/controller.dart | 13 +++++++++++++ 3 files changed, 28 insertions(+) diff --git a/lib/http/api.dart b/lib/http/api.dart index 93226946..9cff2644 100644 --- a/lib/http/api.dart +++ b/lib/http/api.dart @@ -565,4 +565,8 @@ class Api { /// 直播间发送弹幕 static const String sendLiveMsg = '${HttpString.liveBaseUrl}/msg/send'; + + /// 直播间记录 + static const String liveRoomEntry = + '${HttpString.liveBaseUrl}/xlive/web-room/v1/index/roomEntryAction'; } diff --git a/lib/http/live.dart b/lib/http/live.dart index f6fc4ea4..3935f154 100644 --- a/lib/http/live.dart +++ b/lib/http/live.dart @@ -117,4 +117,15 @@ class LiveHttp { }; } } + + // 直播历史记录 + static Future liveRoomEntry({required int roomId}) async { + await Request().post(Api.liveRoomEntry, queryParameters: { + 'room_id': roomId, + 'platform': 'pc', + 'csrf_token': await Request.getCsrf(), + 'csrf': await Request.getCsrf(), + 'visit_id': '', + }); + } } diff --git a/lib/pages/live_room/controller.dart b/lib/pages/live_room/controller.dart index 5d4e2b67..fdc25216 100644 --- a/lib/pages/live_room/controller.dart +++ b/lib/pages/live_room/controller.dart @@ -95,6 +95,7 @@ class LiveRoomController extends GetxController { autoplay: true, ); plPlayerController.isOpenDanmu.value = danmakuSwitch.value; + heartBeat(); } Future queryLiveInfo() async { @@ -278,8 +279,20 @@ class LiveRoomController extends GetxController { } } + // 历史记录 + void heartBeat() { + LiveHttp.liveRoomEntry(roomId: roomId); + } + + String encodeToBase64(String input) { + List bytes = utf8.encode(input); + String base64Str = base64.encode(bytes); + return base64Str; + } + @override void onClose() { + heartBeat(); plSocket?.onClose(); super.onClose(); } From 400b87395a1e8442765e83a922548e5d38758048 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Sep 2024 12:00:38 +0800 Subject: [PATCH 33/35] =?UTF-8?q?mod:=20=E6=A0=87=E9=A2=98=E6=9C=80?= =?UTF-8?q?=E5=A4=A7=E8=A1=8C=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/video/detail/introduction/view.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pages/video/detail/introduction/view.dart b/lib/pages/video/detail/introduction/view.dart index 27e39760..ef49a2ef 100644 --- a/lib/pages/video/detail/introduction/view.dart +++ b/lib/pages/video/detail/introduction/view.dart @@ -324,7 +324,7 @@ class _VideoInfoState extends State with TickerProviderStateMixin { expanded: Text( widget.videoDetail!.title!, softWrap: true, - maxLines: 4, + maxLines: 10, style: const TextStyle( fontSize: 18, fontWeight: FontWeight.bold, From 11aa465a8f9aee7fdc63476df59d4e42d7d2d724 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Sep 2024 14:01:26 +0800 Subject: [PATCH 34/35] =?UTF-8?q?feat:=20=E7=A7=81=E4=BF=A1=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E6=9F=A5=E7=9C=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/pages/whisper/view.dart | 17 +++--- lib/pages/whisper_detail/controller.dart | 13 +++++ lib/pages/whisper_detail/view.dart | 28 ++++------ .../whisper_detail/widget/chat_item.dart | 54 +++++++++++++++++-- 4 files changed, 83 insertions(+), 29 deletions(-) diff --git a/lib/pages/whisper/view.dart b/lib/pages/whisper/view.dart index 7082619f..e97aa79b 100644 --- a/lib/pages/whisper/view.dart +++ b/lib/pages/whisper/view.dart @@ -217,6 +217,7 @@ class SessionItem extends StatelessWidget { final String heroTag = Utils.makeHeroTag(sessionItem.accountInfo?.mid ?? 0); final content = sessionItem.lastMsg.content; final msgStatus = sessionItem.lastMsg.msgStatus; + final int msgType = sessionItem.lastMsg.msgType; return ListTile( onTap: () { @@ -251,13 +252,15 @@ class SessionItem extends StatelessWidget { subtitle: Text( msgStatus == 1 ? '你撤回了一条消息' - : content != null && content != '' - ? (content['text'] ?? - content['content'] ?? - content['title'] ?? - content['reply_content'] ?? - '不支持的消息类型') - : '不支持的消息类型', + : msgType == 2 + ? '[图片]' + : 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 32e0ceb0..ec828afb 100644 --- a/lib/pages/whisper_detail/controller.dart +++ b/lib/pages/whisper_detail/controller.dart @@ -22,6 +22,7 @@ class WhisperDetailController extends GetxController { final TextEditingController replyContentController = TextEditingController(); Box userInfoCache = GStrorage.userInfo; List emoteList = []; + List picList = []; @override void onInit() { @@ -41,6 +42,18 @@ class WhisperDetailController extends GetxController { var res = await MsgHttp.sessionMsg(talkerId: talkerId); if (res['status']) { messageList.value = res['data'].messages; + // 找出图片 + try { + for (var item in messageList) { + if (item.msgType == 2) { + picList.add(item.content['url']); + } + } + picList = picList.reversed.toList(); + } catch (e) { + print('e: $e'); + } + if (messageList.isNotEmpty) { ackSessionMsg(); if (res['data'].eInfos != null) { diff --git a/lib/pages/whisper_detail/view.dart b/lib/pages/whisper_detail/view.dart index 912b5dc5..3ea59343 100644 --- a/lib/pages/whisper_detail/view.dart +++ b/lib/pages/whisper_detail/view.dart @@ -193,27 +193,21 @@ class _WhisperDetailPageState extends State ? const SizedBox() : Align( alignment: Alignment.topCenter, - child: ListView.builder( + child: ListView.separated( 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); - } + return ChatItem( + item: messageList[i], + e_infos: _whisperDetailController.eInfos, + ctr: _whisperDetailController, + ); + }, + separatorBuilder: (_, int i) { + return i == 0 + ? const SizedBox(height: 20) + : const SizedBox.shrink(); }, ), ), diff --git a/lib/pages/whisper_detail/widget/chat_item.dart b/lib/pages/whisper_detail/widget/chat_item.dart index 94347aff..7ddd1d83 100644 --- a/lib/pages/whisper_detail/widget/chat_item.dart +++ b/lib/pages/whisper_detail/widget/chat_item.dart @@ -2,14 +2,18 @@ // ignore_for_file: constant_identifier_names import 'dart:convert'; +import 'package:cached_network_image/cached_network_image.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/network_img_layer.dart'; +import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart'; +import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart'; import 'package:pilipala/utils/route_push.dart'; import 'package:pilipala/utils/utils.dart'; import 'package:pilipala/utils/storage.dart'; import '../../../http/search.dart'; +import '../controller.dart'; enum MsgType { invalid(value: 0, label: "空空的~"), @@ -42,10 +46,12 @@ enum MsgType { class ChatItem extends StatelessWidget { dynamic item; List? e_infos; + WhisperDetailController ctr; ChatItem({ super.key, - this.item, + required this.item, + required this.ctr, this.e_infos, }); @@ -157,10 +163,48 @@ class ChatItem extends StatelessWidget { case MsgType.text: return richTextMessage(context); case MsgType.pic: - return NetworkImgLayer( - width: 220, - height: 220 * content['height'] / content['width'], - src: content['url'], + return InkWell( + onTap: () { + Navigator.of(context).push( + HeroDialogRoute( + builder: (BuildContext context) => InteractiveviewerGallery( + sources: ctr.picList, + initIndex: ctr.picList.indexOf(content['url']), + 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: ctr.picList[index], + child: CachedNetworkImage( + fadeInDuration: const Duration(milliseconds: 0), + imageUrl: ctr.picList[index], + fit: BoxFit.contain, + ), + ), + ), + ); + }, + onPageChanged: (int pageIndex) {}, + ), + ), + ); + }, + child: NetworkImgLayer( + width: 220, + height: 220 * content['height'] / content['width'], + src: content['url'], + ), ); case MsgType.share_v2: return Column( From b6390ea626f3aaa0e02b17761b867f17095cd612 Mon Sep 17 00:00:00 2001 From: guozhigq Date: Sun, 29 Sep 2024 14:07:21 +0800 Subject: [PATCH 35/35] =?UTF-8?q?fix:=20read=E4=B8=93=E6=A0=8F=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E6=B5=8F=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/common/widgets/html_render.dart | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/common/widgets/html_render.dart b/lib/common/widgets/html_render.dart index c626978e..b2aa75ff 100644 --- a/lib/common/widgets/html_render.dart +++ b/lib/common/widgets/html_render.dart @@ -1,11 +1,9 @@ import 'package:cached_network_image/cached_network_image.dart'; import 'package:flutter/material.dart'; import 'package:flutter_html/flutter_html.dart'; -import 'package:get/get.dart'; import 'package:pilipala/plugin/pl_gallery/hero_dialog_route.dart'; import 'package:pilipala/plugin/pl_gallery/interactiveviewer_gallery.dart'; import 'package:pilipala/utils/highlight.dart'; -import 'network_img_layer.dart'; // ignore: must_be_immutable class HtmlRender extends StatelessWidget { @@ -85,11 +83,11 @@ class HtmlRender extends StatelessWidget { }, child: Center( child: Hero( - tag: imgUrl, + tag: imgList?[index] ?? imgUrl, child: CachedNetworkImage( fadeInDuration: const Duration(milliseconds: 0), - imageUrl: imgUrl, + imageUrl: imgList?[index] ?? imgUrl, fit: BoxFit.contain, ), ),